tripe_SOURCES += addrmap.c
tripe_SOURCES += keymgmt.c
tripe_SOURCES += bulkcrypto.c
+tripe_SOURCES += dh.c
tripe_SOURCES += keyset.c
tripe_SOURCES += keyexch.c
tripe_SOURCES += chal.c
{
peer *p;
const kdata *kd;
- const group *g;
+ const dhgrp *g;
const algswitch *algs;
if (!ac)
if ((p = a_findpeer(a, av[0])) == 0) return;
kd = p->kx.kpriv;
}
- g = kd->g;
+ g = kd->grp;
algs = &kd->algs;
- a_info(a,
- "kx-group=%s", g->ops->name,
- "kx-group-order-bits=%lu", (unsigned long)mp_bits(g->r),
- "kx-group-elt-bits=%lu", (unsigned long)g->nbits,
- A_END);
+ g->ops->grpinfo(g, a);
a_info(a,
"hash=%s", algs->h->name,
"mgf=%s", algs->mgf->name,
--- /dev/null
+/* -*-c-*-
+ *
+ * Diffie--Hellman groups
+ *
+ * (c) 2017 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of Trivial IP Encryption (TrIPE).
+ *
+ * TrIPE is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * TrIPE is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TrIPE; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "tripe.h"
+
+/*----- Common utilities --------------------------------------------------*/
+
+/* --- @KLOAD@ --- *
+ *
+ * Arguments: @pre@ = prefix for defined functions
+ * @ty@, @TY@ = key type name (lower- 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.
+ */
+
+#define KLOAD_HALF(pre, ty, TY, which, WHICH, setgroup, setpriv, setpub) \
+static int pre##_ld##which(key_file *kf, key *k, 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->grp = 0; kd->k = 0; kd->K = 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; \
+ setpub; \
+ \
+ /* --- We win --- */ \
+ \
+ rc = 0; \
+ goto done; \
+ \
+fail: \
+ if (kd->grp) { \
+ if (kd->K) pre##_freege(kd->grp, kd->K); \
+ if (kd->k) pre##_freesc(kd->grp, kd->k); \
+ pre##_freegrp(kd->grp); \
+ } \
+ rc = -1; \
+ \
+done: \
+ key_fetchdone(kp); \
+ return (rc); \
+}
+
+#define KLOAD(pre, ty, TY, setgroup, setpriv, setpub) \
+static void pre##_freegrp(dhgrp *); \
+static void pre##_freesc(const dhgrp *, dhsc *); \
+static void pre##_freege(const dhgrp *, dhge *); \
+ KLOAD_HALF(pre, ty, TY, priv, PRIV, setgroup, setpriv, setpub) \
+ KLOAD_HALF(pre, ty, TY, pub, PUB, setgroup, { kd->k = 0; }, setpub)
+
+#ifndef NTRACE
+static void setupstr(mptext_stringctx *sc)
+ { sc->buf = (char *)buf_u; sc->lim = sc->buf + sizeof(buf_u); }
+
+static const char *donestr(mptext_stringctx *sc)
+ { *sc->buf = 0; return ((const char *)buf_u); }
+
+static void addlitstr(const char *p, mptext_stringctx *sc)
+ { mptext_stringops.put(p, strlen(p), sc); }
+
+static void addmpstr(mp *x, int radix, mptext_stringctx *sc)
+{
+ char b[12];
+
+ if (radix == 16) addlitstr("0x", sc);
+ else if (radix == 8) addlitstr("0", sc);
+ else if (radix != 10) { sprintf(b, "%d#", radix); addlitstr(b, sc); }
+ mp_write(x, radix, &mptext_stringops, sc);
+}
+
+static const char *mpstr(mp *x, int radix)
+{
+ mptext_stringctx sc;
+
+ setupstr(&sc);
+ addmpstr(x, radix, &sc);
+ return (donestr(&sc));
+}
+#endif
+
+/*----- Schnorr groups ----------------------------------------------------*/
+
+typedef struct intdh_grp {
+ dhgrp _g;
+ mpmont mm;
+ mp *q, *G;
+ size_t gesz;
+} intdh_grp;
+
+typedef struct intdh_sc { mp *x; } intdh_sc;
+typedef struct intdh_ge { mp *X; } intdh_ge;
+
+static dhgrp *intdh_mkgroup(const dh_param *dp)
+{
+ intdh_grp *g = CREATE(intdh_grp);
+ g->_g.scsz = mp_octets(dp->q);
+ g->gesz = mp_octets(dp->p);
+ mpmont_create(&g->mm, dp->p);
+ g->q = MP_COPY(dp->q);
+ g->G = mpmont_mul(&g->mm, MP_NEW, dp->g, g->mm.r2);
+ return (&g->_g);
+}
+
+static dhsc *intdh_mptosc(const dhgrp *gg, mp *z)
+{
+ const intdh_grp *g = (const intdh_grp *)gg;
+ intdh_sc *x = CREATE(intdh_sc);
+ x->x = MP_NEW; mp_div(0, &x->x, z, g->q);
+ return ((dhsc *)x);
+}
+
+static dhge *intdh_mptoge(const dhgrp *gg, mp *z)
+{
+ const intdh_grp *g = (const intdh_grp *)gg;
+ intdh_ge *Y = CREATE(intdh_ge);
+ mp *t = MP_NEW; mp_div(0, &t, z, g->mm.m);
+ Y->X = mpmont_mul(&g->mm, t, t, g->mm.r2);
+ return ((dhge *)Y);
+}
+
+KLOAD(intdh, dh, DH,
+ { kd->grp = intdh_mkgroup(&p.dp); },
+ { kd->k = intdh_mptosc(kd->grp, p.x); },
+ { kd->K = intdh_mptoge(kd->grp, p.y); })
+
+static const char *intdh_checkgrp(const dhgrp *gg)
+{
+ const intdh_grp *g = (const intdh_grp *)gg;
+ mp *t = MP_NEW;
+
+ if (!pgen_primep(g->mm.m, &rand_global)) return ("p is not prime");
+ if (!pgen_primep(g->q, &rand_global)) return ("q is not prime");
+ mp_div(0, &t, g->mm.m, g->q);
+ if (!MP_EQ(t, MP_ONE)) return ("q is not a subgroup order");
+ t = mpmont_expr(&g->mm, t, g->G, g->q);
+ if (!MP_EQ(t, g->mm.r)) return ("g not in the subgroup");
+ return (0);
+}
+
+static void intdh_grpinfo(const dhgrp *gg, admin *adm)
+{
+ const intdh_grp *g = (const intdh_grp *)gg;
+ a_info(adm,
+ "kx-group=prime",
+ "kx-group-order-bits=%lu", (unsigned long)mp_bits(g->q),
+ "kx-group-elt-bits=%lu", (unsigned long)mp_bits(g->mm.m),
+ A_END);
+}
+
+#ifndef NTRACE
+static void intdh_tracegrp(const dhgrp *gg)
+{
+ const intdh_grp *g = (const intdh_grp *)gg;
+ mp *t = MP_NEW;
+ trace(T_CRYPTO, "crypto: group type `dh'");
+ trace(T_CRYPTO, "crypto: p = %s", mpstr(g->mm.m, 10));
+ trace(T_CRYPTO, "crypto: q = %s", mpstr(g->q, 10));
+ t = mpmont_reduce(&g->mm, t, g->G);
+ trace(T_CRYPTO, "crypto: g = %s", mpstr(t, 10));
+ MP_DROP(t);
+}
+#endif
+
+static int intdh_samegrpp(const dhgrp *gg, const dhgrp *hh)
+{
+ const intdh_grp *g = (const intdh_grp *)gg, *h = (const intdh_grp *)hh;
+ return (MP_EQ(g->mm.m, h->mm.m) && MP_EQ(g->q, h->q) && MP_EQ(g->G, h->G));
+}
+
+static void intdh_freegrp(dhgrp *gg)
+{
+ intdh_grp *g = (intdh_grp *)gg;
+ mpmont_destroy(&g->mm); MP_DROP(g->q); MP_DROP(g->G);
+ DESTROY(g);
+}
+
+static dhsc *intdh_ldsc(const dhgrp *gg, const void *p, size_t sz)
+{ const intdh_grp *g = (const intdh_grp *)gg;
+ intdh_sc *x = CREATE(intdh_sc);
+ mp *t = mp_loadb(MP_NEW, p, sz);
+ mp_div(0, &t, t, g->q); x->x = t;
+ return ((dhsc *)x);
+}
+
+static int intdh_stsc(const dhgrp *gg, void *p, size_t sz, const dhsc *xx)
+{
+ const intdh_sc *x = (const intdh_sc *)xx;
+ mp_storeb(x->x, p, sz);
+ return (0);
+}
+
+static dhsc *intdh_randsc(const dhgrp *gg)
+{
+ const intdh_grp *g = (const intdh_grp *)gg;
+ intdh_sc *x = CREATE(intdh_sc);
+ x->x = mprand_range(MP_NEW, g->q, &rand_global, 0);
+ return ((dhsc *)x);
+}
+
+#ifndef NTRACE
+static const char *intdh_scstr(const dhgrp *gg, const dhsc *xx)
+ { const intdh_sc *x = (const intdh_sc *)xx; return (mpstr(x->x, 10)); }
+#endif
+
+static void intdh_freesc(const dhgrp *gg, dhsc *xx)
+{
+ intdh_sc *x = (intdh_sc *)xx;
+ MP_DROP(x->x); DESTROY(x);
+}
+
+static dhge *intdh_ldge(const dhgrp *gg, buf *b, int fmt)
+{
+ const intdh_grp *g = (const intdh_grp *)gg;
+ intdh_ge *Y;
+ mp *t;
+ const octet *p;
+
+ switch (fmt) {
+ case DHFMT_VAR: case DHFMT_HASH:
+ if ((t = buf_getmp(b)) == 0) return (0);
+ break;
+ case DHFMT_STD:
+ if ((p = buf_get(b, g->gesz)) == 0) return (0);
+ t = mp_loadb(MP_NEW, p, g->gesz);
+ break;
+ default:
+ abort();
+ }
+ Y = CREATE(intdh_ge);
+ mp_div(0, &t, t, g->mm.m);
+ Y->X = mpmont_mul(&g->mm, t, t, g->mm.r2);
+ return ((dhge *)Y);
+}
+
+static int intdh_stge(const dhgrp *gg, buf *b, const dhge *YY, int fmt)
+{
+ const intdh_grp *g = (const intdh_grp *)gg;
+ const intdh_ge *Y = (const intdh_ge *)YY;
+ octet *p;
+ mp *t;
+ int rc;
+
+ t = mpmont_reduce(&g->mm, MP_NEW, Y->X);
+ switch (fmt) {
+ case DHFMT_VAR: case DHFMT_HASH:
+ rc = buf_putmp(b, t);
+ break;
+ case DHFMT_STD:
+ if ((p = buf_get(b, g->gesz)) == 0)
+ rc = -1;
+ else {
+ mp_storeb(t, p, g->gesz);
+ rc = 0;
+ }
+ break;
+ default:
+ abort();
+ }
+ MP_DROP(t);
+ return (rc);
+}
+
+static int intdh_checkge(const dhgrp *gg, const dhge *YY)
+{
+ const intdh_grp *g = (const intdh_grp *)gg;
+ const intdh_ge *Y = (const intdh_ge *)YY;
+ mp *T;
+ int rc = 0;
+
+ if (MP_EQ(Y->X, g->mm.r)) rc = -1;
+ T = mpmont_expr(&g->mm, MP_NEW, Y->X, g->q);
+ if (!MP_EQ(T, g->mm.r)) rc = -1;
+ MP_DROP(T);
+ return (rc);
+}
+
+static int intdh_eq(const dhgrp *gg, const dhge *YY, const dhge *ZZ)
+{
+ const intdh_ge *Y = (const intdh_ge *)YY, *Z = (const intdh_ge *)ZZ;
+ return (MP_EQ(Y->X, Z->X));
+}
+
+static dhge *intdh_mul(const dhgrp *gg, const dhsc *xx, const dhge *YY)
+{
+ const intdh_grp *g = (const intdh_grp *)gg;
+ const intdh_sc *x = (const intdh_sc *)xx;
+ const intdh_ge *Y = (const intdh_ge *)YY;
+ intdh_ge *Z = CREATE(intdh_ge);
+
+ Z->X = mpmont_expr(&g->mm, MP_NEW, Y ? Y->X : g->G, x->x);
+ return ((dhge *)Z);
+}
+
+#ifndef NTRACE
+static const char *intdh_gestr(const dhgrp *gg, const dhge *YY)
+{
+ const intdh_grp *g = (const intdh_grp *)gg;
+ const intdh_ge *Y = (const intdh_ge *)YY;
+ mp *t = mpmont_reduce(&g->mm, MP_NEW, Y->X);
+ const char *p = mpstr(t, 10);
+ MP_DROP(t);
+ return (p);
+}
+#endif
+
+static void intdh_freege(const dhgrp *gg, dhge *YY)
+ { intdh_ge *Y = (intdh_ge *)YY; MP_DROP(Y->X); DESTROY(Y); }
+
+/*----- Elliptic curve groups ---------------------------------------------*/
+
+typedef struct ecdh_grp {
+ dhgrp _g;
+ ec_info ei;
+ ec P;
+} ecdh_grp;
+
+typedef struct ecdh_sc { mp *x; } ecdh_sc;
+typedef struct ecdh_ge { ec Q; } ecdh_ge;
+
+static dhgrp *ecdh_mkgroup(const char *cstr, dstr *e)
+{
+ ecdh_grp *g;
+ ec_info ei;
+ const char *err;
+
+ if ((err = ec_getinfo(&ei, cstr)) != 0) {
+ a_format(e, "decode-failed", "%s", err, A_END);
+ return (0);
+ }
+ g = CREATE(ecdh_grp);
+ g->ei = ei;
+ EC_CREATE(&g->P); EC_IN(g->ei.c, &g->P, &g->ei.g);
+ g->_g.scsz = mp_octets(g->ei.r);
+ return (&g->_g);
+}
+
+static dhsc *ecdh_mptosc(const dhgrp *gg, mp *z)
+{
+ const ecdh_grp *g = (const ecdh_grp *)gg;
+ ecdh_sc *x = CREATE(ecdh_sc);
+ x->x = MP_NEW; mp_div(0, &x->x, z, g->ei.r);
+ return ((dhsc *)x);
+}
+
+static dhge *ecdh_ectoge(const dhgrp *gg, ec *Q)
+{
+ const ecdh_grp *g = (const ecdh_grp *)gg;
+ ecdh_ge *Y = CREATE(ecdh_ge); EC_CREATE(&Y->Q);
+ EC_IN(g->ei.c, &Y->Q, Q);
+ if (EC_CHECK(g->ei.c, &Y->Q))
+ { EC_DESTROY(&Y->Q); DESTROY(Y); return (0); }
+ return ((dhge *)Y);
+}
+
+KLOAD(ecdh, ec, EC,
+ { if ((kd->grp = ecdh_mkgroup(p.cstr, e)) == 0) goto fail; },
+ { kd->k = ecdh_mptosc(kd->grp, p.x); },
+ { if ((kd->K = ecdh_ectoge(kd->grp, &p.p)) == 0) {
+ a_format(e, "bad-public-vector", A_END);
+ goto fail;
+ }
+ })
+
+static const char *ecdh_checkgrp(const dhgrp *gg)
+{
+ const ecdh_grp *g = (const ecdh_grp *)gg;
+ return (ec_checkinfo(&g->ei, &rand_global));
+}
+
+static void ecdh_grpinfo(const dhgrp *gg, admin *adm)
+{
+ const ecdh_grp *g = (const ecdh_grp *)gg;
+ a_info(adm,
+ "kx-group=ec",
+ "kx-group-order-bits=%lu", (unsigned long)mp_bits(g->ei.r),
+ "kx-group-elt-bits=%lu", (unsigned long)2*g->ei.c->f->nbits,
+ A_END);
+}
+
+#ifndef NTRACE
+static void addfestr(field *f, mp *x, mptext_stringctx *sc)
+ { addmpstr(x, F_TYPE(f) == FTY_PRIME ? 10 : 16, sc); }
+
+static void addintfestr(field *f, mp *x, mptext_stringctx *sc)
+ { mp *t = F_OUT(f, MP_NEW, x); addfestr(f, x, sc); MP_DROP(t); }
+
+static const char *intfestr(field *f, mp *x)
+{
+ mptext_stringctx sc;
+ setupstr(&sc);
+ addintfestr(f, x, &sc);
+ return (donestr(&sc));
+}
+
+static void ecdh_tracegrp(const dhgrp *gg)
+{
+ const ecdh_grp *g = (const ecdh_grp *)gg;
+ const ec_curve *c = g->ei.c;
+ field *f = c->f;
+
+ trace(T_CRYPTO, "crypto: group type `ec'");
+ switch (F_TYPE(f)) {
+ case FTY_PRIME:
+ trace(T_CRYPTO, "crypto: prime field `%s'", F_NAME(f));
+ trace(T_CRYPTO, "crypto: p = %s", mpstr(f->q, 10));
+ break;
+ case FTY_BINARY:
+ trace(T_CRYPTO, "crypto: binary field `%s'", F_NAME(f));
+ trace(T_CRYPTO, "crypto: degree = %lu", f->nbits - 1);
+ break;
+ default:
+ trace(T_CRYPTO, "crypto: unknown field type! `%s'", F_NAME(f));
+ break;
+ }
+ trace(T_CRYPTO, "crypto: curve type `%s'", EC_NAME(c));
+ trace(T_CRYPTO, "crypto: curve a = %s", intfestr(f, c->a));
+ trace(T_CRYPTO, "crypto: curve b = %s", intfestr(f, c->b));
+ trace(T_CRYPTO, "crypto: n = %s", mpstr(g->ei.r, 10));
+ trace(T_CRYPTO, "crypto: h = %s", mpstr(g->ei.h, 10));
+}
+#endif
+
+static int ecdh_samegrpp(const dhgrp *gg, const dhgrp *hh)
+{
+ const ecdh_grp *g = (const ecdh_grp *)gg, *h = (const ecdh_grp *)hh;
+ return (ec_sameinfop(&g->ei, &h->ei));
+}
+
+static void ecdh_freegrp(dhgrp *gg)
+{
+ ecdh_grp *g = (ecdh_grp *)gg;
+ EC_DESTROY(&g->P); ec_freeinfo(&g->ei);
+ DESTROY(g);
+}
+
+static dhsc *ecdh_ldsc(const dhgrp *gg, const void *p, size_t sz)
+{
+ const ecdh_grp *g = (const ecdh_grp *)gg;
+ ecdh_sc *x = CREATE(ecdh_sc);
+ mp *t = mp_loadb(MP_NEW, p, sz);
+ mp_div(0, &t, t, g->ei.r); x->x = t;
+ return ((dhsc *)x);
+}
+
+static int ecdh_stsc(const dhgrp *gg, void *p, size_t sz, const dhsc *xx)
+{
+ const ecdh_sc *x = (const ecdh_sc *)xx;
+ mp_storeb(x->x, p, sz);
+ return (0);
+}
+
+static dhsc *ecdh_randsc(const dhgrp *gg)
+{
+ const ecdh_grp *g = (const ecdh_grp *)gg;
+ ecdh_sc *x = CREATE(ecdh_sc);
+ x->x = mprand_range(MP_NEW, g->ei.r, &rand_global, 0);
+ return ((dhsc *)x);
+}
+
+#ifndef NTRACE
+static const char *ecdh_scstr(const dhgrp *gg, const dhsc *xx)
+{
+ const ecdh_sc *x = (const ecdh_sc *)xx;
+ return (mpstr(x->x, 10));
+}
+#endif
+
+static void ecdh_freesc(const dhgrp *gg, dhsc *xx)
+ { ecdh_sc *x = (ecdh_sc *)xx; MP_DROP(x->x); DESTROY(x); }
+
+static dhge *ecdh_ldge(const dhgrp *gg, buf *b, int fmt)
+{
+ const ecdh_grp *g = (const ecdh_grp *)gg;
+ ecdh_ge *Y;
+ ec T = EC_INIT;
+
+ switch (fmt) {
+ case DHFMT_VAR: case DHFMT_HASH: if (buf_getec(b, &T)) return (0); break;
+ case DHFMT_STD: if (ec_getraw(g->ei.c, b, &T)) return (0); break;
+ default:
+ abort();
+ }
+ EC_IN(g->ei.c, &T, &T);
+ Y = CREATE(ecdh_ge); Y->Q = T;
+ return ((dhge *)Y);
+}
+
+static int ecdh_stge(const dhgrp *gg, buf *b, const dhge *YY, int fmt)
+{
+ const ecdh_grp *g = (const ecdh_grp *)gg;
+ const ecdh_ge *Y = (const ecdh_ge *)YY;
+ ec T = EC_INIT;
+ int rc;
+
+ EC_OUT(g->ei.c, &T, &Y->Q);
+ switch (fmt) {
+ case DHFMT_VAR: case DHFMT_HASH: rc = buf_putec(b, &T); break;
+ case DHFMT_STD: rc = ec_putraw(g->ei.c, b, &T); break;
+ default: abort();
+ }
+ EC_DESTROY(&T);
+ return (rc);
+}
+
+static int ecdh_checkge(const dhgrp *gg, const dhge *YY)
+{
+ const ecdh_grp *g = (const ecdh_grp *)gg;
+ const ecdh_ge *Y = (const ecdh_ge *)YY;
+ ec T = EC_INIT;
+ int rc = 0;
+
+ if (EC_ATINF(&Y->Q)) rc = -1;
+ ec_imul(g->ei.c, &T, &Y->Q, g->ei.r);
+ if (!EC_ATINF(&T)) rc = -1;
+ EC_DESTROY(&T);
+ return (rc);
+}
+
+static int ecdh_eq(const dhgrp *gg, const dhge *YY, const dhge *ZZ)
+{
+ const ecdh_grp *g = (const ecdh_grp *)gg;
+ const ecdh_ge *Y = (const ecdh_ge *)YY, *Z = (const ecdh_ge *)ZZ;
+ ec T = EC_INIT, U = EC_INIT; int rc;
+ EC_FIX(g->ei.c, &T, &Y->Q); EC_FIX(g->ei.c, &U, &Z->Q);
+ rc = EC_EQ(&T, &U);
+ EC_DESTROY(&T); EC_DESTROY(&U);
+ return (rc);
+}
+
+static dhge *ecdh_mul(const dhgrp *gg, const dhsc *xx, const dhge *YY)
+{
+ const ecdh_grp *g = (const ecdh_grp *)gg;
+ const ecdh_sc *x = (const ecdh_sc *)xx;
+ const ecdh_ge *Y = (const ecdh_ge *)YY;
+ ecdh_ge *Z = CREATE(ecdh_ge); EC_CREATE(&Z->Q);
+
+ ec_imul(g->ei.c, &Z->Q, Y ? &Y->Q : &g->P, x->x);
+ return ((dhge *)Z);
+}
+
+#ifndef NTRACE
+static const char *ecdh_gestr(const dhgrp *gg, const dhge *YY)
+{
+ const ecdh_grp *g = (const ecdh_grp *)gg;
+ const ecdh_ge *Y = (const ecdh_ge *)YY;
+ ec T = EC_INIT;
+ field *f = g->ei.c->f;
+ mptext_stringctx sc;
+
+ if (EC_ATINF(&Y->Q)) return ("inf");
+ setupstr(&sc);
+ EC_OUT(g->ei.c, &T, &Y->Q);
+ addfestr(f, T.x, &sc);
+ addlitstr(", ", &sc);
+ addfestr(f, T.y, &sc);
+ EC_DESTROY(&T);
+ return (donestr(&sc));
+}
+#endif
+
+static void ecdh_freege(const dhgrp *gg, dhge *YY)
+ { ecdh_ge *Y = (ecdh_ge *)YY; EC_DESTROY(&Y->Q); DESTROY(Y); }
+
+/*----- Diffie--Hellman group table ---------------------------------------*/
+
+const dhops dhtab[] = {
+
+#define COMMA ,
+
+#define DH(name, pre) \
+ { name, pre##_ldpriv, pre##_ldpub, pre##_checkgrp, \
+ pre##_grpinfo, T( pre##_tracegrp COMMA ) pre##_samegrpp, \
+ pre##_freegrp, \
+ pre##_ldsc, pre##_stsc, pre##_randsc, T( pre##_scstr COMMA ) \
+ pre##_freesc, \
+ pre##_ldge, pre##_stge, pre##_checkge, pre##_eq, pre##_mul, \
+ T( pre##_gestr COMMA ) pre##_freege }, \
+
+ DH("dh", intdh)
+ DH("ec", ecdh)
+
+#undef DH
+
+ { 0 }
+};
+
+/*----- That's all, folks -------------------------------------------------*/
/* --- @hashge@ --- *
*
* Arguments: @ghash *h@ = pointer to hash context
- * @group *g@ = pointer to group
- * @ge *x@ = pointer to group element
+ * @const dhgrp *g@ = pointer to group
+ * @const dhge *Y@ = pointer to group element
*
* Returns: ---
*
* @buf_t@.
*/
-static void hashge(ghash *h, group *g, ge *x)
+static void hashge(ghash *h, const dhgrp *g, const dhge *Y)
{
buf b;
buf_init(&b, buf_t, sizeof(buf_t));
- G_TOBUF(g, &b, x);
+ g->ops->stge(g, &b, Y, DHFMT_HASH);
assert(BOK(&b));
GH_HASH(h, BBASE(&b), BLEN(&b));
}
/* --- @mpmask@ --- *
*
* Arguments: @buf *b@ = output buffer
- * @mp *x@ = the plaintext integer
+ * @const dhgrp *g@ = the group
+ * @const dhsc *x@ = the plaintext scalar
* @size_t n@ = the expected size of the plaintext
* @gcipher *mgfc@ = mask-generating function to use
* @const octet *k@ = pointer to key material
*
* Returns: ---
*
- * Use: Masks a multiprecision integer: returns %$x \xor H(k)$%, so
- * it's a random oracle thing rather than an encryption thing.
- * Breaks the output buffer on error.
+ * Use: Masks a scalar: returns %$x \xor H(k)$%, so it's a random
+ * oracle thing rather than an encryption thing. Breaks the
+ * output buffer on error.
*/
-static void mpmask(buf *b, mp *x, size_t n,
+static void mpmask(buf *b, const dhgrp *g, const dhsc *x, size_t n,
const gccipher *mgfc, const octet *k, size_t ksz)
{
gcipher *mgf;
if ((p = buf_get(b, n)) == 0) return;
mgf = GC_INIT(mgfc, k, ksz);
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
- trace(T_CRYPTO, "crypto: masking index = %s", mpstr(x));
+ trace(T_CRYPTO, "crypto: masking scalar = %s", g->ops->scstr(g, x));
trace_block(T_CRYPTO, "crypto: masking key", k, ksz);
}))
- mp_storeb(x, buf_t, n);
+ if (g->ops->stsc(g, buf_t, n, x)) { buf_break(b); return; }
GC_ENCRYPT(mgf, buf_t, p, n);
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
- trace_block(T_CRYPTO, "crypto: index plaintext", buf_t, n);
+ trace_block(T_CRYPTO, "crypto: scalar plaintext", buf_t, n);
trace_block(T_CRYPTO, "crypto: masked ciphertext", p, n);
}))
GC_DESTROY(mgf);
/* --- @mpunmask@ --- *
*
- * Arguments: @mp *d@ = the output integer
+ * Arguments: @const dhgrp *g@ = the group
* @const octet *p@ = pointer to the ciphertext
* @size_t n@ = the size of the ciphertext
* @gcipher *mgfc@ = mask-generating function to use
* @const octet *k@ = pointer to key material
* @size_t ksz@ = size of the key
*
- * Returns: The decrypted integer, or null.
+ * Returns: The decrypted scalar, or null.
*
- * Use: Unmasks a multiprecision integer.
+ * Use: Unmasks a scalar.
*/
-static mp *mpunmask(mp *d, const octet *p, size_t n,
+static dhsc *mpunmask(const dhgrp *g, const octet *p, size_t n,
const gccipher *mgfc, const octet *k, size_t ksz)
{
gcipher *mgf;
+ dhsc *x;
mgf = GC_INIT(mgfc, k, ksz);
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
trace_block(T_CRYPTO, "crypto: masked ciphertext", p, n);
}))
GC_DECRYPT(mgf, p, buf_t, n);
- d = mp_loadb(d, buf_t, n);
+ x = g->ops->ldsc(g, buf_t, n);
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
- trace_block(T_CRYPTO, "crypto: index plaintext", buf_t, n);
- trace(T_CRYPTO, "crypto: unmasked index = %s", mpstr(d));
+ trace_block(T_CRYPTO, "crypto: scalar plaintext", buf_t, n);
+ trace(T_CRYPTO, "crypto: unmasked scalar = %s",
+ x ? g->ops->scstr(g, x) : "<failed>");
}))
GC_DESTROY(mgf);
- return (d);
+ return (x);
}
/* --- @hashcheck@ --- *
*
* Arguments: @keyexch *kx@ = pointer to key-exchange block
- * @ge *kpub@ = sender's public key
- * @ge *cc@ = receiver's challenge
- * @ge *c@ = sender's challenge
- * @ge *y@ = reply to sender's challenge
+ * @const dhge *K@ = sender's public key
+ * @const dhge *CC@ = receiver's challenge
+ * @const dhge *C@ = sender's challenge
+ * @const dhge *Y@ = reply to sender's challenge
*
* Returns: Pointer to the hash value (in @buf_t@)
*
* indices to prove the validity of challenges. This computes
* the masking key used in challenge check values. This is
* really the heart of the whole thing, since it ensures that
- * the index can be recovered from the history of hashing
+ * the scalar can be recovered from the history of hashing
* queries, which gives us (a) a proof that the authentication
* process is zero-knowledge, and (b) a proof that the whole
* key-exchange is deniable.
*/
-static const octet *hashcheck(keyexch *kx, ge *kpub, ge *cc, ge *c, ge *y)
+static const octet *hashcheck(keyexch *kx, const dhge *K,
+ const dhge *CC, const dhge *C, const dhge *Y)
{
ghash *h = GH_INIT(kx->kpriv->algs.h);
- group *g = kx->kpriv->g;
+ const dhgrp *g = kx->kpriv->grp;
HASH_STRING(h, "tripe-expected-reply");
- hashge(h, g, kpub);
- hashge(h, g, cc);
- hashge(h, g, c);
- hashge(h, g, y);
+ hashge(h, g, K);
+ hashge(h, g, CC);
+ hashge(h, g, C);
+ hashge(h, g, Y);
GH_DONE(h, buf_t);
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
trace(T_CRYPTO, "crypto: computing challenge check hash");
- trace(T_CRYPTO, "crypto: public key = %s", gestr(g, kpub));
- trace(T_CRYPTO, "crypto: receiver challenge = %s", gestr(g, cc));
- trace(T_CRYPTO, "crypto: sender challenge = %s", gestr(g, c));
- trace(T_CRYPTO, "crypto: sender reply = %s", gestr(g, y));
+ trace(T_CRYPTO, "crypto: public key = %s", g->ops->gestr(g, K));
+ trace(T_CRYPTO, "crypto: receiver challenge = %s", g->ops->gestr(g, CC));
+ trace(T_CRYPTO, "crypto: sender challenge = %s", g->ops->gestr(g, C));
+ trace(T_CRYPTO, "crypto: sender reply = %s", g->ops->gestr(g, Y));
trace_block(T_CRYPTO, "crypto: hash output", buf_t, kx->kpriv->algs.hashsz);
}))
GH_DESTROY(h);
*
* Arguments: @keyexch *kx@ = pointer to key exchange block
* @buf *b@ = output buffer for challenge
- * @ge *c@ = peer's actual challenge
+ * @const dhge *C@ = peer's actual challenge
* @const octet *hc@ = peer's challenge cookie
*
* Returns: ---
* Use: Writes a full challenge to the message buffer.
*/
-static void sendchallenge(keyexch *kx, buf *b, ge *c, const octet *hc)
+static void sendchallenge(keyexch *kx, buf *b,
+ const dhge *C, const octet *hc)
{
- G_TOBUF(kx->kpriv->g, b, kx->c);
+ const dhgrp *g = kx->kpriv->grp;
+ g->ops->stge(g, b, kx->C, DHFMT_VAR);
buf_put(b, hc, kx->kpriv->algs.hashsz);
- mpmask(b, kx->alpha, kx->kpriv->indexsz, kx->kpriv->algs.mgf,
- hashcheck(kx, kx->kpriv->kpub, c, kx->c, kx->rx),
+ mpmask(b, g, kx->a, g->scsz, kx->kpriv->algs.mgf,
+ hashcheck(kx, kx->kpriv->K, C, kx->C, kx->RX),
kx->kpriv->algs.hashsz);
}
static void kxc_destroy(kxchal *kxc)
{
+ const dhgrp *g = kxc->kx->kpriv->grp;
if (kxc->f & KXF_TIMER)
sel_rmtimer(&kxc->t);
- G_DESTROY(kxc->kx->kpriv->g, kxc->c);
- G_DESTROY(kxc->kx->kpriv->g, kxc->r);
+ g->ops->freege(g, kxc->C);
+ g->ops->freege(g, kxc->R);
ks_drop(kxc->ks);
DESTROY(kxc);
}
/* --- @kxc_bychal@ --- *
*
* Arguments: @keyexch *kx@ = pointer to key exchange block
- * @ge *c@ = challenge from remote host
+ * @const dhge *C@ = challenge from remote host
*
* Returns: Pointer to the challenge block, or null.
*
* Use: Finds a challenge block, given its challenge.
*/
-static kxchal *kxc_bychal(keyexch *kx, ge *c)
+static kxchal *kxc_bychal(keyexch *kx, const dhge *C)
{
+ const dhgrp *g = kx->kpriv->grp;
unsigned i;
for (i = 0; i < kx->nr; i++) {
- if (G_EQ(kx->kpriv->g, c, kx->r[i]->c))
+ if (g->ops->eq(g, C, kx->r[i]->C))
return (kx->r[i]);
}
return (0);
{
stats *st = p_stats(kx->p);
buf *b = p_txstart(kx->p, MSG_KEYEXCH | KX_REPLY);
+ const dhgrp *g = kx->kpriv->grp;
struct timeval tv;
buf bb;
/* --- Build the reply packet --- */
T( trace(T_KEYEXCH, "keyexch: sending reply to `%s'", p_name(kx->p)); )
- sendchallenge(kx, b, kxc->c, kxc->hc);
+ sendchallenge(kx, b, kxc->C, kxc->hc);
buf_init(&bb, buf_i, sizeof(buf_i));
- G_TORAW(kx->kpriv->g, &bb, kxc->r);
+ g->ops->stge(g, &bb, kxc->R, DHFMT_STD);
buf_flip(&bb);
ks_encrypt(kxc->ks, MSG_KEYEXCH | KX_REPLY, &bb, b);
static int doprechallenge(keyexch *kx, buf *b)
{
stats *st = p_stats(kx->p);
- ge *c = G_CREATE(kx->kpriv->g);
+ const dhgrp *g = kx->kpriv->grp;
+ dhge *C = 0;
ghash *h;
/* --- Ensure that we're in a sensible state --- */
/* --- Unpack the packet --- */
- if (G_FROMBUF(kx->kpriv->g, b, c) || BLEFT(b))
+ if ((C = g->ops->ldge(g, b, DHFMT_VAR)) == 0 || BLEFT(b))
goto bad;
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
- trace(T_CRYPTO, "crypto: challenge = %s", gestr(kx->kpriv->g, c));
+ trace(T_CRYPTO, "crypto: challenge = %s", g->ops->gestr(g, C));
}))
/* --- Send out a full challenge by return --- */
b = p_txstart(kx->p, MSG_KEYEXCH | KX_CHAL);
h = GH_INIT(kx->kpriv->algs.h);
HASH_STRING(h, "tripe-cookie");
- hashge(h, kx->kpriv->g, c);
- sendchallenge(kx, b, c, GH_DONE(h, 0));
+ hashge(h, g, C);
+ sendchallenge(kx, b, C, GH_DONE(h, 0));
GH_DESTROY(h);
st->n_kxout++;
st->sz_kxout += BLEN(b);
/* --- Done --- */
- G_DESTROY(kx->kpriv->g, c);
+ g->ops->freege(g, C);
return (0);
bad:
- if (c) G_DESTROY(kx->kpriv->g, c);
+ if (C) g->ops->freege(g, C);
return (-1);
}
static kxchal *respond(keyexch *kx, unsigned msg, buf *b)
{
- group *g = kx->kpriv->g;
+ const dhgrp *g = kx->kpriv->grp;
const algswitch *algs = &kx->kpriv->algs;
- size_t ixsz = kx->kpriv->indexsz;
- ge *c = G_CREATE(g);
- ge *r = G_CREATE(g);
- ge *cc = G_CREATE(g);
+ size_t ixsz = g->scsz;
+ dhge *C = 0;
+ dhge *R = 0;
+ dhge *CC = 0;
const octet *hc, *ck;
size_t x, y, z;
- mp *cv = 0;
+ dhsc *c = 0;
kxchal *kxc;
ghash *h = 0;
buf bb;
/* --- Unpack the packet --- */
- if (G_FROMBUF(g, b, c) ||
+ if ((C = g->ops->ldge(g, b, DHFMT_VAR)) == 0 ||
(hc = buf_get(b, algs->hashsz)) == 0 ||
(ck = buf_get(b, ixsz)) == 0) {
a_warn("KX", "?PEER", kx->p, "invalid", "%s", pkname[msg], A_END);
goto bad;
}
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
- trace(T_CRYPTO, "crypto: challenge = %s", gestr(g, c));
+ trace(T_CRYPTO, "crypto: challenge = %s", g->ops->gestr(g, C));
trace_block(T_CRYPTO, "crypto: cookie", hc, algs->hashsz);
trace_block(T_CRYPTO, "crypto: check-value", ck, ixsz);
}))
* This will also find a challenge block and, if necessary, populate it.
*/
- if ((kxc = kxc_bychal(kx, c)) != 0) {
+ if ((kxc = kxc_bychal(kx, C)) != 0) {
h = GH_INIT(algs->h);
HASH_STRING(h, "tripe-check-hash");
GH_HASH(h, ck, ixsz);
/* --- Compute the reply, and check the magic --- */
- G_EXP(g, r, c, kx->kpriv->kpriv);
- if ((cv = mpunmask(MP_NEW, ck, ixsz, algs->mgf,
- hashcheck(kx, kx->kpub->kpub, kx->c, c, r),
- algs->hashsz)) == 0)
+ R = g->ops->mul(g, kx->kpriv->k, C);
+ if ((c = mpunmask(g, ck, ixsz, algs->mgf,
+ hashcheck(kx, kx->kpub->K, kx->C, C, R),
+ algs->hashsz)) == 0)
goto badcheck;
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
- trace(T_CRYPTO, "crypto: computed reply = %s", gestr(g, r));
- trace(T_CRYPTO, "crypto: recovered log = %s", mpstr(cv));
+ trace(T_CRYPTO, "crypto: computed reply = %s", g->ops->gestr(g, R));
+ trace(T_CRYPTO, "crypto: recovered log = %s", g->ops->scstr(g, c));
}))
- if (MP_CMP(cv, >, g->r) ||
- (G_EXP(g, cc, g->g, cv),
- !G_EQ(g, c, cc)))
- goto badcheck;
+ CC = g->ops->mul(g, c, 0);
+ if (!g->ops->eq(g, CC, C)) goto badcheck;
/* --- Fill in a new challenge block --- */
kxc = kxc_new(kx);
- kxc->c = c; c = 0;
- kxc->r = r; r = G_CREATE(g);
+ kxc->C = C; C = 0;
+ kxc->R = R; R = 0;
h = GH_INIT(algs->h); HASH_STRING(h, "tripe-check-hash");
GH_HASH(h, ck, ixsz);
GH_DONE(h, kxc->ck); GH_DESTROY(h);
h = GH_INIT(algs->h); HASH_STRING(h, "tripe-cookie");
- hashge(h, g, kxc->c);
+ hashge(h, g, kxc->C);
GH_DONE(h, kxc->hc); GH_DESTROY(h);
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
/* --- Work out the shared key --- */
- G_EXP(g, r, kxc->c, kx->alpha);
+ R = g->ops->mul(g, kx->a, kxc->C);
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
- trace(T_CRYPTO, "crypto: shared secret = %s", gestr(g, r));
+ trace(T_CRYPTO, "crypto: shared secret = %s", g->ops->gestr(g, R));
}))
/* --- Compute the switch messages --- */
h = GH_INIT(algs->h); HASH_STRING(h, "tripe-switch-request");
- hashge(h, g, kx->c); hashge(h, g, kxc->c);
+ hashge(h, g, kx->C); hashge(h, g, kxc->C);
GH_DONE(h, kxc->hswrq_out); GH_DESTROY(h);
h = GH_INIT(algs->h); HASH_STRING(h, "tripe-switch-confirm");
- hashge(h, g, kx->c); hashge(h, g, kxc->c);
+ hashge(h, g, kx->C); hashge(h, g, kxc->C);
GH_DONE(h, kxc->hswok_out); GH_DESTROY(h);
h = GH_INIT(algs->h); HASH_STRING(h, "tripe-switch-request");
- hashge(h, g, kxc->c); hashge(h, g, kx->c);
+ hashge(h, g, kxc->C); hashge(h, g, kx->C);
GH_DONE(h, kxc->hswrq_in); GH_DESTROY(h);
h = GH_INIT(algs->h); HASH_STRING(h, "tripe-switch-confirm");
- hashge(h, g, kxc->c); hashge(h, g, kx->c);
+ hashge(h, g, kxc->C); hashge(h, g, kx->C);
GH_DONE(h, kxc->hswok_in); GH_DESTROY(h);
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
/* --- Create a new symmetric keyset --- */
buf_init(&bb, buf_o, sizeof(buf_o));
- G_TOBUF(g, &bb, kx->c); x = BLEN(&bb);
- G_TOBUF(g, &bb, kxc->c); y = BLEN(&bb);
- G_TOBUF(g, &bb, r); z = BLEN(&bb);
+ g->ops->stge(g, &bb, kx->C, DHFMT_HASH); x = BLEN(&bb);
+ g->ops->stge(g, &bb, kxc->C, DHFMT_HASH); y = BLEN(&bb);
+ g->ops->stge(g, &bb, R, DHFMT_HASH); z = BLEN(&bb);
assert(BOK(&bb));
kxc->ks = ks_gen(BBASE(&bb), x, y, z, kx->p);
}
- if (c) G_DESTROY(g, c);
- G_DESTROY(g, cc);
- G_DESTROY(g, r);
- mp_drop(cv);
+ if (C) g->ops->freege(g, C);
+ if (CC) g->ops->freege(g, CC);
+ if (R) g->ops->freege(g, R);
+ if (c) g->ops->freesc(g, c);
return (kxc);
badcheck:
a_warn("KX", "?PEER", kx->p, "bad-expected-reply-log", A_END);
goto bad;
bad:
- if (c) G_DESTROY(g, c);
- G_DESTROY(g, cc);
- G_DESTROY(g, r);
- mp_drop(cv);
+ if (C) g->ops->freege(g, C);
+ if (CC) g->ops->freege(g, CC);
+ if (R) g->ops->freege(g, R);
+ if (c) g->ops->freesc(g, c);
return (0);
}
buf bb;
stats *st = p_stats(kx->p);
struct timeval tv;
+ const dhgrp *g = kx->kpriv->grp;
buf *b;
switch (kx->s) {
T( trace(T_KEYEXCH, "keyexch: sending prechallenge to `%s'",
p_name(kx->p)); )
b = p_txstart(kx->p, MSG_KEYEXCH | KX_PRECHAL);
- G_TOBUF(kx->kpriv->g, b, kx->c);
+ g->ops->stge(g, b, kx->C, DHFMT_VAR);
break;
case KXS_COMMIT:
T( trace(T_KEYEXCH, "keyexch: sending switch request to `%s'",
buf_put(b, kx->hc, kx->kpriv->algs.hashsz);
buf_put(b, kxc->hc, kx->kpriv->algs.hashsz);
buf_init(&bb, buf_i, sizeof(buf_i));
- G_TORAW(kx->kpriv->g, &bb, kxc->r);
+ g->ops->stge(g, &bb, kxc->R, DHFMT_STD);
buf_put(&bb, kxc->hswrq_out, kx->kpriv->algs.hashsz);
buf_flip(&bb);
ks_encrypt(kxc->ks, MSG_KEYEXCH | KX_SWITCH, &bb, b);
static int checkresponse(keyexch *kx, unsigned msg, buf *b)
{
- group *g = kx->kpriv->g;
- ge *r = G_CREATE(g);
+ const dhgrp *g = kx->kpriv->grp;
+ dhge *R;
- if (G_FROMRAW(g, b, r)) {
+ if ((R = g->ops->ldge(g, b, DHFMT_STD)) == 0) {
a_warn("KX", "?PEER", kx->p, "invalid", "%s", pkname[msg], A_END);
goto bad;
}
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
- trace(T_CRYPTO, "crypto: reply = %s", gestr(g, r));
+ trace(T_CRYPTO, "crypto: reply = %s", g->ops->gestr(g, R));
}))
- if (!G_EQ(g, r, kx->rx)) {
+ if (!g->ops->eq(g, R, kx->RX)) {
a_warn("KX", "?PEER", kx->p, "incorrect", "response", A_END);
goto bad;
}
- G_DESTROY(g, r);
+ g->ops->freege(g, R);
return (0);
bad:
- G_DESTROY(g, r);
+ if (R) g->ops->freege(g, R);
return (-1);
}
static void stop(keyexch *kx)
{
+ const dhgrp *g = kx->kpriv->grp;
unsigned i;
if (kx->f & KXF_DEAD)
sel_rmtimer(&kx->t);
for (i = 0; i < kx->nr; i++)
kxc_destroy(kx->r[i]);
- mp_drop(kx->alpha);
- G_DESTROY(kx->kpriv->g, kx->c);
- G_DESTROY(kx->kpriv->g, kx->rx);
+ g->ops->freesc(g, kx->a);
+ g->ops->freege(g, kx->C);
+ g->ops->freege(g, kx->RX);
kx->t_valid = 0;
kx->f |= KXF_DEAD;
kx->f &= ~KXF_TIMER;
static void start(keyexch *kx, time_t now)
{
algswitch *algs = &kx->kpriv->algs;
- group *g = kx->kpriv->g;
+ const dhgrp *g = kx->kpriv->grp;
ghash *h;
assert(kx->f & KXF_DEAD);
kx->f &= ~(KXF_DEAD | KXF_CORK);
kx->nr = 0;
- kx->alpha = mprand_range(MP_NEW, g->r, &rand_global, 0);
- kx->c = G_CREATE(g); G_EXP(g, kx->c, g->g, kx->alpha);
- kx->rx = G_CREATE(g); G_EXP(g, kx->rx, kx->kpub->kpub, kx->alpha);
+ kx->a = g->ops->randsc(g);
+ kx->C = g->ops->mul(g, kx->a, 0);
+ kx->RX = g->ops->mul(g, kx->a, kx->kpub->K);
kx->s = KXS_CHAL;
kx->t_valid = now + T_VALID;
h = GH_INIT(algs->h);
HASH_STRING(h, "tripe-cookie");
- hashge(h, g, kx->c);
+ hashge(h, g, kx->C);
GH_DONE(h, kx->hc);
GH_DESTROY(h);
IF_TRACING(T_KEYEXCH, {
trace(T_KEYEXCH, "keyexch: creating new challenge");
IF_TRACING(T_CRYPTO, {
- trace(T_CRYPTO, "crypto: secret = %s", mpstr(kx->alpha));
- trace(T_CRYPTO, "crypto: challenge = %s", gestr(g, kx->c));
- trace(T_CRYPTO, "crypto: expected response = %s", gestr(g, kx->rx));
+ trace(T_CRYPTO, "crypto: secret = %s", g->ops->scstr(g, kx->a));
+ trace(T_CRYPTO, "crypto: challenge = %s", g->ops->gestr(g, kx->C));
+ trace(T_CRYPTO, "crypto: expected response = %s",
+ g->ops->gestr(g, kx->RX));
trace_block(T_CRYPTO, "crypto: challenge cookie",
kx->hc, algs->hashsz);
})
newkeys:
switchp = ((kx->f & KXF_DEAD) ||
kx->s != KXS_SWITCH ||
- !group_samep(kx->kpriv->g, kpriv->g));
+ kpriv->grp->ops != kx->kpriv->grp->ops ||
+ !kpriv->grp->ops->samegrpp(kpriv->grp, kx->kpriv->grp));
T( trace(T_KEYEXCH, "keyexch: peer `%s' adopting "
"%s priv `%s' and %s pub `%s'; %sforcing exchange", p_name(kx->p),
#include "tripe.h"
-/*----- Key groups --------------------------------------------------------*/
-
-/* The key-loading functions here must fill in the kdata slot @g@ and
- * either @kpriv@ or @kpub@ as appropriate. The caller will take care of
- * determining @kpub@ given a private key, and of ensuring that @kpriv@ is
- * null for a public key.
- */
-
-typedef struct kgops {
- const char *ty;
- int (*loadpriv)(key_data *, kdata *, dstr *, dstr *);
- int (*loadpub)(key_data *, kdata *, dstr *, dstr *);
-} kgops;
-
-/* --- @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.
- */
-
-#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); \
-}
-
-/* --- @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@.
- */
-
-#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; \
- } \
- })
-
-#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[] = {
-#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@ = a choice of algorithms
* @dstr *e@ = where to write error tokens
- * @const group *g@ = the group we're working in
+ * @const dhgrp *grp@ = the group we're working in
*
* Returns: Zero if OK; nonzero on error.
*
* for use by @keyset@ functions.
*/
-static int algs_check(algswitch *a, dstr *e, const group *g)
+static int algs_check(algswitch *a, dstr *e, const dhgrp *grp)
{
a->hashsz = a->h->hashsz;
{
const algswitch *a = &kdx->algs, *aa = &kdy->algs;
- return (group_samep(kdx->g, kdy->g) &&
+ return (kdx->grp->ops == kdy->grp->ops &&
+ kdx->grp->ops->samegrpp(kdx->grp, kdy->grp) &&
a->mgf == aa->mgf && a->h == aa->h &&
a->bulk->ops == aa->bulk->ops &&
a->bulk->ops->samealgsp(a->bulk, aa->bulk));
typedef struct keyhalf {
const char *kind;
- int (*load)(const kgops *, key_data *, kdata *, dstr *, dstr *);
+ int (*load)(key_file *, key *, key_data *,
+ const dhops *, kdata *, dstr *, dstr *);
const char *kr;
key_file *kf;
fwatch w;
/* --- @kh_loadpub@, @kh_loadpriv@ --- *
*
- * Arguments: @const kgops *ko@ = key-group operations for key type
- * @key_data *d@ = key data object as stored in keyring
+ * Arguments: @const dhops *dh@ = Diffie--Hellman operations for key type
+ * @key_file *kf@ = key file from which the key was loaded
+ * @key *k@ = the key object we're loading
+ * @key_data *d@ = the key data to load
* @kdata *kd@ = our key-data object to fill in
* @dstr *t@ = the key tag name
* @dstr *e@ = a string to write error tokens to
* Returns: Zero on success, @-1@ on error.
*
* Use: These functions handle the main difference between public and
- * private key halves. They are responsible for setting @g@,
- * @kpriv@ and @kpub@ appropriately in all keys, handling the
- * mismatch between the largely half-indifferent calling code
- * and the group-specific loading functions.
+ * private key halves. They are responsible for setting @grp@,
+ * @k@ and @K@ appropriately in all keys, handling the mismatch
+ * between the largely half-indifferent calling code and the
+ * group-specific loading functions.
*
* The function @kh_loadpriv@ is also responsible for checking
* the group for goodness. We don't bother checking public
* checked.
*/
-static int kh_loadpub(const kgops *ko, key_data *d, kdata *kd,
- dstr *t, dstr *e)
+static int kh_loadpub(key_file *kf, key *k, key_data *d,
+ const dhops *dh, kdata *kd, dstr *t, dstr *e)
{
int rc;
- if ((rc = ko->loadpub(d, kd, t, e)) != 0)
+ if ((rc = dh->ldpub(kf, k, d, kd, t, e)) != 0)
goto fail_0;
- if (group_check(kd->g, kd->kpub)) {
+ kd->grp->ops = dh;
+ if (kd->grp->ops->checkge(kd->grp, kd->K)) {
a_format(e, "bad-public-group-element", A_END);
goto fail_1;
}
- kd->kpriv = 0;
return (0);
fail_1:
- G_DESTROY(kd->g, kd->kpub);
- G_DESTROYGROUP(kd->g);
+ kd->grp->ops->freege(kd->grp, kd->K);
+ kd->grp->ops->freegrp(kd->grp);
fail_0:
return (-1);
}
-static int kh_loadpriv(const kgops *ko, key_data *d, kdata *kd,
- dstr *t, dstr *e)
+static int kh_loadpriv(key_file *kf, key *k, key_data *d,
+ const dhops *dh, kdata *kd, dstr *t, dstr *e)
{
int rc;
const char *err;
- if ((rc = ko->loadpriv(d, kd, t, e)) != 0)
+ if ((rc = dh->ldpriv(kf, k, d, kd, t, e)) != 0)
goto fail_0;
- if ((err = G_CHECK(kd->g, &rand_global)) != 0) {
+ kd->grp->ops = dh;
+ if ((err = kd->grp->ops->checkgrp(kd->grp)) != 0) {
a_format(e, "bad-group", "%s", err, A_END);
goto fail_1;
}
return (0);
fail_1:
- mp_drop(kd->kpriv);
- G_DESTROY(kd->g, kd->kpub);
- G_DESTROYGROUP(kd->g);
+ kd->grp->ops->freesc(kd->grp, kd->k);
+ kd->grp->ops->freege(kd->grp, kd->K);
+ kd->grp->ops->freegrp(kd->grp);
fail_0:
return (-1);
}
key_data **d;
kdata *kd;
const char *ty;
- const kgops **ko;
+ const dhops *dh;
+ T( const dhgrp *g; )
/* --- Find the key and grab its tag --- */
if (!ty && strncmp(k->type, "tripe-", 6) == 0) ty = k->type + 6;
if (!ty) ty = "dh";
- for (ko = kgtab; *ko; ko++)
- if (strcmp((*ko)->ty, ty) == 0) goto foundko;
+ for (dh = dhtab; dh->name; dh++)
+ if (strcmp(dh->name, ty) == 0) goto founddh;
a_warn("KEYMGMT", "%s-keyring", kh->kind,
"%s", kh->kr, "key", "%s", t.buf,
"unknown-group-type", "%s", ty, A_END);
goto fail_0;
-foundko:
+founddh:
kd = CREATE(kdata);
- if (kh->load(*ko, *d, kd, &t, &e)) {
+ if (kh->load(kh->kf, k, *d, dh, kd, &t, &e)) {
a_warn("KEYMGMT", "%s-keyring", kh->kind,
"%s", kh->kr, "key", "%s", t.buf,
"*%s", e.buf, A_END);
}
if (algs_get(&kd->algs, &e, kh->kf, k) ||
- (kd->kpriv && algs_check(&kd->algs, &e, kd->g))) {
+ (kd->k && algs_check(&kd->algs, &e, kd->grp))) {
a_warn("KEYMGMT", "%s-keyring", kh->kind,
"%s", kh->kr, "key", "%s", t.buf,
"*%s", e.buf, A_END);
}
kd->tag = xstrdup(t.buf);
- kd->indexsz = mp_octets(kd->g->r);
kd->ref = 1;
kd->kn = 0;
kd->t_exp = k->exp;
IF_TRACING(T_KEYMGMT, {
trace(T_KEYMGMT, "keymgmt: loaded %s key `%s'", kh->kind, t.buf);
IF_TRACING(T_CRYPTO, {
- trace(T_CRYPTO, "crypto: r = %s", mpstr(kd->g->r));
- trace(T_CRYPTO, "crypto: h = %s", mpstr(kd->g->h));
- if (kd->kpriv)
- trace(T_CRYPTO, "crypto: x = %s", mpstr(kd->kpriv));
+ g = kd->grp;
+ g->ops->tracegrp(g);
+ if (kd->k)
+ trace(T_CRYPTO, "crypto: k = %s", g->ops->scstr(g, kd->k));
+ trace(T_CRYPTO, "crypto: K = %s", g->ops->gestr(g, kd->K));
kd->algs.bulk->ops->tracealgs(kd->algs.bulk);
})
})
goto done;
fail_2:
- if (kd->kpriv) mp_drop(kd->kpriv);
- G_DESTROY(kd->g, kd->kpub);
- G_DESTROYGROUP(kd->g);
+ if (kd->k) kd->grp->ops->freesc(kd->grp, kd->k);
+ kd->grp->ops->freege(kd->grp, kd->K);
+ kd->grp->ops->freegrp(kd->grp);
fail_1:
DESTROY(kd);
fail_0:
kn->f &= ~KNF_BROKEN;
if (kd->t_exp == kn->kd->t_exp &&
km_samealgsp(kd, kn->kd) &&
- G_EQ(kd->g, kd->kpub, kn->kd->kpub)) {
+ kd->grp->ops->eq(kd->grp, kd->K, kn->kd->K)) {
T( trace(T_KEYMGMT, "keymgmt: key `%s' unchanged", SYM_NAME(kn)); )
continue;
}
void km_unref(kdata *kd)
{
if (--kd->ref) return;
- if (kd->kpriv) mp_drop(kd->kpriv);
- G_DESTROY(kd->g, kd->kpub);
+ if (kd->k) kd->grp->ops->freesc(kd->grp, kd->k);
+ kd->grp->ops->freege(kd->grp, kd->K);
+ kd->grp->ops->freegrp(kd->grp);
xfree(kd->tag);
- G_DESTROYGROUP(kd->g);
DESTROY(kd);
}
/*----- Main code ---------------------------------------------------------*/
-/* --- @mpstr@ --- *
- *
- * Arguments: @mp *m@ = a multiprecision integer
- *
- * Returns: A pointer to the integer's textual representation.
- *
- * Use: Converts a multiprecision integer to a string. Corrupts
- * @buf_u@.
- */
-
-const char *mpstr(mp *m)
-{
- if (mp_writestring(m, (char *)buf_u, sizeof(buf_u), 10))
- return ("<failed>");
- return ((const char *)buf_u);
-}
-
-/* --- @gestr@ --- *
- *
- * Arguments: @group *g@ = a group
- * @ge *x@ = a group element
- *
- * Returns: A pointer to the element's textual representation.
- *
- * Use: Converts a group element to a string. Corrupts
- * @buf_u@.
- */
-
-const char *gestr(group *g, ge *x)
-{
- if (group_writestring(g, x, (char *)buf_u, sizeof(buf_u)))
- return ("<failed>");
- return ((const char *)buf_u);
-}
-
/* --- @timestr@ --- *
*
* Arguments: @time_t t@ = a time to convert
#include <catacomb/rand.h>
#include <catacomb/mp.h>
+#include <catacomb/mpmont.h>
#include <catacomb/mprand.h>
#include <catacomb/dh.h>
#include <catacomb/ec.h>
+#include <catacomb/ec-raw.h>
#include <catacomb/ec-keys.h>
-#include <catacomb/group.h>
#include "priv.h"
#include "protocol.h"
typedef struct keyset keyset;
typedef struct algswitch algswitch;
+typedef struct kdata kdata;
typedef struct admin admin;
+typedef struct dhgrp {
+ const struct dhops *ops;
+ size_t scsz;
+} dhgrp;
+
+typedef struct dhsc dhsc;
+typedef struct dhge dhge;
+
+enum {
+ DHFMT_STD, /* Fixed-width format, suitable for encryption */
+ DHFMT_HASH, /* Deterministic format, suitable for hashing */
+ DHFMT_VAR /* Variable-width-format, mostly a bad idea */
+};
+
typedef struct bulkalgs {
const struct bulkops *ops;
} bulkalgs;
struct rawkey;
+typedef struct dhops {
+ const char *name;
+
+ int (*ldpriv)(key_file */*kf*/, key */*k*/, key_data */*d*/,
+ kdata */*kd*/, dstr */*t*/, dstr */*e*/);
+ /* Load a private key from @d@, storing the data in @kd@. The key's
+ * file and key object are in @kf@ and @k, mostly in case its
+ * attributes are interesting; the key tag is in @t@; errors are
+ * reported by writing tokens to @e@ and returning nonzero.
+ */
+
+ int (*ldpub)(key_file */*kf*/, key */*k*/, key_data */*d*/,
+ kdata */*kd*/, dstr */*t*/, dstr */*e*/);
+ /* Load a public key from @d@, storing the data in @kd@. The key's
+ * file and key object are in @kf@ and @k, mostly in case its
+ * attributes are interesting; the key tag is in @t@; errors are
+ * reported by writing tokens to @e@ and returning nonzero.
+ */
+
+ const char *(*checkgrp)(const dhgrp */*g*/);
+ /* Check that the group is valid; return null on success, or an error
+ * string.
+ */
+
+ void (*grpinfo)(const dhgrp */*g*/, admin */*a*/);
+ /* Report on the group to an admin client. */
+
+ T( void (*tracegrp)(const dhgrp */*g*/); )
+ /* Trace a description of the group. */
+
+ int (*samegrpp)(const dhgrp */*g*/, const dhgrp */*gg*/);
+ /* Return nonzero if the two group objects represent the same
+ * group.
+ */
+
+ void (*freegrp)(dhgrp */*g*/);
+ /* Free a group and the resources it holds. */
+
+ dhsc *(*ldsc)(const dhgrp */*g*/, const void */*p*/, size_t /*sz*/);
+ /* Load a scalar from @p@, @sz@ and return it. Return null on
+ * error.
+ */
+
+ int (*stsc)(const dhgrp */*g*/,
+ void */*p*/, size_t /*sz*/, const dhsc */*x*/);
+ /* Store a scalar at @p@, @sz@. Return nonzero on error. */
+
+ dhsc *(*randsc)(const dhgrp */*g*/);
+ /* Return a random scalar. */
+
+ T( const char *(*scstr)(const dhgrp */*g*/, const dhsc */*x*/); )
+ /* Return a human-readable representation of @x@; @buf_t@ may be used
+ * to hold it.
+ */
+
+ void (*freesc)(const dhgrp */*g*/, dhsc */*x*/);
+ /* Free a scalar and the resources it holds. */
+
+ dhge *(*ldge)(const dhgrp */*g*/, buf */*b*/, int /*fmt*/);
+ /* Load a group element from @b@, encoded using format @fmt@. Return
+ * null on error.
+ */
+
+ int (*stge)(const dhgrp */*g*/, buf */*b*/,
+ const dhge */*Y*/, int /*fmt*/);
+ /* Store a group element in @b@, encoded using format @fmt@. Return
+ * nonzero on error.
+ */
+
+ int (*checkge)(const dhgrp */*h*/, const dhge */*Y*/);
+ /* Check a group element for validity. Return zero if everything
+ * checks out; nonzero on failure.
+ */
+
+ int (*eq)(const dhgrp */*g*/, const dhge */*Y*/, const dhge */*Z*/);
+ /* Return nonzero if @Y@ and @Z@ are equal. */
+
+ dhge *(*mul)(const dhgrp */*g*/, const dhsc */*x*/, const dhge */*Y*/);
+ /* Multiply a group element by a scalar, resulting in a shared-secret
+ * group element. If @y@ is null, then multiply the well-known
+ * generator.
+ */
+
+ T( const char *(*gestr)(const dhgrp */*g*/, const dhge */*Y*/); )
+ /* Return a human-readable representation of @Y@; @buf_t@ may be used
+ * to hold it.
+ */
+
+ void (*freege)(const dhgrp */*g*/, dhge */*Y*/);
+ /* Free a group element and the resources it holds. */
+
+} dhops;
+
typedef struct bulkops {
const char *name;
bulkalgs *bulk; /* Bulk crypto algorithms */
};
-typedef struct kdata {
+struct kdata {
unsigned ref; /* Reference counter */
struct knode *kn; /* Pointer to cache entry */
char *tag; /* Full tag name of the key */
- group *g; /* The group we work in */
- size_t indexsz; /* Size of exponent for the group */
- mp *kpriv; /* The private key (or null) */
- ge *kpub; /* The public key */
+ dhgrp *grp; /* The group we work in */
+ dhsc *k; /* The private key (or null) */
+ dhge *K; /* The public key */
time_t t_exp; /* Expiry time of the key */
algswitch algs; /* Collection of algorithms */
-} kdata;
+};
typedef struct knode {
sym_base _b; /* Symbol table intrusion */
#define HASH_STRING(h, s) GH_HASH((h), (s), sizeof(s))
+extern const dhops dhtab[];
extern const bulkops bulktab[];
/*----- Data structures ---------------------------------------------------*/
typedef struct kxchal {
struct keyexch *kx; /* Pointer back to key exchange */
- ge *c; /* Responder's challenge */
- ge *r; /* My reply to the challenge */
+ dhge *C; /* Responder's challenge */
+ dhge *R; /* My reply to the challenge */
keyset *ks; /* Pointer to temporary keyset */
unsigned f; /* Various useful flags */
sel_timer t; /* Response timer for challenge */
unsigned s; /* Current state in exchange */
sel_timer t; /* Timer for next exchange */
retry rs; /* Retry state */
- mp *alpha; /* My temporary secret */
- ge *c; /* My challenge */
- ge *rx; /* The expected response */
+ dhsc *a; /* My temporary secret */
+ dhge *C; /* My challenge */
+ dhge *RX; /* The expected response */
unsigned nr; /* Number of extant responses */
time_t t_valid; /* When this exchange goes bad */
octet hc[MAXHASHSZ]; /* Hash of my challenge */
/*----- Other handy utilities ---------------------------------------------*/
-/* --- @mpstr@ --- *
- *
- * Arguments: @mp *m@ = a multiprecision integer
- *
- * Returns: A pointer to the integer's textual representation.
- *
- * Use: Converts a multiprecision integer to a string. Corrupts
- * @buf_u@.
- */
-
-extern const char *mpstr(mp */*m*/);
-
-/* --- @gestr@ --- *
- *
- * Arguments: @group *g@ = a group
- * @ge *x@ = a group element
- *
- * Returns: A pointer to the element's textual representation.
- *
- * Use: Converts a group element to a string. Corrupts
- * @buf_u@.
- */
-
-extern const char *gestr(group */*g*/, ge */*x*/);
-
/* --- @timestr@ --- *
*
* Arguments: @time_t t@ = a time to convert