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