Support elliptic curves, and bigger hashes.
[become] / src / check.c
index c899196..f6560d5 100644 (file)
@@ -1,6 +1,6 @@
 /* -*-c-*-
  *
- * $Id: check.c,v 1.13 2004/04/08 01:36:20 mdw Exp $
+ * $Id: check.c,v 1.14 2004/04/17 10:46:08 mdw Exp $
  *
  * Check validity of requests
  *
 /* --- Catacomb headers --- */
 
 #include <catacomb/buf.h>
-#include <catacomb/dsa.h>
+#include <catacomb/gdsa.h>
 #include <catacomb/key.h>
+#include <catacomb/dh.h>
+#include <catacomb/ec-keys.h>
 #include <catacomb/mp.h>
 #include <catacomb/noise.h>
 #include <catacomb/rand.h>
-#include <catacomb/sha.h>
 
 /* --- Local headers --- */
 
@@ -138,13 +139,11 @@ static int check__ask(request *rq, struct sockaddr_in *serv, size_t n_serv)
 
   char buff[2048], rbuff[2048];
   size_t rqlen;
-  octet hmsg[SHA_HASHSZ];
-  octet h[SHA_HASHSZ];
-  key_packstruct kps[DSA_PUBFETCHSZ];
+  gdsa g;
+  const char *p;
+  ghash *h;
   key_packdef *kp;
-  dsa_pub kpub;
   buf b;
-  sha_ctx hc;
   int fd;
   struct sockaddr_in sin;
   socklen_t slen;
@@ -152,7 +151,7 @@ static int check__ask(request *rq, struct sockaddr_in *serv, size_t n_serv)
   int ans;
   fd_set fds;
   struct timeval start, now, tv;
-  mp *m, *r, *s;
+  gdsa_sig s;
   key_file f;
   key *k;
   key_iter ki;
@@ -163,22 +162,18 @@ static int check__ask(request *rq, struct sockaddr_in *serv, size_t n_serv)
 
   if ((key_open(&f, file_PUBKEY, KOPEN_READ, key_moan, 0)) != 0)
     die(1, "couldn't open public keyring");
-  kp = key_fetchinit(dsa_pubfetch, kps, &kpub);
 
   /* --- Build the request packet --- */
 
   rand_noisesrc(RAND_GLOBAL, &noise_source);
   rand_seed(RAND_GLOBAL, 160);
   buf_init(&b, buff, sizeof(buff));
-  rand_get(RAND_GLOBAL, buf_get(&b, SHA_HASHSZ), SHA_HASHSZ);
+  rand_get(RAND_GLOBAL, buf_get(&b, 64), 64);
   buf_putu32(&b, rq->from);
   buf_putu32(&b, rq->to);
   buf_putu16(&b, strlen(rq->cmd));
   buf_put(&b, rq->cmd, strlen(rq->cmd));
   rqlen = BLEN(&b);
-  sha_init(&hc);
-  sha_hash(&hc, buff, rqlen);
-  sha_done(&hc, hmsg);
 
   /* --- Create my socket --- */
 
@@ -278,39 +273,93 @@ static int check__ask(request *rq, struct sockaddr_in *serv, size_t n_serv)
       continue;
     }
 
-    /* --- Unpack and verify the response --- */
-
-    buf_init(&b, rbuff, sz);
-    if (buf_ensure(&b, sizeof(hmsg))) goto bad;
-    if (memcmp(BCUR(&b), hmsg, sizeof(hmsg)) != 0) goto bad;
-    BSTEP(&b, sizeof(hmsg));
-    if ((ans = buf_getbyte(&b)) < 0) goto bad;
-
-    sha_init(&hc);
-    sha_hash(&hc, BBASE(&b), BLEN(&b));
-    sha_done(&hc, h);
-    if ((r = buf_getmp(&b)) == 0 || (s = buf_getmp(&b)) == 0) goto bad;
-    m = mp_loadb(MP_NEW, h, sizeof(h));
+    /* --- The hash length varies with the key --- *
+     *
+     * So we have to unpack once for each key.  This isn't too bad.
+     */
 
+    g.r = &rand_global;
+    g.u = 0;
     key_mkiter(&ki, &f);
     while ((k = key_next(&ki)) != 0) {
       if (key_expired(k)) continue;
-      if (strcmp(k->type, "become-dsa") != 0) continue;
-      if (key_fetch(kp, k)) continue;
-      i = dsa_vrfy(&kpub.dp, kpub.y, m, r, s);
-      dsa_pubfree(&kpub);
-      if (i) {
-       key_fetchdone(kp);
-       key_close(&f);
-       close(fd);
-       mp_drop(m);
-       mp_drop(r);
-       mp_drop(s);
-       return (ans);
-      }
+      if (strcmp(k->type, "become") != 0) continue;
+
+      /* --- Get a hash function --- */
+
+      if ((p = key_getattr(&f, k, "hash")) == 0)
+       p = "sha";
+      if ((g.h = ghash_byname(p)) == 0)
+       continue;
+
+      /* --- Unpack the key --- */
+
+      p = key_getattr(&f, k, "sig");
+      if (!p || strcmp(p, "dsa") == 0) {
+       dh_pub dp;
+       kp = key_fetchinit(dh_pubfetch, 0, &dp);
+       if (key_fetch(kp, k)) goto fail_1;
+       if ((g.g = group_prime(&dp.dp)) == 0) goto fail_1;
+       g.p = G_CREATE(g.g);
+       if (G_FROMINT(g.g, g.p, dp.y)) goto fail_2;
+      } else if (strcmp(p, "ecdsa") == 0) {
+       ec_pub ep;
+       ec_info ei;
+       kp = key_fetchinit(ec_pubfetch, 0, &ep);
+       if (key_fetch(kp, k)) goto fail_1;
+       if (ec_getinfo(&ei, ep.cstr)) goto fail_1;
+       g.g = group_ec(&ei);
+       g.p = G_CREATE(g.g);
+       if (G_FROMEC(g.g, g.p, &ep.p)) goto fail_2;
+      } else
+       goto fail_0;
+
+      /* --- Unpack the response --- */
+
+      h = GH_INIT(g.h);
+      GH_HASH(h, buff, rqlen);
+      buf_init(&b, rbuff, sz);
+      if (buf_ensure(&b, g.h->hashsz)) goto fail_3;
+      if (memcmp(BCUR(&b), GH_DONE(h, 0), g.h->hashsz) != 0) goto fail_3;
+      BSTEP(&b, g.h->hashsz);
+      if ((ans = buf_getbyte(&b)) < 0) goto fail_3;
+      GH_DESTROY(h);
+
+      /* --- Verify the signature --- */
+
+      h = gdsa_beginhash(&g);
+      GH_HASH(h, BBASE(&b), BLEN(&b));
+      gdsa_endhash(&g, h);
+      s.r = s.s = 0;
+      if ((s.r = buf_getmp(&b)) == 0 ||
+         (s.s = buf_getmp(&b)) == 0)
+       goto fail_4;
+      if (gdsa_verify(&g, &s, GH_DONE(h, 0)))
+       goto fail_4;
+
+      mp_drop(s.r); mp_drop(s.s);
+      GH_DESTROY(h);
+      G_DESTROY(g.g, g.p);
+      G_DESTROYGROUP(g.g);
+      key_fetchdone(kp);
+      key_close(&f);
+      return (ans);
+
+      /* --- Tidy up and try again --- */
+
+    fail_4:
+      mp_drop(s.r); mp_drop(s.s);
+    fail_3:
+      GH_DESTROY(h);
+      G_DESTROY(g.g, g.p);
+    fail_2:
+      G_DESTROYGROUP(g.g);
+    fail_1:
+      key_fetchdone(kp);
+    fail_0:
+      continue;
     }
     
-  bad:
     T( trace(TRACE_CLIENT,
             "client: invalid or corrupt reply packet"); )
   }