Incompatible change! Add new signature schemes. Key now implies
authormdw <mdw>
Thu, 8 Apr 2004 01:02:15 +0000 (01:02 +0000)
committermdw <mdw>
Thu, 8 Apr 2004 01:02:15 +0000 (01:02 +0000)
algorithms (integrity checked by new fingerprinting rules), so don't put
that stuff in the manifest.

dsig.c

diff --git a/dsig.c b/dsig.c
index 6a67990..030ad0a 100644 (file)
--- a/dsig.c
+++ b/dsig.c
@@ -1,6 +1,6 @@
 /* -*-c-*-
  *
- * $Id: dsig.c,v 1.8 2004/04/04 19:42:59 mdw Exp $
+ * $Id: dsig.c,v 1.9 2004/04/08 01:02:15 mdw Exp $
  *
  * Verify signatures on distribuitions of files
  *
 /*----- Revision history --------------------------------------------------* 
  *
  * $Log: dsig.c,v $
+ * Revision 1.9  2004/04/08 01:02:15  mdw
+ * Incompatible change!  Add new signature schemes.  Key now implies
+ * algorithms (integrity checked by new fingerprinting rules), so don't put
+ * that stuff in the manifest.
+ *
  * Revision 1.8  2004/04/04 19:42:59  mdw
  * Add set -e.
  *
 #include "key-data.h"
 #include "noise.h"
 
-#include "dsa.h"
+#include "ec.h"
+#include "ec-keys.h"
+#include "dh.h"
+#include "gdsa.h"
+#include "gkcdsa.h"
 #include "rsa.h"
-#include "pkcs1.h"
 
-/*----- Digital signature algorithm ---------------------------------------*/
+#include "sha.h"
+#include "has160.h"
 
-static int dsasign(key *k, const void *m, size_t msz, dstr *d)
-{
-  dsa_priv dp;
-  key_packstruct ks[DSA_PRIVFETCHSZ];
+/*----- Algorithm choice --------------------------------------------------*/
+
+/* --- Relevant type operations --- */
+
+typedef struct sig {
+  const struct sigops *ops;
   key_packdef *kp;
-  size_t sz;
-  octet *p;
-  int e;
+  ghash *h;
+} sig;
 
-  kp = key_fetchinit(dsa_privfetch, ks, &dp);
-  if ((e = key_fetch(kp, k)) != 0) {
-    key_fetchdone(kp);
-    return (e);
-  }
-  sz = mp_octets(dp.dp.q);
-  if (sz < msz)
-    die(EXIT_FAILURE, "hash function too wide for this signing key");
-  DENSURE(d, sz * 2);
-  p = (octet *)d->buf + d->len;
-  rand_get(RAND_GLOBAL, p, sz);
-  dsa_sign(&dp.dp, dp.x, m, msz, p, sz, p, sz, p + sz, sz);
-  d->len += sz * 2;
-  key_fetchdone(kp);
-  return (0);
+typedef struct sigops {
+  const key_fetchdef *kf;              /* Key fetching structure */
+  size_t kdsz;                         /* Size of the key-data structure */
+  sig *(*init)(key */*k*/, void */*kd*/, const gchash */*hc*/);
+  int (*doit)(sig */*s*/, dstr */*d*/);
+  void (*destroy)(sig */*s*/);
+} sigops;
+
+/* --- RSA PKCS1 --- */
+
+typedef struct rsap1_sigctx {
+  sig s;
+  rsa_privctx rp;
+  pkcs1 p1;
+} rsap1_sigctx;
+
+static sig *rsap1_siginit(key *k, void *kd, const gchash *hc)
+{
+  rsap1_sigctx *rs = CREATE(rsap1_sigctx);
+  rsa_privcreate(&rs->rp, kd, &rand_global);
+  rs->p1.r = &rand_global;
+  rs->p1.ep = hc->name;
+  rs->p1.epsz = strlen(hc->name) + 1;
+  rs->s.h = 0;
+  return (&rs->s);
 }
 
-static int dsaverify(key *k, const void *m, size_t msz,
-                    const void *s, size_t ssz)
+static int rsap1_sigdoit(sig *s, dstr *d)
 {
-  dsa_pub dp;
-  key_packstruct ks[DSA_PUBFETCHSZ];
-  key_packdef *kp;
-  size_t sz;
-  const octet *p = s;
-  int e;
+  rsap1_sigctx *rs = (rsap1_sigctx *)s;
+  size_t n;
+  mp *m = rsa_sign(&rs->rp, MP_NEW,
+                  GH_DONE(s->h, 0), GH_CLASS(s->h)->hashsz,
+                  pkcs1_sigencode, &rs->p1);
+  if (!m) return (-1);
+  n = mp_octets(rs->rp.rp->n); dstr_ensure(d, n); mp_storeb(m, d->buf, n);
+  d->len += n; mp_drop(m);
+  return (0);
+}
 
-  kp = key_fetchinit(dsa_pubfetch, ks, &dp);
-  if ((e = key_fetch(kp, k)) != 0) {
-    key_fetchdone(kp);
-    return (e);
-  }
-  sz = ssz / 2;
-  e = dsa_verify(&dp.dp, dp.y, m, msz, p, sz, p + sz, sz);
-  key_fetchdone(kp);
-  return (e);  
+static void rsap1_sigdestroy(sig *s)
+{
+  rsap1_sigctx *rs = (rsap1_sigctx *)s;
+  DESTROY(rs);
 }
 
-/*----- RSA signing -------------------------------------------------------*/
+static const sigops rsap1_sig = {
+  rsa_privfetch, sizeof(rsa_priv),
+  rsap1_siginit, rsap1_sigdoit, rsap1_sigdestroy
+};
+
+typedef struct rsap1_vrfctx {
+  sig s;
+  rsa_pubctx rp;
+  pkcs1 p1;
+} rsap1_vrfctx;
 
-static int rsasign(key *k, const void *m, size_t msz, dstr *d)
+static sig *rsap1_vrfinit(key *k, void *kd, const gchash *hc)
 {
-  rsa_priv rp;
-  rsa_privctx rpc;
-  pkcs1 pk = { 0, 0, 0 };
-  key_packstruct ks[RSA_PRIVFETCHSZ];
-  key_packdef *kp;
-  int e;
+  rsap1_vrfctx *rv = CREATE(rsap1_vrfctx);
+  rsa_pubcreate(&rv->rp, kd);
+  rv->p1.r = &rand_global;
+  rv->p1.ep = hc->name;
+  rv->p1.epsz = strlen(hc->name) + 1;
+  rv->s.h = 0;
+  return (&rv->s);
+}
 
-  kp = key_fetchinit(rsa_privfetch, ks, &rp);
-  if ((e = key_fetch(kp, k)) != 0) {
-    key_fetchdone(kp);
-    return (e);
-  }
-  rsa_privcreate(&rpc, &rp, &rand_global);
-  if (rsa_sign(&rpc, m, msz, d, pkcs1_sigencode, &pk) < 0)
-    die(EXIT_FAILURE, "internal error in rsasign (key too small?)");
-  rsa_privdestroy(&rpc);
-  key_fetchdone(kp);
-  return (0);
+static int rsap1_vrfdoit(sig *s, dstr *d)
+{
+  rsap1_vrfctx *rv = (rsap1_vrfctx *)s;
+  mp *m = mp_loadb(MP_NEW, d->buf, d->len);
+  int rc = rsa_verify(&rv->rp, m,
+                     GH_DONE(s->h, 0), GH_CLASS(s->h)->hashsz,
+                     0, pkcs1_sigdecode, &rv->p1);
+  mp_drop(m);
+  return (rc);
 }
 
-static int rsaverify(key *k, const void *m, size_t msz,
-                    const void *s, size_t ssz)
+static void rsap1_vrfdestroy(sig *s)
+{
+  rsap1_vrfctx *rv = (rsap1_vrfctx *)s;
+  DESTROY(rv);
+}
+
+static const sigops rsap1_vrf = {
+  rsa_pubfetch, sizeof(rsa_pub),
+  rsap1_vrfinit, rsap1_vrfdoit, rsap1_vrfdestroy
+};
+
+/* --- RSA PSS --- */
+
+static const gccipher *getmgf(key *k, const gchash *hc)
 {
-  rsa_pub rp;
-  rsa_pubctx rpc;
-  pkcs1 pk = { 0, 0, 0 };
-  key_packstruct ks[RSA_PUBFETCHSZ];
-  key_packdef *kp;
-  int ok = 0;
   dstr d = DSTR_INIT;
-  int e;
+  const gccipher *gc;
+  const char *mm;
 
-  kp = key_fetchinit(rsa_pubfetch, ks, &rp);
-  if ((e = key_fetch(kp, k)) != 0) {
-    key_fetchdone(kp);
-    return (e);
+  if ((mm = key_getattr(0, k, "mgf-alg")) == 0) {
+    dstr_putf(&d, "%s-mgf", hc->name);
+    mm = d.buf;
   }
-  rsa_pubcreate(&rpc, &rp);
-  if (rsa_verify(&rpc, s, ssz, &d, pkcs1_sigdecode, &pk) > 0 &&
-      msz == d.len && memcmp(d.buf, m, msz) == 0)
-    ok = 1;
+  if ((gc = gcipher_byname(mm)) == 0)
+    die(EXIT_FAILURE, "unknown encryption scheme `%s'", mm);
   dstr_destroy(&d);
-  rsa_pubdestroy(&rpc);
-  key_fetchdone(kp);
-  return (ok);
-}  
+  return (gc);
+}
 
-/*----- Algorithm choice --------------------------------------------------*/
+typedef struct rsapss_sigctx {
+  sig s;
+  rsa_privctx rp;
+  pss p;
+} rsapss_sigctx;
 
-typedef struct sig {
-  const char *name;
-  const char *type;
-  int (*sign)(key */*k*/, const void */*m*/, size_t /*msz*/, dstr */*d*/);
-  int (*verify)(key */*k*/, const void */*m*/, size_t /*msz*/,
-               const void */*s*/, size_t /*ssz*/);
-} sig;
+static sig *rsapss_siginit(key *k, void *kd, const gchash *hc)
+{
+  rsapss_sigctx *rs = CREATE(rsapss_sigctx);
+  rsa_privcreate(&rs->rp, kd, &rand_global);
+  rs->p.r = &rand_global;
+  rs->p.cc = getmgf(k, hc);
+  rs->p.ch = hc;
+  rs->p.ssz = hc->hashsz;
+  return (&rs->s);
+}
 
-static sig sigtab[] = {
-  { "dsa", "dsig-dsa", dsasign, dsaverify },
-  { "rsa", "dsig-rsa", rsasign, rsaverify },
-  { 0, 0, 0 }
+static int rsapss_sigdoit(sig *s, dstr *d)
+{
+  rsapss_sigctx *rs = (rsapss_sigctx *)s;
+  size_t n;
+  mp *m = rsa_sign(&rs->rp, MP_NEW,
+                  GH_DONE(s->h, 0), GH_CLASS(s->h)->hashsz,
+                  pss_encode, &rs->p);
+  if (!m) return (-1);
+  n = mp_octets(rs->rp.rp->n); dstr_ensure(d, n); mp_storeb(m, d->buf, n);
+  d->len += n;  mp_drop(m);
+  return (0);
+}
+
+static void rsapss_sigdestroy(sig *s)
+{
+  rsapss_sigctx *rs = (rsapss_sigctx *)s;
+  DESTROY(rs);
+}
+
+static const sigops rsapss_sig = {
+  rsa_privfetch, sizeof(rsa_priv),
+  rsapss_siginit, rsapss_sigdoit, rsapss_sigdestroy
+};
+
+typedef struct rsapss_vrfctx {
+  sig s;
+  rsa_pubctx rp;
+  pss p;
+} rsapss_vrfctx;
+
+static sig *rsapss_vrfinit(key *k, void *kd, const gchash *hc)
+{
+  rsapss_vrfctx *rv = CREATE(rsapss_vrfctx);
+  rsa_pubcreate(&rv->rp, kd);
+  rv->p.r = &rand_global;
+  rv->p.cc = getmgf(k, hc);
+  rv->p.ch = hc;
+  rv->p.ssz = hc->hashsz;
+  return (&rv->s);
+}
+
+static int rsapss_vrfdoit(sig *s, dstr *d)
+{
+  rsapss_vrfctx *rv = (rsapss_vrfctx *)s;
+  mp *m = mp_loadb(MP_NEW, d->buf, d->len);
+  int rc = rsa_verify(&rv->rp, m,
+                     GH_DONE(s->h, 0), GH_CLASS(s->h)->hashsz,
+                     0, pss_decode, &rv->p);
+  mp_drop(m);
+  return (rc);
+}
+
+static void rsapss_vrfdestroy(sig *s)
+{
+  rsapss_vrfctx *rv = (rsapss_vrfctx *)s;
+  DESTROY(rv);
+}
+
+static const sigops rsapss_vrf = {
+  rsa_pubfetch, sizeof(rsa_pub),
+  rsapss_vrfinit, rsapss_vrfdoit, rsapss_vrfdestroy
+};
+
+/* --- DSA and ECDSA --- */
+
+typedef struct dsa_sigctx {
+  sig s;
+  gdsa g;
+} dsa_sigctx;
+
+static dsa_sigctx *dsa_doinit(key *k, const gprime_param *gp,
+                             mp *y, const gchash *hc)
+{
+  dsa_sigctx *ds = CREATE(dsa_sigctx);
+  dstr t = DSTR_INIT;
+
+  key_fulltag(k, &t);
+  if ((ds->g.g = group_prime(gp)) == 0)
+    die(EXIT_FAILURE, "bad prime group in key `%s'", t.buf);
+  ds->g.u = MP_NEW;
+  ds->g.p = G_CREATE(ds->g.g);
+  if (G_FROMINT(ds->g.g, ds->g.p, y))
+    die(EXIT_FAILURE, "bad public key in key `%s'", t.buf);
+  ds->g.r = &rand_global;
+  ds->g.h = hc;
+  ds->s.h = 0;
+  dstr_destroy(&t);
+  return (ds);
+}
+
+static dsa_sigctx *ecdsa_doinit(key *k, const char *cstr,
+                               ec *y, const gchash *hc)
+{
+  dsa_sigctx *ds = CREATE(dsa_sigctx);
+  ec_info ei;
+  const char *e;
+  dstr t = DSTR_INIT;
+
+  key_fulltag(k, &t);
+  if ((e = ec_getinfo(&ei, cstr)) != 0)
+    die(EXIT_FAILURE, "bad curve in key `%s': %s", t.buf, e);
+  ds->g.g = group_ec(&ei);
+  ds->g.u = MP_NEW;
+  ds->g.p = G_CREATE(ds->g.g);
+  if (G_FROMEC(ds->g.g, ds->g.p, y))
+    die(EXIT_FAILURE, "bad public key in key `%s'", t.buf);
+  ds->g.r = &rand_global;
+  ds->g.h = hc;
+  ds->s.h = 0;
+  dstr_destroy(&t);
+  return (ds);
+}
+
+static sig *dsa_siginit(key *k, void *kd, const gchash *hc)
+{
+  dh_priv *dp = kd;
+  dsa_sigctx *ds = dsa_doinit(k, &dp->dp, dp->y, hc);
+  ds->g.u = MP_COPY(dp->x);
+  return (&ds->s);
+}
+
+static sig *ecdsa_siginit(key *k, void *kd, const gchash *hc)
+{
+  ec_priv *ep = kd;
+  dsa_sigctx *ds = ecdsa_doinit(k, ep->cstr, &ep->p, hc);
+  ds->g.u = MP_COPY(ep->x);
+  return (&ds->s);
+}
+
+static int dsa_sigdoit(sig *s, dstr *d)
+{
+  dsa_sigctx *ds = (dsa_sigctx *)s;
+  gdsa_sig ss = GDSA_SIG_INIT;
+  size_t n = mp_octets(ds->g.g->r);
+
+  gdsa_sign(&ds->g, &ss, GH_DONE(ds->s.h, 0), 0);
+  dstr_ensure(d, 2 * n);
+  mp_storeb(ss.r, d->buf, n);
+  mp_storeb(ss.s, d->buf + n, n);
+  d->len += 2 * n;
+  mp_drop(ss.r); mp_drop(ss.s);
+  return (0);
+}
+
+static void dsa_sigdestroy(sig *s)
+{
+  dsa_sigctx *ds = (dsa_sigctx *)s;
+  G_DESTROY(ds->g.g, ds->g.p);
+  mp_drop(ds->g.u);
+  G_DESTROYGROUP(ds->g.g);
+}
+
+static const sigops dsa_sig = {
+  dh_privfetch, sizeof(dh_priv),
+  dsa_siginit, dsa_sigdoit, dsa_sigdestroy
+};
+
+static const sigops ecdsa_sig = {
+  ec_privfetch, sizeof(ec_priv),
+  ecdsa_siginit, dsa_sigdoit, dsa_sigdestroy
+};
+
+static sig *dsa_vrfinit(key *k, void *kd, const gchash *hc)
+{
+  dh_pub *dp = kd;
+  dsa_sigctx *ds = dsa_doinit(k, &dp->dp, dp->y, hc);
+  return (&ds->s);
+}
+
+static sig *ecdsa_vrfinit(key *k, void *kd, const gchash *hc)
+{
+  ec_pub *ep = kd;
+  dsa_sigctx *ds = ecdsa_doinit(k, ep->cstr, &ep->p, hc);
+  return (&ds->s);
+}
+
+static int dsa_vrfdoit(sig *s, dstr *d)
+{
+  dsa_sigctx *ds = (dsa_sigctx *)s;
+  gdsa_sig ss;
+  size_t n = d->len/2;
+  int rc;
+
+  ss.r = mp_loadb(MP_NEW, d->buf, n);
+  ss.s = mp_loadb(MP_NEW, d->buf + n, d->len - n);
+  rc = gdsa_verify(&ds->g, &ss, GH_DONE(ds->s.h, 0));
+  mp_drop(ss.r); mp_drop(ss.s);
+  return (rc);
+}
+
+static const sigops dsa_vrf = {
+  dh_pubfetch, sizeof(dh_pub),
+  dsa_vrfinit, dsa_vrfdoit, dsa_sigdestroy
 };
 
-/* --- @gethash@ --- *
+static const sigops ecdsa_vrf = {
+  ec_pubfetch, sizeof(ec_pub),
+  ecdsa_vrfinit, dsa_vrfdoit, dsa_sigdestroy
+};
+
+/* --- KCDSA and ECKCDSA --- */
+
+static void kcdsa_privkey(dsa_sigctx *ds, mp *x)
+  { ds->g.u = mp_modinv(MP_NEW, x, ds->g.g->r); }
+
+static void kcdsa_sethash(dsa_sigctx *ds, const gchash *hc)
+  { ds->s.h = gkcdsa_beginhash(&ds->g); }
+
+static sig *kcdsa_siginit(key *k, void *kd, const gchash *hc)
+{
+  dh_priv *dp = kd;
+  dsa_sigctx *ds = dsa_doinit(k, &dp->dp, dp->y, hc);
+  kcdsa_privkey(ds, dp->x);
+  kcdsa_sethash(ds, hc);
+  return (&ds->s);
+}
+
+static sig *eckcdsa_siginit(key *k, void *kd, const gchash *hc)
+{
+  ec_priv *ep = kd;
+  dsa_sigctx *ds = ecdsa_doinit(k, ep->cstr, &ep->p, hc);
+  kcdsa_privkey(ds, ep->x);
+  kcdsa_sethash(ds, hc);
+  return (&ds->s);
+}
+
+static int kcdsa_sigdoit(sig *s, dstr *d)
+{
+  dsa_sigctx *ds = (dsa_sigctx *)s;
+  gkcdsa_sig ss = GKCDSA_SIG_INIT;
+  size_t hsz = ds->g.h->hashsz, n = mp_octets(ds->g.g->r);
+
+  gkcdsa_sign(&ds->g, &ss, GH_DONE(ds->s.h, 0), 0);
+  dstr_ensure(d, hsz + n);
+  memcpy(d->buf, ss.r, hsz);
+  mp_storeb(ss.s, d->buf + hsz, n);
+  d->len += hsz + n;
+  xfree(ss.r); mp_drop(ss.s);
+  return (0);
+}
+
+static const sigops kcdsa_sig = {
+  dh_privfetch, sizeof(dh_priv),
+  kcdsa_siginit, kcdsa_sigdoit, dsa_sigdestroy
+};
+
+static const sigops eckcdsa_sig = {
+  ec_privfetch, sizeof(ec_priv),
+  eckcdsa_siginit, kcdsa_sigdoit, dsa_sigdestroy
+};
+
+static sig *kcdsa_vrfinit(key *k, void *kd, const gchash *hc)
+{
+  dh_pub *dp = kd;
+  dsa_sigctx *ds = dsa_doinit(k, &dp->dp, dp->y, hc);
+  kcdsa_sethash(ds, hc);
+  return (&ds->s);
+}
+
+static sig *eckcdsa_vrfinit(key *k, void *kd, const gchash *hc)
+{
+  ec_pub *ep = kd;
+  dsa_sigctx *ds = ecdsa_doinit(k, ep->cstr, &ep->p, hc);
+  kcdsa_sethash(ds, hc);
+  return (&ds->s);
+}
+
+static int kcdsa_vrfdoit(sig *s, dstr *d)
+{
+  dsa_sigctx *ds = (dsa_sigctx *)s;
+  gkcdsa_sig ss;
+  size_t hsz = ds->g.h->hashsz, n = d->len - hsz;
+  int rc;
+
+  if (d->len < hsz)
+    return (-1);
+  ss.r = (octet *)d->buf;
+  ss.s = mp_loadb(MP_NEW, d->buf + hsz, n);
+  rc = gkcdsa_verify(&ds->g, &ss, GH_DONE(ds->s.h, 0));
+  mp_drop(ss.s);
+  return (rc);
+}
+
+static const sigops kcdsa_vrf = {
+  dh_pubfetch, sizeof(dh_pub),
+  kcdsa_vrfinit, kcdsa_vrfdoit, dsa_sigdestroy
+};
+
+static const sigops eckcdsa_vrf = {
+  ec_pubfetch, sizeof(ec_pub),
+  eckcdsa_vrfinit, kcdsa_vrfdoit, dsa_sigdestroy
+};
+
+/* --- The switch table --- */
+
+static const struct sigtab {
+  const char *name;
+  const sigops *signops;
+  const sigops *verifyops;
+  const gchash *ch;
+} sigtab[] = {
+  { "rsapkcs1",        &rsap1_sig,     &rsap1_vrf,     &sha },
+  { "rsapss",  &rsapss_sig,    &rsapss_vrf,    &sha },
+  { "dsa",     &dsa_sig,       &dsa_vrf,       &sha },
+  { "ecdsa",   &ecdsa_sig,     &ecdsa_vrf,     &sha },
+  { "kcdsa",   &kcdsa_sig,     &kcdsa_vrf,     &has160 },
+  { "eckcdsa", &eckcdsa_sig,   &eckcdsa_vrf,   &has160 },
+  { 0,         0,              0 }
+};
+
+/* --- @getsig@ --- *
  *
- * Arguments:  @const char *name@ = pointer to name string
+ * Arguments:  @key *k@ = the key to load
+ *             @int wantpriv@ = nonzero if we want to sign
  *
- * Returns:    Pointer to appropriate hash class.
+ * Returns:    A signature-making thing.
  *
- * Use:                Chooses a hash function by name.
+ * Use:                Loads a key and starts hashing.
  */
 
-static const gchash *gethash(const char *name)
+static sig *getsig(key *k, int wantpriv)
 {
-  const gchash *const *g, *gg = 0;
-  size_t sz = strlen(name);
-  for (g = ghashtab; *g; g++) {
-    if (strncmp(name, (*g)->name, sz) == 0) {
-      if ((*g)->name[sz] == 0) {
-       gg = *g;
-       break;
-      } else if (gg)
-       return (0);
-      else
-       gg = *g;
+  const char *salg, *halg = 0;
+  dstr d = DSTR_INIT;
+  dstr t = DSTR_INIT;
+  char *p = 0;
+  const char *q;
+  sig *s;
+  const struct sigtab *st;
+  const sigops *so;
+  const gchash *ch;
+  void *kd;
+  int e;
+  key_packdef *kp;
+
+  /* --- Setup stuff --- */
+
+  key_fulltag(k, &t);
+
+  /* --- Get the signature algorithm --- *
+   *
+   * Take the attribute if it's there; otherwise use the key type.
+   */
+
+  if ((q = key_getattr(0, k, "sig-alg")) != 0) {
+    dstr_puts(&d, q);
+    p = d.buf;
+  } else if (strncmp(k->type, "dsig-", 5) == 0 && k->type[5]) {
+    dstr_puts(&d, k->type);
+    p = d.buf + 5;
+  } else
+    die(EXIT_FAILURE, "no signature algorithm for key `%s'", t.buf);
+
+  /* --- Grab the hash algorithm --- *
+   *
+   * Grab it from the signature algorithm if it's there.  But override that
+   * from the attribute.
+   */
+
+  salg = p;
+  if ((p = strchr(p, '-')) != 0) {
+    *p++ = 0;
+    halg = p;
+  }
+  if ((q = key_getattr(0, k, "hash-alg")) != 0)
+    halg = q;
+
+  /* --- Look up the algorithms in the table --- */
+
+  for (st = sigtab; st->name; st++) {
+    if (strcmp(st->name, salg) == 0)
+      goto s_found;
+  }
+  die(EXIT_FAILURE, "signature algorithm `%s' not found in key `%s'",
+      salg, t.buf);
+s_found:;
+  if (!halg)
+    ch = st->ch;
+  else {
+    if ((ch = ghash_byname(halg)) == 0) {
+      die(EXIT_FAILURE, "hash algorithm `%s' not found in key `%s'",
+         halg, t.buf);
     }
   }
-  return (gg);
+  so = wantpriv ? st->signops : st->verifyops;
+
+  /* --- Load the key --- */
+
+  kd = xmalloc(so->kdsz);
+  kp = key_fetchinit(so->kf, 0, kd);
+  if ((e = key_fetch(kp, k)) != 0)
+    die(EXIT_FAILURE, "error fetching key `%s': %s", t.buf, key_strerror(e));
+  s = so->init(k, kd, ch);
+  if (!s->h)
+    s->h = GH_INIT(ch);
+  s->kp = kp;
+  s->ops = so;
+
+  /* --- Free stuff up --- */
+
+  dstr_destroy(&d);
+  dstr_destroy(&t);
+  return (s);
 }
 
-/* --- @getsig@ --- *
+/* --- @freesig@ --- *
  *
- * Arguments:  @const char *name@ = pointer to name string
+ * Arguments:  @sig *s@ = signature-making thing
  *
- * Returns:    Pointer to appropriate signature type.
+ * Returns:    ---
  *
- * Use:                Chooses a signature algorithm by name.
+ * Use:                Frees up a signature-making thing
  */
 
-static sig *getsig(const char *name)
+static void freesig(sig *s)
 {
-  sig *s, *ss = 0;
-  size_t sz = strlen(name);
-  for (s = sigtab; s->name; s++) {
-    if (strncmp(name, s->name, sz) == 0) {
-      if (s->name[sz] == 0) {
-       ss = s;
-       break;
-      } else if (ss)
-       return (0);
-      else
-       ss = s;
-    }
-  }
-  return (ss);
+  GH_DESTROY(s->h);
+  key_fetchdone(s->kp);
+  s->ops->destroy(s);
 }
 
 /*----- Data formatting ---------------------------------------------------*/
@@ -269,8 +666,6 @@ enum {
   /* --- Block tags --- */
 
   T_IDENT = 0,                         /* An identifying marker */
-  T_SIGALG,                            /* Signature algorithm used */
-  T_HASHALG,                           /* Hash algorithm used */
   T_KEYID,                             /* Key identifier */
   T_BEGIN,                             /* Begin hashing here */
   T_COMMENT = T_BEGIN,                 /* A textual comment */
@@ -290,7 +685,7 @@ enum {
 /* --- Name translation table --- */
 
 static const char *tagtab[] = {
-  "ident:", "sigalg:", "hashalg:", "keyid:",
+  "ident:", "keyid:",
   "comment:", "date:", "expires:", "file:",
   "signature:",
   0
@@ -592,8 +987,6 @@ static int bget(block *b, FILE *fp, unsigned bin)
 
     case T_IDENT:
     case T_COMMENT:
-    case T_SIGALG:
-    case T_HASHALG:
       if (getstring(fp, &b->d, bin))
        return (E_EOF);
       break;
@@ -686,8 +1079,6 @@ static void blob(block *b, dstr *d)
   DPUTC(d, b->tag);
   switch (b->tag) {
     case T_IDENT:
-    case T_SIGALG:
-    case T_HASHALG:
     case T_COMMENT:
       DPUTD(d, &b->d);
       DPUTC(d, 0);
@@ -734,8 +1125,6 @@ static void bwrite(block *b, FILE *fp)
   putc(' ', fp);
   switch (b->tag) {
     case T_IDENT:
-    case T_SIGALG:
-    case T_HASHALG:
     case T_COMMENT:
       putstring(fp, b->d.buf, 0);
       break;
@@ -785,7 +1174,7 @@ static void bemit(block *b, FILE *fp, ghash *h, unsigned bin)
     dstr d = DSTR_INIT;
     blob(b, &d);
     if (h)
-      h->ops->hash(h, d.buf, d.len);
+      GH_HASH(h, d.buf, d.len);
     if (fp && bin)
       fwrite(d.buf, d.len, 1, fp);
   }
@@ -830,7 +1219,7 @@ static void keyreport(const char *file, int line, const char *err, void *p)
 static int fhash(const gchash *c, const char *file, void *b)
 {
   FILE *fp = fopen(file, "rb");
-  ghash *h = c->init();
+  ghash *h = GH_INIT(c);
   char buf[4096];
   size_t sz;
   int rc = 0;
@@ -838,11 +1227,11 @@ static int fhash(const gchash *c, const char *file, void *b)
   if (!fp)
     return (-1);
   while ((sz = fread(buf, 1, sizeof(buf), fp)) > 0)
-    h->ops->hash(h, buf, sz);
+    GH_HASH(h, buf, sz);
   if (ferror(fp))
     rc = -1;
-  h->ops->done(h, b);
-  h->ops->destroy(h);
+  GH_DONE(h, b);
+  GH_DESTROY(h);
   fclose(fp);
   return (rc);
 }
@@ -881,13 +1270,10 @@ static int sign(int argc, char *argv[])
 #define f_bogus 4u
 
   unsigned f = 0;
-  const char *kt = 0;
-  const char *ki = 0;
+  const char *ki = "dsig";
   key_file kf;
   key *k;
-  const sig *s = sigtab;
-  const gchash *gch = &rmd160;
-  ghash *h;
+  sig *s;
   time_t exp = KEXP_EXPIRE;
   unsigned verb = 0;
   const char *ifile = 0;
@@ -904,18 +1290,14 @@ static int sign(int argc, char *argv[])
       { "binary",      0,              0,      'b' },
       { "verbose",     0,              0,      'v' },
       { "quiet",       0,              0,      'q' },
-      { "algorithm",   OPTF_ARGREQ,    0,      'a' },
-      { "hash",                OPTF_ARGREQ,    0,      'h' },
       { "comment",     OPTF_ARGREQ,    0,      'c' },
       { "file",                OPTF_ARGREQ,    0,      'f' },
       { "output",      OPTF_ARGREQ,    0,      'o' },
-      { "keytype",     OPTF_ARGREQ,    0,      't' },
-      { "keyid",       OPTF_ARGREQ,    0,      'i' },
-      { "key",         OPTF_ARGREQ,    0,      'i' },
+      { "key",         OPTF_ARGREQ,    0,      'k' },
       { "expire",      OPTF_ARGREQ,    0,      'e' },
       { 0,             0,              0,      0 }
     };
-    int i = mdwopt(argc, argv, "+0vqb a:h:c: f:o: t:i:k:e:", opts, 0, 0, 0);
+    int i = mdwopt(argc, argv, "+0vqb c: f:o: k:e:", opts, 0, 0, 0);
     if (i < 0)
       break;
     switch (i) {
@@ -932,18 +1314,6 @@ static int sign(int argc, char *argv[])
        if (verb > 0)
          verb--;
        break;
-      case 'a':
-       if ((s = getsig(optarg)) == 0) {
-         die(EXIT_FAILURE, "unknown or ambiguous signature algorithm `%s'",
-             optarg);
-       }
-       break;
-      case 'h':
-       if ((gch = gethash(optarg)) == 0) {
-         die(EXIT_FAILURE, "unknown or ambiguous hash function `%s'",
-             optarg);
-       }
-       break;
       case 'c':
        c = optarg;
        break;
@@ -953,10 +1323,6 @@ static int sign(int argc, char *argv[])
       case 'o':
        ofile = optarg;
        break;
-      case 't':
-       kt = optarg;
-       break;
-      case 'i':
       case 'k':
        ki = optarg;
        break;
@@ -978,20 +1344,14 @@ static int sign(int argc, char *argv[])
 
   if (key_open(&kf, keyring, KOPEN_WRITE, keyreport, 0))
     die(EXIT_FAILURE, "couldn't open keyring `%s'", keyring);
-  if (ki) {
-    if ((k = key_bytag(&kf, ki)) == 0)
-      die(EXIT_FAILURE, "couldn't find key `%s'", ki);
-  } else {
-    if (!kt)
-      kt = s->type;
-    if ((k = key_bytype(&kf, kt)) == 0)
-      die(EXIT_FAILURE, "no appropriate key of type `%s'", kt);
-  }
+  if ((k = key_bytag(&kf, ki)) == 0)
+    die(EXIT_FAILURE, "couldn't find key `%s'", ki);
   key_fulltag(k, &d);
   if (exp == KEXP_FOREVER && k->exp != KEXP_FOREVER) {
     die(EXIT_FAILURE, "key `%s' expires: can't create nonexpiring signature",
        d.buf);
   }
+  s = getsig(k, 1);
 
   /* --- Open files --- */
 
@@ -1009,35 +1369,27 @@ static int sign(int argc, char *argv[])
        ofile, strerror(errno));
   }
 
-  h = gch->init();
-
   /* --- Emit the start of the output --- */
 
   binit(&b); b.tag = T_IDENT;
   dstr_putf(&b.d, "%s, Catacomb version " VERSION, QUIS);
   bemit(&b, ofp, 0, f & f_bin);
   
-  breset(&b); b.tag = T_SIGALG; DPUTS(&b.d, s->name);
-  bemit(&b, ofp, h, f & f_bin);
-
-  breset(&b); b.tag = T_HASHALG; DPUTS(&b.d, gch->name);
-  bemit(&b, ofp, h, f & f_bin);
-
   breset(&b); b.tag = T_KEYID; b.k = k->id;
-  bemit(&b, ofp, h, f & f_bin);
+  bemit(&b, ofp, 0, f & f_bin);
 
   /* --- Start hashing, and emit the datestamps and things --- */
 
   {
     time_t now = time(0);
 
-    breset(&b); b.tag = T_DATE; b.t = now; bemit(&b, ofp, h, f & f_bin);
+    breset(&b); b.tag = T_DATE; b.t = now; bemit(&b, ofp, s->h, f & f_bin);
     if (exp == KEXP_EXPIRE)
       exp = now + 86400 * 28;
-    breset(&b); b.tag = T_EXPIRE; b.t = exp; bemit(&b, ofp, h, f & f_bin);
+    breset(&b); b.tag = T_EXPIRE; b.t = exp; bemit(&b, ofp, s->h, f & f_bin);
     if (c) {
       breset(&b); b.tag = T_COMMENT; DPUTS(&b.d, c);
-      bemit(&b, ofp, h, f & f_bin);
+      bemit(&b, ofp, s->h, f & f_bin);
     }
 
     if (!(f & f_bin))
@@ -1061,17 +1413,17 @@ static int sign(int argc, char *argv[])
     if (getstring(ifp, &b.d, f & f_raw))
       break;
     b.tag = T_FILE;
-    DENSURE(&b.b, h->ops->c->hashsz);
-    if (fhash(gch, b.d.buf, b.b.buf)) {
+    DENSURE(&b.b, GH_CLASS(s->h)->hashsz);
+    if (fhash(GH_CLASS(s->h), b.d.buf, b.b.buf)) {
       moan("Error reading `%s': %s", b.d.buf, strerror(errno));
       f |= f_bogus;
     } else {
-      b.b.len += h->ops->c->hashsz;
+      b.b.len += GH_CLASS(s->h)->hashsz;
       if (verb) {
        fhex(stderr, b.b.buf, b.b.len);
        fprintf(stderr, " %s\n", b.d.buf);
       }
-      bemit(&b, ofp, h, f & f_bin);
+      bemit(&b, ofp, s->h, f & f_bin);
     }
   }
 
@@ -1080,9 +1432,7 @@ static int sign(int argc, char *argv[])
   if (!(f & f_bogus)) {
     breset(&b);
     b.tag = T_SIGNATURE;
-    DENSURE(&b.d, h->ops->c->hashsz);
-    h->ops->done(h, b.d.buf);
-    if ((e = s->sign(k, b.d.buf, h->ops->c->hashsz, &b.b)) != 0) {
+    if ((e = s->ops->doit(s, &b.b)) != 0) {
       moan("error creating signature: %s", key_strerror(e));
       f |= f_bogus;
     }
@@ -1094,6 +1444,7 @@ static int sign(int argc, char *argv[])
 
   /* --- Tidy up at the end --- */
 
+  freesig(s);
   bdestroy(&b);
   if (ifile)
     fclose(ifp);
@@ -1135,10 +1486,8 @@ static int verify(int argc, char *argv[])
   unsigned verb = 1;
   key_file kf;
   key *k = 0;
-  sig *s = sigtab;
-  const gchash *gch = &rmd160;
+  sig *s;
   dstr d = DSTR_INIT;
-  ghash *h = 0;
   FILE *fp;
   block b;
   int e;
@@ -1198,11 +1547,11 @@ static int verify(int argc, char *argv[])
   /* --- Read the introductory matter --- */
 
   binit(&b);
-  for (;;) {    
+  for (;;) {
     breset(&b);
     e = bget(&b, fp, f & f_bin);
     if (e < 0)
-      die(EXIT_FAILURE, "error reading packet: %s\n", errtab[-e]);
+      die(EXIT_FAILURE, "error reading packet: %s", errtab[-e]);
     if (e >= T_BEGIN)
       break;
     switch (e) {
@@ -1210,24 +1559,6 @@ static int verify(int argc, char *argv[])
        if (verb > 2)
          printf("INFO ident: `%s'\n", b.d.buf);
        break;
-      case T_SIGALG:
-       if ((s = getsig(b.d.buf)) == 0) {
-         if (verb)
-           printf("FAIL unknown signature algorithm `%s'\n", b.d.buf);
-         exit(EXIT_FAILURE);
-       }
-       if (verb > 2)
-         printf("INFO signature algorithm: %s\n", s->name);
-       break;
-      case T_HASHALG:
-       if ((gch = gethash(b.d.buf)) == 0) {
-         if (verb)
-           printf("FAIL unknown hash function `%s'\n", b.d.buf);
-         exit(EXIT_FAILURE);
-       }
-       if (verb > 2)
-         printf("INFO hash function algorithm: %s\n", gch->name);
-       break;
       case T_KEYID:
        if ((k = key_byid(&kf, b.k)) == 0) {
          if (verb)
@@ -1248,7 +1579,7 @@ static int verify(int argc, char *argv[])
 
   /* --- Initialize the hash function and start reading hashed packets --- */
 
-  h = gch->init();
+  s = getsig(k, 0);
 
   if (!k) {
     if (verb)
@@ -1256,24 +1587,12 @@ static int verify(int argc, char *argv[])
     exit(EXIT_FAILURE);
   }
 
-  {
-    block bb;
-    binit(&bb);
-    breset(&bb); bb.tag = T_SIGALG; DPUTS(&bb.d, s->name);
-    bemit(&bb, 0, h, 0);
-    breset(&bb); bb.tag = T_HASHALG; DPUTS(&bb.d, gch->name);
-    bemit(&bb, 0, h, 0);
-    breset(&bb); bb.tag = T_KEYID; bb.k = k->id;
-    bemit(&bb, 0, h, 0);
-    bdestroy(&bb);
-  }
-
   for (;;) {
     switch (e) {
       case T_COMMENT:
        if (verb > 1)
          printf("INFO comment: `%s'\n", b.d.buf);
-       bemit(&b, 0, h, 0);
+       bemit(&b, 0, s->h, 0);
        break;
       case T_DATE:
        if (verb > 2) {
@@ -1281,7 +1600,7 @@ static int verify(int argc, char *argv[])
          timestring(b.t, &b.d);
          printf("INFO date: %s\n", b.d.buf);
        }
-       bemit(&b, 0, h, 0);
+       bemit(&b, 0, s->h, 0);
        break;
       case T_EXPIRE: {
        time_t now = time(0);
@@ -1295,18 +1614,18 @@ static int verify(int argc, char *argv[])
          timestring(b.t, &b.d);
          printf("INFO expires: %s\n", b.d.buf);
        }
-       bemit(&b, 0, h, 0);
+       bemit(&b, 0, s->h, 0);
       }        break;
       case T_FILE:
        DRESET(&d);
-       DENSURE(&d, gch->hashsz);
-       if (fhash(gch, b.d.buf, d.buf)) {
+       DENSURE(&d, GH_CLASS(s->h)->hashsz);
+       if (fhash(GH_CLASS(s->h), b.d.buf, d.buf)) {
          if (verb > 1) {
            printf("BAD error reading file `%s': %s\n",
                    b.d.buf, strerror(errno));
          }
          f |= f_bogus;
-       } else if (b.b.len != gch->hashsz ||
+       } else if (b.b.len != GH_CLASS(s->h)->hashsz ||
                   memcmp(d.buf, b.b.buf, b.b.len) != 0) {
          if (verb > 1)
            printf("BAD file `%s' has incorrect hash\n", b.d.buf);
@@ -1316,21 +1635,12 @@ static int verify(int argc, char *argv[])
          fhex(stdout, b.b.buf, b.b.len);
          printf(" %s\n", b.d.buf);
        }
-       bemit(&b, 0, h, 0);
+       bemit(&b, 0, s->h, 0);
        break;
       case T_SIGNATURE:
-       DRESET(&b.d);
-       DENSURE(&b.d, h->ops->c->hashsz);
-       b.d.len += h->ops->c->hashsz;
-       h->ops->done(h, b.d.buf);
-       e = s->verify(k, b.d.buf, b.d.len, b.b.buf, b.b.len);
-       if (e != 1) {
-         if (verb > 1) {
-           if (e < 0) {
-             printf("BAD error unpacking key: %s\n", key_strerror(e));
-           } else
-             puts("BAD bad signature");
-         }
+       if (s->ops->doit(s, &b.b)) {
+         if (verb > 1)
+           puts("BAD bad signature");
          f |= f_bogus;
        } else if (verb > 2)
          puts("INFO good signature");
@@ -1352,6 +1662,7 @@ static int verify(int argc, char *argv[])
 done:
   bdestroy(&b);
   dstr_destroy(&d);
+  freesig(s);
   key_close(&kf);
   if (fp != stdin)
     fclose(fp);
@@ -1382,7 +1693,7 @@ static cmd cmdtab[] = {
 /*     "manifest [-0] [-o output]" }, */
   { "sign", sign,
     "sign [-options]\n\
-       [-0bqv] [-a alg] [-h hash] [-t keytype] [-i keyid]\n\
+       [-0bqv] [-c comment] [-k tag] [-i keyid]\n\
        [-e expire] [-f file] [-o output]", "\
 Options:\n\
 \n\
@@ -1390,13 +1701,10 @@ Options:\n\
 -b, --binary           Produce a binary output file.\n\
 -q, --quiet            Produce fewer messages while working.\n\
 -v, --verbose          Produce more messages while working.\n\
--a, --algorithm=SIGALG Use the SIGALG signature algorithm.\n\
--h, --hash=HASHASL     Use the HASHALG message digest algorithm.\n\
 -c, --comment=COMMENT  Include COMMENT in the output file.\n\
 -f, --file=FILE                Read filenames to hash from FILE.\n\
 -o, --output=FILE      Write the signed result to FILE.\n\
--t, --keytype=TYPE     Use a key with type TYPE to do the signing.\n\
--i, --keyid=ID         Use the key with the given ID to do the signing.\n\
+-k, --key=TAG          Use a key named by TAG.\n\
 -e, --expire=TIME      The signature should expire after TIME.\n\
 " },
   { "verify", verify,