Annotation of src/usr.bin/signify/signify.c, Revision 1.89
1.89 ! tedu 1: /* $OpenBSD: signify.c,v 1.88 2014/05/16 18:35:01 tedu Exp $ */
1.1 tedu 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 <stdint.h>
23: #include <fcntl.h>
24: #include <string.h>
25: #include <stdio.h>
1.34 tedu 26: #include <stdlib.h>
1.1 tedu 27: #include <err.h>
28: #include <unistd.h>
29: #include <readpassphrase.h>
30: #include <util.h>
31: #include <sha2.h>
32:
33: #include "crypto_api.h"
1.85 espie 34: #ifndef VERIFY_ONLY
35: #include <stdint.h>
36: #include <stddef.h>
37: #include <ohash.h>
38: #endif
1.1 tedu 39:
40: #define SIGBYTES crypto_sign_ed25519_BYTES
41: #define SECRETBYTES crypto_sign_ed25519_SECRETKEYBYTES
42: #define PUBLICBYTES crypto_sign_ed25519_PUBLICKEYBYTES
43:
44: #define PKALG "Ed"
45: #define KDFALG "BK"
1.13 tedu 46: #define FPLEN 8
47:
1.18 tedu 48: #define COMMENTHDR "untrusted comment: "
49: #define COMMENTHDRLEN 19
50: #define COMMENTMAXLEN 1024
1.51 tedu 51: #define VERIFYWITH "verify with "
1.1 tedu 52:
53: struct enckey {
54: uint8_t pkalg[2];
55: uint8_t kdfalg[2];
56: uint32_t kdfrounds;
57: uint8_t salt[16];
58: uint8_t checksum[8];
1.13 tedu 59: uint8_t fingerprint[FPLEN];
1.1 tedu 60: uint8_t seckey[SECRETBYTES];
61: };
62:
63: struct pubkey {
64: uint8_t pkalg[2];
1.13 tedu 65: uint8_t fingerprint[FPLEN];
1.1 tedu 66: uint8_t pubkey[PUBLICBYTES];
67: };
68:
69: struct sig {
70: uint8_t pkalg[2];
1.13 tedu 71: uint8_t fingerprint[FPLEN];
1.1 tedu 72: uint8_t sig[SIGBYTES];
73: };
74:
75: extern char *__progname;
76:
77: static void
1.31 tedu 78: usage(const char *error)
1.1 tedu 79: {
1.31 tedu 80: if (error)
81: fprintf(stderr, "%s\n", error);
1.9 espie 82: fprintf(stderr, "usage:"
1.15 espie 83: #ifndef VERIFYONLY
1.47 naddy 84: "\t%1$s -C [-q] -p pubkey -x sigfile [file ...]\n"
1.31 tedu 85: "\t%1$s -G [-n] [-c comment] -p pubkey -s seckey\n"
86: "\t%1$s -I [-p pubkey] [-s seckey] [-x sigfile]\n"
87: "\t%1$s -S [-e] [-x sigfile] -s seckey -m message\n"
1.15 espie 88: #endif
1.42 tedu 89: "\t%1$s -V [-eq] [-x sigfile] -p pubkey -m message\n",
1.15 espie 90: __progname);
1.1 tedu 91: exit(1);
92: }
93:
94: static int
1.61 deraadt 95: xopen(const char *fname, int oflags, mode_t mode)
1.1 tedu 96: {
1.53 tedu 97: struct stat sb;
1.1 tedu 98: int fd;
99:
1.31 tedu 100: if (strcmp(fname, "-") == 0) {
1.61 deraadt 101: if ((oflags & O_WRONLY))
1.31 tedu 102: fd = dup(STDOUT_FILENO);
103: else
104: fd = dup(STDIN_FILENO);
105: if (fd == -1)
106: err(1, "dup failed");
107: } else {
1.61 deraadt 108: fd = open(fname, oflags, mode);
1.31 tedu 109: if (fd == -1)
110: err(1, "can't open %s for %s", fname,
1.61 deraadt 111: (oflags & O_WRONLY) ? "writing" : "reading");
1.31 tedu 112: }
1.53 tedu 113: if (fstat(fd, &sb) == -1 || S_ISDIR(sb.st_mode))
1.66 tedu 114: errx(1, "not a valid file: %s", fname);
1.1 tedu 115: return fd;
116: }
117:
118: static void *
119: xmalloc(size_t len)
120: {
121: void *p;
122:
1.89 ! tedu 123: if (!(p = malloc(len)))
1.1 tedu 124: err(1, "malloc %zu", len);
125: return p;
126: }
127:
1.16 tedu 128: static size_t
1.46 tedu 129: parseb64file(const char *filename, char *b64, void *buf, size_t buflen,
1.18 tedu 130: char *comment)
1.16 tedu 131: {
132: char *commentend, *b64end;
133:
134: commentend = strchr(b64, '\n');
135: if (!commentend || commentend - b64 <= COMMENTHDRLEN ||
1.65 tedu 136: memcmp(b64, COMMENTHDR, COMMENTHDRLEN) != 0)
1.16 tedu 137: errx(1, "invalid comment in %s; must start with '%s'",
138: filename, COMMENTHDR);
1.75 tedu 139: *commentend = '\0';
1.40 deraadt 140: if (comment) {
141: if (strlcpy(comment, b64 + COMMENTHDRLEN,
142: COMMENTMAXLEN) >= COMMENTMAXLEN)
1.71 tedu 143: errx(1, "comment too long");
1.40 deraadt 144: }
1.89 ! tedu 145: if (!(b64end = strchr(commentend + 1, '\n')))
1.82 tedu 146: errx(1, "missing new line after base64 in %s", filename);
1.75 tedu 147: *b64end = '\0';
1.77 tedu 148: if (b64_pton(commentend + 1, buf, buflen) != buflen)
1.82 tedu 149: errx(1, "invalid base64 encoding in %s", filename);
1.65 tedu 150: if (memcmp(buf, PKALG, 2) != 0)
1.16 tedu 151: errx(1, "unsupported file %s", filename);
152: return b64end - b64 + 1;
153: }
154:
1.1 tedu 155: static void
1.46 tedu 156: readb64file(const char *filename, void *buf, size_t buflen, char *comment)
1.1 tedu 157: {
158: char b64[2048];
1.10 tedu 159: int rv, fd;
1.1 tedu 160:
161: fd = xopen(filename, O_RDONLY | O_NOFOLLOW, 0);
1.89 ! tedu 162: if ((rv = read(fd, b64, sizeof(b64) - 1)) == -1)
1.7 espie 163: err(1, "read from %s", filename);
1.75 tedu 164: b64[rv] = '\0';
1.46 tedu 165: parseb64file(filename, b64, buf, buflen, comment);
1.41 tedu 166: explicit_bzero(b64, sizeof(b64));
1.1 tedu 167: close(fd);
168: }
169:
1.35 tedu 170: static uint8_t *
1.1 tedu 171: readmsg(const char *filename, unsigned long long *msglenp)
172: {
1.49 tedu 173: unsigned long long msglen = 0;
174: uint8_t *msg = NULL;
1.1 tedu 175: struct stat sb;
1.49 tedu 176: ssize_t x, space;
1.1 tedu 177: int fd;
1.73 tedu 178: const unsigned long long maxmsgsize = 1UL << 30;
1.1 tedu 179:
180: fd = xopen(filename, O_RDONLY | O_NOFOLLOW, 0);
1.49 tedu 181: if (fstat(fd, &sb) == 0 && S_ISREG(sb.st_mode)) {
1.73 tedu 182: if (sb.st_size > maxmsgsize)
1.49 tedu 183: errx(1, "msg too large in %s", filename);
184: space = sb.st_size + 1;
185: } else {
1.67 tedu 186: space = 64 * 1024;
1.49 tedu 187: }
188:
1.67 tedu 189: msg = xmalloc(space + 1);
1.49 tedu 190: while (1) {
191: if (space == 0) {
1.73 tedu 192: if (msglen * 2 > maxmsgsize)
1.67 tedu 193: errx(1, "msg too large in %s", filename);
194: space = msglen;
1.49 tedu 195: if (!(msg = realloc(msg, msglen + space + 1)))
196: errx(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:
1.75 tedu 206: msg[msglen] = '\0';
1.1 tedu 207: close(fd);
208:
209: *msglenp = msglen;
210: return msg;
211: }
212:
213: static void
1.46 tedu 214: writeall(int fd, const void *buf, size_t buflen, const char *filename)
1.1 tedu 215: {
1.11 tedu 216: ssize_t x;
1.38 espie 217:
1.46 tedu 218: while (buflen != 0) {
1.89 ! tedu 219: if ((x = write(fd, buf, buflen)) == -1)
1.38 espie 220: err(1, "write to %s", filename);
1.46 tedu 221: buflen -= x;
1.61 deraadt 222: buf = (char *)buf + x;
1.7 espie 223: }
1.1 tedu 224: }
225:
1.17 deraadt 226: #ifndef VERIFYONLY
1.1 tedu 227: static void
228: writeb64file(const char *filename, const char *comment, const void *buf,
1.61 deraadt 229: size_t buflen, const void *msg, size_t msglen, int oflags, mode_t mode)
1.1 tedu 230: {
231: char header[1024];
232: char b64[1024];
233: int fd, rv;
234:
1.61 deraadt 235: fd = xopen(filename, O_CREAT|oflags|O_NOFOLLOW|O_WRONLY, mode);
1.40 deraadt 236: if (snprintf(header, sizeof(header), "%s%s\n",
237: COMMENTHDR, comment) >= sizeof(header))
1.71 tedu 238: errx(1, "comment too long");
1.7 espie 239: writeall(fd, header, strlen(header), filename);
1.46 tedu 240: if ((rv = b64_ntop(buf, buflen, b64, sizeof(b64)-1)) == -1)
1.82 tedu 241: errx(1, "base64 encode failed");
1.8 espie 242: b64[rv++] = '\n';
1.7 espie 243: writeall(fd, b64, rv, filename);
1.41 tedu 244: explicit_bzero(b64, sizeof(b64));
1.58 tedu 245: if (msg)
246: writeall(fd, msg, msglen, filename);
1.1 tedu 247: close(fd);
248: }
249:
250: static void
1.70 tedu 251: kdf(uint8_t *salt, size_t saltlen, int rounds, int allowstdin, int confirm,
1.50 tedu 252: uint8_t *key, size_t keylen)
1.1 tedu 253: {
254: char pass[1024];
1.48 tedu 255: int rppflags = RPP_ECHO_OFF;
1.1 tedu 256:
257: if (rounds == 0) {
258: memset(key, 0, keylen);
259: return;
260: }
261:
1.50 tedu 262: if (allowstdin && !isatty(STDIN_FILENO))
1.48 tedu 263: rppflags |= RPP_STDIN;
264: if (!readpassphrase("passphrase: ", pass, sizeof(pass), rppflags))
1.39 tedu 265: errx(1, "unable to read passphrase");
1.37 tedu 266: if (strlen(pass) == 0)
267: errx(1, "please provide a password");
1.70 tedu 268: if (confirm && !(rppflags & RPP_STDIN)) {
269: char pass2[1024];
270: if (!readpassphrase("confirm passphrase: ", pass2,
271: sizeof(pass2), rppflags))
272: errx(1, "unable to read passphrase");
273: if (strcmp(pass, pass2) != 0)
274: errx(1, "passwords don't match");
275: explicit_bzero(pass2, sizeof(pass2));
276: }
1.1 tedu 277: if (bcrypt_pbkdf(pass, strlen(pass), salt, saltlen, key,
278: keylen, rounds) == -1)
279: errx(1, "bcrypt pbkdf");
1.41 tedu 280: explicit_bzero(pass, sizeof(pass));
1.1 tedu 281: }
282:
283: static void
284: signmsg(uint8_t *seckey, uint8_t *msg, unsigned long long msglen,
285: uint8_t *sig)
286: {
287: unsigned long long siglen;
288: uint8_t *sigbuf;
289:
290: sigbuf = xmalloc(msglen + SIGBYTES);
291: crypto_sign_ed25519(sigbuf, &siglen, msg, msglen, seckey);
292: memcpy(sig, sigbuf, SIGBYTES);
293: free(sigbuf);
294: }
295:
296: static void
1.27 tedu 297: generate(const char *pubkeyfile, const char *seckeyfile, int rounds,
298: const char *comment)
1.1 tedu 299: {
300: uint8_t digest[SHA512_DIGEST_LENGTH];
301: struct pubkey pubkey;
302: struct enckey enckey;
303: uint8_t xorkey[sizeof(enckey.seckey)];
1.13 tedu 304: uint8_t fingerprint[FPLEN];
1.27 tedu 305: char commentbuf[COMMENTMAXLEN];
1.1 tedu 306: SHA2_CTX ctx;
307: int i;
308:
309: crypto_sign_ed25519_keypair(pubkey.pubkey, enckey.seckey);
1.13 tedu 310: arc4random_buf(fingerprint, sizeof(fingerprint));
1.1 tedu 311:
312: SHA512Init(&ctx);
313: SHA512Update(&ctx, enckey.seckey, sizeof(enckey.seckey));
314: SHA512Final(digest, &ctx);
315:
316: memcpy(enckey.pkalg, PKALG, 2);
317: memcpy(enckey.kdfalg, KDFALG, 2);
318: enckey.kdfrounds = htonl(rounds);
1.13 tedu 319: memcpy(enckey.fingerprint, fingerprint, FPLEN);
1.1 tedu 320: arc4random_buf(enckey.salt, sizeof(enckey.salt));
1.70 tedu 321: kdf(enckey.salt, sizeof(enckey.salt), rounds, 1, 1, xorkey, sizeof(xorkey));
1.1 tedu 322: memcpy(enckey.checksum, digest, sizeof(enckey.checksum));
323: for (i = 0; i < sizeof(enckey.seckey); i++)
324: enckey.seckey[i] ^= xorkey[i];
1.41 tedu 325: explicit_bzero(digest, sizeof(digest));
326: explicit_bzero(xorkey, sizeof(xorkey));
1.1 tedu 327:
1.40 deraadt 328: if (snprintf(commentbuf, sizeof(commentbuf), "%s secret key",
329: comment) >= sizeof(commentbuf))
1.71 tedu 330: errx(1, "comment too long");
1.27 tedu 331: writeb64file(seckeyfile, commentbuf, &enckey,
1.58 tedu 332: sizeof(enckey), NULL, 0, O_EXCL, 0600);
1.41 tedu 333: explicit_bzero(&enckey, sizeof(enckey));
1.1 tedu 334:
335: memcpy(pubkey.pkalg, PKALG, 2);
1.13 tedu 336: memcpy(pubkey.fingerprint, fingerprint, FPLEN);
1.40 deraadt 337: if (snprintf(commentbuf, sizeof(commentbuf), "%s public key",
338: comment) >= sizeof(commentbuf))
1.71 tedu 339: errx(1, "comment too long");
1.27 tedu 340: writeb64file(pubkeyfile, commentbuf, &pubkey,
1.58 tedu 341: sizeof(pubkey), NULL, 0, O_EXCL, 0666);
1.1 tedu 342: }
343:
344: static void
1.16 tedu 345: sign(const char *seckeyfile, const char *msgfile, const char *sigfile,
346: int embedded)
1.1 tedu 347: {
348: struct sig sig;
349: uint8_t digest[SHA512_DIGEST_LENGTH];
350: struct enckey enckey;
351: uint8_t xorkey[sizeof(enckey.seckey)];
352: uint8_t *msg;
1.51 tedu 353: char comment[COMMENTMAXLEN], sigcomment[COMMENTMAXLEN];
354: char *secname;
1.1 tedu 355: unsigned long long msglen;
356: int i, rounds;
357: SHA2_CTX ctx;
358:
1.18 tedu 359: readb64file(seckeyfile, &enckey, sizeof(enckey), comment);
1.1 tedu 360:
1.65 tedu 361: if (memcmp(enckey.kdfalg, KDFALG, 2) != 0)
1.1 tedu 362: errx(1, "unsupported KDF");
363: rounds = ntohl(enckey.kdfrounds);
1.50 tedu 364: kdf(enckey.salt, sizeof(enckey.salt), rounds, strcmp(msgfile, "-") != 0,
1.70 tedu 365: 0, xorkey, sizeof(xorkey));
1.1 tedu 366: for (i = 0; i < sizeof(enckey.seckey); i++)
367: enckey.seckey[i] ^= xorkey[i];
1.41 tedu 368: explicit_bzero(xorkey, sizeof(xorkey));
1.1 tedu 369: SHA512Init(&ctx);
370: SHA512Update(&ctx, enckey.seckey, sizeof(enckey.seckey));
371: SHA512Final(digest, &ctx);
1.65 tedu 372: if (memcmp(enckey.checksum, digest, sizeof(enckey.checksum)) != 0)
1.1 tedu 373: errx(1, "incorrect passphrase");
1.41 tedu 374: explicit_bzero(digest, sizeof(digest));
1.1 tedu 375:
1.16 tedu 376: msg = readmsg(msgfile, &msglen);
1.1 tedu 377:
378: signmsg(enckey.seckey, msg, msglen, sig.sig);
1.13 tedu 379: memcpy(sig.fingerprint, enckey.fingerprint, FPLEN);
1.41 tedu 380: explicit_bzero(&enckey, sizeof(enckey));
1.1 tedu 381:
382: memcpy(sig.pkalg, PKALG, 2);
1.51 tedu 383: if ((secname = strstr(seckeyfile, ".sec")) && strlen(secname) == 4) {
384: if (snprintf(sigcomment, sizeof(sigcomment), VERIFYWITH "%.*s.pub",
1.57 tedu 385: (int)strlen(seckeyfile) - 4, seckeyfile) >= sizeof(sigcomment))
1.71 tedu 386: errx(1, "comment too long");
1.51 tedu 387: } else {
388: if (snprintf(sigcomment, sizeof(sigcomment), "signature from %s",
389: comment) >= sizeof(sigcomment))
1.71 tedu 390: errx(1, "comment too long");
1.51 tedu 391: }
1.16 tedu 392: if (embedded)
1.58 tedu 393: writeb64file(sigfile, sigcomment, &sig, sizeof(sig), msg,
394: msglen, O_TRUNC, 0666);
395: else
396: writeb64file(sigfile, sigcomment, &sig, sizeof(sig), NULL,
397: 0, O_TRUNC, 0666);
1.1 tedu 398:
399: free(msg);
400: }
1.22 tedu 401:
402: static void
403: inspect(const char *seckeyfile, const char *pubkeyfile, const char *sigfile)
404: {
405: struct sig sig;
406: struct enckey enckey;
407: struct pubkey pubkey;
408: char fp[(FPLEN + 2) / 3 * 4 + 1];
409:
410: if (seckeyfile) {
411: readb64file(seckeyfile, &enckey, sizeof(enckey), NULL);
412: b64_ntop(enckey.fingerprint, FPLEN, fp, sizeof(fp));
413: printf("sec fp: %s\n", fp);
414: }
415: if (pubkeyfile) {
416: readb64file(pubkeyfile, &pubkey, sizeof(pubkey), NULL);
417: b64_ntop(pubkey.fingerprint, FPLEN, fp, sizeof(fp));
418: printf("pub fp: %s\n", fp);
419: }
420: if (sigfile) {
421: readb64file(sigfile, &sig, sizeof(sig), NULL);
422: b64_ntop(sig.fingerprint, FPLEN, fp, sizeof(fp));
423: printf("sig fp: %s\n", fp);
424: }
425: }
1.14 tedu 426: #endif
1.1 tedu 427:
428: static void
1.62 tedu 429: verifymsg(struct pubkey *pubkey, uint8_t *msg, unsigned long long msglen,
430: struct sig *sig, int quiet)
1.13 tedu 431: {
432: uint8_t *sigbuf, *dummybuf;
433: unsigned long long siglen, dummylen;
434:
1.65 tedu 435: if (memcmp(pubkey->fingerprint, sig->fingerprint, FPLEN) != 0)
1.62 tedu 436: errx(1, "verification failed: checked against wrong key");
437:
1.13 tedu 438: siglen = SIGBYTES + msglen;
439: sigbuf = xmalloc(siglen);
440: dummybuf = xmalloc(siglen);
1.62 tedu 441: memcpy(sigbuf, sig->sig, SIGBYTES);
1.13 tedu 442: memcpy(sigbuf + SIGBYTES, msg, msglen);
443: if (crypto_sign_ed25519_open(dummybuf, &dummylen, sigbuf, siglen,
1.62 tedu 444: pubkey->pubkey) == -1)
1.13 tedu 445: errx(1, "signature verification failed");
1.42 tedu 446: if (!quiet)
447: printf("Signature Verified\n");
1.13 tedu 448: free(sigbuf);
449: free(dummybuf);
450: }
451:
1.64 tedu 452: static void
453: readpubkey(const char *pubkeyfile, struct pubkey *pubkey,
454: const char *sigcomment)
455: {
1.76 tedu 456: const char *safepath = "/etc/signify/";
1.64 tedu 457:
458: if (!pubkeyfile) {
459: if ((pubkeyfile = strstr(sigcomment, VERIFYWITH))) {
460: pubkeyfile += strlen(VERIFYWITH);
1.76 tedu 461: if (strncmp(pubkeyfile, safepath, strlen(safepath)) != 0 ||
1.64 tedu 462: strstr(pubkeyfile, "/../") != NULL)
463: errx(1, "untrusted path %s", pubkeyfile);
464: } else
1.66 tedu 465: usage("must specify pubkey");
1.64 tedu 466: }
467: readb64file(pubkeyfile, pubkey, sizeof(*pubkey), NULL);
468: }
1.13 tedu 469:
470: static void
1.63 tedu 471: verifysimple(const char *pubkeyfile, const char *msgfile, const char *sigfile,
472: int quiet)
1.1 tedu 473: {
1.64 tedu 474: char sigcomment[COMMENTMAXLEN];
1.1 tedu 475: struct sig sig;
476: struct pubkey pubkey;
1.63 tedu 477: unsigned long long msglen;
1.1 tedu 478: uint8_t *msg;
1.16 tedu 479:
1.63 tedu 480: msg = readmsg(msgfile, &msglen);
1.1 tedu 481:
1.64 tedu 482: readb64file(sigfile, &sig, sizeof(sig), sigcomment);
483: readpubkey(pubkeyfile, &pubkey, sigcomment);
1.63 tedu 484:
485: verifymsg(&pubkey, msg, msglen, &sig, quiet);
486:
487: free(msg);
488: }
489:
490: static uint8_t *
491: verifyembedded(const char *pubkeyfile, const char *sigfile,
492: int quiet, unsigned long long *msglenp)
493: {
1.64 tedu 494: char sigcomment[COMMENTMAXLEN];
1.63 tedu 495: struct sig sig;
496: struct pubkey pubkey;
497: unsigned long long msglen, siglen;
498: uint8_t *msg;
499:
500: msg = readmsg(sigfile, &msglen);
501:
1.64 tedu 502: siglen = parseb64file(sigfile, msg, &sig, sizeof(sig), sigcomment);
503: readpubkey(pubkeyfile, &pubkey, sigcomment);
504:
1.63 tedu 505: msglen -= siglen;
506: memmove(msg, msg + siglen, msglen);
507: msg[msglen] = 0;
1.13 tedu 508:
1.62 tedu 509: verifymsg(&pubkey, msg, msglen, &sig, quiet);
1.63 tedu 510:
511: *msglenp = msglen;
512: return msg;
513: }
514:
515: static void
516: verify(const char *pubkeyfile, const char *msgfile, const char *sigfile,
517: int embedded, int quiet)
518: {
519: unsigned long long msglen;
520: uint8_t *msg;
521: int fd;
522:
1.16 tedu 523: if (embedded) {
1.63 tedu 524: msg = verifyembedded(pubkeyfile, sigfile, quiet, &msglen);
1.30 tedu 525: fd = xopen(msgfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666);
1.16 tedu 526: writeall(fd, msg, msglen, msgfile);
1.68 espie 527: free(msg);
1.16 tedu 528: close(fd);
1.63 tedu 529: } else {
530: verifysimple(pubkeyfile, msgfile, sigfile, quiet);
1.16 tedu 531: }
1.1 tedu 532: }
533:
1.42 tedu 534: #ifndef VERIFYONLY
1.83 tedu 535: #define HASHBUFSIZE 224
1.42 tedu 536: struct checksum {
537: char file[1024];
1.83 tedu 538: char hash[HASHBUFSIZE];
1.81 tedu 539: char algo[32];
1.42 tedu 540: };
541:
1.85 espie 542: static void *
543: ecalloc(size_t s1, size_t s2, void *data)
544: {
1.87 tedu 545: void *p;
546:
1.89 ! tedu 547: if (!(p = calloc(s1, s2)))
1.85 espie 548: err(1, "calloc");
549: return p;
550: }
551:
552: static void
553: efree(void *p, void *data)
554: {
555: free(p);
556: }
557:
1.42 tedu 558: static void
1.85 espie 559: recodehash(char *hash, size_t len)
1.80 tedu 560: {
1.83 tedu 561: uint8_t data[HASHBUFSIZE / 2];
1.80 tedu 562: int i, rv;
563:
1.85 espie 564: if (strlen(hash) == len)
1.80 tedu 565: return;
566: if ((rv = b64_pton(hash, data, sizeof(data))) == -1)
567: errx(1, "invalid base64 encoding");
568: for (i = 0; i < rv; i++)
1.83 tedu 569: snprintf(hash + i * 2, HASHBUFSIZE - i * 2, "%2.2x", data[i]);
1.80 tedu 570: }
571:
1.85 espie 572: static int
573: verifychecksum(struct checksum *c, int quiet)
574: {
575: char buf[HASHBUFSIZE];
1.87 tedu 576:
1.85 espie 577: if (strcmp(c->algo, "SHA256") == 0) {
578: recodehash(c->hash, SHA256_DIGEST_STRING_LENGTH-1);
579: if (!SHA256File(c->file, buf))
580: return 0;
581: } else if (strcmp(c->algo, "SHA512") == 0) {
582: recodehash(c->hash, SHA512_DIGEST_STRING_LENGTH-1);
583: if (!SHA512File(c->file, buf))
584: return 0;
585: } else {
586: errx(1, "can't handle algorithm %s", c->algo);
587: }
588: if (strcmp(c->hash, buf) != 0) {
589: return 0;
590: }
591: if (!quiet)
592: printf("%s: OK\n", c->file);
593: return 1;
594: }
595:
1.80 tedu 596: static void
1.45 tedu 597: verifychecksums(char *msg, int argc, char **argv, int quiet)
1.42 tedu 598: {
1.87 tedu 599: struct ohash_info info = { 0, NULL, ecalloc, efree, NULL };
1.85 espie 600: struct ohash myh;
601: struct checksum c;
1.88 tedu 602: char *e, *line, *endline;
1.85 espie 603: int hasfailed = 0;
1.87 tedu 604: int i, rv;
1.85 espie 605: unsigned int slot;
606:
1.88 tedu 607: ohash_init(&myh, 6, &info);
1.85 espie 608: if (argc) {
609: for (i = 0; i < argc; i++) {
610: slot = ohash_qlookup(&myh, argv[i]);
611: e = ohash_find(&myh, slot);
612: if (e == NULL)
613: ohash_insert(&myh, slot, argv[i]);
614: }
615: }
1.42 tedu 616:
1.45 tedu 617: line = msg;
1.42 tedu 618: while (line && *line) {
619: if ((endline = strchr(line, '\n')))
1.78 tedu 620: *endline++ = '\0';
1.85 espie 621: rv = sscanf(line, "%31s (%1023s = "3s",
622: c.algo, c.file, c.hash);
623: if (rv != 3 || c.file[0] == 0 || c.file[strlen(c.file)-1] != ')')
1.42 tedu 624: errx(1, "unable to parse checksum line %s", line);
1.87 tedu 625: c.file[strlen(c.file) - 1] = '\0';
1.42 tedu 626: line = endline;
1.85 espie 627: if (argc) {
628: slot = ohash_qlookup(&myh, c.file);
629: e = ohash_find(&myh, slot);
630: if (e != NULL) {
1.87 tedu 631: if (verifychecksum(&c, quiet) != 0)
1.85 espie 632: ohash_remove(&myh, slot);
633: }
1.42 tedu 634: } else {
1.87 tedu 635: if (verifychecksum(&c, quiet) == 0) {
1.88 tedu 636: slot = ohash_qlookup(&myh, c.file);
637: e = ohash_find(&myh, slot);
638: if (e == NULL) {
639: if (!(e = strdup(c.file)))
640: err(1, "strdup");
641: ohash_insert(&myh, slot, e);
642: }
1.42 tedu 643: }
644: }
1.85 espie 645: }
1.42 tedu 646:
1.88 tedu 647: for (e = ohash_first(&myh, &slot); e != NULL; e = ohash_next(&myh, &slot)) {
648: fprintf(stderr, "%s: FAIL\n", e);
649: hasfailed = 1;
650: if (argc == 0)
651: free(e);
1.42 tedu 652: }
1.88 tedu 653: ohash_delete(&myh);
1.43 tedu 654: if (hasfailed)
1.42 tedu 655: exit(1);
656: }
657:
658: static void
659: check(const char *pubkeyfile, const char *sigfile, int quiet, int argc,
660: char **argv)
661: {
1.63 tedu 662: unsigned long long msglen;
1.42 tedu 663: uint8_t *msg;
664:
1.63 tedu 665: msg = verifyembedded(pubkeyfile, sigfile, quiet, &msglen);
1.45 tedu 666: verifychecksums((char *)msg, argc, argv, quiet);
1.42 tedu 667:
1.63 tedu 668: free(msg);
1.42 tedu 669: }
670: #endif
671:
1.1 tedu 672: int
673: main(int argc, char **argv)
674: {
1.16 tedu 675: const char *pubkeyfile = NULL, *seckeyfile = NULL, *msgfile = NULL,
1.1 tedu 676: *sigfile = NULL;
677: char sigfilebuf[1024];
1.27 tedu 678: const char *comment = "signify";
1.1 tedu 679: int ch, rounds;
1.16 tedu 680: int embedded = 0;
1.42 tedu 681: int quiet = 0;
1.6 tedu 682: enum {
683: NONE,
1.42 tedu 684: CHECK,
1.6 tedu 685: GENERATE,
1.22 tedu 686: INSPECT,
1.6 tedu 687: SIGN,
688: VERIFY
689: } verb = NONE;
690:
1.1 tedu 691:
692: rounds = 42;
693:
1.42 tedu 694: while ((ch = getopt(argc, argv, "CGISVc:em:np:qs:x:")) != -1) {
1.1 tedu 695: switch (ch) {
1.14 tedu 696: #ifndef VERIFYONLY
1.42 tedu 697: case 'C':
698: if (verb)
699: usage(NULL);
700: verb = CHECK;
701: break;
1.6 tedu 702: case 'G':
703: if (verb)
1.31 tedu 704: usage(NULL);
1.6 tedu 705: verb = GENERATE;
706: break;
1.22 tedu 707: case 'I':
708: if (verb)
1.31 tedu 709: usage(NULL);
1.22 tedu 710: verb = INSPECT;
711: break;
1.6 tedu 712: case 'S':
713: if (verb)
1.31 tedu 714: usage(NULL);
1.6 tedu 715: verb = SIGN;
716: break;
1.14 tedu 717: #endif
1.6 tedu 718: case 'V':
719: if (verb)
1.31 tedu 720: usage(NULL);
1.6 tedu 721: verb = VERIFY;
722: break;
1.27 tedu 723: case 'c':
724: comment = optarg;
725: break;
1.16 tedu 726: case 'e':
727: embedded = 1;
728: break;
1.31 tedu 729: case 'm':
730: msgfile = optarg;
731: break;
1.6 tedu 732: case 'n':
1.1 tedu 733: rounds = 0;
734: break;
1.6 tedu 735: case 'p':
1.1 tedu 736: pubkeyfile = optarg;
737: break;
1.42 tedu 738: case 'q':
739: quiet = 1;
740: break;
1.6 tedu 741: case 's':
1.1 tedu 742: seckeyfile = optarg;
743: break;
1.31 tedu 744: case 'x':
745: sigfile = optarg;
746: break;
1.1 tedu 747: default:
1.31 tedu 748: usage(NULL);
1.1 tedu 749: break;
750: }
751: }
1.2 tedu 752: argc -= optind;
1.9 espie 753: argv += optind;
754:
1.42 tedu 755: #ifndef VERIFYONLY
756: if (verb == CHECK) {
1.86 tedu 757: if (!sigfile)
758: usage("must specify sigfile");
1.42 tedu 759: check(pubkeyfile, sigfile, quiet, argc, argv);
760: return 0;
761: }
762: #endif
763:
1.31 tedu 764: if (argc != 0)
765: usage(NULL);
766:
1.36 tedu 767: if (!sigfile && msgfile) {
768: if (strcmp(msgfile, "-") == 0)
1.66 tedu 769: usage("must specify sigfile with - message");
1.36 tedu 770: if (snprintf(sigfilebuf, sizeof(sigfilebuf), "%s.sig",
771: msgfile) >= sizeof(sigfilebuf))
772: errx(1, "path too long");
773: sigfile = sigfilebuf;
774: }
1.1 tedu 775:
1.36 tedu 776: switch (verb) {
1.14 tedu 777: #ifndef VERIFYONLY
1.36 tedu 778: case GENERATE:
1.31 tedu 779: if (!pubkeyfile || !seckeyfile)
1.66 tedu 780: usage("must specify pubkey and seckey");
1.27 tedu 781: generate(pubkeyfile, seckeyfile, rounds, comment);
1.36 tedu 782: break;
783: case INSPECT:
1.22 tedu 784: inspect(seckeyfile, pubkeyfile, sigfile);
1.36 tedu 785: break;
786: case SIGN:
787: if (!msgfile || !seckeyfile)
1.66 tedu 788: usage("must specify message and seckey");
1.36 tedu 789: sign(seckeyfile, msgfile, sigfile, embedded);
790: break;
1.14 tedu 791: #endif
1.36 tedu 792: case VERIFY:
1.51 tedu 793: if (!msgfile)
1.66 tedu 794: usage("must specify message");
1.42 tedu 795: verify(pubkeyfile, msgfile, sigfile, embedded, quiet);
1.36 tedu 796: break;
797: default:
798: usage(NULL);
799: break;
1.1 tedu 800: }
1.9 espie 801:
1.1 tedu 802: return 0;
803: }