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