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 ---------------------------------------------------*/
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;
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";
goto done;
}
- /* --- Message authentication for bulk data --- */
+ /* --- Bulk crypto transform --- */
- 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);
+ 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 (!(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;
}
- 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;
- }
- } 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;
}
- a->tagsz = a->h->hashsz/2;
}
+ /* --- All done --- */
+
rc = 0;
done:
dstr_destroy(&d);
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
*/
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);
/* --- 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 --- */
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;
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);
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 {