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