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