+static ratelim unauth_limit;
+
+/* --- @dotokenrq@ --- *
+ *
+ * Arguments: @const addr *a@ = sender's address
+ * @buf *b@ = buffer containing the packet
+ *
+ * Returns: ---
+ *
+ * Use: Processes a token-request message.
+ */
+
+static void dotokenrq(const addr *a, buf *b)
+{
+ uint32 id;
+ kdata *kpriv = 0, *kpub = 0;
+ char *pname;
+ const char *tag;
+ size_t sz;
+ buf bb, bbb;
+
+ /* --- Check if we're in danger of overloading --- */
+
+ if (ratelim_withdraw(&unauth_limit, 1)) goto done;
+
+ /* --- Start building the reply --- */
+
+ buf_init(&bbb, buf_o, sizeof(buf_o));
+ buf_putu8(&bbb, MSG_KEYEXCH | KX_TOKEN);
+
+ /* --- Fetch and copy the challenge string --- */
+
+ if (buf_getbuf16(b, &bb)) goto done;
+ buf_putmem16(&bbb, BBASE(&bb), BSZ(&bb));
+
+ /* --- Make our own challenge for the response --- */
+
+ buf_init(&bb, buf_t, sizeof(buf_t));
+ c_new(0, 0, &bb); assert(BOK(&bb)); buf_putbuf16(&bbb, &bb);
+
+ /* --- Figure out which private key I'm supposed to use --- */
+
+ if (buf_getu32(b, &id)) goto done;
+ if ((kpriv = km_findprivbyid(id)) == 0) goto done;
+
+ /* --- Decrypt the message --- */
+
+ buf_init(&bb, buf_t, sizeof(buf_t));
+ if (ies_decrypt(kpriv, MSG_KEYEXCH | KX_TOKENRQ, b, &bb) || BLEFT(b))
+ goto done;
+
+ /* --- Parse the token request and find the sender's public key --- */
+
+ assert(BOK(&bb)); buf_flip(&bb);
+ if ((pname = buf_getmem16(&bb, &sz)) == 0 || memchr(pname, 0, sz))
+ goto done;
+ assert(sz < sizeof(buf_t) - ((const octet *)pname - buf_t));
+ pname[sz] = 0;
+ if ((tag = strchr(pname, '.')) != 0) tag++;
+ else tag = pname;
+ if ((kpub = km_findpub(tag)) == 0) goto done;
+
+ /* --- Build and encrypt the token --- */
+
+ buf_init(&bb, buf_i, sizeof(buf_i));
+ c_new(pname, sz, &bb);
+ assert(BOK(&bb)); buf_flip(&bb);
+ if (ies_encrypt(kpub, MSG_KEYEXCH | KX_TOKEN, &bb, &bbb)) goto done;
+ assert(BOK(&bbb));
+
+ /* --- Send the response -- or at least give it a try --- */
+
+ p_txaddr(a, BBASE(&bbb), BLEN(&bbb));
+
+ /* --- All done --- */
+
+done:
+ if (kpriv) km_unref(kpriv);
+ if (kpub) km_unref(kpub);
+}
+
+/* --- @dotoken@ --- *
+ *
+ * Arguments: @keyexch *kx@ = pointer to key exchange block
+ * @buf *b@ = buffer containing the packet
+ *
+ * Returns: Zero if OK, nonzero of the packet was rejected.
+ *
+ * Use: Processes a token message.
+ */
+
+static int dotoken(keyexch *kx, buf *b)
+{
+ buf bb;
+ buf *bbb;
+ const dhgrp *g = kx->kpriv->grp;
+ octet *p;
+ size_t sz;
+
+ /* --- Make sure this is a sensible message to have received --- */
+
+ if (!kx->p->spec.knock) return (-1);
+
+ /* --- First, collect and verify our challenge --- */
+
+ if (buf_getbuf16(b, &bb) || c_check(0, 0, &bb) || BLEFT(&bb)) return (-1);
+
+ /* --- Start building the knock message from here --- */
+
+ bbb = p_txstart(kx->p, MSG_KEYEXCH | KX_KNOCK);
+
+ /* --- Copy the peer's challenge --- */
+
+ if (buf_getbuf16(b, &bb)) return (-1);
+ buf_putmem16(bbb, BBASE(&bb), BSZ(&bb));
+
+ /* --- Add the key indicator --- */
+
+ buf_putu32(bbb, kx->kpub->id);
+
+ /* --- Building the knock payload --- */
+
+ buf_init(&bb, buf_t, sizeof(buf_t));
+ buf_putstr16(&bb, kx->p->spec.knock);
+ sz = BLEN(&bb)%64; if (sz) sz = 64 - sz;
+ if (ies_decrypt(kx->kpriv, MSG_KEYEXCH | KX_TOKEN, b, &bb)) return (-1);
+ p = buf_get(&bb, sz); assert(p); memset(p, 0, sz);
+ assert(BOK(&bb)); buf_flip(&bb);
+ if (ies_encrypt(kx->kpub, MSG_KEYEXCH | KX_KNOCK, &bb, bbb)) return (-1);
+
+ /* --- Finally, the pre-challenge group element --- */
+
+ g->ops->stge(g, bbb, kx->C, DHFMT_VAR);
+
+ /* --- And we're done --- */
+
+ if (BBAD(bbb)) return (-1);
+ update_stats_tx(kx, BLEN(bbb));
+ p_txend(kx->p);
+ return (0);
+}
+