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