+/* --- @doreply@ --- *
+ *
+ * Arguments: @keyexch *kx@ = pointer to key exchange context
+ * @buf *b@ = buffer containing packet
+ *
+ * Returns: Zero if OK, nonzero if the packet was rejected.
+ *
+ * Use: Handles a reply packet. This doesn't handle the various
+ * switch packets: they're rather too different.
+ */
+
+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));
+ goto bad;
+ }
+ if ((kxc = matchreply(kx, MSG_KEYEXCH | KX_REPLY,
+ hc_in, hc_out, ck, b)) == 0)
+ goto bad;
+ if (BLEFT(b)) {
+ a_warn("invalid reply packet from `%s'", p_name(kx->p));
+ goto bad;
+ }
+ if (kx->s == KXS_CHAL) {
+ commit(kx, kxc);
+ kx->s = KXS_COMMIT;
+ }
+ resend(kx);
+ return (0);
+
+bad:
+ mp_drop(ck);
+ return (-1);
+}
+
+/* --- @doswitch@ --- *
+ *
+ * Arguments: @keyexch *kx@ = pointer to key exchange block
+ * @buf *b@ = pointer to buffer containing packet
+ *
+ * Returns: Zero if OK, nonzero if the packet was rejected.
+ *
+ * Use: Handles a reply with a switch request bolted onto it.
+ */
+
+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));
+ goto bad;
+ }
+ if ((kxc = matchreply(kx, MSG_KEYEXCH | KX_SWITCH,
+ hc_in, hc_out, 0, b)) == 0)
+ goto bad;
+ if ((hswrq = buf_get(b, HASHSZ)) == 0 || BLEFT(b)) {
+ a_warn("invalid switch request from `%s'", p_name(kx->p));
+ goto bad;
+ }
+ IF_TRACING(T_KEYEXCH, {
+ trace_block(T_CRYPTO, "crypto: switch request hash", hswrq, HASHSZ);
+ })
+ if (memcmp(hswrq, kxc->hswrq_in, HASHSZ) != 0) {
+ a_warn("incorrect switch request hash from `%s'", p_name(kx->p));
+ 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;
+ }
+ resend(kx);
+ return (0);
+
+bad:
+ return (-1);
+}
+
+/* --- @doswitchok@ --- *
+ *
+ * Arguments: @keyexch *kx@ = pointer to key exchange block
+ * @buf *b@ = pointer to buffer containing packet
+ *
+ * Returns: Zero if OK, nonzero if the packet was rejected.
+ *
+ * Use: Handles a reply with a switch request bolted onto it.
+ */
+
+static int doswitchok(keyexch *kx, buf *b)
+{
+ const octet *hswok;
+ kxchal *kxc;
+ buf bb;
+
+ if (kx->s < KXS_COMMIT) {
+ a_warn("unexpected switch confirmation from `%s'", p_name(kx->p));
+ 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));
+ 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));
+ goto bad;
+ }
+ IF_TRACING(T_KEYEXCH, {
+ trace_block(T_CRYPTO, "crypto: switch confirmation hash", hswok, HASHSZ);
+ })
+ if (memcmp(hswok, kxc->hswok_in, HASHSZ) != 0) {
+ a_warn("incorrect switch confirmation hash from `%s'", p_name(kx->p));
+ goto bad;
+ }
+ if (kx->s < KXS_SWITCH) {
+ ks_activate(kxc->ks);
+ settimer(kx, ks_tregen(kxc->ks));
+ kx->s = KXS_SWITCH;
+ }
+ return (0);
+
+bad:
+ return (-1);
+}
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @stop@ --- *