/* -*-c-*-
*
- * $Id: keyexch.c,v 1.4 2001/06/22 19:40:36 mdw Exp $
+ * $Id$
*
* Key exchange protocol
*
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-/*----- Revision history --------------------------------------------------*
+/*----- Header files ------------------------------------------------------*/
+
+#include "tripe.h"
+
+/*----- Brief protocol overview -------------------------------------------*
*
- * $Log: keyexch.c,v $
- * Revision 1.4 2001/06/22 19:40:36 mdw
- * Support expiry of other peers' public keys.
+ * 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.
*
- * Revision 1.3 2001/06/19 22:07:09 mdw
- * Cosmetic fixes.
+ * At the beginning of the session, Alice chooses
*
- * Revision 1.2 2001/02/16 21:24:27 mdw
- * Rewrite for new key exchange protocol.
+ * %$\rho_A \inr \{0, \ldots q - 1\}$%
*
- * Revision 1.1 2001/02/03 20:26:37 mdw
- * Initial checkin.
+ * 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-rq}, 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@.
+ */
-/*----- Header files ------------------------------------------------------*/
+/*----- Tunable parameters ------------------------------------------------*/
-#include "tripe.h"
+#define T_VALID MIN(2) /* Challenge validity period */
+#define T_RETRY SEC(10) /* Challenge retransmit interval */
-/*----- Tunable parameters ------------------------------------------------*/
+#define VALIDP(kx, now) ((now) < (kx)->t_valid)
-#define T_VALID MIN(2)
-#define T_RETRY SEC(10)
+/*----- Static tables -----------------------------------------------------*/
-#define ISVALID(kx, now) ((now) < (kx)->t_valid)
+static const char *const pkname[] = {
+ "pre-challenge", "cookie", "challenge",
+ "reply", "switch-rq", "switch-ok"
+};
/*----- Various utilities -------------------------------------------------*/
-/* --- @hashmp@ --- *
+/* --- @hashge@ --- *
*
- * Arguments: @HASH_CTX *r@ = pointer to hash context
- * @mp *m@ = pointer to multiprecision integer
+ * Arguments: @ghash *h@ = pointer to hash context
+ * @ge *x@ = pointer to group element
*
* Returns: ---
*
- * Use: Adds the hash of a multiprecision integer to the context.
- * Corrupts @buf_t@.
+ * Use: Adds the hash of a group element to the context. Corrupts
+ * @buf_t@.
*/
-static void hashmp(HASH_CTX *r, mp *m)
+static void hashge(ghash *h, ge *x)
{
buf b;
buf_init(&b, buf_t, sizeof(buf_t));
- buf_putmp(&b, m);
+ G_TOBUF(gg, &b, x);
assert(BOK(&b));
- HASH(r, BBASE(&b), BLEN(&b));
+ GH_HASH(h, BBASE(&b), BLEN(&b));
+}
+
+/* --- @mpencrypt@, @mpdecrypt@ --- *
+ *
+ * Arguments: @mp *d@ = the destination integer
+ * @mp *x@ = the plaintext/ciphertext integer
+ * @size_t sz@ = the expected size of the plaintext
+ * @const octet *k@ = pointer to key material
+ *
+ * Returns: The encrypted/decrypted integer.
+ *
+ * Use: Encrypts (or decrypts) a multiprecision integer. In fact,
+ * the title is a bit of a misnomer: we actually compute
+ * %$x \xor H(k)$%, so it's a random oracle thing rather than an
+ * encryption thing.
+ */
+
+static mp *mpencrypt(mp *d, mp *x, size_t sz, const octet *k)
+{
+ gcipher *mgf;
+
+ mgf = GC_INIT(algs.mgf, k, algs.hashsz);
+ mp_storeb(x, buf_t, sz);
+ GC_ENCRYPT(mgf, buf_t, buf_t, sz);
+ GC_DESTROY(mgf);
+ return (mp_loadb(d, buf_t, sz));
+}
+
+static mp *mpdecrypt(mp *d, mp *x, size_t sz, const octet *k)
+{
+ gcipher *mgf;
+
+ mgf = GC_INIT(algs.mgf, k, algs.hashsz);
+ mp_storeb(x, buf_t, sz);
+ GC_DECRYPT(mgf, buf_t, buf_t, sz);
+ GC_DESTROY(mgf);
+ return (mp_loadb(d, buf_t, sz));
}
/* --- @timer@ --- *
keyexch *kx = v;
kx->f &= ~KXF_TIMER;
T( trace(T_KEYEXCH, "keyexch: timer has popped"); )
- kx_start(kx);
+ kx_start(kx, 0);
}
/* --- @settimer@ --- *
{
if (kxc->f & KXF_TIMER)
sel_rmtimer(&kxc->t);
- mp_drop(kxc->c);
- mp_drop(kxc->r);
+ G_DESTROY(gg, kxc->c);
+ if (kxc->r) G_DESTROY(gg, kxc->r);
+ mp_drop(kxc->ck);
ks_drop(kxc->ks);
DESTROY(kxc);
}
{
if (kxc->f & KXF_TIMER)
sel_rmtimer(&kxc->t);
+ kxc->f &= ~KXF_TIMER;
}
/* --- @kxc_new@ --- *
/* --- Fill in the new structure --- */
kxc = CREATE(kxchal);
- kxc->c = 0;
+ kxc->c = G_CREATE(gg);
kxc->r = 0;
+ kxc->ck = MP_NEW;
kxc->ks = 0;
kxc->kx = kx;
kxc->f = 0;
/* --- @kxc_bychal@ --- *
*
* Arguments: @keyexch *kx@ = pointer to key exchange block
- * @mp *c@ = challenge from remote host
+ * @ge *c@ = challenge from remote host
*
* Returns: Pointer to the challenge block, or null.
*
* Use: Finds a challenge block, given its challenge.
*/
-static kxchal *kxc_bychal(keyexch *kx, mp *c)
+static kxchal *kxc_bychal(keyexch *kx, ge *c)
{
unsigned i;
for (i = 0; i < kx->nr; i++) {
- if (MP_EQ(c, kx->r[i]->c))
+ if (G_EQ(gg, c, kx->r[i]->c))
return (kx->r[i]);
}
return (0);
unsigned i;
for (i = 0; i < kx->nr; i++) {
- if (memcmp(hc, kx->r[i]->hc, HASHSZ) == 0)
+ if (memcmp(hc, kx->r[i]->hc, algs.hashsz) == 0)
return (kx->r[i]);
}
return (0);
/* --- Build the reply packet --- */
if (!kxc->r)
- buf_putmp(b, kx->c);
+ G_TOBUF(gg, b, kx->c);
else
- buf_put(b, kx->hc, HASHSZ);
- buf_put(b, kxc->hc, HASHSZ);
- buf_put(b, kxc->hrx, HASHSZ);
+ buf_put(b, kx->hc, algs.hashsz);
+ buf_put(b, kxc->hc, algs.hashsz);
+ buf_putmp(b, kxc->ck);
/* --- Maybe send an actual reply, if we have one --- */
} else {
T( trace(T_KEYEXCH, "keyexch: sending reply to `%s'", p_name(kx->p)); )
buf_init(&bb, buf_i, sizeof(buf_i));
- buf_putmp(&bb, kxc->r);
+ G_TOBUF(gg, &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 --- */
/* --- @getreply@ --- *
*
* Arguments: @keyexch *kx@ = pointer to key exchange context
- * @mp *c@ = a challenge
- * @const octet *hrx@ = the supplied expected-reply hash
+ * @ge *c@ = a challenge
+ * @mp *ck@ = the supplied expected-reply check value
*
* Returns: A pointer to the reply, or null if the reply-hash was wrong.
*
* Use: Computes replies to challenges.
*/
-static mp *getreply(keyexch *kx, mp *c, const octet *hrx)
+static ge *getreply(keyexch *kx, ge *c, mp *ck)
{
- mp *r = mpmont_exp(&mg, MP_NEW, c, kpriv.x);
- HASH_CTX h;
- octet buf[HASHSZ];
-
- HASH_INIT(&h);
- HASH_STRING(&h, "tripe-expected-reply");
- hashmp(&h, c);
- hashmp(&h, kx->c);
- hashmp(&h, r);
- HASH_DONE(&h, buf);
+ ge *r = G_CREATE(gg);
+ ge *y = G_CREATE(gg);
+ mp *a = MP_NEW;
+ ghash *h;
+ const octet *hh;
+ int ok;
+
+ G_EXP(gg, r, c, kpriv);
+ h = GH_INIT(algs.h);
+ HASH_STRING(h, "tripe-expected-reply");
+ hashge(h, c);
+ hashge(h, kx->c);
+ hashge(h, r);
+ hh = GH_DONE(h, 0);
+
+ a = mpdecrypt(MP_NEW, ck, mp_octets(gg->r), hh);
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
- trace(T_CRYPTO, "crypto: computed reply = %s", mpstr(r));
- trace_block(T_CRYPTO, "crypto: computed reply hash", buf, HASHSZ);
+ trace(T_CRYPTO, "crypto: computed reply = %s", gestr(gg, r));
+ trace_block(T_CRYPTO, "crypto: computed reply hash", hh, algs.hashsz);
+ trace(T_CRYPTO, "crypto: recovered log = %s", mpstr(a));
}))
- if (memcmp(buf, hrx, HASHSZ) != 0) {
- a_warn("invalid expected-reply hash from `%s'", p_name(kx->p));
- mp_drop(r);
- return (0);
+ GH_DESTROY(h);
+ G_EXP(gg, y, gg->g, a);
+ ok = G_EQ(gg, y, c);
+ if (!ok) {
+ a_warn("KX", "?PEER", kx->p, "bad-expected-reply-log", A_END);
+ IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
+ trace(T_CRYPTO, "crypto: computed challenge = %s", gestr(gg, y));
+ }))
+ G_DESTROY(gg, r);
+ r = 0;
}
+ mp_drop(a);
+ G_DESTROY(gg, y);
return (r);
}
static int dochallenge(keyexch *kx, unsigned msg, buf *b)
{
- mp *c = 0;
- const octet *hc = 0, *hrx = 0;
+ ge *c = G_CREATE(gg);
+ mp *ck = MP_NEW;
+ const octet *hc = 0;
kxchal *kxc;
- HASH_CTX h;
+ ghash *h;
/* --- Ensure that we're in a sensible state --- */
if (kx->s != KXS_CHAL) {
- a_warn("unexpected challenge from `%s'", p_name(kx->p));
+ a_warn("KX", "?PEER", kx->p, "unexpected", "%s", pkname[msg], A_END);
goto bad;
}
/* --- Unpack the packet --- */
- if ((c = buf_getmp(b)) == 0 ||
- (msg >= KX_COOKIE && (hc = buf_get(b, HASHSZ)) == 0) ||
- (msg >= KX_CHAL && (hrx = buf_get(b, HASHSZ)) == 0) ||
+ if (G_FROMBUF(gg, b, c) ||
+ (msg >= KX_COOKIE && (hc = buf_get(b, algs.hashsz)) == 0) ||
+ (msg >= KX_CHAL && (ck = buf_getmp(b)) == 0) ||
BLEFT(b)) {
- a_warn("malformed packet from `%s'", p_name(kx->p));
+ a_warn("KX", "?PEER", kx->p, "invalid", "%s", pkname[msg], A_END);
goto bad;
}
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
- trace(T_CRYPTO, "crypto: challenge = %s", mpstr(c));
- if (hc) trace_block(T_CRYPTO, "crypto: cookie", hc, HASHSZ);
- if (hrx) trace_block(T_CRYPTO, "crypto: response hash", hrx, HASHSZ);
+ trace(T_CRYPTO, "crypto: challenge = %s", gestr(gg, c));
+ if (hc) trace_block(T_CRYPTO, "crypto: cookie", hc, algs.hashsz);
+ if (ck) trace(T_CRYPTO, "crypto: check value = %s", mpstr(ck));
}))
/* --- First, handle a bare challenge --- *
if (!hc && kx->nr >= KX_THRESH) {
T( trace(T_KEYEXCH, "keyexch: too many challenges -- sending cookie"); )
+ a_warn("KX", "?PEER", p_name, "sending-cookie", A_END);
b = p_txstart(kx->p, MSG_KEYEXCH | KX_COOKIE);
- buf_putmp(b, kx->c);
- HASH_INIT(&h);
- HASH_STRING(&h, "tripe-cookie");
- hashmp(&h, c);
- HASH_DONE(&h, buf_get(b, HASHSZ));
+ G_TOBUF(gg, b, kx->c);
+ h = GH_INIT(algs.h);
+ HASH_STRING(h, "tripe-cookie");
+ hashge(h, c);
+ GH_DONE(h, buf_get(b, algs.hashsz));
+ GH_DESTROY(h);
p_txend(kx->p);
goto tidy;
}
/* --- Discard a packet with an invalid cookie --- */
- if (hc && memcmp(hc, kx->hc, HASHSZ) != 0) {
- a_warn("incorrect cookie from `%s'", p_name(kx->p));
+ if (hc && memcmp(hc, kx->hc, algs.hashsz) != 0) {
+ a_warn("KX", "?PEER", "incorrect", "cookie", A_END);
goto bad;
}
if ((kxc = kxc_bychal(kx, c)) == 0) {
size_t x, y, z;
- mp *r;
+ ge *r;
/* --- Be careful here --- *
*
* it.
*/
- if (!hrx)
+ if (!ck)
kxc = kxc_new(kx);
else {
- if ((r = getreply(kx, c, hrx)) == 0)
+ if ((r = getreply(kx, c, ck)) == 0)
goto bad;
kxc = kxc_new(kx);
kxc->r = r;
}
- kxc->c = mp_copy(c);
+ kxc->c = G_CREATE(gg);
+ G_COPY(gg, kxc->c, c);
/* --- Work out the cookie for this challenge --- */
- HASH_INIT(&h);
- HASH_STRING(&h, "tripe-cookie");
- hashmp(&h, kxc->c);
- HASH_DONE(&h, kxc->hc);
+ h = GH_INIT(algs.h);
+ HASH_STRING(h, "tripe-cookie");
+ hashge(h, kxc->c);
+ GH_DONE(h, kxc->hc);
+ GH_DESTROY(h);
+
+ IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
+ trace_block(T_CRYPTO, "crypto: computed cookie", kxc->hc, algs.hashsz);
+ }))
/* --- Compute the expected-reply hash --- */
- HASH_INIT(&h);
- HASH_STRING(&h, "tripe-expected-reply");
- hashmp(&h, kx->c);
- hashmp(&h, kxc->c);
- hashmp(&h, kx->rx);
- HASH_DONE(&h, kxc->hrx);
+ h = GH_INIT(algs.h);
+ HASH_STRING(h, "tripe-expected-reply");
+ hashge(h, kx->c);
+ hashge(h, kxc->c);
+ hashge(h, kx->rx);
+ hc = GH_DONE(h, 0);
+ kxc->ck = mpencrypt(MP_NEW, kx->alpha, mp_octets(gg->r), hc);
+ IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
+ trace_block(T_CRYPTO, "crypto: expected-reply hash", hc, algs.hashsz);
+ trace(T_CRYPTO, "crypto: my reply check = %s", mpstr(kxc->ck));
+ }))
+ GH_DESTROY(h);
/* --- Work out the shared key --- */
- r = mpmont_exp(&mg, MP_NEW, c, kx->alpha);
+ r = G_CREATE(gg);
+ G_EXP(gg, r, c, kx->alpha);
+ IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
+ trace(T_CRYPTO, "crypto: shared secret = %s", gestr(gg, r));
+ }))
/* --- Compute the switch messages --- */
- HASH_INIT(&h); HASH_STRING(&h, "tripe-switch-request");
- hashmp(&h, kx->c); hashmp(&h, kxc->c);
- HASH_DONE(&h, kxc->hswrq_out);
- HASH_INIT(&h); HASH_STRING(&h, "tripe-switch-confirm");
- hashmp(&h, kx->c); hashmp(&h, kxc->c);
- HASH_DONE(&h, kxc->hswok_out);
+ h = GH_INIT(algs.h); HASH_STRING(h, "tripe-switch-request");
+ hashge(h, kx->c); hashge(h, kxc->c);
+ GH_DONE(h, kxc->hswrq_out); GH_DESTROY(h);
+ h = GH_INIT(algs.h); HASH_STRING(h, "tripe-switch-confirm");
+ hashge(h, kx->c); hashge(h, kxc->c);
+ GH_DONE(h, kxc->hswok_out); GH_DESTROY(h);
- HASH_INIT(&h); HASH_STRING(&h, "tripe-switch-request");
- hashmp(&h, kxc->c); hashmp(&h, kx->c);
- HASH_DONE(&h, kxc->hswrq_in);
- HASH_INIT(&h); HASH_STRING(&h, "tripe-switch-confirm");
- hashmp(&h, kxc->c); hashmp(&h, kx->c);
- HASH_DONE(&h, kxc->hswok_in);
+ h = GH_INIT(algs.h); HASH_STRING(h, "tripe-switch-request");
+ hashge(h, kxc->c); hashge(h, kx->c);
+ GH_DONE(h, kxc->hswrq_in); GH_DESTROY(h);
+ h = GH_INIT(algs.h); HASH_STRING(h, "tripe-switch-confirm");
+ hashge(h, kxc->c); hashge(h, kx->c);
+ GH_DONE(h, kxc->hswok_in); GH_DESTROY(h);
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
- trace_block(T_CRYPTO, "crypto: computed cookie", kxc->hc, HASHSZ);
- trace_block(T_CRYPTO, "crypto: my reply hash", kxc->hrx, HASHSZ);
- trace(T_CRYPTO, "crypto: shared secret = %s", mpstr(r));
trace_block(T_CRYPTO, "crypto: outbound switch request",
- kxc->hswrq_out, HASHSZ);
+ kxc->hswrq_out, algs.hashsz);
trace_block(T_CRYPTO, "crypto: outbound switch confirm",
- kxc->hswok_out, HASHSZ);
+ kxc->hswok_out, algs.hashsz);
trace_block(T_CRYPTO, "crypto: inbound switch request",
- kxc->hswrq_in, HASHSZ);
+ kxc->hswrq_in, algs.hashsz);
trace_block(T_CRYPTO, "crypto: inbound switch confirm",
- kxc->hswok_in, HASHSZ);
+ kxc->hswok_in, algs.hashsz);
}))
/* --- Create a new symmetric keyset --- */
buf_init(b, buf_o, sizeof(buf_o));
- buf_putmp(b, kx->c); x = BLEN(b);
- buf_putmp(b, kxc->c); y = BLEN(b);
- buf_putmp(b, r); z = BLEN(b);
+ G_TOBUF(gg, b, kx->c); x = BLEN(b);
+ G_TOBUF(gg, b, kxc->c); y = BLEN(b);
+ G_TOBUF(gg, b, r); z = BLEN(b);
assert(BOK(b));
- kxc->ks = ks_gen(BBASE(b), x, y, z);
- mp_drop(r);
+ kxc->ks = ks_gen(BBASE(b), x, y, z, kx->p);
+ G_DESTROY(gg, r);
}
/* --- Answer the challenge if we need to --- */
- if (hrx && !kxc->r) {
- mp *r;
- if ((r = getreply(kx, c, hrx)) == 0)
+ if (ck && !kxc->r) {
+ ge *r;
+ if ((r = getreply(kx, c, ck)) == 0)
goto bad;
kxc->r = r;
}
/* --- Tidy up and go home --- */
tidy:
- mp_drop(c);
+ G_DESTROY(gg, c);
+ mp_drop(ck);
return (0);
bad:
- mp_drop(c);
+ G_DESTROY(gg, c);
+ mp_drop(ck);
return (-1);
}
T( trace(T_KEYEXCH, "keyexch: sending prechallenge to `%s'",
p_name(kx->p)); )
b = p_txstart(kx->p, MSG_KEYEXCH | KX_PRECHAL);
- buf_putmp(b, kx->c);
+ G_TOBUF(gg, b, kx->c);
break;
case KXS_COMMIT:
T( trace(T_KEYEXCH, "keyexch: sending switch request to `%s'",
p_name(kx->p)); )
kxc = kx->r[0];
b = p_txstart(kx->p, MSG_KEYEXCH | KX_SWITCH);
- buf_put(b, kx->hc, HASHSZ);
- buf_put(b, kxc->hc, HASHSZ);
+ buf_put(b, kx->hc, algs.hashsz);
+ buf_put(b, kxc->hc, algs.hashsz);
buf_init(&bb, buf_i, sizeof(buf_i));
- buf_putmp(&bb, kxc->r);
- buf_put(&bb, kxc->hswrq_out, HASHSZ);
+ G_TOBUF(gg, &bb, kxc->r);
+ buf_put(&bb, kxc->hswrq_out, algs.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'",
kxc = kx->r[0];
b = p_txstart(kx->p, MSG_KEYEXCH | KX_SWITCHOK);
buf_init(&bb, buf_i, sizeof(buf_i));
- buf_put(&bb, kxc->hswok_out, HASHSZ);
+ buf_put(&bb, kxc->hswok_out, algs.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)
- * @const octet *krx@ = his expected-reply hash (optional)
+ * @mp *ck@ = his expected-reply hash (optional)
* @buf *b@ = encrypted remainder of the packet
*
* Returns: A pointer to the challenge block if OK, or null on failure.
* challenge is returned.
*/
-static kxchal *matchreply(keyexch *kx, const octet *hc_in,
- const octet *hc_out, const octet *hrx, buf *b)
+static kxchal *matchreply(keyexch *kx, unsigned ty, const octet *hc_in,
+ const octet *hc_out, mp *ck, buf *b)
{
kxchal *kxc;
buf bb;
- mp *r = 0;
+ ge *r = 0;
/* --- Check the plaintext portions of the data --- */
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
- trace_block(T_CRYPTO, "crypto: challenge", hc_in, HASHSZ);
- trace_block(T_CRYPTO, "crypto: cookie", hc_out, HASHSZ);
- if (hrx) trace_block(T_CRYPTO, "crypto: response hash", hrx, HASHSZ);
+ trace_block(T_CRYPTO, "crypto: challenge", hc_in, algs.hashsz);
+ trace_block(T_CRYPTO, "crypto: cookie", hc_out, algs.hashsz);
+ if (ck) trace(T_CRYPTO, "crypto: check value = %s", mpstr(ck));
}))
- if (memcmp(hc_out, kx->hc, HASHSZ) != 0) {
- a_warn("incorrect cookie from `%s'", p_name(kx->p));
+ if (memcmp(hc_out, kx->hc, algs.hashsz) != 0) {
+ a_warn("KX", "?PEER", kx->p, "incorrect", "cookie", A_END);
goto bad;
}
if ((kxc = kxc_byhc(kx, hc_in)) == 0) {
- a_warn("received reply for unknown challenge from `%s'", p_name(kx->p));
+ a_warn("KX", "?PEER", kx->p, "unknown-challenge", A_END);
goto bad;
}
/* --- Maybe compute a reply for the challenge --- */
if (!kxc->r) {
- if (!hrx) {
- a_warn("unexpected switch request from `%s'", p_name(kx->p));
+ if (!ck) {
+ a_warn("KX", "?PEER", kx->p, "unexpected", "switch-rq", A_END);
goto bad;
}
- if ((r = getreply(kx, kxc->c, hrx)) == 0)
+ if ((r = getreply(kx, kxc->c, ck)) == 0)
goto bad;
kxc->r = r;
r = 0;
/* --- Decrypt the rest of the packet --- */
buf_init(&bb, buf_o, sizeof(buf_o));
- if (ks_decrypt(kxc->ks, b, &bb)) {
- a_warn("failed to decrypt reply from `%s'", p_name(kx->p));
+ if (ks_decrypt(kxc->ks, ty, b, &bb)) {
+ a_warn("KX", "?PEER", kx->p, "decrypt-failed", "reply", A_END);
goto bad;
}
buf_init(b, BBASE(&bb), BLEN(&bb));
- if ((r = buf_getmp(b)) == 0) {
- a_warn("invalid reply packet from `%s'", p_name(kx->p));
+ r = G_CREATE(gg);
+ if (G_FROMBUF(gg, b, r)) {
+ a_warn("KX", "?PEER", kx->p, "invalid", "reply", A_END);
goto bad;
}
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
- trace(T_CRYPTO, "crypto: reply = %s", mpstr(r));
+ trace(T_CRYPTO, "crypto: reply = %s", gestr(gg, r));
}))
- if (!mp_eq(r, kx->rx)) {
- a_warn("incorrect reply from `%s'", p_name(kx->p));
+ if (!G_EQ(gg, r, kx->rx)) {
+ a_warn("KX", "?PEER", kx->p, "incorrect", "reply", A_END);
goto bad;
}
/* --- Done --- */
- mp_drop(r);
+ G_DESTROY(gg, r);
return (kxc);
bad:
- mp_drop(r);
+ if (r) G_DESTROY(gg, r);
return (0);
}
static int doreply(keyexch *kx, buf *b)
{
- const octet *hc_in, *hc_out, *hrx;
+ const octet *hc_in, *hc_out;
+ mp *ck = 0;
kxchal *kxc;
if (kx->s != KXS_CHAL && kx->s != KXS_COMMIT) {
- a_warn("unexpected reply from `%s'", p_name(kx->p));
+ a_warn("KX", "?PEER", kx->p, "unexpected", "reply", A_END);
goto bad;
}
- if ((hc_in = buf_get(b, HASHSZ)) == 0 ||
- (hc_out = buf_get(b, HASHSZ)) == 0 ||
- (hrx = buf_get(b, HASHSZ)) == 0) {
- a_warn("invalid reply packet from `%s'", p_name(kx->p));
+ if ((hc_in = buf_get(b, algs.hashsz)) == 0 ||
+ (hc_out = buf_get(b, algs.hashsz)) == 0 ||
+ (ck = buf_getmp(b)) == 0) {
+ a_warn("KX", "?PEER", kx->p, "invalid", "reply", A_END);
goto bad;
}
- if ((kxc = matchreply(kx, hc_in, hc_out, hrx, 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("KX", "?PEER", kx->p, "invalid", "reply", A_END);
goto bad;
}
if (kx->s == KXS_CHAL) {
return (0);
bad:
+ mp_drop(ck);
return (-1);
}
+/* --- @kxfinish@ --- *
+ *
+ * Arguments: @keyexch *kx@ = pointer to key exchange block
+ *
+ * Returns: ---
+ *
+ * Use: Sets everything up following a successful key exchange.
+ */
+
+static void kxfinish(keyexch *kx)
+{
+ kxchal *kxc = kx->r[0];
+ ks_activate(kxc->ks);
+ settimer(kx, ks_tregen(kxc->ks));
+ kx->s = KXS_SWITCH;
+ a_notify("KXDONE", "?PEER", kx->p, A_END);
+ p_stats(kx->p)->t_kx = time(0);
+}
+
/* --- @doswitch@ --- *
*
* Arguments: @keyexch *kx@ = pointer to key exchange block
const octet *hc_in, *hc_out, *hswrq;
kxchal *kxc;
- if ((hc_in = buf_get(b, HASHSZ)) == 0 ||
- (hc_out = buf_get(b, HASHSZ)) == 0) {
- a_warn("invalid switch request from `%s'", p_name(kx->p));
+ if ((hc_in = buf_get(b, algs.hashsz)) == 0 ||
+ (hc_out = buf_get(b, algs.hashsz)) == 0) {
+ a_warn("KX", "?PEER", kx->p, "invalid", "switch-rq", A_END);
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));
+ if ((hswrq = buf_get(b, algs.hashsz)) == 0 || BLEFT(b)) {
+ a_warn("KX", "?PEER", "invalid", "switch-rq", A_END);
goto bad;
}
IF_TRACING(T_KEYEXCH, {
- trace_block(T_CRYPTO, "crypto: switch request hash", hswrq, HASHSZ);
+ trace_block(T_CRYPTO, "crypto: switch request hash", hswrq, algs.hashsz);
})
- if (memcmp(hswrq, kxc->hswrq_in, HASHSZ) != 0) {
- a_warn("incorrect switch request hash from `%s'", p_name(kx->p));
+ if (memcmp(hswrq, kxc->hswrq_in, algs.hashsz) != 0) {
+ a_warn("KX", "?PEER", kx->p, "incorrect", "switch-rq", A_END);
goto bad;
}
switch (kx->s) {
case KXS_CHAL:
commit(kx, kxc);
case KXS_COMMIT:
- ks_activate(kxc->ks);
- settimer(kx, ks_tregen(kxc->ks));
- kx->s = KXS_SWITCH;
+ kxfinish(kx);
break;
}
resend(kx);
buf bb;
if (kx->s < KXS_COMMIT) {
- a_warn("unexpected switch confirmation from `%s'", p_name(kx->p));
+ a_warn("KX", "?PEER", kx->p, "unexpected", "switch-ok", A_END);
goto bad;
}
kxc = kx->r[0];
buf_init(&bb, buf_o, sizeof(buf_o));
- if (ks_decrypt(kxc->ks, b, &bb)) {
- a_warn("failed to decrypt switch confirmation from `%s'", p_name(kx->p));
+ if (ks_decrypt(kxc->ks, MSG_KEYEXCH | KX_SWITCHOK, b, &bb)) {
+ a_warn("KX", "?PEER", kx->p, "decrypt-failed", "switch-ok", A_END);
goto bad;
}
buf_init(b, BBASE(&bb), BLEN(&bb));
- if ((hswok = buf_get(b, HASHSZ)) == 0 || BLEFT(b)) {
- a_warn("invalid switch confirmation from `%s'", p_name(kx->p));
+ if ((hswok = buf_get(b, algs.hashsz)) == 0 || BLEFT(b)) {
+ a_warn("KX", "?PEER", "invalid", "switch-ok", A_END);
goto bad;
}
IF_TRACING(T_KEYEXCH, {
- trace_block(T_CRYPTO, "crypto: switch confirmation hash", hswok, HASHSZ);
+ trace_block(T_CRYPTO, "crypto: switch confirmation hash",
+ hswok, algs.hashsz);
})
- if (memcmp(hswok, kxc->hswok_in, HASHSZ) != 0) {
- a_warn("incorrect switch confirmation hash from `%s'", p_name(kx->p));
+ if (memcmp(hswok, kxc->hswok_in, algs.hashsz) != 0) {
+ a_warn("KX", "?PEER", kx->p, "incorrect", "switch-ok", A_END);
goto bad;
}
- if (kx->s < KXS_SWITCH) {
- ks_activate(kxc->ks);
- settimer(kx, ks_tregen(kxc->ks));
- kx->s = KXS_SWITCH;
- }
+ if (kx->s < KXS_SWITCH)
+ kxfinish(kx);
return (0);
bad:
for (i = 0; i < kx->nr; i++)
kxc_destroy(kx->r[i]);
mp_drop(kx->alpha);
- mp_drop(kx->c);
- mp_drop(kx->rx);
+ G_DESTROY(gg, kx->c);
+ G_DESTROY(gg, kx->rx);
kx->t_valid = 0;
kx->f |= KXF_DEAD;
kx->f &= ~KXF_TIMER;
static void start(keyexch *kx, time_t now)
{
- HASH_CTX h;
+ ghash *h;
assert(kx->f & KXF_DEAD);
kx->f &= ~KXF_DEAD;
kx->nr = 0;
- kx->alpha = mprand_range(MP_NEW, kpriv.dp.q, &rand_global, 0);
- kx->c = mpmont_exp(&mg, MP_NEW, kpriv.dp.g, kx->alpha);
- kx->rx = mpmont_exp(&mg, MP_NEW, kx->kpub.y, kx->alpha);
+ kx->alpha = mprand_range(MP_NEW, gg->r, &rand_global, 0);
+ kx->c = G_CREATE(gg); G_EXP(gg, kx->c, gg->g, kx->alpha);
+ kx->rx = G_CREATE(gg); G_EXP(gg, kx->rx, kx->kpub, kx->alpha);
kx->s = KXS_CHAL;
kx->t_valid = now + T_VALID;
- HASH_INIT(&h);
- HASH_STRING(&h, "tripe-cookie");
- hashmp(&h, kx->c);
- HASH_DONE(&h, kx->hc);
+ h = GH_INIT(algs.h);
+ HASH_STRING(h, "tripe-cookie");
+ hashge(h, kx->c);
+ GH_DONE(h, kx->hc);
+ GH_DESTROY(h);
IF_TRACING(T_KEYEXCH, {
trace(T_KEYEXCH, "keyexch: creating new challenge");
IF_TRACING(T_CRYPTO, {
trace(T_CRYPTO, "crypto: secret = %s", mpstr(kx->alpha));
- trace(T_CRYPTO, "crypto: challenge = %s", mpstr(kx->c));
- trace(T_CRYPTO, "crypto: expected response = %s", mpstr(kx->rx));
- trace_block(T_CRYPTO, "crypto: challenge cookie", kx->hc, HASHSZ);
+ trace(T_CRYPTO, "crypto: challenge = %s", gestr(gg, kx->c));
+ trace(T_CRYPTO, "crypto: expected response = %s", gestr(gg, kx->rx));
+ trace_block(T_CRYPTO, "crypto: challenge cookie", kx->hc, algs.hashsz);
})
})
}
now = time(0);
if (KEY_EXPIRED(now, kx->texp_kpub)) {
stop(kx);
- a_warn("public key for `%s' has expired", p_name(kx->p));
- dh_pubfree(&kx->kpub);
+ a_warn("KX", "?PEER", kx->p, "public-key-expired", A_END);
+ G_COPY(gg, kx->kpub, gg->i);
kx->f &= ~KXF_PUBKEY;
return (-1);
}
/* --- @kx_start@ --- *
*
* Arguments: @keyexch *kx@ = pointer to key exchange context
+ * @int forcep@ = nonzero to ignore the quiet timer
*
* Returns: ---
*
* this); if no exchange is in progress, one is commenced.
*/
-void kx_start(keyexch *kx)
+void kx_start(keyexch *kx, int forcep)
{
time_t now = time(0);
if (checkpub(kx))
return;
- if (!ISVALID(kx, now)) {
+ if (forcep || !VALIDP(kx, now)) {
stop(kx);
start(kx, now);
+ a_notify("KXSTART", "?PEER", kx->p, A_END);
}
resend(kx);
}
size_t sz = BSZ(b);
int rc;
-#ifndef NTRACE
- static const char *const pkname[] = {
- "prechallenge", "cookie", "challenge",
- "reply", "switch request", "switch confirmation"
- };
-#endif
-
if (checkpub(kx))
return;
- if (!ISVALID(kx, now)) {
+ if (!VALIDP(kx, now)) {
stop(kx);
start(kx, now);
}
rc = doswitchok(kx, b);
break;
default:
- a_warn("unexpected key exchange message type %u from `%p'",
- p_name(kx->p));
+ a_warn("KX", "?PEER", kx->p, "unknown-message", "0x%02x", msg, A_END);
rc = -1;
break;
}
void kx_free(keyexch *kx)
{
stop(kx);
- if (kx->f & KXF_PUBKEY)
- dh_pubfree(&kx->kpub);
+ G_DESTROY(gg, kx->kpub);
}
/* --- @kx_newkeys@ --- *
void kx_newkeys(keyexch *kx)
{
- dh_pub dp;
-
- if (km_getpubkey(p_name(kx->p), &dp, &kx->texp_kpub))
+ if (km_getpubkey(p_name(kx->p), kx->kpub, &kx->texp_kpub))
return;
- if (kx->f & KXF_PUBKEY)
- dh_pubfree(&kx->kpub);
- kx->kpub = dp;
kx->f |= KXF_PUBKEY;
if ((kx->f & KXF_DEAD) || kx->s != KXS_SWITCH) {
T( trace(T_KEYEXCH, "keyexch: restarting key negotiation with `%s'",
{
kx->ks = ks;
kx->p = p;
- if (km_getpubkey(p_name(p), &kx->kpub, &kx->texp_kpub))
+ kx->kpub = G_CREATE(gg);
+ if (km_getpubkey(p_name(p), kx->kpub, &kx->texp_kpub)) {
+ G_DESTROY(gg, kx->kpub);
return (-1);
+ }
kx->f = KXF_DEAD | KXF_PUBKEY;
start(kx, time(0));
resend(kx);
+ /* Don't notify here: the ADD message hasn't gone out yet. */
return (0);
}