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