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