GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/signify/signify.c Lines: 292 460 63.5 %
Date: 2017-11-13 Branches: 120 269 44.6 %

Line Branch Exec Source
1
/* $OpenBSD: signify.c,v 1.128 2017/07/11 23:27:13 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
#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
static void __dead
75
usage(const char *error)
76
{
77
	if (error)
78
		fprintf(stderr, "%s\n", error);
79
	fprintf(stderr, "usage:"
80
#ifndef VERIFYONLY
81
	    "\t%1$s -C [-q] -p pubkey -x sigfile [file ...]\n"
82
	    "\t%1$s -G [-n] [-c comment] -p pubkey -s seckey\n"
83
	    "\t%1$s -S [-ez] [-x sigfile] -s seckey -m message\n"
84
#endif
85
	    "\t%1$s -V [-eqz] [-p pubkey] [-t keytype] [-x sigfile] -m message\n",
86
	    getprogname());
87
	exit(1);
88
}
89
90
int
91
xopen(const char *fname, int oflags, mode_t mode)
92
{
93
214
	struct stat sb;
94
	int fd;
95
96
107
	if (strcmp(fname, "-") == 0) {
97
14
		if ((oflags & O_WRONLY))
98
5
			fd = dup(STDOUT_FILENO);
99
		else
100
9
			fd = dup(STDIN_FILENO);
101
14
		if (fd == -1)
102
			err(1, "dup failed");
103
	} else {
104
93
		fd = open(fname, oflags, mode);
105
93
		if (fd == -1)
106
			err(1, "can't open %s for %s", fname,
107
			    (oflags & O_WRONLY) ? "writing" : "reading");
108
	}
109

214
	if (fstat(fd, &sb) == -1 || S_ISDIR(sb.st_mode))
110
		errx(1, "not a valid file: %s", fname);
111
107
	return fd;
112
107
}
113
114
void *
115
xmalloc(size_t len)
116
{
117
	void *p;
118
119
216
	if (!(p = malloc(len)))
120
		err(1, "malloc %zu", len);
121
108
	return p;
122
}
123
124
static size_t
125
parseb64file(const char *filename, char *b64, void *buf, size_t buflen,
126
    char *comment)
127
{
128
	char *commentend, *b64end;
129
130
116
	commentend = strchr(b64, '\n');
131

174
	if (!commentend || commentend - b64 <= COMMENTHDRLEN ||
132
58
	    memcmp(b64, COMMENTHDR, COMMENTHDRLEN) != 0)
133
		errx(1, "invalid comment in %s; must start with '%s'",
134
		    filename, COMMENTHDR);
135
58
	*commentend = '\0';
136
58
	if (comment) {
137
74
		if (strlcpy(comment, b64 + COMMENTHDRLEN,
138
37
		    COMMENTMAXLEN) >= COMMENTMAXLEN)
139
			errx(1, "comment too long");
140
	}
141
58
	if (!(b64end = strchr(commentend + 1, '\n')))
142
		errx(1, "missing new line after base64 in %s", filename);
143
58
	*b64end = '\0';
144
58
	if (b64_pton(commentend + 1, buf, buflen) != buflen)
145
		errx(1, "invalid base64 encoding in %s", filename);
146
58
	if (memcmp(buf, PKALG, 2) != 0)
147
		errx(1, "unsupported file %s", filename);
148
58
	return b64end - b64 + 1;
149
}
150
151
static void
152
readb64file(const char *filename, void *buf, size_t buflen, char *comment)
153
{
154
90
	char b64[2048];
155
	int rv, fd;
156
157
45
	fd = xopen(filename, O_RDONLY | O_NOFOLLOW, 0);
158
45
	if ((rv = read(fd, b64, sizeof(b64) - 1)) == -1)
159
		err(1, "read from %s", filename);
160
45
	b64[rv] = '\0';
161
45
	parseb64file(filename, b64, buf, buflen, comment);
162
45
	explicit_bzero(b64, sizeof(b64));
163
45
	close(fd);
164
45
}
165
166
static uint8_t *
167
readmsg(const char *filename, unsigned long long *msglenp)
168
{
169
	unsigned long long msglen = 0;
170
	uint8_t *msg = NULL;
171
56
	struct stat sb;
172
	ssize_t x, space;
173
	int fd;
174
	const unsigned long long maxmsgsize = 1UL << 30;
175
176
28
	fd = xopen(filename, O_RDONLY | O_NOFOLLOW, 0);
177

56
	if (fstat(fd, &sb) == 0 && S_ISREG(sb.st_mode)) {
178
28
		if (sb.st_size > maxmsgsize)
179
			errx(1, "msg too large in %s", filename);
180
28
		space = sb.st_size + 1;
181
28
	} else {
182
		space = 64 * 1024 - 1;
183
	}
184
185
28
	msg = xmalloc(space + 1);
186
56
	while (1) {
187
56
		if (space == 0) {
188
			if (msglen * 2 > maxmsgsize)
189
				errx(1, "msg too large in %s", filename);
190
			space = msglen;
191
			if (!(msg = realloc(msg, msglen + space + 1)))
192
				err(1, "realloc");
193
		}
194
56
		if ((x = read(fd, msg + msglen, space)) == -1)
195
			err(1, "read from %s", filename);
196
56
		if (x == 0)
197
			break;
198
28
		space -= x;
199
28
		msglen += x;
200
	}
201
202
28
	msg[msglen] = '\0';
203
28
	close(fd);
204
205
28
	*msglenp = msglen;
206
28
	return msg;
207
28
}
208
209
void
210
writeall(int fd, const void *buf, size_t buflen, const char *filename)
211
{
212
	ssize_t x;
213
214
275
	while (buflen != 0) {
215
55
		if ((x = write(fd, buf, buflen)) == -1)
216
			err(1, "write to %s", filename);
217
55
		buflen -= x;
218
55
		buf = (char *)buf + x;
219
	}
220
55
}
221
222
#ifndef VERIFYONLY
223
static char *
224
createheader(const char *comment, const void *buf, size_t buflen)
225
{
226
32
	char *header;
227
16
	char b64[1024];
228
229
16
	if (b64_ntop(buf, buflen, b64, sizeof(b64)) == -1)
230
		errx(1, "base64 encode failed");
231
16
	if (asprintf(&header, "%s%s\n%s\n", COMMENTHDR, comment, b64) == -1)
232
		err(1, "asprintf failed");
233
16
	explicit_bzero(b64, sizeof(b64));
234
32
	return header;
235
16
}
236
237
static void
238
writekeyfile(const char *filename, const char *comment, const void *buf,
239
    size_t buflen, int oflags, mode_t mode)
240
{
241
	char *header;
242
	int fd;
243
244
	fd = xopen(filename, O_CREAT|oflags|O_NOFOLLOW|O_WRONLY, mode);
245
	header = createheader(comment, buf, buflen);
246
	writeall(fd, header, strlen(header), filename);
247
	freezero(header, strlen(header));
248
	close(fd);
249
}
250
251
static void
252
kdf(uint8_t *salt, size_t saltlen, int rounds, int allowstdin, int confirm,
253
    uint8_t *key, size_t keylen)
254
{
255
32
	char pass[1024];
256
	int rppflags = RPP_ECHO_OFF;
257
258
16
	if (rounds == 0) {
259
16
		memset(key, 0, keylen);
260
16
		return;
261
	}
262
263
	if (allowstdin && !isatty(STDIN_FILENO))
264
		rppflags |= RPP_STDIN;
265
	if (!readpassphrase("passphrase: ", pass, sizeof(pass), rppflags))
266
		errx(1, "unable to read passphrase");
267
	if (strlen(pass) == 0)
268
		errx(1, "please provide a password");
269
	if (confirm && !(rppflags & RPP_STDIN)) {
270
		char pass2[1024];
271
		if (!readpassphrase("confirm passphrase: ", pass2,
272
		    sizeof(pass2), rppflags))
273
			errx(1, "unable to read passphrase");
274
		if (strcmp(pass, pass2) != 0)
275
			errx(1, "passwords don't match");
276
		explicit_bzero(pass2, sizeof(pass2));
277
	}
278
	if (bcrypt_pbkdf(pass, strlen(pass), salt, saltlen, key,
279
	    keylen, rounds) == -1)
280
		errx(1, "bcrypt pbkdf");
281
	explicit_bzero(pass, sizeof(pass));
282
16
}
283
284
static void
285
signmsg(uint8_t *seckey, uint8_t *msg, unsigned long long msglen,
286
    uint8_t *sig)
287
{
288
32
	unsigned long long siglen;
289
	uint8_t *sigbuf;
290
291
16
	sigbuf = xmalloc(msglen + SIGBYTES);
292
16
	crypto_sign_ed25519(sigbuf, &siglen, msg, msglen, seckey);
293
16
	memcpy(sig, sigbuf, SIGBYTES);
294
16
	free(sigbuf);
295
16
}
296
297
static void
298
generate(const char *pubkeyfile, const char *seckeyfile, int rounds,
299
    const char *comment)
300
{
301
	uint8_t digest[SHA512_DIGEST_LENGTH];
302
	struct pubkey pubkey;
303
	struct enckey enckey;
304
	uint8_t xorkey[sizeof(enckey.seckey)];
305
	uint8_t keynum[KEYNUMLEN];
306
	char commentbuf[COMMENTMAXLEN];
307
	SHA2_CTX ctx;
308
	int i, nr;
309
310
	crypto_sign_ed25519_keypair(pubkey.pubkey, enckey.seckey);
311
	arc4random_buf(keynum, sizeof(keynum));
312
313
	SHA512Init(&ctx);
314
	SHA512Update(&ctx, enckey.seckey, sizeof(enckey.seckey));
315
	SHA512Final(digest, &ctx);
316
317
	memcpy(enckey.pkalg, PKALG, 2);
318
	memcpy(enckey.kdfalg, KDFALG, 2);
319
	enckey.kdfrounds = htonl(rounds);
320
	memcpy(enckey.keynum, keynum, KEYNUMLEN);
321
	arc4random_buf(enckey.salt, sizeof(enckey.salt));
322
	kdf(enckey.salt, sizeof(enckey.salt), rounds, 1, 1, xorkey, sizeof(xorkey));
323
	memcpy(enckey.checksum, digest, sizeof(enckey.checksum));
324
	for (i = 0; i < sizeof(enckey.seckey); i++)
325
		enckey.seckey[i] ^= xorkey[i];
326
	explicit_bzero(digest, sizeof(digest));
327
	explicit_bzero(xorkey, sizeof(xorkey));
328
329
	nr = snprintf(commentbuf, sizeof(commentbuf), "%s secret key", comment);
330
	if (nr == -1 || nr >= sizeof(commentbuf))
331
		errx(1, "comment too long");
332
	writekeyfile(seckeyfile, commentbuf, &enckey,
333
	    sizeof(enckey), O_EXCL, 0600);
334
	explicit_bzero(&enckey, sizeof(enckey));
335
336
	memcpy(pubkey.pkalg, PKALG, 2);
337
	memcpy(pubkey.keynum, keynum, KEYNUMLEN);
338
	nr = snprintf(commentbuf, sizeof(commentbuf), "%s public key", comment);
339
	if (nr == -1 || nr >= sizeof(commentbuf))
340
		errx(1, "comment too long");
341
	writekeyfile(pubkeyfile, commentbuf, &pubkey,
342
	    sizeof(pubkey), O_EXCL, 0666);
343
}
344
345
static const char *
346
check_keyname_compliance(const char *pubkeyfile, const char *seckeyfile)
347
{
348
	const char *pos;
349
	size_t len;
350
351
	/* basename may or may not modify input */
352
24
	pos = strrchr(seckeyfile, '/');
353
12
	if (pos != NULL)
354
12
		seckeyfile = pos + 1;
355
356
12
	len = strlen(seckeyfile);
357
12
	if (len < 5) /* ?.key */
358
		goto bad;
359
12
	if (strcmp(seckeyfile + len - 4, ".sec") != 0)
360
		goto bad;
361
12
	if (pubkeyfile != NULL) {
362
		pos = strrchr(pubkeyfile, '/');
363
		if (pos != NULL)
364
			pubkeyfile = pos + 1;
365
366
		if (strlen(pubkeyfile) != len)
367
			goto bad;
368
		if (strcmp(pubkeyfile + len - 4, ".pub") != 0)
369
			goto bad;
370
		if (strncmp(pubkeyfile, seckeyfile, len - 4) != 0)
371
			goto bad;
372
	}
373
374
12
	return seckeyfile;
375
bad:
376
	errx(1, "please use naming scheme of keyname.pub and keyname.sec");
377
}
378
379
uint8_t *
380
createsig(const char *seckeyfile, const char *msgfile, uint8_t *msg,
381
    unsigned long long msglen)
382
{
383
32
	struct enckey enckey;
384
16
	uint8_t xorkey[sizeof(enckey.seckey)];
385
16
	struct sig sig;
386
	char *sighdr;
387
16
	uint8_t digest[SHA512_DIGEST_LENGTH];
388
	int i, nr, rounds;
389
16
	SHA2_CTX ctx;
390
16
	char comment[COMMENTMAXLEN], sigcomment[COMMENTMAXLEN];
391
392
16
	readb64file(seckeyfile, &enckey, sizeof(enckey), comment);
393
394
16
	if (strcmp(seckeyfile, "-") == 0) {
395
4
 		nr = snprintf(sigcomment, sizeof(sigcomment),
396
		    "signature from %s", comment);
397
4
	} else {
398
12
		const char *keyname = check_keyname_compliance(NULL,
399
		    seckeyfile);
400
24
		nr = snprintf(sigcomment, sizeof(sigcomment),
401
12
		    VERIFYWITH "%.*s.pub", (int)strlen(keyname) - 4, keyname);
402
	}
403

32
	if (nr == -1 || nr >= sizeof(sigcomment))
404
		errx(1, "comment too long");
405
406
16
	if (memcmp(enckey.kdfalg, KDFALG, 2) != 0)
407
		errx(1, "unsupported KDF");
408
16
	rounds = ntohl(enckey.kdfrounds);
409
32
	kdf(enckey.salt, sizeof(enckey.salt), rounds, strcmp(msgfile, "-") != 0,
410
16
	    0, xorkey, sizeof(xorkey));
411
2080
	for (i = 0; i < sizeof(enckey.seckey); i++)
412
1024
		enckey.seckey[i] ^= xorkey[i];
413
16
	explicit_bzero(xorkey, sizeof(xorkey));
414
16
	SHA512Init(&ctx);
415
16
	SHA512Update(&ctx, enckey.seckey, sizeof(enckey.seckey));
416
16
	SHA512Final(digest, &ctx);
417
16
	if (memcmp(enckey.checksum, digest, sizeof(enckey.checksum)) != 0)
418
		errx(1, "incorrect passphrase");
419
16
	explicit_bzero(digest, sizeof(digest));
420
421
16
	signmsg(enckey.seckey, msg, msglen, sig.sig);
422
16
	memcpy(sig.keynum, enckey.keynum, KEYNUMLEN);
423
16
	explicit_bzero(&enckey, sizeof(enckey));
424
425
16
	memcpy(sig.pkalg, PKALG, 2);
426
427
16
	sighdr = createheader(sigcomment, &sig, sizeof(sig));
428
16
	return sighdr;
429
16
}
430
431
static void
432
sign(const char *seckeyfile, const char *msgfile, const char *sigfile,
433
    int embedded)
434
{
435
	uint8_t *msg;
436
	char *sighdr;
437
	int fd;
438
24
	unsigned long long msglen;
439
440
12
	msg = readmsg(msgfile, &msglen);
441
442
12
	sighdr = createsig(seckeyfile, msgfile, msg, msglen);
443
444
12
	fd = xopen(sigfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666);
445
12
	writeall(fd, sighdr, strlen(sighdr), sigfile);
446
12
	free(sighdr);
447
12
	if (embedded)
448
8
		writeall(fd, msg, msglen, sigfile);
449
12
	close(fd);
450
451
12
	free(msg);
452
12
}
453
#endif
454
455
static void
456
verifymsg(struct pubkey *pubkey, uint8_t *msg, unsigned long long msglen,
457
    struct sig *sig, int quiet)
458
{
459
	uint8_t *sigbuf, *dummybuf;
460
42
	unsigned long long siglen, dummylen;
461
462
21
	if (memcmp(pubkey->keynum, sig->keynum, KEYNUMLEN) != 0)
463
		errx(1, "verification failed: checked against wrong key");
464
465
21
	siglen = SIGBYTES + msglen;
466
21
	sigbuf = xmalloc(siglen);
467
21
	dummybuf = xmalloc(siglen);
468
21
	memcpy(sigbuf, sig->sig, SIGBYTES);
469
21
	memcpy(sigbuf + SIGBYTES, msg, msglen);
470
42
	if (crypto_sign_ed25519_open(dummybuf, &dummylen, sigbuf, siglen,
471
42
	    pubkey->pubkey) == -1)
472
		errx(1, "signature verification failed");
473
17
	if (!quiet)
474
		printf("Signature Verified\n");
475
17
	free(sigbuf);
476
17
	free(dummybuf);
477
17
}
478
479
static void
480
check_keytype(const char *pubkeyfile, const char *keytype)
481
{
482
	const char *p;
483
	size_t typelen;
484
485
	if (!(p = strrchr(pubkeyfile, '-')))
486
		goto bad;
487
	p++;
488
	typelen = strlen(keytype);
489
	if (strncmp(p, keytype, typelen) != 0)
490
		goto bad;
491
	if (strcmp(p + typelen, ".pub") != 0)
492
		goto bad;
493
	return;
494
495
bad:
496
	errx(1, "incorrect keytype: %s is not %s", pubkeyfile, keytype);
497
}
498
499
static void
500
readpubkey(const char *pubkeyfile, struct pubkey *pubkey,
501
    const char *sigcomment, const char *keytype)
502
{
503
	const char *safepath = "/etc/signify";
504
42
	char keypath[1024];
505
506
21
	if (!pubkeyfile) {
507
1
		pubkeyfile = strstr(sigcomment, VERIFYWITH);
508

2
		if (pubkeyfile && strchr(pubkeyfile, '/') == NULL) {
509
1
			pubkeyfile += strlen(VERIFYWITH);
510
1
			if (keytype)
511
				check_keytype(pubkeyfile, keytype);
512
2
			if (snprintf(keypath, sizeof(keypath), "%s/%s",
513
1
			    safepath, pubkeyfile) >= sizeof(keypath))
514
				errx(1, "name too long %s", pubkeyfile);
515
			pubkeyfile = keypath;
516
		} else
517
			usage("must specify pubkey");
518
1
	}
519
21
	readb64file(pubkeyfile, pubkey, sizeof(*pubkey), NULL);
520
21
}
521
522
static void
523
verifysimple(const char *pubkeyfile, const char *msgfile, const char *sigfile,
524
    int quiet, const char *keytype)
525
{
526
16
	char sigcomment[COMMENTMAXLEN];
527
8
	struct sig sig;
528
8
	struct pubkey pubkey;
529
8
	unsigned long long msglen;
530
	uint8_t *msg;
531
532
8
	msg = readmsg(msgfile, &msglen);
533
534
8
	readb64file(sigfile, &sig, sizeof(sig), sigcomment);
535
8
	readpubkey(pubkeyfile, &pubkey, sigcomment, keytype);
536
537
8
	verifymsg(&pubkey, msg, msglen, &sig, quiet);
538
539
8
	free(msg);
540
8
}
541
542
static uint8_t *
543
verifyembedded(const char *pubkeyfile, const char *sigfile,
544
    int quiet, unsigned long long *msglenp, const char *keytype)
545
{
546
16
	char sigcomment[COMMENTMAXLEN];
547
8
	struct sig sig;
548
8
	struct pubkey pubkey;
549
8
	unsigned long long msglen, siglen;
550
	uint8_t *msg;
551
552
8
	msg = readmsg(sigfile, &msglen);
553
554
8
	siglen = parseb64file(sigfile, msg, &sig, sizeof(sig), sigcomment);
555
8
	readpubkey(pubkeyfile, &pubkey, sigcomment, keytype);
556
557
8
	msglen -= siglen;
558
8
	memmove(msg, msg + siglen, msglen);
559
8
	msg[msglen] = 0;
560
561
8
	verifymsg(&pubkey, msg, msglen, &sig, quiet);
562
563
8
	*msglenp = msglen;
564
8
	return msg;
565
8
}
566
567
static void
568
verify(const char *pubkeyfile, const char *msgfile, const char *sigfile,
569
    int embedded, int quiet, const char *keytype)
570
{
571
24
	unsigned long long msglen;
572
	uint8_t *msg;
573
	int fd;
574
575
12
	if (embedded) {
576
4
		msg = verifyembedded(pubkeyfile, sigfile, quiet, &msglen,
577
		    keytype);
578
4
		fd = xopen(msgfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666);
579
4
		writeall(fd, msg, msglen, msgfile);
580
4
		free(msg);
581
4
		close(fd);
582
4
	} else {
583
8
		verifysimple(pubkeyfile, msgfile, sigfile, quiet, keytype);
584
	}
585
8
}
586
587
#ifndef VERIFYONLY
588
#define HASHBUFSIZE 224
589
struct checksum {
590
	char file[PATH_MAX];
591
	char hash[HASHBUFSIZE];
592
	char algo[32];
593
};
594
595
static void *
596
ecalloc(size_t s1, size_t s2, void *data)
597
{
598
	void *p;
599
600
8
	if (!(p = calloc(s1, s2)))
601
		err(1, "calloc");
602
4
	return p;
603
}
604
605
static void
606
efree(void *p, void *data)
607
{
608
8
	free(p);
609
4
}
610
611
static void
612
recodehash(char *hash, size_t len)
613
{
614
32
	uint8_t data[HASHBUFSIZE / 2];
615
	int i, rv;
616
617
16
	if (strlen(hash) == len)
618
16
		return;
619
	if ((rv = b64_pton(hash, data, sizeof(data))) == -1)
620
		errx(1, "invalid base64 encoding");
621
	for (i = 0; i < rv; i++)
622
		snprintf(hash + i * 2, HASHBUFSIZE - i * 2, "%2.2x", data[i]);
623
16
}
624
625
static int
626
verifychecksum(struct checksum *c, int quiet)
627
{
628
32
	char buf[HASHBUFSIZE];
629
630
16
	if (strcmp(c->algo, "SHA256") == 0) {
631
8
		recodehash(c->hash, SHA256_DIGEST_STRING_LENGTH-1);
632
8
		if (!SHA256File(c->file, buf))
633
			return 0;
634
8
	} else if (strcmp(c->algo, "SHA512") == 0) {
635
8
		recodehash(c->hash, SHA512_DIGEST_STRING_LENGTH-1);
636
8
		if (!SHA512File(c->file, buf))
637
			return 0;
638
	} else {
639
		errx(1, "can't handle algorithm %s", c->algo);
640
	}
641
16
	if (strcmp(c->hash, buf) != 0)
642
		return 0;
643
16
	if (!quiet)
644
		printf("%s: OK\n", c->file);
645
16
	return 1;
646
16
}
647
648
static void
649
verifychecksums(char *msg, int argc, char **argv, int quiet)
650
{
651
8
	struct ohash_info info = { 0, NULL, ecalloc, efree, NULL };
652
4
	struct ohash myh;
653
4
	struct checksum c;
654
	char *e, *line, *endline;
655
	int hasfailed = 0;
656
	int i, rv;
657
4
	unsigned int slot;
658
659
4
	ohash_init(&myh, 6, &info);
660
4
	if (argc) {
661
		for (i = 0; i < argc; i++) {
662
			slot = ohash_qlookup(&myh, argv[i]);
663
			e = ohash_find(&myh, slot);
664
			if (e == NULL)
665
				ohash_insert(&myh, slot, argv[i]);
666
		}
667
	}
668
669
	line = msg;
670

60
	while (line && *line) {
671
16
		if ((endline = strchr(line, '\n')))
672
16
			*endline++ = '\0';
673
#if PATH_MAX < 1024 || HASHBUFSIZE < 224
674
#error sizes are wrong
675
#endif
676
16
		rv = sscanf(line, "%31s (%1023[^)]) = %223s",
677
16
		    c.algo, c.file, c.hash);
678
16
		if (rv != 3)
679
			errx(1, "unable to parse checksum line %s", line);
680
		line = endline;
681
16
		if (argc) {
682
			slot = ohash_qlookup(&myh, c.file);
683
			e = ohash_find(&myh, slot);
684
			if (e != NULL) {
685
				if (verifychecksum(&c, quiet) != 0)
686
					ohash_remove(&myh, slot);
687
			}
688
		} else {
689
16
			if (verifychecksum(&c, quiet) == 0) {
690
				slot = ohash_qlookup(&myh, c.file);
691
				e = ohash_find(&myh, slot);
692
				if (e == NULL) {
693
					if (!(e = strdup(c.file)))
694
						err(1, "strdup");
695
					ohash_insert(&myh, slot, e);
696
				}
697
			}
698
		}
699
	}
700
701
8
	for (e = ohash_first(&myh, &slot); e != NULL; e = ohash_next(&myh, &slot)) {
702
		fprintf(stderr, "%s: FAIL\n", e);
703
		hasfailed = 1;
704
		if (argc == 0)
705
			free(e);
706
	}
707
4
	ohash_delete(&myh);
708
4
	if (hasfailed)
709
		exit(1);
710
4
}
711
712
static void
713
check(const char *pubkeyfile, const char *sigfile, int quiet, int argc,
714
    char **argv)
715
{
716
8
	unsigned long long msglen;
717
	uint8_t *msg;
718
719
4
	msg = verifyembedded(pubkeyfile, sigfile, quiet, &msglen, NULL);
720
4
	verifychecksums((char *)msg, argc, argv, quiet);
721
722
4
	free(msg);
723
4
}
724
725
void *
726
verifyzdata(uint8_t *zdata, unsigned long long zdatalen,
727
    const char *filename, const char *pubkeyfile, const char *keytype)
728
{
729
10
	struct sig sig;
730
5
	char sigcomment[COMMENTMAXLEN];
731
	unsigned long long siglen;
732
5
	struct pubkey pubkey;
733
734
5
	if (zdatalen < sizeof(sig))
735
		errx(1, "signature too short in %s", filename);
736
5
	siglen = parseb64file(filename, zdata, &sig, sizeof(sig),
737
5
	    sigcomment);
738
5
	readpubkey(pubkeyfile, &pubkey, sigcomment, keytype);
739
5
	zdata += siglen;
740
5
	zdatalen -= siglen;
741
5
	verifymsg(&pubkey, zdata, zdatalen, &sig, 1);
742
5
	return zdata;
743
5
}
744
#endif
745
746
int
747
main(int argc, char **argv)
748
{
749
	const char *pubkeyfile = NULL, *seckeyfile = NULL, *msgfile = NULL,
750
	    *sigfile = NULL;
751
74
	char sigfilebuf[PATH_MAX];
752
	const char *comment = "signify";
753
	char *keytype = NULL;
754
	int ch, rounds;
755
	int embedded = 0;
756
	int quiet = 0;
757
	int gzip = 0;
758
	enum {
759
		NONE,
760
		CHECK,
761
		GENERATE,
762
		SIGN,
763
		VERIFY
764
	} verb = NONE;
765
766
37
	if (pledge("stdio rpath wpath cpath tty flock", NULL) == -1)
767
		err(1, "pledge");
768
769
	rounds = 42;
770
771
382
	while ((ch = getopt(argc, argv, "CGSVzc:em:np:qs:t:x:")) != -1) {
772



154
		switch (ch) {
773
#ifndef VERIFYONLY
774
		case 'C':
775
4
			if (verb)
776
				usage(NULL);
777
			verb = CHECK;
778
4
			break;
779
		case 'G':
780
			if (verb)
781
				usage(NULL);
782
			verb = GENERATE;
783
			break;
784
		case 'S':
785
16
			if (verb)
786
				usage(NULL);
787
			verb = SIGN;
788
16
			break;
789
		case 'z':
790
			gzip = 1;
791
9
			break;
792
#endif
793
		case 'V':
794
17
			if (verb)
795
				usage(NULL);
796
			verb = VERIFY;
797
17
			break;
798
		case 'c':
799
			comment = optarg;
800
			break;
801
		case 'e':
802
			embedded = 1;
803
12
			break;
804
		case 'm':
805
28
			msgfile = optarg;
806
28
			break;
807
		case 'n':
808
			rounds = 0;
809
			break;
810
		case 'p':
811
20
			pubkeyfile = optarg;
812
20
			break;
813
		case 'q':
814
			quiet = 1;
815
16
			break;
816
		case 's':
817
16
			seckeyfile = optarg;
818
16
			break;
819
		case 't':
820
			keytype = optarg;
821
			break;
822
		case 'x':
823
16
			sigfile = optarg;
824
16
			break;
825
		default:
826
			usage(NULL);
827
			break;
828
		}
829
	}
830
37
	argc -= optind;
831
37
	argv += optind;
832
833
37
	if (embedded && gzip)
834
		errx(1, "can't combine -e and -z options");
835
836
37
	if (setvbuf(stdout, NULL, _IOLBF, 0) != 0)
837
		err(1, "setvbuf");
838
839
#ifndef VERIFYONLY
840
37
	if (verb == CHECK) {
841
4
		if (pledge("stdio rpath flock cpath wpath", NULL) == -1)
842
			err(1, "pledge");
843
4
		if (!sigfile)
844
			usage("must specify sigfile");
845
4
		check(pubkeyfile, sigfile, quiet, argc, argv);
846
4
		return 0;
847
	}
848
#endif
849
850
33
	if (argc != 0)
851
		usage(NULL);
852
853
33
	if (!sigfile && msgfile) {
854
		int nr;
855
16
		if (strcmp(msgfile, "-") == 0)
856
			usage("must specify sigfile with - message");
857
16
		nr = snprintf(sigfilebuf, sizeof(sigfilebuf),
858
		    "%s.sig", msgfile);
859

32
		if (nr == -1 || nr >= sizeof(sigfilebuf))
860
			errx(1, "path too long");
861
		sigfile = sigfilebuf;
862
16
	}
863
864

33
	switch (verb) {
865
#ifndef VERIFYONLY
866
	case GENERATE:
867
		/* no pledge */
868
		if (!pubkeyfile || !seckeyfile)
869
			usage("must specify pubkey and seckey");
870
		check_keyname_compliance(pubkeyfile, seckeyfile);
871
		generate(pubkeyfile, seckeyfile, rounds, comment);
872
		break;
873
	case SIGN:
874
		/* no pledge */
875
16
		if (gzip) {
876
4
			if (!msgfile || !seckeyfile || !sigfile)
877
				usage("must specify message sigfile seckey");
878
4
			zsign(seckeyfile, msgfile, sigfile);
879
4
		} else {
880
12
			if (!msgfile || !seckeyfile)
881
				usage("must specify message and seckey");
882
12
			sign(seckeyfile, msgfile, sigfile, embedded);
883
		}
884
		break;
885
#endif
886
	case VERIFY:
887

21
		if ((embedded || gzip) &&
888
4
		    (msgfile && strcmp(msgfile, "-") != 0)) {
889
			/* will need to create output file */
890
4
			if (pledge("stdio rpath wpath cpath flock", NULL) == -1)
891
				err(1, "pledge");
892
		} else {
893
13
			if (pledge("stdio rpath flock cpath wpath", NULL) == -1)
894
				err(1, "pledge");
895
		}
896
17
		if (gzip) {
897
5
			zverify(pubkeyfile, msgfile, sigfile, keytype);
898
5
		} else {
899
12
			if (!msgfile)
900
				usage("must specify message");
901
12
			verify(pubkeyfile, msgfile, sigfile, embedded,
902
			    quiet, keytype);
903
		}
904
		break;
905
	default:
906
		if (pledge("stdio flock rpath cpath wpath", NULL) == -1)
907
			err(1, "pledge");
908
		usage(NULL);
909
		break;
910
	}
911
912
29
	return 0;
913
33
}