X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/tripe/blobdiff_plain/737cc2710532a8ce10a58e10566980476506e197..28461f0eaead039da9acbc637392197aabf94815:/keyexch.c diff --git a/keyexch.c b/keyexch.c index f4125da2..ae8c9c5d 100644 --- a/keyexch.c +++ b/keyexch.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: keyexch.c,v 1.9 2003/07/13 11:53:14 mdw Exp $ + * $Id$ * * Key exchange protocol * @@ -26,41 +26,6 @@ * 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" @@ -82,7 +47,7 @@ * * %$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})$% + * %$v_A = \rho_A \xor H(\cookie{expected-reply}, a, 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}$% @@ -97,17 +62,14 @@ * %$\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@. * - * %$\cookie{kx-switch}, c_A, c_B, E_K(r_B^\alpha, w_A))$% + * %$\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@. * @@ -120,53 +82,165 @@ #define T_VALID MIN(2) /* Challenge validity period */ #define T_RETRY SEC(10) /* Challenge retransmit interval */ -#define ISVALID(kx, now) ((now) < (kx)->t_valid) +#define VALIDP(kx, now) ((now) < (kx)->t_valid) + +/*----- Static tables -----------------------------------------------------*/ + +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)); } -/* --- @mpcrypt@ --- * +/* --- @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 using another - * multiprecision integer as the key. This is a slightly grotty - * way to do this, but it's easier than the alternatives. + * Use: Masks a multiprecision integer: returns %$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) +static octet *mpmask(buf *b, mp *x, size_t n, const octet *k, size_t ksz) { - MGF_CTX m; + gcipher *mgf; + octet *p; - MGF_INIT(&m, k, ksz, 0); - mp_storeb(x, buf_t, sz); - MGF_CRYPT(&m, buf_t, buf_t, sz); - return (mp_loadb(d, 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 (p); +} + +/* --- @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, 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 (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@ --- * @@ -184,7 +258,7 @@ static void timer(struct timeval *tv, void *v) keyexch *kx = v; kx->f &= ~KXF_TIMER; T( trace(T_KEYEXCH, "keyexch: timer has popped"); ) - kx_start(kx); + kx_start(kx, 0); } /* --- @settimer@ --- * @@ -236,9 +310,8 @@ static void kxc_destroy(kxchal *kxc) { if (kxc->f & KXF_TIMER) sel_rmtimer(&kxc->t); - mp_drop(kxc->c); - mp_drop(kxc->r); - mp_drop(kxc->ck); + G_DESTROY(gg, kxc->c); + G_DESTROY(gg, kxc->r); ks_drop(kxc->ks); DESTROY(kxc); } @@ -287,9 +360,8 @@ static kxchal *kxc_new(keyexch *kx) /* --- Fill in the new structure --- */ kxc = CREATE(kxchal); - kxc->c = 0; - kxc->r = 0; - kxc->ck = 0; + kxc->c = G_CREATE(gg); + kxc->r = G_CREATE(gg); kxc->ks = 0; kxc->kx = kx; kxc->f = 0; @@ -300,19 +372,19 @@ static kxchal *kxc_new(keyexch *kx) /* --- @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); @@ -333,7 +405,7 @@ static kxchal *kxc_byhc(keyexch *kx, const octet *hc) 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); @@ -362,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) - buf_putmp(b, kx->c); - else - buf_put(b, kx->hc, HASHSZ); - buf_put(b, kxc->hc, 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)); - buf_putmp(&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 --- */ @@ -408,233 +467,249 @@ static void kxc_answer(keyexch *kx, kxchal *kxc) /*----- Individual message handlers ---------------------------------------*/ -/* --- @getreply@ --- * +/* --- @doprechallenge@ --- * * - * Arguments: @keyexch *kx@ = pointer to key exchange context - * @mp *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 mp *getreply(keyexch *kx, mp *c, mp *ck) +static int doprechallenge(keyexch *kx, buf *b) { - mp *r = mpmont_exp(&mg, MP_NEW, c, kpriv.x); - mp *a; - HASH_CTX h; - octet buf[HASHSZ]; - int ok; + stats *st = p_stats(kx->p); + ge *c = G_CREATE(gg); + ghash *h; - HASH_INIT(&h); - HASH_STRING(&h, "tripe-expected-reply"); - hashmp(&h, c); - hashmp(&h, kx->c); - hashmp(&h, r); - HASH_DONE(&h, buf); + /* --- 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 = mpcrypt(MP_NEW, ck, mp_octets(kpriv.dp.q), buf, sizeof(buf)); 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: recovered log = %s", mpstr(a)); + trace(T_CRYPTO, "crypto: challenge = %s", gestr(gg, c)); })) - a = mpmont_exp(&mg, a, kpriv.dp.g, a); - ok = mp_eq(a, 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)); - })) - mp_drop(r); - } - mp_drop(a); - return (ok ? r : 0); + + /* --- 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); + 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) { - mp *c = 0, *ck = 0; - const octet *hc = 0; + ge *c = G_CREATE(gg); + ge *r = G_CREATE(gg); + ge *cc = G_CREATE(gg); + const octet *hc, *ck; + size_t x, y, z; + mp *cv = 0; kxchal *kxc; - HASH_CTX h; - octet buf[HASHSZ]; - - /* --- Ensure that we're in a sensible state --- */ - - if (kx->s != KXS_CHAL) { - a_warn("unexpected challenge from `%s'", p_name(kx->p)); - goto bad; - } + ghash *h = 0; + buf bb; + int ok; /* --- Unpack the packet --- */ - if ((c = buf_getmp(b)) == 0 || - (msg >= KX_COOKIE && (hc = buf_get(b, HASHSZ)) == 0) || - (msg >= KX_CHAL && (ck = buf_getmp(b)) == 0) || - BLEFT(b)) { - a_warn("malformed packet from `%s'", p_name(kx->p)); + if (G_FROMBUF(gg, b, c) || + (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", mpstr(c)); - if (hc) trace_block(T_CRYPTO, "crypto: cookie", hc, HASHSZ); - if (ck) trace(T_CRYPTO, "crypto: check value = %s", mpstr(ck)); + trace(T_CRYPTO, "crypto: challenge = %s", gestr(gg, c)); + 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"); ) - 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)); - 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; } - /* --- 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; - mp *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 = mp_copy(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); - - /* --- 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, buf); - kxc->ck = mpcrypt(MP_NEW, kx->alpha, mp_octets(kpriv.dp.q), - buf, sizeof(buf)); + 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); + + 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"); + 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); + })) /* --- 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)); + 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: 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_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); - 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); - mp_drop(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) { - mp *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: - mp_drop(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: - mp_drop(c); - mp_drop(ck); return (-1); } @@ -659,18 +734,18 @@ static void resend(keyexch *kx) 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_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); break; @@ -680,7 +755,7 @@ static void resend(keyexch *kx) 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, MSG_KEYEXCH | KX_SWITCHOK, &bb, b); break; @@ -698,87 +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; - mp *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 (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)); - goto bad; - } - if ((kxc = kxc_byhc(kx, hc_in)) == 0) { - a_warn("received reply for unknown challenge from `%s'", p_name(kx->p)); - 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("unexpected switch request from `%s'", p_name(kx->p)); - 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("failed to decrypt reply from `%s'", p_name(kx->p)); - 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)); + if (G_FROMRAW(gg, b, r)) { + 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", 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", "response", A_END); goto bad; } - /* --- Done --- */ - - mp_drop(r); - return (kxc); + G_DESTROY(gg, r); + return (0); bad: - mp_drop(r); - return (0); + G_DESTROY(gg, r); + return (-1); } /* --- @commit@ --- * @@ -819,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("unexpected reply from `%s'", p_name(kx->p)); - goto bad; - } - if ((hc_in = buf_get(b, HASHSZ)) == 0 || - (hc_out = buf_get(b, HASHSZ)) == 0 || - (ck = buf_getmp(b)) == 0) { - a_warn("invalid reply packet from `%s'", p_name(kx->p)); + a_warn("KX", "?PEER", kx->p, "unexpected", "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("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) { commit(kx, kxc); kx->s = KXS_COMMIT; @@ -848,10 +895,28 @@ static int doreply(keyexch *kx, buf *b) 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 @@ -867,34 +932,38 @@ static int doswitch(keyexch *kx, buf *b) 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, 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, 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; - break; - } + if (kx->s == KXS_CHAL) + commit(kx, kxc); + if (kx->s < KXS_SWITCH) + kxfinish(kx); resend(kx); return (0); @@ -919,32 +988,27 @@ static int doswitchok(keyexch *kx, buf *b) 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, MSG_KEYEXCH | KX_SWITCHOK, b, &bb)) { - a_warn("failed to decrypt switch confirmation from `%s'", p_name(kx->p)); + if (decryptrest(kx, kxc, KX_SWITCHOK, b)) 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: @@ -979,8 +1043,8 @@ static void stop(keyexch *kx) 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; @@ -999,30 +1063,31 @@ static void stop(keyexch *kx) 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); }) }) } @@ -1045,8 +1110,8 @@ static int checkpub(keyexch *kx) 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); } @@ -1056,6 +1121,7 @@ static int checkpub(keyexch *kx) /* --- @kx_start@ --- * * * Arguments: @keyexch *kx@ = pointer to key exchange context + * @int forcep@ = nonzero to ignore the quiet timer * * Returns: --- * @@ -1064,15 +1130,16 @@ static int checkpub(keyexch *kx) * 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); } @@ -1096,17 +1163,10 @@ void kx_message(keyexch *kx, unsigned msg, buf *b) 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); } @@ -1116,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); @@ -1130,8 +1191,7 @@ void kx_message(keyexch *kx, unsigned msg, buf *b) 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; } @@ -1156,8 +1216,7 @@ void kx_message(keyexch *kx, unsigned msg, buf *b) void kx_free(keyexch *kx) { stop(kx); - if (kx->f & KXF_PUBKEY) - dh_pubfree(&kx->kpub); + G_DESTROY(gg, kx->kpub); } /* --- @kx_newkeys@ --- * @@ -1174,13 +1233,8 @@ void kx_free(keyexch *kx) 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'", @@ -1208,11 +1262,15 @@ int kx_init(keyexch *kx, peer *p, keyset **ks) { 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); }