1 /* $OpenBSD: signify.c,v 1.135 2020/01/21 12:13:21 tb Exp $ */
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 <limits.h>
23 #include <stdint.h>
24 #include <fcntl.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stddef.h>
29 #include <ohash.h>
30 #include <err.h>
31 #include <unistd.h>
32 #include <readpassphrase.h>
33 #include <util.h>
34 #include <sha2.h>
35 
36 #include "crypto_api.h"
37 #include "signify.h"
38 
39 #define SIGBYTES crypto_sign_ed25519_BYTES
40 #define SECRETBYTES crypto_sign_ed25519_SECRETKEYBYTES
41 #define PUBLICBYTES crypto_sign_ed25519_PUBLICKEYBYTES
42 
43 #define PKALG "Ed"
44 #define KDFALG "BK"
45 #define KEYNUMLEN 8
46 
47 #define COMMENTHDR "untrusted comment: "
48 #define COMMENTHDRLEN 19
49 #define COMMENTMAXLEN 1024
50 #define VERIFYWITH "verify with "
51 
52 struct enckey {
53 	uint8_t pkalg[2];
54 	uint8_t kdfalg[2];
55 	uint32_t kdfrounds;
56 	uint8_t salt[16];
57 	uint8_t checksum[8];
58 	uint8_t keynum[KEYNUMLEN];
59 	uint8_t seckey[SECRETBYTES];
60 };
61 
62 struct pubkey {
63 	uint8_t pkalg[2];
64 	uint8_t keynum[KEYNUMLEN];
65 	uint8_t pubkey[PUBLICBYTES];
66 };
67 
68 struct sig {
69 	uint8_t pkalg[2];
70 	uint8_t keynum[KEYNUMLEN];
71 	uint8_t sig[SIGBYTES];
72 };
73 
74 extern char *__progname;
75 
76 static void __dead
usage(const char * error)77 usage(const char *error)
78 {
79 	if (error)
80 		fprintf(stderr, "%s\n", error);
81 	fprintf(stderr, "usage:"
82 #ifndef VERIFYONLY
83 	    "\t%1$s -C [-q] [-p pubkey] [-t keytype] -x sigfile [file ...]\n"
84 	    "\t%1$s -G [-n] [-c comment] -p pubkey -s seckey\n"
85 	    "\t%1$s -S [-enz] [-x sigfile] -s seckey -m message\n"
86 #endif
87 	    "\t%1$s -V [-eqz] [-p pubkey] [-t keytype] [-x sigfile] -m message\n",
88 	    __progname);
89 	exit(1);
90 }
91 
92 int
xopen(const char * fname,int oflags,mode_t mode)93 xopen(const char *fname, int oflags, mode_t mode)
94 {
95 	struct stat sb;
96 	int fd;
97 
98 	if (strcmp(fname, "-") == 0) {
99 		if ((oflags & O_WRONLY))
100 			fd = dup(STDOUT_FILENO);
101 		else
102 			fd = dup(STDIN_FILENO);
103 		if (fd == -1)
104 			err(1, "dup failed");
105 	} else {
106 		fd = open(fname, oflags, mode);
107 		if (fd == -1)
108 			err(1, "can't open %s for %s", fname,
109 			    (oflags & O_WRONLY) ? "writing" : "reading");
110 	}
111 	if (fstat(fd, &sb) == -1 || S_ISDIR(sb.st_mode))
112 		errx(1, "not a valid file: %s", fname);
113 	return fd;
114 }
115 
116 void *
xmalloc(size_t len)117 xmalloc(size_t len)
118 {
119 	void *p;
120 
121 	if (!(p = malloc(len)))
122 		err(1, "malloc %zu", len);
123 	return p;
124 }
125 
126 static size_t
parseb64file(const char * filename,char * b64,void * buf,size_t buflen,char * comment)127 parseb64file(const char *filename, char *b64, void *buf, size_t buflen,
128     char *comment)
129 {
130 	char *commentend, *b64end;
131 
132 	commentend = strchr(b64, '\n');
133 	if (!commentend || commentend - b64 <= COMMENTHDRLEN ||
134 	    memcmp(b64, COMMENTHDR, COMMENTHDRLEN) != 0)
135 		errx(1, "invalid comment in %s; must start with '%s'",
136 		    filename, COMMENTHDR);
137 	*commentend = '\0';
138 	if (comment) {
139 		if (strlcpy(comment, b64 + COMMENTHDRLEN,
140 		    COMMENTMAXLEN) >= COMMENTMAXLEN)
141 			errx(1, "comment too long");
142 	}
143 	if (!(b64end = strchr(commentend + 1, '\n')))
144 		errx(1, "missing new line after base64 in %s", filename);
145 	*b64end = '\0';
146 	if (b64_pton(commentend + 1, buf, buflen) != buflen)
147 		errx(1, "unable to parse %s", filename);
148 	if (memcmp(buf, PKALG, 2) != 0)
149 		errx(1, "unsupported file %s", filename);
150 	*commentend = '\n';
151 	*b64end = '\n';
152 	return b64end - b64 + 1;
153 }
154 
155 static void
readb64file(const char * filename,void * buf,size_t buflen,char * comment)156 readb64file(const char *filename, void *buf, size_t buflen, char *comment)
157 {
158 	char b64[2048];
159 	int rv, fd;
160 
161 	fd = xopen(filename, O_RDONLY | O_NOFOLLOW, 0);
162 	if ((rv = read(fd, b64, sizeof(b64) - 1)) == -1)
163 		err(1, "read from %s", filename);
164 	b64[rv] = '\0';
165 	parseb64file(filename, b64, buf, buflen, comment);
166 	explicit_bzero(b64, sizeof(b64));
167 	close(fd);
168 }
169 
170 static uint8_t *
readmsg(const char * filename,unsigned long long * msglenp)171 readmsg(const char *filename, unsigned long long *msglenp)
172 {
173 	unsigned long long msglen = 0;
174 	uint8_t *msg = NULL;
175 	struct stat sb;
176 	ssize_t x, space;
177 	int fd;
178 	const unsigned long long maxmsgsize = 1UL << 30;
179 
180 	fd = xopen(filename, O_RDONLY | O_NOFOLLOW, 0);
181 	if (fstat(fd, &sb) == 0 && S_ISREG(sb.st_mode)) {
182 		if (sb.st_size > maxmsgsize)
183 			errx(1, "msg too large in %s", filename);
184 		space = sb.st_size + 1;
185 	} else {
186 		space = 64 * 1024 - 1;
187 	}
188 
189 	msg = xmalloc(space + 1);
190 	while (1) {
191 		if (space == 0) {
192 			if (msglen * 2 > maxmsgsize)
193 				errx(1, "msg too large in %s", filename);
194 			space = msglen;
195 			if (!(msg = realloc(msg, msglen + space + 1)))
196 				err(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 
206 	msg[msglen] = '\0';
207 	close(fd);
208 
209 	*msglenp = msglen;
210 	return msg;
211 }
212 
213 void
writeall(int fd,const void * buf,size_t buflen,const char * filename)214 writeall(int fd, const void *buf, size_t buflen, const char *filename)
215 {
216 	ssize_t x;
217 
218 	while (buflen != 0) {
219 		if ((x = write(fd, buf, buflen)) == -1)
220 			err(1, "write to %s", filename);
221 		buflen -= x;
222 		buf = (char *)buf + x;
223 	}
224 }
225 
226 #ifndef VERIFYONLY
227 static char *
createheader(const char * comment,const void * buf,size_t buflen)228 createheader(const char *comment, const void *buf, size_t buflen)
229 {
230 	char *header;
231 	char b64[1024];
232 
233 	if (b64_ntop(buf, buflen, b64, sizeof(b64)) == -1)
234 		errx(1, "base64 encode failed");
235 	if (asprintf(&header, "%s%s\n%s\n", COMMENTHDR, comment, b64) == -1)
236 		err(1, "asprintf failed");
237 	explicit_bzero(b64, sizeof(b64));
238 	return header;
239 }
240 
241 static void
writekeyfile(const char * filename,const char * comment,const void * buf,size_t buflen,int oflags,mode_t mode)242 writekeyfile(const char *filename, const char *comment, const void *buf,
243     size_t buflen, int oflags, mode_t mode)
244 {
245 	char *header;
246 	int fd;
247 
248 	fd = xopen(filename, O_CREAT|oflags|O_NOFOLLOW|O_WRONLY, mode);
249 	header = createheader(comment, buf, buflen);
250 	writeall(fd, header, strlen(header), filename);
251 	freezero(header, strlen(header));
252 	close(fd);
253 }
254 
255 static void
kdf(uint8_t * salt,size_t saltlen,int rounds,int allowstdin,int confirm,uint8_t * key,size_t keylen)256 kdf(uint8_t *salt, size_t saltlen, int rounds, int allowstdin, int confirm,
257     uint8_t *key, size_t keylen)
258 {
259 	char pass[1024];
260 	int rppflags = RPP_ECHO_OFF;
261 	const char *errstr = NULL;
262 
263 	if (rounds == 0) {
264 		memset(key, 0, keylen);
265 		return;
266 	}
267 
268 	if (allowstdin && !isatty(STDIN_FILENO))
269 		rppflags |= RPP_STDIN;
270 	if (!readpassphrase("passphrase: ", pass, sizeof(pass), rppflags))
271 		errx(1, "unable to read passphrase");
272 	if (strlen(pass) == 0)
273 		errx(1, "please provide a password");
274 	if (confirm && !(rppflags & RPP_STDIN)) {
275 		char pass2[1024];
276 		if (!readpassphrase("confirm passphrase: ", pass2,
277 		    sizeof(pass2), rppflags))
278 			errstr = "unable to read passphrase";
279 		if (!errstr && strcmp(pass, pass2) != 0)
280 			errstr = "passwords don't match";
281 		explicit_bzero(pass2, sizeof(pass2));
282 	}
283 	if (!errstr && bcrypt_pbkdf(pass, strlen(pass), salt, saltlen, key,
284 	    keylen, rounds) == -1)
285 		errstr = "bcrypt pbkdf";
286 	explicit_bzero(pass, sizeof(pass));
287 	if (errstr)
288 		errx(1, "%s", errstr);
289 }
290 
291 static void
signmsg(uint8_t * seckey,uint8_t * msg,unsigned long long msglen,uint8_t * sig)292 signmsg(uint8_t *seckey, uint8_t *msg, unsigned long long msglen,
293     uint8_t *sig)
294 {
295 	unsigned long long siglen;
296 	uint8_t *sigbuf;
297 
298 	sigbuf = xmalloc(msglen + SIGBYTES);
299 	crypto_sign_ed25519(sigbuf, &siglen, msg, msglen, seckey);
300 	memcpy(sig, sigbuf, SIGBYTES);
301 	free(sigbuf);
302 }
303 
304 static void
generate(const char * pubkeyfile,const char * seckeyfile,int rounds,const char * comment)305 generate(const char *pubkeyfile, const char *seckeyfile, int rounds,
306     const char *comment)
307 {
308 	uint8_t digest[SHA512_DIGEST_LENGTH];
309 	struct pubkey pubkey;
310 	struct enckey enckey;
311 	uint8_t xorkey[sizeof(enckey.seckey)];
312 	uint8_t keynum[KEYNUMLEN];
313 	char commentbuf[COMMENTMAXLEN];
314 	SHA2_CTX ctx;
315 	int i, nr;
316 
317 	crypto_sign_ed25519_keypair(pubkey.pubkey, enckey.seckey);
318 	arc4random_buf(keynum, sizeof(keynum));
319 
320 	SHA512Init(&ctx);
321 	SHA512Update(&ctx, enckey.seckey, sizeof(enckey.seckey));
322 	SHA512Final(digest, &ctx);
323 
324 	memcpy(enckey.pkalg, PKALG, 2);
325 	memcpy(enckey.kdfalg, KDFALG, 2);
326 	enckey.kdfrounds = htonl(rounds);
327 	memcpy(enckey.keynum, keynum, KEYNUMLEN);
328 	arc4random_buf(enckey.salt, sizeof(enckey.salt));
329 	kdf(enckey.salt, sizeof(enckey.salt), rounds, 1, 1, xorkey, sizeof(xorkey));
330 	memcpy(enckey.checksum, digest, sizeof(enckey.checksum));
331 	for (i = 0; i < sizeof(enckey.seckey); i++)
332 		enckey.seckey[i] ^= xorkey[i];
333 	explicit_bzero(digest, sizeof(digest));
334 	explicit_bzero(xorkey, sizeof(xorkey));
335 
336 	nr = snprintf(commentbuf, sizeof(commentbuf), "%s secret key", comment);
337 	if (nr < 0 || nr >= sizeof(commentbuf))
338 		errx(1, "comment too long");
339 	writekeyfile(seckeyfile, commentbuf, &enckey,
340 	    sizeof(enckey), O_EXCL, 0600);
341 	explicit_bzero(&enckey, sizeof(enckey));
342 
343 	memcpy(pubkey.pkalg, PKALG, 2);
344 	memcpy(pubkey.keynum, keynum, KEYNUMLEN);
345 	nr = snprintf(commentbuf, sizeof(commentbuf), "%s public key", comment);
346 	if (nr < 0 || nr >= sizeof(commentbuf))
347 		errx(1, "comment too long");
348 	writekeyfile(pubkeyfile, commentbuf, &pubkey,
349 	    sizeof(pubkey), O_EXCL, 0666);
350 }
351 
352 static void
check_keyname_compliance(const char * pubkeyfile,const char * seckeyfile)353 check_keyname_compliance(const char *pubkeyfile, const char *seckeyfile)
354 {
355 	const char *pos;
356 	size_t len;
357 
358 	/* basename may or may not modify input */
359 	pos = strrchr(seckeyfile, '/');
360 	if (pos != NULL)
361 		seckeyfile = pos + 1;
362 
363 	len = strlen(seckeyfile);
364 	if (len < 5) /* ?.key */
365 		goto bad;
366 	if (strcmp(pubkeyfile + len - 4, ".pub") != 0 ||
367 	    strcmp(seckeyfile + len - 4, ".sec") != 0)
368 		goto bad;
369 	if (strncmp(pubkeyfile, seckeyfile, len - 4) != 0)
370 		goto bad;
371 	if (pubkeyfile != NULL) {
372 		pos = strrchr(pubkeyfile, '/');
373 		if (pos != NULL)
374 			pubkeyfile = pos + 1;
375 
376 		if (strlen(pubkeyfile) != len)
377 			goto bad;
378 		if (strcmp(pubkeyfile + len - 4, ".pub") != 0)
379 			goto bad;
380 		if (strncmp(pubkeyfile, seckeyfile, len - 4) != 0)
381 			goto bad;
382 	}
383 
384 	return;
385 bad:
386 	errx(1, "please use naming scheme of keyname.pub and keyname.sec");
387 }
388 
389 uint8_t *
createsig(const char * seckeyfile,const char * msgfile,uint8_t * msg,unsigned long long msglen)390 createsig(const char *seckeyfile, const char *msgfile, uint8_t *msg,
391     unsigned long long msglen)
392 {
393 	struct enckey enckey;
394 	uint8_t xorkey[sizeof(enckey.seckey)];
395 	struct sig sig;
396 	char *sighdr;
397 	char *extname;
398 	uint8_t digest[SHA512_DIGEST_LENGTH];
399 	int i, nr, rounds;
400 	SHA2_CTX ctx;
401 	char comment[COMMENTMAXLEN], sigcomment[COMMENTMAXLEN];
402 
403 	readb64file(seckeyfile, &enckey, sizeof(enckey), comment);
404 
405 	extname = strrchr(seckeyfile, '.');
406 	if (extname && strcmp(extname, ".sec") == 0) {
407 		const char *keyname;
408 		/* basename may or may not modify input */
409 		if (!(keyname = strrchr(seckeyfile, '/')))
410 			keyname = seckeyfile;
411 		else
412 			keyname++;
413 		nr = snprintf(sigcomment, sizeof(sigcomment),
414 		    VERIFYWITH "%.*s.pub", (int)strlen(keyname) - 4, keyname);
415 		if (nr == -1 || nr >= sizeof(sigcomment))
416 			errx(1, "comment too long");
417 	} else {
418 		nr = snprintf(sigcomment, sizeof(sigcomment),
419 		    "signature from %s", comment);
420 		if (nr == -1 || nr >= sizeof(sigcomment))
421 			errx(1, "comment too long");
422 	}
423 
424 	if (memcmp(enckey.kdfalg, KDFALG, 2) != 0)
425 		errx(1, "unsupported KDF");
426 	rounds = ntohl(enckey.kdfrounds);
427 	kdf(enckey.salt, sizeof(enckey.salt), rounds, strcmp(msgfile, "-") != 0,
428 	    0, xorkey, sizeof(xorkey));
429 	for (i = 0; i < sizeof(enckey.seckey); i++)
430 		enckey.seckey[i] ^= xorkey[i];
431 	explicit_bzero(xorkey, sizeof(xorkey));
432 	SHA512Init(&ctx);
433 	SHA512Update(&ctx, enckey.seckey, sizeof(enckey.seckey));
434 	SHA512Final(digest, &ctx);
435 	if (memcmp(enckey.checksum, digest, sizeof(enckey.checksum)) != 0)
436 		errx(1, "incorrect passphrase");
437 	explicit_bzero(digest, sizeof(digest));
438 
439 	signmsg(enckey.seckey, msg, msglen, sig.sig);
440 	memcpy(sig.keynum, enckey.keynum, KEYNUMLEN);
441 	explicit_bzero(&enckey, sizeof(enckey));
442 
443 	memcpy(sig.pkalg, PKALG, 2);
444 
445 	sighdr = createheader(sigcomment, &sig, sizeof(sig));
446 	return sighdr;
447 }
448 
449 static void
sign(const char * seckeyfile,const char * msgfile,const char * sigfile,int embedded)450 sign(const char *seckeyfile, const char *msgfile, const char *sigfile,
451     int embedded)
452 {
453 	uint8_t *msg;
454 	char *sighdr;
455 	int fd;
456 	unsigned long long msglen;
457 
458 	msg = readmsg(msgfile, &msglen);
459 
460 	sighdr = createsig(seckeyfile, msgfile, msg, msglen);
461 
462 	fd = xopen(sigfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666);
463 	writeall(fd, sighdr, strlen(sighdr), sigfile);
464 	free(sighdr);
465 	if (embedded)
466 		writeall(fd, msg, msglen, sigfile);
467 	close(fd);
468 
469 	free(msg);
470 }
471 #endif
472 
473 static void
verifymsg(struct pubkey * pubkey,uint8_t * msg,unsigned long long msglen,struct sig * sig,int quiet)474 verifymsg(struct pubkey *pubkey, uint8_t *msg, unsigned long long msglen,
475     struct sig *sig, int quiet)
476 {
477 	uint8_t *sigbuf, *dummybuf;
478 	unsigned long long siglen, dummylen;
479 
480 	if (memcmp(pubkey->keynum, sig->keynum, KEYNUMLEN) != 0)
481 		errx(1, "verification failed: checked against wrong key");
482 
483 	siglen = SIGBYTES + msglen;
484 	sigbuf = xmalloc(siglen);
485 	dummybuf = xmalloc(siglen);
486 	memcpy(sigbuf, sig->sig, SIGBYTES);
487 	memcpy(sigbuf + SIGBYTES, msg, msglen);
488 	if (crypto_sign_ed25519_open(dummybuf, &dummylen, sigbuf, siglen,
489 	    pubkey->pubkey) == -1)
490 		errx(1, "signature verification failed");
491 	if (!quiet)
492 		printf("Signature Verified\n");
493 	free(sigbuf);
494 	free(dummybuf);
495 }
496 
497 static void
check_keytype(const char * pubkeyfile,const char * keytype)498 check_keytype(const char *pubkeyfile, const char *keytype)
499 {
500 	const char *p;
501 	size_t typelen;
502 
503 	if (!(p = strrchr(pubkeyfile, '-')))
504 		goto bad;
505 	p++;
506 	typelen = strlen(keytype);
507 	if (strncmp(p, keytype, typelen) != 0)
508 		goto bad;
509 	if (strcmp(p + typelen, ".pub") != 0)
510 		goto bad;
511 	return;
512 
513 bad:
514 	errx(1, "incorrect keytype: %s is not %s", pubkeyfile, keytype);
515 }
516 
517 static void
readpubkey(const char * pubkeyfile,struct pubkey * pubkey,const char * sigcomment,const char * keytype)518 readpubkey(const char *pubkeyfile, struct pubkey *pubkey,
519     const char *sigcomment, const char *keytype)
520 {
521 	const char *safepath = "/usr/local/etc/signify";
522 	char keypath[PATH_MAX];
523 
524 	if (!pubkeyfile) {
525 		pubkeyfile = strstr(sigcomment, VERIFYWITH);
526 		if (pubkeyfile && strchr(pubkeyfile, '/') == NULL) {
527 			pubkeyfile += strlen(VERIFYWITH);
528 			if (keytype)
529 				check_keytype(pubkeyfile, keytype);
530 			if (snprintf(keypath, sizeof(keypath), "%s/%s",
531 			    safepath, pubkeyfile) >= sizeof(keypath))
532 				errx(1, "name too long %s", pubkeyfile);
533 			pubkeyfile = keypath;
534 		} else
535 			usage("must specify pubkey");
536 	}
537 	readb64file(pubkeyfile, pubkey, sizeof(*pubkey), NULL);
538 }
539 
540 static void
verifysimple(const char * pubkeyfile,const char * msgfile,const char * sigfile,int quiet,const char * keytype)541 verifysimple(const char *pubkeyfile, const char *msgfile, const char *sigfile,
542     int quiet, const char *keytype)
543 {
544 	char sigcomment[COMMENTMAXLEN];
545 	struct sig sig;
546 	struct pubkey pubkey;
547 	unsigned long long msglen;
548 	uint8_t *msg;
549 
550 	msg = readmsg(msgfile, &msglen);
551 
552 	readb64file(sigfile, &sig, sizeof(sig), sigcomment);
553 	readpubkey(pubkeyfile, &pubkey, sigcomment, keytype);
554 
555 	verifymsg(&pubkey, msg, msglen, &sig, quiet);
556 
557 	free(msg);
558 }
559 
560 static uint8_t *
verifyembedded(const char * pubkeyfile,const char * sigfile,int quiet,unsigned long long * msglenp,const char * keytype)561 verifyembedded(const char *pubkeyfile, const char *sigfile,
562     int quiet, unsigned long long *msglenp, const char *keytype)
563 {
564 	char sigcomment[COMMENTMAXLEN];
565 	struct sig sig;
566 	struct pubkey pubkey;
567 	unsigned long long msglen, siglen;
568 	uint8_t *msg;
569 
570 	msg = readmsg(sigfile, &msglen);
571 
572 	siglen = parseb64file(sigfile, msg, &sig, sizeof(sig), sigcomment);
573 	readpubkey(pubkeyfile, &pubkey, sigcomment, keytype);
574 
575 	msglen -= siglen;
576 	memmove(msg, msg + siglen, msglen);
577 	msg[msglen] = 0;
578 
579 	verifymsg(&pubkey, msg, msglen, &sig, quiet);
580 
581 	*msglenp = msglen;
582 	return msg;
583 }
584 
585 static void
verify(const char * pubkeyfile,const char * msgfile,const char * sigfile,int embedded,int quiet,const char * keytype)586 verify(const char *pubkeyfile, const char *msgfile, const char *sigfile,
587     int embedded, int quiet, const char *keytype)
588 {
589 	unsigned long long msglen;
590 	uint8_t *msg;
591 	int fd;
592 
593 	if (embedded) {
594 		msg = verifyembedded(pubkeyfile, sigfile, quiet, &msglen,
595 		    keytype);
596 		fd = xopen(msgfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666);
597 		writeall(fd, msg, msglen, msgfile);
598 		free(msg);
599 		close(fd);
600 	} else {
601 		verifysimple(pubkeyfile, msgfile, sigfile, quiet, keytype);
602 	}
603 }
604 
605 #ifndef VERIFYONLY
606 #define HASHBUFSIZE 224
607 struct checksum {
608 	char file[PATH_MAX];
609 	char hash[HASHBUFSIZE];
610 	char algo[32];
611 };
612 
613 static void *
ecalloc(size_t s1,size_t s2,void * data)614 ecalloc(size_t s1, size_t s2, void *data)
615 {
616 	void *p;
617 
618 	if (!(p = calloc(s1, s2)))
619 		err(1, "calloc");
620 	return p;
621 }
622 
623 static void
efree(void * p,void * data)624 efree(void *p, void *data)
625 {
626 	free(p);
627 }
628 
629 static void
recodehash(char * hash,size_t len)630 recodehash(char *hash, size_t len)
631 {
632 	uint8_t data[HASHBUFSIZE / 2];
633 	int i, rv;
634 
635 	if (strlen(hash) == len)
636 		return;
637 	if ((rv = b64_pton(hash, data, sizeof(data))) == -1)
638 		errx(1, "invalid base64 encoding");
639 	for (i = 0; i < rv; i++)
640 		snprintf(hash + i * 2, HASHBUFSIZE - i * 2, "%2.2x", data[i]);
641 }
642 
643 static int
verifychecksum(struct checksum * c,int quiet)644 verifychecksum(struct checksum *c, int quiet)
645 {
646 	char buf[HASHBUFSIZE];
647 
648 	if (strcmp(c->algo, "SHA256") == 0) {
649 		recodehash(c->hash, SHA256_DIGEST_STRING_LENGTH-1);
650 		if (!SHA256File(c->file, buf))
651 			return 0;
652 	} else if (strcmp(c->algo, "SHA512") == 0) {
653 		recodehash(c->hash, SHA512_DIGEST_STRING_LENGTH-1);
654 		if (!SHA512File(c->file, buf))
655 			return 0;
656 	} else {
657 		errx(1, "can't handle algorithm %s", c->algo);
658 	}
659 	if (strcmp(c->hash, buf) != 0)
660 		return 0;
661 	if (!quiet)
662 		printf("%s: OK\n", c->file);
663 	return 1;
664 }
665 
666 static void
verifychecksums(char * msg,int argc,char ** argv,int quiet)667 verifychecksums(char *msg, int argc, char **argv, int quiet)
668 {
669 	struct ohash_info info = { 0, NULL, ecalloc, efree, NULL };
670 	struct ohash myh;
671 	struct checksum c;
672 	char *e, *line, *endline;
673 	int hasfailed = 0;
674 	int i, rv;
675 	unsigned int slot;
676 
677 	ohash_init(&myh, 6, &info);
678 	if (argc) {
679 		for (i = 0; i < argc; i++) {
680 			slot = ohash_qlookup(&myh, argv[i]);
681 			e = ohash_find(&myh, slot);
682 			if (e == NULL)
683 				ohash_insert(&myh, slot, argv[i]);
684 		}
685 	}
686 
687 	line = msg;
688 	while (line && *line) {
689 		if ((endline = strchr(line, '\n')))
690 			*endline++ = '\0';
691 #if PATH_MAX < 1024 || HASHBUFSIZE < 224
692 #error sizes are wrong
693 #endif
694 		rv = sscanf(line, "%31s (%1023[^)]) = %223s",
695 		    c.algo, c.file, c.hash);
696 		if (rv != 3)
697 			errx(1, "unable to parse checksum line %s", line);
698 		line = endline;
699 		if (argc) {
700 			slot = ohash_qlookup(&myh, c.file);
701 			e = ohash_find(&myh, slot);
702 			if (e != NULL) {
703 				if (verifychecksum(&c, quiet) != 0)
704 					ohash_remove(&myh, slot);
705 			}
706 		} else {
707 			if (verifychecksum(&c, quiet) == 0) {
708 				slot = ohash_qlookup(&myh, c.file);
709 				e = ohash_find(&myh, slot);
710 				if (e == NULL) {
711 					if (!(e = strdup(c.file)))
712 						err(1, "strdup");
713 					ohash_insert(&myh, slot, e);
714 				}
715 			}
716 		}
717 	}
718 
719 	for (e = ohash_first(&myh, &slot); e != NULL; e = ohash_next(&myh, &slot)) {
720 		fprintf(stderr, "%s: FAIL\n", e);
721 		hasfailed = 1;
722 		if (argc == 0)
723 			free(e);
724 	}
725 	ohash_delete(&myh);
726 	if (hasfailed)
727 		exit(1);
728 }
729 
730 static void
check(const char * pubkeyfile,const char * sigfile,const char * keytype,int quiet,int argc,char ** argv)731 check(const char *pubkeyfile, const char *sigfile, const char *keytype,
732     int quiet, int argc, char **argv)
733 {
734 	unsigned long long msglen;
735 	uint8_t *msg;
736 
737 	msg = verifyembedded(pubkeyfile, sigfile, quiet, &msglen, keytype);
738 	verifychecksums((char *)msg, argc, argv, quiet);
739 
740 	free(msg);
741 }
742 
743 void *
verifyzdata(uint8_t * zdata,unsigned long long zdatalen,const char * filename,const char * pubkeyfile,const char * keytype)744 verifyzdata(uint8_t *zdata, unsigned long long zdatalen,
745     const char *filename, const char *pubkeyfile, const char *keytype)
746 {
747 	struct sig sig;
748 	char sigcomment[COMMENTMAXLEN];
749 	unsigned long long siglen;
750 	struct pubkey pubkey;
751 
752 	if (zdatalen < sizeof(sig))
753 		errx(1, "signature too short in %s", filename);
754 	siglen = parseb64file(filename, zdata, &sig, sizeof(sig),
755 	    sigcomment);
756 	readpubkey(pubkeyfile, &pubkey, sigcomment, keytype);
757 	zdata += siglen;
758 	zdatalen -= siglen;
759 	verifymsg(&pubkey, zdata, zdatalen, &sig, 1);
760 	return zdata;
761 }
762 #endif
763 
764 int
main(int argc,char ** argv)765 main(int argc, char **argv)
766 {
767 	const char *pubkeyfile = NULL, *seckeyfile = NULL, *msgfile = NULL,
768 	    *sigfile = NULL;
769 	char sigfilebuf[PATH_MAX];
770 	const char *comment = "signify";
771 	char *keytype = NULL;
772 	int ch;
773 	int none = 0;
774 	int embedded = 0;
775 	int quiet = 0;
776 	int gzip = 0;
777 	enum {
778 		NONE,
779 		CHECK,
780 		GENERATE,
781 		SIGN,
782 		VERIFY
783 	} verb = NONE;
784 
785 	if (pledge("stdio rpath wpath cpath tty", NULL) == -1)
786 		err(1, "pledge");
787 
788 	while ((ch = getopt(argc, argv, "CGSVzc:em:np:qs:t:x:")) != -1) {
789 		switch (ch) {
790 #ifndef VERIFYONLY
791 		case 'C':
792 			if (verb)
793 				usage(NULL);
794 			verb = CHECK;
795 			break;
796 		case 'G':
797 			if (verb)
798 				usage(NULL);
799 			verb = GENERATE;
800 			break;
801 		case 'S':
802 			if (verb)
803 				usage(NULL);
804 			verb = SIGN;
805 			break;
806 		case 'z':
807 			gzip = 1;
808 			break;
809 #endif
810 		case 'V':
811 			if (verb)
812 				usage(NULL);
813 			verb = VERIFY;
814 			break;
815 		case 'c':
816 			comment = optarg;
817 			break;
818 		case 'e':
819 			embedded = 1;
820 			break;
821 		case 'm':
822 			msgfile = optarg;
823 			break;
824 		case 'n':
825 			none = 1;
826 			break;
827 		case 'p':
828 			pubkeyfile = optarg;
829 			break;
830 		case 'q':
831 			quiet = 1;
832 			break;
833 		case 's':
834 			seckeyfile = optarg;
835 			break;
836 		case 't':
837 			keytype = optarg;
838 			break;
839 		case 'x':
840 			sigfile = optarg;
841 			break;
842 		default:
843 			usage(NULL);
844 			break;
845 		}
846 	}
847 	argc -= optind;
848 	argv += optind;
849 
850 	if (embedded && gzip)
851 		errx(1, "can't combine -e and -z options");
852 
853 	if (setvbuf(stdout, NULL, _IOLBF, 0) != 0)
854 		err(1, "setvbuf");
855 
856 #ifndef VERIFYONLY
857 	if (verb == CHECK) {
858 		if (pledge("stdio rpath", NULL) == -1)
859 			err(1, "pledge");
860 		if (!sigfile)
861 			usage("must specify sigfile");
862 		check(pubkeyfile, sigfile, keytype, quiet, argc, argv);
863 		return 0;
864 	}
865 #endif
866 
867 	if (argc != 0)
868 		usage(NULL);
869 
870 	if (!sigfile && msgfile) {
871 		int nr;
872 		if (strcmp(msgfile, "-") == 0)
873 			usage("must specify sigfile with - message");
874 		nr = snprintf(sigfilebuf, sizeof(sigfilebuf),
875 		    "%s.sig", msgfile);
876 		if (nr < 0 || nr >= sizeof(sigfilebuf))
877 			errx(1, "path too long");
878 		sigfile = sigfilebuf;
879 	}
880 
881 	switch (verb) {
882 #ifndef VERIFYONLY
883 	case GENERATE:
884 		/* no pledge */
885 		if (!pubkeyfile || !seckeyfile)
886 			usage("must specify pubkey and seckey");
887 		check_keyname_compliance(pubkeyfile, seckeyfile);
888 		generate(pubkeyfile, seckeyfile, none ? 0 : 42, comment);
889 		break;
890 	case SIGN:
891 		/* no pledge */
892 		if (gzip) {
893 			if (!msgfile || !seckeyfile || !sigfile)
894 				usage("must specify message sigfile seckey");
895 			zsign(seckeyfile, msgfile, sigfile, none);
896 		} else {
897 			if (!msgfile || !seckeyfile)
898 				usage("must specify message and seckey");
899 			sign(seckeyfile, msgfile, sigfile, embedded);
900 		}
901 		break;
902 #endif
903 	case VERIFY:
904 		if ((embedded || gzip) &&
905 		    (msgfile && strcmp(msgfile, "-") != 0)) {
906 			/* will need to create output file */
907 			if (pledge("stdio rpath wpath cpath", NULL) == -1)
908 				err(1, "pledge");
909 		} else {
910 			if (pledge("stdio rpath", NULL) == -1)
911 				err(1, "pledge");
912 		}
913 		if (gzip) {
914 			zverify(pubkeyfile, msgfile, sigfile, keytype);
915 		} else {
916 			if (!msgfile)
917 				usage("must specify message");
918 			verify(pubkeyfile, msgfile, sigfile, embedded,
919 			    quiet, keytype);
920 		}
921 		break;
922 	default:
923 		if (pledge("stdio", NULL) == -1)
924 			err(1, "pledge");
925 		usage(NULL);
926 		break;
927 	}
928 
929 	return 0;
930 }
931