7 * (c) 2005 Straylight/Edgeware
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of Catacomb.
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.
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.
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,
30 /*----- Header files ------------------------------------------------------*/
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>
58 /*----- Utilities ---------------------------------------------------------*/
60 /* --- @keyreport@ --- *
62 * Arguments: @const char *file@ = filename containing the error
63 * @int line@ = line number in file
64 * @const char *err@ = error text message
65 * @void *p@ = unimportant pointer
69 * Use: Reports errors during the opening of a key file.
72 static void keyreport(const char *file, int line, const char *err, void *p)
74 moan("error in keyring `%s' at line `%s': %s", file, line, err);
77 /*----- Static variables --------------------------------------------------*/
79 static const char *keyring = "keyring";
81 /*----- Data formats ------------------------------------------------------*
83 * Our crypto stuff is split into three parts: a header describing the key,
84 * the message itself, and the signature. In a detached signature, the
85 * message is separate, and the header and signature are combined into one
86 * encoded chunk. In an attached signature, the message is included. There
87 * are two forms of message: binary and text. Binary messages are divided
88 * into chunks, each preceded by a 2-octet length, and terminated by a
89 * zero-length chunk. Text messages are byte-stuffed, as for RFC821, and
90 * trailing whitespace before a newline is ignored.
93 typedef struct sigmsg {
94 unsigned f; /* Flags */
98 #define F_HASHMASK (F_BINARY)
99 uint32 keyid; /* Key identifier */
100 time_t t; /* When the signature was made */
101 dstr kh; /* Key fingerprint (sanity check) */
102 dstr sig; /* Signature */
103 sig *s; /* Signature algorithm */
106 #define F_MIDLINE 16u
108 #define F_NOCLOSE 64u
110 #define F_BUFFER 256u
113 /*----- Chunk I/O ---------------------------------------------------------*/
115 static void chunk_write(enc *e, const void *p, size_t n)
121 if (e->ops->write(e, b, 2) || e->ops->write(e, p, n))
122 die(EXIT_FAILURE, "error writing output: %s", strerror(errno));
125 static size_t chunk_read(enc *e, void *p)
130 if (e->ops->read(e, b, 2) != 2)
133 if (n && e->ops->read(e, p, n) != n)
138 if (!errno) die(EXIT_FAILURE, "unexpected end-of-file on input");
139 else die(EXIT_FAILURE, "error reading input: %s", strerror(errno));
143 /*----- Message canonification --------------------------------------------*/
145 #define MSGBUFSZ 65536
146 #define MSGBUFTHRESH 32768
148 typedef struct msgcanon {
152 size_t (*read)(struct msgcanon *, void *);
153 void (*write)(struct msgcanon *, const void *, size_t);
154 void (*close)(struct msgcanon *);
157 #define MC_INIT { 0 }
159 static size_t textread(msgcanon *m, void *bp)
161 size_t n = 0, nsp = 0;
166 if (f & F_EOF) return (0);
168 if ((ch = getc(m->fp)) == EOF) goto eof;
169 if (!(f & (F_DETACH | F_MIDLINE)) && ch == '.') {
171 if (ch == '\n') goto eof;
175 if (n >= MSGBUFTHRESH) goto full;
178 } else if (isspace(ch)) {
180 if (n >= MSGBUFSZ) goto full;
185 if (n >= MSGBUFTHRESH) goto full;
199 static void textwrite(msgcanon *m, const void *bp, size_t n)
201 const char *p = bp, *l = p + n;
205 if (!(f & (F_MIDLINE | F_DETACH)) &&
206 (*p == '.' || *p == '-'))
208 if (*p == '\n') f &= ~F_MIDLINE;
216 static size_t binreadembed(msgcanon *m, void *bp)
217 { return (chunk_read(m->e, bp)); }
218 static size_t binreaddetach(msgcanon *m, void *bp)
219 { return (fread(bp, 1, MSGBUFSZ, m->fp)); }
221 static void binwriteembed(msgcanon *m, const void *bp, size_t n)
222 { chunk_write(m->e, bp, n); }
224 static void nullwrite(msgcanon *m, const void *bp, size_t n) { ; }
226 static void mcsetup_readfile(msgcanon *m, unsigned f, const char *fn)
228 m->f = F_DETACH | (f & F_BINARY);
229 if (!fn || strcmp(fn, "-") == 0) {
232 } else if ((m->fp = fopen(fn, (f & F_BINARY) ? "rb" : "r")) == 0)
233 die(EXIT_FAILURE, "couldn't open file `%s': %s", fn, strerror(errno));
234 m->read = (f & F_BINARY) ? binreaddetach : textread;
237 static void mcsetup_writenull(msgcanon *m) { m->write = nullwrite; }
239 static void mcsetup_read(msgcanon *m, unsigned f, enc **ee, const char *dfn)
243 m->f = f | F_NOCLOSE;
245 if (dfn && !(f & F_DETACH)) die(EXIT_FAILURE, "signature is not detached");
247 if (!(f & F_DETACH)) {
251 m->read = binreadembed;
253 m->read = binreaddetach;
254 if (!dfn || strcmp(dfn, "-") == 0)
256 else if ((m->fp = fopen(dfn, "rb")) == 0)
257 die(EXIT_FAILURE, "can't open `%s': %s", dfn, strerror(errno));
263 if (!(f & F_DETACH)) {
264 if (e->ops->decdone(e) || ferror(e->fp))
265 die(EXIT_FAILURE, "error at end of signature header");
269 } else if (!dfn || strcmp(dfn, "-") == 0)
271 else if ((m->fp = fopen(dfn, "r")) == 0)
272 die(EXIT_FAILURE, "can't read file `%s': %s", dfn, strerror(errno));
278 static void mcsetup_write(msgcanon *m, unsigned f, enc **ee)
282 m->f = f | F_NOCLOSE;
285 m->write = nullwrite;
286 else if (f & F_BINARY) {
290 m->write = binwriteembed;
292 if (e->ops->encdone(e) || ferror(e->fp))
293 die(EXIT_FAILURE, "error at end of signature header");
297 m->write = textwrite;
301 static void mc_endread(msgcanon *m, const encops *eops, enc **ee)
303 if (!(m->f & F_DETACH)) {
307 *ee = initdec(eops, m->fp, checkbdry, "CATSIGN SIGNATURE");
309 if (m->fp && !(m->f & F_NOCLOSE)) {
310 if (ferror(m->fp) || fclose(m->fp))
311 die(EXIT_FAILURE, "error closing message file: %s", strerror(errno));
315 static void mc_endwrite(msgcanon *m, const encops *eops, enc **ee)
317 if (!(m->f & F_BINARY)) {
318 if (m->f & F_MIDLINE) putc('\n', m->fp);
319 if (!(m->f & F_DETACH)) {
320 putc('.', m->fp); putc('\n', m->fp);
321 *ee = initenc(eops, m->fp, "CATSIGN SIGNATURE");
323 } else if (!(m->f & F_DETACH)) {
324 chunk_write(m->e, 0, 0);
327 if (m->fp && !(m->f & F_NOCLOSE)) {
328 if (fflush(m->fp) || ferror(m->fp) || fclose(m->fp))
329 die(EXIT_FAILURE, "error closing message file: %s", strerror(errno));
333 /*----- Signature reading and writing -------------------------------------*/
335 static void sig_init(sigmsg *s, unsigned f, uint32 keyid)
337 s->f = f & F_SIGMASK;
342 dstr_create(&s->sig);
345 static void sig_destroy(sigmsg *s)
347 dstr_destroy(&s->kh);
348 dstr_destroy(&s->sig);
349 if (s->s) freesig(s->s);
352 static void sigtobuffer(sigmsg *s, buf *b, int hashp)
357 if (hashp) buf_putu16(b, s->f & F_HASHMASK);
358 else buf_putu16(b, s->f & F_SIGMASK);
359 buf_putu32(b, s->keyid);
360 buf_putu32(b, HI64(t));
361 buf_putu32(b, LO64(t));
362 buf_putdstr16(b, &s->kh);
366 static void dohash(ghash *h, const void *p, size_t n)
368 /* trace_block(1, "hashing", p, n); */
372 static void sig_hash(sigmsg *s)
377 buf_init(&b, bb, sizeof(bb));
378 sigtobuffer(s, &b, 1);
379 dohash(s->s->h, BBASE(&b), BLEN(&b));
382 static void keyhash(key *k, sig *s, dstr *d)
387 h = GH_INIT(GH_CLASS(s->h));
390 key_fingerprint(k, h, &kf);
391 dstr_ensure(d, GH_CLASS(h)->hashsz);
392 GH_DONE(h, d->buf + d->len);
393 d->len += GH_CLASS(h)->hashsz;
397 static void sig_writeheader(enc *e, sigmsg *s)
402 buf_init(&b, bb, sizeof(bb));
403 sigtobuffer(s, &b, 0);
404 chunk_write(e, BBASE(&b), BLEN(&b));
407 static void sig_writesig(enc *e, sigmsg *s)
408 { chunk_write(e, s->sig.buf, s->sig.len); }
410 static void diechoke(const char *m, void *p)
411 { die(EXIT_FAILURE, "%s%s%s", p, p ? ": " : "", m); }
413 static void sig_readheader(enc *e, sigmsg *s,
414 void (*choke)(const char *, void *), void *p)
423 n = chunk_read(e, bb);
425 if (buf_getu16(&b, &f)) choke("missing flags", p);
426 if (buf_getu32(&b, &x)) choke("missing keyid", p);
428 if (buf_getu32(&b, &x) || buf_getu32(&b, &y))
429 choke("missing datestamp", p);
430 SET64(t, x, y); s->t = GET64(time_t, t);
431 if (buf_getdstr16(&b, &s->kh))
432 choke("missing key hash", p);
434 choke("junk at end", p);
437 static void sig_readsig(enc *e, sigmsg *s)
442 n = chunk_read(e, bb);
443 dstr_putm(&s->sig, bb, n);
446 /*----- Signing -----------------------------------------------------------*/
448 static int sign(int argc, char *argv[])
450 const char *ef = "binary", *fn = 0, *of = 0, *kn = "ccsig", *err;
461 msgcanon mc_in = MC_INIT, mc_out = MC_INIT;
465 static const struct option opt[] = {
466 { "armour", 0, 0, 'a' },
467 { "armor", 0, 0, 'a' },
468 { "binary", 0, 0, 'b' },
469 { "detach", 0, 0, 'd' },
470 { "key", OPTF_ARGREQ, 0, 'k' },
471 { "format", OPTF_ARGREQ, 0, 'f' },
472 { "output", OPTF_ARGREQ, 0, 'o' },
473 { "text", 0, 0, 't' },
476 i = mdwopt(argc, argv, "k:f:o:abdt", opt, 0, 0, 0);
479 case 'k': kn = optarg; break;
480 case 'f': ef = optarg; break;
481 case 'o': of = optarg; break;
482 case 'a': ef = "pem"; break;
483 case 't': f &= ~F_BINARY; break;
484 case 'b': f |= F_BINARY; break;
485 case 'd': f |= F_DETACH; break;
486 default: f |= F_BOGUS; break;
489 if (argc - optind > 1 || (f & F_BOGUS))
490 die(EXIT_FAILURE, "Usage: sign [-OPTIONS] [FILE]");
492 if (key_open(&kf, keyring, KOPEN_READ, keyreport, 0))
493 die(EXIT_FAILURE, "can't open keyring `%s'", keyring);
494 if ((k = key_bytag(&kf, kn)) == 0)
495 die(EXIT_FAILURE, "key `%s' not found", kn);
497 if ((eo = getenc(ef)) == 0)
498 die(EXIT_FAILURE, "encoding `%s' not found", ef);
500 fn = (optind >= argc) ? 0 : argv[optind++];
502 if (!of || strcmp(of, "-") == 0)
504 else if ((ofp = fopen(of, eo->wmode)) == 0) {
505 die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
506 ofp, strerror(errno));
509 /* --- Start the work --- */
511 sig_init(&s, f, k->id);
514 s.s = getsig(k, "ccsig", 1);
515 if ((err = s.s->ops->check(s.s)) != 0)
516 moan("key %s fails check: %s", d.buf, err);
517 keyhash(k, s.s, &s.kh);
519 (f & F_DETACH) ? "CATSIGN SIGNATURE" :
520 (f & F_BINARY) ? "CATSIGN MESSAGE" :
521 "CATSIGN MESSAGE HEADER");
522 sig_writeheader(e, &s);
524 /* --- Hash the message --- */
526 mcsetup_readfile(&mc_in, f, fn);
527 mcsetup_write(&mc_out, f, &e);
530 n = mc_in.read(&mc_in, bb);
532 dohash(s.s->h, bb, n);
533 mc_out.write(&mc_out, bb, n);
535 mc_endread(&mc_in, 0, 0);
536 mc_endwrite(&mc_out, eo, &e);
538 /* --- Write the signature --- */
540 if (s.s->ops->doit(s.s, &s.sig))
541 die(EXIT_FAILURE, "signature failed");
544 if (fflush(ofp) || ferror(ofp) || fclose(ofp))
545 die(EXIT_FAILURE, "error writing signature: %s", strerror(errno));
547 /* --- All done --- */
556 /*----- Verifying ---------------------------------------------------------*/
558 typedef struct vrfctx {
564 static int vrfbdry(const char *b, void *p)
568 if (strcmp(b, "CATSIGN MESSAGE") == 0) {
570 v->m |= F_BINARY | F_DETACH;
572 } else if (strcmp(b, "CATSIGN MESSAGE HEADER") == 0) {
573 v->m |= F_BINARY | F_DETACH;
575 } else if (strcmp(b, "CATSIGN SIGNATURE") == 0) {
583 static void vrfchoke(const char *m, void *p)
586 if (v->verb) printf("FAIL %s: %s\n", v->what, m);
590 static int verify(int argc, char *argv[])
592 const char *ef = "binary", *of = 0, *dfn = 0, *kn = 0, *err;
593 vrfctx v = { 0, 0, 1 };
597 FILE *fp, *ofp = 0, *rfp = 0;
602 dstr d = DSTR_INIT, dd = DSTR_INIT;
604 msgcanon mc_in = MC_INIT;
608 static const struct option opt[] = {
609 { "armour", 0, 0, 'a' },
610 { "armor", 0, 0, 'a' },
611 { "buffer", 0, 0, 'b' },
612 { "key", OPTF_ARGREQ, 0, 'k' },
613 { "format", OPTF_ARGREQ, 0, 'f' },
614 { "output", OPTF_ARGREQ, 0, 'o' },
615 { "quiet", 0, 0, 'q' },
616 { "utc", 0, 0, 'u' },
617 { "gmt", 0, 0, 'u' },
618 { "verbose", 0, 0, 'v' },
621 i = mdwopt(argc, argv, "k:f:o:abquv", opt, 0, 0, 0);
624 case 'a': ef = "pem"; break;
625 case 'b': v.f |= F_BUFFER; break;
626 case 'k': kn = optarg; break;
627 case 'f': ef = optarg; break;
628 case 'o': of = optarg; break;
629 case 'u': v.f |= F_UTC; break;
630 case 'q': if (v.verb > 0) v.verb--; break;
631 case 'v': if (v.verb < 10) v.verb++; break;
632 default: v.f |= F_BOGUS; break;
635 if (argc - optind > 2 || (v.f & F_BOGUS))
636 die(EXIT_FAILURE, "Usage: verify [-OPTIONS] [FILE [MESSAGE]]");
638 if ((eo = getenc(ef)) == 0)
639 die(EXIT_FAILURE, "encoding `%s' not found", ef);
643 else if (strcmp(argv[optind], "-") == 0) {
646 } else if ((fp = fopen(argv[optind], eo->rmode)) == 0) {
647 die(EXIT_FAILURE, "couldn't open file `%s': %s",
648 argv[optind], strerror(errno));
652 if (key_open(&kf, keyring, KOPEN_READ, keyreport, 0))
653 die(EXIT_FAILURE, "can't open keyring `%s'", keyring);
654 if (kn && (kk = key_bytag(&kf, kn)) == 0)
655 die(EXIT_FAILURE, "key `%s' not found", kn);
657 e = initdec(eo, fp, vrfbdry, &v);
659 /* --- Read the header chunk --- */
661 v.what = "malformed header";
662 sig_readheader(e, &s, vrfchoke, &v);
664 if (((s.f ^ v.f) & v.m) != 0) {
665 if (v.verb) printf("FAIL boundary string inconsistent with contents\n");
670 if ((k = key_byid(&kf, s.keyid)) == 0) {
671 if (v.verb) printf("FAIL key id %08lx not found\n",
672 (unsigned long)s.keyid);
675 if (kk && k->id != kk->id) {
677 dstr_reset(&d); key_fulltag(k, &d);
678 dstr_reset(&dd); key_fulltag(kk, &dd);
679 printf("FAIL signing key is %s; expected key %s\n", d.buf, dd.buf);
684 s.s = getsig(k, "ccsig", 0);
685 dstr_reset(&d); key_fulltag(k, &d);
686 if (v.verb && (err = s.s->ops->check(s.s)) != 0)
687 printf("WARN verification key %s fails check: %s\n", d.buf, err);
689 dstr_reset(&dd); keyhash(k, s.s, &dd);
690 if (dd.len != s.kh.len || memcmp(dd.buf, s.kh.buf, dd.len) != 0) {
691 if (v.verb) printf("FAIL key hash mismatch\n");
695 /* --- Now a merry dance --- */
700 dfn = argv[optind++];
701 mcsetup_read(&mc_in, v.f, &e, dfn);
703 if (!of && (v.f & F_DETACH)) {
706 } else if (!of || strcmp(of, "-") == 0) {
710 if (of && !(v.f & F_BUFFER)) {
711 if ((ofp = fopen(of, (v.f & F_BINARY) ? "wb" : "w")) == 0) {
712 die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
713 of, strerror(errno));
716 } else if ((rfp = tmpfile()) == 0)
717 die(EXIT_FAILURE, "couldn't create temporary file: %s", strerror(errno));
719 /* --- Read the message and verify the signature --- */
723 n = mc_in.read(&mc_in, bb);
725 dohash(s.s->h, bb, n);
726 if (rfp) fwrite(bb, 1, n, rfp);
728 mc_endread(&mc_in, eo, &e);
729 if (!(v.f & F_DETACH))
731 if (rfp && (ferror(rfp) || fflush(rfp))) {
732 if (v.verb) printf("FAIL error writing message: %s\n", strerror(errno));
736 /* --- Check the signature --- */
738 if (s.s->ops->doit(s.s, &s.sig)) {
739 if (v.verb) printf("FAIL signature verification failed\n");
743 tm = (v.f & F_UTC) ? gmtime(&s.t) : localtime(&s.t);
744 strftime(bb, sizeof(bb), "%Y-%m-%d %H:%M:%S %Z", tm);
745 printf("INFO good-signature %s\n", d.buf);
746 printf("INFO date %s\n", bb);
749 /* --- Unbuffer buffered input --- */
751 if (v.f & F_BUFFER) {
752 if (!ofp && (ofp = fopen(of, "wb")) == 0) {
753 die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
754 of, strerror(errno));
757 if (v.verb && ofp == stdout) printf("DATA\n");
759 n = fread(bb, 1, sizeof(bb), rfp);
761 if (fwrite(bb, 1, n, ofp) < n)
762 die(EXIT_FAILURE, "error writing output: %s", strerror(errno));
764 if (ferror(rfp) || fclose(rfp))
765 die(EXIT_FAILURE, "error unbuffering output: %s", strerror(errno));
767 if (ofp && (fflush(ofp) || ferror(ofp) || fclose(ofp)))
768 die(EXIT_FAILURE, "error writing output: %s", strerror(errno));
770 /* --- Tidy up --- */
773 if (v.verb && ofp != stdout)
774 printf("OK verified successfully\n");
783 /*----- Reformatting ------------------------------------------------------*/
785 static int format(int argc, char *argv[])
787 const char *ief = "binary", *oef = "binary";
788 const char *dfn = 0, *of = 0, *mf = 0;
790 FILE *fp, *ofp = 0, *mfp = 0;
793 msgcanon mc_in = MC_INIT, mc_out = MC_INIT;
795 vrfctx v = { 0, 0, 1 };
796 unsigned f = 0, fm = ~F_SIGMASK, sf;
797 const encops *ieo, *oeo;
801 static const struct option opt[] = {
802 { "armour-in", 0, 0, 'a' },
803 { "armor-in", 0, 0, 'a' },
804 { "armour-out", 0, 0, 'A' },
805 { "armor-out", 0, 0, 'A' },
806 { "detach", 0, 0, 'D' },
807 { "embed", 0, 0, 'E' },
808 { "format-in", OPTF_ARGREQ, 0, 'f' },
809 { "format-out", OPTF_ARGREQ, 0, 'F' },
810 { "message", OPTF_ARGREQ, 0, 'm' },
811 { "output", OPTF_ARGREQ, 0, 'o' },
814 i = mdwopt(argc, argv, "f:F:m:o:aADE", opt, 0, 0, 0);
817 case 'a': ief = "pem"; break;
818 case 'A': oef = "pem"; break;
819 case 'f': ief = optarg; break;
820 case 'F': oef = optarg; break;
821 case 'D': f |= F_DETACH; fm |= F_DETACH; break;
822 case 'E': f &= ~F_DETACH; fm |= F_DETACH; break;
823 case 'm': mf = optarg; break;
824 case 'o': of = optarg; break;
825 default: f |= F_BOGUS; break;
829 if (argc - optind > 2 || (f & F_BOGUS))
830 die(EXIT_FAILURE, "Usage: format [-OPTIONS] [FILE [MESSAGE]]");
832 if ((ieo = getenc(ief)) == 0)
833 die(EXIT_FAILURE, "encoding `%s' not found", ief);
834 if ((oeo = getenc(oef)) == 0)
835 die(EXIT_FAILURE, "encoding `%s' not found", oef);
839 else if (strcmp(argv[optind], "-") == 0) {
842 } else if ((fp = fopen(argv[optind], ieo->rmode)) == 0) {
843 die(EXIT_FAILURE, "couldn't open file `%s': %s",
844 argv[optind], strerror(errno));
849 dfn = argv[optind++];
851 ie = initdec(ieo, fp, vrfbdry, &v);
853 /* --- Read the header chunk --- */
855 sig_readheader(ie, &s, diechoke, "malformed header");
857 if (((s.f ^ v.f) & v.m) != 0)
858 moan("boundary string inconsistent with contents (ignoring)");
860 mcsetup_read(&mc_in, s.f, &ie, dfn);
862 /* --- Prepare the output stuff --- */
864 if (!of && !mf) of = "-";
866 f = (f & fm) | (s.f & ~fm);
873 mcsetup_writenull(&mc_out);
875 if (strcmp(of, "-") == 0)
877 else if ((ofp = fopen(of, oeo->wmode)) == 0) {
878 die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
879 of, strerror(errno));
881 oe = initenc(oeo, ofp,
882 (f & F_DETACH) ? "CATSIGN SIGNATURE" :
883 (f & F_BINARY) ? "CATSIGN MESSAGE" :
884 "CATSIGN MESSAGE HEADER");
885 sig_writeheader(oe, &s);
886 mcsetup_write(&mc_out, f, &oe);
890 if (strcmp(mf, "-") == 0)
892 else if ((mfp = fopen(mf, (f & F_BINARY) ? "wb" : "w")) == 0) {
893 die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
894 mf, strerror(errno));
898 /* --- Wade through the message body --- */
901 n = mc_in.read(&mc_in, bb);
903 mc_out.write(&mc_out, bb, n);
904 if (mfp) fwrite(bb, 1, n, mfp);
906 mc_endread(&mc_in, ieo, &ie);
907 if (of) mc_endwrite(&mc_out, oeo, &oe);
909 /* --- Write the signature --- */
911 if (!(sf & F_DETACH))
914 sig_writesig(oe, &s);
915 oe->ops->encdone(oe);
918 /* --- All done --- */
920 ie->ops->decdone(ie);
921 if (ferror(fp) || fclose(fp))
922 die(EXIT_FAILURE, "error reading input signature: %s", strerror(errno));
923 if (ofp && (fflush(ofp) || ferror(ofp) || fclose(ofp)))
924 die(EXIT_FAILURE, "error writing output signature: %s", strerror(errno));
925 if (mfp && (fflush(mfp) || ferror(mfp) || fclose(mfp)))
926 die(EXIT_FAILURE, "error writing output message: %s", strerror(errno));
933 static void infochoke(const char *m, void *p)
936 printf("BAD %s: %s\n", v->what, m);
940 static void infokeyreport(const char *file, int line,
941 const char *err, void *p)
944 static int info(int argc, char *argv[])
946 const char *ef = "binary";
947 vrfctx v = { 0, 0, 1 };
960 static const struct option opt[] = {
961 { "armour", 0, 0, 'a' },
962 { "armor", 0, 0, 'a' },
963 { "format", OPTF_ARGREQ, 0, 'f' },
964 { "gmt", 0, 0, 'u' },
965 { "utc", 0, 0, 'u' },
968 i = mdwopt(argc, argv, "f:au", opt, 0, 0, 0);
971 case 'a': ef = "pem"; break;
972 case 'f': ef = optarg; break;
973 case 'u': v.f |= F_UTC; break;
974 default: v.f |= F_BOGUS; break;
977 if (argc - optind > 1 || (v.f & F_BOGUS))
978 die(EXIT_FAILURE, "Usage: info [-OPTIONS] [FILE]");
980 if ((eo = getenc(ef)) == 0)
981 die(EXIT_FAILURE, "encoding `%s' not found", ef);
985 else if (strcmp(argv[optind], "-") == 0) {
988 } else if ((fp = fopen(argv[optind], eo->rmode)) == 0) {
989 die(EXIT_FAILURE, "couldn't open file `%s': %s",
990 argv[optind], strerror(errno));
994 if (key_open(&kf, keyring, KOPEN_READ, infokeyreport, 0)) {
995 printf("NOTE can't open keyring `%s'\n", keyring);
998 e = initdec(eo, fp, vrfbdry, &v);
1000 v.what = "malformed header";
1001 sig_readheader(e, &s, infochoke, &v);
1003 printf("INFO flags %sdetach %sbinary\n",
1004 (s.f & F_DETACH) ? "" : "!",
1005 (s.f & F_BINARY) ? "" : "!");
1007 if (((s.f ^ v.f) & v.m) != 0) {
1008 printf("WARN boundary string inconsistent with contents\n");
1009 printf("INFO expected-flags");
1010 if (v.m & F_DETACH) printf(" %sdetach", (v.f & F_DETACH) ? "" : "!");
1011 if (v.m & F_BINARY) printf(" %sbinary", (v.f & F_BINARY) ? "" : "!");
1016 tm = (v.f & F_UTC) ? gmtime(&s.t) : localtime(&s.t);
1017 strftime(bb, sizeof(bb), "%Y-%m-%d %H:%M:%S %Z", tm);
1018 printf("INFO date %s\n", bb);
1020 if (keyring && (k = key_byid(&kf, s.keyid)) != 0) {
1021 dstr_reset(&d); key_fulltag(k, &d);
1022 printf("INFO key %s\n", d.buf);
1024 printf("INFO unknown-key %08lx\n", (unsigned long)s.keyid);
1026 if (keyring) key_close(&kf);
1032 /*----- Main code ---------------------------------------------------------*/
1036 listtab[i].name, listtab[i].name) \
1037 LI("Signature schemes", sig, \
1038 sigtab[i].name, sigtab[i].name) \
1039 LI("Encodings", enc, \
1040 enctab[i].name, enctab[i].name) \
1041 LI("Hash functions", hash, \
1042 ghashtab[i], ghashtab[i]->name)
1044 MAKELISTTAB(listtab, LISTS)
1046 int cmd_show(int argc, char *argv[])
1048 return (displaylists(listtab, argv + 1));
1051 static int cmd_help(int, char **);
1053 static cmd cmdtab[] = {
1054 { "help", cmd_help, "help [COMMAND...]" },
1055 { "show", cmd_show, "show [ITEM...]" },
1059 "sign [-adt] [-k TAG] [-f FORMAT] [-o OUTPUT] [FILE]", "\
1062 -a, --armour Same as `-f pem'.\n\
1063 -b, --binary Treat the input message as binary data.\n\
1064 -d, --detach Produce a detached signature.\n\
1065 -f, --format=FORMAT Encode as FORMAT.\n\
1066 -k, --key=TAG Use public encryption key named by TAG.\n\
1067 -o, --output=FILE Write output to FILE.\n\
1068 -t, --text Canonify input message as a text file.\n\
1071 "verify [-abquv] [-f FORMAT] [-k TAG] [-o OUTPUT]\n\t\
1072 [FILE [MESSAGE]]", "\
1075 -a, --armour Same as `-f pem'.\n\
1076 -b, --buffer Buffer message until signature is verified.\n\
1077 -f, --format=FORMAT Decode as FORMAT.\n\
1078 -k, --key=TAG Require that the message be signed by key TAG.\n\
1079 -o, --output=FILE Write message to FILE.\n\
1080 -q, --quiet Produce fewer messages.\n\
1081 -u, --utc Show dates in UTC rather than local time.\n\
1082 -v, --verbose Produce more verbose messages.\n\
1085 "info [-au] [-f FORMAT] [FILE]", "\
1088 -a, --armour Same as `-f pem'.\n\
1089 -f, --format=FORMAT Decode as FORMAT.\n\
1090 -u, --utc Show dates in UTC rather than local time.\n\
1093 "format [-auADE] [-f FORMAT] [-F format] [-m FILE] [-o FILE]\n\t\
1094 [FILE [MESSAGE]]", "\
1097 -a, --armour-in Same as `-f pem'.\n\
1098 -A, --armour-out Same as `-F pem'.\n\
1099 -D, --detach Create detached signature.\n\
1100 -E, --embed Create signature with embedded message.\n\
1101 -f, --format-in=FORMAT Decode input as FORMAT.\n\
1102 -F, --format-out=FORMAT Encode output as FORMAT.\n\
1103 -m, --message=FILE Write message to FILE.\n\
1104 -o, --output=FILE Write new signature to FILE.\n\
1107 }; /* " Emacs seems confused. */
1109 static int cmd_help(int argc, char **argv)
1111 sc_help(cmdtab, stdout, argv + 1);
1115 void version(FILE *fp)
1117 pquis(fp, "$, Catacomb version " VERSION "\n");
1120 static void usage(FILE *fp)
1122 pquis(fp, "Usage: $ [-k KEYRING] COMMAND [ARGS]\n");
1125 void help_global(FILE *fp)
1129 Sign and verify data.\n\
1131 Global command-line options:\n\
1133 -h, --help [COMMAND...] Show this help message, or help for COMMANDs.\n\
1134 -v, --version Show program version number.\n\
1135 -u, --usage Show a terse usage message.\n\
1137 -k, --keyring=FILE Read keys from FILE.\n",
1143 * Arguments: @int argc@ = number of command line arguments
1144 * @char *argv[]@ = vector of command line arguments
1146 * Returns: Zero if successful, nonzero otherwise.
1148 * Use: Encrypts or decrypts files.
1151 int main(int argc, char *argv[])
1157 /* --- Initialize the library --- */
1161 rand_noisesrc(RAND_GLOBAL, &noise_source);
1162 rand_seed(RAND_GLOBAL, 160);
1163 /* trace_on(stderr, 1); */
1165 /* --- Parse options --- */
1168 static struct option opts[] = {
1169 { "help", 0, 0, 'h' },
1170 { "version", 0, 0, 'v' },
1171 { "usage", 0, 0, 'u' },
1172 { "keyring", OPTF_ARGREQ, 0, 'k' },
1175 int i = mdwopt(argc, argv, "+hvu k:", opts, 0, 0, 0);
1180 sc_help(cmdtab, stdout, argv + optind);
1202 if (f & f_bogus || argc < 1) {
1207 /* --- Dispatch to the correct subcommand handler --- */
1209 return (findcmd(cmdtab, argv[0])->cmd(argc, argv));
1214 /*----- That's all, folks -------------------------------------------------*/