Annotation of src/usr.bin/signify/signify.c, Revision 1.91
1.91 ! tedu 1: /* $OpenBSD: signify.c,v 1.90 2014/05/30 21:20:49 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];
1.91 ! tedu 233: int fd, rv, nr;
1.1 tedu 234:
1.61 deraadt 235: fd = xopen(filename, O_CREAT|oflags|O_NOFOLLOW|O_WRONLY, mode);
1.91 ! tedu 236: if ((nr = snprintf(header, sizeof(header), "%s%s\n",
! 237: COMMENTHDR, comment)) == -1 || nr >= 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;
1.91 ! tedu 307: int i, nr;
1.1 tedu 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.91 ! tedu 328: if ((nr = snprintf(commentbuf, sizeof(commentbuf), "%s secret key",
! 329: comment)) == -1 || nr >= 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.91 ! tedu 337: if ((nr = snprintf(commentbuf, sizeof(commentbuf), "%s public key",
! 338: comment)) == -1 || nr >= 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;
1.91 ! tedu 356: int i, rounds, nr;
1.1 tedu 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.90 tedu 383: secname = strstr(seckeyfile, ".sec");
384: if (secname && strlen(secname) == 4) {
1.91 ! tedu 385: if ((nr = snprintf(sigcomment, sizeof(sigcomment), VERIFYWITH "%.*s.pub",
! 386: (int)strlen(seckeyfile) - 4, seckeyfile)) == -1 || nr >= sizeof(sigcomment))
1.71 tedu 387: errx(1, "comment too long");
1.51 tedu 388: } else {
1.91 ! tedu 389: if ((nr = snprintf(sigcomment, sizeof(sigcomment), "signature from %s",
! 390: comment)) == -1 || nr >= sizeof(sigcomment))
1.71 tedu 391: errx(1, "comment too long");
1.51 tedu 392: }
1.16 tedu 393: if (embedded)
1.58 tedu 394: writeb64file(sigfile, sigcomment, &sig, sizeof(sig), msg,
395: msglen, O_TRUNC, 0666);
396: else
397: writeb64file(sigfile, sigcomment, &sig, sizeof(sig), NULL,
398: 0, O_TRUNC, 0666);
1.1 tedu 399:
400: free(msg);
401: }
1.22 tedu 402:
403: static void
404: inspect(const char *seckeyfile, const char *pubkeyfile, const char *sigfile)
405: {
406: struct sig sig;
407: struct enckey enckey;
408: struct pubkey pubkey;
409: char fp[(FPLEN + 2) / 3 * 4 + 1];
410:
411: if (seckeyfile) {
412: readb64file(seckeyfile, &enckey, sizeof(enckey), NULL);
413: b64_ntop(enckey.fingerprint, FPLEN, fp, sizeof(fp));
414: printf("sec fp: %s\n", fp);
415: }
416: if (pubkeyfile) {
417: readb64file(pubkeyfile, &pubkey, sizeof(pubkey), NULL);
418: b64_ntop(pubkey.fingerprint, FPLEN, fp, sizeof(fp));
419: printf("pub fp: %s\n", fp);
420: }
421: if (sigfile) {
422: readb64file(sigfile, &sig, sizeof(sig), NULL);
423: b64_ntop(sig.fingerprint, FPLEN, fp, sizeof(fp));
424: printf("sig fp: %s\n", fp);
425: }
426: }
1.14 tedu 427: #endif
1.1 tedu 428:
429: static void
1.62 tedu 430: verifymsg(struct pubkey *pubkey, uint8_t *msg, unsigned long long msglen,
431: struct sig *sig, int quiet)
1.13 tedu 432: {
433: uint8_t *sigbuf, *dummybuf;
434: unsigned long long siglen, dummylen;
435:
1.65 tedu 436: if (memcmp(pubkey->fingerprint, sig->fingerprint, FPLEN) != 0)
1.62 tedu 437: errx(1, "verification failed: checked against wrong key");
438:
1.13 tedu 439: siglen = SIGBYTES + msglen;
440: sigbuf = xmalloc(siglen);
441: dummybuf = xmalloc(siglen);
1.62 tedu 442: memcpy(sigbuf, sig->sig, SIGBYTES);
1.13 tedu 443: memcpy(sigbuf + SIGBYTES, msg, msglen);
444: if (crypto_sign_ed25519_open(dummybuf, &dummylen, sigbuf, siglen,
1.62 tedu 445: pubkey->pubkey) == -1)
1.13 tedu 446: errx(1, "signature verification failed");
1.42 tedu 447: if (!quiet)
448: printf("Signature Verified\n");
1.13 tedu 449: free(sigbuf);
450: free(dummybuf);
451: }
452:
1.64 tedu 453: static void
454: readpubkey(const char *pubkeyfile, struct pubkey *pubkey,
455: const char *sigcomment)
456: {
1.76 tedu 457: const char *safepath = "/etc/signify/";
1.64 tedu 458:
459: if (!pubkeyfile) {
1.90 tedu 460: pubkeyfile = strstr(sigcomment, VERIFYWITH);
461: if (pubkeyfile) {
1.64 tedu 462: pubkeyfile += strlen(VERIFYWITH);
1.76 tedu 463: if (strncmp(pubkeyfile, safepath, strlen(safepath)) != 0 ||
1.64 tedu 464: strstr(pubkeyfile, "/../") != NULL)
465: errx(1, "untrusted path %s", pubkeyfile);
466: } else
1.66 tedu 467: usage("must specify pubkey");
1.64 tedu 468: }
469: readb64file(pubkeyfile, pubkey, sizeof(*pubkey), NULL);
470: }
1.13 tedu 471:
472: static void
1.63 tedu 473: verifysimple(const char *pubkeyfile, const char *msgfile, const char *sigfile,
474: int quiet)
1.1 tedu 475: {
1.64 tedu 476: char sigcomment[COMMENTMAXLEN];
1.1 tedu 477: struct sig sig;
478: struct pubkey pubkey;
1.63 tedu 479: unsigned long long msglen;
1.1 tedu 480: uint8_t *msg;
1.16 tedu 481:
1.63 tedu 482: msg = readmsg(msgfile, &msglen);
1.1 tedu 483:
1.64 tedu 484: readb64file(sigfile, &sig, sizeof(sig), sigcomment);
485: readpubkey(pubkeyfile, &pubkey, sigcomment);
1.63 tedu 486:
487: verifymsg(&pubkey, msg, msglen, &sig, quiet);
488:
489: free(msg);
490: }
491:
492: static uint8_t *
493: verifyembedded(const char *pubkeyfile, const char *sigfile,
494: int quiet, unsigned long long *msglenp)
495: {
1.64 tedu 496: char sigcomment[COMMENTMAXLEN];
1.63 tedu 497: struct sig sig;
498: struct pubkey pubkey;
499: unsigned long long msglen, siglen;
500: uint8_t *msg;
501:
502: msg = readmsg(sigfile, &msglen);
503:
1.64 tedu 504: siglen = parseb64file(sigfile, msg, &sig, sizeof(sig), sigcomment);
505: readpubkey(pubkeyfile, &pubkey, sigcomment);
506:
1.63 tedu 507: msglen -= siglen;
508: memmove(msg, msg + siglen, msglen);
509: msg[msglen] = 0;
1.13 tedu 510:
1.62 tedu 511: verifymsg(&pubkey, msg, msglen, &sig, quiet);
1.63 tedu 512:
513: *msglenp = msglen;
514: return msg;
515: }
516:
517: static void
518: verify(const char *pubkeyfile, const char *msgfile, const char *sigfile,
519: int embedded, int quiet)
520: {
521: unsigned long long msglen;
522: uint8_t *msg;
523: int fd;
524:
1.16 tedu 525: if (embedded) {
1.63 tedu 526: msg = verifyembedded(pubkeyfile, sigfile, quiet, &msglen);
1.30 tedu 527: fd = xopen(msgfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666);
1.16 tedu 528: writeall(fd, msg, msglen, msgfile);
1.68 espie 529: free(msg);
1.16 tedu 530: close(fd);
1.63 tedu 531: } else {
532: verifysimple(pubkeyfile, msgfile, sigfile, quiet);
1.16 tedu 533: }
1.1 tedu 534: }
535:
1.42 tedu 536: #ifndef VERIFYONLY
1.83 tedu 537: #define HASHBUFSIZE 224
1.42 tedu 538: struct checksum {
539: char file[1024];
1.83 tedu 540: char hash[HASHBUFSIZE];
1.81 tedu 541: char algo[32];
1.42 tedu 542: };
543:
1.85 espie 544: static void *
545: ecalloc(size_t s1, size_t s2, void *data)
546: {
1.87 tedu 547: void *p;
548:
1.89 tedu 549: if (!(p = calloc(s1, s2)))
1.85 espie 550: err(1, "calloc");
551: return p;
552: }
553:
554: static void
555: efree(void *p, void *data)
556: {
557: free(p);
558: }
559:
1.42 tedu 560: static void
1.85 espie 561: recodehash(char *hash, size_t len)
1.80 tedu 562: {
1.83 tedu 563: uint8_t data[HASHBUFSIZE / 2];
1.80 tedu 564: int i, rv;
565:
1.85 espie 566: if (strlen(hash) == len)
1.80 tedu 567: return;
568: if ((rv = b64_pton(hash, data, sizeof(data))) == -1)
569: errx(1, "invalid base64 encoding");
570: for (i = 0; i < rv; i++)
1.83 tedu 571: snprintf(hash + i * 2, HASHBUFSIZE - i * 2, "%2.2x", data[i]);
1.80 tedu 572: }
573:
1.85 espie 574: static int
575: verifychecksum(struct checksum *c, int quiet)
576: {
577: char buf[HASHBUFSIZE];
1.87 tedu 578:
1.85 espie 579: if (strcmp(c->algo, "SHA256") == 0) {
580: recodehash(c->hash, SHA256_DIGEST_STRING_LENGTH-1);
581: if (!SHA256File(c->file, buf))
582: return 0;
583: } else if (strcmp(c->algo, "SHA512") == 0) {
584: recodehash(c->hash, SHA512_DIGEST_STRING_LENGTH-1);
585: if (!SHA512File(c->file, buf))
586: return 0;
587: } else {
588: errx(1, "can't handle algorithm %s", c->algo);
589: }
590: if (strcmp(c->hash, buf) != 0) {
591: return 0;
592: }
593: if (!quiet)
594: printf("%s: OK\n", c->file);
595: return 1;
596: }
597:
1.80 tedu 598: static void
1.45 tedu 599: verifychecksums(char *msg, int argc, char **argv, int quiet)
1.42 tedu 600: {
1.87 tedu 601: struct ohash_info info = { 0, NULL, ecalloc, efree, NULL };
1.85 espie 602: struct ohash myh;
603: struct checksum c;
1.88 tedu 604: char *e, *line, *endline;
1.85 espie 605: int hasfailed = 0;
1.87 tedu 606: int i, rv;
1.85 espie 607: unsigned int slot;
608:
1.88 tedu 609: ohash_init(&myh, 6, &info);
1.85 espie 610: if (argc) {
611: for (i = 0; i < argc; i++) {
612: slot = ohash_qlookup(&myh, argv[i]);
613: e = ohash_find(&myh, slot);
614: if (e == NULL)
615: ohash_insert(&myh, slot, argv[i]);
616: }
617: }
1.42 tedu 618:
1.45 tedu 619: line = msg;
1.42 tedu 620: while (line && *line) {
621: if ((endline = strchr(line, '\n')))
1.78 tedu 622: *endline++ = '\0';
1.85 espie 623: rv = sscanf(line, "%31s (%1023s = "3s",
624: c.algo, c.file, c.hash);
625: if (rv != 3 || c.file[0] == 0 || c.file[strlen(c.file)-1] != ')')
1.42 tedu 626: errx(1, "unable to parse checksum line %s", line);
1.87 tedu 627: c.file[strlen(c.file) - 1] = '\0';
1.42 tedu 628: line = endline;
1.85 espie 629: if (argc) {
630: slot = ohash_qlookup(&myh, c.file);
631: e = ohash_find(&myh, slot);
632: if (e != NULL) {
1.87 tedu 633: if (verifychecksum(&c, quiet) != 0)
1.85 espie 634: ohash_remove(&myh, slot);
635: }
1.42 tedu 636: } else {
1.87 tedu 637: if (verifychecksum(&c, quiet) == 0) {
1.88 tedu 638: slot = ohash_qlookup(&myh, c.file);
639: e = ohash_find(&myh, slot);
640: if (e == NULL) {
641: if (!(e = strdup(c.file)))
642: err(1, "strdup");
643: ohash_insert(&myh, slot, e);
644: }
1.42 tedu 645: }
646: }
1.85 espie 647: }
1.42 tedu 648:
1.88 tedu 649: for (e = ohash_first(&myh, &slot); e != NULL; e = ohash_next(&myh, &slot)) {
650: fprintf(stderr, "%s: FAIL\n", e);
651: hasfailed = 1;
652: if (argc == 0)
653: free(e);
1.42 tedu 654: }
1.88 tedu 655: ohash_delete(&myh);
1.43 tedu 656: if (hasfailed)
1.42 tedu 657: exit(1);
658: }
659:
660: static void
661: check(const char *pubkeyfile, const char *sigfile, int quiet, int argc,
662: char **argv)
663: {
1.63 tedu 664: unsigned long long msglen;
1.42 tedu 665: uint8_t *msg;
666:
1.63 tedu 667: msg = verifyembedded(pubkeyfile, sigfile, quiet, &msglen);
1.45 tedu 668: verifychecksums((char *)msg, argc, argv, quiet);
1.42 tedu 669:
1.63 tedu 670: free(msg);
1.42 tedu 671: }
672: #endif
673:
1.1 tedu 674: int
675: main(int argc, char **argv)
676: {
1.16 tedu 677: const char *pubkeyfile = NULL, *seckeyfile = NULL, *msgfile = NULL,
1.1 tedu 678: *sigfile = NULL;
679: char sigfilebuf[1024];
1.27 tedu 680: const char *comment = "signify";
1.1 tedu 681: int ch, rounds;
1.16 tedu 682: int embedded = 0;
1.42 tedu 683: int quiet = 0;
1.6 tedu 684: enum {
685: NONE,
1.42 tedu 686: CHECK,
1.6 tedu 687: GENERATE,
1.22 tedu 688: INSPECT,
1.6 tedu 689: SIGN,
690: VERIFY
691: } verb = NONE;
692:
1.1 tedu 693:
694: rounds = 42;
695:
1.42 tedu 696: while ((ch = getopt(argc, argv, "CGISVc:em:np:qs:x:")) != -1) {
1.1 tedu 697: switch (ch) {
1.14 tedu 698: #ifndef VERIFYONLY
1.42 tedu 699: case 'C':
700: if (verb)
701: usage(NULL);
702: verb = CHECK;
703: break;
1.6 tedu 704: case 'G':
705: if (verb)
1.31 tedu 706: usage(NULL);
1.6 tedu 707: verb = GENERATE;
708: break;
1.22 tedu 709: case 'I':
710: if (verb)
1.31 tedu 711: usage(NULL);
1.22 tedu 712: verb = INSPECT;
713: break;
1.6 tedu 714: case 'S':
715: if (verb)
1.31 tedu 716: usage(NULL);
1.6 tedu 717: verb = SIGN;
718: break;
1.14 tedu 719: #endif
1.6 tedu 720: case 'V':
721: if (verb)
1.31 tedu 722: usage(NULL);
1.6 tedu 723: verb = VERIFY;
724: break;
1.27 tedu 725: case 'c':
726: comment = optarg;
727: break;
1.16 tedu 728: case 'e':
729: embedded = 1;
730: break;
1.31 tedu 731: case 'm':
732: msgfile = optarg;
733: break;
1.6 tedu 734: case 'n':
1.1 tedu 735: rounds = 0;
736: break;
1.6 tedu 737: case 'p':
1.1 tedu 738: pubkeyfile = optarg;
739: break;
1.42 tedu 740: case 'q':
741: quiet = 1;
742: break;
1.6 tedu 743: case 's':
1.1 tedu 744: seckeyfile = optarg;
745: break;
1.31 tedu 746: case 'x':
747: sigfile = optarg;
748: break;
1.1 tedu 749: default:
1.31 tedu 750: usage(NULL);
1.1 tedu 751: break;
752: }
753: }
1.2 tedu 754: argc -= optind;
1.9 espie 755: argv += optind;
756:
1.42 tedu 757: #ifndef VERIFYONLY
758: if (verb == CHECK) {
1.86 tedu 759: if (!sigfile)
760: usage("must specify sigfile");
1.42 tedu 761: check(pubkeyfile, sigfile, quiet, argc, argv);
762: return 0;
763: }
764: #endif
765:
1.31 tedu 766: if (argc != 0)
767: usage(NULL);
768:
1.36 tedu 769: if (!sigfile && msgfile) {
1.91 ! tedu 770: int nr;
1.36 tedu 771: if (strcmp(msgfile, "-") == 0)
1.66 tedu 772: usage("must specify sigfile with - message");
1.91 ! tedu 773: if ((nr = snprintf(sigfilebuf, sizeof(sigfilebuf), "%s.sig",
! 774: msgfile)) == -1 || nr >= sizeof(sigfilebuf))
1.36 tedu 775: errx(1, "path too long");
776: sigfile = sigfilebuf;
777: }
1.1 tedu 778:
1.36 tedu 779: switch (verb) {
1.14 tedu 780: #ifndef VERIFYONLY
1.36 tedu 781: case GENERATE:
1.31 tedu 782: if (!pubkeyfile || !seckeyfile)
1.66 tedu 783: usage("must specify pubkey and seckey");
1.27 tedu 784: generate(pubkeyfile, seckeyfile, rounds, comment);
1.36 tedu 785: break;
786: case INSPECT:
1.22 tedu 787: inspect(seckeyfile, pubkeyfile, sigfile);
1.36 tedu 788: break;
789: case SIGN:
790: if (!msgfile || !seckeyfile)
1.66 tedu 791: usage("must specify message and seckey");
1.36 tedu 792: sign(seckeyfile, msgfile, sigfile, embedded);
793: break;
1.14 tedu 794: #endif
1.36 tedu 795: case VERIFY:
1.51 tedu 796: if (!msgfile)
1.66 tedu 797: usage("must specify message");
1.42 tedu 798: verify(pubkeyfile, msgfile, sigfile, embedded, quiet);
1.36 tedu 799: break;
800: default:
801: usage(NULL);
802: break;
1.1 tedu 803: }
1.9 espie 804:
1.1 tedu 805: return 0;
806: }