chiark / gitweb /
Add some more vectors, and a whinge about how Skipjack test vectors are.
[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 -------------------------------------------------*/