[wpa_supplicant] Cumulative patch from commit dd2daf084

Bug: 156933657
Test: Confirm random dialog token usage from logs
Test: Verify Passpoint ANQP functionality and Passpoint association
Test: act.py -c ../WifiDppConfig.json -tc WifiDppTest
Test: Connect to Passpoint, Open, WPA2, WPA3 networks and run traffic
Test: Regression test passed (Bug: 156969218)

dd2daf084 HE: Process HE 6 GHz band capab from associating HE STA
db603634a HE: Add 6 GHz Band Capabilities element in Beacon and response frames
88911a0aa HE: Add HE 6 GHz Band Capabilities into ieee802_11_parse_elems()
b2c0b83c6 HE: Remove VHT Operation Information from HE Operation element
e297a5bfd HE: Define 6 GHz band capability elements
39f29f250 defconfig: Enable TDLS
025ab330b ACS: Channel selection based freqlist
4ae3f3972 Add a helper function for recognizing BIP enum wpa_alg values
d3cab56c0 Rename WPA_ALG_IGTK to use the correct cipher name for BIP
bd1aebbd0 hostapd: Extend RESET_PN for BIGTK
df49c53f4 Fix a typo in a comment
4294d221d D-Bus: Increase introspection buffer size
79488da57 wolfssl: Do not hardcode include directory in wpa_supplicant build
eb595b3e3 wolfssl: Fix crypto_bignum_rand() implementation
6a28c4dbc wolfssl: Fix compiler warnings on size_t printf format use
f2dbaa8ac SAE: Fix a typo in a comment
038899290 wpa_gui: Fix build with Inkscape 1.0
3e1a13010 nl80211: Change AKM suite limit from warning to debug print
5a04a76aa Ignore Management frames while AP interface is not fully enabled
c82535edd Move deauthentication at AP start to be after beacon configuration
094c8a621 Remove unnecessary key clearing at AP start with nl80211
04030e8c0 nl80211: Remove AP mode interface from bridge for STA-mode-scan
7adea21d2 dpp-nfc: Enable hostapd beaconing for listen state
134ad50b0 dpp-nfc: Clean up debug prints when handover select is received
5d49c1bf7 dpp-nfc: Do not indicate a single channel 1 by default
d0e2d8091 dpp-nfc: Make handover request collision detection more robust
8791e7461 dpp-nfc: Write debug info to summary log
1e0bc897a dpp-nfc: Collision detection for handover request
9ad3fe934 dpp-nfc: Start handover server regardless of init-on-touch setting
24efcdf74 dpp-nfc: Own MAC address fetching from hostapd
8f96f2c3b dpp-nfc: Be more graceful when wpa_supplicant is not available
0b04d3c57 dpp-nfc: Allow wpa_supplicant control interface directory to be set
69dfbe6a9 dpp-nfc: Use Configurator/Enrollee parameters with tag reading
f85fb349f dpp-nfc: More robust determination of the script directory
98e4b3840 DPP2: Chirping in hostapd Enrollee
95471fc3e Handle hostapd_for_each_interface() at the process termination
99809c7a4 nl80211: Disable offchannel-ok in AP mode only if beaconing
0f58c88fc DPP2: Fix CONFIG_DPP2=y build with OpenSSL 1.0.2
44f786678 Clean up GET_CAPABILITY handling of 'strict' argument
3790f3a6e Use per-interface type driver key_mgmt capabilities when possible
8d7502809 Allow per interface type AKM capabilities to be fetched
b67bedf2e nl80211: Fetch information on supported AKMs from the driver
6fffb320f nl80211: Remove QCA vendor specific AKM capability handling
db59827a3 DPP2: Extend TCP encapsulation case to support Configurator as Initiator
0086c1452 DPP: Extend NFC bootstrapping script for more control by caller
890ae336c DPP2: Clean up CONFIG_DPP2 use with configurator connectivity IE
670e15337 DPP2: Fix DPP_CHIRP listen parameter value validation
7f20a3ebd DPP2: Reconfiguration support in Controller
6dcb8aaf1 DPP2: Reconfig Announcement relaying from AP to Controller
3b4f7dfaa DPP2: Fix Presence Announcement processing in Controller
5e2d877cc DPP: Mark internal-to-file functions static
3aaf269f6 DPP: Move TCP encapsulation into a separate source code file
21c612017 DPP: Move configurator backup into a separate source code file
fdbbb7406 DPP: Move authentication functionality into a separate source code file
182f6ae90 DPP2: Remove reconfigured network
3e48c5d4b DPP2: Reconfig Authentication Confirm processing
24b01c706 DPP2: Reconfig Authentication Response processing and Confirm generation
65e94351d DPP2: Reconfig Authentication Request processing and Response generation
3774b6bd0 DPP2: Reconfig Authentication Request generation and transmission
66ac616cd DPP2: Process received Reconfig Announcement frame
0c043d9de DPP2: Reconfig Announcement transmission
92492dd3a DPP2: Extend connector matching for reconfiguration
961435097 DPP2: Move connStatus object building into a helper function
94f73f90e DPP: Move signed connector checking into a helper function
94a28a494 DPP: Move parsing of own connector into a helper function
d4ae12355 DPP: Move PKEX functionality into a separate source code file
87b657261 DPP: Move crypto routines into a separate source code file
16626dff9 DPP2: Derive bk ("base key")
76029c6e1 DPP: Use EVP_PKEY_get0_EC_KEY() when a const reference is sufficient
0a488ef35 DPP: Track ending time for remain-on-channel operations
481fdfc46 DPP2: Fix URI version parser
7dd768c3c DPP2: Version information in bootstrapping info URI
cbafc8ef4 Fix truncated control interface command detection
5a0718a19 DPP2: Report MUD URL and bandSupport in control interface events
769139c49 DPP2: Do not include Protocol Version in Auth Req when testing v1
fad64b416 DPP: Move dppCon signing to a set of helper functions
12c8eacf7 DPP: Allow version number to be overridden for testing purposes
c3c38bc8b DPP2: Detect PFS downgrade attack while processing EAPOL-Key msg 3/4
9561925b4 DPP2: Detect PFS downgrade attack while processing EAPOL-Key msg 2/4
68422fedb DPP2: Parse DPP KDE in EAPOL-Key Key Data field
143e3d8bc DPP2: Add DPP KDE into EAPOL-Key msg 2/4 when using DPP AKM
b11a12401 DPP2: Add DPP KDE into EAPOL-Key msg 3/4 when using DPP AKM
85d545699 DPP2: Indicate if PFS was used in control interface STATUS
1f5f00008 DPP2: Try to negotiate PFS only if AP supports version 2 or newer
f6c22dcde Use a local pointer to simply current_ssid accesses in sme_associate()
42acf1292 DPP2: Add Protocol Version attribute to network introduction messages
96b6dd21a Increase wpa_supplicant control interface buffer size
a7d6098fb Add PRINTF_FORMAT for printf wrapper functions

Change-Id: I26a15ac578ce81d1e35f426085661e5c7b43b6ef
diff --git a/src/common/dpp_auth.c b/src/common/dpp_auth.c
new file mode 100644
index 0000000..f79cfef
--- /dev/null
+++ b/src/common/dpp_auth.c
@@ -0,0 +1,1976 @@
+/*
+ * DPP authentication exchange
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "crypto/random.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+u8 dpp_protocol_key_override[600];
+size_t dpp_protocol_key_override_len = 0;
+u8 dpp_nonce_override[DPP_MAX_NONCE_LEN];
+size_t dpp_nonce_override_len = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static void dpp_build_attr_i_bootstrap_key_hash(struct wpabuf *msg,
+						const u8 *hash)
+{
+	if (hash) {
+		wpa_printf(MSG_DEBUG, "DPP: I-Bootstrap Key Hash");
+		wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
+		wpabuf_put_le16(msg, SHA256_MAC_LEN);
+		wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
+	}
+}
+
+
+static void dpp_auth_success(struct dpp_authentication *auth)
+{
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Authentication success - clear temporary keys");
+	os_memset(auth->Mx, 0, sizeof(auth->Mx));
+	auth->Mx_len = 0;
+	os_memset(auth->Nx, 0, sizeof(auth->Nx));
+	auth->Nx_len = 0;
+	os_memset(auth->Lx, 0, sizeof(auth->Lx));
+	auth->Lx_len = 0;
+	os_memset(auth->k1, 0, sizeof(auth->k1));
+	os_memset(auth->k2, 0, sizeof(auth->k2));
+
+	auth->auth_success = 1;
+}
+
+
+static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth,
+					  const struct wpabuf *pi,
+					  size_t nonce_len,
+					  const u8 *r_pubkey_hash,
+					  const u8 *i_pubkey_hash,
+					  unsigned int neg_freq)
+{
+	struct wpabuf *msg;
+	u8 clear[4 + DPP_MAX_NONCE_LEN + 4 + 1];
+	u8 wrapped_data[4 + DPP_MAX_NONCE_LEN + 4 + 1 + AES_BLOCK_SIZE];
+	u8 *pos;
+	const u8 *addr[2];
+	size_t len[2], siv_len, attr_len;
+	u8 *attr_start, *attr_end;
+
+	/* Build DPP Authentication Request frame attributes */
+	attr_len = 2 * (4 + SHA256_MAC_LEN) + 4 + (pi ? wpabuf_len(pi) : 0) +
+		4 + sizeof(wrapped_data);
+	if (neg_freq > 0)
+		attr_len += 4 + 2;
+#ifdef CONFIG_DPP2
+	attr_len += 5;
+#endif /* CONFIG_DPP2 */
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ)
+		attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+	msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ, attr_len);
+	if (!msg)
+		return NULL;
+
+	attr_start = wpabuf_put(msg, 0);
+
+	/* Responder Bootstrapping Key Hash */
+	dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
+
+	/* Initiator Bootstrapping Key Hash */
+	dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
+
+	/* Initiator Protocol Key */
+	if (pi) {
+		wpabuf_put_le16(msg, DPP_ATTR_I_PROTOCOL_KEY);
+		wpabuf_put_le16(msg, wpabuf_len(pi));
+		wpabuf_put_buf(msg, pi);
+	}
+
+	/* Channel */
+	if (neg_freq > 0) {
+		u8 op_class, channel;
+
+		if (ieee80211_freq_to_channel_ext(neg_freq, 0, 0, &op_class,
+						  &channel) ==
+		    NUM_HOSTAPD_MODES) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Unsupported negotiation frequency request: %d",
+				   neg_freq);
+			wpabuf_free(msg);
+			return NULL;
+		}
+		wpabuf_put_le16(msg, DPP_ATTR_CHANNEL);
+		wpabuf_put_le16(msg, 2);
+		wpabuf_put_u8(msg, op_class);
+		wpabuf_put_u8(msg, channel);
+	}
+
+#ifdef CONFIG_DPP2
+	/* Protocol Version */
+	if (DPP_VERSION > 1) {
+		wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+		wpabuf_put_le16(msg, 1);
+		wpabuf_put_u8(msg, DPP_VERSION);
+	}
+#endif /* CONFIG_DPP2 */
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+		goto skip_wrapped_data;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* Wrapped data ({I-nonce, I-capabilities}k1) */
+	pos = clear;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_REQ) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
+		goto skip_i_nonce;
+	}
+	if (dpp_test == DPP_TEST_INVALID_I_NONCE_AUTH_REQ) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-nonce");
+		WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+		pos += 2;
+		WPA_PUT_LE16(pos, nonce_len - 1);
+		pos += 2;
+		os_memcpy(pos, auth->i_nonce, nonce_len - 1);
+		pos += nonce_len - 1;
+		goto skip_i_nonce;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* I-nonce */
+	WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+	pos += 2;
+	WPA_PUT_LE16(pos, nonce_len);
+	pos += 2;
+	os_memcpy(pos, auth->i_nonce, nonce_len);
+	pos += nonce_len;
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_i_nonce:
+	if (dpp_test == DPP_TEST_NO_I_CAPAB_AUTH_REQ) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no I-capab");
+		goto skip_i_capab;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* I-capabilities */
+	WPA_PUT_LE16(pos, DPP_ATTR_I_CAPABILITIES);
+	pos += 2;
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	auth->i_capab = auth->allowed_roles;
+	*pos++ = auth->i_capab;
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_ZERO_I_CAPAB) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - zero I-capabilities");
+		pos[-1] = 0;
+	}
+skip_i_capab:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	attr_end = wpabuf_put(msg, 0);
+
+	/* OUI, OUI type, Crypto Suite, DPP frame type */
+	addr[0] = wpabuf_head_u8(msg) + 2;
+	len[0] = 3 + 1 + 1 + 1;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+	/* Attributes before Wrapped Data */
+	addr[1] = attr_start;
+	len[1] = attr_end - attr_start;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+	siv_len = pos - clear;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
+	if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len,
+			    2, addr, len, wrapped_data) < 0) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+	siv_len += AES_BLOCK_SIZE;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, siv_len);
+
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, siv_len);
+	wpabuf_put_data(msg, wrapped_data, siv_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+		dpp_build_attr_status(msg, DPP_STATUS_OK);
+	}
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Authentication Request frame attributes", msg);
+
+	return msg;
+}
+
+
+static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth,
+					   enum dpp_status_error status,
+					   const struct wpabuf *pr,
+					   size_t nonce_len,
+					   const u8 *r_pubkey_hash,
+					   const u8 *i_pubkey_hash,
+					   const u8 *r_nonce, const u8 *i_nonce,
+					   const u8 *wrapped_r_auth,
+					   size_t wrapped_r_auth_len,
+					   const u8 *siv_key)
+{
+	struct wpabuf *msg;
+#define DPP_AUTH_RESP_CLEAR_LEN 2 * (4 + DPP_MAX_NONCE_LEN) + 4 + 1 + \
+		4 + 4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE
+	u8 clear[DPP_AUTH_RESP_CLEAR_LEN];
+	u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN + AES_BLOCK_SIZE];
+	const u8 *addr[2];
+	size_t len[2], siv_len, attr_len;
+	u8 *attr_start, *attr_end, *pos;
+
+	auth->waiting_auth_conf = 1;
+	auth->auth_resp_tries = 0;
+
+	/* Build DPP Authentication Response frame attributes */
+	attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
+		4 + (pr ? wpabuf_len(pr) : 0) + 4 + sizeof(wrapped_data);
+#ifdef CONFIG_DPP2
+	attr_len += 5;
+#endif /* CONFIG_DPP2 */
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP)
+		attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+	msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len);
+	if (!msg)
+		return NULL;
+
+	attr_start = wpabuf_put(msg, 0);
+
+	/* DPP Status */
+	if (status != 255)
+		dpp_build_attr_status(msg, status);
+
+	/* Responder Bootstrapping Key Hash */
+	dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
+
+	/* Initiator Bootstrapping Key Hash (mutual authentication) */
+	dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
+
+	/* Responder Protocol Key */
+	if (pr) {
+		wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
+		wpabuf_put_le16(msg, wpabuf_len(pr));
+		wpabuf_put_buf(msg, pr);
+	}
+
+#ifdef CONFIG_DPP2
+	/* Protocol Version */
+	if (auth->peer_version >= 2) {
+		wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+		wpabuf_put_le16(msg, 1);
+		wpabuf_put_u8(msg, DPP_VERSION);
+	}
+#endif /* CONFIG_DPP2 */
+
+	attr_end = wpabuf_put(msg, 0);
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+		goto skip_wrapped_data;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */
+	pos = clear;
+
+	if (r_nonce) {
+		/* R-nonce */
+		WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE);
+		pos += 2;
+		WPA_PUT_LE16(pos, nonce_len);
+		pos += 2;
+		os_memcpy(pos, r_nonce, nonce_len);
+		pos += nonce_len;
+	}
+
+	if (i_nonce) {
+		/* I-nonce */
+		WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+		pos += 2;
+		WPA_PUT_LE16(pos, nonce_len);
+		pos += 2;
+		os_memcpy(pos, i_nonce, nonce_len);
+#ifdef CONFIG_TESTING_OPTIONS
+		if (dpp_test == DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP) {
+			wpa_printf(MSG_INFO, "DPP: TESTING - I-nonce mismatch");
+			pos[nonce_len / 2] ^= 0x01;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
+		pos += nonce_len;
+	}
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_NO_R_CAPAB_AUTH_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no R-capab");
+		goto skip_r_capab;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* R-capabilities */
+	WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES);
+	pos += 2;
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
+		DPP_CAPAB_ENROLLEE;
+	*pos++ = auth->r_capab;
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_ZERO_R_CAPAB) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - zero R-capabilities");
+		pos[-1] = 0;
+	} else if (dpp_test == DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP) {
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - incompatible R-capabilities");
+		if ((auth->i_capab & DPP_CAPAB_ROLE_MASK) ==
+		    (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE))
+			pos[-1] = 0;
+		else
+			pos[-1] = auth->configurator ? DPP_CAPAB_ENROLLEE :
+				DPP_CAPAB_CONFIGURATOR;
+	}
+skip_r_capab:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (wrapped_r_auth) {
+		/* {R-auth}ke */
+		WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA);
+		pos += 2;
+		WPA_PUT_LE16(pos, wrapped_r_auth_len);
+		pos += 2;
+		os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len);
+		pos += wrapped_r_auth_len;
+	}
+
+	/* OUI, OUI type, Crypto Suite, DPP frame type */
+	addr[0] = wpabuf_head_u8(msg) + 2;
+	len[0] = 3 + 1 + 1 + 1;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+	/* Attributes before Wrapped Data */
+	addr[1] = attr_start;
+	len[1] = attr_end - attr_start;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+	siv_len = pos - clear;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
+	if (aes_siv_encrypt(siv_key, auth->curve->hash_len, clear, siv_len,
+			    2, addr, len, wrapped_data) < 0) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+	siv_len += AES_BLOCK_SIZE;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, siv_len);
+
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, siv_len);
+	wpabuf_put_data(msg, wrapped_data, siv_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+		dpp_build_attr_status(msg, DPP_STATUS_OK);
+	}
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Authentication Response frame attributes", msg);
+	return msg;
+}
+
+
+static int dpp_auth_build_resp_ok(struct dpp_authentication *auth)
+{
+	size_t nonce_len;
+	size_t secret_len;
+	struct wpabuf *msg, *pr = NULL;
+	u8 r_auth[4 + DPP_MAX_HASH_LEN];
+	u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE], *w_r_auth;
+	size_t wrapped_r_auth_len;
+	int ret = -1;
+	const u8 *r_pubkey_hash, *i_pubkey_hash, *r_nonce, *i_nonce;
+	enum dpp_status_error status = DPP_STATUS_OK;
+#ifdef CONFIG_TESTING_OPTIONS
+	u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
+	if (!auth->own_bi)
+		return -1;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_nonce_override_len > 0) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - override R-nonce");
+		nonce_len = dpp_nonce_override_len;
+		os_memcpy(auth->r_nonce, dpp_nonce_override, nonce_len);
+	} else {
+		nonce_len = auth->curve->nonce_len;
+		if (random_get_bytes(auth->r_nonce, nonce_len)) {
+			wpa_printf(MSG_ERROR,
+				   "DPP: Failed to generate R-nonce");
+			goto fail;
+		}
+	}
+#else /* CONFIG_TESTING_OPTIONS */
+	nonce_len = auth->curve->nonce_len;
+	if (random_get_bytes(auth->r_nonce, nonce_len)) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce");
+		goto fail;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", auth->r_nonce, nonce_len);
+
+	EVP_PKEY_free(auth->own_protocol_key);
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_protocol_key_override_len) {
+		const struct dpp_curve_params *tmp_curve;
+
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - override protocol key");
+		auth->own_protocol_key = dpp_set_keypair(
+			&tmp_curve, dpp_protocol_key_override,
+			dpp_protocol_key_override_len);
+	} else {
+		auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+	}
+#else /* CONFIG_TESTING_OPTIONS */
+	auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (!auth->own_protocol_key)
+		goto fail;
+
+	pr = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+	if (!pr)
+		goto fail;
+
+	/* ECDH: N = pR * PI */
+	if (dpp_ecdh(auth->own_protocol_key, auth->peer_protocol_key,
+		     auth->Nx, &secret_len) < 0)
+		goto fail;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
+			auth->Nx, auth->secret_len);
+	auth->Nx_len = auth->secret_len;
+
+	if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
+			  auth->curve->hash_len) < 0)
+		goto fail;
+
+	if (auth->own_bi && auth->peer_bi) {
+		/* Mutual authentication */
+		if (dpp_auth_derive_l_responder(auth) < 0)
+			goto fail;
+	}
+
+	if (dpp_derive_bk_ke(auth) < 0)
+		goto fail;
+
+	/* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+	WPA_PUT_LE16(r_auth, DPP_ATTR_R_AUTH_TAG);
+	WPA_PUT_LE16(&r_auth[2], auth->curve->hash_len);
+	if (dpp_gen_r_auth(auth, r_auth + 4) < 0)
+		goto fail;
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - R-auth mismatch");
+		r_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+			    r_auth, 4 + auth->curve->hash_len,
+			    0, NULL, NULL, wrapped_r_auth) < 0)
+		goto fail;
+	wrapped_r_auth_len = 4 + auth->curve->hash_len + AES_BLOCK_SIZE;
+	wpa_hexdump(MSG_DEBUG, "DPP: {R-auth}ke",
+		    wrapped_r_auth, wrapped_r_auth_len);
+	w_r_auth = wrapped_r_auth;
+
+	r_pubkey_hash = auth->own_bi->pubkey_hash;
+	if (auth->peer_bi)
+		i_pubkey_hash = auth->peer_bi->pubkey_hash;
+	else
+		i_pubkey_hash = NULL;
+
+	i_nonce = auth->i_nonce;
+	r_nonce = auth->r_nonce;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+		r_pubkey_hash = NULL;
+	} else if (dpp_test ==
+		   DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - invalid R-Bootstrap Key Hash");
+		os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+		test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+		r_pubkey_hash = test_hash;
+	} else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+		i_pubkey_hash = NULL;
+	} else if (dpp_test ==
+		   DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - invalid I-Bootstrap Key Hash");
+		if (i_pubkey_hash)
+			os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+		else
+			os_memset(test_hash, 0, SHA256_MAC_LEN);
+		test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+		i_pubkey_hash = test_hash;
+	} else if (dpp_test == DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no R-Proto Key");
+		wpabuf_free(pr);
+		pr = NULL;
+	} else if (dpp_test == DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - invalid R-Proto Key");
+		wpabuf_free(pr);
+		pr = wpabuf_alloc(2 * auth->curve->prime_len);
+		if (!pr || dpp_test_gen_invalid_key(pr, auth->curve) < 0)
+			goto fail;
+	} else if (dpp_test == DPP_TEST_NO_R_AUTH_AUTH_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth");
+		w_r_auth = NULL;
+		wrapped_r_auth_len = 0;
+	} else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+		status = 255;
+	} else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+		status = 254;
+	} else if (dpp_test == DPP_TEST_NO_R_NONCE_AUTH_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no R-nonce");
+		r_nonce = NULL;
+	} else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
+		i_nonce = NULL;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	msg = dpp_auth_build_resp(auth, status, pr, nonce_len,
+				  r_pubkey_hash, i_pubkey_hash,
+				  r_nonce, i_nonce,
+				  w_r_auth, wrapped_r_auth_len,
+				  auth->k2);
+	if (!msg)
+		goto fail;
+	wpabuf_free(auth->resp_msg);
+	auth->resp_msg = msg;
+	ret = 0;
+fail:
+	wpabuf_free(pr);
+	return ret;
+}
+
+
+static int dpp_auth_build_resp_status(struct dpp_authentication *auth,
+				      enum dpp_status_error status)
+{
+	struct wpabuf *msg;
+	const u8 *r_pubkey_hash, *i_pubkey_hash, *i_nonce;
+#ifdef CONFIG_TESTING_OPTIONS
+	u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (!auth->own_bi)
+		return -1;
+	wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
+
+	r_pubkey_hash = auth->own_bi->pubkey_hash;
+	if (auth->peer_bi)
+		i_pubkey_hash = auth->peer_bi->pubkey_hash;
+	else
+		i_pubkey_hash = NULL;
+
+	i_nonce = auth->i_nonce;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+		r_pubkey_hash = NULL;
+	} else if (dpp_test ==
+		   DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - invalid R-Bootstrap Key Hash");
+		os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+		test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+		r_pubkey_hash = test_hash;
+	} else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+		i_pubkey_hash = NULL;
+	} else if (dpp_test ==
+		   DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - invalid I-Bootstrap Key Hash");
+		if (i_pubkey_hash)
+			os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+		else
+			os_memset(test_hash, 0, SHA256_MAC_LEN);
+		test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+		i_pubkey_hash = test_hash;
+	} else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+		status = 255;
+	} else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
+		i_nonce = NULL;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	msg = dpp_auth_build_resp(auth, status, NULL, auth->curve->nonce_len,
+				  r_pubkey_hash, i_pubkey_hash,
+				  NULL, i_nonce, NULL, 0, auth->k1);
+	if (!msg)
+		return -1;
+	wpabuf_free(auth->resp_msg);
+	auth->resp_msg = msg;
+	return 0;
+}
+
+
+struct dpp_authentication *
+dpp_auth_req_rx(struct dpp_global *dpp, void *msg_ctx, u8 dpp_allowed_roles,
+		int qr_mutual, struct dpp_bootstrap_info *peer_bi,
+		struct dpp_bootstrap_info *own_bi,
+		unsigned int freq, const u8 *hdr, const u8 *attr_start,
+		size_t attr_len)
+{
+	EVP_PKEY *pi = NULL;
+	EVP_PKEY_CTX *ctx = NULL;
+	size_t secret_len;
+	const u8 *addr[2];
+	size_t len[2];
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	const u8 *wrapped_data, *i_proto, *i_nonce, *i_capab, *i_bootstrap,
+		*channel;
+	u16 wrapped_data_len, i_proto_len, i_nonce_len, i_capab_len,
+		i_bootstrap_len, channel_len;
+	struct dpp_authentication *auth = NULL;
+#ifdef CONFIG_DPP2
+	const u8 *version;
+	u16 version_len;
+#endif /* CONFIG_DPP2 */
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_STOP_AT_AUTH_REQ) {
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - stop at Authentication Request");
+		return NULL;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+		wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+			"Missing or invalid required Wrapped Data attribute");
+		return NULL;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
+		    wrapped_data, wrapped_data_len);
+	attr_len = wrapped_data - 4 - attr_start;
+
+	auth = dpp_alloc_auth(dpp, msg_ctx);
+	if (!auth)
+		goto fail;
+	if (peer_bi && peer_bi->configurator_params &&
+	    dpp_set_configurator(auth, peer_bi->configurator_params) < 0)
+		goto fail;
+	auth->peer_bi = peer_bi;
+	auth->own_bi = own_bi;
+	auth->curve = own_bi->curve;
+	auth->curr_freq = freq;
+
+	auth->peer_version = 1; /* default to the first version */
+#ifdef CONFIG_DPP2
+	version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
+			       &version_len);
+	if (version && DPP_VERSION > 1) {
+		if (version_len < 1 || version[0] == 0) {
+			dpp_auth_fail(auth,
+				      "Invalid Protocol Version attribute");
+			goto fail;
+		}
+		auth->peer_version = version[0];
+		wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
+			   auth->peer_version);
+	}
+#endif /* CONFIG_DPP2 */
+
+	channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL,
+			       &channel_len);
+	if (channel) {
+		int neg_freq;
+
+		if (channel_len < 2) {
+			dpp_auth_fail(auth, "Too short Channel attribute");
+			goto fail;
+		}
+
+		neg_freq = ieee80211_chan_to_freq(NULL, channel[0], channel[1]);
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Initiator requested different channel for negotiation: op_class=%u channel=%u --> freq=%d",
+			   channel[0], channel[1], neg_freq);
+		if (neg_freq < 0) {
+			dpp_auth_fail(auth,
+				      "Unsupported Channel attribute value");
+			goto fail;
+		}
+
+		if (auth->curr_freq != (unsigned int) neg_freq) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Changing negotiation channel from %u MHz to %u MHz",
+				   freq, neg_freq);
+			auth->curr_freq = neg_freq;
+		}
+	}
+
+	i_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_PROTOCOL_KEY,
+			       &i_proto_len);
+	if (!i_proto) {
+		dpp_auth_fail(auth,
+			      "Missing required Initiator Protocol Key attribute");
+		goto fail;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key",
+		    i_proto, i_proto_len);
+
+	/* M = bR * PI */
+	pi = dpp_set_pubkey_point(own_bi->pubkey, i_proto, i_proto_len);
+	if (!pi) {
+		dpp_auth_fail(auth, "Invalid Initiator Protocol Key");
+		goto fail;
+	}
+	dpp_debug_print_key("Peer (Initiator) Protocol Key", pi);
+
+	if (dpp_ecdh(own_bi->pubkey, pi, auth->Mx, &secret_len) < 0)
+		goto fail;
+	auth->secret_len = secret_len;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
+			auth->Mx, auth->secret_len);
+	auth->Mx_len = auth->secret_len;
+
+	if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
+			  auth->curve->hash_len) < 0)
+		goto fail;
+
+	addr[0] = hdr;
+	len[0] = DPP_HDR_LEN;
+	addr[1] = attr_start;
+	len[1] = attr_len;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		goto fail;
+	if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    2, addr, len, unwrapped) < 0) {
+		dpp_auth_fail(auth, "AES-SIV decryption failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped, unwrapped_len);
+
+	if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+		dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+			       &i_nonce_len);
+	if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+		dpp_auth_fail(auth, "Missing or invalid I-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+	os_memcpy(auth->i_nonce, i_nonce, i_nonce_len);
+
+	i_capab = dpp_get_attr(unwrapped, unwrapped_len,
+			       DPP_ATTR_I_CAPABILITIES,
+			       &i_capab_len);
+	if (!i_capab || i_capab_len < 1) {
+		dpp_auth_fail(auth, "Missing or invalid I-capabilities");
+		goto fail;
+	}
+	auth->i_capab = i_capab[0];
+	wpa_printf(MSG_DEBUG, "DPP: I-capabilities: 0x%02x", auth->i_capab);
+
+	bin_clear_free(unwrapped, unwrapped_len);
+	unwrapped = NULL;
+
+	switch (auth->i_capab & DPP_CAPAB_ROLE_MASK) {
+	case DPP_CAPAB_ENROLLEE:
+		if (!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Local policy does not allow Configurator role");
+			goto not_compatible;
+		}
+		wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
+		auth->configurator = 1;
+		break;
+	case DPP_CAPAB_CONFIGURATOR:
+		if (!(dpp_allowed_roles & DPP_CAPAB_ENROLLEE)) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Local policy does not allow Enrollee role");
+			goto not_compatible;
+		}
+		wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
+		auth->configurator = 0;
+		break;
+	case DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE:
+		if (dpp_allowed_roles & DPP_CAPAB_ENROLLEE) {
+			wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
+			auth->configurator = 0;
+		} else if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) {
+			wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
+			auth->configurator = 1;
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Local policy does not allow Configurator/Enrollee role");
+			goto not_compatible;
+		}
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "DPP: Unexpected role in I-capabilities");
+		wpa_msg(auth->msg_ctx, MSG_INFO,
+			DPP_EVENT_FAIL "Invalid role in I-capabilities 0x%02x",
+			auth->i_capab & DPP_CAPAB_ROLE_MASK);
+		goto fail;
+	}
+
+	auth->peer_protocol_key = pi;
+	pi = NULL;
+	if (qr_mutual && !peer_bi && own_bi->type == DPP_BOOTSTRAP_QR_CODE) {
+		char hex[SHA256_MAC_LEN * 2 + 1];
+
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Mutual authentication required with QR Codes, but peer info is not yet available - request more time");
+		if (dpp_auth_build_resp_status(auth,
+					       DPP_STATUS_RESPONSE_PENDING) < 0)
+			goto fail;
+		i_bootstrap = dpp_get_attr(attr_start, attr_len,
+					   DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+					   &i_bootstrap_len);
+		if (i_bootstrap && i_bootstrap_len == SHA256_MAC_LEN) {
+			auth->response_pending = 1;
+			os_memcpy(auth->waiting_pubkey_hash,
+				  i_bootstrap, i_bootstrap_len);
+			wpa_snprintf_hex(hex, sizeof(hex), i_bootstrap,
+					 i_bootstrap_len);
+		} else {
+			hex[0] = '\0';
+		}
+
+		wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_SCAN_PEER_QR_CODE
+			"%s", hex);
+		return auth;
+	}
+	if (dpp_auth_build_resp_ok(auth) < 0)
+		goto fail;
+
+	return auth;
+
+not_compatible:
+	wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
+		"i-capab=0x%02x", auth->i_capab);
+	if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)
+		auth->configurator = 1;
+	else
+		auth->configurator = 0;
+	auth->peer_protocol_key = pi;
+	pi = NULL;
+	if (dpp_auth_build_resp_status(auth, DPP_STATUS_NOT_COMPATIBLE) < 0)
+		goto fail;
+
+	auth->remove_on_tx_status = 1;
+	return auth;
+fail:
+	bin_clear_free(unwrapped, unwrapped_len);
+	EVP_PKEY_free(pi);
+	EVP_PKEY_CTX_free(ctx);
+	dpp_auth_deinit(auth);
+	return NULL;
+}
+
+
+int dpp_notify_new_qr_code(struct dpp_authentication *auth,
+			   struct dpp_bootstrap_info *peer_bi)
+{
+	if (!auth || !auth->response_pending ||
+	    os_memcmp(auth->waiting_pubkey_hash, peer_bi->pubkey_hash,
+		      SHA256_MAC_LEN) != 0)
+		return 0;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: New scanned QR Code has matching public key that was needed to continue DPP Authentication exchange with "
+		   MACSTR, MAC2STR(auth->peer_mac_addr));
+	auth->peer_bi = peer_bi;
+
+	if (dpp_auth_build_resp_ok(auth) < 0)
+		return -1;
+
+	return 1;
+}
+
+
+static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth,
+					   enum dpp_status_error status)
+{
+	struct wpabuf *msg;
+	u8 i_auth[4 + DPP_MAX_HASH_LEN];
+	size_t i_auth_len;
+	u8 r_nonce[4 + DPP_MAX_NONCE_LEN];
+	size_t r_nonce_len;
+	const u8 *addr[2];
+	size_t len[2], attr_len;
+	u8 *wrapped_i_auth;
+	u8 *wrapped_r_nonce;
+	u8 *attr_start, *attr_end;
+	const u8 *r_pubkey_hash, *i_pubkey_hash;
+#ifdef CONFIG_TESTING_OPTIONS
+	u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	wpa_printf(MSG_DEBUG, "DPP: Build Authentication Confirmation");
+
+	i_auth_len = 4 + auth->curve->hash_len;
+	r_nonce_len = 4 + auth->curve->nonce_len;
+	/* Build DPP Authentication Confirmation frame attributes */
+	attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
+		4 + i_auth_len + r_nonce_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF)
+		attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+	msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, attr_len);
+	if (!msg)
+		goto fail;
+
+	attr_start = wpabuf_put(msg, 0);
+
+	r_pubkey_hash = auth->peer_bi->pubkey_hash;
+	if (auth->own_bi)
+		i_pubkey_hash = auth->own_bi->pubkey_hash;
+	else
+		i_pubkey_hash = NULL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_NO_STATUS_AUTH_CONF) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+		goto skip_status;
+	} else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_CONF) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+		status = 254;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* DPP Status */
+	dpp_build_attr_status(msg, status);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_status:
+	if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+		r_pubkey_hash = NULL;
+	} else if (dpp_test ==
+		   DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - invalid R-Bootstrap Key Hash");
+		os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+		test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+		r_pubkey_hash = test_hash;
+	} else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+		i_pubkey_hash = NULL;
+	} else if (dpp_test ==
+		   DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - invalid I-Bootstrap Key Hash");
+		if (i_pubkey_hash)
+			os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+		else
+			os_memset(test_hash, 0, SHA256_MAC_LEN);
+		test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+		i_pubkey_hash = test_hash;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* Responder Bootstrapping Key Hash */
+	dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
+
+	/* Initiator Bootstrapping Key Hash (mutual authentication) */
+	dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF)
+		goto skip_wrapped_data;
+	if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
+		i_auth_len = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	attr_end = wpabuf_put(msg, 0);
+
+	/* OUI, OUI type, Crypto Suite, DPP frame type */
+	addr[0] = wpabuf_head_u8(msg) + 2;
+	len[0] = 3 + 1 + 1 + 1;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+	/* Attributes before Wrapped Data */
+	addr[1] = attr_start;
+	len[1] = attr_end - attr_start;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+	if (status == DPP_STATUS_OK) {
+		/* I-auth wrapped with ke */
+		wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+		wpabuf_put_le16(msg, i_auth_len + AES_BLOCK_SIZE);
+		wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE);
+
+#ifdef CONFIG_TESTING_OPTIONS
+		if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
+			goto skip_i_auth;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+		/* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |]
+		 *	      1) */
+		WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG);
+		WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len);
+		if (dpp_gen_i_auth(auth, i_auth + 4) < 0)
+			goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+		if (dpp_test == DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF) {
+			wpa_printf(MSG_INFO, "DPP: TESTING - I-auth mismatch");
+			i_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
+		}
+skip_i_auth:
+#endif /* CONFIG_TESTING_OPTIONS */
+		if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+				    i_auth, i_auth_len,
+				    2, addr, len, wrapped_i_auth) < 0)
+			goto fail;
+		wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke",
+			    wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE);
+	} else {
+		/* R-nonce wrapped with k2 */
+		wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+		wpabuf_put_le16(msg, r_nonce_len + AES_BLOCK_SIZE);
+		wrapped_r_nonce = wpabuf_put(msg, r_nonce_len + AES_BLOCK_SIZE);
+
+		WPA_PUT_LE16(r_nonce, DPP_ATTR_R_NONCE);
+		WPA_PUT_LE16(&r_nonce[2], auth->curve->nonce_len);
+		os_memcpy(r_nonce + 4, auth->r_nonce, auth->curve->nonce_len);
+
+		if (aes_siv_encrypt(auth->k2, auth->curve->hash_len,
+				    r_nonce, r_nonce_len,
+				    2, addr, len, wrapped_r_nonce) < 0)
+			goto fail;
+		wpa_hexdump(MSG_DEBUG, "DPP: {R-nonce}k2",
+			    wrapped_r_nonce, r_nonce_len + AES_BLOCK_SIZE);
+	}
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+		dpp_build_attr_status(msg, DPP_STATUS_OK);
+	}
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Authentication Confirmation frame attributes",
+			msg);
+	if (status == DPP_STATUS_OK)
+		dpp_auth_success(auth);
+
+	return msg;
+
+fail:
+	wpabuf_free(msg);
+	return NULL;
+}
+
+
+static int dpp_autogen_bootstrap_key(struct dpp_authentication *auth)
+{
+	struct dpp_bootstrap_info *bi;
+
+	if (auth->own_bi)
+		return 0; /* already generated */
+
+	bi = os_zalloc(sizeof(*bi));
+	if (!bi)
+		return -1;
+	bi->type = DPP_BOOTSTRAP_QR_CODE;
+	if (dpp_keygen(bi, auth->peer_bi->curve->name, NULL, 0) < 0 ||
+	    dpp_gen_uri(bi) < 0)
+		goto fail;
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Auto-generated own bootstrapping key info: URI %s",
+		   bi->uri);
+
+	auth->tmp_own_bi = auth->own_bi = bi;
+
+	return 0;
+fail:
+	dpp_bootstrap_info_free(bi);
+	return -1;
+}
+
+
+struct dpp_authentication * dpp_auth_init(struct dpp_global *dpp, void *msg_ctx,
+					  struct dpp_bootstrap_info *peer_bi,
+					  struct dpp_bootstrap_info *own_bi,
+					  u8 dpp_allowed_roles,
+					  unsigned int neg_freq,
+					  struct hostapd_hw_modes *own_modes,
+					  u16 num_modes)
+{
+	struct dpp_authentication *auth;
+	size_t nonce_len;
+	size_t secret_len;
+	struct wpabuf *pi = NULL;
+	const u8 *r_pubkey_hash, *i_pubkey_hash;
+#ifdef CONFIG_TESTING_OPTIONS
+	u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	auth = dpp_alloc_auth(dpp, msg_ctx);
+	if (!auth)
+		return NULL;
+	if (peer_bi->configurator_params &&
+	    dpp_set_configurator(auth, peer_bi->configurator_params) < 0)
+		goto fail;
+	auth->initiator = 1;
+	auth->waiting_auth_resp = 1;
+	auth->allowed_roles = dpp_allowed_roles;
+	auth->configurator = !!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR);
+	auth->peer_bi = peer_bi;
+	auth->own_bi = own_bi;
+	auth->curve = peer_bi->curve;
+
+	if (dpp_autogen_bootstrap_key(auth) < 0 ||
+	    dpp_prepare_channel_list(auth, neg_freq, own_modes, num_modes) < 0)
+		goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_nonce_override_len > 0) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - override I-nonce");
+		nonce_len = dpp_nonce_override_len;
+		os_memcpy(auth->i_nonce, dpp_nonce_override, nonce_len);
+	} else {
+		nonce_len = auth->curve->nonce_len;
+		if (random_get_bytes(auth->i_nonce, nonce_len)) {
+			wpa_printf(MSG_ERROR,
+				   "DPP: Failed to generate I-nonce");
+			goto fail;
+		}
+	}
+#else /* CONFIG_TESTING_OPTIONS */
+	nonce_len = auth->curve->nonce_len;
+	if (random_get_bytes(auth->i_nonce, nonce_len)) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce");
+		goto fail;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", auth->i_nonce, nonce_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_protocol_key_override_len) {
+		const struct dpp_curve_params *tmp_curve;
+
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - override protocol key");
+		auth->own_protocol_key = dpp_set_keypair(
+			&tmp_curve, dpp_protocol_key_override,
+			dpp_protocol_key_override_len);
+	} else {
+		auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+	}
+#else /* CONFIG_TESTING_OPTIONS */
+	auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (!auth->own_protocol_key)
+		goto fail;
+
+	pi = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+	if (!pi)
+		goto fail;
+
+	/* ECDH: M = pI * BR */
+	if (dpp_ecdh(auth->own_protocol_key, auth->peer_bi->pubkey,
+		     auth->Mx, &secret_len) < 0)
+		goto fail;
+	auth->secret_len = secret_len;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
+			auth->Mx, auth->secret_len);
+	auth->Mx_len = auth->secret_len;
+
+	if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
+			  auth->curve->hash_len) < 0)
+		goto fail;
+
+	r_pubkey_hash = auth->peer_bi->pubkey_hash;
+	i_pubkey_hash = auth->own_bi->pubkey_hash;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+		r_pubkey_hash = NULL;
+	} else if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - invalid R-Bootstrap Key Hash");
+		os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+		test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+		r_pubkey_hash = test_hash;
+	} else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+		i_pubkey_hash = NULL;
+	} else if (dpp_test == DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - invalid I-Bootstrap Key Hash");
+		os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+		test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+		i_pubkey_hash = test_hash;
+	} else if (dpp_test == DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no I-Proto Key");
+		wpabuf_free(pi);
+		pi = NULL;
+	} else if (dpp_test == DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-Proto Key");
+		wpabuf_free(pi);
+		pi = wpabuf_alloc(2 * auth->curve->prime_len);
+		if (!pi || dpp_test_gen_invalid_key(pi, auth->curve) < 0)
+			goto fail;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (neg_freq && auth->num_freq == 1 && auth->freq[0] == neg_freq)
+		neg_freq = 0;
+	auth->req_msg = dpp_auth_build_req(auth, pi, nonce_len, r_pubkey_hash,
+					   i_pubkey_hash, neg_freq);
+	if (!auth->req_msg)
+		goto fail;
+
+out:
+	wpabuf_free(pi);
+	return auth;
+fail:
+	dpp_auth_deinit(auth);
+	auth = NULL;
+	goto out;
+}
+static void
+dpp_auth_resp_rx_status(struct dpp_authentication *auth, const u8 *hdr,
+			const u8 *attr_start, size_t attr_len,
+			const u8 *wrapped_data, u16 wrapped_data_len,
+			enum dpp_status_error status)
+{
+	const u8 *addr[2];
+	size_t len[2];
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	const u8 *i_nonce, *r_capab;
+	u16 i_nonce_len, r_capab_len;
+
+	if (status == DPP_STATUS_NOT_COMPATIBLE) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Responder reported incompatible roles");
+	} else if (status == DPP_STATUS_RESPONSE_PENDING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Responder reported more time needed");
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Responder reported failure (status %d)",
+			   status);
+		dpp_auth_fail(auth, "Responder reported failure");
+		return;
+	}
+
+	addr[0] = hdr;
+	len[0] = DPP_HDR_LEN;
+	addr[1] = attr_start;
+	len[1] = attr_len;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		goto fail;
+	if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    2, addr, len, unwrapped) < 0) {
+		dpp_auth_fail(auth, "AES-SIV decryption failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped, unwrapped_len);
+
+	if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+		dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+			       &i_nonce_len);
+	if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+		dpp_auth_fail(auth, "Missing or invalid I-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+	if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
+		dpp_auth_fail(auth, "I-nonce mismatch");
+		goto fail;
+	}
+
+	r_capab = dpp_get_attr(unwrapped, unwrapped_len,
+			       DPP_ATTR_R_CAPABILITIES,
+			       &r_capab_len);
+	if (!r_capab || r_capab_len < 1) {
+		dpp_auth_fail(auth, "Missing or invalid R-capabilities");
+		goto fail;
+	}
+	auth->r_capab = r_capab[0];
+	wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
+	if (status == DPP_STATUS_NOT_COMPATIBLE) {
+		wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
+			"r-capab=0x%02x", auth->r_capab);
+	} else if (status == DPP_STATUS_RESPONSE_PENDING) {
+		u8 role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
+
+		if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
+		    (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
+			wpa_msg(auth->msg_ctx, MSG_INFO,
+				DPP_EVENT_FAIL "Unexpected role in R-capabilities 0x%02x",
+				role);
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Continue waiting for full DPP Authentication Response");
+			wpa_msg(auth->msg_ctx, MSG_INFO,
+				DPP_EVENT_RESPONSE_PENDING "%s",
+				auth->tmp_own_bi ? auth->tmp_own_bi->uri : "");
+		}
+	}
+fail:
+	bin_clear_free(unwrapped, unwrapped_len);
+}
+
+
+struct wpabuf *
+dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
+		 const u8 *attr_start, size_t attr_len)
+{
+	EVP_PKEY *pr;
+	size_t secret_len;
+	const u8 *addr[2];
+	size_t len[2];
+	u8 *unwrapped = NULL, *unwrapped2 = NULL;
+	size_t unwrapped_len = 0, unwrapped2_len = 0;
+	const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *r_proto,
+		*r_nonce, *i_nonce, *r_capab, *wrapped2, *r_auth;
+	u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
+		r_proto_len, r_nonce_len, i_nonce_len, r_capab_len,
+		wrapped2_len, r_auth_len;
+	u8 r_auth2[DPP_MAX_HASH_LEN];
+	u8 role;
+#ifdef CONFIG_DPP2
+	const u8 *version;
+	u16 version_len;
+#endif /* CONFIG_DPP2 */
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_STOP_AT_AUTH_RESP) {
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - stop at Authentication Response");
+		return NULL;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (!auth->initiator || !auth->peer_bi || auth->reconfig) {
+		dpp_auth_fail(auth, "Unexpected Authentication Response");
+		return NULL;
+	}
+
+	auth->waiting_auth_resp = 0;
+
+	wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+		dpp_auth_fail(auth,
+			      "Missing or invalid required Wrapped Data attribute");
+		return NULL;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
+		    wrapped_data, wrapped_data_len);
+
+	attr_len = wrapped_data - 4 - attr_start;
+
+	r_bootstrap = dpp_get_attr(attr_start, attr_len,
+				   DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+				   &r_bootstrap_len);
+	if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+		dpp_auth_fail(auth,
+			      "Missing or invalid required Responder Bootstrapping Key Hash attribute");
+		return NULL;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
+		    r_bootstrap, r_bootstrap_len);
+	if (os_memcmp(r_bootstrap, auth->peer_bi->pubkey_hash,
+		      SHA256_MAC_LEN) != 0) {
+		dpp_auth_fail(auth,
+			      "Unexpected Responder Bootstrapping Key Hash value");
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Expected Responder Bootstrapping Key Hash",
+			    auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+		return NULL;
+	}
+
+	i_bootstrap = dpp_get_attr(attr_start, attr_len,
+				   DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+				   &i_bootstrap_len);
+	if (i_bootstrap) {
+		if (i_bootstrap_len != SHA256_MAC_LEN) {
+			dpp_auth_fail(auth,
+				      "Invalid Initiator Bootstrapping Key Hash attribute");
+			return NULL;
+		}
+		wpa_hexdump(MSG_MSGDUMP,
+			    "DPP: Initiator Bootstrapping Key Hash",
+			    i_bootstrap, i_bootstrap_len);
+		if (!auth->own_bi ||
+		    os_memcmp(i_bootstrap, auth->own_bi->pubkey_hash,
+			      SHA256_MAC_LEN) != 0) {
+			dpp_auth_fail(auth,
+				      "Initiator Bootstrapping Key Hash attribute did not match");
+			return NULL;
+		}
+	} else if (auth->own_bi && auth->own_bi->type == DPP_BOOTSTRAP_PKEX) {
+		/* PKEX bootstrapping mandates use of mutual authentication */
+		dpp_auth_fail(auth,
+			      "Missing Initiator Bootstrapping Key Hash attribute");
+		return NULL;
+	} else if (auth->own_bi &&
+		   auth->own_bi->type == DPP_BOOTSTRAP_NFC_URI &&
+		   auth->own_bi->nfc_negotiated) {
+		/* NFC negotiated connection handover bootstrapping mandates
+		 * use of mutual authentication */
+		dpp_auth_fail(auth,
+			      "Missing Initiator Bootstrapping Key Hash attribute");
+		return NULL;
+	}
+
+	auth->peer_version = 1; /* default to the first version */
+#ifdef CONFIG_DPP2
+	version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
+			       &version_len);
+	if (version && DPP_VERSION > 1) {
+		if (version_len < 1 || version[0] == 0) {
+			dpp_auth_fail(auth,
+				      "Invalid Protocol Version attribute");
+			return NULL;
+		}
+		auth->peer_version = version[0];
+		wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
+			   auth->peer_version);
+	}
+#endif /* CONFIG_DPP2 */
+
+	status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
+			      &status_len);
+	if (!status || status_len < 1) {
+		dpp_auth_fail(auth,
+			      "Missing or invalid required DPP Status attribute");
+		return NULL;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+	auth->auth_resp_status = status[0];
+	if (status[0] != DPP_STATUS_OK) {
+		dpp_auth_resp_rx_status(auth, hdr, attr_start,
+					attr_len, wrapped_data,
+					wrapped_data_len, status[0]);
+		return NULL;
+	}
+
+	if (!i_bootstrap && auth->own_bi) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Responder decided not to use mutual authentication");
+		auth->own_bi = NULL;
+	}
+
+	wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_DIRECTION "mutual=%d",
+		auth->own_bi != NULL);
+
+	r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY,
+			       &r_proto_len);
+	if (!r_proto) {
+		dpp_auth_fail(auth,
+			      "Missing required Responder Protocol Key attribute");
+		return NULL;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key",
+		    r_proto, r_proto_len);
+
+	/* N = pI * PR */
+	pr = dpp_set_pubkey_point(auth->own_protocol_key, r_proto, r_proto_len);
+	if (!pr) {
+		dpp_auth_fail(auth, "Invalid Responder Protocol Key");
+		return NULL;
+	}
+	dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
+
+	if (dpp_ecdh(auth->own_protocol_key, pr, auth->Nx, &secret_len) < 0) {
+		dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
+		goto fail;
+	}
+	EVP_PKEY_free(auth->peer_protocol_key);
+	auth->peer_protocol_key = pr;
+	pr = NULL;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
+			auth->Nx, auth->secret_len);
+	auth->Nx_len = auth->secret_len;
+
+	if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
+			  auth->curve->hash_len) < 0)
+		goto fail;
+
+	addr[0] = hdr;
+	len[0] = DPP_HDR_LEN;
+	addr[1] = attr_start;
+	len[1] = attr_len;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		goto fail;
+	if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    2, addr, len, unwrapped) < 0) {
+		dpp_auth_fail(auth, "AES-SIV decryption failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped, unwrapped_len);
+
+	if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+		dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
+			       &r_nonce_len);
+	if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
+		dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
+	os_memcpy(auth->r_nonce, r_nonce, r_nonce_len);
+
+	i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+			       &i_nonce_len);
+	if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+		dpp_auth_fail(auth, "Missing or invalid I-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+	if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
+		dpp_auth_fail(auth, "I-nonce mismatch");
+		goto fail;
+	}
+
+	if (auth->own_bi) {
+		/* Mutual authentication */
+		if (dpp_auth_derive_l_initiator(auth) < 0)
+			goto fail;
+	}
+
+	r_capab = dpp_get_attr(unwrapped, unwrapped_len,
+			       DPP_ATTR_R_CAPABILITIES,
+			       &r_capab_len);
+	if (!r_capab || r_capab_len < 1) {
+		dpp_auth_fail(auth, "Missing or invalid R-capabilities");
+		goto fail;
+	}
+	auth->r_capab = r_capab[0];
+	wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
+	role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
+	if ((auth->allowed_roles ==
+	     (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE)) &&
+	    (role == DPP_CAPAB_CONFIGURATOR || role == DPP_CAPAB_ENROLLEE)) {
+		/* Peer selected its role, so move from "either role" to the
+		 * role that is compatible with peer's selection. */
+		auth->configurator = role == DPP_CAPAB_ENROLLEE;
+		wpa_printf(MSG_DEBUG, "DPP: Acting as %s",
+			   auth->configurator ? "Configurator" : "Enrollee");
+	} else if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
+		   (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
+		wpa_printf(MSG_DEBUG, "DPP: Incompatible role selection");
+		wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+			"Unexpected role in R-capabilities 0x%02x",
+			role);
+		if (role != DPP_CAPAB_ENROLLEE &&
+		    role != DPP_CAPAB_CONFIGURATOR)
+			goto fail;
+		bin_clear_free(unwrapped, unwrapped_len);
+		auth->remove_on_tx_status = 1;
+		return dpp_auth_build_conf(auth, DPP_STATUS_NOT_COMPATIBLE);
+	}
+
+	wrapped2 = dpp_get_attr(unwrapped, unwrapped_len,
+				DPP_ATTR_WRAPPED_DATA, &wrapped2_len);
+	if (!wrapped2 || wrapped2_len < AES_BLOCK_SIZE) {
+		dpp_auth_fail(auth,
+			      "Missing or invalid Secondary Wrapped Data");
+		goto fail;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped2, wrapped2_len);
+
+	if (dpp_derive_bk_ke(auth) < 0)
+		goto fail;
+
+	unwrapped2_len = wrapped2_len - AES_BLOCK_SIZE;
+	unwrapped2 = os_malloc(unwrapped2_len);
+	if (!unwrapped2)
+		goto fail;
+	if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+			    wrapped2, wrapped2_len,
+			    0, NULL, NULL, unwrapped2) < 0) {
+		dpp_auth_fail(auth, "AES-SIV decryption failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped2, unwrapped2_len);
+
+	if (dpp_check_attrs(unwrapped2, unwrapped2_len) < 0) {
+		dpp_auth_fail(auth,
+			      "Invalid attribute in secondary unwrapped data");
+		goto fail;
+	}
+
+	r_auth = dpp_get_attr(unwrapped2, unwrapped2_len, DPP_ATTR_R_AUTH_TAG,
+			       &r_auth_len);
+	if (!r_auth || r_auth_len != auth->curve->hash_len) {
+		dpp_auth_fail(auth,
+			      "Missing or invalid Responder Authenticating Tag");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Received Responder Authenticating Tag",
+		    r_auth, r_auth_len);
+	/* R-auth' = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+	if (dpp_gen_r_auth(auth, r_auth2) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: Calculated Responder Authenticating Tag",
+		    r_auth2, r_auth_len);
+	if (os_memcmp(r_auth, r_auth2, r_auth_len) != 0) {
+		dpp_auth_fail(auth, "Mismatching Responder Authenticating Tag");
+		bin_clear_free(unwrapped, unwrapped_len);
+		bin_clear_free(unwrapped2, unwrapped2_len);
+		auth->remove_on_tx_status = 1;
+		return dpp_auth_build_conf(auth, DPP_STATUS_AUTH_FAILURE);
+	}
+
+	bin_clear_free(unwrapped, unwrapped_len);
+	bin_clear_free(unwrapped2, unwrapped2_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF) {
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - Authentication Response in place of Confirm");
+		if (dpp_auth_build_resp_ok(auth) < 0)
+			return NULL;
+		return wpabuf_dup(auth->resp_msg);
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	return dpp_auth_build_conf(auth, DPP_STATUS_OK);
+
+fail:
+	bin_clear_free(unwrapped, unwrapped_len);
+	bin_clear_free(unwrapped2, unwrapped2_len);
+	EVP_PKEY_free(pr);
+	return NULL;
+}
+
+
+static int dpp_auth_conf_rx_failure(struct dpp_authentication *auth,
+				    const u8 *hdr,
+				    const u8 *attr_start, size_t attr_len,
+				    const u8 *wrapped_data,
+				    u16 wrapped_data_len,
+				    enum dpp_status_error status)
+{
+	const u8 *addr[2];
+	size_t len[2];
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	const u8 *r_nonce;
+	u16 r_nonce_len;
+
+	/* Authentication Confirm failure cases are expected to include
+	 * {R-nonce}k2 in the Wrapped Data attribute. */
+
+	addr[0] = hdr;
+	len[0] = DPP_HDR_LEN;
+	addr[1] = attr_start;
+	len[1] = attr_len;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped) {
+		dpp_auth_fail(auth, "Authentication failed");
+		goto fail;
+	}
+	if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    2, addr, len, unwrapped) < 0) {
+		dpp_auth_fail(auth, "AES-SIV decryption failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped, unwrapped_len);
+
+	if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+		dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
+			       &r_nonce_len);
+	if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
+		dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
+		goto fail;
+	}
+	if (os_memcmp(r_nonce, auth->r_nonce, r_nonce_len) != 0) {
+		wpa_hexdump(MSG_DEBUG, "DPP: Received R-nonce",
+			    r_nonce, r_nonce_len);
+		wpa_hexdump(MSG_DEBUG, "DPP: Expected R-nonce",
+			    auth->r_nonce, r_nonce_len);
+		dpp_auth_fail(auth, "R-nonce mismatch");
+		goto fail;
+	}
+
+	if (status == DPP_STATUS_NOT_COMPATIBLE)
+		dpp_auth_fail(auth, "Peer reported incompatible R-capab role");
+	else if (status == DPP_STATUS_AUTH_FAILURE)
+		dpp_auth_fail(auth, "Peer reported authentication failure)");
+
+fail:
+	bin_clear_free(unwrapped, unwrapped_len);
+	return -1;
+}
+
+
+int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
+		     const u8 *attr_start, size_t attr_len)
+{
+	const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *i_auth;
+	u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
+		i_auth_len;
+	const u8 *addr[2];
+	size_t len[2];
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	u8 i_auth2[DPP_MAX_HASH_LEN];
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - stop at Authentication Confirm");
+		return -1;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (auth->initiator || !auth->own_bi || !auth->waiting_auth_conf ||
+	    auth->reconfig) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: initiator=%d own_bi=%d waiting_auth_conf=%d",
+			   auth->initiator, !!auth->own_bi,
+			   auth->waiting_auth_conf);
+		dpp_auth_fail(auth, "Unexpected Authentication Confirm");
+		return -1;
+	}
+
+	auth->waiting_auth_conf = 0;
+
+	wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+		dpp_auth_fail(auth,
+			      "Missing or invalid required Wrapped Data attribute");
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
+		    wrapped_data, wrapped_data_len);
+
+	attr_len = wrapped_data - 4 - attr_start;
+
+	r_bootstrap = dpp_get_attr(attr_start, attr_len,
+				   DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+				   &r_bootstrap_len);
+	if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+		dpp_auth_fail(auth,
+			      "Missing or invalid required Responder Bootstrapping Key Hash attribute");
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
+		    r_bootstrap, r_bootstrap_len);
+	if (os_memcmp(r_bootstrap, auth->own_bi->pubkey_hash,
+		      SHA256_MAC_LEN) != 0) {
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Expected Responder Bootstrapping Key Hash",
+			    auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+		dpp_auth_fail(auth,
+			      "Responder Bootstrapping Key Hash mismatch");
+		return -1;
+	}
+
+	i_bootstrap = dpp_get_attr(attr_start, attr_len,
+				   DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+				   &i_bootstrap_len);
+	if (i_bootstrap) {
+		if (i_bootstrap_len != SHA256_MAC_LEN) {
+			dpp_auth_fail(auth,
+				      "Invalid Initiator Bootstrapping Key Hash attribute");
+			return -1;
+		}
+		wpa_hexdump(MSG_MSGDUMP,
+			    "DPP: Initiator Bootstrapping Key Hash",
+			    i_bootstrap, i_bootstrap_len);
+		if (!auth->peer_bi ||
+		    os_memcmp(i_bootstrap, auth->peer_bi->pubkey_hash,
+			      SHA256_MAC_LEN) != 0) {
+			dpp_auth_fail(auth,
+				      "Initiator Bootstrapping Key Hash mismatch");
+			return -1;
+		}
+	} else if (auth->peer_bi) {
+		/* Mutual authentication and peer did not include its
+		 * Bootstrapping Key Hash attribute. */
+		dpp_auth_fail(auth,
+			      "Missing Initiator Bootstrapping Key Hash attribute");
+		return -1;
+	}
+
+	status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
+			      &status_len);
+	if (!status || status_len < 1) {
+		dpp_auth_fail(auth,
+			      "Missing or invalid required DPP Status attribute");
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+	if (status[0] == DPP_STATUS_NOT_COMPATIBLE ||
+	    status[0] == DPP_STATUS_AUTH_FAILURE)
+		return dpp_auth_conf_rx_failure(auth, hdr, attr_start,
+						attr_len, wrapped_data,
+						wrapped_data_len, status[0]);
+
+	if (status[0] != DPP_STATUS_OK) {
+		dpp_auth_fail(auth, "Authentication failed");
+		return -1;
+	}
+
+	addr[0] = hdr;
+	len[0] = DPP_HDR_LEN;
+	addr[1] = attr_start;
+	len[1] = attr_len;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		return -1;
+	if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    2, addr, len, unwrapped) < 0) {
+		dpp_auth_fail(auth, "AES-SIV decryption failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped, unwrapped_len);
+
+	if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+		dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	i_auth = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
+			      &i_auth_len);
+	if (!i_auth || i_auth_len != auth->curve->hash_len) {
+		dpp_auth_fail(auth,
+			      "Missing or invalid Initiator Authenticating Tag");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Received Initiator Authenticating Tag",
+		    i_auth, i_auth_len);
+	/* I-auth' = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
+	if (dpp_gen_i_auth(auth, i_auth2) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: Calculated Initiator Authenticating Tag",
+		    i_auth2, i_auth_len);
+	if (os_memcmp(i_auth, i_auth2, i_auth_len) != 0) {
+		dpp_auth_fail(auth, "Mismatching Initiator Authenticating Tag");
+		goto fail;
+	}
+
+	bin_clear_free(unwrapped, unwrapped_len);
+	dpp_auth_success(auth);
+	return 0;
+fail:
+	bin_clear_free(unwrapped, unwrapped_len);
+	return -1;
+}