1*a40f4206Stedu /* $OpenBSD: signify.c,v 1.45 2014/03/05 23:03:19 tedu Exp $ */ 24215a5deStedu /* 34215a5deStedu * Copyright (c) 2013 Ted Unangst <tedu@openbsd.org> 44215a5deStedu * 54215a5deStedu * Permission to use, copy, modify, and distribute this software for any 64215a5deStedu * purpose with or without fee is hereby granted, provided that the above 74215a5deStedu * copyright notice and this permission notice appear in all copies. 84215a5deStedu * 94215a5deStedu * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 104215a5deStedu * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 114215a5deStedu * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 124215a5deStedu * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 134215a5deStedu * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 144215a5deStedu * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 154215a5deStedu * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 164215a5deStedu */ 174215a5deStedu #include <sys/stat.h> 184215a5deStedu 194215a5deStedu #include <netinet/in.h> 204215a5deStedu #include <resolv.h> 214215a5deStedu 224215a5deStedu #include <stdint.h> 234215a5deStedu #include <fcntl.h> 244215a5deStedu #include <string.h> 254215a5deStedu #include <stdio.h> 26ebde6afdStedu #include <stdlib.h> 274215a5deStedu #include <err.h> 284215a5deStedu #include <unistd.h> 294215a5deStedu #include <readpassphrase.h> 304215a5deStedu #include <util.h> 314215a5deStedu #include <sha2.h> 324215a5deStedu 334215a5deStedu #include "crypto_api.h" 344215a5deStedu 354215a5deStedu #define SIGBYTES crypto_sign_ed25519_BYTES 364215a5deStedu #define SECRETBYTES crypto_sign_ed25519_SECRETKEYBYTES 374215a5deStedu #define PUBLICBYTES crypto_sign_ed25519_PUBLICKEYBYTES 384215a5deStedu 394215a5deStedu #define PKALG "Ed" 404215a5deStedu #define KDFALG "BK" 411c9c770cStedu #define FPLEN 8 421c9c770cStedu 431c9c770cStedu #define COMMENTHDR "untrusted comment: " 441453d2a0Stedu #define COMMENTHDRLEN 19 451453d2a0Stedu #define COMMENTMAXLEN 1024 464215a5deStedu 474215a5deStedu struct enckey { 484215a5deStedu uint8_t pkalg[2]; 494215a5deStedu uint8_t kdfalg[2]; 504215a5deStedu uint32_t kdfrounds; 514215a5deStedu uint8_t salt[16]; 524215a5deStedu uint8_t checksum[8]; 531c9c770cStedu uint8_t fingerprint[FPLEN]; 544215a5deStedu uint8_t seckey[SECRETBYTES]; 554215a5deStedu }; 564215a5deStedu 574215a5deStedu struct pubkey { 584215a5deStedu uint8_t pkalg[2]; 591c9c770cStedu uint8_t fingerprint[FPLEN]; 604215a5deStedu uint8_t pubkey[PUBLICBYTES]; 614215a5deStedu }; 624215a5deStedu 634215a5deStedu struct sig { 644215a5deStedu uint8_t pkalg[2]; 651c9c770cStedu uint8_t fingerprint[FPLEN]; 664215a5deStedu uint8_t sig[SIGBYTES]; 674215a5deStedu }; 684215a5deStedu 694215a5deStedu extern char *__progname; 704215a5deStedu 714215a5deStedu static void 72f2adbe28Stedu usage(const char *error) 734215a5deStedu { 74f2adbe28Stedu if (error) 75f2adbe28Stedu fprintf(stderr, "%s\n", error); 7642efb9f2Sespie fprintf(stderr, "usage:" 7735e4c3d2Sespie #ifndef VERIFYONLY 7858559f60Stedu "\t%1$s -C [-q] -p pubkey -x sigfile [files...]\n" 79f2adbe28Stedu "\t%1$s -G [-n] [-c comment] -p pubkey -s seckey\n" 80f2adbe28Stedu "\t%1$s -I [-p pubkey] [-s seckey] [-x sigfile]\n" 81f2adbe28Stedu "\t%1$s -S [-e] [-x sigfile] -s seckey -m message\n" 8235e4c3d2Sespie #endif 8358559f60Stedu "\t%1$s -V [-eq] [-x sigfile] -p pubkey -m message\n", 8435e4c3d2Sespie __progname); 854215a5deStedu exit(1); 864215a5deStedu } 874215a5deStedu 884215a5deStedu static int 894215a5deStedu xopen(const char *fname, int flags, mode_t mode) 904215a5deStedu { 914215a5deStedu int fd; 924215a5deStedu 93f2adbe28Stedu if (strcmp(fname, "-") == 0) { 94f2adbe28Stedu if ((flags & O_WRONLY)) 95f2adbe28Stedu fd = dup(STDOUT_FILENO); 96f2adbe28Stedu else 97f2adbe28Stedu fd = dup(STDIN_FILENO); 98f2adbe28Stedu if (fd == -1) 99f2adbe28Stedu err(1, "dup failed"); 100f2adbe28Stedu } else { 1014215a5deStedu fd = open(fname, flags, mode); 1024215a5deStedu if (fd == -1) 103f2adbe28Stedu err(1, "can't open %s for %s", fname, 104f2adbe28Stedu (flags & O_WRONLY) ? "writing" : "reading"); 105f2adbe28Stedu } 1064215a5deStedu return fd; 1074215a5deStedu } 1084215a5deStedu 1094215a5deStedu static void * 1104215a5deStedu xmalloc(size_t len) 1114215a5deStedu { 1124215a5deStedu void *p; 1134215a5deStedu 1144215a5deStedu p = malloc(len); 1154215a5deStedu if (!p) 1164215a5deStedu err(1, "malloc %zu", len); 1174215a5deStedu return p; 1184215a5deStedu } 1194215a5deStedu 1204215a5deStedu static void 121d59d433dSespie readall(int fd, void *buf, size_t len, const char *filename) 1224215a5deStedu { 1237dec58f2Stedu ssize_t x; 1247dec58f2Stedu 125bcd4d29fSespie while (len != 0) { 1267dec58f2Stedu x = read(fd, buf, len); 127bcd4d29fSespie if (x == -1) 128d59d433dSespie err(1, "read from %s", filename); 129bcd4d29fSespie len -= x; 130bcd4d29fSespie buf = (char*)buf + x; 131bcd4d29fSespie } 132d59d433dSespie } 1334215a5deStedu 13427f66874Stedu static size_t 1351453d2a0Stedu parseb64file(const char *filename, char *b64, void *buf, size_t len, 1361453d2a0Stedu char *comment) 13727f66874Stedu { 13827f66874Stedu int rv; 13927f66874Stedu char *commentend, *b64end; 14027f66874Stedu 14127f66874Stedu commentend = strchr(b64, '\n'); 14227f66874Stedu if (!commentend || commentend - b64 <= COMMENTHDRLEN || 14327f66874Stedu memcmp(b64, COMMENTHDR, COMMENTHDRLEN)) 14427f66874Stedu errx(1, "invalid comment in %s; must start with '%s'", 14527f66874Stedu filename, COMMENTHDR); 1461453d2a0Stedu *commentend = 0; 147ce3e40b1Sderaadt if (comment) { 148ce3e40b1Sderaadt if (strlcpy(comment, b64 + COMMENTHDRLEN, 149ce3e40b1Sderaadt COMMENTMAXLEN) >= COMMENTMAXLEN) 150ce3e40b1Sderaadt err(1, "comment too long"); 151ce3e40b1Sderaadt } 15227f66874Stedu b64end = strchr(commentend + 1, '\n'); 15327f66874Stedu if (!b64end) 15427f66874Stedu errx(1, "missing new line after b64 in %s", filename); 15527f66874Stedu *b64end = 0; 15627f66874Stedu rv = b64_pton(commentend + 1, buf, len); 15727f66874Stedu if (rv != len) 15827f66874Stedu errx(1, "invalid b64 encoding in %s", filename); 15927f66874Stedu if (memcmp(buf, PKALG, 2)) 16027f66874Stedu errx(1, "unsupported file %s", filename); 16127f66874Stedu return b64end - b64 + 1; 16227f66874Stedu } 16327f66874Stedu 1644215a5deStedu static void 1651453d2a0Stedu readb64file(const char *filename, void *buf, size_t len, char *comment) 1664215a5deStedu { 1674215a5deStedu char b64[2048]; 168f030c3d3Stedu int rv, fd; 1694215a5deStedu 1704215a5deStedu fd = xopen(filename, O_RDONLY | O_NOFOLLOW, 0); 1714215a5deStedu memset(b64, 0, sizeof(b64)); 1724215a5deStedu rv = read(fd, b64, sizeof(b64) - 1); 1734215a5deStedu if (rv == -1) 174d59d433dSespie err(1, "read from %s", filename); 1751453d2a0Stedu parseb64file(filename, b64, buf, len, comment); 176c1ca80caStedu explicit_bzero(b64, sizeof(b64)); 1774215a5deStedu close(fd); 1784215a5deStedu } 1794215a5deStedu 180eee7f9deStedu static uint8_t * 1814215a5deStedu readmsg(const char *filename, unsigned long long *msglenp) 1824215a5deStedu { 1834215a5deStedu unsigned long long msglen; 1844215a5deStedu uint8_t *msg; 1854215a5deStedu struct stat sb; 1864215a5deStedu int fd; 1874215a5deStedu 1884215a5deStedu fd = xopen(filename, O_RDONLY | O_NOFOLLOW, 0); 1897dcdad2dSlteo if (fstat(fd, &sb) == -1) 1907dcdad2dSlteo err(1, "fstat on %s", filename); 1919831e76dStedu if (!S_ISREG(sb.st_mode)) 1929831e76dStedu errx(1, "%s must be a regular file", filename); 1934215a5deStedu msglen = sb.st_size; 1944215a5deStedu if (msglen > (1UL << 30)) 1954215a5deStedu errx(1, "msg too large in %s", filename); 196*a40f4206Stedu msg = xmalloc(msglen + 1); 197d59d433dSespie readall(fd, msg, msglen, filename); 198*a40f4206Stedu msg[msglen] = 0; 1994215a5deStedu close(fd); 2004215a5deStedu 2014215a5deStedu *msglenp = msglen; 2024215a5deStedu return msg; 2034215a5deStedu } 2044215a5deStedu 2054215a5deStedu static void 206d59d433dSespie writeall(int fd, const void *buf, size_t len, const char *filename) 2074215a5deStedu { 2087dec58f2Stedu ssize_t x; 2097dec58f2Stedu 210bcd4d29fSespie while (len != 0) { 2117dec58f2Stedu x = write(fd, buf, len); 212bcd4d29fSespie if (x == -1) 213d59d433dSespie err(1, "write to %s", filename); 214bcd4d29fSespie len -= x; 215bcd4d29fSespie buf = (char*)buf + x; 216bcd4d29fSespie } 217d59d433dSespie } 2184215a5deStedu 2196ffce13fSderaadt #ifndef VERIFYONLY 2204215a5deStedu static void 22127f66874Stedu appendall(const char *filename, const void *buf, size_t len) 22227f66874Stedu { 22327f66874Stedu int fd; 22427f66874Stedu 2259831e76dStedu fd = xopen(filename, O_NOFOLLOW | O_WRONLY | O_APPEND, 0); 22627f66874Stedu writeall(fd, buf, len, filename); 22727f66874Stedu close(fd); 22827f66874Stedu } 22927f66874Stedu 23027f66874Stedu static void 2314215a5deStedu writeb64file(const char *filename, const char *comment, const void *buf, 232d609a234Sespie size_t len, int flags, mode_t mode) 2334215a5deStedu { 2344215a5deStedu char header[1024]; 2354215a5deStedu char b64[1024]; 2364215a5deStedu int fd, rv; 2374215a5deStedu 2389831e76dStedu fd = xopen(filename, O_CREAT|flags|O_NOFOLLOW|O_WRONLY, mode); 239ce3e40b1Sderaadt if (snprintf(header, sizeof(header), "%s%s\n", 240ce3e40b1Sderaadt COMMENTHDR, comment) >= sizeof(header)) 241ce3e40b1Sderaadt err(1, "comment too long"); 242d59d433dSespie writeall(fd, header, strlen(header), filename); 243e01c72c4Sespie if ((rv = b64_ntop(buf, len, b64, sizeof(b64)-1)) == -1) 2444215a5deStedu errx(1, "b64 encode failed"); 245e01c72c4Sespie b64[rv++] = '\n'; 246d59d433dSespie writeall(fd, b64, rv, filename); 247c1ca80caStedu explicit_bzero(b64, sizeof(b64)); 2484215a5deStedu close(fd); 2494215a5deStedu } 2504215a5deStedu 2514215a5deStedu static void 2524215a5deStedu kdf(uint8_t *salt, size_t saltlen, int rounds, uint8_t *key, size_t keylen) 2534215a5deStedu { 2544215a5deStedu char pass[1024]; 2554215a5deStedu 2564215a5deStedu if (rounds == 0) { 2574215a5deStedu memset(key, 0, keylen); 2584215a5deStedu return; 2594215a5deStedu } 2604215a5deStedu 26158ac87a3Stedu if (!readpassphrase("passphrase: ", pass, sizeof(pass), RPP_ECHO_OFF)) 26258ac87a3Stedu errx(1, "unable to read passphrase"); 2630e5a52c1Stedu if (strlen(pass) == 0) 2640e5a52c1Stedu errx(1, "please provide a password"); 2654215a5deStedu if (bcrypt_pbkdf(pass, strlen(pass), salt, saltlen, key, 2664215a5deStedu keylen, rounds) == -1) 2674215a5deStedu errx(1, "bcrypt pbkdf"); 268c1ca80caStedu explicit_bzero(pass, sizeof(pass)); 2694215a5deStedu } 2704215a5deStedu 2714215a5deStedu static void 2724215a5deStedu signmsg(uint8_t *seckey, uint8_t *msg, unsigned long long msglen, 2734215a5deStedu uint8_t *sig) 2744215a5deStedu { 2754215a5deStedu unsigned long long siglen; 2764215a5deStedu uint8_t *sigbuf; 2774215a5deStedu 2784215a5deStedu sigbuf = xmalloc(msglen + SIGBYTES); 2794215a5deStedu crypto_sign_ed25519(sigbuf, &siglen, msg, msglen, seckey); 2804215a5deStedu memcpy(sig, sigbuf, SIGBYTES); 2814215a5deStedu free(sigbuf); 2824215a5deStedu } 2834215a5deStedu 2844215a5deStedu static void 285bd7b638bStedu generate(const char *pubkeyfile, const char *seckeyfile, int rounds, 286bd7b638bStedu const char *comment) 2874215a5deStedu { 2884215a5deStedu uint8_t digest[SHA512_DIGEST_LENGTH]; 2894215a5deStedu struct pubkey pubkey; 2904215a5deStedu struct enckey enckey; 2914215a5deStedu uint8_t xorkey[sizeof(enckey.seckey)]; 2921c9c770cStedu uint8_t fingerprint[FPLEN]; 293bd7b638bStedu char commentbuf[COMMENTMAXLEN]; 2944215a5deStedu SHA2_CTX ctx; 2954215a5deStedu int i; 2964215a5deStedu 2974215a5deStedu crypto_sign_ed25519_keypair(pubkey.pubkey, enckey.seckey); 2981c9c770cStedu arc4random_buf(fingerprint, sizeof(fingerprint)); 2994215a5deStedu 3004215a5deStedu SHA512Init(&ctx); 3014215a5deStedu SHA512Update(&ctx, enckey.seckey, sizeof(enckey.seckey)); 3024215a5deStedu SHA512Final(digest, &ctx); 3034215a5deStedu 3044215a5deStedu memcpy(enckey.pkalg, PKALG, 2); 3054215a5deStedu memcpy(enckey.kdfalg, KDFALG, 2); 3064215a5deStedu enckey.kdfrounds = htonl(rounds); 3071c9c770cStedu memcpy(enckey.fingerprint, fingerprint, FPLEN); 3084215a5deStedu arc4random_buf(enckey.salt, sizeof(enckey.salt)); 3094215a5deStedu kdf(enckey.salt, sizeof(enckey.salt), rounds, xorkey, sizeof(xorkey)); 3104215a5deStedu memcpy(enckey.checksum, digest, sizeof(enckey.checksum)); 3114215a5deStedu for (i = 0; i < sizeof(enckey.seckey); i++) 3124215a5deStedu enckey.seckey[i] ^= xorkey[i]; 313c1ca80caStedu explicit_bzero(digest, sizeof(digest)); 314c1ca80caStedu explicit_bzero(xorkey, sizeof(xorkey)); 3154215a5deStedu 316ce3e40b1Sderaadt if (snprintf(commentbuf, sizeof(commentbuf), "%s secret key", 317ce3e40b1Sderaadt comment) >= sizeof(commentbuf)) 318ce3e40b1Sderaadt err(1, "comment too long"); 319bd7b638bStedu writeb64file(seckeyfile, commentbuf, &enckey, 320d609a234Sespie sizeof(enckey), O_EXCL, 0600); 321c1ca80caStedu explicit_bzero(&enckey, sizeof(enckey)); 3224215a5deStedu 3234215a5deStedu memcpy(pubkey.pkalg, PKALG, 2); 3241c9c770cStedu memcpy(pubkey.fingerprint, fingerprint, FPLEN); 325ce3e40b1Sderaadt if (snprintf(commentbuf, sizeof(commentbuf), "%s public key", 326ce3e40b1Sderaadt comment) >= sizeof(commentbuf)) 327ce3e40b1Sderaadt err(1, "comment too long"); 328bd7b638bStedu writeb64file(pubkeyfile, commentbuf, &pubkey, 329d609a234Sespie sizeof(pubkey), O_EXCL, 0666); 3304215a5deStedu } 3314215a5deStedu 3324215a5deStedu static void 33327f66874Stedu sign(const char *seckeyfile, const char *msgfile, const char *sigfile, 33427f66874Stedu int embedded) 3354215a5deStedu { 3364215a5deStedu struct sig sig; 3374215a5deStedu uint8_t digest[SHA512_DIGEST_LENGTH]; 3384215a5deStedu struct enckey enckey; 3394215a5deStedu uint8_t xorkey[sizeof(enckey.seckey)]; 3404215a5deStedu uint8_t *msg; 3411453d2a0Stedu char comment[COMMENTMAXLEN], sigcomment[1024]; 3424215a5deStedu unsigned long long msglen; 3434215a5deStedu int i, rounds; 3444215a5deStedu SHA2_CTX ctx; 3454215a5deStedu 3461453d2a0Stedu readb64file(seckeyfile, &enckey, sizeof(enckey), comment); 3474215a5deStedu 3484215a5deStedu if (memcmp(enckey.kdfalg, KDFALG, 2)) 3494215a5deStedu errx(1, "unsupported KDF"); 3504215a5deStedu rounds = ntohl(enckey.kdfrounds); 3514215a5deStedu kdf(enckey.salt, sizeof(enckey.salt), rounds, xorkey, sizeof(xorkey)); 3524215a5deStedu for (i = 0; i < sizeof(enckey.seckey); i++) 3534215a5deStedu enckey.seckey[i] ^= xorkey[i]; 354c1ca80caStedu explicit_bzero(xorkey, sizeof(xorkey)); 3554215a5deStedu SHA512Init(&ctx); 3564215a5deStedu SHA512Update(&ctx, enckey.seckey, sizeof(enckey.seckey)); 3574215a5deStedu SHA512Final(digest, &ctx); 3584215a5deStedu if (memcmp(enckey.checksum, digest, sizeof(enckey.checksum))) 3594215a5deStedu errx(1, "incorrect passphrase"); 360c1ca80caStedu explicit_bzero(digest, sizeof(digest)); 3614215a5deStedu 36227f66874Stedu msg = readmsg(msgfile, &msglen); 3634215a5deStedu 3644215a5deStedu signmsg(enckey.seckey, msg, msglen, sig.sig); 3651c9c770cStedu memcpy(sig.fingerprint, enckey.fingerprint, FPLEN); 366c1ca80caStedu explicit_bzero(&enckey, sizeof(enckey)); 3674215a5deStedu 3684215a5deStedu memcpy(sig.pkalg, PKALG, 2); 369ce3e40b1Sderaadt if (snprintf(sigcomment, sizeof(sigcomment), "signature from %s", 370ce3e40b1Sderaadt comment) >= sizeof(sigcomment)) 371ce3e40b1Sderaadt err(1, "comment too long"); 372d609a234Sespie writeb64file(sigfile, sigcomment, &sig, sizeof(sig), O_TRUNC, 0666); 37327f66874Stedu if (embedded) 37427f66874Stedu appendall(sigfile, msg, msglen); 3754215a5deStedu 3764215a5deStedu free(msg); 3774215a5deStedu } 37837f70c32Stedu 37937f70c32Stedu static void 38037f70c32Stedu inspect(const char *seckeyfile, const char *pubkeyfile, const char *sigfile) 38137f70c32Stedu { 38237f70c32Stedu struct sig sig; 38337f70c32Stedu struct enckey enckey; 38437f70c32Stedu struct pubkey pubkey; 38537f70c32Stedu char fp[(FPLEN + 2) / 3 * 4 + 1]; 38637f70c32Stedu 38737f70c32Stedu if (seckeyfile) { 38837f70c32Stedu readb64file(seckeyfile, &enckey, sizeof(enckey), NULL); 38937f70c32Stedu b64_ntop(enckey.fingerprint, FPLEN, fp, sizeof(fp)); 39037f70c32Stedu printf("sec fp: %s\n", fp); 39137f70c32Stedu } 39237f70c32Stedu if (pubkeyfile) { 39337f70c32Stedu readb64file(pubkeyfile, &pubkey, sizeof(pubkey), NULL); 39437f70c32Stedu b64_ntop(pubkey.fingerprint, FPLEN, fp, sizeof(fp)); 39537f70c32Stedu printf("pub fp: %s\n", fp); 39637f70c32Stedu } 39737f70c32Stedu if (sigfile) { 39837f70c32Stedu readb64file(sigfile, &sig, sizeof(sig), NULL); 39937f70c32Stedu b64_ntop(sig.fingerprint, FPLEN, fp, sizeof(fp)); 40037f70c32Stedu printf("sig fp: %s\n", fp); 40137f70c32Stedu } 40237f70c32Stedu } 403665ab7d9Stedu #endif 4044215a5deStedu 4054215a5deStedu static void 4061c9c770cStedu verifymsg(uint8_t *pubkey, uint8_t *msg, unsigned long long msglen, 40758559f60Stedu uint8_t *sig, int quiet) 4081c9c770cStedu { 4091c9c770cStedu uint8_t *sigbuf, *dummybuf; 4101c9c770cStedu unsigned long long siglen, dummylen; 4111c9c770cStedu 4121c9c770cStedu siglen = SIGBYTES + msglen; 4131c9c770cStedu sigbuf = xmalloc(siglen); 4141c9c770cStedu dummybuf = xmalloc(siglen); 4151c9c770cStedu memcpy(sigbuf, sig, SIGBYTES); 4161c9c770cStedu memcpy(sigbuf + SIGBYTES, msg, msglen); 4171c9c770cStedu if (crypto_sign_ed25519_open(dummybuf, &dummylen, sigbuf, siglen, 4181c9c770cStedu pubkey) == -1) 4191c9c770cStedu errx(1, "signature verification failed"); 42058559f60Stedu if (!quiet) 42158559f60Stedu printf("Signature Verified\n"); 4221c9c770cStedu free(sigbuf); 4231c9c770cStedu free(dummybuf); 4241c9c770cStedu } 4251c9c770cStedu 4261c9c770cStedu 4271c9c770cStedu static void 42827f66874Stedu verify(const char *pubkeyfile, const char *msgfile, const char *sigfile, 42958559f60Stedu int embedded, int quiet) 4304215a5deStedu { 4314215a5deStedu struct sig sig; 4324215a5deStedu struct pubkey pubkey; 43327f66874Stedu unsigned long long msglen, siglen = 0; 4344215a5deStedu uint8_t *msg; 43527f66874Stedu int fd; 43627f66874Stedu 43727f66874Stedu msg = readmsg(embedded ? sigfile : msgfile, &msglen); 4384215a5deStedu 4391453d2a0Stedu readb64file(pubkeyfile, &pubkey, sizeof(pubkey), NULL); 44027f66874Stedu if (embedded) { 4411453d2a0Stedu siglen = parseb64file(sigfile, msg, &sig, sizeof(sig), NULL); 44227f66874Stedu msg += siglen; 44327f66874Stedu msglen -= siglen; 44427f66874Stedu } else { 4451453d2a0Stedu readb64file(sigfile, &sig, sizeof(sig), NULL); 44627f66874Stedu } 4474215a5deStedu 44837f70c32Stedu if (memcmp(pubkey.fingerprint, sig.fingerprint, FPLEN)) { 44937f70c32Stedu #ifndef VERIFYONLY 45037f70c32Stedu inspect(NULL, pubkeyfile, sigfile); 45137f70c32Stedu #endif 4521c9c770cStedu errx(1, "verification failed: checked against wrong key"); 45337f70c32Stedu } 4541c9c770cStedu 45558559f60Stedu verifymsg(pubkey.pubkey, msg, msglen, sig.sig, quiet); 45627f66874Stedu if (embedded) { 4579831e76dStedu fd = xopen(msgfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666); 45827f66874Stedu writeall(fd, msg, msglen, msgfile); 45927f66874Stedu close(fd); 46027f66874Stedu } 46127f66874Stedu 46227f66874Stedu free(msg - siglen); 4634215a5deStedu } 4644215a5deStedu 46558559f60Stedu #ifndef VERIFYONLY 46658559f60Stedu struct checksum { 46758559f60Stedu char file[1024]; 46858559f60Stedu char hash[1024]; 46958559f60Stedu char algo[256]; 47058559f60Stedu }; 47158559f60Stedu 47258559f60Stedu static void 473*a40f4206Stedu verifychecksums(char *msg, int argc, char **argv, int quiet) 47458559f60Stedu { 47558559f60Stedu char buf[1024]; 476*a40f4206Stedu char *line, *endline; 47758559f60Stedu struct checksum *checksums = NULL, *c = NULL; 47858559f60Stedu int nchecksums = 0; 4798972cca3Stedu int i, j, uselist, count, hasfailed; 48058559f60Stedu int *failures; 48158559f60Stedu 482*a40f4206Stedu line = msg; 48358559f60Stedu while (line && *line) { 48458559f60Stedu if (!(checksums = realloc(checksums, 48558559f60Stedu sizeof(*c) * (nchecksums + 1)))) 48658559f60Stedu err(1, "realloc"); 48758559f60Stedu c = &checksums[nchecksums++]; 48858559f60Stedu if ((endline = strchr(line, '\n'))) 48958559f60Stedu *endline++ = 0; 49058559f60Stedu if (sscanf(line, "%255s %1023s = %1023s", 49158559f60Stedu c->algo, buf, c->hash) != 3 || 49258559f60Stedu buf[0] != '(' || buf[strlen(buf) - 1] != ')') 49358559f60Stedu errx(1, "unable to parse checksum line %s", line); 49458559f60Stedu buf[strlen(buf) - 1] = 0; 49558559f60Stedu strlcpy(c->file, buf + 1, sizeof(c->file)); 49658559f60Stedu line = endline; 49758559f60Stedu } 49858559f60Stedu 49958559f60Stedu if (argc) { 50058559f60Stedu uselist = 0; 50158559f60Stedu count = argc; 50258559f60Stedu } else { 50358559f60Stedu uselist = 1; 50458559f60Stedu count = nchecksums; 50558559f60Stedu } 5068972cca3Stedu if (!(failures = calloc(count, sizeof(int)))) 5078972cca3Stedu err(1, "calloc"); 50858559f60Stedu for (i = 0; i < count; i++) { 50958559f60Stedu if (uselist) { 51058559f60Stedu c = &checksums[i]; 51158559f60Stedu } else { 51258559f60Stedu for (j = 0; j < nchecksums; j++) { 51358559f60Stedu c = &checksums[j]; 51458559f60Stedu if (strcmp(c->file, argv[i]) == 0) 51558559f60Stedu break; 51658559f60Stedu } 51758559f60Stedu if (j == nchecksums) { 51858559f60Stedu failures[i] = 1; 51958559f60Stedu continue; 52058559f60Stedu } 52158559f60Stedu } 52258559f60Stedu 52358559f60Stedu if (strcmp(c->algo, "SHA256") == 0) { 52458559f60Stedu if (!SHA256File(c->file, buf)) { 52558559f60Stedu failures[i] = 1; 52658559f60Stedu continue; 52758559f60Stedu } 52858559f60Stedu } else if (strcmp(c->algo, "SHA512") == 0) { 52958559f60Stedu if (!SHA512File(c->file, buf)) { 53058559f60Stedu failures[i] = 1; 53158559f60Stedu continue; 53258559f60Stedu } 53358559f60Stedu } else { 53458559f60Stedu errx(1, "can't handle algorithm %s", c->algo); 53558559f60Stedu } 53658559f60Stedu if (strcmp(c->hash, buf) != 0) { 53758559f60Stedu failures[i] = 1; 53858559f60Stedu continue; 53958559f60Stedu } 54058559f60Stedu if (!quiet) 54158559f60Stedu printf("%s: OK\n", c->file); 54258559f60Stedu } 5438972cca3Stedu hasfailed = 0; 54458559f60Stedu for (i = 0; i < count; i++) { 54558559f60Stedu if (failures[i]) { 54658559f60Stedu fprintf(stderr, "%s: FAIL\n", 54758559f60Stedu uselist ? checksums[i].file : argv[i]); 5488972cca3Stedu hasfailed = 1; 54958559f60Stedu } 55058559f60Stedu } 5518972cca3Stedu if (hasfailed) 55258559f60Stedu exit(1); 55358559f60Stedu free(checksums); 5548972cca3Stedu free(failures); 55558559f60Stedu } 55658559f60Stedu 55758559f60Stedu static void 55858559f60Stedu check(const char *pubkeyfile, const char *sigfile, int quiet, int argc, 55958559f60Stedu char **argv) 56058559f60Stedu { 56158559f60Stedu struct sig sig; 56258559f60Stedu struct pubkey pubkey; 56358559f60Stedu unsigned long long msglen, siglen; 56458559f60Stedu uint8_t *msg; 56558559f60Stedu 56658559f60Stedu msg = readmsg(sigfile, &msglen); 56758559f60Stedu 56858559f60Stedu readb64file(pubkeyfile, &pubkey, sizeof(pubkey), NULL); 56958559f60Stedu siglen = parseb64file(sigfile, msg, &sig, sizeof(sig), NULL); 57058559f60Stedu msg += siglen; 57158559f60Stedu msglen -= siglen; 57258559f60Stedu 57358559f60Stedu if (memcmp(pubkey.fingerprint, sig.fingerprint, FPLEN)) { 57458559f60Stedu #ifndef VERIFYONLY 57558559f60Stedu inspect(NULL, pubkeyfile, sigfile); 57658559f60Stedu #endif 57758559f60Stedu errx(1, "verification failed: checked against wrong key"); 57858559f60Stedu } 57958559f60Stedu 58058559f60Stedu verifymsg(pubkey.pubkey, msg, msglen, sig.sig, quiet); 581*a40f4206Stedu verifychecksums((char *)msg, argc, argv, quiet); 58258559f60Stedu 58358559f60Stedu free(msg - siglen); 58458559f60Stedu } 58558559f60Stedu #endif 58658559f60Stedu 5874215a5deStedu int 5884215a5deStedu main(int argc, char **argv) 5894215a5deStedu { 59027f66874Stedu const char *pubkeyfile = NULL, *seckeyfile = NULL, *msgfile = NULL, 5914215a5deStedu *sigfile = NULL; 5924215a5deStedu char sigfilebuf[1024]; 593bd7b638bStedu const char *comment = "signify"; 5944215a5deStedu int ch, rounds; 59527f66874Stedu int embedded = 0; 59658559f60Stedu int quiet = 0; 597a6bade58Stedu enum { 598a6bade58Stedu NONE, 59958559f60Stedu CHECK, 600a6bade58Stedu GENERATE, 60137f70c32Stedu INSPECT, 602a6bade58Stedu SIGN, 603a6bade58Stedu VERIFY 604a6bade58Stedu } verb = NONE; 605a6bade58Stedu 6064215a5deStedu 6074215a5deStedu rounds = 42; 6084215a5deStedu 60958559f60Stedu while ((ch = getopt(argc, argv, "CGISVc:em:np:qs:x:")) != -1) { 6104215a5deStedu switch (ch) { 611665ab7d9Stedu #ifndef VERIFYONLY 61258559f60Stedu case 'C': 61358559f60Stedu if (verb) 61458559f60Stedu usage(NULL); 61558559f60Stedu verb = CHECK; 61658559f60Stedu break; 617a6bade58Stedu case 'G': 618a6bade58Stedu if (verb) 619f2adbe28Stedu usage(NULL); 620a6bade58Stedu verb = GENERATE; 6214215a5deStedu break; 62237f70c32Stedu case 'I': 62337f70c32Stedu if (verb) 624f2adbe28Stedu usage(NULL); 62537f70c32Stedu verb = INSPECT; 62637f70c32Stedu break; 6274215a5deStedu case 'S': 628a6bade58Stedu if (verb) 629f2adbe28Stedu usage(NULL); 630a6bade58Stedu verb = SIGN; 6314215a5deStedu break; 632665ab7d9Stedu #endif 6334215a5deStedu case 'V': 634a6bade58Stedu if (verb) 635f2adbe28Stedu usage(NULL); 636a6bade58Stedu verb = VERIFY; 637a6bade58Stedu break; 638bd7b638bStedu case 'c': 639bd7b638bStedu comment = optarg; 640bd7b638bStedu break; 64127f66874Stedu case 'e': 64227f66874Stedu embedded = 1; 64327f66874Stedu break; 644f2adbe28Stedu case 'm': 645f2adbe28Stedu msgfile = optarg; 646f2adbe28Stedu break; 647a6bade58Stedu case 'n': 648a6bade58Stedu rounds = 0; 649a6bade58Stedu break; 650a6bade58Stedu case 'p': 651a6bade58Stedu pubkeyfile = optarg; 652a6bade58Stedu break; 65358559f60Stedu case 'q': 65458559f60Stedu quiet = 1; 65558559f60Stedu break; 656a6bade58Stedu case 's': 657a6bade58Stedu seckeyfile = optarg; 6584215a5deStedu break; 659f2adbe28Stedu case 'x': 660f2adbe28Stedu sigfile = optarg; 661f2adbe28Stedu break; 6624215a5deStedu default: 663f2adbe28Stedu usage(NULL); 6644215a5deStedu break; 6654215a5deStedu } 6664215a5deStedu } 667bcc39c47Stedu argc -= optind; 66842efb9f2Sespie argv += optind; 66942efb9f2Sespie 67058559f60Stedu #ifndef VERIFYONLY 67158559f60Stedu if (verb == CHECK) { 67258559f60Stedu if (!pubkeyfile || !sigfile) 67358559f60Stedu usage("need pubkey and sigfile"); 67458559f60Stedu check(pubkeyfile, sigfile, quiet, argc, argv); 67558559f60Stedu return 0; 67658559f60Stedu } 67758559f60Stedu #endif 67858559f60Stedu 67958559f60Stedu quiet = 1; /* retain quiet default for 5.5 release */ 68058559f60Stedu 681f2adbe28Stedu if (argc != 0) 682f2adbe28Stedu usage(NULL); 683f2adbe28Stedu 6849127dd95Stedu if (!sigfile && msgfile) { 685f2adbe28Stedu if (strcmp(msgfile, "-") == 0) 686f2adbe28Stedu errx(1, "must specify sigfile with - message"); 6874215a5deStedu if (snprintf(sigfilebuf, sizeof(sigfilebuf), "%s.sig", 68827f66874Stedu msgfile) >= sizeof(sigfilebuf)) 6894215a5deStedu errx(1, "path too long"); 6904215a5deStedu sigfile = sigfilebuf; 6914215a5deStedu } 6929127dd95Stedu 6939127dd95Stedu switch (verb) { 694665ab7d9Stedu #ifndef VERIFYONLY 6959127dd95Stedu case GENERATE: 6969127dd95Stedu if (!pubkeyfile || !seckeyfile) 6979127dd95Stedu usage("need pubkey and seckey"); 6989127dd95Stedu generate(pubkeyfile, seckeyfile, rounds, comment); 6999127dd95Stedu break; 7009127dd95Stedu case INSPECT: 7019127dd95Stedu inspect(seckeyfile, pubkeyfile, sigfile); 7029127dd95Stedu break; 7039127dd95Stedu case SIGN: 7049127dd95Stedu if (!msgfile || !seckeyfile) 7059127dd95Stedu usage("need message and seckey"); 70627f66874Stedu sign(seckeyfile, msgfile, sigfile, embedded); 7079127dd95Stedu break; 708665ab7d9Stedu #endif 7099127dd95Stedu case VERIFY: 7109127dd95Stedu if (!msgfile || !pubkeyfile) 7119127dd95Stedu usage("need message and pubkey"); 71258559f60Stedu verify(pubkeyfile, msgfile, sigfile, embedded, quiet); 7139127dd95Stedu break; 7149127dd95Stedu default: 7159127dd95Stedu usage(NULL); 7169127dd95Stedu break; 71742efb9f2Sespie } 71842efb9f2Sespie 7194215a5deStedu return 0; 7204215a5deStedu } 721