GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/signify/signify.c Lines: 0 355 0.0 %
Date: 2016-12-06 Branches: 0 255 0.0 %

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