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