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