X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/tripe/blobdiff_plain/35c8b547dde529693875087d67fa60bf88319d2b..5290b9d5791238de2e19ec479087bda6e3c787e2:/server/keymgmt.c diff --git a/server/keymgmt.c b/server/keymgmt.c index ef4f6a6b..8f0d1a2a 100644 --- a/server/keymgmt.c +++ b/server/keymgmt.c @@ -42,142 +42,131 @@ typedef struct kgops { int (*loadpub)(key_data *, kdata *, dstr *, dstr *); } kgops; -/* --- Diffie-Hellman --- */ - -static int kgdh_priv(key_data *d, kdata *kd, dstr *t, dstr *e) -{ - key_packstruct kps[DH_PRIVFETCHSZ]; - key_packdef *kp; - dh_priv dp; - int rc; - - kp = key_fetchinit(dh_privfetch, kps, &dp); - if ((rc = key_unpack(kp, d, t)) != 0) { - a_format(e, "unpack-failed", "%s", key_strerror(rc), A_END); - goto fail_0; - } - kd->g = group_prime(&dp.dp); - kd->kpriv = MP_COPY(dp.x); - rc = 0; - goto done; -fail_0: - rc = -1; -done: - key_fetchdone(kp); - return (rc); -} - -static int kgdh_pub(key_data *d, kdata *kd, dstr *t, dstr *e) -{ - key_packstruct kps[DH_PUBFETCHSZ]; - key_packdef *kp; - dh_pub dp; - int rc; - - kp = key_fetchinit(dh_pubfetch, kps, &dp); - if ((rc = key_unpack(kp, d, t)) != 0) { - a_format(e, "unpack-failed", "%s", key_strerror(rc), A_END); - goto fail_0; - } - kd->g = group_prime(&dp.dp); - kd->kpub = G_CREATE(kd->g); - if (G_FROMINT(kd->g, kd->kpub, dp.y)) { - a_format(e, "bad-public-vector", A_END); - goto fail_1; - } - rc = 0; - goto done; -fail_1: - G_DESTROY(kd->g, kd->kpub); - G_DESTROYGROUP(kd->g); -fail_0: - rc = -1; -done: - key_fetchdone(kp); - return (rc); -} - -static const kgops kgdh_ops = { "dh", kgdh_priv, kgdh_pub }; - -/* --- Elliptic curve --- */ - -static int kgec_priv(key_data *d, kdata *kd, dstr *t, dstr *e) -{ - key_packstruct kps[EC_PRIVFETCHSZ]; - key_packdef *kp; - ec_priv ep; - ec_info ei; - const char *err; - int rc; +/* --- @KLOAD@ --- * + * + * Arguments: @ty@, @TY@ = key type name (lower- and upper-case) + * @which@, @WHICH@ = `pub' or `priv' (and upper-case) + * @setgroup@ = code to initialize @kd->g@ + * @setpriv@ = code to initialize @kd->kpriv@ + * @setpub@ = code to initialize @kd->kpub@ + * + * Use: Generates the body of one of the (rather tedious) key loading + * functions. See the description of @KEYTYPES@ below for the + * details. + */ - kp = key_fetchinit(ec_privfetch, kps, &ep); - if ((rc = key_unpack(kp, d, t)) != 0) { - a_format(e, "unpack-failed", "%s", key_strerror(rc), A_END); - goto fail_0; - } - if ((err = ec_getinfo(&ei, ep.cstr)) != 0) { - a_format(e, "decode-failed", "%s", err, A_END); - goto fail_0; - } - kd->g = group_ec(&ei); - kd->kpriv = MP_COPY(ep.x); - rc = 0; - goto done; -fail_0: - rc = -1; -done: - key_fetchdone(kp); - return (rc); +#define KLOAD(ty, TY, which, WHICH, setgroup, setpriv, setpub) \ +static int kg##ty##_##which(key_data *d, kdata *kd, dstr *t, dstr *e) \ +{ \ + key_packstruct kps[TY##_##WHICH##FETCHSZ]; \ + key_packdef *kp; \ + ty##_##which p; \ + int rc; \ + \ + /* --- Initialize things we've not set up yet --- */ \ + \ + kd->g = 0; kd->kpub = 0; \ + \ + /* --- Unpack the key --- */ \ + \ + kp = key_fetchinit(ty##_##which##fetch, kps, &p); \ + if ((rc = key_unpack(kp, d, t)) != 0) { \ + a_format(e, "unpack-failed", "%s", key_strerror(rc), A_END); \ + goto fail; \ + } \ + \ + /* --- Extract the pieces of the key --- */ \ + \ + setgroup; \ + setpriv; \ + kd->kpub = G_CREATE(kd->g); \ + setpub; \ + \ + /* --- We win --- */ \ + \ + rc = 0; \ + goto done; \ + \ +fail: \ + if (kd->kpub) G_DESTROY(kd->g, kd->kpub); \ + if (kd->g) G_DESTROYGROUP(kd->g); \ + rc = -1; \ + \ +done: \ + key_fetchdone(kp); \ + return (rc); \ } -static int kgec_pub(key_data *d, kdata *kd, dstr *t, dstr *e) -{ - key_packstruct kps[EC_PUBFETCHSZ]; - key_packdef *kp; - ec_pub ep; - ec_info ei; - const char *err; - int rc; +/* --- @KEYTYPES@ --- * + * + * A list of the various key types, and how to unpack them. Each entry in + * the list has the form + * + * _(ty, TY, setgroup, setpriv, setpub) + * + * The @ty@ and @TY@ are lower- and upper-case versions of the key type name, + * and there should be @key_fetchdef@s called @ty_{priv,pub}fetch@. + * + * The @setgroup@, @setpriv@ and @setpub@ items are code fragments which are + * passed to @KLOAD@ to build appropriate key-loading methods. By the time + * these code fragments are run, the key has been unpacked from the incoming + * key data using @ty_whichfetch@ into a @ty_which@ structure named @p@. + * They can report errors by writing an appropriate token sequence to @e@ and + * jumping to @fail@. + */ - kp = key_fetchinit(ec_pubfetch, kps, &ep); - if ((rc = key_unpack(kp, d, t)) != 0) { - a_format(e, "unpack-failed", "%s", key_strerror(rc), A_END); - goto fail_0; - } - if ((err = ec_getinfo(&ei, ep.cstr)) != 0) { - a_format(e, "decode-failed", "%s", err, A_END); - goto fail_0; - } - kd->g = group_ec(&ei); - kd->kpub = G_CREATE(kd->g); - if (G_FROMEC(kd->g, kd->kpub, &ep.p)) { - a_format(e, "bad-public-vector", A_END); - goto fail_1; - } - rc = 0; - goto done; -fail_1: - G_DESTROY(kd->g, kd->kpub); - G_DESTROYGROUP(kd->g); -fail_0: - rc = -1; -done: - key_fetchdone(kp); - return (rc); -} +#define KEYTYPES(_) \ + \ + /* --- Diffie-Hellman --- */ \ + \ + _(dh, DH, \ + { kd->g = group_prime(&p.dp); }, \ + { kd->kpriv = MP_COPY(p.x); }, \ + { if (G_FROMINT(kd->g, kd->kpub, p.y)) { \ + a_format(e, "bad-public-vector", A_END); \ + goto fail; \ + } \ + }) \ + \ + /* --- Elliptic curves --- */ \ + \ + _(ec, EC, \ + { ec_info ei; const char *err; \ + if ((err = ec_getinfo(&ei, p.cstr)) != 0) { \ + a_format(e, "decode-failed", "%s", err, A_END); \ + goto fail; \ + } \ + kd->g = group_ec(&ei); \ + }, \ + { kd->kpriv = MP_COPY(p.x); }, \ + { if (G_FROMEC(kd->g, kd->kpub, &p.p)) { \ + a_format(e, "bad-public-vector", A_END); \ + goto fail; \ + } \ + }) -static const kgops kgec_ops = { "ec", kgec_priv, kgec_pub }; +#define KEYTYPE_DEF(ty, TY, setgroup, setpriv, setpub) \ + KLOAD(ty, TY, priv, PRIV, setgroup, setpriv, \ + { G_EXP(kd->g, kd->kpub, kd->g->g, kd->kpriv); }) \ + KLOAD(ty, TY, pub, PUB, setgroup, { }, setpub) \ + static const kgops kg##ty##_ops = { #ty, kg##ty##_priv, kg##ty##_pub }; +KEYTYPES(KEYTYPE_DEF) /* --- Table of supported key types --- */ -static const kgops *kgtab[] = { &kgdh_ops, &kgec_ops, 0 }; +static const kgops *kgtab[] = { +#define KEYTYPE_ENTRY(ty, TY, setgroup, setpriv, setpub) &kg##ty##_ops, + KEYTYPES(KEYTYPE_ENTRY) +#undef KEYTYPE_ENTRY + 0 +}; /*----- Algswitch stuff ---------------------------------------------------*/ /* --- @algs_get@ --- * * * Arguments: @algswitch *a@ = where to put the algorithms - * @dstr *e@ = where to write errror tokens + * @dstr *e@ = where to write error tokens * @key_file *kf@ = key file * @key *k@ = key to inspect * @@ -189,18 +178,11 @@ static const kgops *kgtab[] = { &kgdh_ops, &kgec_ops, 0 }; static int algs_get(algswitch *a, dstr *e, key_file *kf, key *k) { const char *p; + const bulkcrypto *bulk; char *q, *qq; - dstr d = DSTR_INIT; + dstr d = DSTR_INIT, dd = DSTR_INIT; int rc = -1; - /* --- Symmetric encryption for bulk data --- */ - - if ((p = key_getattr(kf, k, "cipher")) == 0) p = "blowfish-cbc"; - if ((a->c = gcipher_byname(p)) == 0) { - a_format(e, "unknown-cipher", "%s", p, A_END); - goto done; - } - /* --- Hash function --- */ if ((p = key_getattr(kf, k, "hash")) == 0) p = "rmd160"; @@ -221,44 +203,93 @@ static int algs_get(algswitch *a, dstr *e, key_file *kf, key *k) goto done; } - /* --- Message authentication for bulk data --- */ + /* --- Bulk crypto transform --- */ + + if ((p = key_getattr(kf, k, "bulk")) == 0) p = "v0"; + for (bulk = bulktab; bulk->name && strcmp(p, bulk->name) != 0; bulk++); + if (!bulk->name) { + a_format(e, "unknown-bulk-transform", "%s", p, A_END); + goto done; + } + a->bulk = bulk; + + /* --- Symmetric encryption for bulk data --- */ - if ((p = key_getattr(kf, k, "mac")) != 0) { + if (!(a->bulk->prim & BCP_CIPHER)) + a->c = 0; + else { + if ((p = key_getattr(kf, k, "cipher")) == 0) p = "blowfish-cbc"; + if ((a->c = gcipher_byname(p)) == 0) { + a_format(e, "unknown-cipher", "%s", p, A_END); + goto done; + } + } + + /* --- Block cipher for miscellaneous use --- */ + + if (!(a->bulk->prim & BCP_BLKC)) + a->b = 0; + else { + if ((p = key_getattr(kf, k, "blkc")) == 0) { + dstr_reset(&dd); + dstr_puts(&dd, a->c ? a->c->name : "rijndael-"); + if ((q = strrchr(dd.buf, '-')) != 0) *q = 0; + p = dd.buf; + } dstr_reset(&d); - dstr_puts(&d, p); - if ((q = strchr(d.buf, '/')) != 0) - *q++ = 0; - if ((a->m = gmac_byname(d.buf)) == 0) { - a_format(e, "unknown-mac", "%s", d.buf, A_END); + dstr_putf(&d, "%s-ecb", p); + if ((a->b = gcipher_byname(d.buf)) == 0) { + a_format(e, "unknown-blkc", "%s", p, A_END); goto done; } - if (!q) - a->tagsz = a->m->hashsz; - else { - unsigned long n = strtoul(q, &qq, 0); - if (*qq) { - a_format(e, "bad-tag-length-string", "%s", q, A_END); + } + + /* --- Message authentication for bulk data --- */ + + if (!(a->bulk->prim & BCP_MAC)) { + a->m = 0; + a->tagsz = 0; + } else { + 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) { + a_format(e, "unknown-mac", "%s", d.buf, A_END); goto done; } - if (n%8 || n/8 > a->m->hashsz) { - a_format(e, "bad-tag-length", "%lu", n, A_END); + if (!q) + a->tagsz = a->m->hashsz; + else { + unsigned long n = strtoul(q, &qq, 0); + if (*qq) { + a_format(e, "bad-tag-length-string", "%s", q, A_END); + goto done; + } + if (n%8 || n/8 > a->m->hashsz) { + a_format(e, "bad-tag-length", "%lu", n, A_END); + goto done; + } + a->tagsz = n/8; + } + } else { + dstr_reset(&d); + dstr_putf(&d, "%s-hmac", a->h->name); + if ((a->m = gmac_byname(d.buf)) == 0) { + a_format(e, "no-hmac-for-hash", "%s", a->h->name, A_END); goto done; } - a->tagsz = n/8; + a->tagsz = a->h->hashsz/2; } - } else { - dstr_reset(&d); - dstr_putf(&d, "%s-hmac", a->h->name); - if ((a->m = gmac_byname(d.buf)) == 0) { - a_format(e, "no-hmac-for-hash", "%s", a->h->name, A_END); - goto done; - } - a->tagsz = a->h->hashsz/2; } + /* --- All done --- */ + rc = 0; done: dstr_destroy(&d); + dstr_destroy(&dd); return (rc); } @@ -278,6 +309,10 @@ done: static int algs_check(algswitch *a, dstr *e, const group *g) { + /* --- Check the bulk crypto transform --- */ + + if (a->bulk->check(a, e)) return (-1); + /* --- Derive the key sizes --- * * * Must ensure that we have non-empty keys. This isn't ideal, but it @@ -286,22 +321,28 @@ static int algs_check(algswitch *a, dstr *e, const group *g) */ a->hashsz = a->h->hashsz; - if ((a->cksz = keysz(a->hashsz, a->c->keysz)) == 0) { + if (a->c && (a->cksz = keysz(a->hashsz, a->c->keysz)) == 0) { a_format(e, "cipher", "%s", a->c->name, "no-key-size", "%lu", (unsigned long)a->hashsz, A_END); return (-1); } - if ((a->mksz = keysz(a->hashsz, a->m->keysz)) == 0) { + if (a->m && (a->mksz = keysz(a->hashsz, a->m->keysz)) == 0) { a_format(e, "mac", "%s", a->m->name, "no-key-size", "%lu", (unsigned long)a->hashsz, A_END); return (-1); } + if (a->b && (a->bksz = keysz(a->hashsz, a->b->keysz)) == 0) { + a_format(e, "blkc", "%.*s", strlen(a->b->name) - 4, a->b->name, + "no-key-size", "%lu", (unsigned long)a->hashsz, + A_END); + return (-1); + } /* --- Derive the data limit --- */ - if (a->c->blksz < 16) a->expsz = MEG(64); + if (a->c && a->c->blksz < 16) a->expsz = MEG(64); else a->expsz = MEG(2048); /* --- Ensure the MGF accepts hashes as keys --- */ @@ -332,7 +373,9 @@ int km_samealgsp(const kdata *kdx, const kdata *kdy) { const algswitch *a = &kdx->algs, *aa = &kdy->algs; - return (group_samep(kdx->g, kdy->g) && a->c == aa->c && + return (group_samep(kdx->g, kdy->g) && + a->bulk == aa->bulk && + a->c == aa->c && a->b == aa->b && a->mgf == aa->mgf && a->h == aa->h && a->m == aa->m && a->tagsz == aa->tagsz); } @@ -379,7 +422,7 @@ static int kh_loadpub(const kgops *ko, key_data *d, kdata *kd, if ((rc = ko->loadpub(d, kd, t, e)) != 0) goto fail_0; if (group_check(kd->g, kd->kpub)) { - a_format(e, "bad-public-group-element"); + a_format(e, "bad-public-group-element", A_END); goto fail_1; } kd->kpriv = 0; @@ -404,12 +447,11 @@ static int kh_loadpriv(const kgops *ko, key_data *d, kdata *kd, a_format(e, "bad-group", "%s", err, A_END); goto fail_1; } - kd->kpub = G_CREATE(kd->g); - G_EXP(kd->g, kd->kpub, kd->g->g, kd->kpriv); return (0); fail_1: mp_drop(kd->kpriv); + G_DESTROY(kd->g, kd->kpub); G_DESTROYGROUP(kd->g); fail_0: return (-1); @@ -462,7 +504,7 @@ static int kh_reopen(keyhalf *kh) if (key_open(kf, kh->kr, KOPEN_READ, keymoan, kh)) { a_warn("KEYMGMT", "%s-keyring", kh->kind, "%s", kh->kr, - "read-error", "?ERRNO", A_END); + "io-error", "?ERRNO", A_END); DESTROY(kf); return (-1); } else { @@ -841,6 +883,7 @@ void km_unref(kdata *kd) G_DESTROY(kd->g, kd->kpub); xfree(kd->tag); G_DESTROYGROUP(kd->g); + DESTROY(kd); } /*----- That's all, folks -------------------------------------------------*/