udpkey.c: Refactor protocol reply logic.
authorMark Wooding <mdw@distorted.org.uk>
Wed, 18 Jun 2014 20:52:35 +0000 (21:52 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Thu, 19 Jun 2014 08:54:04 +0000 (09:54 +0100)
Also add a protocol summary comment.

udpkey.c

index ca337cc..b3dca9b 100644 (file)
--- a/udpkey.c
+++ b/udpkey.c
@@ -469,6 +469,19 @@ static void debug_ge(const char *what, group *g, ge *X)
 }
 #endif
 
+/*----- Protocol summary --------------------------------------------------*
+ *
+ *   * Request
+ *     memz            KEYTAG  tag of wanted secret
+ *     ge              U       public vector
+ *
+ *   * Response
+ *     ge              V       public vector: V = v P
+ *     ge              W       encrypted clue: W = R - Y = r P - v U
+ *     mem[TAGSZ]      TAG     MAC tag on ciphertext
+ *     mem[KSZ]        CT      secret, encrypted with Z = r X
+ */
+
 /*----- Listening for requests --------------------------------------------*/
 
 /* Rate limiting parameters.
@@ -482,34 +495,220 @@ static void debug_ge(const char *what, group *g, ge *X)
 #define RATE_REFILL 10                 /* Credits per second. */
 #define RATE_CREDIT 1000               /* Initial credit. */
 
+static time_t now;
+
+static int fetch_key(const char *tag, struct sockaddr_in *sin,
+                    key **ky, struct kinfo *k)
+{
+  dstr d = DSTR_INIT, dd = DSTR_INIT;
+  key_data **kkd;
+  char *p, *q;
+  const char *pp;
+  struct in_addr in;
+  int ch, mlen, rc = -1;
+
+  /* Find the key. */
+  kfupdate();
+  if (key_qtag(kf, tag, &d, ky, &kkd)) {
+    complain(LOG_WARNING, "unknown key tag `%s' from %s:%d",
+            tag, inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
+    goto done;
+  }
+
+  /* And make sure that it has the right shape. */
+  if (((*ky)->k->e & KF_ENCMASK) != KENC_BINARY) {
+    complain(LOG_ERR, "key %s is not plain binary data", d.buf);
+    goto done;
+  }
+
+  /* Find the list of clients, and look up the caller's address in the
+   * list.  Entries have the form ADDRESS[/LEN][=TAG] and are separated by
+   * `;'.
+   */
+  if ((pp = key_getattr(kf, *ky, "clients")) == 0) {
+    complain(LOG_WARNING,
+            "key %s requested from %s:%d has no `clients' attribute",
+            d.buf, inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
+    goto done;
+  }
+  dstr_puts(&dd, pp);
+  p = dd.buf;
+  while (*p) {
+    q = p;
+    while (isdigit((unsigned char)*q) || *q == '.') q++;
+    ch = *q; *q++ = 0;
+    if (!inet_aton(p, &in)) goto skip;
+    if (ch != '/')
+      mlen = 32;
+    else {
+      p = q;
+      while (isdigit((unsigned char)*q)) q++;
+      ch = *q; *q++ = 0;
+      mlen = atoi(p);
+    }
+    if (((sin->sin_addr.s_addr ^ in.s_addr) &
+        htonl(0xffffffff << (32 - mlen))) == 0)
+      goto match;
+  skip:
+    if (!ch) break;
+    p = q;
+    while (*p && *p != ';') p++;
+    if (*p) p++;
+  }
+  complain(LOG_WARNING, "access to key %s denied to %s:%d",
+          d.buf, inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
+  goto done;
+
+match:
+  /* Build a tag name for the caller's KEM key, either from the client
+   * match or the source address.
+   */
+  if (ch != '=') {
+    DRESET(&dd);
+    dstr_puts(&dd, "client-");
+    dstr_puts(&dd, inet_ntoa(sin->sin_addr));
+    p = dd.buf;
+  } else {
+    p = q;
+    while (*q && *q != ';') q++;
+    if (*q == ';') *q++ = 0;
+  }
+
+  /* Report the match. */
+  complain(LOG_NOTICE, "client %s:%d (`%s') requests key %s",
+          inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), p, d.buf);
+
+  /* Load the KEM key. */
+  if (loadkey(p, k, 0)) goto done;
+  D( debug_ge("X", k.g, k.X); )
+
+  /* All complete. */
+  rc = 0;
+
+done:
+  /* Clean everything up. */
+  dstr_destroy(&d);
+  dstr_destroy(&dd);
+  if (rc) k_free(k);
+  return (rc);
+}
+
+static int respond_v0(buf *bin, buf *bout, struct sockaddr_in *sin)
+{
+  ge *R = 0, *U = 0, *V = 0, *W = 0, *Y = 0, *Z = 0;
+  mp *r = MP_NEW, *v = MP_NEW;
+  octet *kk, *t, *tt;
+  char *p;
+  size_t sz;
+  ghash *h = 0;
+  gmac *m = 0;
+  gcipher *c = 0;
+  struct kinfo k;
+  key *ky;
+  size_t ksz;
+  int rc = -1;
+
+  /* Clear out the key state. */
+  k_init(&k);
+
+  /* Extract the key tag name. */
+  if ((p = buf_getmemz(bin, &sz)) == 0) {
+    complain(LOG_WARNING, "invalid key tag from %s:%d",
+            inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
+    goto done;
+  }
+
+  /* Find the client's key and check that it's allowed. */
+  if (fetch_key(p, sin, &ky, &k)) goto done;
+
+  /* Read the caller's ephemeral key. */
+  R = G_CREATE(k.g); W = G_CREATE(k.g);
+  U = G_CREATE(k.g); V = G_CREATE(k.g);
+  Y = G_CREATE(k.g); Z = G_CREATE(k.g);
+  if (G_FROMBUF(k.g, bin, U)) {
+    complain(LOG_WARNING, "failed to read ephemeral vector from %s:%d",
+            inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
+    goto done;
+  }
+  D( debug_ge("U", k.g, U); )
+  if (BLEFT(bin)) {
+    complain(LOG_WARNING, "trailing junk in request from %s:%d",
+            inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
+    goto done;
+  }
+
+  /* Ephemeral Diffie--Hellman.  Choose v in GF(q) at random; compute
+   * V = v P and -Y = (-v) U.
+   */
+  v = mprand_range(v, k.g->r, &rand_global, 0);
+  G_EXP(k.g, V, k.g->g, v);
+  D( debug_mp("v", v); debug_ge("V", k.g, V); )
+    v = mp_sub(v, k.g->r, v);
+  G_EXP(k.g, Y, U, v);
+  D( debug_ge("-Y", k.g, Y); )
+
+  /* DLIES.  Choose r in GF(q) at random; compute R = r P and Z = r X.  Mask
+   * the clue R as W = R - Y.  (Doing the subtraction here makes life easier
+   * at the other end, since we can determine -Y by negating v whereas the
+   * recipient must subtract vectors which may be less efficient.)
+   */
+  r = mprand_range(r, k.g->r, &rand_global, 0);
+  G_EXP(k.g, R, k.g->g, r);
+  D( debug_mp("r", r); debug_ge("R", k.g, R); )
+    G_EXP(k.g, Z, k.X, r);
+  G_MUL(k.g, W, R, Y);
+  D( debug_ge("Z", k.g, Z); debug_ge("W", k.g, W); )
+
+  /* Derive encryption and integrity keys. */
+  derive(&k, R, Z, "cipher", k.cc->name, k.cc->keysz, &kk, &ksz);
+  c = GC_INIT(k.cc, kk, ksz);
+  derive(&k, R, Z, "mac", k.mc->name, k.mc->keysz, &kk, &ksz);
+  m = GM_KEY(k.mc, kk, ksz);
+
+  /* Build the ciphertext and compute a MAC tag over it. */
+  rc = 0;
+  if (G_TOBUF(k.g, bout, V) ||
+      G_TOBUF(k.g, bout, W))
+    goto done;
+  if ((t = buf_get(bout, k.tagsz)) == 0) goto done;
+  sz = ky->k->u.k.sz;
+  if (BENSURE(bout, sz)) goto done;
+  GC_ENCRYPT(c, ky->k->u.k.k, BCUR(bout), sz);
+  h = GM_INIT(m);
+  GH_HASH(h, BCUR(bout), sz);
+  tt = GH_DONE(h, 0); memcpy(t, tt, k.tagsz);
+  BSTEP(bout, sz);
+
+done:
+  /* Clear everything up and go home. */
+  if (R) G_DESTROY(k.g, R);
+  if (U) G_DESTROY(k.g, U);
+  if (V) G_DESTROY(k.g, V);
+  if (W) G_DESTROY(k.g, W);
+  if (Y) G_DESTROY(k.g, Y);
+  if (Z) G_DESTROY(k.g, Z);
+  if (c) GC_DESTROY(c);
+  if (m) GM_DESTROY(m);
+  if (h) GH_DESTROY(h);
+  if (r) MP_DROP(r);
+  if (v) MP_DROP(v);
+  k_free(&k);
+  return (rc);
+}
+
 static int dolisten(int argc, char *argv[])
 {
   int sk;
-  char *p, *q, ch;
-  const char *pp;
+  char *p;
   char *aspec;
   ssize_t n;
-  size_t sz;
   fd_set fdin;
   struct sockaddr_in sin;
-  struct in_addr in;
-  int mlen;
   socklen_t len;
   buf bin, bout;
-  dstr d = DSTR_INIT, dd = DSTR_INIT;
   FILE *fp = 0;
-  key *ky;
-  key_data **kkd;
-  mp *r = MP_NEW, *v = MP_NEW;
-  ge *R = 0, *U = 0, *V = 0, *W = 0, *Y = 0, *Z = 0;
-  ghash *h = 0;
-  gmac *m = 0;
-  gcipher *c = 0;
-  octet *kk, *t, *tt;
-  size_t ksz;
-  struct kinfo k;
   unsigned bucket = 0, toks;
-  time_t last = 0, now;
+  time_t last = 0;
 
   /* Set up the socket address. */
   sin.sin_family = AF_INET;
@@ -542,9 +741,6 @@ static int dolisten(int argc, char *argv[])
 
   for (;;) {
 
-    /* Clear out the key state. */
-    k_init(&k);
-
     /* Wait for something to happen. */
     FD_ZERO(&fdin);
     FD_SET(sk, &fdin);
@@ -558,7 +754,7 @@ static int dolisten(int argc, char *argv[])
     if (n < 0) {
       if (errno != EAGAIN && errno != EINTR)
        complain(LOG_ERR, "unexpected receive error: %s", strerror(errno));
-      goto again;
+      continue;
     }
 
     /* Refill the bucket, and see whether we should reject this packet. */
@@ -570,182 +766,32 @@ static int dolisten(int argc, char *argv[])
     last = now;
     if (bucket > RATE_CREDIT &&
        grand_range(&rand_global, bucket - RATE_CREDIT))
-      goto again;
+      continue;
     bucket++;
 
     /* Set up the input buffer for parsing the request. */
     buf_init(&bin, ibuf, n);
-
-    /* Extract the key tag name. */
-    if ((p = buf_getmemz(&bin, &sz)) == 0) {
-      complain(LOG_WARNING, "invalid key tag from %s:%d",
-              inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
-      goto again;
-    }
-
-    /* Find the key. */
-    kfupdate();
-    if (key_qtag(kf, p, &d, &ky, &kkd)) {
-      complain(LOG_WARNING, "unknown key tag `%s' from %s:%d",
-              p, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
-      goto again;
-    }
-
-    /* And make sure that it has the right shape. */
-    if ((ky->k->e & KF_ENCMASK) != KENC_BINARY) {
-      complain(LOG_ERR, "key %s is not plain binary data", d.buf);
-      goto again;
-    }
-
-    /* Find the list of clients, and look up the caller's address in the
-     * list.  Entries have the form ADDRESS[/LEN][=TAG] and are separated by
-     * `;'.
-     */
-    if ((pp = key_getattr(kf, ky, "clients")) == 0) {
-      complain(LOG_WARNING,
-              "key %s requested from %s:%d has no `clients' attribute",
-              d.buf, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
-      goto again;
-    }
-    dstr_puts(&dd, pp);
-    p = dd.buf;
-    while (*p) {
-      q = p;
-      while (isdigit((unsigned char)*q) || *q == '.') q++;
-      ch = *q; *q++ = 0;
-      if (!inet_aton(p, &in)) goto skip;
-      if (ch != '/')
-       mlen = 32;
-      else {
-       p = q;
-       while (isdigit((unsigned char)*q)) q++;
-       ch = *q; *q++ = 0;
-       mlen = atoi(p);
-      }
-      if (((sin.sin_addr.s_addr ^ in.s_addr) &
-          (0xffffffff << (32 - mlen))) == 0)
-       goto match;
-    skip:
-      if (!ch) break;
-      p = q;
-      while (*p && *p != ';') p++;
-      if (*p) p++;
-    }
-    complain(LOG_WARNING, "access to key %s denied to %s:%d",
-            d.buf, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
-    goto again;
-
-  match:
-    /* Build a tag name for the caller's KEM key, either from the client
-     * match or the source address.
-     */
-    if (ch != '=') {
-      DRESET(&dd);
-      dstr_puts(&dd, "client-");
-      dstr_puts(&dd, inet_ntoa(sin.sin_addr));
-      p = dd.buf;
-    } else {
-      p = q;
-      while (*q && *q != ';') q++;
-      if (*q == ';') *q++ = 0;
-    }
-
-    /* Report the match. */
-    complain(LOG_NOTICE, "client %s:%d (`%s') requests key %s",
-            inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), p, d.buf);
-
-    /* Load the KEM key. */
-    if (loadkey(p, &k, 0)) goto again;
-    D( debug_ge("X", k.g, k.X); )
-
-    /* Read the caller's ephemeral key. */
-    R = G_CREATE(k.g); W = G_CREATE(k.g);
-    U = G_CREATE(k.g); V = G_CREATE(k.g);
-    Y = G_CREATE(k.g); Z = G_CREATE(k.g);
-    if (G_FROMBUF(k.g, &bin, U)) {
-      complain(LOG_WARNING, "failed to read ephemeral vector from %s:%d",
-              inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
-      goto again;
-    }
-    D( debug_ge("U", k.g, U); )
-    if (BLEFT(&bin)) {
-      complain(LOG_WARNING, "trailing junk in request from %s:%d",
-              inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
-      goto again;
-    }
-
-    /* Ephemeral Diffie--Hellman.  Choose v in GF(q) at random; compute
-     * V = v P and -Y = (-v) U.
-     */
-    v = mprand_range(v, k.g->r, &rand_global, 0);
-    G_EXP(k.g, V, k.g->g, v);
-    D( debug_mp("v", v); debug_ge("V", k.g, V); )
-    v = mp_sub(v, k.g->r, v);
-    G_EXP(k.g, Y, U, v);
-    D( debug_ge("-Y", k.g, Y); )
-
-    /* DLIES.  Choose r in GF(q) at random; compute R = r P and Z = r X.
-     * Mask the clue R as W = R - Y.  (Doing the subtraction here makes life
-     * easier at the other end, since we can determine -Y by negating v
-     * whereas the recipient must subtract vectors which may be less
-     * efficient.)
-     */
-    r = mprand_range(r, k.g->r, &rand_global, 0);
-    G_EXP(k.g, R, k.g->g, r);
-    D( debug_mp("r", r); debug_ge("R", k.g, R); )
-    G_EXP(k.g, Z, k.X, r);
-    G_MUL(k.g, W, R, Y);
-    D( debug_ge("Z", k.g, Z); debug_ge("W", k.g, W); )
-
-    /* Derive encryption and integrity keys. */
-    derive(&k, R, Z, "cipher", k.cc->name, k.cc->keysz, &kk, &ksz);
-    c = GC_INIT(k.cc, kk, ksz);
-    derive(&k, R, Z, "mac", k.mc->name, k.mc->keysz, &kk, &ksz);
-    m = GM_KEY(k.mc, kk, ksz);
-
-    /* Build the ciphertext and compute a MAC tag over it. */
     buf_init(&bout, obuf, sizeof(obuf));
-    if (G_TOBUF(k.g, &bout, V) ||
-       G_TOBUF(k.g, &bout, W))
-      goto bad;
-    if ((t = buf_get(&bout, k.tagsz)) == 0) goto bad;
-    sz = ky->k->u.k.sz;
-    if (BENSURE(&bout, sz)) goto bad;
-    GC_ENCRYPT(c, ky->k->u.k.k, BCUR(&bout), sz);
-    h = GM_INIT(m);
-    GH_HASH(h, BCUR(&bout), sz);
-    tt = GH_DONE(h, 0); memcpy(t, tt, k.tagsz);
-    BSTEP(&bout, sz);
+
+    /* Handle the client's message. */
+    if (respond_v0(&bin, &bout, &sin)) continue;
 
     /* Send the reply packet back to the caller. */
+    if (!BOK(&bout)) goto bad;
     if (sendto(sk, BBASE(&bout), BLEN(&bout), 0,
               (struct sockaddr *)&sin, len) < 0) {
       complain(LOG_ERR, "failed to send response to %s:%d: %s",
               inet_ntoa(sin.sin_addr), ntohs(sin.sin_port),
               strerror(errno));
-      goto again;
+      continue;
     }
 
-    goto again;
+    continue;
 
   bad:
     /* Report a problem building the reply. */
     complain(LOG_ERR, "failed to construct response to %s:%d",
             inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
-
-  again:
-    /* Free stuff for the next iteration. */
-    DRESET(&d); DRESET(&dd);
-    if (R) { G_DESTROY(k.g, R); R = 0; }
-    if (U) { G_DESTROY(k.g, U); U = 0; }
-    if (V) { G_DESTROY(k.g, V); V = 0; }
-    if (W) { G_DESTROY(k.g, W); W = 0; }
-    if (Y) { G_DESTROY(k.g, Y); Y = 0; }
-    if (Z) { G_DESTROY(k.g, Z); Z = 0; }
-    if (c) { GC_DESTROY(c); c = 0; }
-    if (m) { GM_DESTROY(m); m = 0; }
-    if (h) { GH_DESTROY(h); h = 0; }
-    k_free(&k);
   }
 
   return (-1);