From de7bd20be1c41f8f70e98ab498ffb4a82800a9d8 Mon Sep 17 00:00:00 2001 Message-Id: From: Mark Wooding Date: Wed, 1 Nov 2006 15:10:50 +0000 Subject: [PATCH] keyexch: Simplify key-exchange protocol. Organization: Straylight/Edgeware From: Mark Wooding Remove pointless cookie message. Only allocate challenge blocks on receipt of a full challenge. Also fix bugs introduced in previous changes. --- keyexch.c | 590 +++++++++++++++++++++++++---------------------- keymgmt.c | 2 + tripe-protocol.h | 11 +- tripe.h | 4 +- 4 files changed, 327 insertions(+), 280 deletions(-) diff --git a/keyexch.c b/keyexch.c index f6786e09..3ec122c0 100644 --- a/keyexch.c +++ b/keyexch.c @@ -62,13 +62,10 @@ * %$\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))$% + * %$\cookie{kx-reply}, R_A, c_B, v_A, E_K(r_B^\alpha))$% * Challenge accpeted: here's the answer. Commit to my challenge. Move * to @KXS_COMMIT@. * @@ -116,41 +113,134 @@ static void hashge(ghash *h, ge *x) GH_HASH(h, BBASE(&b), BLEN(&b)); } -/* --- @mpencrypt@, @mpdecrypt@ --- * +/* --- @mpmask@ --- * * - * Arguments: @mp *d@ = the destination integer - * @mp *x@ = the plaintext/ciphertext integer - * @size_t sz@ = the expected size of the plaintext + * Arguments: @buf *b@ = output buffer + * @mp *x@ = the plaintext integer + * @size_t n@ = the expected size of the plaintext * @const octet *k@ = pointer to key material + * @size_t ksz@ = size of the key * - * Returns: The encrypted/decrypted integer. + * Returns: Pointer to the output. * - * 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. + * Use: Masks a multiprecision integer: returns %$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) +static octet *mpmask(buf *b, mp *x, size_t n, const octet *k, size_t ksz) { gcipher *mgf; + octet *p; - mgf = GC_INIT(algs.mgf, k, algs.hashsz); - mp_storeb(x, buf_t, sz); - GC_ENCRYPT(mgf, buf_t, buf_t, sz); + if ((p = buf_get(b, n)) == 0) + return (0); + mgf = GC_INIT(algs.mgf, k, ksz); + IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, { + trace(T_CRYPTO, "masking index = %s", mpstr(x)); + trace_block(T_CRYPTO, "masking key", k, ksz); + })) + mp_storeb(x, buf_t, n); + GC_ENCRYPT(mgf, buf_t, p, n); + IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, { + trace_block(T_CRYPTO, "index plaintext", buf_t, n); + trace_block(T_CRYPTO, "masked ciphertext", p, n); + })) GC_DESTROY(mgf); - return (mp_loadb(d, buf_t, sz)); + return (p); } -static mp *mpdecrypt(mp *d, mp *x, size_t sz, const octet *k) +/* --- @mpunmask@ --- * + * + * Arguments: @mp *d@ = the output integer + * @const octet *p@ = pointer to the ciphertext + * @size_t n@ = the size of the ciphertext + * @const octet *k@ = pointer to key material + * @size_t ksz@ = size of the key + * + * Returns: The decrypted integer, or null. + * + * Use: Unmasks a multiprecision integer. + */ + +static mp *mpunmask(mp *d, const octet *p, size_t n, + const octet *k, size_t ksz) { gcipher *mgf; - mgf = GC_INIT(algs.mgf, k, algs.hashsz); - mp_storeb(x, buf_t, sz); - GC_DECRYPT(mgf, buf_t, buf_t, sz); + mgf = GC_INIT(algs.mgf, k, ksz); + IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, { + trace_block(T_CRYPTO, "unmasking key", k, ksz); + trace_block(T_CRYPTO, "masked ciphertext", p, n); + })) + GC_DECRYPT(mgf, p, buf_t, n); + d = mp_loadb(d, buf_t, n); + IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, { + trace_block(T_CRYPTO, "index plaintext", buf_t, n); + trace(T_CRYPTO, "unmasked index = %s", mpstr(d)); + })) GC_DESTROY(mgf); - return (mp_loadb(d, buf_t, sz)); + return (d); +} + +/* --- @hashcheck@ --- * + * + * Arguments: @ge *kpub@ = sender's public key + * @ge *cc@ = receiver's challenge + * @ge *c@ = sender's challenge + * @ge *y@ = reply to sender's challenge + * + * Returns: Pointer to the hash value (in @buf_t@) + * + * Use: Computes the check-value hash, used to mask or unmask + * indices to prove the validity of challenges. This computes + * the masking key used in challenge check values. This is + * really the heart of the whole thing, since it ensures that + * the index can be recovered from the history of hashing + * queries, which gives us (a) a proof that the authentication + * process is zero-knowledge, and (b) a proof that the whole + * key-exchange is deniable. + */ + +static const octet *hashcheck(ge *kpub, ge *cc, ge *c, ge *y) +{ + ghash *h = GH_INIT(algs.h); + + HASH_STRING(h, "tripe-expected-reply"); + hashge(h, kpub); + hashge(h, cc); + hashge(h, c); + hashge(h, y); + GH_DONE(h, buf_t); + IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, { + trace(T_CRYPTO, "computing challenge check hash"); + trace(T_CRYPTO, "public key = %s", gestr(gg, kpub)); + trace(T_CRYPTO, "receiver challenge = %s", gestr(gg, cc)); + trace(T_CRYPTO, "sender challenge = %s", gestr(gg, c)); + trace(T_CRYPTO, "sender reply = %s", gestr(gg, y)); + trace_block(T_CRYPTO, "hash output", buf_t, algs.hashsz); + })) + GH_DESTROY(h); + return (buf_t); +} + +/* --- @sendchallenge@ --- * + * + * Arguments: @keyexch *kx@ = pointer to key exchange block + * @buf *b@ = output buffer for challenge + * @ge *c@ = peer's actual challenge + * @const octet *hc@ = peer's challenge cookie + * + * Returns: --- + * + * Use: Writes a full challenge to the message buffer. + */ + +static void sendchallenge(keyexch *kx, buf *b, ge *c, const octet *hc) +{ + G_TOBUF(gg, b, kx->c); + buf_put(b, hc, algs.hashsz); + mpmask(b, kx->alpha, indexsz, + hashcheck(kpub, c, kx->c, kx->rx), algs.hashsz); } /* --- @timer@ --- * @@ -221,8 +311,7 @@ static void kxc_destroy(kxchal *kxc) if (kxc->f & KXF_TIMER) sel_rmtimer(&kxc->t); G_DESTROY(gg, kxc->c); - if (kxc->r) G_DESTROY(gg, kxc->r); - mp_drop(kxc->ck); + G_DESTROY(gg, kxc->r); ks_drop(kxc->ks); DESTROY(kxc); } @@ -272,8 +361,7 @@ static kxchal *kxc_new(keyexch *kx) kxc = CREATE(kxchal); kxc->c = G_CREATE(gg); - kxc->r = 0; - kxc->ck = MP_NEW; + kxc->r = G_CREATE(gg); kxc->ks = 0; kxc->kx = kx; kxc->f = 0; @@ -346,31 +434,18 @@ static void kxc_timer(struct timeval *tv, void *v) static void kxc_answer(keyexch *kx, kxchal *kxc) { stats *st = p_stats(kx->p); - buf *b = p_txstart(kx->p, MSG_KEYEXCH | (kxc->r ? KX_REPLY : KX_CHAL)); + buf *b = p_txstart(kx->p, MSG_KEYEXCH | KX_REPLY); struct timeval tv; buf bb; /* --- Build the reply packet --- */ - if (!kxc->r) - G_TOBUF(gg, b, kx->c); - else - 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 --- */ - - if (!kxc->r) { - T( trace(T_KEYEXCH, "keyexch: resending challenge to `%s'", - p_name(kx->p)); ) - } else { - T( trace(T_KEYEXCH, "keyexch: sending reply to `%s'", p_name(kx->p)); ) - buf_init(&bb, buf_i, sizeof(buf_i)); - G_TORAW(gg, &bb, kxc->r); - buf_flip(&bb); - ks_encrypt(kxc->ks, MSG_KEYEXCH | KX_REPLY, &bb, b); - } + T( trace(T_KEYEXCH, "keyexch: sending reply to `%s'", p_name(kx->p)); ) + sendchallenge(kx, b, kxc->c, kxc->hc); + buf_init(&bb, buf_i, sizeof(buf_i)); + G_TORAW(gg, &bb, kxc->r); + buf_flip(&bb); + ks_encrypt(kxc->ks, MSG_KEYEXCH | KX_REPLY, &bb, b); /* --- Update the statistics --- */ @@ -392,122 +467,99 @@ static void kxc_answer(keyexch *kx, kxchal *kxc) /*----- Individual message handlers ---------------------------------------*/ -/* --- @getreply@ --- * +/* --- @doprechallenge@ --- * * - * Arguments: @keyexch *kx@ = pointer to key exchange context - * @ge *c@ = a challenge - * @mp *ck@ = the supplied expected-reply check value + * Arguments: @keyexch *kx@ = pointer to key exchange block + * @buf *b@ = buffer containing the packet * - * Returns: A pointer to the reply, or null if the reply-hash was wrong. + * Returns: Zero if OK, nonzero of the packet was rejected. * - * Use: Computes replies to challenges. + * Use: Processes a pre-challenge message. */ -static ge *getreply(keyexch *kx, ge *c, mp *ck) +static int doprechallenge(keyexch *kx, buf *b) { - ge *r = G_CREATE(gg); - ge *y = G_CREATE(gg); - mp *a = MP_NEW; + stats *st = p_stats(kx->p); + ge *c = G_CREATE(gg); 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, kx->kpub); - hashge(h, c); - hashge(h, kx->c); - hashge(h, r); - hh = GH_DONE(h, 0); + /* --- Ensure that we're in a sensible state --- */ + + if (kx->s != KXS_CHAL) { + a_warn("KX", "?PEER", kx->p, "unexpected", "pre-challenge", A_END); + goto bad; + } + + /* --- Unpack the packet --- */ + + if (G_FROMBUF(gg, b, c) || BLEFT(b)) + goto bad; - 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", gestr(gg, r)); - trace_block(T_CRYPTO, "crypto: computed reply hash", hh, algs.hashsz); - trace(T_CRYPTO, "crypto: recovered log = %s", mpstr(a)); + trace(T_CRYPTO, "crypto: challenge = %s", gestr(gg, c)); })) + + /* --- Send out a full challenge by return --- */ + + b = p_txstart(kx->p, MSG_KEYEXCH | KX_CHAL); + h = GH_INIT(algs.h); + HASH_STRING(h, "tripe-cookie"); + hashge(h, c); + sendchallenge(kx, b, c, GH_DONE(h, 0)); GH_DESTROY(h); - if (MP_CMP(a, >=, gg->r)) - ok = 0; - else{ - 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); + st->n_kxout++; + st->sz_kxout += BLEN(b); + p_txend(kx->p); + + /* --- Done --- */ + + G_DESTROY(gg, c); + return (0); + +bad: + if (c) G_DESTROY(gg, c); + return (-1); } -/* --- @dochallenge@ --- * +/* --- @respond@ --- * * * Arguments: @keyexch *kx@ = pointer to key exchange block - * @unsigned msg@ = message code for the packet + * @unsigned msg@ = message code for this packet * @buf *b@ = buffer containing the packet * - * Returns: Zero if OK, nonzero if the packet was rejected. + * Returns: Key-exchange challenge block, or null. * - * Use: Processes a packet containing a challenge. + * Use: Computes a response for the given challenge, entering it into + * a challenge block and so on. */ -static int dochallenge(keyexch *kx, unsigned msg, buf *b) +static kxchal *respond(keyexch *kx, unsigned msg, buf *b) { ge *c = G_CREATE(gg); - mp *ck = MP_NEW; - const octet *hc = 0; + ge *r = G_CREATE(gg); + ge *cc = G_CREATE(gg); + const octet *hc, *ck; + size_t x, y, z; + mp *cv = 0; kxchal *kxc; - ghash *h; - - /* --- Ensure that we're in a sensible state --- */ - - if (kx->s != KXS_CHAL) { - a_warn("KX", "?PEER", kx->p, "unexpected", "%s", pkname[msg], A_END); - goto bad; - } + ghash *h = 0; + buf bb; + int ok; /* --- Unpack the packet --- */ 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)) { + (hc = buf_get(b, algs.hashsz)) == 0 || + (ck = buf_get(b, indexsz)) == 0) { 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", 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)); + trace_block(T_CRYPTO, "crypto: cookie", hc, algs.hashsz); + trace_block(T_CRYPTO, "crypto: check-value", ck, indexsz); })) - /* --- First, handle a bare challenge --- * - * - * If the table is heavily loaded, just emit a cookie and return. - */ - - 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); - 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, algs.hashsz) != 0) { @@ -515,34 +567,47 @@ static int dochallenge(keyexch *kx, unsigned msg, buf *b) goto bad; } - /* --- Find a challenge block for this packet --- * + /* --- Recover the check value and verify it --- * + * + * To avoid recomputation on replays, we store a hash of the `right' + * value. The `correct' value is unique, so this is right. * - * If there isn't one already, create a new one. + * This will also find a challenge block and, if necessary, populate it. */ - if ((kxc = kxc_bychal(kx, c)) == 0) { - size_t x, y, z; - ge *r; - - /* --- Be careful here --- * - * - * If this is a full challenge, and it's the first time I've seen it, I - * want to be able to throw it away before committing a table entry to - * it. - */ - - if (!ck) - kxc = kxc_new(kx); - else { - if ((r = getreply(kx, c, ck)) == 0) - goto bad; - kxc = kxc_new(kx); - kxc->r = r; - } - kxc->c = G_CREATE(gg); + if ((kxc = kxc_bychal(kx, c)) != 0) { + h = GH_INIT(algs.h); + HASH_STRING(h, "tripe-check-hash"); + GH_HASH(h, ck, indexsz); + ok = !memcmp(kxc->ck, GH_DONE(h, 0), algs.hashsz); + GH_DESTROY(h); + if (!ok) goto badcheck; + } else { + + /* --- Compute the reply, and check the magic --- */ + + G_EXP(gg, r, c, kpriv); + cv = mpunmask(MP_NEW, ck, indexsz, + hashcheck(kx->kpub, kx->c, c, r), algs.hashsz); + IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, { + trace(T_CRYPTO, "crypto: computed reply = %s", gestr(gg, r)); + trace(T_CRYPTO, "crypto: recovered log = %s", mpstr(cv)); + })) + if (MP_CMP(cv, >, gg->r) || + (G_EXP(gg, cc, gg->g, cv), !G_EQ(gg, c, cc))) + goto badcheck; + + /* --- Fill in a new challenge block --- */ + + kxc = kxc_new(kx); G_COPY(gg, kxc->c, c); + G_COPY(gg, kxc->r, r); - /* --- Work out the cookie for this challenge --- */ + h = GH_INIT(algs.h); + HASH_STRING(h, "tripe-check-hash"); + GH_HASH(h, ck, indexsz); + GH_DONE(h, kxc->hc); + GH_DESTROY(h); h = GH_INIT(algs.h); HASH_STRING(h, "tripe-cookie"); @@ -554,25 +619,8 @@ static int dochallenge(keyexch *kx, unsigned msg, buf *b) trace_block(T_CRYPTO, "crypto: computed cookie", kxc->hc, algs.hashsz); })) - /* --- Compute the expected-reply hash --- */ - - h = GH_INIT(algs.h); - HASH_STRING(h, "tripe-expected-reply"); - hashge(h, kpub); - 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 = 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)); @@ -607,37 +655,61 @@ static int dochallenge(keyexch *kx, unsigned msg, buf *b) /* --- Create a new symmetric keyset --- */ - buf_init(b, buf_o, sizeof(buf_o)); - 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)); + buf_init(&bb, buf_o, sizeof(buf_o)); + G_TOBUF(gg, &bb, kx->c); x = BLEN(&bb); + G_TOBUF(gg, &bb, kxc->c); y = BLEN(&bb); + G_TOBUF(gg, &bb, r); z = BLEN(&bb); + assert(BOK(&bb)); - kxc->ks = ks_gen(BBASE(b), x, y, z, kx->p); - G_DESTROY(gg, r); + kxc->ks = ks_gen(BBASE(&bb), x, y, z, kx->p); } - /* --- Answer the challenge if we need to --- */ + G_DESTROY(gg, c); + G_DESTROY(gg, cc); + G_DESTROY(gg, r); + mp_drop(cv); + return (kxc); - if (ck && !kxc->r) { - ge *r; - if ((r = getreply(kx, c, ck)) == 0) - goto bad; - kxc->r = r; - } +badcheck: + a_warn("KX", "?PEER", kx->p, "bad-expected-reply-log", A_END); + goto bad; +bad: + G_DESTROY(gg, c); + G_DESTROY(gg, cc); + G_DESTROY(gg, r); + mp_drop(cv); + return (0); +} - kxc_answer(kx, kxc); +/* --- @dochallenge@ --- * + * + * Arguments: @keyexch *kx@ = pointer to key exchange block + * @unsigned msg@ = message code for the packet + * @buf *b@ = buffer containing the packet + * + * Returns: Zero if OK, nonzero if the packet was rejected. + * + * Use: Processes a packet containing a challenge. + */ - /* --- Tidy up and go home --- */ +static int dochallenge(keyexch *kx, buf *b) +{ + kxchal *kxc; -tidy: - G_DESTROY(gg, c); - mp_drop(ck); + if (kx->s != KXS_CHAL) { + a_warn("KX", "?PEER", kx->p, "unexpected", "challenge", A_END); + goto bad; + } + if ((kxc = respond(kx, KX_CHAL, b)) == 0) + goto bad; + if (BLEFT(b)) { + a_warn("KX", "?PEER", kx->p, "invalid", "challenge", A_END); + goto bad; + } + kxc_answer(kx, kxc); return (0); bad: - G_DESTROY(gg, c); - mp_drop(ck); return (-1); } @@ -672,7 +744,7 @@ static void resend(keyexch *kx) buf_put(b, kx->hc, algs.hashsz); buf_put(b, kxc->hc, algs.hashsz); buf_init(&bb, buf_i, sizeof(buf_i)); - G_TOBUF(gg, &bb, kxc->r); + G_TORAW(gg, &bb, kxc->r); buf_put(&bb, kxc->hswrq_out, algs.hashsz); buf_flip(&bb); ks_encrypt(kxc->ks, MSG_KEYEXCH | KX_SWITCH, &bb, b); @@ -701,88 +773,66 @@ static void resend(keyexch *kx) settimer(kx, time(0) + T_RETRY); } -/* --- @matchreply@ --- * +/* --- @decryptrest@ --- * * * 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) + * @kxchal *kxc@ = pointer to challenge block + * @unsigned msg@ = type of incoming message * @buf *b@ = encrypted remainder of the packet * - * Returns: A pointer to the challenge block if OK, or null on failure. + * Returns: Zero if OK, nonzero on some kind of error. * - * Use: Checks a reply or switch packet, ensuring that its contents - * are sensible and correct. If they are, @*b@ is set to point - * to the remainder of the encrypted data, and the correct - * challenge is returned. + * Use: Decrypts the remainder of the packet, and points @b@ at the + * recovered plaintext. */ -static kxchal *matchreply(keyexch *kx, unsigned ty, const octet *hc_in, - const octet *hc_out, mp *ck, buf *b) +static int decryptrest(keyexch *kx, kxchal *kxc, unsigned msg, buf *b) { - kxchal *kxc; buf bb; - 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, 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, 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("KX", "?PEER", kx->p, "unknown-challenge", A_END); - goto bad; + buf_init(&bb, buf_o, sizeof(buf_o)); + if (ks_decrypt(kxc->ks, MSG_KEYEXCH | msg, b, &bb)) { + a_warn("KX", "?PEER", kx->p, "decrypt-failed", "%s", pkname[msg], A_END); + return (-1); } + buf_init(b, BBASE(&bb), BLEN(&bb)); + return (0); +} - /* --- Maybe compute a reply for the challenge --- */ - - if (!kxc->r) { - if (!ck) { - a_warn("KX", "?PEER", kx->p, "unexpected", "switch-rq", A_END); - goto bad; - } - if ((r = getreply(kx, kxc->c, ck)) == 0) - goto bad; - kxc->r = r; - r = 0; - } +/* --- @checkresponse@ --- * + * + * Arguments: @keyexch *kx@ = pointer to key exchange context + * @unsigned msg@ = type of incoming message + * @buf *b@ = decrypted remainder of the packet + * + * Returns: Zero if OK, nonzero on some kind of error. + * + * Use: Checks a reply or switch packet, ensuring that its response + * is correct. + */ - /* --- Decrypt the rest of the packet --- */ +static int checkresponse(keyexch *kx, unsigned msg, buf *b) +{ + ge *r = G_CREATE(gg); - buf_init(&bb, buf_o, sizeof(buf_o)); - 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)); - r = G_CREATE(gg); if (G_FROMRAW(gg, b, r)) { - a_warn("KX", "?PEER", kx->p, "invalid", "reply", A_END); + 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: reply = %s", gestr(gg, r)); })) if (!G_EQ(gg, r, kx->rx)) { - a_warn("KX", "?PEER", kx->p, "incorrect", "reply", A_END); + a_warn("KX", "?PEER", kx->p, "incorrect", "response", A_END); goto bad; } - /* --- Done --- */ - G_DESTROY(gg, r); - return (kxc); + return (0); bad: - if (r) G_DESTROY(gg, r); - return (0); + G_DESTROY(gg, r); + return (-1); } /* --- @commit@ --- * @@ -823,27 +873,20 @@ static void commit(keyexch *kx, kxchal *kxc) static int doreply(keyexch *kx, buf *b) { - const octet *hc_in, *hc_out; - mp *ck = 0; kxchal *kxc; if (kx->s != KXS_CHAL && kx->s != KXS_COMMIT) { a_warn("KX", "?PEER", kx->p, "unexpected", "reply", A_END); goto bad; } - 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, MSG_KEYEXCH | KX_REPLY, - hc_in, hc_out, ck, b)) == 0) + if ((kxc = respond(kx, KX_REPLY, b)) == 0 || + decryptrest(kx, kxc, KX_REPLY, b) || + checkresponse(kx, KX_REPLY, b)) goto bad; if (BLEFT(b)) { a_warn("KX", "?PEER", kx->p, "invalid", "reply", A_END); goto bad; - } + } if (kx->s == KXS_CHAL) { commit(kx, kxc); kx->s = KXS_COMMIT; @@ -852,7 +895,6 @@ static int doreply(keyexch *kx, buf *b) return (0); bad: - mp_drop(ck); return (-1); } @@ -895,8 +937,17 @@ static int doswitch(keyexch *kx, buf *b) a_warn("KX", "?PEER", kx->p, "invalid", "switch-rq", A_END); goto bad; } - if ((kxc = matchreply(kx, MSG_KEYEXCH | KX_SWITCH, - hc_in, hc_out, 0, b)) == 0) + IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, { + trace_block(T_CRYPTO, "crypto: challenge", hc_in, algs.hashsz); + trace_block(T_CRYPTO, "crypto: cookie", hc_out, algs.hashsz); + })) + if ((kxc = kxc_byhc(kx, hc_in)) == 0 || + memcmp(hc_out, kx->hc, algs.hashsz) != 0) { + a_warn("KX", "?PEER", kx->p, "incorrect", "switch-rq", A_END); + goto bad; + } + if (decryptrest(kx, kxc, KX_SWITCH, b) || + checkresponse(kx, KX_SWITCH, b)) goto bad; if ((hswrq = buf_get(b, algs.hashsz)) == 0 || BLEFT(b)) { a_warn("KX", "?PEER", "invalid", "switch-rq", A_END); @@ -909,13 +960,10 @@ static int doswitch(keyexch *kx, buf *b) 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: - kxfinish(kx); - break; - } + if (kx->s == KXS_CHAL) + commit(kx, kxc); + if (kx->s < KXS_SWITCH) + kxfinish(kx); resend(kx); return (0); @@ -945,11 +993,8 @@ static int doswitchok(keyexch *kx, buf *b) } kxc = kx->r[0]; buf_init(&bb, buf_o, sizeof(buf_o)); - if (ks_decrypt(kxc->ks, MSG_KEYEXCH | KX_SWITCHOK, b, &bb)) { - a_warn("KX", "?PEER", kx->p, "decrypt-failed", "switch-ok", A_END); + if (decryptrest(kx, kxc, KX_SWITCHOK, b)) goto bad; - } - buf_init(b, BBASE(&bb), BLEN(&bb)); if ((hswok = buf_get(b, algs.hashsz)) == 0 || BLEFT(b)) { a_warn("KX", "?PEER", "invalid", "switch-ok", A_END); goto bad; @@ -1131,9 +1176,10 @@ void kx_message(keyexch *kx, unsigned msg, buf *b) switch (msg) { case KX_PRECHAL: - case KX_COOKIE: + rc = doprechallenge(kx, b); + break; case KX_CHAL: - rc = dochallenge(kx, msg, b); + rc = dochallenge(kx, b); break; case KX_REPLY: rc = doreply(kx, b); diff --git a/keymgmt.c b/keymgmt.c index f645d156..edd31b2a 100644 --- a/keymgmt.c +++ b/keymgmt.c @@ -36,6 +36,7 @@ group *gg; mp *kpriv; ge *kpub; algswitch algs; +size_t indexsz; /*----- Static variables --------------------------------------------------*/ @@ -403,6 +404,7 @@ tymatch:; G_DESTROY(g, kpub); kpub = G_CREATE(g); G_EXP(g, kpub, g->g, x); + indexsz = mp_octets(g->r); /* --- Dump out the group --- */ diff --git a/tripe-protocol.h b/tripe-protocol.h index bfa97487..6ffaeb2d 100644 --- a/tripe-protocol.h +++ b/tripe-protocol.h @@ -61,12 +61,11 @@ #define MSG_KEYEXCH 0x10 #define KX_PRECHAL 0u -#define KX_COOKIE 1u -#define KX_CHAL 2u -#define KX_REPLY 3u -#define KX_SWITCH 4u -#define KX_SWITCHOK 5u -#define KX_NMSG 6u +#define KX_CHAL 1u +#define KX_REPLY 2u +#define KX_SWITCH 3u +#define KX_SWITCHOK 4u +#define KX_NMSG 5u /* --- Miscellaneous packets --- */ diff --git a/tripe.h b/tripe.h index 5f62f16d..2182b3bf 100644 --- a/tripe.h +++ b/tripe.h @@ -212,7 +212,6 @@ typedef struct keyset { */ #define KX_NCHAL 16u -#define KX_THRESH 4u typedef struct kxchal { struct keyexch *kx; /* Pointer back to key exchange */ @@ -222,7 +221,7 @@ typedef struct kxchal { unsigned f; /* Various useful flags */ sel_timer t; /* Response timer for challenge */ octet hc[MAXHASHSZ]; /* Hash of his challenge */ - mp *ck; /* The check value */ + octet ck[MAXHASHSZ]; /* His magical check value */ octet hswrq_in[MAXHASHSZ]; /* Inbound switch request message */ octet hswok_in[MAXHASHSZ]; /* Inbound switch confirmation */ octet hswrq_out[MAXHASHSZ]; /* Outbound switch request message */ @@ -425,6 +424,7 @@ typedef struct admin { extern sel_state sel; /* Global I/O event state */ extern group *gg; /* The group we work in */ +extern size_t indexsz; /* Size of exponent for the group */ extern mp *kpriv; /* Our private key */ extern ge *kpub; /* Our public key */ extern octet buf_i[PKBUFSZ], buf_o[PKBUFSZ], buf_t[PKBUFSZ]; -- [mdw]