X-Git-Url: https://git.distorted.org.uk/u/mdw/catacomb/blobdiff_plain/d470270aa476e8100eb78b1958ce5dd557983195..b817bfc642225b8c3c0b6a7e42d1fb949b61a606:/dsig.c diff --git a/dsig.c b/dsig.c index e591538..6e624c6 100644 --- a/dsig.c +++ b/dsig.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: dsig.c,v 1.4 2000/08/04 23:23:44 mdw Exp $ + * $Id: dsig.c,v 1.10 2004/04/08 01:36:15 mdw Exp $ * * Verify signatures on distribuitions of files * @@ -27,23 +27,6 @@ * MA 02111-1307, USA. */ -/*----- Revision history --------------------------------------------------* - * - * $Log: dsig.c,v $ - * Revision 1.4 2000/08/04 23:23:44 mdw - * Various fixes. - * - * Revision 1.3 2000/07/15 20:53:23 mdw - * More hash functions. Bug fix in getstring. - * - * Revision 1.2 2000/07/01 11:27:22 mdw - * Use new PKCS#1 padding functions rather than rolling by hand. - * - * Revision 1.1 2000/06/17 10:54:29 mdw - * Program to generate and verify signatures on multiple files. - * - */ - /*----- Header files ------------------------------------------------------*/ #include "config.h" @@ -68,188 +51,570 @@ #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" - -#include "md4.h" -#include "md5.h" -#include "rmd128.h" -#include "rmd160.h" -#include "rmd256.h" -#include "rmd320.h" + #include "sha.h" -#include "tiger.h" +#include "has160.h" -/*----- Digital signature algorithm ---------------------------------------*/ +/*----- Algorithm choice --------------------------------------------------*/ -static int dsasign(key *k, const void *m, size_t msz, dstr *d) -{ - dsa_priv dp; - key_packstruct ks[DSA_PRIVFETCHSZ]; +/* --- 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 = 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; - 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, s, sz, s + 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 const gchash *hashtab[] = { - &rmd160, &tiger, &sha, &rmd128, &rmd256, &rmd320, &md5, &md4, 0 }; -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 }; -/* --- @gethash@ --- * +/* --- 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 +}; + +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 **g, *gg = 0; - size_t sz = strlen(name); - for (g = hashtab; *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 ---------------------------------------------------*/ @@ -265,8 +630,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 */ @@ -286,7 +649,7 @@ enum { /* --- Name translation table --- */ static const char *tagtab[] = { - "ident:", "sigalg:", "hashalg:", "keyid:", + "ident:", "keyid:", "comment:", "date:", "expires:", "file:", "signature:", 0 @@ -588,8 +951,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; @@ -682,8 +1043,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); @@ -730,8 +1089,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; @@ -781,7 +1138,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); } @@ -816,17 +1173,17 @@ static void keyreport(const char *file, int line, const char *err, void *p) * * Arguments: @const gchash *c@ = pointer to hash class * @const char *file@ = file to hash - * @octet *b@ = pointer to output buffer + * @void *b@ = pointer to output buffer * * Returns: Zero if it worked, or nonzero for a system error. * * Use: Hashes a file. */ -static int fhash(const gchash *c, const char *file, octet *b) +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; @@ -834,11 +1191,11 @@ static int fhash(const gchash *c, const char *file, octet *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); } @@ -872,20 +1229,15 @@ static void fhex(FILE *fp, const void *p, size_t sz) static int sign(int argc, char *argv[]) { - enum { - f_raw = 1, - f_bin = 2, - f_bogus = 4 - }; +#define f_raw 1u +#define f_bin 2u +#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; @@ -902,18 +1254,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) { @@ -930,18 +1278,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; @@ -951,10 +1287,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; @@ -976,20 +1308,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 --- */ @@ -1013,12 +1339,6 @@ static int sign(int argc, char *argv[]) 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, 0, f & f_bin); - - breset(&b); b.tag = T_HASHALG; DPUTS(&b.d, gch->name); - bemit(&b, ofp, 0, f & f_bin); - breset(&b); b.tag = T_KEYID; b.k = k->id; bemit(&b, ofp, 0, f & f_bin); @@ -1027,14 +1347,13 @@ static int sign(int argc, char *argv[]) { time_t now = time(0); - h = gch->init(); - 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)) @@ -1058,17 +1377,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); } } @@ -1077,9 +1396,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; } @@ -1091,6 +1408,7 @@ static int sign(int argc, char *argv[]) /* --- Tidy up at the end --- */ + freesig(s); bdestroy(&b); if (ifile) fclose(ifp); @@ -1114,26 +1432,26 @@ static int sign(int argc, char *argv[]) if (f & f_bogus) die(EXIT_FAILURE, "error(s) occurred while creating signature"); return (EXIT_SUCCESS); + +#undef f_raw +#undef f_bin +#undef f_bogus } /*----- Signature verification --------------------------------------------*/ static int verify(int argc, char *argv[]) { - enum { - f_bogus = 1, - f_bin = 2, - f_ok = 4 - }; +#define f_bogus 1u +#define f_bin 2u +#define f_ok 4u unsigned f = 0; unsigned verb = 1; key_file kf; key *k = 0; - sig *s = sigtab; - const gchash *gch = &rmd160; + sig *s; dstr d = DSTR_INIT; - ghash *h; FILE *fp; block b; int e; @@ -1193,11 +1511,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) { @@ -1205,24 +1523,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) @@ -1243,18 +1543,20 @@ 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) puts("FAIL no keyid packet found"); exit(EXIT_FAILURE); } + 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) { @@ -1262,7 +1564,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); @@ -1276,18 +1578,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); @@ -1297,21 +1599,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"); @@ -1333,6 +1626,7 @@ static int verify(int argc, char *argv[]) done: bdestroy(&b); dstr_destroy(&d); + freesig(s); key_close(&kf); if (fp != stdin) fclose(fp); @@ -1343,6 +1637,10 @@ done: puts("OK signature verified"); } return (f & f_bogus ? EXIT_FAILURE : EXIT_SUCCESS); + +#undef f_bogus +#undef f_bin +#undef f_ok } /*----- Main code ---------------------------------------------------------*/ @@ -1350,6 +1648,7 @@ done: typedef struct cmd { const char *name; int (*func)(int /*argc*/, char */*argv*/[]); + const char *usage; const char *help; } cmd; @@ -1358,13 +1657,61 @@ static cmd cmdtab[] = { /* "manifest [-0] [-o output]" }, */ { "sign", sign, "sign [-options]\n\ - [-0v] [-a alg] [-h hash] [-t keytype] [-i keyid]\n\ - [-e expire] [-f file] [-o output]" }, + [-0bqv] [-c comment] [-k tag] [-i keyid]\n\ + [-e expire] [-f file] [-o output]", "\ +Options:\n\ +\n\ +-0, --null Read null-terminated filenames from stdin.\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\ +-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\ +-k, --key=TAG Use a key named by TAG.\n\ +-e, --expire=TIME The signature should expire after TIME.\n\ +" }, { "verify", verify, - "verify [-qv] [file]" }, + "verify [-qv] [file]", "\ +Options:\n\ +\n\ +-q, --quiet Produce fewer messages while working.\n\ +-v, --verbose Produce more messages while working.\n\ +" }, { 0, 0, 0 } }; +/* --- @findcmd@ --- * + * + * Arguments: @const char *name@ = a command name + * + * Returns: Pointer to the command structure. + * + * Use: Looks up a command by name. If the command isn't found, an + * error is reported and the program is terminated. + */ + +static cmd *findcmd(const char *name) +{ + cmd *c, *chosen = 0; + size_t sz = strlen(name); + + for (c = cmdtab; c->name; c++) { + if (strncmp(name, c->name, sz) == 0) { + if (c->name[sz] == 0) { + chosen = c; + break; + } else if (chosen) + die(EXIT_FAILURE, "ambiguous command name `%s'", name); + else + chosen = c; + } + } + if (!chosen) + die(EXIT_FAILURE, "unknown command name `%s'", name); + return (chosen); +} + static void version(FILE *fp) { pquis(fp, "$, Catacomb version " VERSION "\n"); @@ -1375,17 +1722,27 @@ static void usage(FILE *fp) pquis(fp, "Usage: $ [-k keyring] command [args]\n"); } -static void help(FILE *fp) +static void help(FILE *fp, char **argv) { cmd *c; - version(fp); - fputc('\n', fp); - usage(fp); - fputs("\n\ + + if (*argv) { + c = findcmd(*argv); + fprintf(fp, "Usage: %s [-k keyring] %s\n", QUIS, c->usage); + if (c->help) { + fputc('\n', fp); + fputs(c->help, fp); + } + } else { + version(fp); + fputc('\n', fp); + usage(fp); + fputs("\n\ Create and verify signatures on lists of files.\n\ \n", fp); - for (c = cmdtab; c->name; c++) - fprintf(fp, "%s\n", c->help); + for (c = cmdtab; c->name; c++) + fprintf(fp, "%s\n", c->usage); + } } /* --- @main@ --- * @@ -1402,12 +1759,8 @@ Create and verify signatures on lists of files.\n\ int main(int argc, char *argv[]) { unsigned f = 0; - cmd *c = 0, *cc = 0; - size_t n; - enum { - f_bogus = 1 - }; +#define f_bogus 1u /* --- Initialize the library --- */ @@ -1431,7 +1784,7 @@ int main(int argc, char *argv[]) break; switch (i) { case 'h': - help(stdout); + help(stdout, argv + optind); exit(0); break; case 'v': @@ -1460,21 +1813,9 @@ int main(int argc, char *argv[]) /* --- Dispatch to the correct subcommand handler --- */ - n = strlen(argv[0]); - for (c = cmdtab; c->name; c++) { - if (strncmp(argv[0], c->name, n) == 0) { - if (c->name[n] == 0) { - cc = c; - break; - } else if (cc) - die(EXIT_FAILURE, "ambiguous command name `%s'", argv[0]); - else - cc = c; - } - } - if (!cc) - die(EXIT_FAILURE, "unknown command `%s'", argv[0]); - return (cc->func(argc, argv)); + return (findcmd(argv[0])->func(argc, argv)); + +#undef f_bogus } /*----- That's all, folks -------------------------------------------------*/