chiark / gitweb /
Catcrypt tools: Roll out progress indicator stuff from hashsum.
[catacomb] / catcrypt.c
1 /* -*-c-*-
2  *
3  * $Id$
4  *
5  * Command-line encryption tool
6  *
7  * (c) 2004 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 <errno.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40
41 #include <mLib/base64.h>
42 #include <mLib/dstr.h>
43 #include <mLib/mdwopt.h>
44 #include <mLib/quis.h>
45 #include <mLib/report.h>
46 #include <mLib/sub.h>
47
48 #include "buf.h"
49 #include "rand.h"
50 #include "noise.h"
51 #include "mprand.h"
52 #include "key.h"
53 #include "cc.h"
54
55 #include "ectab.h"
56 #include "ptab.h"
57
58 /*----- Static variables --------------------------------------------------*/
59
60 static const char *keyring = "keyring";
61
62 /*----- Data format -------------------------------------------------------*/
63
64 /* --- Overview --- *
65  *
66  * The encrypted message is divided into chunks, each preceded by a two-octet
67  * length.  The chunks don't need to be large -- the idea is that we can
68  * stream the chunks in and out.
69  *
70  * The first chunk is a header.  It contains the decryption key-id, and maybe
71  * the verification key-id if the message is signed.
72  *
73  * Next comes the key-encapsulation chunk.  This is decrypted in some
74  * KEM-specific way to yield a secret hash.  The hash is expanded using an
75  * MGF (or similar) to make a symmetric encryption and MAC key.
76  *
77  * If the message is signed, there comes a signature chunk.  The signature is
78  * on the header and key-encapsulation chunks, and further output of the MGF.
79  * This means that the recipient can modify the message and still have a
80  * valid signature, so it's not useful for proving things to other people;
81  * but it also means that the recipient knows that the message is from
82  * someone who knows the hash, which limits the possiblities to (a) whoever
83  * encrypted the message (good!) and (b) whoever knows the recipient's
84  * private key.
85  *
86  * Then come message chunks.  Each one begins with a MAC over an implicit
87  * sequence number and the ciphertext.  The final chunk's ciphertext is
88  * empty; no other chunk is empty.  Thus can the correct end-of-file be
89  * discerned.
90  */
91
92 /*----- Chunk I/O ---------------------------------------------------------*/
93
94 static void chunk_write(enc *e, buf *b)
95 {
96   octet l[2];
97   size_t n = BLEN(b);
98   assert(n <= MASK16);
99   STORE16(l, n);
100   if (e->ops->write(e, l, 2) ||
101       e->ops->write(e, BBASE(b), BLEN(b)))
102     die(EXIT_FAILURE, "error writing output: %s", strerror(errno));
103 }
104
105 static void chunk_read(enc *e, dstr *d, buf *b)
106 {
107   octet l[2];
108   size_t n;
109
110   dstr_reset(d);
111   errno = 0;
112   if (e->ops->read(e, l, 2) != 2)
113     goto err;
114   n = LOAD16(l);
115   dstr_ensure(d, n);
116   if (e->ops->read(e, d->buf, n) != n)
117     goto err;
118   d->len = n;
119   buf_init(b, d->buf, d->len);
120   return;
121
122 err:
123   if (!errno) die(EXIT_FAILURE, "unexpected end-of-file on input");
124   else die(EXIT_FAILURE, "error reading input: %s", strerror(errno));
125 }
126
127 /*----- Encryption --------------------------------------------------------*/
128
129 static int encrypt(int argc, char *argv[])
130 {
131   const char *fn, *of = 0, *kn = "ccrypt", *skn = 0;
132   FILE *ofp = 0;
133   FILE *fp = 0;
134   const char *ef = "binary";
135   fprogress ff;
136   const char *err;
137   int i;
138   int en;
139   size_t n, chsz;
140   dstr d = DSTR_INIT;
141   octet *tag, *ct;
142   buf b;
143   size_t seq;
144   char bb[65536];
145   unsigned f = 0;
146   key_file kf;
147   key *k;
148   key *sk = 0;
149   kem *km;
150   sig *s = 0;
151   gcipher *cx, *c;
152   gmac *m;
153   ghash *h;
154   const encops *eo;
155   enc *e;
156
157 #define f_bogus 1u
158 #define f_nocheck 2u
159 #define f_progress 4u
160
161   for (;;) {
162     static const struct option opt[] = {
163       { "key",          OPTF_ARGREQ,    0,      'k' },
164       { "sign-key",     OPTF_ARGREQ,    0,      's' },
165       { "armour",       0,              0,      'a' },
166       { "armor",        0,              0,      'a' },
167       { "format",       OPTF_ARGREQ,    0,      'f' },
168       { "output",       OPTF_ARGREQ,    0,      'o' },
169       { "progress",     0,              0,      'p' },
170       { "nocheck",      0,              0,      'C' },
171       { 0,              0,              0,      0 }
172     };
173     i = mdwopt(argc, argv, "k:s:af:o:pC", opt, 0, 0, 0);
174     if (i < 0) break;
175     switch (i) {
176       case 'k': kn = optarg; break;
177       case 's': skn = optarg; break;
178       case 'a': ef = "pem"; break;
179       case 'f': ef = optarg; break;
180       case 'o': of = optarg; break;
181       case 'p': f |= f_progress; break;
182       case 'C': f |= f_nocheck; break;
183       default: f |= f_bogus; break;
184     }
185   }
186   if (argc - optind > 1 || (f & f_bogus))
187     die(EXIT_FAILURE, "Usage: encrypt [-OPTIONS] [FILE]");
188
189   if (key_open(&kf, keyring, KOPEN_READ, key_moan, 0))
190     die(EXIT_FAILURE, "can't open keyring `%s'", keyring);
191   if ((k = key_bytag(&kf, kn)) == 0)
192     die(EXIT_FAILURE, "key `%s' not found", kn);
193   if (skn && (sk = key_bytag(&kf, skn)) == 0)
194     die(EXIT_FAILURE, "key `%s' not found", skn);
195
196   if ((eo = getenc(ef)) == 0)
197     die(EXIT_FAILURE, "encoding `%s' not found", ef);
198
199   fn = optind < argc ? argv[optind++] : "-";
200   if (strcmp(fn, "-") == 0)
201     fp = stdin;
202   else if ((fp = fopen(fn, "rb")) == 0) {
203     die(EXIT_FAILURE, "couldn't open file `%s': %s",
204         fn, strerror(errno));
205   }
206
207   if (!of || strcmp(of, "-") == 0)
208     ofp = stdout;
209   else if ((ofp = fopen(of, eo->wmode)) == 0) {
210     die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
211         ofp, strerror(errno));
212   }
213
214   dstr_reset(&d);
215   key_fulltag(k, &d);
216   e = initenc(eo, ofp, "CATCRYPT ENCRYPTED MESSAGE");
217   km = getkem(k, "cckem", 0);
218   if (!(f & f_nocheck) && (err = km->ops->check(km)) != 0)
219     moan("key %s fails check: %s", d.buf, err);
220   if (sk) {
221     dstr_reset(&d);
222     key_fulltag(sk, &d);
223     s = getsig(sk, "ccsig", 1);
224     if ((err = s->ops->check(s)) != 0)
225       moan("key %s fails check: %s", d.buf, err);
226   }
227
228   /* --- Build the header chunk --- */
229
230   dstr_reset(&d);
231   dstr_ensure(&d, 256);
232   buf_init(&b, d.buf, 256);
233   buf_putu32(&b, k->id);
234   if (sk) buf_putu32(&b, sk->id);
235   assert(BOK(&b));
236   if (s) GH_HASHBUF16(s->h, BBASE(&b), BLEN(&b));
237   chunk_write(e, &b);
238
239   /* --- Build the KEM chunk --- */
240
241   dstr_reset(&d);
242   if (setupkem(km, &d, &cx, &c, &m))
243     die(EXIT_FAILURE, "failed to encapsulate key");
244   buf_init(&b, d.buf, d.len);
245   BSTEP(&b, d.len);
246   if (s) GH_HASHBUF16(s->h, BBASE(&b), BLEN(&b));
247   chunk_write(e, &b);
248
249   /* --- Write the signature chunk --- */
250
251   if (s) {
252     GC_ENCRYPT(cx, 0, bb, 1024);
253     GH_HASH(s->h, bb, 1024);
254     dstr_reset(&d);
255     if ((en = s->ops->doit(s, &d)) != 0)
256       die(EXIT_FAILURE, "error creating signature: %s", key_strerror(en));
257     buf_init(&b, d.buf, d.len);
258     BSTEP(&b, d.len);
259     chunk_write(e, &b);
260   }
261
262   /* --- Now do the main crypto --- */
263
264   if (f & f_progress) {
265     if (fprogress_init(&ff, fn, fp)) {
266       die(EXIT_FAILURE, "failed to initialize progress display: %s",
267           strerror(errno));
268     }
269   }
270
271   assert(GC_CLASS(c)->blksz <= sizeof(bb));
272   dstr_ensure(&d, sizeof(bb) + GM_CLASS(m)->hashsz);
273   seq = 0;
274   chsz = MASK16 - GM_CLASS(m)->hashsz;
275   for (;;) {
276     h = GM_INIT(m);
277     GH_HASHU32(h, seq);
278     seq++;
279     if (GC_CLASS(c)->blksz) {
280       GC_ENCRYPT(cx, 0, bb, GC_CLASS(c)->blksz);
281       GC_SETIV(c, bb);
282     }
283     n = fread(bb, 1, chsz, fp);
284     if (!n) break;
285     if (f & f_progress) fprogress_update(&ff, n);
286     buf_init(&b, d.buf, d.sz);
287     tag = buf_get(&b, GM_CLASS(m)->hashsz);
288     ct = buf_get(&b, n);
289     assert(tag); assert(ct);
290     GC_ENCRYPT(c, bb, ct, n);
291     GH_HASH(h, ct, n);
292     GH_DONE(h, tag);
293     GH_DESTROY(h);
294     chunk_write(e, &b);
295   }
296
297   /* --- Final terminator packet --- */
298
299   buf_init(&b, d.buf, d.sz);
300   tag = buf_get(&b, GM_CLASS(m)->hashsz);
301   assert(tag);
302   GH_DONE(h, tag);
303   GH_DESTROY(h);
304   chunk_write(e, &b);
305
306   /* --- All done --- */
307
308   if (f & f_progress) fprogress_done(&ff);
309   e->ops->encdone(e);
310   GM_DESTROY(m);
311   GC_DESTROY(c);
312   GC_DESTROY(cx);
313   freeenc(e);
314   if (s) freesig(s);
315   freekem(km);
316   if (fp != stdin) fclose(fp);
317   if (of) fclose(ofp);
318   key_close(&kf);
319   dstr_destroy(&d);
320   return (0);
321
322 #undef f_bogus
323 #undef f_nocheck
324 #undef f_progress
325 }
326
327 /*---- Decryption ---------------------------------------------------------*/
328
329 static int decrypt(int argc, char *argv[])
330 {
331   const char *fn, *of = 0;
332   FILE *ofp = 0, *rfp = 0;
333   FILE *fp = 0;
334   const char *ef = "binary";
335   fprogress ff;
336   int i;
337   size_t n;
338   dstr d = DSTR_INIT;
339   buf b;
340   key_file kf;
341   size_t seq;
342   uint32 id;
343   key *k;
344   key *sk = 0;
345   kem *km;
346   sig *s = 0;
347   gcipher *cx;
348   gcipher *c;
349   ghash *h;
350   gmac *m;
351   octet *tag;
352   unsigned f = 0;
353   const encops *eo;
354   const char *err;
355   int verb = 1;
356   enc *e;
357
358 #define f_bogus 1u
359 #define f_buffer 2u
360 #define f_nocheck 4u
361 #define f_progress 8u
362
363   for (;;) {
364     static const struct option opt[] = {
365       { "armour",       0,              0,      'a' },
366       { "armor",        0,              0,      'a' },
367       { "buffer",       0,              0,      'b' },
368       { "verbose",      0,              0,      'v' },
369       { "quiet",        0,              0,      'q' },
370       { "nocheck",      0,              0,      'C' },
371       { "format",       OPTF_ARGREQ,    0,      'f' },
372       { "output",       OPTF_ARGREQ,    0,      'o' },
373       { "progress",     0,              0,      'p' },
374       { 0,              0,              0,      0 }
375     };
376     i = mdwopt(argc, argv, "abf:o:pqvC", opt, 0, 0, 0);
377     if (i < 0) break;
378     switch (i) {
379       case 'a': ef = "pem"; break;
380       case 'b': f |= f_buffer; break;
381       case 'v': verb++; break;
382       case 'q': if (verb) verb--; break;
383       case 'C': f |= f_nocheck; break;
384       case 'f': ef = optarg; break;
385       case 'o': of = optarg; break;
386       case 'p': f |= f_progress; break;
387       default: f |= f_bogus; break;
388     }
389   }
390   if (argc - optind > 1 || (f & f_bogus))
391     die(EXIT_FAILURE, "Usage: decrypt [-OPTIONS] [FILE]");
392
393   if ((eo = getenc(ef)) == 0)
394     die(EXIT_FAILURE, "encoding `%s' not found", ef);
395
396   fn = optind < argc ? argv[optind++] : "-";
397   if (strcmp(fn, "-") == 0)
398     fp = stdin;
399   else if ((fp = fopen(fn, eo->rmode)) == 0) {
400     die(EXIT_FAILURE, "couldn't open file `%s': %s",
401         fn, strerror(errno));
402   }
403
404   if (key_open(&kf, keyring, KOPEN_READ, key_moan, 0))
405     die(EXIT_FAILURE, "can't open keyring `%s'", keyring);
406
407   e = initdec(eo, fp, checkbdry, "CATCRYPT ENCRYPTED MESSAGE");
408
409   if (f & f_progress) {
410     if (fprogress_init(&ff, fn, fp)) {
411       die(EXIT_FAILURE, "failed to initialize progress display: %s",
412           strerror(errno));
413     }
414   }
415
416   /* --- Read the header chunk --- */
417
418   chunk_read(e, &d, &b);
419   if (f & f_progress)
420     fprogress_update(&ff, BLEFT(&b)*e->ops->ncook/e->ops->nraw);
421   if (buf_getu32(&b, &id)) {
422     if (f & f_progress) fprogress_done(&ff);
423     if (verb) printf("FAIL malformed header: missing keyid\n");
424     exit(EXIT_FAILURE);
425   }
426   if ((k = key_byid(&kf, id)) == 0) {
427     if (f & f_progress) fprogress_done(&ff);
428     if (verb) printf("FAIL key id %08lx not found\n", (unsigned long)id);
429     exit(EXIT_FAILURE);
430   }
431   if (BLEFT(&b)) {
432     if (buf_getu32(&b, &id)) {
433       if (f & f_progress) fprogress_done(&ff);
434       if (verb) printf("FAIL malformed header: missing signature keyid\n");
435       exit(EXIT_FAILURE);
436     }
437     if ((sk = key_byid(&kf, id)) == 0) {
438       if (f & f_progress) fprogress_done(&ff);
439       if (verb) printf("FAIL key id %08lx not found\n", (unsigned long)id);
440       exit(EXIT_FAILURE);
441     }
442   }
443   if (BLEFT(&b)) {
444     if (f & f_progress) fprogress_done(&ff);
445     if (verb) printf("FAIL malformed header: junk at end\n");
446     exit(EXIT_FAILURE);
447   }
448   if (sk) {
449     s = getsig(sk, "ccsig", 0);
450     if (!(f & f_nocheck) && verb && (err = s->ops->check(s)) != 0) {
451       dstr_reset(&d);
452       key_fulltag(sk, &d);
453       printf("WARN verification key %s fails check: %s\n", d.buf, err);
454     }
455     GH_HASHBUF16(s->h, BBASE(&b), BSZ(&b));
456   }
457
458   /* --- Find the key --- */
459
460   km = getkem(k, "cckem", 1);
461
462   /* --- Read the KEM chunk --- */
463
464   chunk_read(e, &d, &b);
465   if (f & f_progress)
466     fprogress_update(&ff, BLEFT(&b)*e->ops->ncook/e->ops->nraw);
467   if (setupkem(km, &d, &cx, &c, &m)) {
468     if (f & f_progress) fprogress_done(&ff);
469     if (verb) printf("FAIL failed to decapsulate key\n");
470     exit(EXIT_FAILURE);
471   }
472   if (s) GH_HASHBUF16(s->h, d.buf, d.len);
473
474   /* --- Verify the signature, if there is one --- */
475
476   if (sk) {
477     dstr_reset(&d);
478     dstr_ensure(&d, 1024);
479     GC_ENCRYPT(cx, 0, d.buf, 1024);
480     GH_HASH(s->h, d.buf, 1024);
481     chunk_read(e, &d, &b);
482     if (f & f_progress)
483       fprogress_update(&ff, BLEFT(&b)*e->ops->ncook/e->ops->nraw);
484     if (s->ops->doit(s, &d)) {
485       if (f & f_progress) fprogress_done(&ff);
486       if (verb) printf("FAIL signature verification failed\n");
487       exit(EXIT_FAILURE);
488     }
489     if (verb) {
490       dstr_reset(&d);
491       key_fulltag(sk, &d);
492       if (f & f_progress) fprogress_clear(&ff);
493       printf("INFO good-signature %s\n", d.buf);
494     }
495     freesig(s);
496   } else if (verb) {
497     if (f & f_progress) fprogress_clear(&ff);
498     printf("INFO no-signature\n");
499   }
500
501   /* --- Now decrypt the main body --- */
502
503   if (!of || strcmp(of, "-") == 0) {
504     ofp = stdout;
505     f |= f_buffer;
506   }
507   if (!(f & f_buffer)) {
508     if ((ofp = fopen(of, "wb")) == 0) {
509       if (f & f_progress) fprogress_done(&ff);
510       die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
511           ofp, strerror(errno));
512     }
513     rfp = ofp;
514   } else if ((rfp = tmpfile()) == 0) {
515     if (f & f_progress) fprogress_done(&ff);
516     die(EXIT_FAILURE, "couldn't create temporary file: %s", strerror(errno));
517   }
518
519   seq = 0;
520   dstr_ensure(&d, GC_CLASS(c)->blksz);
521   dstr_ensure(&d, 4);
522   for (;;) {
523     if (GC_CLASS(c)->blksz) {
524       GC_ENCRYPT(cx, 0, d.buf, GC_CLASS(c)->blksz);
525       GC_SETIV(c, d.buf);
526     }
527     h = GM_INIT(m);
528     GH_HASHU32(h, seq);
529     seq++;
530     chunk_read(e, &d, &b);
531     if (f & f_progress)
532       fprogress_update(&ff, BLEFT(&b)*e->ops->ncook/e->ops->nraw);
533     if ((tag = buf_get(&b, GM_CLASS(m)->hashsz)) == 0) {
534       if (f & f_progress) fprogress_done(&ff);
535       if (verb) printf("FAIL bad ciphertext chunk: no tag\n");
536       exit(EXIT_FAILURE);
537     }
538     GH_HASH(h, BCUR(&b), BLEFT(&b));
539     if (memcmp(tag, GH_DONE(h, 0), GM_CLASS(m)->hashsz) != 0) {
540       if (f & f_progress) fprogress_done(&ff);
541       if (verb)
542         printf("FAIL bad ciphertext chunk: authentication failure\n");
543       exit(EXIT_FAILURE);
544     }
545     GH_DESTROY(h);
546     if (!BLEFT(&b))
547       break;
548     GC_DECRYPT(c, BCUR(&b), BCUR(&b), BLEFT(&b));
549     if (fwrite(BCUR(&b), 1, BLEFT(&b), rfp) != BLEFT(&b)) {
550       if (f & f_progress) fprogress_done(&ff);
551       if (verb) printf("FAIL error writing output: %s\n", strerror(errno));
552       exit(EXIT_FAILURE);
553     }
554   }
555
556   if (f & f_progress) fprogress_done(&ff);
557   if (fflush(rfp) || ferror(rfp)) {
558     if (verb) printf("FAIL error writing output: %s\n", strerror(errno));
559     exit(EXIT_FAILURE);
560   }
561   if (f & f_buffer) {
562     if (!ofp && (ofp = fopen(of, "wb")) == 0) {
563       die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
564           of, strerror(errno));
565     }
566     rewind(rfp);
567     if (f & f_progress) fprogress_init(&ff, "copying buffer", rfp);
568     dstr_reset(&d);
569     dstr_ensure(&d, 65536);
570     if (verb && ofp == stdout) printf("DATA\n");
571     for (;;) {
572       n = fread(d.buf, 1, d.sz, rfp);
573       if (!n) break;
574       if (f & f_progress) fprogress_update(&ff, n);
575       if (fwrite(d.buf, 1, n, ofp) < n) {
576         if (f & f_progress) fprogress_done(&ff);
577         die(EXIT_FAILURE, "error writing output: %s", strerror(errno));
578       }
579     }
580     if (f & f_progress) fprogress_done(&ff);
581     if (ferror(rfp) || fclose(rfp))
582       die(EXIT_FAILURE, "error unbuffering output: %s", strerror(errno));
583   }
584
585   e->ops->decdone(e);
586   if (verb && ofp != stdout)
587     printf("OK decrypted successfully\n");
588   if (ofp && (fflush(ofp) || ferror(ofp) || fclose(ofp)))
589       die(EXIT_FAILURE, "error writing output: %s", strerror(errno));
590   freeenc(e);
591   GC_DESTROY(c);
592   GC_DESTROY(cx);
593   GM_DESTROY(m);
594   freekem(km);
595   if (fp != stdin) fclose(fp);
596   key_close(&kf);
597   dstr_destroy(&d);
598   return (0);
599
600 #undef f_bogus
601 #undef f_buffer
602 #undef f_nocheck
603 #undef f_progress
604 }
605
606 /*----- Main code ---------------------------------------------------------*/
607
608 #define LISTS(LI)                                                       \
609   LI("Lists", list,                                                     \
610      listtab[i].name, listtab[i].name)                                  \
611   LI("Key-encapsulation mechanisms", kem,                               \
612      kemtab[i].name, kemtab[i].name)                                    \
613   LI("Signature schemes", sig,                                          \
614      sigtab[i].name, sigtab[i].name)                                    \
615   LI("Encodings", enc,                                                  \
616      enctab[i].name, enctab[i].name)                                    \
617   LI("Symmetric encryption algorithms", cipher,                         \
618      gciphertab[i], gciphertab[i]->name)                                \
619   LI("Hash functions", hash,                                            \
620      ghashtab[i], ghashtab[i]->name)                                    \
621   LI("Message authentication codes", mac,                               \
622      gmactab[i], gmactab[i]->name)
623
624 MAKELISTTAB(listtab, LISTS)
625
626 int cmd_show(int argc, char *argv[])
627 {
628   return (displaylists(listtab, argv + 1));
629 }
630
631 static int cmd_help(int, char **);
632
633 static cmd cmdtab[] = {
634   { "help", cmd_help, "help [COMMAND...]" },
635   { "show", cmd_show, "show [ITEM...]" },
636   CMD_ENCODE,
637   CMD_DECODE,
638   { "encrypt", encrypt,
639     "encrypt [-apC] [-k TAG] [-s TAG] [-f FORMAT]\n\t\
640 [-o OUTPUT] [FILE]", "\
641 Options:\n\
642 \n\
643 -a, --armour            Same as `-f pem'.\n\
644 -f, --format=FORMAT     Encode as FORMAT.\n\
645 -k, --key=TAG           Use public encryption key named by TAG.\n\
646 -s, --sign-key=TAG      Use private signature key named by TAG.\n\
647 -o, --output=FILE       Write output to FILE.\n\
648 -p, --progress          Show progress on large files.\n\
649 -C, --nocheck           Don't check the public key.\n\
650 " },
651   { "decrypt", decrypt,
652     "decrypt [-abpqvC] [-f FORMAT] [-o OUTPUT] [FILE]", "\
653 Options:\n\
654 \n\
655 -a, --armour            Same as `-f pem'.\n\
656 -b, --buffer            Buffer output until we're sure we have it all.\n\
657 -f, --format=FORMAT     Decode as FORMAT.\n\
658 -o, --output=FILE       Write output to FILE.\n\
659 -p, --progress          Show progress on large files.\n\
660 -q, --quiet             Produce fewer messages.\n\
661 -v, --verbose           Produce more verbose messages.\n\
662 -C, --nocheck           Don't check the private key.\n\
663 " },
664   { 0, 0, 0 }
665 };
666
667 static int cmd_help(int argc, char **argv)
668 {
669   sc_help(cmdtab, stdout, argv + 1);
670   return (0);
671 }
672
673 void version(FILE *fp)
674 {
675   pquis(fp, "$, Catacomb version " VERSION "\n");
676 }
677
678 static void usage(FILE *fp)
679 {
680   pquis(fp, "Usage: $ [-k KEYRING] COMMAND [ARGS]\n");
681 }
682
683 void help_global(FILE *fp)
684 {
685   usage(fp);
686   fputs("\n\
687 Encrypt and decrypt files.\n\
688 \n\
689 Global command-line options:\n\
690 \n\
691 -h, --help [COMMAND...] Show this help message, or help for COMMANDs.\n\
692 -v, --version           Show program version number.\n\
693 -u, --usage             Show a terse usage message.\n\
694 \n\
695 -k, --keyring=FILE      Read keys from FILE.\n",
696         fp);
697 }
698
699 /* --- @main@ --- *
700  *
701  * Arguments:   @int argc@ = number of command line arguments
702  *              @char *argv[]@ = vector of command line arguments
703  *
704  * Returns:     Zero if successful, nonzero otherwise.
705  *
706  * Use:         Encrypts or decrypts files.
707  */
708
709 int main(int argc, char *argv[])
710 {
711   unsigned f = 0;
712
713 #define f_bogus 1u
714
715   /* --- Initialize the library --- */
716
717   ego(argv[0]);
718   sub_init();
719   rand_noisesrc(RAND_GLOBAL, &noise_source);
720   rand_seed(RAND_GLOBAL, 160);
721
722   /* --- Parse options --- */
723
724   for (;;) {
725     static struct option opts[] = {
726       { "help",         0,              0,      'h' },
727       { "version",      0,              0,      'v' },
728       { "usage",        0,              0,      'u' },
729       { "keyring",      OPTF_ARGREQ,    0,      'k' },
730       { 0,              0,              0,      0 }
731     };
732     int i = mdwopt(argc, argv, "+hvu k:", opts, 0, 0, 0);
733     if (i < 0)
734       break;
735     switch (i) {
736       case 'h':
737         sc_help(cmdtab, stdout, argv + optind);
738         exit(0);
739         break;
740       case 'v':
741         version(stdout);
742         exit(0);
743         break;
744       case 'u':
745         usage(stdout);
746         exit(0);
747       case 'k':
748         keyring = optarg;
749         break;
750       default:
751         f |= f_bogus;
752         break;
753     }
754   }
755
756   argc -= optind;
757   argv += optind;
758   optind = 0;
759   if (f & f_bogus || argc < 1) {
760     usage(stderr);
761     exit(EXIT_FAILURE);
762   }
763
764   /* --- Dispatch to the correct subcommand handler --- */
765
766   return (findcmd(cmdtab, argv[0])->cmd(argc, argv));
767
768 #undef f_bogus
769 }
770
771 /*----- That's all, folks -------------------------------------------------*/