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