Add new hash functions. Provide full help for subcommands. Run the
[u/mdw/catacomb] / dsig.c
1 /* -*-c-*-
2 *
3 * $Id: dsig.c,v 1.7 2001/02/23 09:04:17 mdw Exp $
4 *
5 * Verify signatures on distribuitions of files
6 *
7 * (c) 2000 Straylight/Edgeware
8 */
9
10 /*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of Catacomb.
13 *
14 * Catacomb is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Library General Public License as
16 * published by the Free Software Foundation; either version 2 of the
17 * License, or (at your option) any later version.
18 *
19 * Catacomb is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Library General Public License for more details.
23 *
24 * You should have received a copy of the GNU Library General Public
25 * License along with Catacomb; if not, write to the Free
26 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27 * MA 02111-1307, USA.
28 */
29
30 /*----- Revision history --------------------------------------------------*
31 *
32 * $Log: dsig.c,v $
33 * Revision 1.7 2001/02/23 09:04:17 mdw
34 * Add new hash functions. Provide full help for subcommands. Run the
35 * hash function over parts of the header in a canonical order.
36 *
37 * Revision 1.6 2000/12/06 20:33:27 mdw
38 * Make flags be macros rather than enumerations, to ensure that they're
39 * unsigned.
40 *
41 * Revision 1.5 2000/10/08 12:12:09 mdw
42 * Shut up some warnings.
43 *
44 * Revision 1.4 2000/08/04 23:23:44 mdw
45 * Various <ctype.h> fixes.
46 *
47 * Revision 1.3 2000/07/15 20:53:23 mdw
48 * More hash functions. Bug fix in getstring.
49 *
50 * Revision 1.2 2000/07/01 11:27:22 mdw
51 * Use new PKCS#1 padding functions rather than rolling by hand.
52 *
53 * Revision 1.1 2000/06/17 10:54:29 mdw
54 * Program to generate and verify signatures on multiple files.
55 *
56 */
57
58 /*----- Header files ------------------------------------------------------*/
59
60 #include "config.h"
61
62 #include <ctype.h>
63 #include <errno.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67
68 #include <mLib/alloc.h>
69 #include <mLib/base64.h>
70 #include <mLib/mdwopt.h>
71 #include <mLib/quis.h>
72 #include <mLib/report.h>
73 #include <mLib/sub.h>
74
75 #include "getdate.h"
76 #include "grand.h"
77 #include "ghash.h"
78 #include "key.h"
79 #include "key-data.h"
80 #include "noise.h"
81
82 #include "dsa.h"
83 #include "rsa.h"
84 #include "pkcs1.h"
85
86 #include "md2.h"
87 #include "md4.h"
88 #include "md5.h"
89 #include "rmd128.h"
90 #include "rmd160.h"
91 #include "rmd256.h"
92 #include "rmd320.h"
93 #include "sha.h"
94 #include "sha256.h"
95 #include "sha384.h"
96 #include "sha512.h"
97 #include "tiger.h"
98
99 /*----- Digital signature algorithm ---------------------------------------*/
100
101 static int dsasign(key *k, const void *m, size_t msz, dstr *d)
102 {
103 dsa_priv dp;
104 key_packstruct ks[DSA_PRIVFETCHSZ];
105 key_packdef *kp;
106 size_t sz;
107 octet *p;
108 int e;
109
110 kp = key_fetchinit(dsa_privfetch, ks, &dp);
111 if ((e = key_fetch(kp, k)) != 0) {
112 key_fetchdone(kp);
113 return (e);
114 }
115 sz = mp_octets(dp.dp.q);
116 if (sz < msz)
117 die(EXIT_FAILURE, "hash function too wide for this signing key");
118 DENSURE(d, sz * 2);
119 p = (octet *)d->buf + d->len;
120 rand_get(RAND_GLOBAL, p, sz);
121 dsa_sign(&dp.dp, dp.x, m, msz, p, sz, p, sz, p + sz, sz);
122 d->len += sz * 2;
123 key_fetchdone(kp);
124 return (0);
125 }
126
127 static int dsaverify(key *k, const void *m, size_t msz,
128 const void *s, size_t ssz)
129 {
130 dsa_pub dp;
131 key_packstruct ks[DSA_PUBFETCHSZ];
132 key_packdef *kp;
133 size_t sz;
134 const octet *p = s;
135 int e;
136
137 kp = key_fetchinit(dsa_pubfetch, ks, &dp);
138 if ((e = key_fetch(kp, k)) != 0) {
139 key_fetchdone(kp);
140 return (e);
141 }
142 sz = ssz / 2;
143 e = dsa_verify(&dp.dp, dp.y, m, msz, p, sz, p + sz, sz);
144 key_fetchdone(kp);
145 return (e);
146 }
147
148 /*----- RSA signing -------------------------------------------------------*/
149
150 static int rsasign(key *k, const void *m, size_t msz, dstr *d)
151 {
152 rsa_priv rp;
153 rsa_privctx rpc;
154 pkcs1 pk = { 0, 0, 0 };
155 key_packstruct ks[RSA_PRIVFETCHSZ];
156 key_packdef *kp;
157 int e;
158
159 kp = key_fetchinit(rsa_privfetch, ks, &rp);
160 if ((e = key_fetch(kp, k)) != 0) {
161 key_fetchdone(kp);
162 return (e);
163 }
164 rsa_privcreate(&rpc, &rp, &rand_global);
165 if (rsa_sign(&rpc, m, msz, d, pkcs1_sigencode, &pk) < 0)
166 die(EXIT_FAILURE, "internal error in rsasign (key too small?)");
167 rsa_privdestroy(&rpc);
168 key_fetchdone(kp);
169 return (0);
170 }
171
172 static int rsaverify(key *k, const void *m, size_t msz,
173 const void *s, size_t ssz)
174 {
175 rsa_pub rp;
176 rsa_pubctx rpc;
177 pkcs1 pk = { 0, 0, 0 };
178 key_packstruct ks[RSA_PUBFETCHSZ];
179 key_packdef *kp;
180 int ok = 0;
181 dstr d = DSTR_INIT;
182 int e;
183
184 kp = key_fetchinit(rsa_pubfetch, ks, &rp);
185 if ((e = key_fetch(kp, k)) != 0) {
186 key_fetchdone(kp);
187 return (e);
188 }
189 rsa_pubcreate(&rpc, &rp);
190 if (rsa_verify(&rpc, s, ssz, &d, pkcs1_sigdecode, &pk) > 0 &&
191 msz == d.len && memcmp(d.buf, m, msz) == 0)
192 ok = 1;
193 dstr_destroy(&d);
194 rsa_pubdestroy(&rpc);
195 key_fetchdone(kp);
196 return (ok);
197 }
198
199 /*----- Algorithm choice --------------------------------------------------*/
200
201 typedef struct sig {
202 const char *name;
203 const char *type;
204 int (*sign)(key */*k*/, const void */*m*/, size_t /*msz*/, dstr */*d*/);
205 int (*verify)(key */*k*/, const void */*m*/, size_t /*msz*/,
206 const void */*s*/, size_t /*ssz*/);
207 } sig;
208
209 static const gchash *hashtab[] = {
210 &rmd160, &tiger, &sha, &sha256, &sha384, &sha512,
211 &rmd128, &rmd256, &rmd320, &md5, &md4, &md2, 0 };
212 static sig sigtab[] = {
213 { "dsa", "dsig-dsa", dsasign, dsaverify },
214 { "rsa", "dsig-rsa", rsasign, rsaverify },
215 { 0, 0, 0 }
216 };
217
218 /* --- @gethash@ --- *
219 *
220 * Arguments: @const char *name@ = pointer to name string
221 *
222 * Returns: Pointer to appropriate hash class.
223 *
224 * Use: Chooses a hash function by name.
225 */
226
227 static const gchash *gethash(const char *name)
228 {
229 const gchash **g, *gg = 0;
230 size_t sz = strlen(name);
231 for (g = hashtab; *g; g++) {
232 if (strncmp(name, (*g)->name, sz) == 0) {
233 if ((*g)->name[sz] == 0) {
234 gg = *g;
235 break;
236 } else if (gg)
237 return (0);
238 else
239 gg = *g;
240 }
241 }
242 return (gg);
243 }
244
245 /* --- @getsig@ --- *
246 *
247 * Arguments: @const char *name@ = pointer to name string
248 *
249 * Returns: Pointer to appropriate signature type.
250 *
251 * Use: Chooses a signature algorithm by name.
252 */
253
254 static sig *getsig(const char *name)
255 {
256 sig *s, *ss = 0;
257 size_t sz = strlen(name);
258 for (s = sigtab; s->name; s++) {
259 if (strncmp(name, s->name, sz) == 0) {
260 if (s->name[sz] == 0) {
261 ss = s;
262 break;
263 } else if (ss)
264 return (0);
265 else
266 ss = s;
267 }
268 }
269 return (ss);
270 }
271
272 /*----- Data formatting ---------------------------------------------------*/
273
274 /* --- Binary data structure --- *
275 *
276 * The binary format, which is used for hashing and for the optional binary
277 * output, consists of a sequence of tagged blocks. The tag describes the
278 * format and meaining of the following data.
279 */
280
281 enum {
282 /* --- Block tags --- */
283
284 T_IDENT = 0, /* An identifying marker */
285 T_SIGALG, /* Signature algorithm used */
286 T_HASHALG, /* Hash algorithm used */
287 T_KEYID, /* Key identifier */
288 T_BEGIN, /* Begin hashing here */
289 T_COMMENT = T_BEGIN, /* A textual comment */
290 T_DATE, /* Creation date of signature */
291 T_EXPIRE, /* Expiry date of signature */
292 T_FILE, /* File and corresponding hash */
293 T_SIGNATURE, /* Final signature block */
294
295 /* --- Error messages --- */
296
297 E_EOF = -1,
298 E_BIN = -2,
299 E_TAG = -3,
300 E_DATE = -4
301 };
302
303 /* --- Name translation table --- */
304
305 static const char *tagtab[] = {
306 "ident:", "sigalg:", "hashalg:", "keyid:",
307 "comment:", "date:", "expires:", "file:",
308 "signature:",
309 0
310 };
311
312 static const char *errtab[] = {
313 "Off-by-one bug",
314 "Unexpected end-of-file",
315 "Binary object too large",
316 "Unrecognized tag",
317 "Bad date string"
318 };
319
320 /* --- Memory representation of block types --- */
321
322 typedef struct block {
323 int tag; /* Type tag */
324 dstr d; /* String data */
325 dstr b; /* Binary data */
326 time_t t; /* Timestamp */
327 uint32 k; /* Keyid */
328 } block;
329
330 /* --- @getstring@ --- *
331 *
332 * Arguments: @FILE *fp@ = stream from which to read
333 * @dstr *d@ = destination string
334 * @unsigned raw@ = raw or cooked read
335 *
336 * Returns: Zero if OK, nonzero on end-of-file.
337 *
338 * Use: Reads a filename (or something similar) from a stream.
339 */
340
341 static int getstring(FILE *fp, dstr *d, unsigned raw)
342 {
343 int ch;
344 int q = 0;
345
346 /* --- Raw: just read exactly what's written up to a null byte --- */
347
348 if (raw) {
349 if ((ch = getc(fp)) == EOF)
350 return (EOF);
351 for (;;) {
352 if (!ch)
353 break;
354 DPUTC(d, ch);
355 if ((ch = getc(fp)) == EOF)
356 break;
357 }
358 DPUTZ(d);
359 return (0);
360 }
361
362 /* --- Skip as far as whitespace --- *
363 *
364 * Also skip past comments.
365 */
366
367 again:
368 ch = getc(fp);
369 while (isspace(ch))
370 ch = getc(fp);
371 if (ch == '#') {
372 do ch = getc(fp); while (ch != '\n' && ch != EOF);
373 goto again;
374 }
375 if (ch == EOF)
376 return (EOF);
377
378 /* --- If the character is a quote then read a quoted string --- */
379
380 switch (ch) {
381 case '`':
382 ch = '\'';
383 case '\'':
384 case '\"':
385 q = ch;
386 ch = getc(fp);
387 break;
388 }
389
390 /* --- Now read all sorts of interesting things --- */
391
392 for (;;) {
393
394 /* --- Handle an escaped thing --- */
395
396 if (ch == '\\') {
397 ch = getc(fp);
398 if (ch == EOF)
399 break;
400 switch (ch) {
401 case 'a': ch = '\a'; break;
402 case 'b': ch = '\b'; break;
403 case 'f': ch = '\f'; break;
404 case 'n': ch = '\n'; break;
405 case 'r': ch = '\r'; break;
406 case 't': ch = '\t'; break;
407 case 'v': ch = '\v'; break;
408 }
409 DPUTC(d, ch);
410 ch = getc(fp);
411 continue;
412 }
413
414 /* --- If it's a quote or some other end marker then stop --- */
415
416 if (ch == q || (!q && isspace((unsigned char)ch)))
417 break;
418
419 /* --- Otherwise contribute and continue --- */
420
421 DPUTC(d, ch);
422 if ((ch = getc(fp)) == EOF)
423 break;
424 }
425
426 /* --- Done --- */
427
428 DPUTZ(d);
429 return (0);
430 }
431
432 /* --- @putstring@ --- *
433 *
434 * Arguments: @FILE *fp@ = stream to write on
435 * @const char *p@ = pointer to text
436 * @unsigned raw@ = whether the string is to be written raw
437 *
438 * Returns: ---
439 *
440 * Use: Emits a string to a stream.
441 */
442
443 static void putstring(FILE *fp, const char *p, unsigned raw)
444 {
445 size_t sz = strlen(p);
446 unsigned qq;
447 const char *q;
448
449 /* --- Just write the string null terminated if raw --- */
450
451 if (raw) {
452 fwrite(p, 1, sz + 1, fp);
453 return;
454 }
455
456 /* --- Check for any dodgy characters --- */
457
458 qq = 0;
459 for (q = p; *q; q++) {
460 if (isspace((unsigned char)*q)) {
461 qq = '\"';
462 break;
463 }
464 }
465
466 if (qq)
467 putc(qq, fp);
468
469 /* --- Emit the string --- */
470
471 for (q = p; *q; q++) {
472 switch (*q) {
473 case '\a': fputc('\\', fp); fputc('a', fp); break;
474 case '\b': fputc('\\', fp); fputc('b', fp); break;
475 case '\f': fputc('\\', fp); fputc('f', fp); break;
476 case '\n': fputc('\\', fp); fputc('n', fp); break;
477 case '\r': fputc('\\', fp); fputc('r', fp); break;
478 case '\t': fputc('\\', fp); fputc('t', fp); break;
479 case '\v': fputc('\\', fp); fputc('v', fp); break;
480 case '`': fputc('\\', fp); fputc('`', fp); break;
481 case '\'': fputc('\\', fp); fputc('\'', fp); break;
482 case '\"': fputc('\\', fp); fputc('\"', fp); break;
483 default:
484 putc(*q, fp);
485 break;
486 }
487 }
488
489 /* --- Done --- */
490
491 if (qq)
492 putc(qq, fp);
493 }
494
495 /* --- @timestring@ --- *
496 *
497 * Arguments: @time_t t@ = a timestamp
498 * @dstr *d@ = a string to write on
499 *
500 * Returns: ---
501 *
502 * Use: Writes a textual representation of the timestamp to the
503 * string.
504 */
505
506 static void timestring(time_t t, dstr *d)
507 {
508 if (t == KEXP_FOREVER)
509 DPUTS(d, "forever");
510 else {
511 struct tm *tm = localtime(&t);
512 DENSURE(d, 32);
513 d->len += strftime(d->buf + d->len, 32, "%Y-%m-%d %H:%M:%S %Z", tm);
514 DPUTZ(d);
515 }
516 }
517
518 /* --- @breset@ --- *
519 *
520 * Arguments: @block *b@ = block to reset
521 *
522 * Returns: ---
523 *
524 * Use: Resets a block so that more stuff can be put in it.
525 */
526
527 static void breset(block *b)
528 {
529 b->tag = 0;
530 DRESET(&b->d);
531 DRESET(&b->b);
532 b->k = 0;
533 b->t = KEXP_EXPIRE;
534 }
535
536 /* --- @binit@ --- *
537 *
538 * Arguments: @block *b@ = block to initialize
539 *
540 * Returns: ---
541 *
542 * Use: Initializes a block as something to read into.
543 */
544
545 static void binit(block *b)
546 {
547 dstr_create(&b->d);
548 dstr_create(&b->b);
549 breset(b);
550 }
551
552 /* --- @bdestroy@ --- *
553 *
554 * Arguments: @block *b@ = block to destroy
555 *
556 * Returns: ---
557 *
558 * Use: Destroys a block's contents.
559 */
560
561 static void bdestroy(block *b)
562 {
563 dstr_destroy(&b->d);
564 dstr_destroy(&b->b);
565 }
566
567 /* --- @bget@ --- *
568 *
569 * Arguments: @block *b@ = pointer to block
570 * @FILE *fp@ = stream to read from
571 * @unsigned bin@ = binary switch
572 *
573 * Returns: Tag of block, or an error tag.
574 *
575 * Use: Reads a block from a stream.
576 */
577
578 static int bget(block *b, FILE *fp, unsigned bin)
579 {
580 int tag;
581
582 /* --- Read the tag --- */
583
584 if (bin)
585 tag = getc(fp);
586 else {
587 dstr d = DSTR_INIT;
588 if (getstring(fp, &d, 0))
589 return (E_EOF);
590 for (tag = 0; tagtab[tag]; tag++) {
591 if (strcmp(tagtab[tag], d.buf) == 0)
592 goto done;
593 }
594 return (E_TAG);
595 done:;
596 }
597
598 /* --- Decide what to do next --- */
599
600 breset(b);
601 b->tag = tag;
602 switch (tag) {
603
604 /* --- Reading of strings --- */
605
606 case T_IDENT:
607 case T_COMMENT:
608 case T_SIGALG:
609 case T_HASHALG:
610 if (getstring(fp, &b->d, bin))
611 return (E_EOF);
612 break;
613
614 /* --- Timestamps --- */
615
616 case T_DATE:
617 case T_EXPIRE:
618 if (bin) {
619 octet buf[8];
620 if (fread(buf, sizeof(buf), 1, fp) < 1)
621 return (E_EOF);
622 b->t = ((time_t)(((LOAD32(buf + 0) << 16) << 16) & ~MASK32) |
623 (time_t)LOAD32(buf + 4));
624 } else {
625 if (getstring(fp, &b->d, 0))
626 return (E_EOF);
627 if (strcmp(b->d.buf, "forever") == 0)
628 b->t = KEXP_FOREVER;
629 else if ((b->t = get_date(b->d.buf, 0)) == -1)
630 return (E_DATE);
631 }
632 break;
633
634 /* --- Key ids --- */
635
636 case T_KEYID:
637 if (bin) {
638 octet buf[4];
639 if (fread(buf, sizeof(buf), 1, fp) < 1)
640 return (E_EOF);
641 b->k = LOAD32(buf);
642 } else {
643 if (getstring(fp, &b->d, 0))
644 return (E_EOF);
645 b->k = strtoul(b->d.buf, 0, 16);
646 }
647 break;
648
649 /* --- Reading of binary data --- */
650
651 case T_FILE:
652 case T_SIGNATURE:
653 if (bin) {
654 octet buf[2];
655 uint32 sz;
656 if (fread(buf, sizeof(buf), 1, fp) < 1)
657 return (E_EOF);
658 sz = LOAD16(buf);
659 if (sz > 4096)
660 return (E_BIN);
661 DENSURE(&b->b, sz);
662 if (fread(b->b.buf + b->b.len, 1, sz, fp) < sz)
663 return (E_EOF);
664 b->b.len += sz;
665 } else {
666 base64_ctx b64;
667 if (getstring(fp, &b->d, 0))
668 return (E_EOF);
669 base64_init(&b64);
670 base64_decode(&b64, b->d.buf, b->d.len, &b->b);
671 base64_decode(&b64, 0, 0, &b->b);
672 DRESET(&b->d);
673 }
674 if (tag == T_FILE && getstring(fp, &b->d, bin))
675 return (E_EOF);
676 break;
677
678 /* --- Anything else --- */
679
680 default:
681 return (E_TAG);
682 }
683
684 return (tag);
685 }
686
687 /* --- @blob@ --- *
688 *
689 * Arguments: @block *b@ = pointer to block to emit
690 * @dstr *d@ = output buffer
691 *
692 * Returns: ---
693 *
694 * Use: Encodes a block in a binary format.
695 */
696
697 static void blob(block *b, dstr *d)
698 {
699 DPUTC(d, b->tag);
700 switch (b->tag) {
701 case T_IDENT:
702 case T_SIGALG:
703 case T_HASHALG:
704 case T_COMMENT:
705 DPUTD(d, &b->d);
706 DPUTC(d, 0);
707 break;
708 case T_DATE:
709 case T_EXPIRE:
710 DENSURE(d, 8);
711 STORE32(d->buf + d->len, ((b->t & ~MASK32) >> 16) >> 16);
712 STORE32(d->buf + d->len + 4, b->t);
713 d->len += 8;
714 break;
715 case T_KEYID:
716 DENSURE(d, 4);
717 STORE32(d->buf + d->len, b->k);
718 d->len += 4;
719 break;
720 case T_FILE:
721 case T_SIGNATURE:
722 DENSURE(d, 2);
723 STORE16(d->buf + d->len, b->b.len);
724 d->len += 2;
725 DPUTD(d, &b->b);
726 if (b->tag == T_FILE) {
727 DPUTD(d, &b->d);
728 DPUTC(d, 0);
729 }
730 break;
731 }
732 }
733
734 /* --- @bwrite@ --- *
735 *
736 * Arguments: @block *b@ = pointer to block to write
737 * @FILE *fp@ = stream to write on
738 *
739 * Returns: ---
740 *
741 * Use: Writes a block on a stream in a textual format.
742 */
743
744 static void bwrite(block *b, FILE *fp)
745 {
746 fputs(tagtab[b->tag], fp);
747 putc(' ', fp);
748 switch (b->tag) {
749 case T_IDENT:
750 case T_SIGALG:
751 case T_HASHALG:
752 case T_COMMENT:
753 putstring(fp, b->d.buf, 0);
754 break;
755 case T_DATE:
756 case T_EXPIRE: {
757 dstr d = DSTR_INIT;
758 timestring(b->t, &d);
759 putstring(fp, d.buf, 0);
760 dstr_destroy(&d);
761 } break;
762 case T_KEYID:
763 fprintf(fp, "%08lx", (unsigned long)b->k);
764 break;
765 case T_FILE:
766 case T_SIGNATURE: {
767 dstr d = DSTR_INIT;
768 base64_ctx b64;
769 base64_init(&b64);
770 b64.maxline = 0;
771 base64_encode(&b64, b->b.buf, b->b.len, &d);
772 base64_encode(&b64, 0, 0, &d);
773 dstr_write(&d, fp);
774 if (b->tag == T_FILE) {
775 putc(' ', fp);
776 putstring(fp, b->d.buf, 0);
777 }
778 } break;
779 }
780 putc('\n', fp);
781 }
782
783 /* --- @bemit@ --- *
784 *
785 * Arguments: @block *b@ = pointer to block to write
786 * @FILE *fp@ = file to write on
787 * @ghash *h@ = pointer to hash function
788 * @unsigned bin@ = binary/text flag
789 *
790 * Returns: ---
791 *
792 * Use: Spits out a block properly.
793 */
794
795 static void bemit(block *b, FILE *fp, ghash *h, unsigned bin)
796 {
797 if (h || (fp && bin)) {
798 dstr d = DSTR_INIT;
799 blob(b, &d);
800 if (h)
801 h->ops->hash(h, d.buf, d.len);
802 if (fp && bin)
803 fwrite(d.buf, d.len, 1, fp);
804 }
805 if (fp && !bin)
806 bwrite(b, fp);
807 }
808
809 /*----- Static variables --------------------------------------------------*/
810
811 static const char *keyring = "keyring";
812
813 /*----- Other shared functions --------------------------------------------*/
814
815 /* --- @keyreport@ --- *
816 *
817 * Arguments: @const char *file@ = filename containing the error
818 * @int line@ = line number in file
819 * @const char *err@ = error text message
820 * @void *p@ = unimportant pointer
821 *
822 * Returns: ---
823 *
824 * Use: Reports errors during the opening of a key file.
825 */
826
827 static void keyreport(const char *file, int line, const char *err, void *p)
828 {
829 moan("error in keyring `%s' at line `%s': %s", file, line, err);
830 }
831
832 /* --- @fhash@ --- *
833 *
834 * Arguments: @const gchash *c@ = pointer to hash class
835 * @const char *file@ = file to hash
836 * @void *b@ = pointer to output buffer
837 *
838 * Returns: Zero if it worked, or nonzero for a system error.
839 *
840 * Use: Hashes a file.
841 */
842
843 static int fhash(const gchash *c, const char *file, void *b)
844 {
845 FILE *fp = fopen(file, "rb");
846 ghash *h = c->init();
847 char buf[4096];
848 size_t sz;
849 int rc = 0;
850
851 if (!fp)
852 return (-1);
853 while ((sz = fread(buf, 1, sizeof(buf), fp)) > 0)
854 h->ops->hash(h, buf, sz);
855 if (ferror(fp))
856 rc = -1;
857 h->ops->done(h, b);
858 h->ops->destroy(h);
859 fclose(fp);
860 return (rc);
861 }
862
863 /* --- @fhex@ --- *
864 *
865 * Arguments: @FILE *fp@ = file to write on
866 * @const void *p@ = pointer to data to be written
867 * @size_t sz@ = size of the data to write
868 *
869 * Returns: ---
870 *
871 * Use: Emits a hex dump to a stream.
872 */
873
874 static void fhex(FILE *fp, const void *p, size_t sz)
875 {
876 const octet *q = p;
877 if (!sz)
878 return;
879 for (;;) {
880 fprintf(fp, "%02x", *q++);
881 sz--;
882 if (!sz)
883 break;
884 /* putc(' ', fp); */
885 }
886 }
887
888 /*----- Signature generation ----------------------------------------------*/
889
890 static int sign(int argc, char *argv[])
891 {
892 #define f_raw 1u
893 #define f_bin 2u
894 #define f_bogus 4u
895
896 unsigned f = 0;
897 const char *kt = 0;
898 const char *ki = 0;
899 key_file kf;
900 key *k;
901 const sig *s = sigtab;
902 const gchash *gch = &rmd160;
903 ghash *h;
904 time_t exp = KEXP_EXPIRE;
905 unsigned verb = 0;
906 const char *ifile = 0;
907 const char *ofile = 0;
908 const char *c = 0;
909 FILE *ifp, *ofp;
910 dstr d = DSTR_INIT;
911 block b;
912 int e;
913
914 for (;;) {
915 static struct option opts[] = {
916 { "null", 0, 0, '0' },
917 { "binary", 0, 0, 'b' },
918 { "verbose", 0, 0, 'v' },
919 { "quiet", 0, 0, 'q' },
920 { "algorithm", OPTF_ARGREQ, 0, 'a' },
921 { "hash", OPTF_ARGREQ, 0, 'h' },
922 { "comment", OPTF_ARGREQ, 0, 'c' },
923 { "file", OPTF_ARGREQ, 0, 'f' },
924 { "output", OPTF_ARGREQ, 0, 'o' },
925 { "keytype", OPTF_ARGREQ, 0, 't' },
926 { "keyid", OPTF_ARGREQ, 0, 'i' },
927 { "key", OPTF_ARGREQ, 0, 'i' },
928 { "expire", OPTF_ARGREQ, 0, 'e' },
929 { 0, 0, 0, 0 }
930 };
931 int i = mdwopt(argc, argv, "+0vqb a:h:c: f:o: t:i:k:e:", opts, 0, 0, 0);
932 if (i < 0)
933 break;
934 switch (i) {
935 case '0':
936 f |= f_raw;
937 break;
938 case 'b':
939 f |= f_bin;
940 break;
941 case 'v':
942 verb++;
943 break;
944 case 'q':
945 if (verb > 0)
946 verb--;
947 break;
948 case 'a':
949 if ((s = getsig(optarg)) == 0) {
950 die(EXIT_FAILURE, "unknown or ambiguous signature algorithm `%s'",
951 optarg);
952 }
953 break;
954 case 'h':
955 if ((gch = gethash(optarg)) == 0) {
956 die(EXIT_FAILURE, "unknown or ambiguous hash function `%s'",
957 optarg);
958 }
959 break;
960 case 'c':
961 c = optarg;
962 break;
963 case 'f':
964 ifile = optarg;
965 break;
966 case 'o':
967 ofile = optarg;
968 break;
969 case 't':
970 kt = optarg;
971 break;
972 case 'i':
973 case 'k':
974 ki = optarg;
975 break;
976 case 'e':
977 if (strcmp(optarg, "forever") == 0)
978 exp = KEXP_FOREVER;
979 else if ((exp = get_date(optarg, 0)) == -1)
980 die(EXIT_FAILURE, "bad expiry time");
981 break;
982 default:
983 f |= f_bogus;
984 break;
985 }
986 }
987 if (optind != argc || (f & f_bogus))
988 die(EXIT_FAILURE, "Usage: sign [-options]");
989
990 /* --- Locate the signing key --- */
991
992 if (key_open(&kf, keyring, KOPEN_WRITE, keyreport, 0))
993 die(EXIT_FAILURE, "couldn't open keyring `%s'", keyring);
994 if (ki) {
995 if ((k = key_bytag(&kf, ki)) == 0)
996 die(EXIT_FAILURE, "couldn't find key `%s'", ki);
997 } else {
998 if (!kt)
999 kt = s->type;
1000 if ((k = key_bytype(&kf, kt)) == 0)
1001 die(EXIT_FAILURE, "no appropriate key of type `%s'", kt);
1002 }
1003 key_fulltag(k, &d);
1004 if (exp == KEXP_FOREVER && k->exp != KEXP_FOREVER) {
1005 die(EXIT_FAILURE, "key `%s' expires: can't create nonexpiring signature",
1006 d.buf);
1007 }
1008
1009 /* --- Open files --- */
1010
1011 if (!ifile)
1012 ifp = stdin;
1013 else if ((ifp = fopen(ifile, (f & f_raw) ? "rb" : "r")) == 0) {
1014 die(EXIT_FAILURE, "couldn't open input file `%s': %s",
1015 ifile, strerror(errno));
1016 }
1017
1018 if (!ofile)
1019 ofp = stdout;
1020 else if ((ofp = fopen(ofile, (f & f_bin) ? "wb" : "w")) == 0) {
1021 die(EXIT_FAILURE, "couldn't open output file `%s': %s",
1022 ofile, strerror(errno));
1023 }
1024
1025 h = gch->init();
1026
1027 /* --- Emit the start of the output --- */
1028
1029 binit(&b); b.tag = T_IDENT;
1030 dstr_putf(&b.d, "%s, Catacomb version " VERSION, QUIS);
1031 bemit(&b, ofp, 0, f & f_bin);
1032
1033 breset(&b); b.tag = T_SIGALG; DPUTS(&b.d, s->name);
1034 bemit(&b, ofp, h, f & f_bin);
1035
1036 breset(&b); b.tag = T_HASHALG; DPUTS(&b.d, gch->name);
1037 bemit(&b, ofp, h, f & f_bin);
1038
1039 breset(&b); b.tag = T_KEYID; b.k = k->id;
1040 bemit(&b, ofp, h, f & f_bin);
1041
1042 /* --- Start hashing, and emit the datestamps and things --- */
1043
1044 {
1045 time_t now = time(0);
1046
1047 breset(&b); b.tag = T_DATE; b.t = now; bemit(&b, ofp, h, f & f_bin);
1048 if (exp == KEXP_EXPIRE)
1049 exp = now + 86400 * 28;
1050 breset(&b); b.tag = T_EXPIRE; b.t = exp; bemit(&b, ofp, h, f & f_bin);
1051 if (c) {
1052 breset(&b); b.tag = T_COMMENT; DPUTS(&b.d, c);
1053 bemit(&b, ofp, h, f & f_bin);
1054 }
1055
1056 if (!(f & f_bin))
1057 putc('\n', ofp);
1058 }
1059
1060 /* --- Now hash the various files --- */
1061
1062 for (;;) {
1063
1064 /* --- Stop on an output error --- */
1065
1066 if (ferror(ofp)) {
1067 f |= f_bogus;
1068 break;
1069 }
1070
1071 /* --- Read the next filename to hash --- */
1072
1073 breset(&b);
1074 if (getstring(ifp, &b.d, f & f_raw))
1075 break;
1076 b.tag = T_FILE;
1077 DENSURE(&b.b, h->ops->c->hashsz);
1078 if (fhash(gch, b.d.buf, b.b.buf)) {
1079 moan("Error reading `%s': %s", b.d.buf, strerror(errno));
1080 f |= f_bogus;
1081 } else {
1082 b.b.len += h->ops->c->hashsz;
1083 if (verb) {
1084 fhex(stderr, b.b.buf, b.b.len);
1085 fprintf(stderr, " %s\n", b.d.buf);
1086 }
1087 bemit(&b, ofp, h, f & f_bin);
1088 }
1089 }
1090
1091 /* --- Create the signature --- */
1092
1093 if (!(f & f_bogus)) {
1094 breset(&b);
1095 b.tag = T_SIGNATURE;
1096 DENSURE(&b.d, h->ops->c->hashsz);
1097 h->ops->done(h, b.d.buf);
1098 if ((e = s->sign(k, b.d.buf, h->ops->c->hashsz, &b.b)) != 0) {
1099 moan("error creating signature: %s", key_strerror(e));
1100 f |= f_bogus;
1101 }
1102 if (!(f & f_bogus)) {
1103 bemit(&b, ofp, 0, f & f_bin);
1104 key_used(&kf, k, exp);
1105 }
1106 }
1107
1108 /* --- Tidy up at the end --- */
1109
1110 bdestroy(&b);
1111 if (ifile)
1112 fclose(ifp);
1113 if (ofile) {
1114 if (fclose(ofp))
1115 f |= f_bogus;
1116 } else {
1117 if (fflush(ofp))
1118 f |= f_bogus;
1119 }
1120 if ((e = key_close(&kf)) != 0) {
1121 switch (e) {
1122 case KWRITE_FAIL:
1123 die(EXIT_FAILURE, "couldn't write file `%s': %s",
1124 keyring, strerror(errno));
1125 case KWRITE_BROKEN:
1126 die(EXIT_FAILURE, "keyring file `%s' broken: %s (repair manually)",
1127 keyring, strerror(errno));
1128 }
1129 }
1130 if (f & f_bogus)
1131 die(EXIT_FAILURE, "error(s) occurred while creating signature");
1132 return (EXIT_SUCCESS);
1133
1134 #undef f_raw
1135 #undef f_bin
1136 #undef f_bogus
1137 }
1138
1139 /*----- Signature verification --------------------------------------------*/
1140
1141 static int verify(int argc, char *argv[])
1142 {
1143 #define f_bogus 1u
1144 #define f_bin 2u
1145 #define f_ok 4u
1146
1147 unsigned f = 0;
1148 unsigned verb = 1;
1149 key_file kf;
1150 key *k = 0;
1151 sig *s = sigtab;
1152 const gchash *gch = &rmd160;
1153 dstr d = DSTR_INIT;
1154 ghash *h = 0;
1155 FILE *fp;
1156 block b;
1157 int e;
1158
1159 /* --- Parse the options --- */
1160
1161 for (;;) {
1162 static struct option opts[] = {
1163 { "verbose", 0, 0, 'v' },
1164 { "quiet", 0, 0, 'q' },
1165 { 0, 0, 0, 0 }
1166 };
1167 int i = mdwopt(argc, argv, "+vq", opts, 0, 0, 0);
1168 if (i < 0)
1169 break;
1170 switch (i) {
1171 case 'v':
1172 verb++;
1173 break;
1174 case 'q':
1175 if (verb)
1176 verb--;
1177 break;
1178 default:
1179 f |= f_bogus;
1180 break;
1181 }
1182 }
1183 argc -= optind;
1184 argv += optind;
1185 if ((f & f_bogus) || argc > 1)
1186 die(EXIT_FAILURE, "Usage: verify [-qv] [file]");
1187
1188 /* --- Open the key file, and start reading the input file --- */
1189
1190 if (key_open(&kf, keyring, KOPEN_READ, keyreport, 0))
1191 die(EXIT_FAILURE, "couldn't open keyring `%s'\n", keyring);
1192 if (argc < 1)
1193 fp = stdin;
1194 else {
1195 if ((fp = fopen(argv[0], "rb")) == 0) {
1196 die(EXIT_FAILURE, "couldn't open file `%s': %s\n",
1197 argv[0], strerror(errno));
1198 }
1199 if (getc(fp) == 0) {
1200 ungetc(0, fp);
1201 f |= f_bin;
1202 } else {
1203 fclose(fp);
1204 if ((fp = fopen(argv[0], "r")) == 0) {
1205 die(EXIT_FAILURE, "couldn't open file `%s': %s\n",
1206 argv[0], strerror(errno));
1207 }
1208 }
1209 }
1210
1211 /* --- Read the introductory matter --- */
1212
1213 binit(&b);
1214 for (;;) {
1215 breset(&b);
1216 e = bget(&b, fp, f & f_bin);
1217 if (e < 0)
1218 die(EXIT_FAILURE, "error reading packet: %s\n", errtab[-e]);
1219 if (e >= T_BEGIN)
1220 break;
1221 switch (e) {
1222 case T_IDENT:
1223 if (verb > 2)
1224 printf("INFO ident: `%s'\n", b.d.buf);
1225 break;
1226 case T_SIGALG:
1227 if ((s = getsig(b.d.buf)) == 0) {
1228 if (verb)
1229 printf("FAIL unknown signature algorithm `%s'\n", b.d.buf);
1230 exit(EXIT_FAILURE);
1231 }
1232 if (verb > 2)
1233 printf("INFO signature algorithm: %s\n", s->name);
1234 break;
1235 case T_HASHALG:
1236 if ((gch = gethash(b.d.buf)) == 0) {
1237 if (verb)
1238 printf("FAIL unknown hash function `%s'\n", b.d.buf);
1239 exit(EXIT_FAILURE);
1240 }
1241 if (verb > 2)
1242 printf("INFO hash function algorithm: %s\n", gch->name);
1243 break;
1244 case T_KEYID:
1245 if ((k = key_byid(&kf, b.k)) == 0) {
1246 if (verb)
1247 printf("FAIL key %08lx not found\n", (unsigned long)b.k);
1248 exit(EXIT_FAILURE);
1249 }
1250 if (verb > 2) {
1251 DRESET(&b.d);
1252 key_fulltag(k, &b.d);
1253 printf("INFO key: %s\n", b.d.buf);
1254 }
1255 break;
1256 default:
1257 die(EXIT_FAILURE, "(internal) unknown packet type\n");
1258 break;
1259 }
1260 }
1261
1262 /* --- Initialize the hash function and start reading hashed packets --- */
1263
1264 h = gch->init();
1265
1266 if (!k) {
1267 if (verb)
1268 puts("FAIL no keyid packet found");
1269 exit(EXIT_FAILURE);
1270 }
1271
1272 {
1273 block bb;
1274 binit(&bb);
1275 breset(&bb); bb.tag = T_SIGALG; DPUTS(&bb.d, s->name);
1276 bemit(&bb, 0, h, 0);
1277 breset(&bb); bb.tag = T_HASHALG; DPUTS(&bb.d, gch->name);
1278 bemit(&bb, 0, h, 0);
1279 breset(&bb); bb.tag = T_KEYID; bb.k = k->id;
1280 bemit(&bb, 0, h, 0);
1281 bdestroy(&bb);
1282 }
1283
1284 for (;;) {
1285 switch (e) {
1286 case T_COMMENT:
1287 if (verb > 1)
1288 printf("INFO comment: `%s'\n", b.d.buf);
1289 bemit(&b, 0, h, 0);
1290 break;
1291 case T_DATE:
1292 if (verb > 2) {
1293 DRESET(&b.d);
1294 timestring(b.t, &b.d);
1295 printf("INFO date: %s\n", b.d.buf);
1296 }
1297 bemit(&b, 0, h, 0);
1298 break;
1299 case T_EXPIRE: {
1300 time_t now = time(0);
1301 if (b.t < now) {
1302 if (verb > 1)
1303 puts("BAD signature has expired");
1304 f |= f_bogus;
1305 }
1306 if (verb > 2) {
1307 DRESET(&b.d);
1308 timestring(b.t, &b.d);
1309 printf("INFO expires: %s\n", b.d.buf);
1310 }
1311 bemit(&b, 0, h, 0);
1312 } break;
1313 case T_FILE:
1314 DRESET(&d);
1315 DENSURE(&d, gch->hashsz);
1316 if (fhash(gch, b.d.buf, d.buf)) {
1317 if (verb > 1) {
1318 printf("BAD error reading file `%s': %s\n",
1319 b.d.buf, strerror(errno));
1320 }
1321 f |= f_bogus;
1322 } else if (b.b.len != gch->hashsz ||
1323 memcmp(d.buf, b.b.buf, b.b.len) != 0) {
1324 if (verb > 1)
1325 printf("BAD file `%s' has incorrect hash\n", b.d.buf);
1326 f |= f_bogus;
1327 } else if (verb > 3) {
1328 fputs("INFO hash: ", stdout);
1329 fhex(stdout, b.b.buf, b.b.len);
1330 printf(" %s\n", b.d.buf);
1331 }
1332 bemit(&b, 0, h, 0);
1333 break;
1334 case T_SIGNATURE:
1335 DRESET(&b.d);
1336 DENSURE(&b.d, h->ops->c->hashsz);
1337 b.d.len += h->ops->c->hashsz;
1338 h->ops->done(h, b.d.buf);
1339 e = s->verify(k, b.d.buf, b.d.len, b.b.buf, b.b.len);
1340 if (e != 1) {
1341 if (verb > 1) {
1342 if (e < 0) {
1343 printf("BAD error unpacking key: %s\n", key_strerror(e));
1344 } else
1345 puts("BAD bad signature");
1346 }
1347 f |= f_bogus;
1348 } else if (verb > 2)
1349 puts("INFO good signature");
1350 goto done;
1351 default:
1352 if (verb)
1353 printf("FAIL invalid packet type %i\n", e);
1354 exit(EXIT_FAILURE);
1355 break;
1356 }
1357 breset(&b);
1358 e = bget(&b, fp, f & f_bin);
1359 if (e < 0) {
1360 if (verb)
1361 printf("FAIL error reading packet: %s\n", errtab[-e]);
1362 exit(EXIT_FAILURE);
1363 }
1364 }
1365 done:
1366 bdestroy(&b);
1367 dstr_destroy(&d);
1368 key_close(&kf);
1369 if (fp != stdin)
1370 fclose(fp);
1371 if (verb) {
1372 if (f & f_bogus)
1373 puts("FAIL signature invalid");
1374 else
1375 puts("OK signature verified");
1376 }
1377 return (f & f_bogus ? EXIT_FAILURE : EXIT_SUCCESS);
1378
1379 #undef f_bogus
1380 #undef f_bin
1381 #undef f_ok
1382 }
1383
1384 /*----- Main code ---------------------------------------------------------*/
1385
1386 typedef struct cmd {
1387 const char *name;
1388 int (*func)(int /*argc*/, char */*argv*/[]);
1389 const char *usage;
1390 const char *help;
1391 } cmd;
1392
1393 static cmd cmdtab[] = {
1394 /* { "manifest", manifest, */
1395 /* "manifest [-0] [-o output]" }, */
1396 { "sign", sign,
1397 "sign [-options]\n\
1398 [-0bqv] [-a alg] [-h hash] [-t keytype] [-i keyid]\n\
1399 [-e expire] [-f file] [-o output]", "\
1400 Options:\n\
1401 \n\
1402 -0, --null Read null-terminated filenames from stdin.\n\
1403 -b, --binary Produce a binary output file.\n\
1404 -q, --quiet Produce fewer messages while working.\n\
1405 -v, --verbose Produce more messages while working.\n\
1406 -a, --algorithm=SIGALG Use the SIGALG signature algorithm.\n\
1407 -h, --hash=HASHASL Use the HASHALG message digest algorithm.\n\
1408 -c, --comment=COMMENT Include COMMENT in the output file.\n\
1409 -f, --file=FILE Read filenames to hash from FILE.\n\
1410 -o, --output=FILE Write the signed result to FILE.\n\
1411 -t, --keytype=TYPE Use a key with type TYPE to do the signing.\n\
1412 -i, --keyid=ID Use the key with the given ID to do the signing.\n\
1413 -e, --expire=TIME The signature should expire after TIME.\n\
1414 " },
1415 { "verify", verify,
1416 "verify [-qv] [file]", "\
1417 Options:\n\
1418 \n\
1419 -q, --quiet Produce fewer messages while working.\n\
1420 -v, --verbose Produce more messages while working.\n\
1421 " },
1422 { 0, 0, 0 }
1423 };
1424
1425 /* --- @findcmd@ --- *
1426 *
1427 * Arguments: @const char *name@ = a command name
1428 *
1429 * Returns: Pointer to the command structure.
1430 *
1431 * Use: Looks up a command by name. If the command isn't found, an
1432 * error is reported and the program is terminated.
1433 */
1434
1435 static cmd *findcmd(const char *name)
1436 {
1437 cmd *c, *chosen = 0;
1438 size_t sz = strlen(name);
1439
1440 for (c = cmdtab; c->name; c++) {
1441 if (strncmp(name, c->name, sz) == 0) {
1442 if (c->name[sz] == 0) {
1443 chosen = c;
1444 break;
1445 } else if (chosen)
1446 die(EXIT_FAILURE, "ambiguous command name `%s'", name);
1447 else
1448 chosen = c;
1449 }
1450 }
1451 if (!chosen)
1452 die(EXIT_FAILURE, "unknown command name `%s'", name);
1453 return (chosen);
1454 }
1455
1456 static void version(FILE *fp)
1457 {
1458 pquis(fp, "$, Catacomb version " VERSION "\n");
1459 }
1460
1461 static void usage(FILE *fp)
1462 {
1463 pquis(fp, "Usage: $ [-k keyring] command [args]\n");
1464 }
1465
1466 static void help(FILE *fp, char **argv)
1467 {
1468 cmd *c;
1469
1470 if (*argv) {
1471 c = findcmd(*argv);
1472 fprintf(fp, "Usage: %s [-k keyring] %s\n", QUIS, c->usage);
1473 if (c->help) {
1474 fputc('\n', fp);
1475 fputs(c->help, fp);
1476 }
1477 } else {
1478 version(fp);
1479 fputc('\n', fp);
1480 usage(fp);
1481 fputs("\n\
1482 Create and verify signatures on lists of files.\n\
1483 \n", fp);
1484 for (c = cmdtab; c->name; c++)
1485 fprintf(fp, "%s\n", c->usage);
1486 }
1487 }
1488
1489 /* --- @main@ --- *
1490 *
1491 * Arguments: @int argc@ = number of command line arguments
1492 * @char *argv[]@ = vector of command line arguments
1493 *
1494 * Returns: Zero if successful, nonzero otherwise.
1495 *
1496 * Use: Signs or verifies signatures on lists of files. Useful for
1497 * ensuring that a distribution is unmolested.
1498 */
1499
1500 int main(int argc, char *argv[])
1501 {
1502 unsigned f = 0;
1503
1504 #define f_bogus 1u
1505
1506 /* --- Initialize the library --- */
1507
1508 ego(argv[0]);
1509 sub_init();
1510 rand_noisesrc(RAND_GLOBAL, &noise_source);
1511 rand_seed(RAND_GLOBAL, 160);
1512
1513 /* --- Parse options --- */
1514
1515 for (;;) {
1516 static struct option opts[] = {
1517 { "help", 0, 0, 'h' },
1518 { "version", 0, 0, 'v' },
1519 { "usage", 0, 0, 'u' },
1520 { "keyring", OPTF_ARGREQ, 0, 'k' },
1521 { 0, 0, 0, 0 }
1522 };
1523 int i = mdwopt(argc, argv, "+hvu k:", opts, 0, 0, 0);
1524 if (i < 0)
1525 break;
1526 switch (i) {
1527 case 'h':
1528 help(stdout, argv + optind);
1529 exit(0);
1530 break;
1531 case 'v':
1532 version(stdout);
1533 exit(0);
1534 break;
1535 case 'u':
1536 usage(stdout);
1537 exit(0);
1538 case 'k':
1539 keyring = optarg;
1540 break;
1541 default:
1542 f |= f_bogus;
1543 break;
1544 }
1545 }
1546
1547 argc -= optind;
1548 argv += optind;
1549 optind = 0;
1550 if (f & f_bogus || argc < 1) {
1551 usage(stderr);
1552 exit(EXIT_FAILURE);
1553 }
1554
1555 /* --- Dispatch to the correct subcommand handler --- */
1556
1557 return (findcmd(argv[0])->func(argc, argv));
1558
1559 #undef f_bogus
1560 }
1561
1562 /*----- That's all, folks -------------------------------------------------*/