+ 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, &bb, b);
+ }
+
+ /* --- Update the statistics --- */
+
+ if (BOK(b)) {
+ st->n_kxout++;
+ st->sz_kxout += BLEN(b);
+ p_txend(kx->p);
+ }
+
+ /* --- Schedule another resend --- */
+
+ if (kxc->f & KXF_TIMER)
+ sel_rmtimer(&kxc->t);
+ gettimeofday(&tv, 0);
+ tv.tv_sec += T_RETRY;
+ sel_addtimer(&sel, &kxc->t, &tv, kxc_timer, kxc);
+ kxc->f |= KXF_TIMER;
+}
+
+/*----- Individual message handlers ---------------------------------------*/
+
+/* --- @getreply@ --- *
+ *
+ * Arguments: @keyexch *kx@ = pointer to key exchange context
+ * @mp *c@ = a challenge
+ * @const octet *hrx@ = the supplied expected-reply hash
+ *
+ * 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)
+{
+ 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);
+ 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);
+ }))
+ if (memcmp(buf, hrx, HASHSZ) != 0) {
+ a_warn("invalid expected-reply hash from `%s'", p_name(kx->p));
+ mp_drop(r);
+ return (0);
+ }
+ return (r);
+}
+
+/* --- @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.
+ */
+
+static int dochallenge(keyexch *kx, unsigned msg, buf *b)
+{
+ mp *c = 0;
+ const octet *hc = 0, *hrx = 0;
+ kxchal *kxc;
+ HASH_CTX h;
+
+ /* --- 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;
+ }
+
+ /* --- 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) ||
+ BLEFT(b)) {
+ a_warn("malformed packet from `%s'", p_name(kx->p));
+ 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);
+ }))
+
+ /* --- 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));
+ goto bad;
+ }
+
+ /* --- Find a challenge block for this packet --- *