chiark / gitweb /
Check freshness on signatures.
[catacomb] / catsign.c
1 /* -*-c-*-
2  *
3  * $Id$
4  *
5  * Sign files
6  *
7  * (c) 2005 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 /*----- Header files ------------------------------------------------------*/
31
32 #include "config.h"
33
34 #include <ctype.h>
35 #include <errno.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <time.h>
40
41 #include <mLib/base64.h>
42 #include <mLib/dstr.h>
43 #include <mLib/mdwopt.h>
44 #include <mLib/quis.h>
45 #include <mLib/report.h>
46 #include <mLib/sub.h>
47
48 #include "buf.h"
49 #include "rand.h"
50 #include "noise.h"
51 #include "mprand.h"
52 #include "key.h"
53 #include "getdate.h"
54 #include "cc.h"
55
56 #include "ectab.h"
57 #include "ptab.h"
58
59 /*----- Utilities ---------------------------------------------------------*/
60
61 /* --- @keyreport@ --- *
62  *
63  * Arguments:   @const char *file@ = filename containing the error
64  *              @int line@ = line number in file
65  *              @const char *err@ = error text message
66  *              @void *p@ = unimportant pointer
67  *
68  * Returns:     ---
69  *
70  * Use:         Reports errors during the opening of a key file.
71  */
72
73 static void keyreport(const char *file, int line, const char *err, void *p)
74 {
75   moan("error in keyring `%s' at line `%s': %s", file, line, err);
76 }
77
78 /*----- Static variables --------------------------------------------------*/
79
80 static const char *keyring = "keyring";
81
82 /*----- Data formats ------------------------------------------------------*
83  *
84  * Our crypto stuff is split into three parts: a header describing the key,
85  * the message itself, and the signature.  In a detached signature, the
86  * message is separate, and the header and signature are combined into one
87  * encoded chunk.  In an attached signature, the message is included.  There
88  * are two forms of message: binary and text.  Binary messages are divided
89  * into chunks, each preceded by a 2-octet length, and terminated by a
90  * zero-length chunk.  Text messages are byte-stuffed, as for RFC821, and
91  * trailing whitespace before a newline is ignored.
92  */
93
94 typedef struct sigmsg {
95   unsigned f;                           /* Flags */
96 #define F_DETACH 1u
97 #define F_BINARY 2u
98 #define F_SIGMASK 3u
99 #define F_HASHMASK (F_BINARY)
100   uint32 keyid;                         /* Key identifier */
101   time_t t;                             /* When the signature was made */
102   dstr kh;                              /* Key fingerprint (sanity check) */
103   dstr sig;                             /* Signature */
104   sig *s;                               /* Signature algorithm */
105 } sigmsg;
106
107 #define F_MIDLINE 16u
108 #define F_EOF 32u
109 #define F_NOCLOSE 64u
110 #define F_BOGUS 128u
111 #define F_BUFFER 256u
112 #define F_UTC 512u
113
114 /*----- Chunk I/O ---------------------------------------------------------*/
115
116 static void chunk_write(enc *e, const void *p, size_t n)
117 {
118   octet b[2];
119
120   assert(n <= 0xffff);
121   STORE16(b, n);
122   if (e->ops->write(e, b, 2) || e->ops->write(e, p, n))
123     die(EXIT_FAILURE, "error writing output: %s", strerror(errno));
124 }
125
126 static size_t chunk_read(enc *e, void *p)
127 {
128   octet b[2];
129   size_t n;
130
131   if (e->ops->read(e, b, 2) != 2)
132     goto err;
133   n = LOAD16(b);
134   if (n && e->ops->read(e, p, n) != n)
135     goto err;
136   return (n);
137
138 err:
139   if (!errno) die(EXIT_FAILURE, "unexpected end-of-file on input");
140   else die(EXIT_FAILURE, "error reading input: %s", strerror(errno));
141   return (0);
142 }
143
144 /*----- Message canonification --------------------------------------------*/
145
146 #define MSGBUFSZ 65536
147 #define MSGBUFTHRESH 32768
148
149 typedef struct msgcanon {
150   unsigned f;
151   FILE *fp;
152   enc *e;
153   size_t (*read)(struct msgcanon *, void *);
154   void (*write)(struct msgcanon *, const void *, size_t);
155   void (*close)(struct msgcanon *);
156 } msgcanon;
157
158 #define MC_INIT { 0 }
159
160 static size_t textread(msgcanon *m, void *bp)
161 {
162   size_t n = 0, nsp = 0;
163   int ch;
164   char *b = bp;
165   unsigned f = m->f;
166
167   if (f & F_EOF) return (0);
168   for (;;) {
169     if ((ch = getc(m->fp)) == EOF) goto eof;
170     if (!(f & (F_DETACH | F_MIDLINE)) && ch == '.') {
171       ch = getc(m->fp);
172       if (ch == '\n') goto eof;
173     }
174     if (ch == '\n') {
175       n -= nsp; nsp = 0;
176       if (n >= MSGBUFTHRESH) goto full;
177       b[n++] = ch;
178       f &= ~F_MIDLINE;
179     } else if (isspace(ch)) {
180       f |= F_MIDLINE;
181       if (n >= MSGBUFSZ) goto full;
182       b[n++] = ch; nsp++;
183     } else {
184       f |= F_MIDLINE;
185       nsp = 0;
186       if (n >= MSGBUFTHRESH) goto full;
187       b[n++] = ch;
188     }
189   }
190 eof:
191   f |= F_EOF;
192   goto done;
193 full:
194   ungetc(ch, m->fp);
195 done:
196   m->f = f;
197   return (n);
198 }
199
200 static void textwrite(msgcanon *m, const void *bp, size_t n)
201 {
202   const char *p = bp, *l = p + n;
203   unsigned f = m->f;
204
205   while (p < l) {
206     if (!(f & (F_MIDLINE | F_DETACH)) &&
207         (*p == '.' || *p == '-'))
208       putc('.', m->fp);
209     if (*p == '\n') f &= ~F_MIDLINE;
210     else f |= F_MIDLINE;
211     putc(*p, m->fp);
212     p++;
213   }
214   m->f = f;
215 }
216
217 static size_t binreadembed(msgcanon *m, void *bp)
218   { return (chunk_read(m->e, bp)); }
219 static size_t binreaddetach(msgcanon *m, void *bp)
220   { return (fread(bp, 1, MSGBUFSZ, m->fp)); }
221
222 static void binwriteembed(msgcanon *m, const void *bp, size_t n)
223   { chunk_write(m->e, bp, n); }
224
225 static void nullwrite(msgcanon *m, const void *bp, size_t n) { ; }
226
227 static void mcsetup_readfile(msgcanon *m, unsigned f, const char *fn)
228 {
229   m->f = F_DETACH | (f & F_BINARY);
230   if (!fn || strcmp(fn, "-") == 0) {
231     m->fp = stdin;
232     m->f |= F_NOCLOSE;
233   } else if ((m->fp = fopen(fn, (f & F_BINARY) ? "rb" : "r")) == 0)
234     die(EXIT_FAILURE, "couldn't open file `%s': %s", fn, strerror(errno));
235   m->read = (f & F_BINARY) ? binreaddetach : textread;
236 }
237
238 static void mcsetup_writenull(msgcanon *m) { m->write = nullwrite; }
239
240 static void mcsetup_read(msgcanon *m, unsigned f, enc **ee, const char *dfn)
241 {
242   enc *e = *ee;
243
244   m->f = f | F_NOCLOSE;
245
246   if (dfn && !(f & F_DETACH)) die(EXIT_FAILURE, "signature is not detached");
247   if (f & F_BINARY) {
248     if (!(f & F_DETACH)) {
249       m->e = e;
250       *ee = 0;
251       m->fp = e->fp;
252       m->read = binreadembed;
253     } else {
254       m->read = binreaddetach;
255       if (!dfn || strcmp(dfn, "-") == 0)
256         m->fp = stdin;
257       else if ((m->fp = fopen(dfn, "rb")) == 0)
258         die(EXIT_FAILURE, "can't open `%s': %s", dfn, strerror(errno));
259       else
260         m->f &= ~F_NOCLOSE;
261     }
262   } else {
263     m->read = textread;
264     if (!(f & F_DETACH)) {
265       if (e->ops->decdone(e) || ferror(e->fp))
266         die(EXIT_FAILURE, "error at end of signature header");
267       m->fp = e->fp;
268       e->ops->destroy(e);
269       *ee = 0;
270     } else if (!dfn || strcmp(dfn, "-") == 0)
271       m->fp = stdin;
272     else if ((m->fp = fopen(dfn, "r")) == 0)
273       die(EXIT_FAILURE, "can't read file `%s': %s", dfn, strerror(errno));
274     else
275       m->f &= ~F_NOCLOSE;
276   }
277 }
278
279 static void mcsetup_write(msgcanon *m, unsigned f, enc **ee)
280 {
281   enc *e = *ee;
282
283   m->f = f | F_NOCLOSE;
284
285   if (f & F_DETACH)
286     m->write = nullwrite;
287   else if (f & F_BINARY) {
288     m->e = e;
289     *ee = 0;
290     m->fp = e->fp;
291     m->write = binwriteembed;
292   } else {
293     if (e->ops->encdone(e) || ferror(e->fp))
294       die(EXIT_FAILURE, "error at end of signature header");
295     m->fp = e->fp;
296     e->ops->destroy(e);
297     *ee = 0;
298     m->write = textwrite;
299   }
300 }
301
302 static void mc_endread(msgcanon *m, const encops *eops, enc **ee)
303 {
304   if (!(m->f & F_DETACH)) {
305     if (m->f & F_BINARY)
306       *ee = m->e;
307     else
308       *ee = initdec(eops, m->fp, checkbdry, "CATSIGN SIGNATURE");
309   }
310   if (m->fp && !(m->f & F_NOCLOSE)) {
311     if (ferror(m->fp) || fclose(m->fp))
312       die(EXIT_FAILURE, "error closing message file: %s", strerror(errno));
313   }    
314 }
315
316 static void mc_endwrite(msgcanon *m, const encops *eops, enc **ee)
317 {
318   if (!(m->f & F_BINARY)) {
319     if (m->f & F_MIDLINE) putc('\n', m->fp);
320     if (!(m->f & F_DETACH)) {
321       putc('.', m->fp); putc('\n', m->fp);
322       *ee = initenc(eops, m->fp, "CATSIGN SIGNATURE");
323     }
324   } else if (!(m->f & F_DETACH)) {
325     chunk_write(m->e, 0, 0);
326     *ee = m->e;
327   }
328   if (m->fp && !(m->f & F_NOCLOSE)) {
329     if (fflush(m->fp) || ferror(m->fp) || fclose(m->fp))
330       die(EXIT_FAILURE, "error closing message file: %s", strerror(errno));
331   }    
332 }
333
334 /*----- Signature reading and writing -------------------------------------*/
335
336 static void sig_init(sigmsg *s, unsigned f, uint32 keyid)
337 {
338   s->f = f & F_SIGMASK;
339   s->keyid = keyid;
340   time(&s->t);
341   s->s = 0;
342   dstr_create(&s->kh);
343   dstr_create(&s->sig);
344 }
345
346 static void sig_destroy(sigmsg *s)
347 {
348   dstr_destroy(&s->kh);
349   dstr_destroy(&s->sig);
350   if (s->s) freesig(s->s);
351 }
352
353 static void sigtobuffer(sigmsg *s, buf *b, int hashp)
354 {
355   kludge64 t;
356
357   ASSIGN64(t, s->t);
358   if (hashp) buf_putu16(b, s->f & F_HASHMASK);
359   else buf_putu16(b, s->f & F_SIGMASK);
360   buf_putu32(b, s->keyid);
361   buf_putu32(b, HI64(t));
362   buf_putu32(b, LO64(t));
363   buf_putdstr16(b, &s->kh);
364   assert(BOK(b));
365 }
366
367 static void dohash(ghash *h, const void *p, size_t n)
368 {
369 /*   trace_block(1, "hashing", p, n); */
370   GH_HASH(h, p, n);
371 }
372
373 static void sig_hash(sigmsg *s)
374 {
375   octet bb[16384];
376   buf b;
377
378   buf_init(&b, bb, sizeof(bb));
379   sigtobuffer(s, &b, 1);
380   dohash(s->s->h, BBASE(&b), BLEN(&b));
381 }
382
383 static void keyhash(key *k, sig *s, dstr *d)
384 {
385   ghash *h;
386   key_filter kf;
387
388   h = GH_INIT(GH_CLASS(s->h));
389   kf.f = KCAT_PUB;
390   kf.m = KF_CATMASK;
391   key_fingerprint(k, h, &kf);
392   dstr_ensure(d, GH_CLASS(h)->hashsz);
393   GH_DONE(h, d->buf + d->len);
394   d->len += GH_CLASS(h)->hashsz;
395   GH_DESTROY(h);
396 }
397
398 static void sig_writeheader(enc *e, sigmsg *s)
399 {
400   octet bb[16384];
401   buf b;
402
403   buf_init(&b, bb, sizeof(bb));
404   sigtobuffer(s, &b, 0);
405   chunk_write(e, BBASE(&b), BLEN(&b));
406 }
407
408 static void sig_writesig(enc *e, sigmsg *s)
409   { chunk_write(e, s->sig.buf, s->sig.len); }
410
411 static void diechoke(const char *m, void *p)
412   { die(EXIT_FAILURE, "%s%s%s", p, p ? ": " : "", m); }
413
414 static void sig_readheader(enc *e, sigmsg *s,
415                            void (*choke)(const char *, void *), void *p)
416 {
417   uint16 f;
418   octet bb[MSGBUFSZ];
419   uint32 x, y;
420   kludge64 t;
421   buf b;
422   size_t n;
423
424   n = chunk_read(e, bb);
425   buf_init(&b, bb, n);
426   if (buf_getu16(&b, &f)) choke("missing flags", p);
427   if (buf_getu32(&b, &x)) choke("missing keyid", p);
428   sig_init(s, f, x);
429   if (buf_getu32(&b, &x) || buf_getu32(&b, &y))
430     choke("missing datestamp", p);
431   SET64(t, x, y); s->t = GET64(time_t, t);
432   if (buf_getdstr16(&b, &s->kh))
433     choke("missing key hash", p);
434   if (BLEFT(&b))
435     choke("junk at end", p);
436 }
437
438 static void sig_readsig(enc *e, sigmsg *s)
439 {
440   octet bb[MSGBUFSZ];
441   size_t n;
442
443   n = chunk_read(e, bb);
444   dstr_putm(&s->sig, bb, n);
445 }
446
447 /*----- Signing -----------------------------------------------------------*/
448
449 static int sign(int argc, char *argv[])
450 {
451   const char *ef = "binary", *fn = 0, *of = 0, *kn = "ccsig", *err;
452   unsigned f = 0;
453   key_file kf;
454   key *k;
455   sigmsg s;
456   FILE *ofp = 0;
457   int i;
458   char bb[MSGBUFSZ];
459   size_t n;
460   dstr d = DSTR_INIT;  
461   const encops *eo;
462   msgcanon mc_in = MC_INIT, mc_out = MC_INIT;
463   enc *e;
464
465   for (;;) {
466     static const struct option opt[] = {
467       { "armour",       0,              0,      'a' },
468       { "armor",        0,              0,      'a' },
469       { "binary",       0,              0,      'b' },
470       { "detach",       0,              0,      'd' },
471       { "key",          OPTF_ARGREQ,    0,      'k' },
472       { "format",       OPTF_ARGREQ,    0,      'f' },
473       { "output",       OPTF_ARGREQ,    0,      'o' },
474       { "text",         0,              0,      't' },
475       { 0,              0,              0,      0 }
476     };
477     i = mdwopt(argc, argv, "k:f:o:abdt", opt, 0, 0, 0);
478     if (i < 0) break;
479     switch (i) {
480       case 'k': kn = optarg; break;
481       case 'f': ef = optarg; break;
482       case 'o': of = optarg; break;
483       case 'a': ef = "pem"; break;
484       case 't': f &= ~F_BINARY; break;
485       case 'b': f |= F_BINARY; break;
486       case 'd': f |= F_DETACH; break;
487       default: f |= F_BOGUS; break;
488     }
489   }
490   if (argc - optind > 1 || (f & F_BOGUS))
491     die(EXIT_FAILURE, "Usage: sign [-OPTIONS] [FILE]");
492
493   if (key_open(&kf, keyring, KOPEN_READ, keyreport, 0))
494     die(EXIT_FAILURE, "can't open keyring `%s'", keyring);
495   if ((k = key_bytag(&kf, kn)) == 0)
496     die(EXIT_FAILURE, "key `%s' not found", kn);
497
498   if ((eo = getenc(ef)) == 0)
499     die(EXIT_FAILURE, "encoding `%s' not found", ef);
500
501   fn = (optind >= argc) ? 0 : argv[optind++];
502
503   if (!of || strcmp(of, "-") == 0)
504     ofp = stdout;
505   else if ((ofp = fopen(of, eo->wmode)) == 0) {
506     die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
507         ofp, strerror(errno));
508   }
509
510   /* --- Start the work --- */
511
512   sig_init(&s, f, k->id);
513   dstr_reset(&d);
514   key_fulltag(k, &d);
515   s.s = getsig(k, "ccsig", 1);
516   if ((err = s.s->ops->check(s.s)) != 0)
517     moan("key %s fails check: %s", d.buf, err);
518   keyhash(k, s.s, &s.kh);
519   e = initenc(eo, ofp,
520               (f & F_DETACH) ? "CATSIGN SIGNATURE" :
521               (f & F_BINARY) ? "CATSIGN MESSAGE" :
522               "CATSIGN MESSAGE HEADER");
523   sig_writeheader(e, &s);
524
525   /* --- Hash the message --- */
526
527   mcsetup_readfile(&mc_in, f, fn);
528   mcsetup_write(&mc_out, f, &e);
529   sig_hash(&s);
530   for (;;) {
531     n = mc_in.read(&mc_in, bb);
532     if (!n) break;
533     dohash(s.s->h, bb, n);
534     mc_out.write(&mc_out, bb, n);
535   }
536   mc_endread(&mc_in, 0, 0);
537   mc_endwrite(&mc_out, eo, &e);
538
539   /* --- Write the signature --- */
540
541   if (s.s->ops->doit(s.s, &s.sig))
542     die(EXIT_FAILURE, "signature failed");
543   sig_writesig(e, &s);
544   e->ops->encdone(e);
545   if (fflush(ofp) || ferror(ofp) || fclose(ofp))
546     die(EXIT_FAILURE, "error writing signature: %s", strerror(errno));
547
548   /* --- All done --- */
549
550   freeenc(e);
551   key_close(&kf);
552   sig_destroy(&s);
553   dstr_destroy(&d);
554   return (0);
555 }
556
557 /*----- Verifying ---------------------------------------------------------*/
558
559 typedef struct vrfctx {
560   unsigned f, m;
561   int verb;
562   const char *what;
563 } vrfctx;
564
565 static int vrfbdry(const char *b, void *p)
566 {
567   vrfctx *v = p;
568
569   if (strcmp(b, "CATSIGN MESSAGE") == 0) {
570     v->f |= F_BINARY;
571     v->m |= F_BINARY | F_DETACH;
572     return (1);
573   } else if (strcmp(b, "CATSIGN MESSAGE HEADER") == 0) {
574     v->m |= F_BINARY | F_DETACH;
575     return (1);
576   } else if (strcmp(b, "CATSIGN SIGNATURE") == 0) {
577     v->f |= F_DETACH;
578     v->m |= F_DETACH;
579     return (1);
580   } else
581     return (0);
582 }
583
584 static void vrfchoke(const char *m, void *p)
585 {
586   vrfctx *v = p;
587   if (v->verb) printf("FAIL %s: %s\n", v->what, m);
588   exit(EXIT_FAILURE);
589 }
590
591 static int verify(int argc, char *argv[])
592 {
593   const char *ef = "binary", *of = 0, *dfn = 0, *kn = 0, *err;
594   vrfctx v = { 0, 0, 1 };
595   key_file kf;
596   key *k, *kk = 0;
597   sigmsg s;
598   FILE *fp, *ofp = 0, *rfp = 0;
599   struct tm *tm;
600   int i;
601   char bb[MSGBUFSZ];
602   size_t n;
603   time_t t_fresh = 0;
604   dstr d = DSTR_INIT, dd = DSTR_INIT;
605   const encops *eo;
606   msgcanon mc_in = MC_INIT;
607   enc *e;
608
609   for (;;) {
610     static const struct option opt[] = {
611       { "armour",       0,              0,      'a' },
612       { "armor",        0,              0,      'a' },
613       { "buffer",       0,              0,      'b' },
614       { "key",          OPTF_ARGREQ,    0,      'k' },
615       { "format",       OPTF_ARGREQ,    0,      'f' },
616       { "output",       OPTF_ARGREQ,    0,      'o' },
617       { "quiet",        0,              0,      'q' },
618       { "utc",          0,              0,      'u' },
619       { "fresh-time",   0,              0,      't' },
620       { "gmt",          0,              0,      'u' },
621       { "verbose",      0,              0,      'v' },
622       { 0,              0,              0,      0 }
623     };
624     i = mdwopt(argc, argv, "k:f:o:abqt:uv", opt, 0, 0, 0);
625     if (i < 0) break;
626     switch (i) {
627       case 'a': ef = "pem"; break;
628       case 'b': v.f |= F_BUFFER; break;
629       case 'k': kn = optarg; break;
630       case 'f': ef = optarg; break;
631       case 'o': of = optarg; break;
632       case 'u': v.f |= F_UTC; break;
633       case 't':
634         if (strcmp(optarg, "always") == 0) t_fresh = 0;
635         else if ((t_fresh = get_date(optarg, 0)) < 0)
636           die(EXIT_FAILURE, "bad freshness time");
637         break;
638       case 'q': if (v.verb > 0) v.verb--; break;
639       case 'v': if (v.verb < 10) v.verb++; break;
640       default: v.f |= F_BOGUS; break;
641     }
642   }
643   if (argc - optind > 2 || (v.f & F_BOGUS))
644     die(EXIT_FAILURE, "Usage: verify [-OPTIONS] [FILE [MESSAGE]]");
645
646   if ((eo = getenc(ef)) == 0)
647     die(EXIT_FAILURE, "encoding `%s' not found", ef);
648
649   if (optind >= argc)
650     fp = stdin;
651   else if (strcmp(argv[optind], "-") == 0) {
652     fp = stdin;
653     optind++;
654   } else if ((fp = fopen(argv[optind], eo->rmode)) == 0) {
655     die(EXIT_FAILURE, "couldn't open file `%s': %s",
656         argv[optind], strerror(errno));
657   } else
658     optind++;
659
660   if (key_open(&kf, keyring, KOPEN_READ, keyreport, 0))
661     die(EXIT_FAILURE, "can't open keyring `%s'", keyring);
662   if (kn && (kk = key_bytag(&kf, kn)) == 0)
663     die(EXIT_FAILURE, "key `%s' not found", kn);
664
665   e = initdec(eo, fp, vrfbdry, &v);
666
667   /* --- Read the header chunk --- */
668
669   v.what = "malformed header";
670   sig_readheader(e, &s, vrfchoke, &v);
671
672   if (((s.f ^ v.f) & v.m) != 0) {
673     if (v.verb) printf("FAIL boundary string inconsistent with contents\n");
674     exit(EXIT_FAILURE);
675   }
676   v.f |= s.f;
677
678   if ((k = key_byid(&kf, s.keyid)) == 0) {
679     if (v.verb) printf("FAIL key id %08lx not found\n",
680                        (unsigned long)s.keyid);
681     exit(EXIT_FAILURE);
682   }
683   if (kk && k->id != kk->id) {
684     if (v.verb) {
685       dstr_reset(&d); key_fulltag(k, &d);
686       dstr_reset(&dd); key_fulltag(kk, &dd);
687       printf("FAIL signing key is %s; expected key %s\n", d.buf, dd.buf);
688     }
689     exit(EXIT_FAILURE);
690   }
691
692   s.s = getsig(k, "ccsig", 0);
693   dstr_reset(&d); key_fulltag(k, &d);
694   if (v.verb && (err = s.s->ops->check(s.s)) != 0)
695     printf("WARN verification key %s fails check: %s\n", d.buf, err);
696
697   dstr_reset(&dd); keyhash(k, s.s, &dd);
698   if (dd.len != s.kh.len || memcmp(dd.buf, s.kh.buf, dd.len) != 0) {
699     if (v.verb) printf("FAIL key hash mismatch\n");
700     exit(EXIT_FAILURE);
701   }    
702
703   /* --- Now a merry dance --- */
704
705   if (v.f & F_DETACH)
706     sig_readsig(e, &s);
707   if (optind < argc)
708     dfn = argv[optind++];
709   mcsetup_read(&mc_in, v.f, &e, dfn);
710
711   if (!of && (v.f & F_DETACH)) {
712     rfp = ofp = 0;
713     v.f &= ~F_BUFFER;
714   } else if (!of || strcmp(of, "-") == 0) {
715     v.f |= F_BUFFER;
716     ofp = stdout;
717   }
718   if (of && !(v.f & F_BUFFER)) {
719     if ((ofp = fopen(of, (v.f & F_BINARY) ? "wb" : "w")) == 0) {
720       die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
721           of, strerror(errno));
722     }
723     rfp = ofp;
724   } else if ((rfp = tmpfile()) == 0)
725     die(EXIT_FAILURE, "couldn't create temporary file: %s", strerror(errno));
726
727   /* --- Read the message and verify the signature --- */
728
729   sig_hash(&s);
730   for (;;) {
731     n = mc_in.read(&mc_in, bb);
732     if (!n) break;
733     dohash(s.s->h, bb, n);
734     if (rfp) fwrite(bb, 1, n, rfp);
735   }
736   mc_endread(&mc_in, eo, &e);
737   if (!(v.f & F_DETACH))
738     sig_readsig(e, &s);
739   if (rfp && (ferror(rfp) || fflush(rfp))) {
740     if (v.verb) printf("FAIL error writing message: %s\n", strerror(errno));
741     exit(EXIT_FAILURE);
742   }
743
744   /* --- Check the signature --- */
745
746   if (s.s->ops->doit(s.s, &s.sig)) {
747     if (v.verb) printf("FAIL signature verification failed\n");
748     exit(EXIT_FAILURE);
749   }
750   if (t_fresh && s.t < t_fresh) {
751     if (v.verb) printf("FAIL signature is stale\n");
752     exit(EXIT_FAILURE);
753   }
754   if (s.t > time(0)) {
755     if (v.verb) printf("FAIL signature timestamp in the future\n");
756     exit(EXIT_FAILURE);
757   }
758   if (v.verb) {
759     tm = (v.f & F_UTC) ? gmtime(&s.t) : localtime(&s.t);
760     strftime(bb, sizeof(bb), "%Y-%m-%d %H:%M:%S %Z", tm);
761     printf("INFO good-signature %s\n", d.buf);
762     printf("INFO date %s\n", bb);
763   }
764
765   /* --- Unbuffer buffered input --- */
766
767   if (v.f & F_BUFFER) {
768     if (!ofp && (ofp = fopen(of, "wb")) == 0) {
769       die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
770           of, strerror(errno));
771     }
772     rewind(rfp);
773     if (v.verb && ofp == stdout) printf("DATA\n");
774     for (;;) {
775       n = fread(bb, 1, sizeof(bb), rfp);
776       if (!n) break;
777       if (fwrite(bb, 1, n, ofp) < n)
778         die(EXIT_FAILURE, "error writing output: %s", strerror(errno));
779     }
780     if (ferror(rfp) || fclose(rfp))
781       die(EXIT_FAILURE, "error unbuffering output: %s", strerror(errno));
782   }
783   if (ofp && (fflush(ofp) || ferror(ofp) || fclose(ofp)))
784       die(EXIT_FAILURE, "error writing output: %s", strerror(errno));    
785
786   /* --- Tidy up --- */
787
788   e->ops->decdone(e);
789   if (v.verb && ofp != stdout)
790     printf("OK verified successfully\n");
791   freeenc(e);
792   key_close(&kf);
793   sig_destroy(&s);
794   dstr_destroy(&d);
795   dstr_destroy(&dd);
796   return (0);  
797 }
798
799 /*----- Reformatting ------------------------------------------------------*/
800
801 static int format(int argc, char *argv[])
802 {
803   const char *ief = "binary", *oef = "binary";
804   const char *dfn = 0, *of = 0, *mf = 0;
805   sigmsg s;
806   FILE *fp, *ofp = 0, *mfp = 0;
807   int i;
808   size_t n;
809   msgcanon mc_in = MC_INIT, mc_out = MC_INIT;
810   char bb[MSGBUFSZ];
811   vrfctx v = { 0, 0, 1 };
812   unsigned f = 0, fm = ~F_SIGMASK, sf;
813   const encops *ieo, *oeo;
814   enc *ie, *oe;
815
816   for (;;) {
817     static const struct option opt[] = {
818       { "armour-in",    0,              0,      'a' },
819       { "armor-in",     0,              0,      'a' },
820       { "armour-out",   0,              0,      'A' },
821       { "armor-out",    0,              0,      'A' },
822       { "detach",       0,              0,      'D' },
823       { "embed",        0,              0,      'E' },
824       { "format-in",    OPTF_ARGREQ,    0,      'f' },
825       { "format-out",   OPTF_ARGREQ,    0,      'F' },
826       { "message",      OPTF_ARGREQ,    0,      'm' },
827       { "output",       OPTF_ARGREQ,    0,      'o' },
828       { 0,              0,              0,      0 }
829     };
830     i = mdwopt(argc, argv, "f:F:m:o:aADE", opt, 0, 0, 0);
831     if (i < 0) break;
832     switch (i) {
833       case 'a': ief = "pem"; break;
834       case 'A': oef = "pem"; break;
835       case 'f': ief = optarg; break;
836       case 'F': oef = optarg; break;
837       case 'D': f |= F_DETACH; fm |= F_DETACH; break;
838       case 'E': f &= ~F_DETACH; fm |= F_DETACH; break;
839       case 'm': mf = optarg; break;
840       case 'o': of = optarg; break;
841       default: f |= F_BOGUS; break;
842     }
843   }
844
845   if (argc - optind > 2 || (f & F_BOGUS))
846     die(EXIT_FAILURE, "Usage: format [-OPTIONS] [FILE [MESSAGE]]");
847
848   if ((ieo = getenc(ief)) == 0)
849     die(EXIT_FAILURE, "encoding `%s' not found", ief);
850   if ((oeo = getenc(oef)) == 0)
851     die(EXIT_FAILURE, "encoding `%s' not found", oef);
852
853   if (optind >= argc)
854     fp = stdin;
855   else if (strcmp(argv[optind], "-") == 0) {
856     fp = stdin;
857     optind++;
858   } else if ((fp = fopen(argv[optind], ieo->rmode)) == 0) {
859     die(EXIT_FAILURE, "couldn't open file `%s': %s",
860         argv[optind], strerror(errno));
861   } else
862     optind++;
863
864   if (optind < argc)
865     dfn = argv[optind++];
866
867   ie = initdec(ieo, fp, vrfbdry, &v);
868
869   /* --- Read the header chunk --- */
870
871   sig_readheader(ie, &s, diechoke, "malformed header");
872
873   if (((s.f ^ v.f) & v.m) != 0)
874     moan("boundary string inconsistent with contents (ignoring)");
875   
876   mcsetup_read(&mc_in, s.f, &ie, dfn);
877
878   /* --- Prepare the output stuff --- */
879
880   if (!of && !mf) of = "-";
881   sf = s.f;
882   f = (f & fm) | (s.f & ~fm);
883   s.f = f & F_SIGMASK;
884
885   if (sf & F_DETACH)
886     sig_readsig(ie, &s);
887
888   if (!of)
889     mcsetup_writenull(&mc_out);
890   else {
891     if (strcmp(of, "-") == 0)
892       ofp = stdout;
893     else if ((ofp = fopen(of, oeo->wmode)) == 0) {
894       die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
895           of, strerror(errno));
896     }
897     oe = initenc(oeo, ofp,
898                  (f & F_DETACH) ? "CATSIGN SIGNATURE" :
899                  (f & F_BINARY) ? "CATSIGN MESSAGE" :
900                  "CATSIGN MESSAGE HEADER");
901     sig_writeheader(oe, &s);
902     mcsetup_write(&mc_out, f, &oe);
903   }
904
905   if (mf) {
906     if (strcmp(mf, "-") == 0)
907       mfp = stdout;
908     else if ((mfp = fopen(mf, (f & F_BINARY) ? "wb" : "w")) == 0) {
909       die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
910           mf, strerror(errno));
911     }
912   }
913
914   /* --- Wade through the message body --- */
915
916   for (;;) {
917     n = mc_in.read(&mc_in, bb);
918     if (!n) break;
919     mc_out.write(&mc_out, bb, n);
920     if (mfp) fwrite(bb, 1, n, mfp);
921   }
922   mc_endread(&mc_in, ieo, &ie);
923   if (of) mc_endwrite(&mc_out, oeo, &oe);
924
925   /* --- Write the signature --- */
926
927   if (!(sf & F_DETACH))
928     sig_readsig(ie, &s);
929   if (of) {
930     sig_writesig(oe, &s);
931     oe->ops->encdone(oe);
932   }
933
934   /* --- All done --- */
935
936   ie->ops->decdone(ie);
937   if (ferror(fp) || fclose(fp))
938     die(EXIT_FAILURE, "error reading input signature: %s", strerror(errno));
939   if (ofp && (fflush(ofp) || ferror(ofp) || fclose(ofp)))
940     die(EXIT_FAILURE, "error writing output signature: %s", strerror(errno));
941   if (mfp && (fflush(mfp) || ferror(mfp) || fclose(mfp)))
942     die(EXIT_FAILURE, "error writing output message: %s", strerror(errno));
943   freeenc(ie);
944   if (of) freeenc(oe);
945   sig_destroy(&s);
946   return (0);
947 }
948
949 static void infochoke(const char *m, void *p)
950 {
951   vrfctx *v = p;
952   printf("BAD %s: %s\n", v->what, m);
953   exit(EXIT_FAILURE);
954 }
955
956 static void infokeyreport(const char *file, int line,
957                           const char *err, void *p)
958 { /*whatever*/; }
959
960 static int info(int argc, char *argv[])
961 {
962   const char *ef = "binary";
963   vrfctx v = { 0, 0, 1 };
964   key_file kf;
965   key *k;
966   sigmsg s;
967   FILE *fp;
968   int i;
969   struct tm *tm;
970   char bb[256];
971   dstr d = DSTR_INIT;
972   const encops *eo;
973   enc *e;
974
975   for (;;) {
976     static const struct option opt[] = {
977       { "armour",       0,              0,      'a' },
978       { "armor",        0,              0,      'a' },
979       { "format",       OPTF_ARGREQ,    0,      'f' },
980       { "gmt",          0,              0,      'u' },
981       { "utc",          0,              0,      'u' },
982       { 0,              0,              0,      0 }
983     };
984     i = mdwopt(argc, argv, "f:au", opt, 0, 0, 0);
985     if (i < 0) break;
986     switch (i) {
987       case 'a': ef = "pem"; break;
988       case 'f': ef = optarg; break;
989       case 'u': v.f |= F_UTC; break;
990       default: v.f |= F_BOGUS; break;
991     }
992   }
993   if (argc - optind > 1 || (v.f & F_BOGUS))
994     die(EXIT_FAILURE, "Usage: info [-OPTIONS] [FILE]");
995
996   if ((eo = getenc(ef)) == 0)
997     die(EXIT_FAILURE, "encoding `%s' not found", ef);
998
999   if (optind >= argc)
1000     fp = stdin;
1001   else if (strcmp(argv[optind], "-") == 0) {
1002     fp = stdin;
1003     optind++;
1004   } else if ((fp = fopen(argv[optind], eo->rmode)) == 0) {
1005     die(EXIT_FAILURE, "couldn't open file `%s': %s",
1006         argv[optind], strerror(errno));
1007   } else
1008     optind++;
1009
1010   if (key_open(&kf, keyring, KOPEN_READ, infokeyreport, 0)) {
1011     printf("NOTE can't open keyring `%s'\n", keyring);
1012     keyring = 0;
1013   }
1014   e = initdec(eo, fp, vrfbdry, &v);
1015
1016   v.what = "malformed header";
1017   sig_readheader(e, &s, infochoke, &v);
1018
1019   printf("INFO flags %sdetach %sbinary\n",
1020          (s.f & F_DETACH) ? "" : "!",
1021          (s.f & F_BINARY) ? "" : "!");
1022
1023   if (((s.f ^ v.f) & v.m) != 0) {
1024     printf("WARN boundary string inconsistent with contents\n");
1025     printf("INFO expected-flags");
1026     if (v.m & F_DETACH) printf(" %sdetach", (v.f & F_DETACH) ? "" : "!");
1027     if (v.m & F_BINARY) printf(" %sbinary", (v.f & F_BINARY) ? "" : "!");
1028     putchar('\n');
1029   }
1030   v.f |= s.f;
1031
1032   tm = (v.f & F_UTC) ? gmtime(&s.t) : localtime(&s.t);
1033   strftime(bb, sizeof(bb), "%Y-%m-%d %H:%M:%S %Z", tm);
1034   printf("INFO date %s\n", bb);
1035
1036   if (keyring && (k = key_byid(&kf, s.keyid)) != 0) {
1037     dstr_reset(&d); key_fulltag(k, &d);
1038     printf("INFO key %s\n", d.buf);
1039   } else
1040     printf("INFO unknown-key %08lx\n", (unsigned long)s.keyid);
1041
1042   if (keyring) key_close(&kf);
1043   dstr_destroy(&d);
1044   sig_destroy(&s);
1045   return (0);
1046 }
1047
1048 /*----- Main code ---------------------------------------------------------*/
1049
1050 #define LISTS(LI)                                                       \
1051   LI("Lists", list,                                                     \
1052      listtab[i].name, listtab[i].name)                                  \
1053   LI("Signature schemes", sig,                                          \
1054      sigtab[i].name, sigtab[i].name)                                    \
1055   LI("Encodings", enc,                                                  \
1056      enctab[i].name, enctab[i].name)                                    \
1057   LI("Hash functions", hash,                                            \
1058      ghashtab[i], ghashtab[i]->name)
1059
1060 MAKELISTTAB(listtab, LISTS)
1061
1062 int cmd_show(int argc, char *argv[])
1063 {
1064   return (displaylists(listtab, argv + 1));
1065 }
1066
1067 static int cmd_help(int, char **);
1068
1069 static cmd cmdtab[] = {
1070   { "help", cmd_help, "help [COMMAND...]" },
1071   { "show", cmd_show, "show [ITEM...]" },
1072   CMD_ENCODE,
1073   CMD_DECODE,
1074   { "sign", sign,
1075     "sign [-adt] [-k TAG] [-f FORMAT] [-o OUTPUT] [FILE]", "\
1076 Options:\n\
1077 \n\
1078 -a, --armour            Same as `-f pem'.\n\
1079 -b, --binary            Treat the input message as binary data.\n\
1080 -d, --detach            Produce a detached signature.\n\
1081 -f, --format=FORMAT     Encode as FORMAT.\n\
1082 -k, --key=TAG           Use public encryption key named by TAG.\n\
1083 -o, --output=FILE       Write output to FILE.\n\
1084 -t, --text              Canonify input message as a text file.\n\
1085 " },
1086   { "verify", verify,
1087     "verify [-abquv] [-f FORMAT] [-k TAG] [-o OUTPUT]\n\t\
1088 [FILE [MESSAGE]]", "\
1089 Options:\n\
1090 \n\
1091 -a, --armour            Same as `-f pem'.\n\
1092 -b, --buffer            Buffer message until signature is verified.\n\
1093 -f, --format=FORMAT     Decode as FORMAT.\n\
1094 -k, --key=TAG           Require that the message be signed by key TAG.\n\
1095 -o, --output=FILE       Write message to FILE.\n\
1096 -q, --quiet             Produce fewer messages.\n\
1097 -t, --freshtime=TIME    Only accept signatures made after this time.\n\
1098 -u, --utc               Show dates in UTC rather than local time.\n\
1099 -v, --verbose           Produce more verbose messages.\n\
1100 " },
1101   { "info", info,
1102     "info [-au] [-f FORMAT] [FILE]", "\
1103 Options:\n\
1104 \n\
1105 -a, --armour            Same as `-f pem'.\n\
1106 -f, --format=FORMAT     Decode as FORMAT.\n\
1107 -u, --utc               Show dates in UTC rather than local time.\n\
1108 "},
1109   { "format", format,
1110     "format [-auADE] [-f FORMAT] [-F format] [-m FILE] [-o FILE]\n\t\
1111 [FILE [MESSAGE]]", "\
1112 Options:\n\
1113 \n\
1114 -a, --armour-in         Same as `-f pem'.\n\
1115 -A, --armour-out        Same as `-F pem'.\n\
1116 -D, --detach            Create detached signature.\n\
1117 -E, --embed             Create signature with embedded message.\n\
1118 -f, --format-in=FORMAT  Decode input as FORMAT.\n\
1119 -F, --format-out=FORMAT Encode output as FORMAT.\n\
1120 -m, --message=FILE      Write message to FILE.\n\
1121 -o, --output=FILE       Write new signature to FILE.\n\
1122 "},
1123   { 0, 0, 0 }
1124 }; /* " Emacs seems confused. */
1125
1126 static int cmd_help(int argc, char **argv)
1127 {
1128   sc_help(cmdtab, stdout, argv + 1);
1129   return (0);
1130 }
1131
1132 void version(FILE *fp)
1133 {
1134   pquis(fp, "$, Catacomb version " VERSION "\n");
1135 }
1136
1137 static void usage(FILE *fp)
1138 {
1139   pquis(fp, "Usage: $ [-k KEYRING] COMMAND [ARGS]\n");
1140 }
1141
1142 void help_global(FILE *fp)
1143 {
1144   usage(fp);
1145   fputs("\n\
1146 Sign and verify data.\n\
1147 \n\
1148 Global command-line options:\n\
1149 \n\
1150 -h, --help [COMMAND...] Show this help message, or help for COMMANDs.\n\
1151 -v, --version           Show program version number.\n\
1152 -u, --usage             Show a terse usage message.\n\
1153 \n\
1154 -k, --keyring=FILE      Read keys from FILE.\n",
1155         fp);
1156 }
1157
1158 /* --- @main@ --- *
1159  *
1160  * Arguments:   @int argc@ = number of command line arguments
1161  *              @char *argv[]@ = vector of command line arguments
1162  *
1163  * Returns:     Zero if successful, nonzero otherwise.
1164  *
1165  * Use:         Encrypts or decrypts files.
1166  */
1167
1168 int main(int argc, char *argv[])
1169 {
1170   unsigned f = 0;
1171
1172 #define f_bogus 1u
1173
1174   /* --- Initialize the library --- */
1175
1176   ego(argv[0]);
1177   sub_init();
1178   rand_noisesrc(RAND_GLOBAL, &noise_source);
1179   rand_seed(RAND_GLOBAL, 160);
1180 /*   trace_on(stderr, 1); */
1181
1182   /* --- Parse options --- */
1183
1184   for (;;) {
1185     static struct option opts[] = {
1186       { "help",         0,              0,      'h' },
1187       { "version",      0,              0,      'v' },
1188       { "usage",        0,              0,      'u' },
1189       { "keyring",      OPTF_ARGREQ,    0,      'k' },
1190       { 0,              0,              0,      0 }
1191     };
1192     int i = mdwopt(argc, argv, "+hvu k:", opts, 0, 0, 0);
1193     if (i < 0)
1194       break;
1195     switch (i) {
1196       case 'h':
1197         sc_help(cmdtab, stdout, argv + optind);
1198         exit(0);
1199         break;
1200       case 'v':
1201         version(stdout);
1202         exit(0);
1203         break;
1204       case 'u':
1205         usage(stdout);
1206         exit(0);
1207       case 'k':
1208         keyring = optarg;
1209         break;
1210       default:
1211         f |= f_bogus;
1212         break;
1213     }
1214   }
1215
1216   argc -= optind;
1217   argv += optind;
1218   optind = 0;
1219   if (f & f_bogus || argc < 1) {
1220     usage(stderr);
1221     exit(EXIT_FAILURE);
1222   }
1223
1224   /* --- Dispatch to the correct subcommand handler --- */
1225
1226   return (findcmd(cmdtab, argv[0])->cmd(argc, argv));
1227
1228 #undef f_bogus
1229 }
1230
1231 /*----- That's all, folks -------------------------------------------------*/