/* -*-c-*-
*
- * $Id: keyexch.c,v 1.5 2002/01/13 14:54:40 mdw Exp $
+ * $Id: keyexch.c,v 1.10 2003/10/15 09:29:38 mdw Exp $
*
* Key exchange protocol
*
/*----- Revision history --------------------------------------------------*
*
* $Log: keyexch.c,v $
+ * Revision 1.10 2003/10/15 09:29:38 mdw
+ * Cosmetic fix to changelog comment.
+ *
+ * Revision 1.9 2003/07/13 11:53:14 mdw
+ * Add protocol commentary.
+ *
+ * Revision 1.8 2003/07/13 11:19:49 mdw
+ * Incompatible protocol fix! Include message type code under MAC tag to
+ * prevent cut-and-paste from key-exchange messages to general packet
+ * transport.
+ *
+ * Revision 1.7 2003/05/17 11:01:28 mdw
+ * Handle flags on challenge timers correctly to prevent confusing the event
+ * list.
+ *
+ * Revision 1.6 2003/04/06 10:26:35 mdw
+ * Report peer name on decrypt errors.
+ *
* Revision 1.5 2002/01/13 14:54:40 mdw
* Patch up zero-knowledge property by passing an encrypted log with a
* challenge, so that the prover can verify that the challenge is good.
#include "tripe.h"
+/*----- Brief protocol overview -------------------------------------------*
+ *
+ * Let %$G$% be a cyclic group; let %$g$% be a generator of %$G$%, and let
+ * %$q$% be the order of %$G$%; for a key %$K$%, let %$E_K(\cdot)$% denote
+ * application of the symmetric packet protocol to a message; let
+ * %$H(\cdot)$% be the random oracle. Let $\alpha \inr \{0,\ldots,q - 1\}$%
+ * be Alice's private key; let %$a = g^\alpha$% be her public key; let %$b$%
+ * be Bob's public key.
+ *
+ * At the beginning of the session, Alice chooses
+ *
+ * %$\rho_A \inr \{0, \ldots q - 1\}$%
+ *
+ * We also have:
+ *
+ * %$r_A = g^{\rho_A}$% Alice's challenge
+ * %$c_A = H(\cookie{cookie}, r_A)$% Alice's cookie
+ * %$v_A = \rho_A \xor H(\cookie{expected-reply}, r_A, r_B, b^{\rho_A})$%
+ * Alice's challenge check value
+ * %$r_B^\alpha = a^{\rho_B}$% Alice's reply
+ * %$K = r_B^{\rho_A} = r_B^{\rho_A} = g^{\rho_A\rho_B}$%
+ * Alice and Bob's shared secret key
+ * %$w_A = H(\cookie{switch-request}, c_A, c_B)$%
+ * Alice's switch request value
+ * %$u_A = H(\cookie{switch-confirm}, c_A, c_B)$%
+ * Alice's switch confirm value
+ *
+ * The messages are then:
+ *
+ * %$\cookie{kx-pre-challenge}, r_A$%
+ * Initial greeting. In state @KXS_CHAL@.
+ *
+ * %$\cookie{kx-cookie}, r_A, c_B$%
+ * My table is full but I got your message.
+ *
+ * %$\cookie{kx-challenge}, r_A, c_B, v_A$%
+ * Here's a full challenge for you to answer.
+ *
+ * %$\cookie{kx-reply}, c_A, c_B, v_A, E_K(r_B^\alpha))$%
+ * Challenge accpeted: here's the answer. Commit to my challenge. Move
+ * to @KXS_COMMIT@.
+ *
+ * %$\cookie{kx-switch}, c_A, c_B, E_K(r_B^\alpha, w_A))$%
+ * Reply received: here's my reply. Committed; send data; move to
+ * @KXS_SWITCH@.
+ *
+ * %$\cookie{kx-switch-ok}, E_K(u_A))$%
+ * Switch received. Committed; send data; move to @KXS_SWITCH@.
+ */
+
/*----- Tunable parameters ------------------------------------------------*/
-#define T_VALID MIN(2)
-#define T_RETRY SEC(10)
+#define T_VALID MIN(2) /* Challenge validity period */
+#define T_RETRY SEC(10) /* Challenge retransmit interval */
#define ISVALID(kx, now) ((now) < (kx)->t_valid)
{
if (kxc->f & KXF_TIMER)
sel_rmtimer(&kxc->t);
+ kxc->f &= ~KXF_TIMER;
}
/* --- @kxc_new@ --- *
buf_init(&bb, buf_i, sizeof(buf_i));
buf_putmp(&bb, kxc->r);
buf_flip(&bb);
- ks_encrypt(kxc->ks, &bb, b);
+ ks_encrypt(kxc->ks, MSG_KEYEXCH | KX_REPLY, &bb, b);
}
/* --- Update the statistics --- */
buf_putmp(b, r); z = BLEN(b);
assert(BOK(b));
- kxc->ks = ks_gen(BBASE(b), x, y, z);
+ kxc->ks = ks_gen(BBASE(b), x, y, z, kx->p);
mp_drop(r);
}
buf_putmp(&bb, kxc->r);
buf_put(&bb, kxc->hswrq_out, HASHSZ);
buf_flip(&bb);
- ks_encrypt(kxc->ks, &bb, b);
+ ks_encrypt(kxc->ks, MSG_KEYEXCH | KX_SWITCH, &bb, b);
break;
case KXS_SWITCH:
T( trace(T_KEYEXCH, "keyexch: sending switch confirmation to `%s'",
buf_init(&bb, buf_i, sizeof(buf_i));
buf_put(&bb, kxc->hswok_out, HASHSZ);
buf_flip(&bb);
- ks_encrypt(kxc->ks, &bb, b);
+ ks_encrypt(kxc->ks, MSG_KEYEXCH | KX_SWITCHOK, &bb, b);
break;
default:
abort();
/* --- @matchreply@ --- *
*
* Arguments: @keyexch *kx@ = pointer to key exchange context
+ * @unsigned ty@ = type of incoming message
* @const octet *hc_in@ = a hash of his challenge
* @const octet *hc_out@ = a hash of my challenge (cookie)
* @mp *ck@ = his expected-reply hash (optional)
* challenge is returned.
*/
-static kxchal *matchreply(keyexch *kx, const octet *hc_in,
+static kxchal *matchreply(keyexch *kx, unsigned ty, const octet *hc_in,
const octet *hc_out, mp *ck, buf *b)
{
kxchal *kxc;
/* --- Decrypt the rest of the packet --- */
buf_init(&bb, buf_o, sizeof(buf_o));
- if (ks_decrypt(kxc->ks, b, &bb)) {
+ if (ks_decrypt(kxc->ks, ty, b, &bb)) {
a_warn("failed to decrypt reply from `%s'", p_name(kx->p));
goto bad;
}
a_warn("invalid reply packet from `%s'", p_name(kx->p));
goto bad;
}
- if ((kxc = matchreply(kx, hc_in, hc_out, ck, b)) == 0)
+ if ((kxc = matchreply(kx, MSG_KEYEXCH | KX_REPLY,
+ hc_in, hc_out, ck, b)) == 0)
goto bad;
if (BLEFT(b)) {
a_warn("invalid reply packet from `%s'", p_name(kx->p));
a_warn("invalid switch request from `%s'", p_name(kx->p));
goto bad;
}
- if ((kxc = matchreply(kx, hc_in, hc_out, 0, b)) == 0)
+ if ((kxc = matchreply(kx, MSG_KEYEXCH | KX_SWITCH,
+ hc_in, hc_out, 0, b)) == 0)
goto bad;
if ((hswrq = buf_get(b, HASHSZ)) == 0 || BLEFT(b)) {
a_warn("invalid switch request from `%s'", p_name(kx->p));
}
kxc = kx->r[0];
buf_init(&bb, buf_o, sizeof(buf_o));
- if (ks_decrypt(kxc->ks, b, &bb)) {
+ if (ks_decrypt(kxc->ks, MSG_KEYEXCH | KX_SWITCHOK, b, &bb)) {
a_warn("failed to decrypt switch confirmation from `%s'", p_name(kx->p));
goto bad;
}