Annotation of src/usr.bin/signify/signify.c, Revision 1.3
1.3 ! tedu 1: /* $OpenBSD: signify.c,v 1.2 2013/12/31 03:08:48 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.3 ! tedu 67: fprintf(stderr, "usage: %s [-N] [-I input] [-P pubkey] [-S seckey] "
! 68: "-V generate|sign|verify\n", __progname);
1.1 tedu 69: exit(1);
70: }
71:
72: static int
73: xopen(const char *fname, int flags, mode_t mode)
74: {
75: int fd;
76:
77: fd = open(fname, flags, mode);
78: if (fd == -1)
79: err(1, "open %s", fname);
80: return fd;
81: }
82:
83: static void *
84: xmalloc(size_t len)
85: {
86: void *p;
87:
88: p = malloc(len);
89: if (!p)
90: err(1, "malloc %zu", len);
91: return p;
92: }
93:
94: static void
95: readall(int fd, void *buf, size_t len)
96: {
97: if (read(fd, buf, len) != len)
98: err(1, "read");
99: }
100:
101: static void
102: readb64file(const char *filename, void *buf, size_t len)
103: {
104: char b64[2048];
105: int i, rv, fd;
106:
107: fd = xopen(filename, O_RDONLY | O_NOFOLLOW, 0);
108: memset(b64, 0, sizeof(b64));
109: rv = read(fd, b64, sizeof(b64) - 1);
110: if (rv == -1)
111: err(1, "read in %s", filename);
112: for (i = 0; i < rv; i++)
113: if (b64[i] == '\n')
114: break;
115: if (i == rv)
116: errx(1, "no newline in %s", filename);
117: rv = b64_pton(b64 + i, buf, len);
118: if (rv != len)
119: errx(1, "invalid b64 encoding in %s", filename);
120: memset(b64, 0, sizeof(b64));
121: close(fd);
122: if (memcmp(buf, PKALG, 2))
123: errx(1, "unsupported file %s", filename);
124: }
125:
126: uint8_t *
127: readmsg(const char *filename, unsigned long long *msglenp)
128: {
129: unsigned long long msglen;
130: uint8_t *msg;
131: struct stat sb;
132: int fd;
133:
134: fd = xopen(filename, O_RDONLY | O_NOFOLLOW, 0);
135: fstat(fd, &sb);
136: msglen = sb.st_size;
137: if (msglen > (1UL << 30))
138: errx(1, "msg too large in %s", filename);
139: msg = xmalloc(msglen);
140: readall(fd, msg, msglen);
141: close(fd);
142:
143: *msglenp = msglen;
144: return msg;
145: }
146:
147: static void
148: writeall(int fd, const void *buf, size_t len)
149: {
150: if (write(fd, buf, len) != len)
151: err(1, "write");
152: }
153:
154: static void
155: writeb64file(const char *filename, const char *comment, const void *buf,
156: size_t len, mode_t mode)
157: {
158: char header[1024];
159: char b64[1024];
160: int fd, rv;
161:
162: fd = xopen(filename, O_CREAT|O_EXCL|O_NOFOLLOW|O_RDWR, mode);
163: snprintf(header, sizeof(header), "signify -- %s\n", comment);
164: writeall(fd, header, strlen(header));
165: if ((rv = b64_ntop(buf, len, b64, sizeof(b64))) == -1)
166: errx(1, "b64 encode failed");
167: writeall(fd, b64, rv);
168: memset(b64, 0, sizeof(b64));
169: close(fd);
170: }
171:
172: static void
173: kdf(uint8_t *salt, size_t saltlen, int rounds, uint8_t *key, size_t keylen)
174: {
175: char pass[1024];
176:
177: if (rounds == 0) {
178: memset(key, 0, keylen);
179: return;
180: }
181:
182: if (!readpassphrase("passphrase: ", pass, sizeof(pass), 0))
183: errx(1, "readpassphrase");
184: if (bcrypt_pbkdf(pass, strlen(pass), salt, saltlen, key,
185: keylen, rounds) == -1)
186: errx(1, "bcrypt pbkdf");
187: memset(pass, 0, sizeof(pass));
188: }
189:
190: static void
191: signmsg(uint8_t *seckey, uint8_t *msg, unsigned long long msglen,
192: uint8_t *sig)
193: {
194: unsigned long long siglen;
195: uint8_t *sigbuf;
196:
197: sigbuf = xmalloc(msglen + SIGBYTES);
198: crypto_sign_ed25519(sigbuf, &siglen, msg, msglen, seckey);
199: memcpy(sig, sigbuf, SIGBYTES);
200: free(sigbuf);
201: }
202:
203: static void
204: verifymsg(uint8_t *pubkey, uint8_t *msg, unsigned long long msglen,
205: uint8_t *sig)
206: {
207: uint8_t *sigbuf, *dummybuf;
208: unsigned long long siglen, dummylen;
209:
210: siglen = SIGBYTES + msglen;
211: sigbuf = xmalloc(siglen);
212: dummybuf = xmalloc(siglen);
213: memcpy(sigbuf, sig, SIGBYTES);
214: memcpy(sigbuf + SIGBYTES, msg, msglen);
215: if (crypto_sign_ed25519_open(dummybuf, &dummylen, sigbuf, siglen,
216: pubkey) == -1)
217: errx(1, "signature failed");
218: free(sigbuf);
219: free(dummybuf);
220: }
221:
222: static void
223: generate(const char *pubkeyfile, const char *seckeyfile, int rounds)
224: {
225: uint8_t digest[SHA512_DIGEST_LENGTH];
226: struct pubkey pubkey;
227: struct enckey enckey;
228: uint8_t xorkey[sizeof(enckey.seckey)];
229: SHA2_CTX ctx;
230: int i;
231:
232: crypto_sign_ed25519_keypair(pubkey.pubkey, enckey.seckey);
233:
234: SHA512Init(&ctx);
235: SHA512Update(&ctx, enckey.seckey, sizeof(enckey.seckey));
236: SHA512Final(digest, &ctx);
237:
238: memcpy(enckey.pkalg, PKALG, 2);
239: memcpy(enckey.kdfalg, KDFALG, 2);
240: enckey.kdfrounds = htonl(rounds);
241: arc4random_buf(enckey.salt, sizeof(enckey.salt));
242: kdf(enckey.salt, sizeof(enckey.salt), rounds, xorkey, sizeof(xorkey));
243: memcpy(enckey.checksum, digest, sizeof(enckey.checksum));
244: for (i = 0; i < sizeof(enckey.seckey); i++)
245: enckey.seckey[i] ^= xorkey[i];
246: memset(digest, 0, sizeof(digest));
247: memset(xorkey, 0, sizeof(xorkey));
248:
249: writeb64file(seckeyfile, "secret key", &enckey,
250: sizeof(enckey), 0600);
251: memset(&enckey, 0, sizeof(enckey));
252:
253: memcpy(pubkey.pkalg, PKALG, 2);
254: writeb64file(pubkeyfile, "public key", &pubkey,
255: sizeof(pubkey), 0666);
256: }
257:
258: static void
259: sign(const char *seckeyfile, const char *inputfile, const char *sigfile)
260: {
261: struct sig sig;
262: uint8_t digest[SHA512_DIGEST_LENGTH];
263: struct enckey enckey;
264: uint8_t xorkey[sizeof(enckey.seckey)];
265: uint8_t *msg;
266: unsigned long long msglen;
267: int i, rounds;
268: SHA2_CTX ctx;
269:
270: readb64file(seckeyfile, &enckey, sizeof(enckey));
271:
272: if (memcmp(enckey.kdfalg, KDFALG, 2))
273: errx(1, "unsupported KDF");
274: rounds = ntohl(enckey.kdfrounds);
275: kdf(enckey.salt, sizeof(enckey.salt), rounds, xorkey, sizeof(xorkey));
276: for (i = 0; i < sizeof(enckey.seckey); i++)
277: enckey.seckey[i] ^= xorkey[i];
278: memset(xorkey, 0, sizeof(xorkey));
279: SHA512Init(&ctx);
280: SHA512Update(&ctx, enckey.seckey, sizeof(enckey.seckey));
281: SHA512Final(digest, &ctx);
282: if (memcmp(enckey.checksum, digest, sizeof(enckey.checksum)))
283: errx(1, "incorrect passphrase");
284: memset(digest, 0, sizeof(digest));
285:
286: msg = readmsg(inputfile, &msglen);
287:
288: signmsg(enckey.seckey, msg, msglen, sig.sig);
289: memset(&enckey, 0, sizeof(enckey));
290:
291: memcpy(sig.pkalg, PKALG, 2);
292: writeb64file(sigfile, "signature", &sig, sizeof(sig), 0666);
293:
294: free(msg);
295: }
296:
297: static void
298: verify(const char *pubkeyfile, const char *inputfile, const char *sigfile)
299: {
300: struct sig sig;
301: struct pubkey pubkey;
302: unsigned long long msglen;
303: uint8_t *msg;
304:
305: readb64file(pubkeyfile, &pubkey, sizeof(pubkey));
306: readb64file(sigfile, &sig, sizeof(sig));
307:
308: msg = readmsg(inputfile, &msglen);
309:
310: verifymsg(pubkey.pubkey, msg, msglen, sig.sig);
311: printf("verified\n");
312:
313: free(msg);
314: }
315:
316: int
317: main(int argc, char **argv)
318: {
319: const char *verb = NULL;
320: const char *pubkeyfile = NULL, *seckeyfile = NULL, *inputfile = NULL,
321: *sigfile = NULL;
322: char sigfilebuf[1024];
323: int ch, rounds;
324:
325: rounds = 42;
326:
1.2 tedu 327: while ((ch = getopt(argc, argv, "I:NO:P:S:V:")) != -1) {
1.1 tedu 328: switch (ch) {
329: case 'I':
330: inputfile = optarg;
331: break;
332: case 'N':
333: rounds = 0;
334: break;
335: case 'O':
336: sigfile = optarg;
337: break;
338: case 'P':
339: pubkeyfile = optarg;
340: break;
341: case 'S':
342: seckeyfile = optarg;
343: break;
344: case 'V':
345: verb = optarg;
346: break;
347: default:
348: usage();
349: break;
350: }
351: }
1.2 tedu 352: argc -= optind;
1.1 tedu 353: if (argc != 0)
354: usage();
355:
356: if (inputfile && !sigfile) {
357: if (snprintf(sigfilebuf, sizeof(sigfilebuf), "%s.sig",
1.2 tedu 358: inputfile) >= sizeof(sigfilebuf))
1.1 tedu 359: errx(1, "path too long");
360: sigfile = sigfilebuf;
361: }
362:
363: if (streq(verb, "generate")) {
364: if (!pubkeyfile || !seckeyfile)
365: usage();
366: generate(pubkeyfile, seckeyfile, rounds);
367: } else if (streq(verb, "sign")) {
368: if (!seckeyfile || !inputfile)
369: usage();
370: sign(seckeyfile, inputfile, sigfile);
371: } else if (streq(verb, "verify")) {
372: if (!pubkeyfile || !inputfile)
373: usage();
374: verify(pubkeyfile, inputfile, sigfile);
375: } else {
376: usage();
377: }
378: return 0;
379: }