chiark / gitweb /
server/: Replace the Diffie--Hellman group abstraction.
authorMark Wooding <mdw@distorted.org.uk>
Thu, 26 May 2016 08:26:09 +0000 (09:26 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sun, 14 May 2017 17:19:08 +0000 (18:19 +0100)
Downside: this basically involves duplicating Catacomb's `group'
implementation.

Upside: I've been able to improve a few things.  Most notably, this new
abstraction covers scalars as well as group elements, which extends the
possibilities.  Because the new abstraction takes over responsibility
for tracing and reporting, I've been able to make group-specific
improvements.

More subtly, I've also introduced an additional group-element encoding
format.  Previously, there was the `buffer format' (a sequence of
length-prefixed items) and the `raw format' (a binary blob with a known
length, used in encrypted messages).  But there's an additional source
of length leakage for secret values, which is in hashing: so I've
introduced a new `hash format', which currently works the same as
`buffer' for compatibility's sake, but could later be switched -- say,
by a key attribute -- to work like `raw'.

I've also passed the key file and object through to the DH operations,
so that they can pick up additional attributes from the key.  Nothing
takes advantage of this yet, though.

server/Makefile.am
server/admin.c
server/dh.c [new file with mode: 0644]
server/keyexch.c
server/keymgmt.c
server/servutil.c
server/tripe.h

index 5498806..a5cfdc4 100644 (file)
@@ -45,6 +45,7 @@ tripe_SOURCES         += servutil.c
 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
index b12a3be..eb965c1 100644 (file)
@@ -1720,7 +1720,7 @@ static void acmd_algs(admin *a, unsigned ac, char *av[])
 {
   peer *p;
   const kdata *kd;
-  const group *g;
+  const dhgrp *g;
   const algswitch *algs;
 
   if (!ac)
@@ -1729,14 +1729,10 @@ static void acmd_algs(admin *a, unsigned ac, char *av[])
     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,
diff --git a/server/dh.c b/server/dh.c
new file mode 100644 (file)
index 0000000..bbaa123
--- /dev/null
@@ -0,0 +1,634 @@
+/* -*-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 -------------------------------------------------*/
index 0c1ed82..fb0e453 100644 (file)
@@ -97,8 +97,8 @@ static const char *const pkname[] = {
 /* --- @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:    ---
  *
@@ -106,12 +106,12 @@ static const char *const pkname[] = {
  *             @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));
 }
@@ -119,7 +119,8 @@ static void hashge(ghash *h, group *g, ge *x)
 /* --- @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
@@ -127,12 +128,12 @@ static void hashge(ghash *h, group *g, ge *x)
  *
  * 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;
@@ -141,13 +142,13 @@ static void mpmask(buf *b, mp *x, size_t n,
   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);
@@ -155,22 +156,23 @@ static void mpmask(buf *b, mp *x, size_t n,
 
 /* --- @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, {
@@ -178,22 +180,23 @@ static mp *mpunmask(mp *d, const octet *p, size_t n,
     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@)
  *
@@ -201,29 +204,30 @@ static mp *mpunmask(mp *d, const octet *p, size_t n,
  *             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);
@@ -234,7 +238,7 @@ static const octet *hashcheck(keyexch *kx, ge *kpub, ge *cc, ge *c, ge *y)
  *
  * 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:    ---
@@ -242,12 +246,14 @@ static const octet *hashcheck(keyexch *kx, ge *kpub, ge *cc, ge *c, ge *y)
  * 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);
 }
 
@@ -394,10 +400,11 @@ static void rs_reset(retry *rs) { rs->t = 0; }
 
 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);
 }
@@ -459,19 +466,20 @@ static kxchal *kxc_new(keyexch *kx)
 /* --- @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);
@@ -522,15 +530,16 @@ static void kxc_answer(keyexch *kx, kxchal *kxc)
 {
   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);
 
@@ -567,7 +576,8 @@ static void kxc_answer(keyexch *kx, kxchal *kxc)
 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 --- */
@@ -579,11 +589,11 @@ static int doprechallenge(keyexch *kx, buf *b)
 
   /* --- 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 --- */
@@ -591,8 +601,8 @@ static int doprechallenge(keyexch *kx, buf *b)
   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);
@@ -600,11 +610,11 @@ static int doprechallenge(keyexch *kx, buf *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);
 }
 
@@ -622,15 +632,15 @@ bad:
 
 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;
@@ -638,14 +648,14 @@ static kxchal *respond(keyexch *kx, unsigned msg, buf *b)
 
   /* --- 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);
   }))
@@ -665,7 +675,7 @@ static kxchal *respond(keyexch *kx, unsigned msg, buf *b)
    * 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);
@@ -676,32 +686,30 @@ static kxchal *respond(keyexch *kx, unsigned msg, buf *b)
 
     /* --- 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, {
@@ -711,25 +719,25 @@ static kxchal *respond(keyexch *kx, unsigned msg, buf *b)
 
     /* --- 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, {
@@ -746,28 +754,28 @@ static kxchal *respond(keyexch *kx, unsigned msg, buf *b)
     /* --- 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);
 }
 
@@ -818,6 +826,7 @@ static void resend(keyexch *kx)
   buf bb;
   stats *st = p_stats(kx->p);
   struct timeval tv;
+  const dhgrp *g = kx->kpriv->grp;
   buf *b;
 
   switch (kx->s) {
@@ -825,7 +834,7 @@ static void resend(keyexch *kx)
       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'",
@@ -835,7 +844,7 @@ static void resend(keyexch *kx)
       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);
@@ -907,26 +916,26 @@ static int decryptrest(keyexch *kx, kxchal *kxc, unsigned msg, buf *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);
 }
 
@@ -1135,6 +1144,7 @@ bad:
 
 static void stop(keyexch *kx)
 {
+  const dhgrp *g = kx->kpriv->grp;
   unsigned i;
 
   if (kx->f & KXF_DEAD)
@@ -1144,9 +1154,9 @@ static void stop(keyexch *kx)
     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;
@@ -1166,31 +1176,32 @@ static void stop(keyexch *kx)
 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);
     })
@@ -1441,7 +1452,8 @@ void kx_newkeys(keyexch *kx)
 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),
index 2324f15..9e4bc5b 100644 (file)
 
 #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@ --- *
@@ -226,7 +93,7 @@ done:
  *
  * 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.
  *
@@ -236,7 +103,7 @@ done:
  *             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;
 
@@ -266,7 +133,8 @@ int km_samealgsp(const kdata *kdx, const kdata *kdy)
 {
   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));
@@ -276,7 +144,8 @@ int km_samealgsp(const kdata *kdx, const kdata *kdy)
 
 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;
@@ -285,8 +154,10 @@ typedef struct keyhalf {
 
 /* --- @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
@@ -294,10 +165,10 @@ typedef struct keyhalf {
  * 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
@@ -306,45 +177,46 @@ typedef struct keyhalf {
  *             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);
 }
@@ -456,7 +328,8 @@ static kdata *kh_load(keyhalf *kh, const char *tag, int complainp)
   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 --- */
 
@@ -479,16 +352,16 @@ static kdata *kh_load(keyhalf *kh, const char *tag, int complainp)
   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);
@@ -496,7 +369,7 @@ foundko:
   }
 
   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);
@@ -504,7 +377,6 @@ foundko:
   }
 
   kd->tag = xstrdup(t.buf);
-  kd->indexsz = mp_octets(kd->g->r);
   kd->ref = 1;
   kd->kn = 0;
   kd->t_exp = k->exp;
@@ -512,10 +384,11 @@ foundko:
   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);
     })
   })
@@ -523,9 +396,9 @@ foundko:
   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:
@@ -637,7 +510,7 @@ static int kh_refresh(keyhalf *kh)
     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;
     }
@@ -767,10 +640,10 @@ void km_ref(kdata *kd) { kd->ref++; }
 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);
 }
 
index dbca451..f19ce53 100644 (file)
@@ -34,41 +34,6 @@ octet buf_i[PKBUFSZ], buf_o[PKBUFSZ], buf_t[PKBUFSZ], buf_u[PKBUFSZ];
 
 /*----- 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
index 24cec43..d8b35de 100644 (file)
 #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;
@@ -175,6 +191,99 @@ typedef struct bulkchal {
 
 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;
 
@@ -268,17 +377,16 @@ struct algswitch {
   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 */
@@ -292,6 +400,7 @@ typedef struct knode {
 
 #define HASH_STRING(h, s) GH_HASH((h), (s), sizeof(s))
 
+extern const dhops dhtab[];
 extern const bulkops bulktab[];
 
 /*----- Data structures ---------------------------------------------------*/
@@ -381,8 +490,8 @@ typedef struct retry {
 
 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 */
@@ -404,9 +513,9 @@ typedef struct keyexch {
   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 */
@@ -1596,31 +1705,6 @@ extern const tunnel_ops tun_slip;
 
 /*----- 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