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