/* -*-c-*-
*
- * $Id: dsig.c,v 1.5 2000/10/08 12:12:09 mdw Exp $
+ * $Id: dsig.c,v 1.11 2004/04/08 16:27:49 mdw Exp $
*
* Verify signatures on distribuitions of files
*
* MA 02111-1307, USA.
*/
-/*----- Revision history --------------------------------------------------*
- *
- * $Log: dsig.c,v $
- * Revision 1.5 2000/10/08 12:12:09 mdw
- * Shut up some warnings.
- *
- * Revision 1.4 2000/08/04 23:23:44 mdw
- * Various <ctype.h> 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"
#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"
+
+/*----- Algorithm choice --------------------------------------------------*/
-/*----- Digital signature algorithm ---------------------------------------*/
+/* --- Relevant type operations --- */
-static int dsasign(key *k, const void *m, size_t msz, dstr *d)
-{
- dsa_priv dp;
- key_packstruct ks[DSA_PRIVFETCHSZ];
+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 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
+};
+
+/* --- DSA and ECDSA --- */
+
+typedef struct dsa_sigctx {
+ sig s;
+ gdsa g;
+} dsa_sigctx;
+
+static void dsa_initcommon(dsa_sigctx *ds, const gchash *hc,
+ const char *ktag)
+{
+ ds->g.r = &rand_global;
+ ds->g.h = hc;
+ ds->g.u = MP_NEW;
+ ds->s.h = 0;
+}
+
+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.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);
+ dsa_initcommon(ds, hc, t.buf);
+ 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.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);
+ dsa_initcommon(ds, hc, t.buf);
+ 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 }
};
-/* --- @gethash@ --- *
+/* --- @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 ---------------------------------------------------*/
/* --- 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 */
/* --- Name translation table --- */
static const char *tagtab[] = {
- "ident:", "sigalg:", "hashalg:", "keyid:",
+ "ident:", "keyid:",
"comment:", "date:", "expires:", "file:",
"signature:",
0
case T_IDENT:
case T_COMMENT:
- case T_SIGALG:
- case T_HASHALG:
if (getstring(fp, &b->d, bin))
return (E_EOF);
break;
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);
putc(' ', fp);
switch (b->tag) {
case T_IDENT:
- case T_SIGALG:
- case T_HASHALG:
case T_COMMENT:
putstring(fp, b->d.buf, 0);
break;
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);
}
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;
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);
}
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;
{ "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) {
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;
case 'o':
ofile = optarg;
break;
- case 't':
- kt = optarg;
- break;
- case 'i':
case 'k':
ki = optarg;
break;
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 --- */
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);
{
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))
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);
}
}
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;
}
/* --- Tidy up at the end --- */
+ freesig(s);
bdestroy(&b);
if (ifile)
fclose(ifp);
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 = 0;
FILE *fp;
block b;
int e;
/* --- 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) {
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)
/* --- 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) {
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);
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);
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");
done:
bdestroy(&b);
dstr_destroy(&d);
+ freesig(s);
key_close(&kf);
if (fp != stdin)
fclose(fp);
puts("OK signature verified");
}
return (f & f_bogus ? EXIT_FAILURE : EXIT_SUCCESS);
+
+#undef f_bogus
+#undef f_bin
+#undef f_ok
}
/*----- Main code ---------------------------------------------------------*/
typedef struct cmd {
const char *name;
int (*func)(int /*argc*/, char */*argv*/[]);
+ const char *usage;
const char *help;
} cmd;
/* "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");
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@ --- *
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 --- */
break;
switch (i) {
case 'h':
- help(stdout);
+ help(stdout, argv + optind);
exit(0);
break;
case 'v':
/* --- 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 -------------------------------------------------*/