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