X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/tripe/blobdiff_plain/b4e566689f469cc1cffe99e50e16a346d0f9832d..b5c45da15703d85b506a1ea1eb38c318c3418ed3:/keymgmt.c diff --git a/keymgmt.c b/keymgmt.c index 2935f1c9..e05706ac 100644 --- a/keymgmt.c +++ b/keymgmt.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: keymgmt.c,v 1.5 2004/04/08 01:36:17 mdw Exp $ + * $Id: keymgmt.c,v 1.6 2004/04/18 18:08:11 mdw Exp $ * * Key loading and storing * @@ -34,6 +34,7 @@ group *gg; mp *kpriv; +algswitch algs; /*----- Static variables --------------------------------------------------*/ @@ -159,6 +160,132 @@ static const kgops kgec_ops = { "tripe-ec", kgec_priv, kgec_pub }; static const kgops *kgtab[] = { &kgdh_ops, &kgec_ops, 0 }; +/*----- Algswitch stuff ---------------------------------------------------*/ + +/* --- @algs_get@ --- * + * + * Arguments: @algswitch *a@ = where to put the algorithms + * @key_file *kf@ = key file (for some stupid reason) + * @key *k@ = key to inspect + * + * Returns: Null if OK, or an error message. + * + * Use: Extracts an algorithm choice from a key. + */ + +static const char *algs_get(algswitch *a, key_file *kf, key *k) +{ + const char *p; + char *q; + dstr d = DSTR_INIT; + const char *e; + +#define FAIL(msg) do { e = msg; goto done; } while (0) + + if ((p = key_getattr(kf, k, "cipher")) == 0) + p = "blowfish-cbc"; + if ((a->c = gcipher_byname(p)) == 0) + FAIL("unknown cipher"); + + if ((p = key_getattr(kf, k, "hash")) == 0) + p = "rmd160"; + if ((a->h = ghash_byname(p)) == 0) + FAIL("unknown hash function"); + + if ((p = key_getattr(kf, k, "mgf")) != 0) { + dstr_reset(&d); + dstr_putf(&d, "%s-mgf"); + p = d.buf; + } + if ((a->mgf = gcipher_byname(p)) == 0) + FAIL("unknown MGF cipher"); + + if ((p = key_getattr(kf, k, "mac")) != 0) { + dstr_reset(&d); + dstr_puts(&d, p); + if ((q = strchr(d.buf, '/')) != 0) + *q++ = 0; + if ((a->m = gmac_byname(d.buf)) == 0) + FAIL("unknown message authentication code"); + if (!q) + a->tagsz = a->m->hashsz; + else { + unsigned long n = strtoul(q, &q, 0); + if (*q) FAIL("bad tag length string"); + if (n%8 || n > ~(size_t)0) FAIL("bad tag length"); + a->tagsz = n/8; + } + } else { + dstr_reset(&d); + dstr_putf(&d, "%s-hmac", a->h->name); + if ((a->m = gmac_byname(d.buf)) == 0) + FAIL("failed to derive HMAC from hash function"); + a->tagsz = a->h->hashsz/2; + } + + e = 0; +done: + dstr_destroy(&d); + return (e); +} + +/* --- @algs_check@ --- * + * + * Arguments: @algswitch *a@ = a choice of algorithms + * @const group *g@ = the group we're working in + * + * Returns: Null if OK, or an error message. + * + * Use: Checks an algorithm choice for sensibleness. This also + * derives some useful information from the choices, and you + * must call this before committing the algorithm selection + * for use by @keyset@ functions. + */ + +static const char *algs_check(algswitch *a, const group *g) +{ + /* --- Derive the key sizes --- * + * + * Must ensure that we have non-empty keys. This isn't ideal, but it + * provides a handy sanity check. + */ + + a->hashsz = a->h->hashsz; + if ((a->cksz = keysz(a->hashsz, a->c->keysz)) == 0) + return ("no key size found for cipher"); + if ((a->mksz = keysz(a->hashsz, a->m->keysz)) == 0) + return ("no key size found for MAC"); + + /* --- Ensure that the tag size is sane --- */ + + if (a->tagsz > a->m->hashsz) return ("tag length too large"); + + /* --- Ensure the MGF accepts hashes as keys --- */ + + if (keysz(a->hashsz, a->mgf->keysz) != a->hashsz) + return ("MGF not suitable -- restrictive key schedule"); + + /* --- All ship-shape and Bristol-fashion --- */ + + return (0); +} + +/* --- @algs_samep@ --- * + * + * Arguments: @const algswitch *a, *aa@ = two algorithm selections + * + * Returns: Nonzero if the two selections are the same. + * + * Use: Checks sameness of algorithm selections: used to ensure that + * peers are using sensible algorithms. + */ + +static int algs_samep(const algswitch *a, const algswitch *aa) +{ + return (a->c == aa->c && a->mgf == aa->mgf && a->h == aa->h && + a->m == aa->m && a->tagsz == aa->tagsz); +} + /*----- Main code ---------------------------------------------------------*/ /* --- @keymoan@ --- * @@ -196,6 +323,7 @@ static int loadpriv(dstr *d) int rc = -1; const kgops **ko; const char *e; + algswitch a; /* --- Open the private key file --- */ @@ -237,6 +365,15 @@ tymatch:; goto done_1; } + /* --- Collect the algorithms --- */ + + if ((e = algs_get(&a, &kf, k)) != 0 || + (e = algs_check(&a, g)) != 0) { + dstr_putf(d, "bad symmetric algorithm selection in private key `%s': %s", + t.buf, e); + goto done_1; + } + /* --- Good, we're happy --- * * * Dodginess! We change the group over here, but don't free any old group @@ -263,12 +400,18 @@ tymatch:; trace(T_CRYPTO, "crypto: r = %s", mpstr(g->r)); trace(T_CRYPTO, "crypto: h = %s", mpstr(g->h)); trace(T_CRYPTO, "crypto: x = %s", mpstr(x)); + trace(T_CRYPTO, "crypto: cipher = %s", a.c->name); + trace(T_CRYPTO, "crypto: mgf = %s", a.mgf->name); + trace(T_CRYPTO, "crypto: hash = %s", a.h->name); + trace(T_CRYPTO, "crypto: mac = %s/%lu", + a.m->name, (unsigned long)a.tagsz * 8); }) }) /* --- Success! --- */ gg = g; g = 0; + algs = a; kpriv = x; x = 0; rc = 0; @@ -367,6 +510,7 @@ int km_interval(void) void km_init(const char *priv, const char *pub, const char *tag) { dstr d = DSTR_INIT; + const gchash *const *hh; kr_priv = priv; kr_pub = pub; @@ -374,6 +518,13 @@ void km_init(const char *priv, const char *pub, const char *tag) fwatch_init(&w_priv, kr_priv); fwatch_init(&w_pub, kr_pub); + for (hh = ghashtab; *hh; hh++) { + if ((*hh)->hashsz > MAXHASHSZ) { + die(EXIT_FAILURE, "INTERNAL ERROR: %s hash length %lu > MAXHASHSZ %d", + (*hh)->name, (unsigned long)(*hh)->hashsz, MAXHASHSZ); + } + } + DRESET(&d); if (loadpriv(&d)) die(EXIT_FAILURE, "%s", d.buf); @@ -401,12 +552,13 @@ int km_getpubkey(const char *tag, ge *kpub, time_t *t_exp) const char *e; group *g = 0; ge *p = 0; + algswitch a; int rc = -1; /* --- Find the key --- */ if (key_qtag(kf_pub, tag, &t, &k, &kd)) { - a_warn("private key `%s' not found in keyring `%s'", tag_priv, kr_priv); + a_warn("public key `%s' not found in keyring `%s'", tag, kr_pub); goto done; } @@ -445,6 +597,17 @@ tymatch:; goto done; } + /* --- Check the algorithms --- */ + + if ((e = algs_get(&a, kf_pub, k)) != 0) { + a_warn("public key `%s' has bad algorithm selection: %s", t.buf, e); + goto done; + } + if (!algs_samep(&a, &algs)) { + a_warn("public key `%s' specifies different algorithms", t.buf); + goto done; + } + /* --- Dump the public key --- */ IF_TRACING(T_KEYMGMT, {