Found in crybaby's working tree.
[udpkey] / udpkey.c
index 769bdb1..6d37dc3 100644 (file)
--- a/udpkey.c
+++ b/udpkey.c
@@ -414,9 +414,9 @@ static void kfupdate(void)
  * derivation.  The group elements U and Z are the cryptographic inputs
  * for the derivation.
  *
- * Basically all we do is compute H(what || U || Z).
+ * Basically all we do is compute H(what || U || V || Z).
  */
-static int derive(struct kinfo *k, ge *U, ge *Z,
+static int derive(struct kinfo *k, ge *U, ge *V, ge *Z,
                  const char *what, const char *name, const octet *ksz,
                  octet **kk, size_t *n)
 {
@@ -436,7 +436,8 @@ static int derive(struct kinfo *k, ge *U, ge *Z,
   buf_init(&b, obuf, sizeof(obuf));
   buf_put(&b, "udpkey-", 7);
   buf_putstrz(&b, what);
-  G_TORAW(k->g, &b, U);
+  if (U) G_TORAW(k->g, &b, U);
+  if (V) G_TORAW(k->g, &b, V);
   G_TORAW(k->g, &b, Z);
   if (BBAD(&b)) {
     complain(LOG_ERR, "overflow while deriving key (prepare preimage)!");
@@ -469,6 +470,67 @@ static void debug_ge(const char *what, group *g, ge *X)
 }
 #endif
 
+/* Derive the symmetric keys for Diffie--Hellman-based symmetric crypto,
+ * given (optional) sender public key U, (optional) recipient public key V,
+ * and secret Z, constructing cipher and mac objects for the actual work.
+ *
+ * Your protocol must be consistent about whether it sends U and/or V: the
+ * formatting is ambiguous if you sometimes use one and sometimes the other.
+ */
+static void dh_derive_keys(struct kinfo *k, ge *U, ge *V, ge *Z,
+                          gcipher **cc, gmac **mm)
+{
+  octet *kk;
+  size_t ksz;
+
+  derive(k, U, V, Z, "cipher", k->cc->name, k->cc->keysz, &kk, &ksz);
+  *cc = GC_INIT(k->cc, kk, ksz);
+  derive(k, U, V, Z, "mac", k->mc->name, k->mc->keysz, &kk, &ksz);
+  *mm = GM_KEY(k->mc, kk, ksz);
+}
+
+/* Prepare for an IES encryption.  Given a public key X, prepare a clue R and
+ * secret Z for later.
+ */
+static void ies_enc_prepare(struct kinfo *k, ge *X, ge *R, ge *Z)
+{
+  mp *r = mprand_range(MP_NEW, k->g->r, &rand_global, 0);
+  G_EXP(k->g, R, k->g->g, r);
+  G_EXP(k->g, Z, X, r);
+  D( debug_mp("r", r); debug_ge("R", k.g, R); )
+  MP_DROP(r);
+}
+
+/* Actually perform an IES encryption.  Given a prepared clue R and secret Z,
+ * and an SZ-byte plaintext message P, write the ciphertext to B.  Note that
+ * the clue itself is /not/ written to B: you must do that yourself.
+ */
+static int ies_enc_perform(struct kinfo *k, buf *b, ge *R, ge *Z,
+                          const void *p, size_t sz)
+{
+  octet *t, *tt, *ct;
+  gcipher *c;
+  gmac *m;
+  ghash *h;
+
+  if ((t = buf_get(b, k->tagsz)) == 0 ||
+      (ct = buf_get(b, sz)) == 0) {
+    complain(LOG_ERR, "overflow while writing ciphertext");
+    return (-1);
+  }
+
+  dh_derive_keys(k, R, 0, Z, &c, &m);
+  GC_ENCRYPT(c, p, ct, sz);
+  h = GM_INIT(m);
+  GH_HASH(h, ct, sz);
+  tt = GH_DONE(h, 0);
+  memcpy(t, tt, k->tagsz);
+
+  GC_DESTROY(c);
+  GM_DESTROY(m);
+  GH_DESTROY(h);
+}
+
 /*----- Protocol summary --------------------------------------------------*
  *
  * There are two protocol versions.  The original version works as follows.
@@ -479,41 +541,41 @@ static void debug_ge(const char *what, group *g, ge *X)
  *
  *   * 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
+ *     ge              W       masked IES clue: W = R - Y = r P - v U
+ *     iesct[*](H(R, r X))
+ *       mem[*]        S       secret
  *
  * The new version provides forward secrecy, which involves additional flows.
  *
- *   * Greeting
+ *   * Request
  *     u8              0       marker byte for new protocol
  *     u8              1       packet type
  *     mem8            KEYTAG  wanted secret tag
+ *     ge              U       public vector: U = u P
  *
  *   * Challenge
  *     u8              17      packet type
- *     u32             REF     server's reference
- *     ge              R       public DLIES vector: R = r P
- *     ge              W       masked DH vector: W = V - Y = v P - r X
+ *     ge              R       IES clue
+ *     iesct[*](H(R, r X))
+ *       ge            U       client's public vector (confirms receipt)
+ *       ge            V       public vector: V = v P
+ *       mem[*]        a       nonce
  *
  *   * Response
  *     u8              0       marker byte for new protocol
  *     u8              2       packet type
- *     mem8            KEYTAG  wanted secret tag
- *     u32             REF     reference from challenge
- *     ge              U       public DH vector
- *     mem[HASHSZ]     H0      hash; H0||H1 = H(U, V, Z), where Z = v U
+ *     mem[*]          a       server's nonce (confirm receipt of V)
  *
- *   * Reply
+ *   * Answer
  *     u8              18      packet type
- *     mem[TAGSZ]      TAG     MAC tag on ciphertext
- *     mem[KSZ]        CT      secret, encrypted with H1
+ *     iesct[*](H(U, V, Z))
+ *       mem[*]        S       secret
  */
 
-#define FWS_GREET 0x01
-#define FWS_CHALL 0x11
+#define FWS_REQ 0x01
+#define FWS_CHAL 0x11
 #define FWS_RESP 0x02
-#define FWS_REPLY 0x12
+#define FWS_ANS 0x12
 
 /*----- Listening for requests --------------------------------------------*/
 
@@ -542,6 +604,8 @@ static time_t now;
  * Secrets are kept in a linked list, ordered by expiry time.  At any given
  * time there is at most one unexpired secret (because we only make a new one
  * when the old one expires).
+ *
+ * The nonce has the form ...
  */
 
 struct secret {
@@ -694,16 +758,11 @@ done:
 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;
+  mp *v = MP_NEW;
+  struct kinfo k;
   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. */
@@ -750,32 +809,16 @@ static int respond_v0(buf *bin, buf *bout, struct sockaddr_in *sin)
    * 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);
+  ies_enc_prepare(&k, k.X, R, Z);
   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. */
+  /* Write the ciphertext. */
   rc = 0;
   if (G_TOBUF(k.g, bout, V) ||
-      G_TOBUF(k.g, bout, W))
+      G_TOBUF(k.g, bout, W) ||
+      ies_enc_perform(&k, bout, R, Z, ky->k->u.k.k, ky->k->u.k.sz))
     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. */
@@ -785,10 +828,6 @@ done:
   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);
@@ -1010,9 +1049,9 @@ static int receive_v0(struct query *q, struct server *s, buf *bin, buf *bout)
   D( debug_ge("R", s->k.g, R);
      debug_ge("Y", s->k.g, Y);
      debug_ge("Z", s->k.g, Z); )
-  derive(&s->k, R, Z, "cipher", s->k.cc->name, s->k.cc->keysz, &kk, &ksz);
+  derive(&s->k, R, 0, Z, "cipher", s->k.cc->name, s->k.cc->keysz, &kk, &ksz);
   c = GC_INIT(s->k.cc, kk, ksz);
-  derive(&s->k, R, Z, "mac", s->k.mc->name, s->k.mc->keysz, &kk, &ksz);
+  derive(&s->k, R, 0, Z, "mac", s->k.mc->name, s->k.mc->keysz, &kk, &ksz);
   m = GM_KEY(s->k.mc, kk, ksz);
 
   /* Find where the MAC tag is. */