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