/* -*-c-*-
*
- * $Id: keyexch.c,v 1.9 2003/07/13 11:53:14 mdw Exp $
+ * $Id: keyexch.c,v 1.12 2004/04/08 01:36:17 mdw Exp $
*
* Key exchange protocol
*
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-/*----- Revision history --------------------------------------------------*
- *
- * $Log: keyexch.c,v $
- * Revision 1.9 2003/07/13 11:53:14 mdw
- * Add protocol commentary.
- *
- * Revision 1.8 2003/07/13 11:19:49 mdw
- * Incopatible 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.
- *
- * Revision 1.4 2001/06/22 19:40:36 mdw
- * Support expiry of other peers' public keys.
- *
- * Revision 1.3 2001/06/19 22:07:09 mdw
- * Cosmetic fixes.
- *
- * Revision 1.2 2001/02/16 21:24:27 mdw
- * Rewrite for new key exchange protocol.
- *
- * Revision 1.1 2001/02/03 20:26:37 mdw
- * Initial checkin.
- *
- */
-
/*----- Header files ------------------------------------------------------*/
#include "tripe.h"
/*----- Various utilities -------------------------------------------------*/
-/* --- @hashmp@ --- *
+/* --- @hashge@ --- *
*
* Arguments: @HASH_CTX *r@ = pointer to hash context
- * @mp *m@ = pointer to multiprecision integer
+ * @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(HASH_CTX *r, 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));
}
*
* Returns: The encrypted/decrypted integer.
*
- * Use: Encrypts (or decrypts) a multiprecision integer using another
- * multiprecision integer as the key. This is a slightly grotty
- * way to do this, but it's easier than the alternatives.
+ * 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 *mpcrypt(mp *d, mp *x, size_t sz, const octet *k, size_t ksz)
{
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);
/* --- Fill in the new structure --- */
kxc = CREATE(kxchal);
- kxc->c = 0;
+ kxc->c = G_CREATE(gg);
kxc->r = 0;
- kxc->ck = 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);
/* --- 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);
} 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, MSG_KEYEXCH | KX_REPLY, &bb, b);
}
/* --- @getreply@ --- *
*
* Arguments: @keyexch *kx@ = pointer to key exchange context
- * @mp *c@ = a challenge
+ * @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, mp *ck)
+static ge *getreply(keyexch *kx, ge *c, mp *ck)
{
- mp *r = mpmont_exp(&mg, MP_NEW, c, kpriv.x);
- mp *a;
+ ge *r = G_CREATE(gg);
+ ge *y = G_CREATE(gg);
+ mp *a = MP_NEW;
HASH_CTX h;
octet buf[HASHSZ];
int ok;
+ G_EXP(gg, r, c, kpriv);
HASH_INIT(&h);
HASH_STRING(&h, "tripe-expected-reply");
- hashmp(&h, c);
- hashmp(&h, kx->c);
- hashmp(&h, r);
+ hashge(&h, c);
+ hashge(&h, kx->c);
+ hashge(&h, r);
HASH_DONE(&h, buf);
- a = mpcrypt(MP_NEW, ck, mp_octets(kpriv.dp.q), buf, sizeof(buf));
+ a = mpcrypt(MP_NEW, ck, mp_octets(gg->r), buf, sizeof(buf));
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
- trace(T_CRYPTO, "crypto: computed reply = %s", mpstr(r));
+ trace(T_CRYPTO, "crypto: computed reply = %s", gestr(gg, r));
trace_block(T_CRYPTO, "crypto: computed reply hash", buf, HASHSZ);
trace(T_CRYPTO, "crypto: recovered log = %s", mpstr(a));
}))
- a = mpmont_exp(&mg, a, kpriv.dp.g, a);
- ok = mp_eq(a, c);
+ G_EXP(gg, y, gg->g, a);
+ ok = G_EQ(gg, y, c);
if (!ok) {
a_warn("invalid expected-reply check from `%s'", p_name(kx->p));
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
- trace(T_CRYPTO, "crypto: computed challenge = %s", mpstr(a));
+ trace(T_CRYPTO, "crypto: computed challenge = %s", gestr(gg, y));
}))
- mp_drop(r);
+ G_DESTROY(gg, r);
+ r = 0;
}
mp_drop(a);
- return (ok ? r : 0);
+ G_DESTROY(gg, y);
+ return (r);
}
/* --- @dochallenge@ --- *
static int dochallenge(keyexch *kx, unsigned msg, buf *b)
{
- mp *c = 0, *ck = 0;
+ ge *c = G_CREATE(gg);
+ mp *ck = MP_NEW;
const octet *hc = 0;
kxchal *kxc;
HASH_CTX h;
/* --- Unpack the packet --- */
- if ((c = buf_getmp(b)) == 0 ||
+ if (G_FROMBUF(gg, b, c) ||
(msg >= KX_COOKIE && (hc = buf_get(b, HASHSZ)) == 0) ||
(msg >= KX_CHAL && (ck = buf_getmp(b)) == 0) ||
BLEFT(b)) {
}
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
- trace(T_CRYPTO, "crypto: challenge = %s", mpstr(c));
+ trace(T_CRYPTO, "crypto: challenge = %s", gestr(gg, c));
if (hc) trace_block(T_CRYPTO, "crypto: cookie", hc, HASHSZ);
if (ck) trace(T_CRYPTO, "crypto: check value = %s", mpstr(ck));
}))
if (!hc && kx->nr >= KX_THRESH) {
T( trace(T_KEYEXCH, "keyexch: too many challenges -- sending cookie"); )
b = p_txstart(kx->p, MSG_KEYEXCH | KX_COOKIE);
- buf_putmp(b, kx->c);
+ G_TOBUF(gg, b, kx->c);
HASH_INIT(&h);
HASH_STRING(&h, "tripe-cookie");
- hashmp(&h, c);
+ hashge(&h, c);
HASH_DONE(&h, buf_get(b, HASHSZ));
p_txend(kx->p);
goto tidy;
if ((kxc = kxc_bychal(kx, c)) == 0) {
size_t x, y, z;
- mp *r;
+ ge *r;
/* --- Be careful here --- *
*
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);
+ hashge(&h, kxc->c);
HASH_DONE(&h, kxc->hc);
/* --- 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);
+ hashge(&h, kx->c);
+ hashge(&h, kxc->c);
+ hashge(&h, kx->rx);
HASH_DONE(&h, buf);
- kxc->ck = mpcrypt(MP_NEW, kx->alpha, mp_octets(kpriv.dp.q),
+ kxc->ck = mpcrypt(MP_NEW, kx->alpha, mp_octets(gg->r),
buf, sizeof(buf));
/* --- Work out the shared key --- */
- trace(T_CRYPTO, "debug: c = %s", mpstr(c));
- trace(T_CRYPTO, "debug: alpha = %s", mpstr(kx->alpha));
- r = mpmont_exp(&mg, MP_NEW, c, kx->alpha);
- trace(T_CRYPTO, "debug: r = %s", mpstr(r));
+ r = G_CREATE(gg);
+ G_EXP(gg, r, c, kx->alpha);
/* --- Compute the switch messages --- */
HASH_INIT(&h); HASH_STRING(&h, "tripe-switch-request");
- hashmp(&h, kx->c); hashmp(&h, kxc->c);
+ hashge(&h, kx->c); hashge(&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);
+ hashge(&h, kx->c); hashge(&h, kxc->c);
HASH_DONE(&h, kxc->hswok_out);
HASH_INIT(&h); HASH_STRING(&h, "tripe-switch-request");
- hashmp(&h, kxc->c); hashmp(&h, kx->c);
+ hashge(&h, kxc->c); hashge(&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);
+ hashge(&h, kxc->c); hashge(&h, kx->c);
HASH_DONE(&h, kxc->hswok_in);
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
trace_block(T_CRYPTO, "crypto: expected-reply hash",
buf, HASHSZ);
trace(T_CRYPTO, "crypto: my reply check = %s", mpstr(kxc->ck));
- trace(T_CRYPTO, "crypto: shared secret = %s", mpstr(r));
+ trace(T_CRYPTO, "crypto: shared secret = %s", gestr(gg, r));
trace_block(T_CRYPTO, "crypto: outbound switch request",
kxc->hswrq_out, HASHSZ);
trace_block(T_CRYPTO, "crypto: outbound switch confirm",
/* --- 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, kx->p);
- mp_drop(r);
+ G_DESTROY(gg, r);
}
/* --- Answer the challenge if we need to --- */
if (ck && !kxc->r) {
- mp *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'",
buf_put(b, kx->hc, HASHSZ);
buf_put(b, kxc->hc, HASHSZ);
buf_init(&bb, buf_i, sizeof(buf_i));
- buf_putmp(&bb, kxc->r);
+ G_TOBUF(gg, &bb, kxc->r);
buf_put(&bb, kxc->hswrq_out, HASHSZ);
buf_flip(&bb);
ks_encrypt(kxc->ks, MSG_KEYEXCH | KX_SWITCH, &bb, b);
{
kxchal *kxc;
buf bb;
- mp *r = 0;
+ ge *r = 0;
/* --- Check the plaintext portions of the data --- */
goto bad;
}
buf_init(b, BBASE(&bb), BLEN(&bb));
- if ((r = buf_getmp(b)) == 0) {
+ r = G_CREATE(gg);
+ if (G_FROMBUF(gg, b, r)) {
a_warn("invalid reply packet from `%s'", p_name(kx->p));
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)) {
+ if (!G_EQ(gg, r, kx->rx)) {
a_warn("incorrect reply from `%s'", p_name(kx->p));
goto bad;
}
/* --- Done --- */
- mp_drop(r);
+ G_DESTROY(gg, r);
return (kxc);
bad:
- mp_drop(r);
+ if (r) G_DESTROY(gg, r);
return (0);
}
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;
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);
+ hashge(&h, kx->c);
HASH_DONE(&h, kx->hc);
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(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, HASHSZ);
})
})
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);
+ G_COPY(gg, kx->kpub, gg->i);
kx->f &= ~KXF_PUBKEY;
return (-1);
}
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);