Annotation of src/usr.bin/signify/signify.c, Revision 1.11
1.11 ! tedu 1: /* $OpenBSD: signify.c,v 1.10 2014/01/03 17:13:42 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>
26: #include <err.h>
27: #include <unistd.h>
28: #include <readpassphrase.h>
29: #include <util.h>
30: #include <sha2.h>
31:
32: #include "crypto_api.h"
33:
34: #define streq(a, b) (strcmp(a, b) == 0)
35:
36: #define SIGBYTES crypto_sign_ed25519_BYTES
37: #define SECRETBYTES crypto_sign_ed25519_SECRETKEYBYTES
38: #define PUBLICBYTES crypto_sign_ed25519_PUBLICKEYBYTES
39:
40: #define PKALG "Ed"
41: #define KDFALG "BK"
42:
43: struct enckey {
44: uint8_t pkalg[2];
45: uint8_t kdfalg[2];
46: uint32_t kdfrounds;
47: uint8_t salt[16];
48: uint8_t checksum[8];
49: uint8_t seckey[SECRETBYTES];
50: };
51:
52: struct pubkey {
53: uint8_t pkalg[2];
54: uint8_t pubkey[PUBLICBYTES];
55: };
56:
57: struct sig {
58: uint8_t pkalg[2];
59: uint8_t sig[SIGBYTES];
60: };
61:
62: extern char *__progname;
63:
64: static void
65: usage(void)
66: {
1.9 espie 67: fprintf(stderr, "usage:"
68: "\t%s [-n] -p pubkey -s seckey -G\n"
69: "\t%s [-o output] -s seckey -S input\n"
70: "\t%s [-o output] -p pubkey -V input\n",
71: __progname, __progname, __progname);
1.1 tedu 72: exit(1);
73: }
74:
75: static int
76: xopen(const char *fname, int flags, mode_t mode)
77: {
78: int fd;
79:
80: fd = open(fname, flags, mode);
81: if (fd == -1)
82: err(1, "open %s", fname);
83: return fd;
84: }
85:
86: static void *
87: xmalloc(size_t len)
88: {
89: void *p;
90:
91: p = malloc(len);
92: if (!p)
93: err(1, "malloc %zu", len);
94: return p;
95: }
96:
97: static void
1.7 espie 98: readall(int fd, void *buf, size_t len, const char *filename)
1.1 tedu 99: {
1.11 ! tedu 100: ssize_t x;
! 101:
! 102: x = read(fd, buf, len);
1.7 espie 103: if (x == -1) {
104: err(1, "read from %s", filename);
105: } else if (x != len) {
106: errx(1, "short read from %s", filename);
107: }
1.1 tedu 108: }
109:
110: static void
111: readb64file(const char *filename, void *buf, size_t len)
112: {
113: char b64[2048];
1.10 tedu 114: int rv, fd;
115: char *commentend;
1.1 tedu 116:
117: fd = xopen(filename, O_RDONLY | O_NOFOLLOW, 0);
118: memset(b64, 0, sizeof(b64));
119: rv = read(fd, b64, sizeof(b64) - 1);
120: if (rv == -1)
1.7 espie 121: err(1, "read from %s", filename);
1.10 tedu 122: commentend = strchr(b64, '\n');
123: if (!commentend)
1.1 tedu 124: errx(1, "no newline in %s", filename);
1.10 tedu 125: rv = b64_pton(commentend + 1, buf, len);
1.1 tedu 126: if (rv != len)
127: errx(1, "invalid b64 encoding in %s", filename);
128: memset(b64, 0, sizeof(b64));
129: close(fd);
130: if (memcmp(buf, PKALG, 2))
131: errx(1, "unsupported file %s", filename);
132: }
133:
134: uint8_t *
135: readmsg(const char *filename, unsigned long long *msglenp)
136: {
137: unsigned long long msglen;
138: uint8_t *msg;
139: struct stat sb;
140: int fd;
141:
142: fd = xopen(filename, O_RDONLY | O_NOFOLLOW, 0);
143: fstat(fd, &sb);
144: msglen = sb.st_size;
145: if (msglen > (1UL << 30))
146: errx(1, "msg too large in %s", filename);
147: msg = xmalloc(msglen);
1.7 espie 148: readall(fd, msg, msglen, filename);
1.1 tedu 149: close(fd);
150:
151: *msglenp = msglen;
152: return msg;
153: }
154:
155: static void
1.7 espie 156: writeall(int fd, const void *buf, size_t len, const char *filename)
1.1 tedu 157: {
1.11 ! tedu 158: ssize_t x;
! 159:
! 160: x = write(fd, buf, len);
1.7 espie 161: if (x == -1) {
162: err(1, "write to %s", filename);
163: } else if (x != len) {
164: errx(1, "short write to %s", filename);
165: }
1.1 tedu 166: }
167:
168: static void
169: writeb64file(const char *filename, const char *comment, const void *buf,
170: size_t len, mode_t mode)
171: {
172: char header[1024];
173: char b64[1024];
174: int fd, rv;
175:
176: fd = xopen(filename, O_CREAT|O_EXCL|O_NOFOLLOW|O_RDWR, mode);
177: snprintf(header, sizeof(header), "signify -- %s\n", comment);
1.7 espie 178: writeall(fd, header, strlen(header), filename);
1.8 espie 179: if ((rv = b64_ntop(buf, len, b64, sizeof(b64)-1)) == -1)
1.1 tedu 180: errx(1, "b64 encode failed");
1.8 espie 181: b64[rv++] = '\n';
1.7 espie 182: writeall(fd, b64, rv, filename);
1.1 tedu 183: memset(b64, 0, sizeof(b64));
184: close(fd);
185: }
186:
187: static void
188: kdf(uint8_t *salt, size_t saltlen, int rounds, uint8_t *key, size_t keylen)
189: {
190: char pass[1024];
191:
192: if (rounds == 0) {
193: memset(key, 0, keylen);
194: return;
195: }
196:
197: if (!readpassphrase("passphrase: ", pass, sizeof(pass), 0))
198: errx(1, "readpassphrase");
199: if (bcrypt_pbkdf(pass, strlen(pass), salt, saltlen, key,
200: keylen, rounds) == -1)
201: errx(1, "bcrypt pbkdf");
202: memset(pass, 0, sizeof(pass));
203: }
204:
205: static void
206: signmsg(uint8_t *seckey, uint8_t *msg, unsigned long long msglen,
207: uint8_t *sig)
208: {
209: unsigned long long siglen;
210: uint8_t *sigbuf;
211:
212: sigbuf = xmalloc(msglen + SIGBYTES);
213: crypto_sign_ed25519(sigbuf, &siglen, msg, msglen, seckey);
214: memcpy(sig, sigbuf, SIGBYTES);
215: free(sigbuf);
216: }
217:
218: static void
219: verifymsg(uint8_t *pubkey, uint8_t *msg, unsigned long long msglen,
220: uint8_t *sig)
221: {
222: uint8_t *sigbuf, *dummybuf;
223: unsigned long long siglen, dummylen;
224:
225: siglen = SIGBYTES + msglen;
226: sigbuf = xmalloc(siglen);
227: dummybuf = xmalloc(siglen);
228: memcpy(sigbuf, sig, SIGBYTES);
229: memcpy(sigbuf + SIGBYTES, msg, msglen);
230: if (crypto_sign_ed25519_open(dummybuf, &dummylen, sigbuf, siglen,
231: pubkey) == -1)
232: errx(1, "signature failed");
233: free(sigbuf);
234: free(dummybuf);
235: }
236:
237: static void
238: generate(const char *pubkeyfile, const char *seckeyfile, int rounds)
239: {
240: uint8_t digest[SHA512_DIGEST_LENGTH];
241: struct pubkey pubkey;
242: struct enckey enckey;
243: uint8_t xorkey[sizeof(enckey.seckey)];
244: SHA2_CTX ctx;
245: int i;
246:
247: crypto_sign_ed25519_keypair(pubkey.pubkey, enckey.seckey);
248:
249: SHA512Init(&ctx);
250: SHA512Update(&ctx, enckey.seckey, sizeof(enckey.seckey));
251: SHA512Final(digest, &ctx);
252:
253: memcpy(enckey.pkalg, PKALG, 2);
254: memcpy(enckey.kdfalg, KDFALG, 2);
255: enckey.kdfrounds = htonl(rounds);
256: arc4random_buf(enckey.salt, sizeof(enckey.salt));
257: kdf(enckey.salt, sizeof(enckey.salt), rounds, xorkey, sizeof(xorkey));
258: memcpy(enckey.checksum, digest, sizeof(enckey.checksum));
259: for (i = 0; i < sizeof(enckey.seckey); i++)
260: enckey.seckey[i] ^= xorkey[i];
261: memset(digest, 0, sizeof(digest));
262: memset(xorkey, 0, sizeof(xorkey));
263:
264: writeb64file(seckeyfile, "secret key", &enckey,
265: sizeof(enckey), 0600);
266: memset(&enckey, 0, sizeof(enckey));
267:
268: memcpy(pubkey.pkalg, PKALG, 2);
269: writeb64file(pubkeyfile, "public key", &pubkey,
270: sizeof(pubkey), 0666);
271: }
272:
273: static void
274: sign(const char *seckeyfile, const char *inputfile, const char *sigfile)
275: {
276: struct sig sig;
277: uint8_t digest[SHA512_DIGEST_LENGTH];
278: struct enckey enckey;
279: uint8_t xorkey[sizeof(enckey.seckey)];
280: uint8_t *msg;
281: unsigned long long msglen;
282: int i, rounds;
283: SHA2_CTX ctx;
284:
285: readb64file(seckeyfile, &enckey, sizeof(enckey));
286:
287: if (memcmp(enckey.kdfalg, KDFALG, 2))
288: errx(1, "unsupported KDF");
289: rounds = ntohl(enckey.kdfrounds);
290: kdf(enckey.salt, sizeof(enckey.salt), rounds, xorkey, sizeof(xorkey));
291: for (i = 0; i < sizeof(enckey.seckey); i++)
292: enckey.seckey[i] ^= xorkey[i];
293: memset(xorkey, 0, sizeof(xorkey));
294: SHA512Init(&ctx);
295: SHA512Update(&ctx, enckey.seckey, sizeof(enckey.seckey));
296: SHA512Final(digest, &ctx);
297: if (memcmp(enckey.checksum, digest, sizeof(enckey.checksum)))
298: errx(1, "incorrect passphrase");
299: memset(digest, 0, sizeof(digest));
300:
301: msg = readmsg(inputfile, &msglen);
302:
303: signmsg(enckey.seckey, msg, msglen, sig.sig);
304: memset(&enckey, 0, sizeof(enckey));
305:
306: memcpy(sig.pkalg, PKALG, 2);
307: writeb64file(sigfile, "signature", &sig, sizeof(sig), 0666);
308:
309: free(msg);
310: }
311:
312: static void
313: verify(const char *pubkeyfile, const char *inputfile, const char *sigfile)
314: {
315: struct sig sig;
316: struct pubkey pubkey;
317: unsigned long long msglen;
318: uint8_t *msg;
319:
320: readb64file(pubkeyfile, &pubkey, sizeof(pubkey));
321: readb64file(sigfile, &sig, sizeof(sig));
322:
323: msg = readmsg(inputfile, &msglen);
324:
325: verifymsg(pubkey.pubkey, msg, msglen, sig.sig);
326: printf("verified\n");
327:
328: free(msg);
329: }
330:
331: int
332: main(int argc, char **argv)
333: {
334: const char *pubkeyfile = NULL, *seckeyfile = NULL, *inputfile = NULL,
335: *sigfile = NULL;
336: char sigfilebuf[1024];
337: int ch, rounds;
1.6 tedu 338: enum {
339: NONE,
340: GENERATE,
341: SIGN,
342: VERIFY
343: } verb = NONE;
344:
1.1 tedu 345:
346: rounds = 42;
347:
1.9 espie 348: while ((ch = getopt(argc, argv, "GSVno:p:s:")) != -1) {
1.1 tedu 349: switch (ch) {
1.6 tedu 350: case 'G':
351: if (verb)
352: usage();
353: verb = GENERATE;
354: break;
355: case 'S':
356: if (verb)
357: usage();
358: verb = SIGN;
359: break;
360: case 'V':
361: if (verb)
362: usage();
363: verb = VERIFY;
364: break;
365: case 'n':
1.1 tedu 366: rounds = 0;
367: break;
1.6 tedu 368: case 'o':
1.1 tedu 369: sigfile = optarg;
370: break;
1.6 tedu 371: case 'p':
1.1 tedu 372: pubkeyfile = optarg;
373: break;
1.6 tedu 374: case 's':
1.1 tedu 375: seckeyfile = optarg;
376: break;
377: default:
378: usage();
379: break;
380: }
381: }
1.2 tedu 382: argc -= optind;
1.9 espie 383: argv += optind;
384:
385: if (verb == NONE)
1.1 tedu 386: usage();
387:
1.6 tedu 388: if (verb == GENERATE) {
1.9 espie 389: if (!pubkeyfile || !seckeyfile || argc != 0)
1.1 tedu 390: usage();
391: generate(pubkeyfile, seckeyfile, rounds);
1.9 espie 392: } else {
393: if (argc != 1)
1.1 tedu 394: usage();
1.9 espie 395:
396: inputfile = argv[0];
397:
398: if (!sigfile) {
399: if (snprintf(sigfilebuf, sizeof(sigfilebuf), "%s.sig",
400: inputfile) >= sizeof(sigfilebuf))
401: errx(1, "path too long");
402: sigfile = sigfilebuf;
403: }
404:
405: if (verb == SIGN) {
406: if (!seckeyfile)
407: usage();
408: sign(seckeyfile, inputfile, sigfile);
409: } else if (verb == VERIFY) {
410: if (!pubkeyfile)
411: usage();
412: verify(pubkeyfile, inputfile, sigfile);
413: }
1.1 tedu 414: }
1.9 espie 415:
1.1 tedu 416: return 0;
417: }