X-Git-Url: https://git.distorted.org.uk/u/mdw/catacomb/blobdiff_plain/ba6e6b64033b1f9de49feccb5c9cd438354481f7..0f00dc4c8eb47e67bc0f148c2dd109f73a451e0a:/catsign.c diff --git a/catsign.c b/catsign.c deleted file mode 100644 index 3bbd7b2..0000000 --- a/catsign.c +++ /dev/null @@ -1,1276 +0,0 @@ -/* -*-c-*- - * - * $Id$ - * - * Sign files - * - * (c) 2005 Straylight/Edgeware - */ - -/*----- Licensing notice --------------------------------------------------* - * - * This file is part of Catacomb. - * - * Catacomb is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * Catacomb is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with Catacomb; if not, write to the Free - * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -/*----- Header files ------------------------------------------------------*/ - -#define _FILE_OFFSET_BITS 64 - -#include "config.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "buf.h" -#include "rand.h" -#include "noise.h" -#include "mprand.h" -#include "key.h" -#include "getdate.h" -#include "cc.h" - -#include "ectab.h" -#include "ptab.h" - -/*----- Static variables --------------------------------------------------*/ - -static const char *keyring = "keyring"; - -/*----- Data formats ------------------------------------------------------* - * - * Our crypto stuff is split into three parts: a header describing the key, - * the message itself, and the signature. In a detached signature, the - * message is separate, and the header and signature are combined into one - * encoded chunk. In an attached signature, the message is included. There - * are two forms of message: binary and text. Binary messages are divided - * into chunks, each preceded by a 2-octet length, and terminated by a - * zero-length chunk. Text messages are byte-stuffed, as for RFC821, and - * trailing whitespace before a newline is ignored. - */ - -typedef struct sigmsg { - unsigned f; /* Flags */ -#define F_DETACH 1u -#define F_BINARY 2u -#define F_SIGMASK 3u -#define F_HASHMASK (F_BINARY) - uint32 keyid; /* Key identifier */ - time_t t; /* When the signature was made */ - dstr kh; /* Key fingerprint (sanity check) */ - dstr sig; /* Signature */ - sig *s; /* Signature algorithm */ -} sigmsg; - -#define F_MIDLINE 16u -#define F_EOF 32u -#define F_NOCLOSE 64u -#define F_BOGUS 128u -#define F_BUFFER 256u -#define F_UTC 512u -#define F_NOCHECK 1024u -#define F_PROGRESS 2048u - -/*----- Chunk I/O ---------------------------------------------------------*/ - -static void chunk_write(enc *e, const void *p, size_t n) -{ - octet b[2]; - - assert(n <= 0xffff); - STORE16(b, n); - if (e->ops->write(e, b, 2) || e->ops->write(e, p, n)) - die(EXIT_FAILURE, "error writing output: %s", strerror(errno)); -} - -static size_t chunk_read(enc *e, void *p) -{ - octet b[2]; - size_t n; - - if (e->ops->read(e, b, 2) != 2) - goto err; - n = LOAD16(b); - if (n && e->ops->read(e, p, n) != n) - goto err; - return (n); - -err: - if (!errno) die(EXIT_FAILURE, "unexpected end-of-file on input"); - else die(EXIT_FAILURE, "error reading input: %s", strerror(errno)); - return (0); -} - -/*----- Message canonification --------------------------------------------*/ - -#define MSGBUFSZ 65536 -#define MSGBUFTHRESH 32768 - -typedef struct msgcanon { - unsigned f; - FILE *fp; - enc *e; - fprogress ff; - size_t (*read)(struct msgcanon *, void *); - void (*write)(struct msgcanon *, const void *, size_t); - void (*close)(struct msgcanon *); -} msgcanon; - -#define MC_INIT { 0 } - -static size_t textread(msgcanon *m, void *bp) -{ - size_t n = 0, nsp = 0; - int ch; - char *b = bp; - unsigned f = m->f; - - if (f & F_EOF) return (0); - for (;;) { - if ((ch = getc(m->fp)) == EOF) goto eof; - if (!(f & (F_DETACH | F_MIDLINE)) && ch == '.') { - ch = getc(m->fp); - if (ch == '\n') goto eof; - } - if (ch == '\n') { - n -= nsp; nsp = 0; - if (n >= MSGBUFTHRESH) goto full; - b[n++] = ch; - f &= ~F_MIDLINE; - } else if (isspace(ch)) { - f |= F_MIDLINE; - if (n >= MSGBUFSZ) goto full; - b[n++] = ch; nsp++; - } else { - f |= F_MIDLINE; - nsp = 0; - if (n >= MSGBUFTHRESH) goto full; - b[n++] = ch; - } - } -eof: - f |= F_EOF; - goto done; -full: - ungetc(ch, m->fp); -done: - m->f = f; - if (m->f & F_PROGRESS) fprogress_update(&m->ff, n); - return (n); -} - -static void textwrite(msgcanon *m, const void *bp, size_t n) -{ - const char *p = bp, *l = p + n; - unsigned f = m->f; - - while (p < l) { - if (!(f & (F_MIDLINE | F_DETACH)) && - (*p == '.' || *p == '-')) - putc('.', m->fp); - if (*p == '\n') f &= ~F_MIDLINE; - else f |= F_MIDLINE; - putc(*p, m->fp); - p++; - } - m->f = f; -} - -static size_t binreadembed(msgcanon *m, void *bp) -{ - size_t n = chunk_read(m->e, bp); - if (m->f & F_PROGRESS) fprogress_update(&m->ff, n); - return (n); -} - -static size_t binreaddetach(msgcanon *m, void *bp) -{ - size_t n = fread(bp, 1, MSGBUFSZ - 1, m->fp); - if (m->f & F_PROGRESS) fprogress_update(&m->ff, n); - return (n); -} - -static void binwriteembed(msgcanon *m, const void *bp, size_t n) - { chunk_write(m->e, bp, n); } - -static void nullwrite(msgcanon *m, const void *bp, size_t n) { ; } - -static void mcsetup_readfile(msgcanon *m, unsigned f, const char *fn) -{ - m->f = F_DETACH | (f & (F_BINARY | F_PROGRESS)); - if (!fn || strcmp(fn, "-") == 0) { - m->fp = stdin; - m->f |= F_NOCLOSE; - } else if ((m->fp = fopen(fn, (f & F_BINARY) ? "rb" : "r")) == 0) - die(EXIT_FAILURE, "couldn't open file `%s': %s", fn, strerror(errno)); - if (m->f & F_PROGRESS) { - if (fprogress_init(&m->ff, fn, m->fp)) { - die(EXIT_FAILURE, "failed to initialize progress indicator: %s", - strerror(errno)); - } - } - m->read = (f & F_BINARY) ? binreaddetach : textread; -} - -static void mcsetup_writenull(msgcanon *m) { m->write = nullwrite; } - -static void mcsetup_read(msgcanon *m, unsigned f, enc **ee, - const char *fn, const char *dfn) -{ - enc *e = *ee; - - m->f = f | F_NOCLOSE; - - if (dfn && !(f & F_DETACH)) die(EXIT_FAILURE, "signature is not detached"); - if (f & F_BINARY) { - if (!(f & F_DETACH)) { - m->e = e; - *ee = 0; - m->fp = e->fp; - m->read = binreadembed; - if (m->f & F_PROGRESS) { - if (fprogress_init(&m->ff, fn, m->fp)) { - die(EXIT_FAILURE, "failed to initialize progress indicator: %s", - strerror(errno)); - } - } - } else { - m->read = binreaddetach; - if (!dfn || strcmp(dfn, "-") == 0) - m->fp = stdin; - else if ((m->fp = fopen(dfn, "rb")) == 0) - die(EXIT_FAILURE, "can't open `%s': %s", dfn, strerror(errno)); - else - m->f &= ~F_NOCLOSE; - if (m->f & F_PROGRESS) { - if (fprogress_init(&m->ff, dfn, m->fp)) { - die(EXIT_FAILURE, "failed to initialize progress indicator: %s", - strerror(errno)); - } - } - } - } else { - m->read = textread; - if (!(f & F_DETACH)) { - if (e->ops->decdone(e) || ferror(e->fp)) - die(EXIT_FAILURE, "error at end of signature header"); - m->fp = e->fp; - e->ops->destroy(e); - *ee = 0; - if (m->f & F_PROGRESS) { - if (fprogress_init(&m->ff, fn, m->fp)) { - die(EXIT_FAILURE, "failed to initialize progress indicator: %s", - strerror(errno)); - } - } - } else { - if (!dfn || strcmp(dfn, "-") == 0) - m->fp = stdin; - else if ((m->fp = fopen(dfn, "r")) == 0) - die(EXIT_FAILURE, "can't read file `%s': %s", dfn, strerror(errno)); - else - m->f &= ~F_NOCLOSE; - if (m->f & F_PROGRESS) { - if (fprogress_init(&m->ff, dfn, m->fp)) { - die(EXIT_FAILURE, "failed to initialize progress indicator: %s", - strerror(errno)); - } - } - } - } -} - -static void mcsetup_write(msgcanon *m, unsigned f, enc **ee) -{ - enc *e = *ee; - - m->f = f | F_NOCLOSE; - - if (f & F_DETACH) - m->write = nullwrite; - else if (f & F_BINARY) { - m->e = e; - *ee = 0; - m->fp = e->fp; - m->write = binwriteembed; - } else { - if (e->ops->encdone(e) || ferror(e->fp)) - die(EXIT_FAILURE, "error at end of signature header"); - m->fp = e->fp; - e->ops->destroy(e); - *ee = 0; - m->write = textwrite; - } -} - -static void mc_endread(msgcanon *m, const encops *eops, enc **ee) -{ - if (!(m->f & F_DETACH)) { - if (m->f & F_BINARY) - *ee = m->e; - else - *ee = initdec(eops, m->fp, checkbdry, "CATSIGN SIGNATURE"); - } - if (m->fp && !(m->f & F_NOCLOSE)) { - if (ferror(m->fp) || fclose(m->fp)) - die(EXIT_FAILURE, "error closing message file: %s", strerror(errno)); - } - if (m->f & F_PROGRESS) fprogress_done(&m->ff); -} - -static void mc_endwrite(msgcanon *m, const encops *eops, enc **ee) -{ - if (!(m->f & F_BINARY)) { - if (m->f & F_MIDLINE) putc('\n', m->fp); - if (!(m->f & F_DETACH)) { - putc('.', m->fp); putc('\n', m->fp); - *ee = initenc(eops, m->fp, "CATSIGN SIGNATURE"); - } - } else if (!(m->f & F_DETACH)) { - chunk_write(m->e, 0, 0); - *ee = m->e; - } - if (m->fp && !(m->f & F_NOCLOSE)) { - if (fflush(m->fp) || ferror(m->fp) || fclose(m->fp)) - die(EXIT_FAILURE, "error closing message file: %s", strerror(errno)); - } -} - -/*----- Signature reading and writing -------------------------------------*/ - -static void sig_init(sigmsg *s, unsigned f, uint32 keyid) -{ - s->f = f & F_SIGMASK; - s->keyid = keyid; - time(&s->t); - s->s = 0; - dstr_create(&s->kh); - dstr_create(&s->sig); -} - -static void sig_destroy(sigmsg *s) -{ - dstr_destroy(&s->kh); - dstr_destroy(&s->sig); - if (s->s) freesig(s->s); -} - -static void sigtobuffer(sigmsg *s, buf *b, int hashp) -{ - kludge64 t; - - ASSIGN64(t, s->t); - if (hashp) buf_putu16(b, s->f & F_HASHMASK); - else buf_putu16(b, s->f & F_SIGMASK); - buf_putu32(b, s->keyid); - buf_putu32(b, HI64(t)); - buf_putu32(b, LO64(t)); - buf_putdstr16(b, &s->kh); - assert(BOK(b)); -} - -static void dohash(ghash *h, const void *p, size_t n) -{ -/* trace_block(1, "hashing", p, n); */ - GH_HASH(h, p, n); -} - -static void sig_hash(sigmsg *s) -{ - octet bb[16384]; - buf b; - - buf_init(&b, bb, sizeof(bb)); - sigtobuffer(s, &b, 1); - dohash(s->s->h, BBASE(&b), BLEN(&b)); -} - -static void keyhash(key *k, sig *s, dstr *d) -{ - ghash *h; - key_filter kf; - - h = GH_INIT(s->ch); - kf.f = KCAT_PUB; - kf.m = KF_CATMASK; - key_fingerprint(k, h, &kf); - dstr_ensure(d, GH_CLASS(h)->hashsz); - GH_DONE(h, d->buf + d->len); - d->len += GH_CLASS(h)->hashsz; - GH_DESTROY(h); -} - -static void sig_writeheader(enc *e, sigmsg *s) -{ - octet bb[16384]; - buf b; - - buf_init(&b, bb, sizeof(bb)); - sigtobuffer(s, &b, 0); - chunk_write(e, BBASE(&b), BLEN(&b)); -} - -static void sig_writesig(enc *e, sigmsg *s) - { chunk_write(e, s->sig.buf, s->sig.len); } - -static void diechoke(const char *m, void *p) - { die(EXIT_FAILURE, "%s%s%s", p, p ? ": " : "", m); } - -static void sig_readheader(enc *e, sigmsg *s, - void (*choke)(const char *, void *), void *p) -{ - uint16 f; - octet bb[MSGBUFSZ]; - uint32 x, y; - kludge64 t; - buf b; - size_t n; - - n = chunk_read(e, bb); - buf_init(&b, bb, n); - if (buf_getu16(&b, &f)) choke("missing flags", p); - if (buf_getu32(&b, &x)) choke("missing keyid", p); - sig_init(s, f, x); - if (buf_getu32(&b, &x) || buf_getu32(&b, &y)) - choke("missing datestamp", p); - SET64(t, x, y); s->t = GET64(time_t, t); - if (buf_getdstr16(&b, &s->kh)) - choke("missing key hash", p); - if (BLEFT(&b)) - choke("junk at end", p); -} - -static void sig_readsig(enc *e, sigmsg *s) -{ - octet bb[MSGBUFSZ]; - size_t n; - - n = chunk_read(e, bb); - dstr_putm(&s->sig, bb, n); -} - -/*----- Signing -----------------------------------------------------------*/ - -static int sign(int argc, char *argv[]) -{ - const char *ef = "binary", *fn = 0, *of = 0, *kn = "ccsig", *err; - unsigned f = 0; - key_file kf; - key *k; - sigmsg s; - FILE *ofp = 0; - int i; - char bb[MSGBUFSZ]; - size_t n; - dstr d = DSTR_INIT; - const encops *eo; - msgcanon mc_in = MC_INIT, mc_out = MC_INIT; - enc *e; - - for (;;) { - static const struct option opt[] = { - { "armour", 0, 0, 'a' }, - { "armor", 0, 0, 'a' }, - { "binary", 0, 0, 'b' }, - { "detach", 0, 0, 'd' }, - { "key", OPTF_ARGREQ, 0, 'k' }, - { "format", OPTF_ARGREQ, 0, 'f' }, - { "output", OPTF_ARGREQ, 0, 'o' }, - { "progress", 0, 0, 'p' }, - { "text", 0, 0, 't' }, - { "nocheck", 0, 0, 'C' }, - { 0, 0, 0, 0 } - }; - i = mdwopt(argc, argv, "k:f:o:abdptC", opt, 0, 0, 0); - if (i < 0) break; - switch (i) { - case 'k': kn = optarg; break; - case 'f': ef = optarg; break; - case 'o': of = optarg; break; - case 'a': ef = "pem"; break; - case 't': f &= ~F_BINARY; break; - case 'b': f |= F_BINARY; break; - case 'd': f |= F_DETACH; break; - case 'C': f |= F_NOCHECK; break; - case 'p': f |= F_PROGRESS; break; - default: f |= F_BOGUS; break; - } - } - if (argc - optind > 1 || (f & F_BOGUS)) - die(EXIT_FAILURE, "Usage: sign [-OPTIONS] [FILE]"); - - if (key_open(&kf, keyring, KOPEN_READ, key_moan, 0)) - die(EXIT_FAILURE, "can't open keyring `%s'", keyring); - if ((k = key_bytag(&kf, kn)) == 0) - die(EXIT_FAILURE, "key `%s' not found", kn); - - if ((eo = getenc(ef)) == 0) - die(EXIT_FAILURE, "encoding `%s' not found", ef); - - fn = (optind >= argc) ? 0 : argv[optind++]; - - if (!of || strcmp(of, "-") == 0) - ofp = stdout; - else if ((ofp = fopen(of, eo->wmode)) == 0) { - die(EXIT_FAILURE, "couldn't open file `%s' for output: %s", - ofp, strerror(errno)); - } - - /* --- Start the work --- */ - - sig_init(&s, f, k->id); - dstr_reset(&d); - key_fulltag(k, &d); - s.s = getsig(k, "ccsig", 1); - if (!(f & F_NOCHECK) && (err = s.s->ops->check(s.s)) != 0) - moan("key %s fails check: %s", d.buf, err); - keyhash(k, s.s, &s.kh); - e = initenc(eo, ofp, - (f & F_DETACH) ? "CATSIGN SIGNATURE" : - (f & F_BINARY) ? "CATSIGN MESSAGE" : - "CATSIGN MESSAGE HEADER"); - sig_writeheader(e, &s); - - /* --- Hash the message --- */ - - mcsetup_readfile(&mc_in, f, fn); - mcsetup_write(&mc_out, f, &e); - sig_hash(&s); - for (;;) { - n = mc_in.read(&mc_in, bb); - if (!n) break; - dohash(s.s->h, bb, n); - mc_out.write(&mc_out, bb, n); - } - mc_endread(&mc_in, 0, 0); - mc_endwrite(&mc_out, eo, &e); - - /* --- Write the signature --- */ - - if (s.s->ops->doit(s.s, &s.sig)) - die(EXIT_FAILURE, "signature failed"); - sig_writesig(e, &s); - e->ops->encdone(e); - if (fflush(ofp) || ferror(ofp) || fclose(ofp)) - die(EXIT_FAILURE, "error writing signature: %s", strerror(errno)); - - /* --- All done --- */ - - freeenc(e); - key_close(&kf); - sig_destroy(&s); - dstr_destroy(&d); - return (0); -} - -/*----- Verifying ---------------------------------------------------------*/ - -typedef struct vrfctx { - unsigned f, m; - int verb; - const char *what; -} vrfctx; - -static int vrfbdry(const char *b, void *p) -{ - vrfctx *v = p; - - if (strcmp(b, "CATSIGN MESSAGE") == 0) { - v->f |= F_BINARY; - v->m |= F_BINARY | F_DETACH; - return (1); - } else if (strcmp(b, "CATSIGN MESSAGE HEADER") == 0) { - v->m |= F_BINARY | F_DETACH; - return (1); - } else if (strcmp(b, "CATSIGN SIGNATURE") == 0) { - v->f |= F_DETACH; - v->m |= F_DETACH; - return (1); - } else - return (0); -} - -static void vrfchoke(const char *m, void *p) -{ - vrfctx *v = p; - if (v->verb) printf("FAIL %s: %s\n", v->what, m); - exit(EXIT_FAILURE); -} - -static int verify(int argc, char *argv[]) -{ - const char *ef = "binary", *of = 0, *fn, *dfn = 0, *kn = 0, *err; - vrfctx v = { 0, 0, 1 }; - key_file kf; - key *k, *kk = 0; - sigmsg s; - FILE *fp, *ofp = 0, *rfp = 0; - fprogress ff; - struct tm *tm; - int i; - char bb[MSGBUFSZ]; - size_t n; - time_t t_fresh = 0; - dstr d = DSTR_INIT, dd = DSTR_INIT; - const encops *eo; - msgcanon mc_in = MC_INIT; - enc *e; - - for (;;) { - static const struct option opt[] = { - { "armour", 0, 0, 'a' }, - { "armor", 0, 0, 'a' }, - { "buffer", 0, 0, 'b' }, - { "key", OPTF_ARGREQ, 0, 'k' }, - { "format", OPTF_ARGREQ, 0, 'f' }, - { "output", OPTF_ARGREQ, 0, 'o' }, - { "progress", 0, 0, 'p' }, - { "quiet", 0, 0, 'q' }, - { "utc", 0, 0, 'u' }, - { "fresh-time", 0, 0, 't' }, - { "gmt", 0, 0, 'u' }, - { "verbose", 0, 0, 'v' }, - { "nocheck", 0, 0, 'C' }, - { 0, 0, 0, 0 } - }; - i = mdwopt(argc, argv, "k:f:o:abpqt:uvC", opt, 0, 0, 0); - if (i < 0) break; - switch (i) { - case 'a': ef = "pem"; break; - case 'b': v.f |= F_BUFFER; break; - case 'k': kn = optarg; break; - case 'f': ef = optarg; break; - case 'o': of = optarg; break; - case 'u': v.f |= F_UTC; break; - case 'C': v.f |= F_NOCHECK; break; - case 't': - if (strcmp(optarg, "always") == 0) t_fresh = 0; - else if ((t_fresh = get_date(optarg, 0)) < 0) - die(EXIT_FAILURE, "bad freshness time"); - break; - case 'q': if (v.verb > 0) v.verb--; break; - case 'v': if (v.verb < 10) v.verb++; break; - case 'p': v.f |= F_PROGRESS; break; - default: v.f |= F_BOGUS; break; - } - } - if (argc - optind > 2 || (v.f & F_BOGUS)) - die(EXIT_FAILURE, "Usage: verify [-OPTIONS] [FILE [MESSAGE]]"); - - if ((eo = getenc(ef)) == 0) - die(EXIT_FAILURE, "encoding `%s' not found", ef); - - fn = optind < argc ? argv[optind++] : "-"; - if (strcmp(fn, "-") == 0) - fp = stdin; - else if ((fp = fopen(fn, eo->rmode)) == 0) { - die(EXIT_FAILURE, "couldn't open file `%s': %s", - fn, strerror(errno)); - } - - if (key_open(&kf, keyring, KOPEN_READ, key_moan, 0)) - die(EXIT_FAILURE, "can't open keyring `%s'", keyring); - if (kn && (kk = key_bytag(&kf, kn)) == 0) - die(EXIT_FAILURE, "key `%s' not found", kn); - - e = initdec(eo, fp, vrfbdry, &v); - - /* --- Read the header chunk --- */ - - v.what = "malformed header"; - sig_readheader(e, &s, vrfchoke, &v); - - if (((s.f ^ v.f) & v.m) != 0) { - if (v.verb) printf("FAIL boundary string inconsistent with contents\n"); - exit(EXIT_FAILURE); - } - v.f |= s.f; - - if ((k = key_byid(&kf, s.keyid)) == 0) { - if (v.verb) printf("FAIL key id %08lx not found\n", - (unsigned long)s.keyid); - exit(EXIT_FAILURE); - } - if (kk && k->id != kk->id) { - if (v.verb) { - dstr_reset(&d); key_fulltag(k, &d); - dstr_reset(&dd); key_fulltag(kk, &dd); - printf("FAIL signing key is %s; expected key %s\n", d.buf, dd.buf); - } - exit(EXIT_FAILURE); - } - - s.s = getsig(k, "ccsig", 0); - dstr_reset(&d); key_fulltag(k, &d); - if (!(v.f & F_NOCHECK) && v.verb && (err = s.s->ops->check(s.s)) != 0) - printf("WARN verification key %s fails check: %s\n", d.buf, err); - - dstr_reset(&dd); keyhash(k, s.s, &dd); - if (dd.len != s.kh.len || memcmp(dd.buf, s.kh.buf, dd.len) != 0) { - if (v.verb) printf("FAIL key hash mismatch\n"); - exit(EXIT_FAILURE); - } - - /* --- Now a merry dance --- */ - - if (v.f & F_DETACH) - sig_readsig(e, &s); - if (optind < argc) - dfn = argv[optind++]; - mcsetup_read(&mc_in, v.f, &e, fn, dfn); - - if (!of && (v.f & F_DETACH)) { - rfp = ofp = 0; - v.f &= ~F_BUFFER; - } else if (!of || strcmp(of, "-") == 0) { - v.f |= F_BUFFER; - ofp = stdout; - } - if (of && !(v.f & F_BUFFER)) { - if ((ofp = fopen(of, (v.f & F_BINARY) ? "wb" : "w")) == 0) { - die(EXIT_FAILURE, "couldn't open file `%s' for output: %s", - of, strerror(errno)); - } - rfp = ofp; - } else if ((rfp = tmpfile()) == 0) - die(EXIT_FAILURE, "couldn't create temporary file: %s", strerror(errno)); - - /* --- Read the message and verify the signature --- */ - - sig_hash(&s); - for (;;) { - n = mc_in.read(&mc_in, bb); - if (!n) break; - dohash(s.s->h, bb, n); - if (rfp) fwrite(bb, 1, n, rfp); - } - mc_endread(&mc_in, eo, &e); - if (!(v.f & F_DETACH)) - sig_readsig(e, &s); - if (rfp && (ferror(rfp) || fflush(rfp))) { - if (v.verb) printf("FAIL error writing message: %s\n", strerror(errno)); - exit(EXIT_FAILURE); - } - - /* --- Check the signature --- */ - - if (s.s->ops->doit(s.s, &s.sig)) { - if (v.verb) printf("FAIL signature verification failed\n"); - exit(EXIT_FAILURE); - } - if (t_fresh && s.t < t_fresh) { - if (v.verb) printf("FAIL signature is stale\n"); - exit(EXIT_FAILURE); - } - if (s.t > time(0)) { - if (v.verb) printf("FAIL signature timestamp in the future\n"); - exit(EXIT_FAILURE); - } - if (v.verb) { - tm = (v.f & F_UTC) ? gmtime(&s.t) : localtime(&s.t); - strftime(bb, sizeof(bb), "%Y-%m-%d %H:%M:%S %Z", tm); - printf("INFO good-signature %s\n", d.buf); - printf("INFO date %s\n", bb); - } - - /* --- Unbuffer buffered input --- */ - - if (v.f & F_BUFFER) { - if (!ofp && (ofp = fopen(of, "wb")) == 0) { - die(EXIT_FAILURE, "couldn't open file `%s' for output: %s", - of, strerror(errno)); - } - rewind(rfp); - if (v.f & F_PROGRESS) fprogress_init(&ff, "copying buffer", rfp); - if (v.verb && ofp == stdout) printf("DATA\n"); - for (;;) { - n = fread(bb, 1, sizeof(bb), rfp); - if (!n) break; - if (v.f & F_PROGRESS) fprogress_update(&ff, n); - if (fwrite(bb, 1, n, ofp) < n) { - if (v.f & F_PROGRESS) fprogress_done(&ff); - die(EXIT_FAILURE, "error writing output: %s", strerror(errno)); - } - } - if (v.f & F_PROGRESS) fprogress_done(&ff); - if (ferror(rfp) || fclose(rfp)) - die(EXIT_FAILURE, "error unbuffering output: %s", strerror(errno)); - } - if (ofp && (fflush(ofp) || ferror(ofp) || fclose(ofp))) - die(EXIT_FAILURE, "error writing output: %s", strerror(errno)); - - /* --- Tidy up --- */ - - e->ops->decdone(e); - if (v.verb && ofp != stdout) - printf("OK verified successfully\n"); - freeenc(e); - key_close(&kf); - sig_destroy(&s); - dstr_destroy(&d); - dstr_destroy(&dd); - return (0); -} - -/*----- Reformatting ------------------------------------------------------*/ - -static int format(int argc, char *argv[]) -{ - const char *ief = "binary", *oef = "binary"; - const char *fn, *dfn = 0, *of = 0, *mf = 0; - sigmsg s; - FILE *fp, *ofp = 0, *mfp = 0; - int i; - size_t n; - msgcanon mc_in = MC_INIT, mc_out = MC_INIT; - char bb[MSGBUFSZ]; - vrfctx v = { 0, 0, 1 }; - unsigned f = 0, fm = ~F_SIGMASK, sf; - const encops *ieo, *oeo; - enc *ie, *oe; - - for (;;) { - static const struct option opt[] = { - { "armour-in", 0, 0, 'a' }, - { "armor-in", 0, 0, 'a' }, - { "armour-out", 0, 0, 'A' }, - { "armor-out", 0, 0, 'A' }, - { "detach", 0, 0, 'D' }, - { "embed", 0, 0, 'E' }, - { "format-in", OPTF_ARGREQ, 0, 'f' }, - { "format-out", OPTF_ARGREQ, 0, 'F' }, - { "message", OPTF_ARGREQ, 0, 'm' }, - { "output", OPTF_ARGREQ, 0, 'o' }, - { "progress", 0, 0, 'p' }, - { 0, 0, 0, 0 } - }; - i = mdwopt(argc, argv, "f:F:m:o:apADE", opt, 0, 0, 0); - if (i < 0) break; - switch (i) { - case 'a': ief = "pem"; break; - case 'A': oef = "pem"; break; - case 'f': ief = optarg; break; - case 'F': oef = optarg; break; - case 'D': f |= F_DETACH; fm |= F_DETACH; break; - case 'E': f &= ~F_DETACH; fm |= F_DETACH; break; - case 'm': mf = optarg; break; - case 'o': of = optarg; break; - case 'p': f |= F_PROGRESS; break; - default: f |= F_BOGUS; break; - } - } - - if (argc - optind > 2 || (f & F_BOGUS)) - die(EXIT_FAILURE, "Usage: format [-OPTIONS] [FILE [MESSAGE]]"); - - if ((ieo = getenc(ief)) == 0) - die(EXIT_FAILURE, "encoding `%s' not found", ief); - if ((oeo = getenc(oef)) == 0) - die(EXIT_FAILURE, "encoding `%s' not found", oef); - - fn = optind < argc ? argv[optind++] : "-"; - if (strcmp(fn, "-") == 0) - fp = stdin; - else if ((fp = fopen(fn, ieo->rmode)) == 0) { - die(EXIT_FAILURE, "couldn't open file `%s': %s", - fn, strerror(errno)); - } - - if (optind < argc) - dfn = argv[optind++]; - - ie = initdec(ieo, fp, vrfbdry, &v); - - /* --- Read the header chunk --- */ - - sig_readheader(ie, &s, diechoke, "malformed header"); - - if (((s.f ^ v.f) & v.m) != 0) - moan("boundary string inconsistent with contents (ignoring)"); - - mcsetup_read(&mc_in, s.f, &ie, fn, dfn); - - /* --- Prepare the output stuff --- */ - - if (!of && !mf) of = "-"; - sf = s.f; - f = (f & fm) | (s.f & ~fm); - s.f = f & F_SIGMASK; - - if (sf & F_DETACH) - sig_readsig(ie, &s); - - if (!of) - mcsetup_writenull(&mc_out); - else { - if (strcmp(of, "-") == 0) - ofp = stdout; - else if ((ofp = fopen(of, oeo->wmode)) == 0) { - die(EXIT_FAILURE, "couldn't open file `%s' for output: %s", - of, strerror(errno)); - } - oe = initenc(oeo, ofp, - (f & F_DETACH) ? "CATSIGN SIGNATURE" : - (f & F_BINARY) ? "CATSIGN MESSAGE" : - "CATSIGN MESSAGE HEADER"); - sig_writeheader(oe, &s); - mcsetup_write(&mc_out, f, &oe); - } - - if (mf) { - if (strcmp(mf, "-") == 0) - mfp = stdout; - else if ((mfp = fopen(mf, (f & F_BINARY) ? "wb" : "w")) == 0) { - die(EXIT_FAILURE, "couldn't open file `%s' for output: %s", - mf, strerror(errno)); - } - } - - /* --- Wade through the message body --- */ - - for (;;) { - n = mc_in.read(&mc_in, bb); - if (!n) break; - mc_out.write(&mc_out, bb, n); - if (mfp) fwrite(bb, 1, n, mfp); - } - mc_endread(&mc_in, ieo, &ie); - if (of) mc_endwrite(&mc_out, oeo, &oe); - - /* --- Write the signature --- */ - - if (!(sf & F_DETACH)) - sig_readsig(ie, &s); - if (of) { - sig_writesig(oe, &s); - oe->ops->encdone(oe); - } - - /* --- All done --- */ - - ie->ops->decdone(ie); - if (ferror(fp) || fclose(fp)) - die(EXIT_FAILURE, "error reading input signature: %s", strerror(errno)); - if (ofp && (fflush(ofp) || ferror(ofp) || fclose(ofp))) - die(EXIT_FAILURE, "error writing output signature: %s", strerror(errno)); - if (mfp && (fflush(mfp) || ferror(mfp) || fclose(mfp))) - die(EXIT_FAILURE, "error writing output message: %s", strerror(errno)); - freeenc(ie); - if (of) freeenc(oe); - sig_destroy(&s); - return (0); -} - -static void infochoke(const char *m, void *p) -{ - vrfctx *v = p; - printf("BAD %s: %s\n", v->what, m); - exit(EXIT_FAILURE); -} - -static void infokeyreport(const char *file, int line, - const char *err, void *p) -{ /*whatever*/; } - -static int info(int argc, char *argv[]) -{ - const char *ef = "binary"; - vrfctx v = { 0, 0, 1 }; - key_file kf; - key *k; - sigmsg s; - FILE *fp; - int i; - struct tm *tm; - char bb[256]; - dstr d = DSTR_INIT; - const encops *eo; - enc *e; - - for (;;) { - static const struct option opt[] = { - { "armour", 0, 0, 'a' }, - { "armor", 0, 0, 'a' }, - { "format", OPTF_ARGREQ, 0, 'f' }, - { "gmt", 0, 0, 'u' }, - { "utc", 0, 0, 'u' }, - { 0, 0, 0, 0 } - }; - i = mdwopt(argc, argv, "f:au", opt, 0, 0, 0); - if (i < 0) break; - switch (i) { - case 'a': ef = "pem"; break; - case 'f': ef = optarg; break; - case 'u': v.f |= F_UTC; break; - default: v.f |= F_BOGUS; break; - } - } - if (argc - optind > 1 || (v.f & F_BOGUS)) - die(EXIT_FAILURE, "Usage: info [-OPTIONS] [FILE]"); - - if ((eo = getenc(ef)) == 0) - die(EXIT_FAILURE, "encoding `%s' not found", ef); - - if (optind >= argc) - fp = stdin; - else if (strcmp(argv[optind], "-") == 0) { - fp = stdin; - optind++; - } else if ((fp = fopen(argv[optind], eo->rmode)) == 0) { - die(EXIT_FAILURE, "couldn't open file `%s': %s", - argv[optind], strerror(errno)); - } else - optind++; - - if (key_open(&kf, keyring, KOPEN_READ, infokeyreport, 0)) { - printf("NOTE can't open keyring `%s'\n", keyring); - keyring = 0; - } - e = initdec(eo, fp, vrfbdry, &v); - - v.what = "malformed header"; - sig_readheader(e, &s, infochoke, &v); - - printf("INFO flags %sdetach %sbinary\n", - (s.f & F_DETACH) ? "" : "!", - (s.f & F_BINARY) ? "" : "!"); - - if (((s.f ^ v.f) & v.m) != 0) { - printf("WARN boundary string inconsistent with contents\n"); - printf("INFO expected-flags"); - if (v.m & F_DETACH) printf(" %sdetach", (v.f & F_DETACH) ? "" : "!"); - if (v.m & F_BINARY) printf(" %sbinary", (v.f & F_BINARY) ? "" : "!"); - putchar('\n'); - } - v.f |= s.f; - - tm = (v.f & F_UTC) ? gmtime(&s.t) : localtime(&s.t); - strftime(bb, sizeof(bb), "%Y-%m-%d %H:%M:%S %Z", tm); - printf("INFO date %s\n", bb); - - if (keyring && (k = key_byid(&kf, s.keyid)) != 0) { - dstr_reset(&d); key_fulltag(k, &d); - printf("INFO key %s\n", d.buf); - } else - printf("INFO unknown-key %08lx\n", (unsigned long)s.keyid); - - if (keyring) key_close(&kf); - dstr_destroy(&d); - sig_destroy(&s); - return (0); -} - -/*----- Main code ---------------------------------------------------------*/ - -#define LISTS(LI) \ - LI("Lists", list, \ - listtab[i].name, listtab[i].name) \ - LI("Signature schemes", sig, \ - sigtab[i].name, sigtab[i].name) \ - LI("Encodings", enc, \ - enctab[i].name, enctab[i].name) \ - LI("Hash functions", hash, \ - ghashtab[i], ghashtab[i]->name) - -MAKELISTTAB(listtab, LISTS) - -int cmd_show(int argc, char *argv[]) -{ - return (displaylists(listtab, argv + 1)); -} - -static int cmd_help(int, char **); - -static cmd cmdtab[] = { - { "help", cmd_help, "help [COMMAND...]" }, - { "show", cmd_show, "show [ITEM...]" }, - CMD_ENCODE, - CMD_DECODE, - { "sign", sign, - "sign [-adptC] [-k TAG] [-f FORMAT] [-o OUTPUT] [FILE]", "\ -Options:\n\ -\n\ --a, --armour Same as `-f pem'.\n\ --b, --binary Treat the input message as binary data.\n\ --d, --detach Produce a detached signature.\n\ --f, --format=FORMAT Encode as FORMAT.\n\ --k, --key=TAG Use public encryption key named by TAG.\n\ --o, --output=FILE Write output to FILE.\n\ --p, --progress Show progress on large files.\n\ --t, --text Canonify input message as a text file.\n\ --C, --nocheck Don't check the private key.\n\ -" }, - { "verify", verify, - "verify [-abpquvC] [-f FORMAT] [-k TAG] [-o OUTPUT]\n\t\ -[FILE [MESSAGE]]", "\ -Options:\n\ -\n\ --a, --armour Same as `-f pem'.\n\ --b, --buffer Buffer message until signature is verified.\n\ --f, --format=FORMAT Decode as FORMAT.\n\ --k, --key=TAG Require that the message be signed by key TAG.\n\ --o, --output=FILE Write message to FILE.\n\ --p, --progress Show progress on large files.\n\ --q, --quiet Produce fewer messages.\n\ --t, --freshtime=TIME Only accept signatures made after this time.\n\ --u, --utc Show dates in UTC rather than local time.\n\ --v, --verbose Produce more verbose messages.\n\ --C, --nocheck Don't check the public key.\n\ -" }, - { "info", info, - "info [-au] [-f FORMAT] [FILE]", "\ -Options:\n\ -\n\ --a, --armour Same as `-f pem'.\n\ --f, --format=FORMAT Decode as FORMAT.\n\ --u, --utc Show dates in UTC rather than local time.\n\ -"}, - { "format", format, - "format [-apuADE] [-f FORMAT] [-F format] [-m FILE] [-o FILE]\n\t\ -[FILE [MESSAGE]]", "\ -Options:\n\ -\n\ --a, --armour-in Same as `-f pem'.\n\ --A, --armour-out Same as `-F pem'.\n\ --D, --detach Create detached signature.\n\ --E, --embed Create signature with embedded message.\n\ --f, --format-in=FORMAT Decode input as FORMAT.\n\ --F, --format-out=FORMAT Encode output as FORMAT.\n\ --m, --message=FILE Write message to FILE.\n\ --o, --output=FILE Write new signature to FILE.\n\ --p, --progress Show progress on large files.\n\ -"}, - { 0, 0, 0 } -}; /* " Emacs seems confused. */ - -static int cmd_help(int argc, char **argv) -{ - sc_help(cmdtab, stdout, argv + 1); - return (0); -} - -void version(FILE *fp) -{ - pquis(fp, "$, Catacomb version " VERSION "\n"); -} - -static void usage(FILE *fp) -{ - pquis(fp, "Usage: $ [-k KEYRING] COMMAND [ARGS]\n"); -} - -void help_global(FILE *fp) -{ - usage(fp); - fputs("\n\ -Sign and verify data.\n\ -\n\ -Global command-line options:\n\ -\n\ --h, --help [COMMAND...] Show this help message, or help for COMMANDs.\n\ --v, --version Show program version number.\n\ --u, --usage Show a terse usage message.\n\ -\n\ --k, --keyring=FILE Read keys from FILE.\n", - fp); -} - -/* --- @main@ --- * - * - * Arguments: @int argc@ = number of command line arguments - * @char *argv[]@ = vector of command line arguments - * - * Returns: Zero if successful, nonzero otherwise. - * - * Use: Encrypts or decrypts files. - */ - -int main(int argc, char *argv[]) -{ - unsigned f = 0; - -#define f_bogus 1u - - /* --- Initialize the library --- */ - - ego(argv[0]); - sub_init(); - rand_noisesrc(RAND_GLOBAL, &noise_source); - rand_seed(RAND_GLOBAL, 160); -/* trace_on(stderr, 1); */ - - /* --- Parse options --- */ - - for (;;) { - static struct option opts[] = { - { "help", 0, 0, 'h' }, - { "version", 0, 0, 'v' }, - { "usage", 0, 0, 'u' }, - { "keyring", OPTF_ARGREQ, 0, 'k' }, - { 0, 0, 0, 0 } - }; - int i = mdwopt(argc, argv, "+hvu k:", opts, 0, 0, 0); - if (i < 0) - break; - switch (i) { - case 'h': - sc_help(cmdtab, stdout, argv + optind); - exit(0); - break; - case 'v': - version(stdout); - exit(0); - break; - case 'u': - usage(stdout); - exit(0); - case 'k': - keyring = optarg; - break; - default: - f |= f_bogus; - break; - } - } - - argc -= optind; - argv += optind; - optind = 0; - if (f & f_bogus || argc < 1) { - usage(stderr); - exit(EXIT_FAILURE); - } - - /* --- Dispatch to the correct subcommand handler --- */ - - return (findcmd(cmdtab, argv[0])->cmd(argc, argv)); - -#undef f_bogus -} - -/*----- That's all, folks -------------------------------------------------*/