2 * usign - tiny signify replacement
4 * Copyright (C) 2015 Felix Fietkau <nbd@openwrt.org>
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
36 uint8_t fingerprint
[8];
37 uint8_t pubkey
[EDSIGN_PUBLIC_KEY_SIZE
];
46 uint8_t fingerprint
[8];
52 uint8_t fingerprint
[8];
53 uint8_t sig
[EDSIGN_SIGNATURE_SIZE
];
56 static const char *pubkeyfile
;
57 static const char *pubkeydir
;
58 static const char *sigfile
;
59 static const char *seckeyfile
;
60 static const char *comment
;
70 static uint64_t fingerprint_u64(const uint8_t *data
)
74 #define ADD(_v) val = (val << 8) | _v
89 file_error(const char *filename
, bool _read
)
91 if (!quiet
|| cmd
!= CMD_VERIFY
)
92 fprintf(stderr
, "Cannot open file '%s' for %s\n", filename
,
93 _read
? "reading" : "writing");
98 open_file(const char *filename
, bool _read
)
102 if (!strcmp(filename
, "-"))
103 return _read
? stdin
: stdout
;
105 f
= fopen(filename
, _read
? "r" : "w");
107 file_error(filename
, _read
);
113 get_file(const char *filename
, char *buf
, int buflen
)
115 FILE *f
= open_file(filename
, true);
119 char *cur
= fgets(buf
, buflen
, f
);
122 fprintf(stderr
, "Premature end of file\n");
126 if (strchr(buf
, '\n'))
130 len
= fread(buf
, 1, buflen
- 1, f
);
135 get_base64_file(const char *file
, void *dest
, int size
, void *buf
, int buflen
)
137 get_file(file
, buf
, buflen
- 1);
138 return b64_decode(buf
, dest
, size
) == size
;
141 static void write_file(const char *name
, const uint8_t *fingerprint
,
142 const char *prefix
, char *buf
)
146 f
= open_file(name
, false);
147 fputs("untrusted comment: ", f
);
151 fprintf(f
, "%s %"PRIx64
, prefix
,
152 fingerprint_u64(fingerprint
));
153 fprintf(f
, "\n%s\n", buf
);
157 static int verify(const char *msgfile
)
161 struct edsign_verify_state vst
;
165 f
= open_file(msgfile
, true);
167 fprintf(stderr
, "Cannot open message file\n");
171 if (!get_base64_file(sigfile
, &sig
, sizeof(sig
), buf
, sizeof(buf
)) ||
172 memcmp(sig
.pkalg
, "Ed", 2) != 0) {
173 fprintf(stderr
, "Failed to decode signature\n");
178 snprintf(buf
, sizeof(buf
), "%s/%"PRIx64
, pubkeydir
,
179 fingerprint_u64(sig
.fingerprint
));
183 if (!get_base64_file(pubkeyfile
, &pkey
, sizeof(pkey
), buf
, sizeof(buf
)) ||
184 memcmp(pkey
.pkalg
, "Ed", 2) != 0) {
185 fprintf(stderr
, "Failed to decode public key\n");
189 edsign_verify_init(&vst
, sig
.sig
, pkey
.pubkey
);
192 int len
= fread(buf
, 1, sizeof(buf
), f
);
193 edsign_verify_add(&vst
, buf
, len
);
197 if (!edsign_verify(&vst
, sig
.sig
, pkey
.pubkey
)) {
199 fprintf(stderr
, "verification failed\n");
204 fprintf(stderr
, "OK\n");
208 static int sign(const char *msgfile
)
221 if (!get_base64_file(seckeyfile
, &skey
, sizeof(skey
), buf
, sizeof(buf
)) ||
222 memcmp(skey
.pkalg
, "Ed", 2) != 0) {
223 fprintf(stderr
, "Failed to decode secret key\n");
227 if (skey
.kdfrounds
) {
228 fprintf(stderr
, "Password protected secret keys are not supported\n");
232 mfd
= open(msgfile
, O_RDONLY
, 0);
233 if (mfd
< 0 || fstat(mfd
, &st
) < 0 ||
234 (m
= mmap(0, st
.st_size
, PROT_READ
, MAP_PRIVATE
, mfd
, 0)) == MAP_FAILED
) {
237 perror("Cannot open message file");
242 memcpy(sig
.fingerprint
, skey
.fingerprint
, sizeof(sig
.fingerprint
));
243 edsign_sec_to_pub(pubkey
, skey
.seckey
);
244 edsign_sign(sig
.sig
, pubkey
, skey
.seckey
, m
, mlen
);
248 if (b64_encode(&sig
, sizeof(sig
), buf
, sizeof(buf
)) < 0)
251 write_file(sigfile
, sig
.fingerprint
, "signed by key", buf
);
256 static int fingerprint(void)
265 get_base64_file(seckeyfile
, &skey
, sizeof(skey
), buf
, sizeof(buf
)))
266 fp
= skey
.fingerprint
;
267 else if (pubkeyfile
&&
268 get_base64_file(pubkeyfile
, &pkey
, sizeof(pkey
), buf
, sizeof(buf
)))
269 fp
= pkey
.fingerprint
;
271 get_base64_file(sigfile
, &sig
, sizeof(sig
), buf
, sizeof(buf
)))
272 fp
= sig
.fingerprint
;
276 fprintf(stdout
, "%"PRIx64
"\n", fingerprint_u64(fp
));
280 static int generate(void)
282 struct seckey skey
= {
287 struct pubkey pkey
= {
290 struct sha512_state s
;
294 f
= fopen("/dev/urandom", "r");
296 fread(skey
.fingerprint
, sizeof(skey
.fingerprint
), 1, f
) != 1 ||
297 fread(skey
.seckey
, EDSIGN_SECRET_KEY_SIZE
, 1, f
) != 1 ||
298 fread(skey
.salt
, sizeof(skey
.salt
), 1, f
) != 1) {
299 fprintf(stderr
, "Can't read data from /dev/urandom\n");
305 ed25519_prepare(skey
.seckey
);
306 edsign_sec_to_pub(skey
.seckey
+ 32, skey
.seckey
);
309 sha512_add(&s
, skey
.seckey
, sizeof(skey
.seckey
));
310 memcpy(skey
.checksum
, sha512_final_get(&s
), sizeof(skey
.checksum
));
312 if (b64_encode(&skey
, sizeof(skey
), buf
, sizeof(buf
)) < 0)
315 write_file(seckeyfile
, skey
.fingerprint
, "private key", buf
);
317 memcpy(pkey
.fingerprint
, skey
.fingerprint
, sizeof(pkey
.fingerprint
));
318 memcpy(pkey
.pubkey
, skey
.seckey
+ 32, sizeof(pkey
.pubkey
));
320 if (b64_encode(&pkey
, sizeof(pkey
), buf
, sizeof(buf
)) < 0)
323 write_file(pubkeyfile
, pkey
.fingerprint
, "public key", buf
);
328 static int usage(const char *cmd
)
331 "Usage: %s <command> <options>\n"
333 " -V: verify (needs at least -m and -p|-P)\n"
334 " -S: sign (needs at least -m and -s)\n"
335 " -F: print key fingerprint of public/secret key or signature\n"
336 " -G: generate a new keypair (needs at least -p and -s)\n"
338 " -c <comment>: add comment to keys\n"
339 " -m <file>: message file\n"
340 " -p <file>: public key file (verify/fingerprint only)\n"
341 " -P <path>: public key directory (verify only)\n"
342 " -q: quiet (do not print verification result, use return code only)\n"
343 " -s <file>: secret key file (sign/fingerprint only)\n"
344 " -x <file>: signature file (defaults to <message file>.sig)\n"
350 static void set_cmd(const char *prog
, int val
)
358 int main(int argc
, char **argv
)
360 const char *msgfile
= NULL
;
363 while ((ch
= getopt(argc
, argv
, "FGSVc:m:P:p:qs:x:")) != -1) {
366 set_cmd(argv
[0], CMD_VERIFY
);
369 set_cmd(argv
[0], CMD_SIGN
);
372 set_cmd(argv
[0], CMD_FINGERPRINT
);
375 set_cmd(argv
[0], CMD_GENERATE
);
399 return usage(argv
[0]);
403 if (!sigfile
&& msgfile
) {
404 char *buf
= alloca(strlen(msgfile
) + 5);
406 if (!strcmp(msgfile
, "-")) {
407 fprintf(stderr
, "Need signature file when reading message from stdin\n");
411 sprintf(buf
, "%s.sig", msgfile
);
417 if ((!pubkeyfile
&& !pubkeydir
) || !msgfile
)
418 return usage(argv
[0]);
419 return verify(msgfile
);
421 if (!seckeyfile
|| !msgfile
|| !sigfile
)
422 return usage(argv
[0]);
423 return sign(msgfile
);
424 case CMD_FINGERPRINT
:
425 if (!!seckeyfile
+ !!pubkeyfile
+ !!sigfile
!= 1) {
426 fprintf(stderr
, "Need one secret/public key or signature\n");
427 return usage(argv
[0]);
429 return fingerprint();
431 if (!seckeyfile
|| !pubkeyfile
)
432 return usage(argv
[0]);
435 return usage(argv
[0]);