Rearrange the file tree.
[u/mdw/catacomb] / catsign.c
diff --git a/catsign.c b/catsign.c
deleted file mode 100644 (file)
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 <ctype.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-#include <mLib/base64.h>
-#include <mLib/dstr.h>
-#include <mLib/mdwopt.h>
-#include <mLib/quis.h>
-#include <mLib/report.h>
-#include <mLib/sub.h>
-
-#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 -------------------------------------------------*/