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