1 /* $OpenBSD: signify.c,v 1.135 2020/01/21 12:13:21 tb Exp $ */
2 /*
3 * Copyright (c) 2013 Ted Unangst <tedu@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17 #include <sys/stat.h>
18
19 #include <netinet/in.h>
20 #include <resolv.h>
21
22 #include <limits.h>
23 #include <stdint.h>
24 #include <fcntl.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stddef.h>
29 #include <ohash.h>
30 #include <err.h>
31 #include <unistd.h>
32 #include <readpassphrase.h>
33 #include <util.h>
34 #include <sha2.h>
35
36 #include "crypto_api.h"
37 #include "signify.h"
38
39 #define SIGBYTES crypto_sign_ed25519_BYTES
40 #define SECRETBYTES crypto_sign_ed25519_SECRETKEYBYTES
41 #define PUBLICBYTES crypto_sign_ed25519_PUBLICKEYBYTES
42
43 #define PKALG "Ed"
44 #define KDFALG "BK"
45 #define KEYNUMLEN 8
46
47 #define COMMENTHDR "untrusted comment: "
48 #define COMMENTHDRLEN 19
49 #define COMMENTMAXLEN 1024
50 #define VERIFYWITH "verify with "
51
52 struct enckey {
53 uint8_t pkalg[2];
54 uint8_t kdfalg[2];
55 uint32_t kdfrounds;
56 uint8_t salt[16];
57 uint8_t checksum[8];
58 uint8_t keynum[KEYNUMLEN];
59 uint8_t seckey[SECRETBYTES];
60 };
61
62 struct pubkey {
63 uint8_t pkalg[2];
64 uint8_t keynum[KEYNUMLEN];
65 uint8_t pubkey[PUBLICBYTES];
66 };
67
68 struct sig {
69 uint8_t pkalg[2];
70 uint8_t keynum[KEYNUMLEN];
71 uint8_t sig[SIGBYTES];
72 };
73
74 extern char *__progname;
75
76 static void __dead
usage(const char * error)77 usage(const char *error)
78 {
79 if (error)
80 fprintf(stderr, "%s\n", error);
81 fprintf(stderr, "usage:"
82 #ifndef VERIFYONLY
83 "\t%1$s -C [-q] [-p pubkey] [-t keytype] -x sigfile [file ...]\n"
84 "\t%1$s -G [-n] [-c comment] -p pubkey -s seckey\n"
85 "\t%1$s -S [-enz] [-x sigfile] -s seckey -m message\n"
86 #endif
87 "\t%1$s -V [-eqz] [-p pubkey] [-t keytype] [-x sigfile] -m message\n",
88 __progname);
89 exit(1);
90 }
91
92 int
xopen(const char * fname,int oflags,mode_t mode)93 xopen(const char *fname, int oflags, mode_t mode)
94 {
95 struct stat sb;
96 int fd;
97
98 if (strcmp(fname, "-") == 0) {
99 if ((oflags & O_WRONLY))
100 fd = dup(STDOUT_FILENO);
101 else
102 fd = dup(STDIN_FILENO);
103 if (fd == -1)
104 err(1, "dup failed");
105 } else {
106 fd = open(fname, oflags, mode);
107 if (fd == -1)
108 err(1, "can't open %s for %s", fname,
109 (oflags & O_WRONLY) ? "writing" : "reading");
110 }
111 if (fstat(fd, &sb) == -1 || S_ISDIR(sb.st_mode))
112 errx(1, "not a valid file: %s", fname);
113 return fd;
114 }
115
116 void *
xmalloc(size_t len)117 xmalloc(size_t len)
118 {
119 void *p;
120
121 if (!(p = malloc(len)))
122 err(1, "malloc %zu", len);
123 return p;
124 }
125
126 static size_t
parseb64file(const char * filename,char * b64,void * buf,size_t buflen,char * comment)127 parseb64file(const char *filename, char *b64, void *buf, size_t buflen,
128 char *comment)
129 {
130 char *commentend, *b64end;
131
132 commentend = strchr(b64, '\n');
133 if (!commentend || commentend - b64 <= COMMENTHDRLEN ||
134 memcmp(b64, COMMENTHDR, COMMENTHDRLEN) != 0)
135 errx(1, "invalid comment in %s; must start with '%s'",
136 filename, COMMENTHDR);
137 *commentend = '\0';
138 if (comment) {
139 if (strlcpy(comment, b64 + COMMENTHDRLEN,
140 COMMENTMAXLEN) >= COMMENTMAXLEN)
141 errx(1, "comment too long");
142 }
143 if (!(b64end = strchr(commentend + 1, '\n')))
144 errx(1, "missing new line after base64 in %s", filename);
145 *b64end = '\0';
146 if (b64_pton(commentend + 1, buf, buflen) != buflen)
147 errx(1, "unable to parse %s", filename);
148 if (memcmp(buf, PKALG, 2) != 0)
149 errx(1, "unsupported file %s", filename);
150 *commentend = '\n';
151 *b64end = '\n';
152 return b64end - b64 + 1;
153 }
154
155 static void
readb64file(const char * filename,void * buf,size_t buflen,char * comment)156 readb64file(const char *filename, void *buf, size_t buflen, char *comment)
157 {
158 char b64[2048];
159 int rv, fd;
160
161 fd = xopen(filename, O_RDONLY | O_NOFOLLOW, 0);
162 if ((rv = read(fd, b64, sizeof(b64) - 1)) == -1)
163 err(1, "read from %s", filename);
164 b64[rv] = '\0';
165 parseb64file(filename, b64, buf, buflen, comment);
166 explicit_bzero(b64, sizeof(b64));
167 close(fd);
168 }
169
170 static uint8_t *
readmsg(const char * filename,unsigned long long * msglenp)171 readmsg(const char *filename, unsigned long long *msglenp)
172 {
173 unsigned long long msglen = 0;
174 uint8_t *msg = NULL;
175 struct stat sb;
176 ssize_t x, space;
177 int fd;
178 const unsigned long long maxmsgsize = 1UL << 30;
179
180 fd = xopen(filename, O_RDONLY | O_NOFOLLOW, 0);
181 if (fstat(fd, &sb) == 0 && S_ISREG(sb.st_mode)) {
182 if (sb.st_size > maxmsgsize)
183 errx(1, "msg too large in %s", filename);
184 space = sb.st_size + 1;
185 } else {
186 space = 64 * 1024 - 1;
187 }
188
189 msg = xmalloc(space + 1);
190 while (1) {
191 if (space == 0) {
192 if (msglen * 2 > maxmsgsize)
193 errx(1, "msg too large in %s", filename);
194 space = msglen;
195 if (!(msg = realloc(msg, msglen + space + 1)))
196 err(1, "realloc");
197 }
198 if ((x = read(fd, msg + msglen, space)) == -1)
199 err(1, "read from %s", filename);
200 if (x == 0)
201 break;
202 space -= x;
203 msglen += x;
204 }
205
206 msg[msglen] = '\0';
207 close(fd);
208
209 *msglenp = msglen;
210 return msg;
211 }
212
213 void
writeall(int fd,const void * buf,size_t buflen,const char * filename)214 writeall(int fd, const void *buf, size_t buflen, const char *filename)
215 {
216 ssize_t x;
217
218 while (buflen != 0) {
219 if ((x = write(fd, buf, buflen)) == -1)
220 err(1, "write to %s", filename);
221 buflen -= x;
222 buf = (char *)buf + x;
223 }
224 }
225
226 #ifndef VERIFYONLY
227 static char *
createheader(const char * comment,const void * buf,size_t buflen)228 createheader(const char *comment, const void *buf, size_t buflen)
229 {
230 char *header;
231 char b64[1024];
232
233 if (b64_ntop(buf, buflen, b64, sizeof(b64)) == -1)
234 errx(1, "base64 encode failed");
235 if (asprintf(&header, "%s%s\n%s\n", COMMENTHDR, comment, b64) == -1)
236 err(1, "asprintf failed");
237 explicit_bzero(b64, sizeof(b64));
238 return header;
239 }
240
241 static void
writekeyfile(const char * filename,const char * comment,const void * buf,size_t buflen,int oflags,mode_t mode)242 writekeyfile(const char *filename, const char *comment, const void *buf,
243 size_t buflen, int oflags, mode_t mode)
244 {
245 char *header;
246 int fd;
247
248 fd = xopen(filename, O_CREAT|oflags|O_NOFOLLOW|O_WRONLY, mode);
249 header = createheader(comment, buf, buflen);
250 writeall(fd, header, strlen(header), filename);
251 freezero(header, strlen(header));
252 close(fd);
253 }
254
255 static void
kdf(uint8_t * salt,size_t saltlen,int rounds,int allowstdin,int confirm,uint8_t * key,size_t keylen)256 kdf(uint8_t *salt, size_t saltlen, int rounds, int allowstdin, int confirm,
257 uint8_t *key, size_t keylen)
258 {
259 char pass[1024];
260 int rppflags = RPP_ECHO_OFF;
261 const char *errstr = NULL;
262
263 if (rounds == 0) {
264 memset(key, 0, keylen);
265 return;
266 }
267
268 if (allowstdin && !isatty(STDIN_FILENO))
269 rppflags |= RPP_STDIN;
270 if (!readpassphrase("passphrase: ", pass, sizeof(pass), rppflags))
271 errx(1, "unable to read passphrase");
272 if (strlen(pass) == 0)
273 errx(1, "please provide a password");
274 if (confirm && !(rppflags & RPP_STDIN)) {
275 char pass2[1024];
276 if (!readpassphrase("confirm passphrase: ", pass2,
277 sizeof(pass2), rppflags))
278 errstr = "unable to read passphrase";
279 if (!errstr && strcmp(pass, pass2) != 0)
280 errstr = "passwords don't match";
281 explicit_bzero(pass2, sizeof(pass2));
282 }
283 if (!errstr && bcrypt_pbkdf(pass, strlen(pass), salt, saltlen, key,
284 keylen, rounds) == -1)
285 errstr = "bcrypt pbkdf";
286 explicit_bzero(pass, sizeof(pass));
287 if (errstr)
288 errx(1, "%s", errstr);
289 }
290
291 static void
signmsg(uint8_t * seckey,uint8_t * msg,unsigned long long msglen,uint8_t * sig)292 signmsg(uint8_t *seckey, uint8_t *msg, unsigned long long msglen,
293 uint8_t *sig)
294 {
295 unsigned long long siglen;
296 uint8_t *sigbuf;
297
298 sigbuf = xmalloc(msglen + SIGBYTES);
299 crypto_sign_ed25519(sigbuf, &siglen, msg, msglen, seckey);
300 memcpy(sig, sigbuf, SIGBYTES);
301 free(sigbuf);
302 }
303
304 static void
generate(const char * pubkeyfile,const char * seckeyfile,int rounds,const char * comment)305 generate(const char *pubkeyfile, const char *seckeyfile, int rounds,
306 const char *comment)
307 {
308 uint8_t digest[SHA512_DIGEST_LENGTH];
309 struct pubkey pubkey;
310 struct enckey enckey;
311 uint8_t xorkey[sizeof(enckey.seckey)];
312 uint8_t keynum[KEYNUMLEN];
313 char commentbuf[COMMENTMAXLEN];
314 SHA2_CTX ctx;
315 int i, nr;
316
317 crypto_sign_ed25519_keypair(pubkey.pubkey, enckey.seckey);
318 arc4random_buf(keynum, sizeof(keynum));
319
320 SHA512Init(&ctx);
321 SHA512Update(&ctx, enckey.seckey, sizeof(enckey.seckey));
322 SHA512Final(digest, &ctx);
323
324 memcpy(enckey.pkalg, PKALG, 2);
325 memcpy(enckey.kdfalg, KDFALG, 2);
326 enckey.kdfrounds = htonl(rounds);
327 memcpy(enckey.keynum, keynum, KEYNUMLEN);
328 arc4random_buf(enckey.salt, sizeof(enckey.salt));
329 kdf(enckey.salt, sizeof(enckey.salt), rounds, 1, 1, xorkey, sizeof(xorkey));
330 memcpy(enckey.checksum, digest, sizeof(enckey.checksum));
331 for (i = 0; i < sizeof(enckey.seckey); i++)
332 enckey.seckey[i] ^= xorkey[i];
333 explicit_bzero(digest, sizeof(digest));
334 explicit_bzero(xorkey, sizeof(xorkey));
335
336 nr = snprintf(commentbuf, sizeof(commentbuf), "%s secret key", comment);
337 if (nr < 0 || nr >= sizeof(commentbuf))
338 errx(1, "comment too long");
339 writekeyfile(seckeyfile, commentbuf, &enckey,
340 sizeof(enckey), O_EXCL, 0600);
341 explicit_bzero(&enckey, sizeof(enckey));
342
343 memcpy(pubkey.pkalg, PKALG, 2);
344 memcpy(pubkey.keynum, keynum, KEYNUMLEN);
345 nr = snprintf(commentbuf, sizeof(commentbuf), "%s public key", comment);
346 if (nr < 0 || nr >= sizeof(commentbuf))
347 errx(1, "comment too long");
348 writekeyfile(pubkeyfile, commentbuf, &pubkey,
349 sizeof(pubkey), O_EXCL, 0666);
350 }
351
352 static void
check_keyname_compliance(const char * pubkeyfile,const char * seckeyfile)353 check_keyname_compliance(const char *pubkeyfile, const char *seckeyfile)
354 {
355 const char *pos;
356 size_t len;
357
358 /* basename may or may not modify input */
359 pos = strrchr(seckeyfile, '/');
360 if (pos != NULL)
361 seckeyfile = pos + 1;
362
363 len = strlen(seckeyfile);
364 if (len < 5) /* ?.key */
365 goto bad;
366 if (strcmp(pubkeyfile + len - 4, ".pub") != 0 ||
367 strcmp(seckeyfile + len - 4, ".sec") != 0)
368 goto bad;
369 if (strncmp(pubkeyfile, seckeyfile, len - 4) != 0)
370 goto bad;
371 if (pubkeyfile != NULL) {
372 pos = strrchr(pubkeyfile, '/');
373 if (pos != NULL)
374 pubkeyfile = pos + 1;
375
376 if (strlen(pubkeyfile) != len)
377 goto bad;
378 if (strcmp(pubkeyfile + len - 4, ".pub") != 0)
379 goto bad;
380 if (strncmp(pubkeyfile, seckeyfile, len - 4) != 0)
381 goto bad;
382 }
383
384 return;
385 bad:
386 errx(1, "please use naming scheme of keyname.pub and keyname.sec");
387 }
388
389 uint8_t *
createsig(const char * seckeyfile,const char * msgfile,uint8_t * msg,unsigned long long msglen)390 createsig(const char *seckeyfile, const char *msgfile, uint8_t *msg,
391 unsigned long long msglen)
392 {
393 struct enckey enckey;
394 uint8_t xorkey[sizeof(enckey.seckey)];
395 struct sig sig;
396 char *sighdr;
397 char *extname;
398 uint8_t digest[SHA512_DIGEST_LENGTH];
399 int i, nr, rounds;
400 SHA2_CTX ctx;
401 char comment[COMMENTMAXLEN], sigcomment[COMMENTMAXLEN];
402
403 readb64file(seckeyfile, &enckey, sizeof(enckey), comment);
404
405 extname = strrchr(seckeyfile, '.');
406 if (extname && strcmp(extname, ".sec") == 0) {
407 const char *keyname;
408 /* basename may or may not modify input */
409 if (!(keyname = strrchr(seckeyfile, '/')))
410 keyname = seckeyfile;
411 else
412 keyname++;
413 nr = snprintf(sigcomment, sizeof(sigcomment),
414 VERIFYWITH "%.*s.pub", (int)strlen(keyname) - 4, keyname);
415 if (nr == -1 || nr >= sizeof(sigcomment))
416 errx(1, "comment too long");
417 } else {
418 nr = snprintf(sigcomment, sizeof(sigcomment),
419 "signature from %s", comment);
420 if (nr == -1 || nr >= sizeof(sigcomment))
421 errx(1, "comment too long");
422 }
423
424 if (memcmp(enckey.kdfalg, KDFALG, 2) != 0)
425 errx(1, "unsupported KDF");
426 rounds = ntohl(enckey.kdfrounds);
427 kdf(enckey.salt, sizeof(enckey.salt), rounds, strcmp(msgfile, "-") != 0,
428 0, xorkey, sizeof(xorkey));
429 for (i = 0; i < sizeof(enckey.seckey); i++)
430 enckey.seckey[i] ^= xorkey[i];
431 explicit_bzero(xorkey, sizeof(xorkey));
432 SHA512Init(&ctx);
433 SHA512Update(&ctx, enckey.seckey, sizeof(enckey.seckey));
434 SHA512Final(digest, &ctx);
435 if (memcmp(enckey.checksum, digest, sizeof(enckey.checksum)) != 0)
436 errx(1, "incorrect passphrase");
437 explicit_bzero(digest, sizeof(digest));
438
439 signmsg(enckey.seckey, msg, msglen, sig.sig);
440 memcpy(sig.keynum, enckey.keynum, KEYNUMLEN);
441 explicit_bzero(&enckey, sizeof(enckey));
442
443 memcpy(sig.pkalg, PKALG, 2);
444
445 sighdr = createheader(sigcomment, &sig, sizeof(sig));
446 return sighdr;
447 }
448
449 static void
sign(const char * seckeyfile,const char * msgfile,const char * sigfile,int embedded)450 sign(const char *seckeyfile, const char *msgfile, const char *sigfile,
451 int embedded)
452 {
453 uint8_t *msg;
454 char *sighdr;
455 int fd;
456 unsigned long long msglen;
457
458 msg = readmsg(msgfile, &msglen);
459
460 sighdr = createsig(seckeyfile, msgfile, msg, msglen);
461
462 fd = xopen(sigfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666);
463 writeall(fd, sighdr, strlen(sighdr), sigfile);
464 free(sighdr);
465 if (embedded)
466 writeall(fd, msg, msglen, sigfile);
467 close(fd);
468
469 free(msg);
470 }
471 #endif
472
473 static void
verifymsg(struct pubkey * pubkey,uint8_t * msg,unsigned long long msglen,struct sig * sig,int quiet)474 verifymsg(struct pubkey *pubkey, uint8_t *msg, unsigned long long msglen,
475 struct sig *sig, int quiet)
476 {
477 uint8_t *sigbuf, *dummybuf;
478 unsigned long long siglen, dummylen;
479
480 if (memcmp(pubkey->keynum, sig->keynum, KEYNUMLEN) != 0)
481 errx(1, "verification failed: checked against wrong key");
482
483 siglen = SIGBYTES + msglen;
484 sigbuf = xmalloc(siglen);
485 dummybuf = xmalloc(siglen);
486 memcpy(sigbuf, sig->sig, SIGBYTES);
487 memcpy(sigbuf + SIGBYTES, msg, msglen);
488 if (crypto_sign_ed25519_open(dummybuf, &dummylen, sigbuf, siglen,
489 pubkey->pubkey) == -1)
490 errx(1, "signature verification failed");
491 if (!quiet)
492 printf("Signature Verified\n");
493 free(sigbuf);
494 free(dummybuf);
495 }
496
497 static void
check_keytype(const char * pubkeyfile,const char * keytype)498 check_keytype(const char *pubkeyfile, const char *keytype)
499 {
500 const char *p;
501 size_t typelen;
502
503 if (!(p = strrchr(pubkeyfile, '-')))
504 goto bad;
505 p++;
506 typelen = strlen(keytype);
507 if (strncmp(p, keytype, typelen) != 0)
508 goto bad;
509 if (strcmp(p + typelen, ".pub") != 0)
510 goto bad;
511 return;
512
513 bad:
514 errx(1, "incorrect keytype: %s is not %s", pubkeyfile, keytype);
515 }
516
517 static void
readpubkey(const char * pubkeyfile,struct pubkey * pubkey,const char * sigcomment,const char * keytype)518 readpubkey(const char *pubkeyfile, struct pubkey *pubkey,
519 const char *sigcomment, const char *keytype)
520 {
521 const char *safepath = "/usr/local/etc/signify";
522 char keypath[PATH_MAX];
523
524 if (!pubkeyfile) {
525 pubkeyfile = strstr(sigcomment, VERIFYWITH);
526 if (pubkeyfile && strchr(pubkeyfile, '/') == NULL) {
527 pubkeyfile += strlen(VERIFYWITH);
528 if (keytype)
529 check_keytype(pubkeyfile, keytype);
530 if (snprintf(keypath, sizeof(keypath), "%s/%s",
531 safepath, pubkeyfile) >= sizeof(keypath))
532 errx(1, "name too long %s", pubkeyfile);
533 pubkeyfile = keypath;
534 } else
535 usage("must specify pubkey");
536 }
537 readb64file(pubkeyfile, pubkey, sizeof(*pubkey), NULL);
538 }
539
540 static void
verifysimple(const char * pubkeyfile,const char * msgfile,const char * sigfile,int quiet,const char * keytype)541 verifysimple(const char *pubkeyfile, const char *msgfile, const char *sigfile,
542 int quiet, const char *keytype)
543 {
544 char sigcomment[COMMENTMAXLEN];
545 struct sig sig;
546 struct pubkey pubkey;
547 unsigned long long msglen;
548 uint8_t *msg;
549
550 msg = readmsg(msgfile, &msglen);
551
552 readb64file(sigfile, &sig, sizeof(sig), sigcomment);
553 readpubkey(pubkeyfile, &pubkey, sigcomment, keytype);
554
555 verifymsg(&pubkey, msg, msglen, &sig, quiet);
556
557 free(msg);
558 }
559
560 static uint8_t *
verifyembedded(const char * pubkeyfile,const char * sigfile,int quiet,unsigned long long * msglenp,const char * keytype)561 verifyembedded(const char *pubkeyfile, const char *sigfile,
562 int quiet, unsigned long long *msglenp, const char *keytype)
563 {
564 char sigcomment[COMMENTMAXLEN];
565 struct sig sig;
566 struct pubkey pubkey;
567 unsigned long long msglen, siglen;
568 uint8_t *msg;
569
570 msg = readmsg(sigfile, &msglen);
571
572 siglen = parseb64file(sigfile, msg, &sig, sizeof(sig), sigcomment);
573 readpubkey(pubkeyfile, &pubkey, sigcomment, keytype);
574
575 msglen -= siglen;
576 memmove(msg, msg + siglen, msglen);
577 msg[msglen] = 0;
578
579 verifymsg(&pubkey, msg, msglen, &sig, quiet);
580
581 *msglenp = msglen;
582 return msg;
583 }
584
585 static void
verify(const char * pubkeyfile,const char * msgfile,const char * sigfile,int embedded,int quiet,const char * keytype)586 verify(const char *pubkeyfile, const char *msgfile, const char *sigfile,
587 int embedded, int quiet, const char *keytype)
588 {
589 unsigned long long msglen;
590 uint8_t *msg;
591 int fd;
592
593 if (embedded) {
594 msg = verifyembedded(pubkeyfile, sigfile, quiet, &msglen,
595 keytype);
596 fd = xopen(msgfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666);
597 writeall(fd, msg, msglen, msgfile);
598 free(msg);
599 close(fd);
600 } else {
601 verifysimple(pubkeyfile, msgfile, sigfile, quiet, keytype);
602 }
603 }
604
605 #ifndef VERIFYONLY
606 #define HASHBUFSIZE 224
607 struct checksum {
608 char file[PATH_MAX];
609 char hash[HASHBUFSIZE];
610 char algo[32];
611 };
612
613 static void *
ecalloc(size_t s1,size_t s2,void * data)614 ecalloc(size_t s1, size_t s2, void *data)
615 {
616 void *p;
617
618 if (!(p = calloc(s1, s2)))
619 err(1, "calloc");
620 return p;
621 }
622
623 static void
efree(void * p,void * data)624 efree(void *p, void *data)
625 {
626 free(p);
627 }
628
629 static void
recodehash(char * hash,size_t len)630 recodehash(char *hash, size_t len)
631 {
632 uint8_t data[HASHBUFSIZE / 2];
633 int i, rv;
634
635 if (strlen(hash) == len)
636 return;
637 if ((rv = b64_pton(hash, data, sizeof(data))) == -1)
638 errx(1, "invalid base64 encoding");
639 for (i = 0; i < rv; i++)
640 snprintf(hash + i * 2, HASHBUFSIZE - i * 2, "%2.2x", data[i]);
641 }
642
643 static int
verifychecksum(struct checksum * c,int quiet)644 verifychecksum(struct checksum *c, int quiet)
645 {
646 char buf[HASHBUFSIZE];
647
648 if (strcmp(c->algo, "SHA256") == 0) {
649 recodehash(c->hash, SHA256_DIGEST_STRING_LENGTH-1);
650 if (!SHA256File(c->file, buf))
651 return 0;
652 } else if (strcmp(c->algo, "SHA512") == 0) {
653 recodehash(c->hash, SHA512_DIGEST_STRING_LENGTH-1);
654 if (!SHA512File(c->file, buf))
655 return 0;
656 } else {
657 errx(1, "can't handle algorithm %s", c->algo);
658 }
659 if (strcmp(c->hash, buf) != 0)
660 return 0;
661 if (!quiet)
662 printf("%s: OK\n", c->file);
663 return 1;
664 }
665
666 static void
verifychecksums(char * msg,int argc,char ** argv,int quiet)667 verifychecksums(char *msg, int argc, char **argv, int quiet)
668 {
669 struct ohash_info info = { 0, NULL, ecalloc, efree, NULL };
670 struct ohash myh;
671 struct checksum c;
672 char *e, *line, *endline;
673 int hasfailed = 0;
674 int i, rv;
675 unsigned int slot;
676
677 ohash_init(&myh, 6, &info);
678 if (argc) {
679 for (i = 0; i < argc; i++) {
680 slot = ohash_qlookup(&myh, argv[i]);
681 e = ohash_find(&myh, slot);
682 if (e == NULL)
683 ohash_insert(&myh, slot, argv[i]);
684 }
685 }
686
687 line = msg;
688 while (line && *line) {
689 if ((endline = strchr(line, '\n')))
690 *endline++ = '\0';
691 #if PATH_MAX < 1024 || HASHBUFSIZE < 224
692 #error sizes are wrong
693 #endif
694 rv = sscanf(line, "%31s (%1023[^)]) = %223s",
695 c.algo, c.file, c.hash);
696 if (rv != 3)
697 errx(1, "unable to parse checksum line %s", line);
698 line = endline;
699 if (argc) {
700 slot = ohash_qlookup(&myh, c.file);
701 e = ohash_find(&myh, slot);
702 if (e != NULL) {
703 if (verifychecksum(&c, quiet) != 0)
704 ohash_remove(&myh, slot);
705 }
706 } else {
707 if (verifychecksum(&c, quiet) == 0) {
708 slot = ohash_qlookup(&myh, c.file);
709 e = ohash_find(&myh, slot);
710 if (e == NULL) {
711 if (!(e = strdup(c.file)))
712 err(1, "strdup");
713 ohash_insert(&myh, slot, e);
714 }
715 }
716 }
717 }
718
719 for (e = ohash_first(&myh, &slot); e != NULL; e = ohash_next(&myh, &slot)) {
720 fprintf(stderr, "%s: FAIL\n", e);
721 hasfailed = 1;
722 if (argc == 0)
723 free(e);
724 }
725 ohash_delete(&myh);
726 if (hasfailed)
727 exit(1);
728 }
729
730 static void
check(const char * pubkeyfile,const char * sigfile,const char * keytype,int quiet,int argc,char ** argv)731 check(const char *pubkeyfile, const char *sigfile, const char *keytype,
732 int quiet, int argc, char **argv)
733 {
734 unsigned long long msglen;
735 uint8_t *msg;
736
737 msg = verifyembedded(pubkeyfile, sigfile, quiet, &msglen, keytype);
738 verifychecksums((char *)msg, argc, argv, quiet);
739
740 free(msg);
741 }
742
743 void *
verifyzdata(uint8_t * zdata,unsigned long long zdatalen,const char * filename,const char * pubkeyfile,const char * keytype)744 verifyzdata(uint8_t *zdata, unsigned long long zdatalen,
745 const char *filename, const char *pubkeyfile, const char *keytype)
746 {
747 struct sig sig;
748 char sigcomment[COMMENTMAXLEN];
749 unsigned long long siglen;
750 struct pubkey pubkey;
751
752 if (zdatalen < sizeof(sig))
753 errx(1, "signature too short in %s", filename);
754 siglen = parseb64file(filename, zdata, &sig, sizeof(sig),
755 sigcomment);
756 readpubkey(pubkeyfile, &pubkey, sigcomment, keytype);
757 zdata += siglen;
758 zdatalen -= siglen;
759 verifymsg(&pubkey, zdata, zdatalen, &sig, 1);
760 return zdata;
761 }
762 #endif
763
764 int
main(int argc,char ** argv)765 main(int argc, char **argv)
766 {
767 const char *pubkeyfile = NULL, *seckeyfile = NULL, *msgfile = NULL,
768 *sigfile = NULL;
769 char sigfilebuf[PATH_MAX];
770 const char *comment = "signify";
771 char *keytype = NULL;
772 int ch;
773 int none = 0;
774 int embedded = 0;
775 int quiet = 0;
776 int gzip = 0;
777 enum {
778 NONE,
779 CHECK,
780 GENERATE,
781 SIGN,
782 VERIFY
783 } verb = NONE;
784
785 if (pledge("stdio rpath wpath cpath tty", NULL) == -1)
786 err(1, "pledge");
787
788 while ((ch = getopt(argc, argv, "CGSVzc:em:np:qs:t:x:")) != -1) {
789 switch (ch) {
790 #ifndef VERIFYONLY
791 case 'C':
792 if (verb)
793 usage(NULL);
794 verb = CHECK;
795 break;
796 case 'G':
797 if (verb)
798 usage(NULL);
799 verb = GENERATE;
800 break;
801 case 'S':
802 if (verb)
803 usage(NULL);
804 verb = SIGN;
805 break;
806 case 'z':
807 gzip = 1;
808 break;
809 #endif
810 case 'V':
811 if (verb)
812 usage(NULL);
813 verb = VERIFY;
814 break;
815 case 'c':
816 comment = optarg;
817 break;
818 case 'e':
819 embedded = 1;
820 break;
821 case 'm':
822 msgfile = optarg;
823 break;
824 case 'n':
825 none = 1;
826 break;
827 case 'p':
828 pubkeyfile = optarg;
829 break;
830 case 'q':
831 quiet = 1;
832 break;
833 case 's':
834 seckeyfile = optarg;
835 break;
836 case 't':
837 keytype = optarg;
838 break;
839 case 'x':
840 sigfile = optarg;
841 break;
842 default:
843 usage(NULL);
844 break;
845 }
846 }
847 argc -= optind;
848 argv += optind;
849
850 if (embedded && gzip)
851 errx(1, "can't combine -e and -z options");
852
853 if (setvbuf(stdout, NULL, _IOLBF, 0) != 0)
854 err(1, "setvbuf");
855
856 #ifndef VERIFYONLY
857 if (verb == CHECK) {
858 if (pledge("stdio rpath", NULL) == -1)
859 err(1, "pledge");
860 if (!sigfile)
861 usage("must specify sigfile");
862 check(pubkeyfile, sigfile, keytype, quiet, argc, argv);
863 return 0;
864 }
865 #endif
866
867 if (argc != 0)
868 usage(NULL);
869
870 if (!sigfile && msgfile) {
871 int nr;
872 if (strcmp(msgfile, "-") == 0)
873 usage("must specify sigfile with - message");
874 nr = snprintf(sigfilebuf, sizeof(sigfilebuf),
875 "%s.sig", msgfile);
876 if (nr < 0 || nr >= sizeof(sigfilebuf))
877 errx(1, "path too long");
878 sigfile = sigfilebuf;
879 }
880
881 switch (verb) {
882 #ifndef VERIFYONLY
883 case GENERATE:
884 /* no pledge */
885 if (!pubkeyfile || !seckeyfile)
886 usage("must specify pubkey and seckey");
887 check_keyname_compliance(pubkeyfile, seckeyfile);
888 generate(pubkeyfile, seckeyfile, none ? 0 : 42, comment);
889 break;
890 case SIGN:
891 /* no pledge */
892 if (gzip) {
893 if (!msgfile || !seckeyfile || !sigfile)
894 usage("must specify message sigfile seckey");
895 zsign(seckeyfile, msgfile, sigfile, none);
896 } else {
897 if (!msgfile || !seckeyfile)
898 usage("must specify message and seckey");
899 sign(seckeyfile, msgfile, sigfile, embedded);
900 }
901 break;
902 #endif
903 case VERIFY:
904 if ((embedded || gzip) &&
905 (msgfile && strcmp(msgfile, "-") != 0)) {
906 /* will need to create output file */
907 if (pledge("stdio rpath wpath cpath", NULL) == -1)
908 err(1, "pledge");
909 } else {
910 if (pledge("stdio rpath", NULL) == -1)
911 err(1, "pledge");
912 }
913 if (gzip) {
914 zverify(pubkeyfile, msgfile, sigfile, keytype);
915 } else {
916 if (!msgfile)
917 usage("must specify message");
918 verify(pubkeyfile, msgfile, sigfile, embedded,
919 quiet, keytype);
920 }
921 break;
922 default:
923 if (pledge("stdio", NULL) == -1)
924 err(1, "pledge");
925 usage(NULL);
926 break;
927 }
928
929 return 0;
930 }
931