+ 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);