[automerger skipped] Merge "P2P: Fix a corner case in peer addition based on PD Request" into pi-dev am: 47afebf11e -s ours am: b897a71946 -s ours am: 5a7b49e449 -s ours am: 687ceb1bd2 -s ours

am skip reason: skip tag Change-Id Ief7781491bd545fc1028b9b386fe7a4564102848 with SHA-1 61dece6d28 is already in history

Original change: https://googleplex-android-review.googlesource.com/c/platform/external/wpa_supplicant_8/+/13856735

Change-Id: Idc4c80940db4f52e8e13d58cdceae5eb2226405a
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index 3bde8d5..f2b15d3 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -552,6 +552,12 @@
 ifdef CONFIG_DPP
 L_CFLAGS += -DCONFIG_DPP
 OBJS += src/common/dpp.c
+OBJS += src/common/dpp_auth.c
+OBJS += src/common/dpp_backup.c
+OBJS += src/common/dpp_crypto.c
+OBJS += src/common/dpp_pkex.c
+OBJS += src/common/dpp_reconfig.c
+OBJS += src/common/dpp_tcp.c
 OBJS += src/ap/dpp_hostapd.c
 OBJS += src/ap/gas_query_ap.c
 NEED_AES_SIV=y
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 9c7fc5c..95ef085 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -572,6 +572,12 @@
 ifdef CONFIG_DPP
 CFLAGS += -DCONFIG_DPP
 OBJS += ../src/common/dpp.o
+OBJS += ../src/common/dpp_auth.o
+OBJS += ../src/common/dpp_backup.o
+OBJS += ../src/common/dpp_crypto.o
+OBJS += ../src/common/dpp_pkex.o
+OBJS += ../src/common/dpp_reconfig.o
+OBJS += ../src/common/dpp_tcp.o
 OBJS += ../src/ap/dpp_hostapd.o
 OBJS += ../src/ap/gas_query_ap.o
 NEED_AES_SIV=y
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index 6e8352f..edc69f4 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -1428,6 +1428,8 @@
 		hapd->dpp_ignore_netaccesskey_mismatch = atoi(value);
 	} else if (os_strcasecmp(cmd, "dpp_test") == 0) {
 		dpp_test = atoi(value);
+	} else if (os_strcasecmp(cmd, "dpp_version_override") == 0) {
+		dpp_version_override = atoi(value);
 #endif /* CONFIG_DPP */
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifdef CONFIG_MBO
@@ -2163,6 +2165,32 @@
 	if (hwaddr_aton(cmd, addr))
 		return -1;
 
+	if (is_broadcast_ether_addr(addr) && os_strstr(cmd, " BIGTK")) {
+		if (hapd->last_bigtk_alg == WPA_ALG_NONE)
+			return -1;
+
+		wpa_printf(MSG_INFO, "TESTING: Reset BIPN for BIGTK");
+
+		/* First, use a zero key to avoid any possible duplicate key
+		 * avoidance in the driver. */
+		if (hostapd_drv_set_key(hapd->conf->iface, hapd,
+					hapd->last_bigtk_alg,
+					broadcast_ether_addr,
+					hapd->last_bigtk_key_idx, 0, 1, NULL, 0,
+					zero, hapd->last_bigtk_len,
+					KEY_FLAG_GROUP_TX_DEFAULT) < 0)
+			return -1;
+
+		/* Set the previously configured key to reset its TSC */
+		return hostapd_drv_set_key(hapd->conf->iface, hapd,
+					   hapd->last_bigtk_alg,
+					   broadcast_ether_addr,
+					   hapd->last_bigtk_key_idx, 0, 1, NULL,
+					   0, hapd->last_bigtk,
+					   hapd->last_bigtk_len,
+					   KEY_FLAG_GROUP_TX_DEFAULT);
+	}
+
 	if (is_broadcast_ether_addr(addr) && os_strstr(cmd, "IGTK")) {
 		if (hapd->last_igtk_alg == WPA_ALG_NONE)
 			return -1;
@@ -3706,6 +3734,13 @@
 	} else if (os_strncmp(buf, "DPP_PKEX_REMOVE ", 16) == 0) {
 		if (hostapd_dpp_pkex_remove(hapd, buf + 16) < 0)
 			reply_len = -1;
+#ifdef CONFIG_DPP2
+	} else if (os_strncmp(buf, "DPP_CHIRP ", 10) == 0) {
+		if (hostapd_dpp_chirp(hapd, buf + 9) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "DPP_STOP_CHIRP") == 0) {
+		hostapd_dpp_chirp_stop(hapd);
+#endif /* CONFIG_DPP2 */
 #endif /* CONFIG_DPP */
 #ifdef RADIUS_SERVER
 	} else if (os_strncmp(buf, "DAC_REQUEST ", 12) == 0) {
@@ -4193,6 +4228,11 @@
 #ifdef CONFIG_TESTING_OPTIONS
 #ifdef CONFIG_DPP
 	dpp_test = DPP_TEST_DISABLED;
+#ifdef CONFIG_DPP2
+	dpp_version_override = 2;
+#else /* CONFIG_DPP2 */
+	dpp_version_override = 1;
+#endif /* CONFIG_DPP2 */
 #endif /* CONFIG_DPP */
 #endif /* CONFIG_TESTING_OPTIONS */
 
diff --git a/hostapd/hidl/1.2/hostapd.cpp b/hostapd/hidl/1.2/hostapd.cpp
index 537353a..7789ed6 100644
--- a/hostapd/hidl/1.2/hostapd.cpp
+++ b/hostapd/hidl/1.2/hostapd.cpp
@@ -196,6 +196,10 @@
 			// 160MHz channel
 			return 134;
 		}
+		if (channel == 2) {
+			// 20MHz channel
+			return 136;
+		}
 		// Error
 		return 0;
 	}
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 812c09a..9fae06d 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -210,7 +210,7 @@
 # Frequency list can be provided as range using hyphen ('-') or individual
 # frequencies can be specified by comma (',') separated values
 # Default: all frequencies allowed in selected hw_mode
-#freqlist=2437,5945,5965
+#freqlist=2437,5955,5975
 #freqlist=2437,5985-6105
 
 # Exclude DFS channels from ACS
@@ -822,11 +822,11 @@
 #he_rts_threshold=0
 
 # HE operating channel information; see matching vht_* parameters for details.
-# On the 6 GHz band the center freq calculation starts from 5.940 GHz offset.
-# For example idx=3 would result in 5955 MHz center frequency. In addition,
+# On the 6 GHz band the center freq calculation starts from 5.950 GHz offset.
+# For example idx=3 would result in 5965 MHz center frequency. In addition,
 # he_oper_chwidth is ignored, and the channel width is derived from the
 # configured operating class or center frequency indexes (see
-# IEEE P802.11ax/D4.3 Annex E, Table E-4).
+# IEEE P802.11ax/D6.1 Annex E, Table E-4).
 #he_oper_chwidth
 #he_oper_centr_freq_seg0_idx
 #he_oper_centr_freq_seg1_idx
diff --git a/hostapd/main.c b/hostapd/main.c
index 3ce8126..cdcb692 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -676,7 +676,11 @@
 #endif /* CONFIG_ETH_P_OUI */
 #ifdef CONFIG_DPP
 	os_memset(&dpp_conf, 0, sizeof(dpp_conf));
+	dpp_conf.cb_ctx = &interfaces;
 	/* TODO: dpp_conf.msg_ctx? */
+#ifdef CONFIG_DPP2
+	dpp_conf.remove_bi = hostapd_dpp_remove_bi;
+#endif /* CONFIG_DPP2 */
 	interfaces.dpp = dpp_global_init(&dpp_conf);
 	if (!interfaces.dpp)
 		return -1;
@@ -916,8 +920,11 @@
 			!!(interfaces.iface[i]->drv_flags &
 			   WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
 		hostapd_interface_deinit_free(interfaces.iface[i]);
+		interfaces.iface[i] = NULL;
 	}
 	os_free(interfaces.iface);
+	interfaces.iface = NULL;
+	interfaces.count = 0;
 
 #ifdef CONFIG_DPP
 	dpp_global_deinit(interfaces.dpp);
diff --git a/src/ap/acs.c b/src/ap/acs.c
index 5c01610..aa2ceb0 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -512,6 +512,17 @@
 }
 
 
+static int is_in_freqlist(struct hostapd_iface *iface,
+			  struct hostapd_channel_data *chan)
+{
+	if (!iface->conf->acs_freq_list.num)
+		return 1;
+
+	return freq_range_list_includes(&iface->conf->acs_freq_list,
+					chan->freq);
+}
+
+
 static void acs_survey_mode_interference_factor(
 	struct hostapd_iface *iface, struct hostapd_hw_modes *mode)
 {
@@ -527,6 +538,9 @@
 		if (!is_in_chanlist(iface, chan))
 			continue;
 
+		if (!is_in_freqlist(iface, chan))
+			continue;
+
 		wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)",
 			   chan->chan, chan->freq);
 
@@ -651,6 +665,9 @@
 		if (!is_in_chanlist(iface, chan))
 			continue;
 
+		if (!is_in_freqlist(iface, chan))
+			continue;
+
 		if (!chan_bw_allowed(chan, bw, 1, 1)) {
 			wpa_printf(MSG_DEBUG,
 				   "ACS: Channel %d: BW %u is not supported",
@@ -1013,6 +1030,9 @@
 		if (!is_in_chanlist(iface, chan))
 			continue;
 
+		if (!is_in_freqlist(iface, chan))
+			continue;
+
 		*freq++ = chan->freq;
 	}
 
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 1f284f0..f157659 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -418,6 +418,7 @@
 		    const struct ieee80211_vht_capabilities *vht_capab,
 		    const struct ieee80211_he_capabilities *he_capab,
 		    size_t he_capab_len,
+		    const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
 		    u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
 		    int set)
 {
@@ -439,6 +440,7 @@
 	params.vht_capabilities = vht_capab;
 	params.he_capab = he_capab;
 	params.he_capab_len = he_capab_len;
+	params.he_6ghz_capab = he_6ghz_capab;
 	params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED);
 	params.vht_opmode = vht_opmode;
 	params.flags = hostapd_sta_flags_to_drv(flags);
@@ -650,6 +652,12 @@
 }
 
 
+bool hostapd_drv_nl80211(struct hostapd_data *hapd)
+{
+	return hapd->driver && os_strcmp(hapd->driver->name, "nl80211") == 0;
+}
+
+
 int hostapd_driver_scan(struct hostapd_data *hapd,
 			struct wpa_driver_scan_params *params)
 {
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 56d1ad8..5738c1c 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -43,6 +43,7 @@
 		    const struct ieee80211_vht_capabilities *vht_capab,
 		    const struct ieee80211_he_capabilities *he_capab,
 		    size_t he_capab_len,
+		    const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
 		    u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
 		    int set);
 int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
@@ -80,6 +81,7 @@
 			    u16 *flags, u8 *dfs_domain);
 int hostapd_driver_commit(struct hostapd_data *hapd);
 int hostapd_drv_none(struct hostapd_data *hapd);
+bool hostapd_drv_nl80211(struct hostapd_data *hapd);
 int hostapd_driver_scan(struct hostapd_data *hapd,
 			struct wpa_driver_scan_params *params);
 struct wpa_scan_results * hostapd_driver_get_scan_results(
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 47ced9a..22e672c 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -463,6 +463,9 @@
 			3 + sizeof(struct ieee80211_he_operation) +
 			3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
 			3 + sizeof(struct ieee80211_spatial_reuse);
+		if (is_6ghz_op_class(hapd->iconf->op_class))
+			buflen += sizeof(struct ieee80211_he_6ghz_oper_info) +
+				3 + sizeof(struct ieee80211_he_6ghz_band_cap);
 	}
 #endif /* CONFIG_IEEE80211AX */
 
@@ -570,6 +573,7 @@
 		pos = hostapd_eid_he_operation(hapd, pos);
 		pos = hostapd_eid_spatial_reuse(hapd, pos);
 		pos = hostapd_eid_he_mu_edca_parameter_set(hapd, pos);
+		pos = hostapd_eid_he_6ghz_band_cap(hapd, pos);
 	}
 #endif /* CONFIG_IEEE80211AX */
 
@@ -1161,6 +1165,9 @@
 			3 + sizeof(struct ieee80211_he_operation) +
 			3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
 			3 + sizeof(struct ieee80211_spatial_reuse);
+		if (is_6ghz_op_class(hapd->iconf->op_class))
+			tail_len += sizeof(struct ieee80211_he_6ghz_oper_info) +
+				3 + sizeof(struct ieee80211_he_6ghz_band_cap);
 	}
 #endif /* CONFIG_IEEE80211AX */
 
@@ -1288,6 +1295,7 @@
 		tailpos = hostapd_eid_he_operation(hapd, tailpos);
 		tailpos = hostapd_eid_spatial_reuse(hapd, tailpos);
 		tailpos = hostapd_eid_he_mu_edca_parameter_set(hapd, tailpos);
+		tailpos = hostapd_eid_he_6ghz_band_cap(hapd, tailpos);
 	}
 #endif /* CONFIG_IEEE80211AX */
 
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index c86f01b..178fd12 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -26,6 +26,10 @@
 static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator);
 static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx);
 static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd);
+#ifdef CONFIG_DPP2
+static void hostapd_dpp_reconfig_reply_wait_timeout(void *eloop_ctx,
+						    void *timeout_ctx);
+#endif /* CONFIG_DPP2 */
 
 static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
@@ -237,6 +241,10 @@
 				     hapd, NULL);
 		eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd,
 				     NULL);
+#ifdef CONFIG_DPP2
+		eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+				     hapd, NULL);
+#endif /* CONFIG_DPP2 */
 		hostapd_drv_send_action_cancel_wait(hapd);
 		dpp_auth_deinit(hapd->dpp_auth);
 		hapd->dpp_auth = NULL;
@@ -539,6 +547,10 @@
 				     hapd, NULL);
 		eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd,
 				     NULL);
+#ifdef CONFIG_DPP2
+		eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+				     hapd, NULL);
+#endif /* CONFIG_DPP2 */
 		hostapd_drv_send_action_cancel_wait(hapd);
 		dpp_auth_deinit(hapd->dpp_auth);
 	}
@@ -618,6 +630,10 @@
 	wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR,
 		   MAC2STR(src));
 
+#ifdef CONFIG_DPP2
+	hostapd_dpp_chirp_stop(hapd);
+#endif /* CONFIG_DPP2 */
+
 	r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
 				   &r_bootstrap_len);
 	if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
@@ -1197,6 +1213,145 @@
 	}
 }
 
+
+static void hostapd_dpp_reconfig_reply_wait_timeout(void *eloop_ctx,
+						    void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
+	if (!auth)
+		return;
+
+	wpa_printf(MSG_DEBUG, "DPP: Reconfig Reply wait timeout");
+	hostapd_dpp_listen_stop(hapd);
+	dpp_auth_deinit(auth);
+	hapd->dpp_auth = NULL;
+}
+
+
+static void
+hostapd_dpp_rx_reconfig_announcement(struct hostapd_data *hapd, const u8 *src,
+				     const u8 *hdr, const u8 *buf, size_t len,
+				     unsigned int freq)
+{
+	const u8 *csign_hash;
+	u16 csign_hash_len;
+	struct dpp_configurator *conf;
+	struct dpp_authentication *auth;
+	unsigned int wait_time, max_wait_time;
+
+	if (hapd->dpp_auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore Reconfig Announcement during ongoing Authentication");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Reconfig Announcement from " MACSTR,
+		   MAC2STR(src));
+
+	csign_hash = dpp_get_attr(buf, len, DPP_ATTR_C_SIGN_KEY_HASH,
+				  &csign_hash_len);
+	if (!csign_hash || csign_hash_len != SHA256_MAC_LEN) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+			"Missing or invalid required Configurator C-sign key Hash attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Configurator C-sign key Hash (kid)",
+		    csign_hash, csign_hash_len);
+	conf = dpp_configurator_find_kid(hapd->iface->interfaces->dpp,
+					 csign_hash);
+	if (!conf) {
+		if (dpp_relay_rx_action(hapd->iface->interfaces->dpp,
+					src, hdr, buf, len, freq, NULL,
+					NULL) == 0)
+			return;
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No matching Configurator information found");
+		return;
+	}
+
+	auth = dpp_reconfig_init(hapd->iface->interfaces->dpp, hapd->msg_ctx,
+				 conf, freq);
+	if (!auth)
+		return;
+	hostapd_dpp_set_testing_options(hapd, auth);
+	if (dpp_set_configurator(auth, hapd->dpp_configurator_params) < 0) {
+		dpp_auth_deinit(auth);
+		return;
+	}
+
+	os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
+	hapd->dpp_auth = auth;
+
+	hapd->dpp_in_response_listen = 0;
+	hapd->dpp_auth_ok_on_ack = 0;
+	wait_time = 2000; /* TODO: hapd->max_remain_on_chan; */
+	max_wait_time = hapd->dpp_resp_wait_time ?
+		hapd->dpp_resp_wait_time : 2000;
+	if (wait_time > max_wait_time)
+		wait_time = max_wait_time;
+	wait_time += 10; /* give the driver some extra time to complete */
+	eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
+			       hostapd_dpp_reconfig_reply_wait_timeout,
+			       hapd, NULL);
+	wait_time -= 10;
+
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d",
+		MAC2STR(src), freq, DPP_PA_RECONFIG_AUTH_REQ);
+	if (hostapd_drv_send_action(hapd, freq, wait_time, src,
+				    wpabuf_head(auth->reconfig_req_msg),
+				    wpabuf_len(auth->reconfig_req_msg)) < 0) {
+		dpp_auth_deinit(hapd->dpp_auth);
+		hapd->dpp_auth = NULL;
+	}
+}
+
+
+static void
+hostapd_dpp_rx_reconfig_auth_resp(struct hostapd_data *hapd, const u8 *src,
+				  const u8 *hdr, const u8 *buf, size_t len,
+				  unsigned int freq)
+{
+	struct dpp_authentication *auth = hapd->dpp_auth;
+	struct wpabuf *conf;
+
+	wpa_printf(MSG_DEBUG, "DPP: Reconfig Authentication Response from "
+		   MACSTR, MAC2STR(src));
+
+	if (!auth || !auth->reconfig || !auth->configurator) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Reconfig Authentication in progress - drop");
+		return;
+	}
+
+	if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+			   MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+		return;
+	}
+
+	conf = dpp_reconfig_auth_resp_rx(auth, hdr, buf, len);
+	if (!conf)
+		return;
+
+	eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+			     hapd, NULL);
+
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d",
+		MAC2STR(src), freq, DPP_PA_RECONFIG_AUTH_CONF);
+	if (hostapd_drv_send_action(hapd, freq, 500, src,
+				    wpabuf_head(conf), wpabuf_len(conf)) < 0) {
+		wpabuf_free(conf);
+		dpp_auth_deinit(hapd->dpp_auth);
+		hapd->dpp_auth = NULL;
+		return;
+	}
+	wpabuf_free(conf);
+}
+
 #endif /* CONFIG_DPP2 */
 
 
@@ -1206,9 +1361,13 @@
 					    enum dpp_status_error status)
 {
 	struct wpabuf *msg;
+	size_t len;
 
-	msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_RESP,
-			    5 + 5 + 4 + os_strlen(hapd->conf->dpp_connector));
+	len = 5 + 5 + 4 + os_strlen(hapd->conf->dpp_connector);
+#ifdef CONFIG_DPP2
+	len += 5;
+#endif /* CONFIG_DPP2 */
+	msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_RESP, len);
 	if (!msg)
 		return;
 
@@ -1281,6 +1440,15 @@
 skip_connector:
 #endif /* CONFIG_TESTING_OPTIONS */
 
+#ifdef CONFIG_DPP2
+	if (DPP_VERSION > 1) {
+		/* Protocol Version */
+		wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+		wpabuf_put_le16(msg, 1);
+		wpabuf_put_u8(msg, DPP_VERSION);
+	}
+#endif /* CONFIG_DPP2 */
+
 	wpa_printf(MSG_DEBUG, "DPP: Send Peer Discovery Response to " MACSTR
 		   " status=%d", MAC2STR(src), status);
 	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
@@ -1650,6 +1818,14 @@
 		hostapd_dpp_rx_presence_announcement(hapd, src, hdr, buf, len,
 						     freq);
 		break;
+	case DPP_PA_RECONFIG_ANNOUNCEMENT:
+		hostapd_dpp_rx_reconfig_announcement(hapd, src, hdr, buf, len,
+						     freq);
+		break;
+	case DPP_PA_RECONFIG_AUTH_RESP:
+		hostapd_dpp_rx_reconfig_auth_resp(hapd, src, hdr, buf, len,
+						  freq);
+		break;
 #endif /* CONFIG_DPP2 */
 	default:
 		wpa_printf(MSG_DEBUG,
@@ -1679,7 +1855,7 @@
 	struct wpabuf *resp;
 
 	wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa));
-	if (!auth || !auth->auth_success ||
+	if (!auth || (!auth->auth_success && !auth->reconfig_success) ||
 	    os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
 #ifdef CONFIG_DPP2
 		if (dpp_relay_rx_gas_req(hapd->iface->interfaces->dpp, sa, data,
@@ -1715,6 +1891,8 @@
 	eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
 	eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
 #ifdef CONFIG_DPP2
+		eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+				     hapd, NULL);
 	if (ok && auth->peer_version >= 2 &&
 	    auth->conf_resp_status == DPP_STATUS_OK) {
 		wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result");
@@ -1959,10 +2137,13 @@
 	eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
 	eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
 #ifdef CONFIG_DPP2
+	eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+			     hapd, NULL);
 	eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd,
 			     NULL);
 	eloop_cancel_timeout(hostapd_dpp_conn_status_result_wait_timeout, hapd,
 			     NULL);
+	hostapd_dpp_chirp_stop(hapd);
 #endif /* CONFIG_DPP2 */
 	dpp_auth_deinit(hapd->dpp_auth);
 	hapd->dpp_auth = NULL;
@@ -1971,3 +2152,320 @@
 	os_free(hapd->dpp_configurator_params);
 	hapd->dpp_configurator_params = NULL;
 }
+
+
+#ifdef CONFIG_DPP2
+
+static void hostapd_dpp_chirp_next(void *eloop_ctx, void *timeout_ctx);
+
+static void hostapd_dpp_chirp_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+
+	wpa_printf(MSG_DEBUG, "DPP: No chirp response received");
+	hostapd_drv_send_action_cancel_wait(hapd);
+	hostapd_dpp_chirp_next(hapd, NULL);
+}
+
+
+static void hostapd_dpp_chirp_start(struct hostapd_data *hapd)
+{
+	struct wpabuf *msg;
+	int type;
+
+	msg = hapd->dpp_presence_announcement;
+	type = DPP_PA_PRESENCE_ANNOUNCEMENT;
+	wpa_printf(MSG_DEBUG, "DPP: Chirp on %d MHz", hapd->dpp_chirp_freq);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d",
+		MAC2STR(broadcast), hapd->dpp_chirp_freq, type);
+	if (hostapd_drv_send_action(
+		    hapd, hapd->dpp_chirp_freq, 2000, broadcast,
+		    wpabuf_head(msg), wpabuf_len(msg)) < 0 ||
+	    eloop_register_timeout(2, 0, hostapd_dpp_chirp_timeout,
+				   hapd, NULL) < 0)
+		hostapd_dpp_chirp_stop(hapd);
+}
+
+
+static struct hostapd_hw_modes *
+dpp_get_mode(struct hostapd_data *hapd,
+	     enum hostapd_hw_mode mode)
+{
+	struct hostapd_hw_modes *modes = hapd->iface->hw_features;
+	u16 num_modes = hapd->iface->num_hw_features;
+	u16 i;
+
+	for (i = 0; i < num_modes; i++) {
+		if (modes[i].mode != mode ||
+		    !modes[i].num_channels || !modes[i].channels)
+			continue;
+		return &modes[i];
+	}
+
+	return NULL;
+}
+
+
+static void
+hostapd_dpp_chirp_scan_res_handler(struct hostapd_iface *iface)
+{
+	struct hostapd_data *hapd = iface->bss[0];
+	struct wpa_scan_results *scan_res;
+	struct dpp_bootstrap_info *bi = hapd->dpp_chirp_bi;
+	unsigned int i;
+	struct hostapd_hw_modes *mode;
+	int c;
+
+	if (!bi)
+		return;
+
+	hapd->dpp_chirp_scan_done = 1;
+
+	scan_res = hostapd_driver_get_scan_results(hapd);
+
+	os_free(hapd->dpp_chirp_freqs);
+	hapd->dpp_chirp_freqs = NULL;
+
+	/* Channels from own bootstrapping info */
+	if (bi) {
+		for (i = 0; i < bi->num_freq; i++)
+			int_array_add_unique(&hapd->dpp_chirp_freqs,
+					     bi->freq[i]);
+	}
+
+	/* Preferred chirping channels */
+	int_array_add_unique(&hapd->dpp_chirp_freqs, 2437);
+
+	mode = dpp_get_mode(hapd, HOSTAPD_MODE_IEEE80211A);
+	if (mode) {
+		int chan44 = 0, chan149 = 0;
+
+		for (c = 0; c < mode->num_channels; c++) {
+			struct hostapd_channel_data *chan = &mode->channels[c];
+
+			if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+					  HOSTAPD_CHAN_RADAR))
+				continue;
+			if (chan->freq == 5220)
+				chan44 = 1;
+			if (chan->freq == 5745)
+				chan149 = 1;
+		}
+		if (chan149)
+			int_array_add_unique(&hapd->dpp_chirp_freqs, 5745);
+		else if (chan44)
+			int_array_add_unique(&hapd->dpp_chirp_freqs, 5220);
+	}
+
+	mode = dpp_get_mode(hapd, HOSTAPD_MODE_IEEE80211AD);
+	if (mode) {
+		for (c = 0; c < mode->num_channels; c++) {
+			struct hostapd_channel_data *chan = &mode->channels[c];
+
+			if ((chan->flag & (HOSTAPD_CHAN_DISABLED |
+					   HOSTAPD_CHAN_RADAR)) ||
+			    chan->freq != 60480)
+				continue;
+			int_array_add_unique(&hapd->dpp_chirp_freqs, 60480);
+			break;
+		}
+	}
+
+	/* Add channels from scan results for APs that advertise Configurator
+	 * Connectivity element */
+	for (i = 0; scan_res && i < scan_res->num; i++) {
+		struct wpa_scan_res *bss = scan_res->res[i];
+		size_t ie_len = bss->ie_len;
+
+		if (!ie_len)
+			ie_len = bss->beacon_ie_len;
+		if (get_vendor_ie((const u8 *) (bss + 1), ie_len,
+				  DPP_CC_IE_VENDOR_TYPE))
+			int_array_add_unique(&hapd->dpp_chirp_freqs,
+					     bss->freq);
+	}
+
+	if (!hapd->dpp_chirp_freqs ||
+	    eloop_register_timeout(0, 0, hostapd_dpp_chirp_next,
+				   hapd, NULL) < 0)
+		hostapd_dpp_chirp_stop(hapd);
+
+	wpa_scan_results_free(scan_res);
+}
+
+
+static void hostapd_dpp_chirp_next(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	int i;
+
+	if (hapd->dpp_chirp_listen)
+		hostapd_dpp_listen_stop(hapd);
+
+	if (hapd->dpp_chirp_freq == 0) {
+		if (hapd->dpp_chirp_round % 4 == 0 &&
+		    !hapd->dpp_chirp_scan_done) {
+			struct wpa_driver_scan_params params;
+			int ret;
+
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Update channel list for chirping");
+			os_memset(&params, 0, sizeof(params));
+			ret = hostapd_driver_scan(hapd, &params);
+			if (ret < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "DPP: Failed to request a scan ret=%d (%s)",
+					   ret, strerror(-ret));
+				hostapd_dpp_chirp_scan_res_handler(hapd->iface);
+			} else {
+				hapd->iface->scan_cb =
+					hostapd_dpp_chirp_scan_res_handler;
+			}
+			return;
+		}
+		hapd->dpp_chirp_freq = hapd->dpp_chirp_freqs[0];
+		hapd->dpp_chirp_round++;
+		wpa_printf(MSG_DEBUG, "DPP: Start chirping round %d",
+			   hapd->dpp_chirp_round);
+	} else {
+		for (i = 0; hapd->dpp_chirp_freqs[i]; i++)
+			if (hapd->dpp_chirp_freqs[i] == hapd->dpp_chirp_freq)
+				break;
+		if (!hapd->dpp_chirp_freqs[i]) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Previous chirp freq %d not found",
+				   hapd->dpp_chirp_freq);
+			return;
+		}
+		i++;
+		if (hapd->dpp_chirp_freqs[i]) {
+			hapd->dpp_chirp_freq = hapd->dpp_chirp_freqs[i];
+		} else {
+			hapd->dpp_chirp_iter--;
+			if (hapd->dpp_chirp_iter <= 0) {
+				wpa_printf(MSG_DEBUG,
+					   "DPP: Chirping iterations completed");
+				hostapd_dpp_chirp_stop(hapd);
+				return;
+			}
+			hapd->dpp_chirp_freq = 0;
+			hapd->dpp_chirp_scan_done = 0;
+			if (eloop_register_timeout(30, 0,
+						   hostapd_dpp_chirp_next,
+						   hapd, NULL) < 0) {
+				hostapd_dpp_chirp_stop(hapd);
+				return;
+			}
+			if (hapd->dpp_chirp_listen) {
+				wpa_printf(MSG_DEBUG,
+					   "DPP: Listen on %d MHz during chirp 30 second wait",
+					hapd->dpp_chirp_listen);
+				/* TODO: start listen on the channel */
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "DPP: Wait 30 seconds before starting the next chirping round");
+			}
+			return;
+		}
+	}
+
+	hostapd_dpp_chirp_start(hapd);
+}
+
+
+int hostapd_dpp_chirp(struct hostapd_data *hapd, const char *cmd)
+{
+	const char *pos;
+	int iter = 1, listen_freq = 0;
+	struct dpp_bootstrap_info *bi;
+
+	pos = os_strstr(cmd, " own=");
+	if (!pos)
+		return -1;
+	pos += 5;
+	bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos));
+	if (!bi) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Identified bootstrap info not found");
+		return -1;
+	}
+
+	pos = os_strstr(cmd, " iter=");
+	if (pos) {
+		iter = atoi(pos + 6);
+		if (iter <= 0)
+			return -1;
+	}
+
+	pos = os_strstr(cmd, " listen=");
+	if (pos) {
+		listen_freq = atoi(pos + 8);
+		if (listen_freq <= 0)
+			return -1;
+	}
+
+	hostapd_dpp_chirp_stop(hapd);
+	hapd->dpp_allowed_roles = DPP_CAPAB_ENROLLEE;
+	hapd->dpp_qr_mutual = 0;
+	hapd->dpp_chirp_bi = bi;
+	hapd->dpp_presence_announcement = dpp_build_presence_announcement(bi);
+	if (!hapd->dpp_presence_announcement)
+		return -1;
+	hapd->dpp_chirp_iter = iter;
+	hapd->dpp_chirp_round = 0;
+	hapd->dpp_chirp_scan_done = 0;
+	hapd->dpp_chirp_listen = listen_freq;
+
+	return eloop_register_timeout(0, 0, hostapd_dpp_chirp_next, hapd, NULL);
+}
+
+
+void hostapd_dpp_chirp_stop(struct hostapd_data *hapd)
+{
+	if (hapd->dpp_presence_announcement) {
+		hostapd_drv_send_action_cancel_wait(hapd);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CHIRP_STOPPED);
+	}
+	hapd->dpp_chirp_bi = NULL;
+	wpabuf_free(hapd->dpp_presence_announcement);
+	hapd->dpp_presence_announcement = NULL;
+	if (hapd->dpp_chirp_listen)
+		hostapd_dpp_listen_stop(hapd);
+	hapd->dpp_chirp_listen = 0;
+	hapd->dpp_chirp_freq = 0;
+	os_free(hapd->dpp_chirp_freqs);
+	hapd->dpp_chirp_freqs = NULL;
+	eloop_cancel_timeout(hostapd_dpp_chirp_next, hapd, NULL);
+	eloop_cancel_timeout(hostapd_dpp_chirp_timeout, hapd, NULL);
+	if (hapd->iface->scan_cb == hostapd_dpp_chirp_scan_res_handler) {
+		/* TODO: abort ongoing scan */
+		hapd->iface->scan_cb = NULL;
+	}
+}
+
+
+static int handle_dpp_remove_bi(struct hostapd_iface *iface, void *ctx)
+{
+	struct dpp_bootstrap_info *bi = ctx;
+	size_t i;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		struct hostapd_data *hapd = iface->bss[i];
+
+		if (bi == hapd->dpp_chirp_bi)
+			hostapd_dpp_chirp_stop(hapd);
+	}
+
+	return 0;
+}
+
+
+void hostapd_dpp_remove_bi(void *ctx, struct dpp_bootstrap_info *bi)
+{
+	struct hapd_interfaces *interfaces = ctx;
+
+	hostapd_for_each_interface(interfaces, handle_dpp_remove_bi, bi);
+}
+
+#endif /* CONFIG_DPP2 */
diff --git a/src/ap/dpp_hostapd.h b/src/ap/dpp_hostapd.h
index b1fa99e..7e74185 100644
--- a/src/ap/dpp_hostapd.h
+++ b/src/ap/dpp_hostapd.h
@@ -10,6 +10,8 @@
 #ifndef DPP_HOSTAPD_H
 #define DPP_HOSTAPD_H
 
+struct dpp_bootstrap_info;
+
 int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd);
 int hostapd_dpp_nfc_uri(struct hostapd_data *hapd, const char *cmd);
 int hostapd_dpp_nfc_handover_req(struct hostapd_data *hapd, const char *cmd);
@@ -39,4 +41,8 @@
 void hostapd_dpp_init_global(struct hapd_interfaces *ifaces);
 void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces);
 
+int hostapd_dpp_chirp(struct hostapd_data *hapd, const char *cmd);
+void hostapd_dpp_chirp_stop(struct hostapd_data *hapd);
+void hostapd_dpp_remove_bi(void *ctx, struct dpp_bootstrap_info *bi);
+
 #endif /* DPP_HOSTAPD_H */
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 5515ab3..f9af038 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -76,6 +76,8 @@
 	int ret;
 
 	for (i = 0; i < interfaces->count; i++) {
+		if (!interfaces->iface[i])
+			continue;
 		ret = cb(interfaces->iface[i], ctx);
 		if (ret)
 			return ret;
@@ -1175,12 +1177,12 @@
 #endif /* CONFIG_MESH */
 
 	if (flush_old_stations)
-		hostapd_flush_old_stations(hapd,
-					   WLAN_REASON_PREV_AUTH_NOT_VALID);
+		hostapd_flush(hapd);
 	hostapd_set_privacy(hapd, 0);
 
 #ifdef CONFIG_WEP
-	hostapd_broadcast_wep_clear(hapd);
+	if (!hostapd_drv_nl80211(hapd))
+		hostapd_broadcast_wep_clear(hapd);
 	if (hostapd_setup_encryption(conf->iface, hapd))
 		return -1;
 #endif /* CONFIG_WEP */
@@ -1369,6 +1371,21 @@
 	if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
 		return -1;
 
+	if (flush_old_stations && !conf->start_disabled &&
+	    conf->broadcast_deauth) {
+		u8 addr[ETH_ALEN];
+
+		/* Should any previously associated STA not have noticed that
+		 * the AP had stopped and restarted, send one more
+		 * deauthentication notification now that the AP is ready to
+		 * operate. */
+		wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+			"Deauthenticate all stations at BSS start");
+		os_memset(addr, 0xff, ETH_ALEN);
+		hostapd_drv_sta_deauth(hapd, addr,
+				       WLAN_REASON_PREV_AUTH_NOT_VALID);
+	}
+
 	if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0)
 		return -1;
 
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index c8f691e..a616136 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -349,6 +349,11 @@
 	int last_igtk_key_idx;
 	u8 last_igtk[WPA_IGTK_MAX_LEN];
 	size_t last_igtk_len;
+
+	enum wpa_alg last_bigtk_alg;
+	int last_bigtk_key_idx;
+	u8 last_bigtk[WPA_BIGTK_MAX_LEN];
+	size_t last_bigtk_len;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 #ifdef CONFIG_MBO
@@ -386,6 +391,16 @@
 	unsigned int dpp_resp_wait_time;
 	unsigned int dpp_resp_max_tries;
 	unsigned int dpp_resp_retry_time;
+#ifdef CONFIG_DPP2
+	struct wpabuf *dpp_presence_announcement;
+	struct dpp_bootstrap_info *dpp_chirp_bi;
+	int dpp_chirp_freq;
+	int *dpp_chirp_freqs;
+	int dpp_chirp_iter;
+	int dpp_chirp_round;
+	int dpp_chirp_scan_done;
+	int dpp_chirp_listen;
+#endif /* CONFIG_DPP2 */
 #ifdef CONFIG_TESTING_OPTIONS
 	char *dpp_config_obj_override;
 	char *dpp_discovery_override;
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index e6aa83d..565e9af 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -3181,6 +3181,12 @@
 					 elems.he_capabilities_len);
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
+		if (is_6ghz_op_class(hapd->iconf->op_class)) {
+			resp = copy_sta_he_6ghz_capab(hapd, sta,
+						      elems.he_6ghz_band_cap);
+			if (resp != WLAN_STATUS_SUCCESS)
+				return resp;
+		}
 	}
 #endif /* CONFIG_IEEE80211AX */
 
@@ -3365,7 +3371,8 @@
 		dpp_pfs_free(sta->dpp_pfs);
 		sta->dpp_pfs = NULL;
 
-		if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
+		if (DPP_VERSION > 1 &&
+		    (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
 		    hapd->conf->dpp_netaccesskey && sta->wpa_sm &&
 		    wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP &&
 		    elems.owe_dh) {
@@ -3626,6 +3633,7 @@
 			    sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
 			    sta->flags & WLAN_STA_HE ? &he_cap : NULL,
 			    sta->flags & WLAN_STA_HE ? sta->he_capab_len : 0,
+			    sta->he_6ghz_capab,
 			    sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
 			    sta->vht_opmode, sta->p2p_ie ? 1 : 0,
 			    set)) {
@@ -3785,6 +3793,7 @@
 		p = hostapd_eid_he_operation(hapd, p);
 		p = hostapd_eid_spatial_reuse(hapd, p);
 		p = hostapd_eid_he_mu_edca_parameter_set(hapd, p);
+		p = hostapd_eid_he_6ghz_band_cap(hapd, p);
 	}
 #endif /* CONFIG_IEEE80211AX */
 
@@ -3843,7 +3852,7 @@
 #endif /* CONFIG_OWE */
 
 #ifdef CONFIG_DPP2
-	if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
+	if (DPP_VERSION > 1 && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
 	    sta && sta->dpp_pfs && status_code == WLAN_STATUS_SUCCESS &&
 	    wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP) {
 		os_memcpy(p, wpabuf_head(sta->dpp_pfs->ie),
@@ -4857,6 +4866,11 @@
 		return 0;
 	}
 
+	if (hapd->iface->state != HAPD_IFACE_ENABLED) {
+		wpa_printf(MSG_DEBUG, "MGMT: Ignore management frame while interface is not enabled (SA=" MACSTR " DA=" MACSTR " subtype=%u)",
+			   MAC2STR(mgmt->sa), MAC2STR(mgmt->da), stype);
+		return 1;
+	}
 
 	if (stype == WLAN_FC_STYPE_PROBE_REQ) {
 		handle_probe_req(hapd, mgmt, len, ssi_signal);
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index c7bdb4b..ea8c608 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -63,6 +63,7 @@
 u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_he_6ghz_band_cap(struct hostapd_data *hapd, u8 *eid);
 
 int hostapd_ht_operation_update(struct hostapd_iface *iface);
 void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
@@ -95,6 +96,8 @@
 u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
 		      enum ieee80211_op_mode opmode, const u8 *he_capab,
 		      size_t he_capab_len);
+u16 copy_sta_he_6ghz_capab(struct hostapd_data *hapd, struct sta_info *sta,
+			   const u8 *he_6ghz_capab);
 int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
 				 enum ieee80211_op_mode mode);
 void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
index 57c6b18..f1f2442 100644
--- a/src/ap/ieee802_11_he.c
+++ b/src/ap/ieee802_11_he.c
@@ -311,6 +311,54 @@
 }
 
 
+u8 * hostapd_eid_he_6ghz_band_cap(struct hostapd_data *hapd, u8 *eid)
+{
+	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+	struct ieee80211_he_6ghz_band_cap *cap;
+	u32 vht_cap;
+	u8 ht_info;
+	u8 params;
+	u8 *pos;
+
+	if (!mode || !is_6ghz_op_class(hapd->iconf->op_class))
+		return eid;
+
+	vht_cap = hapd->iface->conf->vht_capab;
+	ht_info = mode->a_mpdu_params;
+
+	pos = eid;
+	*pos++ = WLAN_EID_EXTENSION;
+	*pos++ = 1 + sizeof(*cap);
+	*pos++ = WLAN_EID_EXT_HE_6GHZ_BAND_CAP;
+
+	/* Minimum MPDU Start Spacing B0..B2 */
+	params = (ht_info >> 2) & HE_6GHZ_BAND_CAP_MIN_MPDU_START_SPACE_MASK;
+
+	/* Maximum A-MPDU Length Exponent B3..B5 */
+	params |= ((((vht_cap & VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX) >>
+		     VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX_SHIFT) &
+		    HE_6GHZ_BAND_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK) <<
+		   HE_6GHZ_BAND_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT);
+
+	/* Maximum MPDU Length B6..B7 */
+	params |= ((((vht_cap & VHT_CAP_MAX_MPDU_LENGTH_MASK) >>
+		     VHT_CAP_MAX_MPDU_LENGTH_MASK_SHIFT) &
+		    HE_6GHZ_BAND_CAP_MAX_MPDU_LENGTH_MASK) <<
+		   HE_6GHZ_BAND_CAP_MAX_MPDU_LENGTH_SHIFT);
+
+	cap = (struct ieee80211_he_6ghz_band_cap *) pos;
+	cap->a_mpdu_params = params;
+	cap->info = HE_6GHZ_BAND_CAP_SMPS_DISABLED;
+	if (vht_cap & VHT_CAP_RX_ANTENNA_PATTERN)
+		cap->info |= HE_6GHZ_BAND_CAP_RX_ANTENNA_PATTERN;
+	if (vht_cap & VHT_CAP_TX_ANTENNA_PATTERN)
+		cap->info |= HE_6GHZ_BAND_CAP_TX_ANTENNA_PATTERN;
+	pos += sizeof(*cap);
+
+	return pos;
+}
+
+
 void hostapd_get_he_capab(struct hostapd_data *hapd,
 			  const struct ieee80211_he_capabilities *he_cap,
 			  struct ieee80211_he_capabilities *neg_he_cap,
@@ -415,6 +463,32 @@
 }
 
 
+u16 copy_sta_he_6ghz_capab(struct hostapd_data *hapd, struct sta_info *sta,
+			   const u8 *he_6ghz_capab)
+{
+	if (!he_6ghz_capab || !hapd->iconf->ieee80211ax ||
+	    !is_6ghz_op_class(hapd->iconf->op_class)) {
+		sta->flags &= ~WLAN_STA_6GHZ;
+		os_free(sta->he_6ghz_capab);
+		sta->he_6ghz_capab = NULL;
+		return WLAN_STATUS_SUCCESS;
+	}
+
+	if (!sta->he_6ghz_capab) {
+		sta->he_6ghz_capab =
+			os_zalloc(sizeof(struct ieee80211_he_6ghz_band_cap));
+		if (!sta->he_6ghz_capab)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	sta->flags |= WLAN_STA_6GHZ;
+	os_memcpy(sta->he_6ghz_capab, he_6ghz_capab,
+		  sizeof(struct ieee80211_he_6ghz_band_cap));
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
 int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
 				 enum ieee80211_op_mode mode)
 {
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 113b4ef..74a837f 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -886,9 +886,9 @@
 
 u8 * hostapd_eid_dpp_cc(struct hostapd_data *hapd, u8 *eid, size_t len)
 {
-#ifdef CONFIG_DPP2
 	u8 *pos = eid;
 
+#ifdef CONFIG_DPP2
 	if (!hapd->conf->dpp_configurator_connectivity || len < 6)
 		return pos;
 
@@ -897,10 +897,9 @@
 	WPA_PUT_BE24(pos, OUI_WFA);
 	pos += 3;
 	*pos++ = DPP_CC_OUI_TYPE;
+#endif /* CONFIG_DPP2 */
 
 	return pos;
-#endif /* CONFIG_DPP2 */
-	return eid;
 }
 
 
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 93f1f0c..67b5e98 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -326,6 +326,7 @@
 	os_free(sta->vht_capabilities);
 	os_free(sta->vht_operation);
 	os_free(sta->he_capab);
+	os_free(sta->he_6ghz_capab);
 	hostapd_free_psk_list(sta->psk);
 	os_free(sta->identity);
 	os_free(sta->radius_cui);
@@ -1424,7 +1425,8 @@
 	int res;
 
 	buf[0] = '\0';
-	res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+	res = os_snprintf(buf, buflen,
+			  "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
 			  (flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
 			  (flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
 			  (flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""),
@@ -1444,6 +1446,7 @@
 			  (flags & WLAN_STA_HT ? "[HT]" : ""),
 			  (flags & WLAN_STA_VHT ? "[VHT]" : ""),
 			  (flags & WLAN_STA_HE ? "[HE]" : ""),
+			  (flags & WLAN_STA_6GHZ ? "[6GHZ]" : ""),
 			  (flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""),
 			  (flags & WLAN_STA_WNM_SLEEP_MODE ?
 			   "[WNM_SLEEP_MODE]" : ""));
@@ -1515,7 +1518,7 @@
 	if (hostapd_sta_add(hapd, sta->addr, 0, 0,
 			    sta->supported_rates,
 			    sta->supported_rates_len,
-			    0, NULL, NULL, NULL, 0,
+			    0, NULL, NULL, NULL, 0, NULL,
 			    sta->flags, 0, 0, 0, 0)) {
 		hostapd_logger(hapd, sta->addr,
 			       HOSTAPD_MODULE_IEEE80211,
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 308aa29..940d315 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -38,6 +38,7 @@
 #define WLAN_STA_PENDING_FILS_ERP BIT(22)
 #define WLAN_STA_MULTI_AP BIT(23)
 #define WLAN_STA_HE BIT(24)
+#define WLAN_STA_6GHZ BIT(25)
 #define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
 #define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
 #define WLAN_STA_NONERP BIT(31)
@@ -170,6 +171,7 @@
 	u8 vht_opmode;
 	struct ieee80211_he_capabilities *he_capab;
 	size_t he_capab_len;
+	struct ieee80211_he_6ghz_band_cap *he_6ghz_capab;
 
 	int sa_query_count; /* number of pending SA Query requests;
 			     * 0 = no SA Query in progress */
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 30e7258..019e535 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -14,6 +14,7 @@
 #include "utils/bitfield.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ocv.h"
+#include "common/dpp.h"
 #include "crypto/aes.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/aes_siv.h"
@@ -3079,6 +3080,24 @@
 	}
 #endif /* CONFIG_P2P */
 
+#ifdef CONFIG_DPP2
+	if (DPP_VERSION > 1 && kde.dpp_kde) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: peer Protocol Version %u Flags 0x%x",
+			   kde.dpp_kde[0], kde.dpp_kde[1]);
+		if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP &&
+		    wpa_auth->conf.dpp_pfs != 2 &&
+		    (kde.dpp_kde[1] & DPP_KDE_PFS_ALLOWED) &&
+		    !sm->dpp_z) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Peer indicated it supports PFS and local configuration allows this, but PFS was not negotiated for the association");
+			wpa_sta_disconnect(wpa_auth, sm->addr,
+					   WLAN_REASON_PREV_AUTH_NOT_VALID);
+			return;
+		}
+	}
+#endif /* CONFIG_DPP2 */
+
 #ifdef CONFIG_IEEE80211R_AP
 	if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
 		/*
@@ -3397,6 +3416,11 @@
 	if (conf->transition_disable)
 		kde_len += 2 + RSN_SELECTOR_LEN + 1;
 
+#ifdef CONFIG_DPP2
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP)
+		kde_len += 2 + RSN_SELECTOR_LEN + 2;
+#endif /* CONFIG_DPP2 */
+
 	kde = os_malloc(kde_len);
 	if (!kde)
 		goto done;
@@ -3492,6 +3516,22 @@
 		pos = wpa_add_kde(pos, WFA_KEY_DATA_TRANSITION_DISABLE,
 				  &conf->transition_disable, 1, NULL, 0);
 
+#ifdef CONFIG_DPP2
+	if (DPP_VERSION > 1 && sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP) {
+		u8 payload[2];
+
+		payload[0] = DPP_VERSION; /* Protocol Version */
+		payload[1] = 0; /* Flags */
+		if (conf->dpp_pfs == 0)
+			payload[1] |= DPP_KDE_PFS_ALLOWED;
+		else if (conf->dpp_pfs == 1)
+			payload[1] |= DPP_KDE_PFS_ALLOWED |
+				DPP_KDE_PFS_REQUIRED;
+		pos = wpa_add_kde(pos, WFA_KEY_DATA_DPP,
+				  payload, sizeof(payload), NULL, 0);
+	}
+#endif /* CONFIG_DPP2 */
+
 	wpa_send_eapol(sm->wpa_auth, sm,
 		       (secure ? WPA_KEY_INFO_SECURE : 0) |
 		       (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 44ab830..05d87ac 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -453,15 +453,23 @@
 				os_memcpy(sta->last_tk, key, key_len);
 			sta->last_tk_len = key_len;
 		}
-	} else if (alg == WPA_ALG_IGTK ||
+	} else if (alg == WPA_ALG_BIP_CMAC_128 ||
 		   alg == WPA_ALG_BIP_GMAC_128 ||
 		   alg == WPA_ALG_BIP_GMAC_256 ||
 		   alg == WPA_ALG_BIP_CMAC_256) {
-		hapd->last_igtk_alg = alg;
-		hapd->last_igtk_key_idx = idx;
-		if (key)
-			os_memcpy(hapd->last_igtk, key, key_len);
-		hapd->last_igtk_len = key_len;
+		if (idx == 4 || idx == 5) {
+			hapd->last_igtk_alg = alg;
+			hapd->last_igtk_key_idx = idx;
+			if (key)
+				os_memcpy(hapd->last_igtk, key, key_len);
+			hapd->last_igtk_len = key_len;
+		} else if (idx == 6 || idx == 7) {
+			hapd->last_bigtk_alg = alg;
+			hapd->last_bigtk_key_idx = idx;
+			if (key)
+				os_memcpy(hapd->last_bigtk, key, key_len);
+			hapd->last_bigtk_len = key_len;
+		}
 	} else {
 		hapd->last_gtk_alg = alg;
 		hapd->last_gtk_key_idx = idx;
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index af0aaca..ba08ac2 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -277,7 +277,8 @@
 void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
 		     logger_level level, const char *txt);
 void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr,
-		      logger_level level, const char *fmt, ...);
+		      logger_level level, const char *fmt, ...)
+	PRINTF_FORMAT(4, 5);
 void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
 		      struct wpa_state_machine *sm, int key_info,
 		      const u8 *key_rsc, const u8 *nonce,
diff --git a/src/common/defs.h b/src/common/defs.h
index bcf6f54..bbe3120 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -190,7 +190,7 @@
 	WPA_ALG_WEP,
 	WPA_ALG_TKIP,
 	WPA_ALG_CCMP,
-	WPA_ALG_IGTK,
+	WPA_ALG_BIP_CMAC_128,
 	WPA_ALG_GCMP,
 	WPA_ALG_SMS4,
 	WPA_ALG_KRK,
@@ -201,6 +201,14 @@
 	WPA_ALG_BIP_CMAC_256
 };
 
+static inline int wpa_alg_bip(enum wpa_alg alg)
+{
+	return alg == WPA_ALG_BIP_CMAC_128 ||
+		alg == WPA_ALG_BIP_GMAC_128 ||
+		alg == WPA_ALG_BIP_GMAC_256 ||
+		alg == WPA_ALG_BIP_CMAC_256;
+}
+
 /**
  * enum wpa_states - wpa_supplicant state
  *
diff --git a/src/common/dpp.c b/src/common/dpp.c
index b33ab15..d09f0d1 100644
--- a/src/common/dpp.c
+++ b/src/common/dpp.c
@@ -8,47 +8,33 @@
  */
 
 #include "utils/includes.h"
-#include <fcntl.h>
 #include <openssl/opensslv.h>
 #include <openssl/err.h>
-#include <openssl/asn1.h>
-#include <openssl/asn1t.h>
 
 #include "utils/common.h"
 #include "utils/base64.h"
 #include "utils/json.h"
-#include "utils/ip_addr.h"
-#include "utils/eloop.h"
 #include "common/ieee802_11_common.h"
-#include "common/ieee802_11_defs.h"
 #include "common/wpa_ctrl.h"
 #include "common/gas.h"
 #include "crypto/crypto.h"
 #include "crypto/random.h"
 #include "crypto/aes.h"
 #include "crypto/aes_siv.h"
-#include "crypto/sha384.h"
-#include "crypto/sha512.h"
-#include "tls/asn1.h"
 #include "drivers/driver.h"
 #include "dpp.h"
+#include "dpp_i.h"
 
 
 static const char * dpp_netrole_str(enum dpp_netrole netrole);
 
 #ifdef CONFIG_TESTING_OPTIONS
+#ifdef CONFIG_DPP2
+int dpp_version_override = 2;
+#else
+int dpp_version_override = 1;
+#endif
 enum dpp_test_behavior dpp_test = DPP_TEST_DISABLED;
-u8 dpp_pkex_own_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
-u8 dpp_pkex_peer_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
-u8 dpp_pkex_ephemeral_key_override[600];
-size_t dpp_pkex_ephemeral_key_override_len = 0;
-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;
-
-static int dpp_test_gen_invalid_key(struct wpabuf *msg,
-				    const struct dpp_curve_params *curve);
 #endif /* CONFIG_TESTING_OPTIONS */
 
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
@@ -56,24 +42,6 @@
 	 LIBRESSL_VERSION_NUMBER < 0x20700000L)
 /* Compatibility wrappers for older versions. */
 
-static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
-{
-	sig->r = r;
-	sig->s = s;
-	return 1;
-}
-
-
-static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr,
-			   const BIGNUM **ps)
-{
-	if (pr)
-		*pr = sig->r;
-	if (ps)
-		*ps = sig->s;
-}
-
-
 #ifdef CONFIG_DPP2
 static EC_KEY * EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
 {
@@ -86,700 +54,7 @@
 #endif
 
 
-struct dpp_connection {
-	struct dl_list list;
-	struct dpp_controller *ctrl;
-	struct dpp_relay_controller *relay;
-	struct dpp_global *global;
-	struct dpp_authentication *auth;
-	int sock;
-	u8 mac_addr[ETH_ALEN];
-	unsigned int freq;
-	u8 msg_len[4];
-	size_t msg_len_octets;
-	struct wpabuf *msg;
-	struct wpabuf *msg_out;
-	size_t msg_out_pos;
-	unsigned int read_eloop:1;
-	unsigned int write_eloop:1;
-	unsigned int on_tcp_tx_complete_gas_done:1;
-	unsigned int on_tcp_tx_complete_remove:1;
-	unsigned int on_tcp_tx_complete_auth_ok:1;
-};
-
-/* Remote Controller */
-struct dpp_relay_controller {
-	struct dl_list list;
-	struct dpp_global *global;
-	u8 pkhash[SHA256_MAC_LEN];
-	struct hostapd_ip_addr ipaddr;
-	void *cb_ctx;
-	void (*tx)(void *ctx, const u8 *addr, unsigned int freq, const u8 *msg,
-		   size_t len);
-	void (*gas_resp_tx)(void *ctx, const u8 *addr, u8 dialog_token,
-			    int prot, struct wpabuf *buf);
-	struct dl_list conn; /* struct dpp_connection */
-};
-
-/* Local Controller */
-struct dpp_controller {
-	struct dpp_global *global;
-	u8 allowed_roles;
-	int qr_mutual;
-	int sock;
-	struct dl_list conn; /* struct dpp_connection */
-	char *configurator_params;
-};
-
-struct dpp_global {
-	void *msg_ctx;
-	struct dl_list bootstrap; /* struct dpp_bootstrap_info */
-	struct dl_list configurator; /* struct dpp_configurator */
-#ifdef CONFIG_DPP2
-	struct dl_list controllers; /* struct dpp_relay_controller */
-	struct dpp_controller *controller;
-	struct dl_list tcp_init; /* struct dpp_connection */
-	void *cb_ctx;
-	int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
-	void (*remove_bi)(void *ctx, struct dpp_bootstrap_info *bi);
-#endif /* CONFIG_DPP2 */
-};
-
-static const struct dpp_curve_params dpp_curves[] = {
-	/* The mandatory to support and the default NIST P-256 curve needs to
-	 * be the first entry on this list. */
-	{ "prime256v1", 32, 32, 16, 32, "P-256", 19, "ES256" },
-	{ "secp384r1", 48, 48, 24, 48, "P-384", 20, "ES384" },
-	{ "secp521r1", 64, 64, 32, 66, "P-521", 21, "ES512" },
-	{ "brainpoolP256r1", 32, 32, 16, 32, "BP-256", 28, "BS256" },
-	{ "brainpoolP384r1", 48, 48, 24, 48, "BP-384", 29, "BS384" },
-	{ "brainpoolP512r1", 64, 64, 32, 64, "BP-512", 30, "BS512" },
-	{ NULL, 0, 0, 0, 0, NULL, 0, NULL }
-};
-
-
-/* Role-specific elements for PKEX */
-
-/* NIST P-256 */
-static const u8 pkex_init_x_p256[32] = {
-	0x56, 0x26, 0x12, 0xcf, 0x36, 0x48, 0xfe, 0x0b,
-	0x07, 0x04, 0xbb, 0x12, 0x22, 0x50, 0xb2, 0x54,
-	0xb1, 0x94, 0x64, 0x7e, 0x54, 0xce, 0x08, 0x07,
-	0x2e, 0xec, 0xca, 0x74, 0x5b, 0x61, 0x2d, 0x25
- };
-static const u8 pkex_init_y_p256[32] = {
-	0x3e, 0x44, 0xc7, 0xc9, 0x8c, 0x1c, 0xa1, 0x0b,
-	0x20, 0x09, 0x93, 0xb2, 0xfd, 0xe5, 0x69, 0xdc,
-	0x75, 0xbc, 0xad, 0x33, 0xc1, 0xe7, 0xc6, 0x45,
-	0x4d, 0x10, 0x1e, 0x6a, 0x3d, 0x84, 0x3c, 0xa4
- };
-static const u8 pkex_resp_x_p256[32] = {
-	0x1e, 0xa4, 0x8a, 0xb1, 0xa4, 0xe8, 0x42, 0x39,
-	0xad, 0x73, 0x07, 0xf2, 0x34, 0xdf, 0x57, 0x4f,
-	0xc0, 0x9d, 0x54, 0xbe, 0x36, 0x1b, 0x31, 0x0f,
-	0x59, 0x91, 0x52, 0x33, 0xac, 0x19, 0x9d, 0x76
-};
-static const u8 pkex_resp_y_p256[32] = {
-	0xd9, 0xfb, 0xf6, 0xb9, 0xf5, 0xfa, 0xdf, 0x19,
-	0x58, 0xd8, 0x3e, 0xc9, 0x89, 0x7a, 0x35, 0xc1,
-	0xbd, 0xe9, 0x0b, 0x77, 0x7a, 0xcb, 0x91, 0x2a,
-	0xe8, 0x21, 0x3f, 0x47, 0x52, 0x02, 0x4d, 0x67
-};
-
-/* NIST P-384 */
-static const u8 pkex_init_x_p384[48] = {
-	0x95, 0x3f, 0x42, 0x9e, 0x50, 0x7f, 0xf9, 0xaa,
-	0xac, 0x1a, 0xf2, 0x85, 0x2e, 0x64, 0x91, 0x68,
-	0x64, 0xc4, 0x3c, 0xb7, 0x5c, 0xf8, 0xc9, 0x53,
-	0x6e, 0x58, 0x4c, 0x7f, 0xc4, 0x64, 0x61, 0xac,
-	0x51, 0x8a, 0x6f, 0xfe, 0xab, 0x74, 0xe6, 0x12,
-	0x81, 0xac, 0x38, 0x5d, 0x41, 0xe6, 0xb9, 0xa3
-};
-static const u8 pkex_init_y_p384[48] = {
-	0x76, 0x2f, 0x68, 0x84, 0xa6, 0xb0, 0x59, 0x29,
-	0x83, 0xa2, 0x6c, 0xa4, 0x6c, 0x3b, 0xf8, 0x56,
-	0x76, 0x11, 0x2a, 0x32, 0x90, 0xbd, 0x07, 0xc7,
-	0x37, 0x39, 0x9d, 0xdb, 0x96, 0xf3, 0x2b, 0xb6,
-	0x27, 0xbb, 0x29, 0x3c, 0x17, 0x33, 0x9d, 0x94,
-	0xc3, 0xda, 0xac, 0x46, 0xb0, 0x8e, 0x07, 0x18
-};
-static const u8 pkex_resp_x_p384[48] = {
-	0xad, 0xbe, 0xd7, 0x1d, 0x3a, 0x71, 0x64, 0x98,
-	0x5f, 0xb4, 0xd6, 0x4b, 0x50, 0xd0, 0x84, 0x97,
-	0x4b, 0x7e, 0x57, 0x70, 0xd2, 0xd9, 0xf4, 0x92,
-	0x2a, 0x3f, 0xce, 0x99, 0xc5, 0x77, 0x33, 0x44,
-	0x14, 0x56, 0x92, 0xcb, 0xae, 0x46, 0x64, 0xdf,
-	0xe0, 0xbb, 0xd7, 0xb1, 0x29, 0x20, 0x72, 0xdf
-};
-static const u8 pkex_resp_y_p384[48] = {
-	0xab, 0xa7, 0xdf, 0x52, 0xaa, 0xe2, 0x35, 0x0c,
-	0xe3, 0x75, 0x32, 0xe6, 0xbf, 0x06, 0xc8, 0x7c,
-	0x38, 0x29, 0x4c, 0xec, 0x82, 0xac, 0xd7, 0xa3,
-	0x09, 0xd2, 0x0e, 0x22, 0x5a, 0x74, 0x52, 0xa1,
-	0x7e, 0x54, 0x4e, 0xfe, 0xc6, 0x29, 0x33, 0x63,
-	0x15, 0xe1, 0x7b, 0xe3, 0x40, 0x1c, 0xca, 0x06
-};
-
-/* NIST P-521 */
-static const u8 pkex_init_x_p521[66] = {
-	0x00, 0x16, 0x20, 0x45, 0x19, 0x50, 0x95, 0x23,
-	0x0d, 0x24, 0xbe, 0x00, 0x87, 0xdc, 0xfa, 0xf0,
-	0x58, 0x9a, 0x01, 0x60, 0x07, 0x7a, 0xca, 0x76,
-	0x01, 0xab, 0x2d, 0x5a, 0x46, 0xcd, 0x2c, 0xb5,
-	0x11, 0x9a, 0xff, 0xaa, 0x48, 0x04, 0x91, 0x38,
-	0xcf, 0x86, 0xfc, 0xa4, 0xa5, 0x0f, 0x47, 0x01,
-	0x80, 0x1b, 0x30, 0xa3, 0xae, 0xe8, 0x1c, 0x2e,
-	0xea, 0xcc, 0xf0, 0x03, 0x9f, 0x77, 0x4c, 0x8d,
-	0x97, 0x76
-};
-static const u8 pkex_init_y_p521[66] = {
-	0x00, 0xb3, 0x8e, 0x02, 0xe4, 0x2a, 0x63, 0x59,
-	0x12, 0xc6, 0x10, 0xba, 0x3a, 0xf9, 0x02, 0x99,
-	0x3f, 0x14, 0xf0, 0x40, 0xde, 0x5c, 0xc9, 0x8b,
-	0x02, 0x55, 0xfa, 0x91, 0xb1, 0xcc, 0x6a, 0xbd,
-	0xe5, 0x62, 0xc0, 0xc5, 0xe3, 0xa1, 0x57, 0x9f,
-	0x08, 0x1a, 0xa6, 0xe2, 0xf8, 0x55, 0x90, 0xbf,
-	0xf5, 0xa6, 0xc3, 0xd8, 0x52, 0x1f, 0xb7, 0x02,
-	0x2e, 0x7c, 0xc8, 0xb3, 0x20, 0x1e, 0x79, 0x8d,
-	0x03, 0xa8
-};
-static const u8 pkex_resp_x_p521[66] = {
-	0x00, 0x79, 0xe4, 0x4d, 0x6b, 0x5e, 0x12, 0x0a,
-	0x18, 0x2c, 0xb3, 0x05, 0x77, 0x0f, 0xc3, 0x44,
-	0x1a, 0xcd, 0x78, 0x46, 0x14, 0xee, 0x46, 0x3f,
-	0xab, 0xc9, 0x59, 0x7c, 0x85, 0xa0, 0xc2, 0xfb,
-	0x02, 0x32, 0x99, 0xde, 0x5d, 0xe1, 0x0d, 0x48,
-	0x2d, 0x71, 0x7d, 0x8d, 0x3f, 0x61, 0x67, 0x9e,
-	0x2b, 0x8b, 0x12, 0xde, 0x10, 0x21, 0x55, 0x0a,
-	0x5b, 0x2d, 0xe8, 0x05, 0x09, 0xf6, 0x20, 0x97,
-	0x84, 0xb4
-};
-static const u8 pkex_resp_y_p521[66] = {
-	0x00, 0x46, 0x63, 0x39, 0xbe, 0xcd, 0xa4, 0x2d,
-	0xca, 0x27, 0x74, 0xd4, 0x1b, 0x91, 0x33, 0x20,
-	0x83, 0xc7, 0x3b, 0xa4, 0x09, 0x8b, 0x8e, 0xa3,
-	0x88, 0xe9, 0x75, 0x7f, 0x56, 0x7b, 0x38, 0x84,
-	0x62, 0x02, 0x7c, 0x90, 0x51, 0x07, 0xdb, 0xe9,
-	0xd0, 0xde, 0xda, 0x9a, 0x5d, 0xe5, 0x94, 0xd2,
-	0xcf, 0x9d, 0x4c, 0x33, 0x91, 0xa6, 0xc3, 0x80,
-	0xa7, 0x6e, 0x7e, 0x8d, 0xf8, 0x73, 0x6e, 0x53,
-	0xce, 0xe1
-};
-
-/* Brainpool P-256r1 */
-static const u8 pkex_init_x_bp_p256r1[32] = {
-	0x46, 0x98, 0x18, 0x6c, 0x27, 0xcd, 0x4b, 0x10,
-	0x7d, 0x55, 0xa3, 0xdd, 0x89, 0x1f, 0x9f, 0xca,
-	0xc7, 0x42, 0x5b, 0x8a, 0x23, 0xed, 0xf8, 0x75,
-	0xac, 0xc7, 0xe9, 0x8d, 0xc2, 0x6f, 0xec, 0xd8
-};
-static const u8 pkex_init_y_bp_p256r1[32] = {
-	0x93, 0xca, 0xef, 0xa9, 0x66, 0x3e, 0x87, 0xcd,
-	0x52, 0x6e, 0x54, 0x13, 0xef, 0x31, 0x67, 0x30,
-	0x15, 0x13, 0x9d, 0x6d, 0xc0, 0x95, 0x32, 0xbe,
-	0x4f, 0xab, 0x5d, 0xf7, 0xbf, 0x5e, 0xaa, 0x0b
-};
-static const u8 pkex_resp_x_bp_p256r1[32] = {
-	0x90, 0x18, 0x84, 0xc9, 0xdc, 0xcc, 0xb5, 0x2f,
-	0x4a, 0x3f, 0x4f, 0x18, 0x0a, 0x22, 0x56, 0x6a,
-	0xa9, 0xef, 0xd4, 0xe6, 0xc3, 0x53, 0xc2, 0x1a,
-	0x23, 0x54, 0xdd, 0x08, 0x7e, 0x10, 0xd8, 0xe3
-};
-static const u8 pkex_resp_y_bp_p256r1[32] = {
-	0x2a, 0xfa, 0x98, 0x9b, 0xe3, 0xda, 0x30, 0xfd,
-	0x32, 0x28, 0xcb, 0x66, 0xfb, 0x40, 0x7f, 0xf2,
-	0xb2, 0x25, 0x80, 0x82, 0x44, 0x85, 0x13, 0x7e,
-	0x4b, 0xb5, 0x06, 0xc0, 0x03, 0x69, 0x23, 0x64
-};
-
-/* Brainpool P-384r1 */
-static const u8 pkex_init_x_bp_p384r1[48] = {
-	0x0a, 0x2c, 0xeb, 0x49, 0x5e, 0xb7, 0x23, 0xbd,
-	0x20, 0x5b, 0xe0, 0x49, 0xdf, 0xcf, 0xcf, 0x19,
-	0x37, 0x36, 0xe1, 0x2f, 0x59, 0xdb, 0x07, 0x06,
-	0xb5, 0xeb, 0x2d, 0xae, 0xc2, 0xb2, 0x38, 0x62,
-	0xa6, 0x73, 0x09, 0xa0, 0x6c, 0x0a, 0xa2, 0x30,
-	0x99, 0xeb, 0xf7, 0x1e, 0x47, 0xb9, 0x5e, 0xbe
-};
-static const u8 pkex_init_y_bp_p384r1[48] = {
-	0x54, 0x76, 0x61, 0x65, 0x75, 0x5a, 0x2f, 0x99,
-	0x39, 0x73, 0xca, 0x6c, 0xf9, 0xf7, 0x12, 0x86,
-	0x54, 0xd5, 0xd4, 0xad, 0x45, 0x7b, 0xbf, 0x32,
-	0xee, 0x62, 0x8b, 0x9f, 0x52, 0xe8, 0xa0, 0xc9,
-	0xb7, 0x9d, 0xd1, 0x09, 0xb4, 0x79, 0x1c, 0x3e,
-	0x1a, 0xbf, 0x21, 0x45, 0x66, 0x6b, 0x02, 0x52
-};
-static const u8 pkex_resp_x_bp_p384r1[48] = {
-	0x03, 0xa2, 0x57, 0xef, 0xe8, 0x51, 0x21, 0xa0,
-	0xc8, 0x9e, 0x21, 0x02, 0xb5, 0x9a, 0x36, 0x25,
-	0x74, 0x22, 0xd1, 0xf2, 0x1b, 0xa8, 0x9a, 0x9b,
-	0x97, 0xbc, 0x5a, 0xeb, 0x26, 0x15, 0x09, 0x71,
-	0x77, 0x59, 0xec, 0x8b, 0xb7, 0xe1, 0xe8, 0xce,
-	0x65, 0xb8, 0xaf, 0xf8, 0x80, 0xae, 0x74, 0x6c
-};
-static const u8 pkex_resp_y_bp_p384r1[48] = {
-	0x2f, 0xd9, 0x6a, 0xc7, 0x3e, 0xec, 0x76, 0x65,
-	0x2d, 0x38, 0x7f, 0xec, 0x63, 0x26, 0x3f, 0x04,
-	0xd8, 0x4e, 0xff, 0xe1, 0x0a, 0x51, 0x74, 0x70,
-	0xe5, 0x46, 0x63, 0x7f, 0x5c, 0xc0, 0xd1, 0x7c,
-	0xfb, 0x2f, 0xea, 0xe2, 0xd8, 0x0f, 0x84, 0xcb,
-	0xe9, 0x39, 0x5c, 0x64, 0xfe, 0xcb, 0x2f, 0xf1
-};
-
-/* Brainpool P-512r1 */
-static const u8 pkex_init_x_bp_p512r1[64] = {
-	0x4c, 0xe9, 0xb6, 0x1c, 0xe2, 0x00, 0x3c, 0x9c,
-	0xa9, 0xc8, 0x56, 0x52, 0xaf, 0x87, 0x3e, 0x51,
-	0x9c, 0xbb, 0x15, 0x31, 0x1e, 0xc1, 0x05, 0xfc,
-	0x7c, 0x77, 0xd7, 0x37, 0x61, 0x27, 0xd0, 0x95,
-	0x98, 0xee, 0x5d, 0xa4, 0x3d, 0x09, 0xdb, 0x3d,
-	0xfa, 0x89, 0x9e, 0x7f, 0xa6, 0xa6, 0x9c, 0xff,
-	0x83, 0x5c, 0x21, 0x6c, 0x3e, 0xf2, 0xfe, 0xdc,
-	0x63, 0xe4, 0xd1, 0x0e, 0x75, 0x45, 0x69, 0x0f
-};
-static const u8 pkex_init_y_bp_p512r1[64] = {
-	0x50, 0xb5, 0x9b, 0xfa, 0x45, 0x67, 0x75, 0x94,
-	0x44, 0xe7, 0x68, 0xb0, 0xeb, 0x3e, 0xb3, 0xb8,
-	0xf9, 0x99, 0x05, 0xef, 0xae, 0x6c, 0xbc, 0xe3,
-	0xe1, 0xd2, 0x51, 0x54, 0xdf, 0x59, 0xd4, 0x45,
-	0x41, 0x3a, 0xa8, 0x0b, 0x76, 0x32, 0x44, 0x0e,
-	0x07, 0x60, 0x3a, 0x6e, 0xbe, 0xfe, 0xe0, 0x58,
-	0x52, 0xa0, 0xaa, 0x8b, 0xd8, 0x5b, 0xf2, 0x71,
-	0x11, 0x9a, 0x9e, 0x8f, 0x1a, 0xd1, 0xc9, 0x99
-};
-static const u8 pkex_resp_x_bp_p512r1[64] = {
-	0x2a, 0x60, 0x32, 0x27, 0xa1, 0xe6, 0x94, 0x72,
-	0x1c, 0x48, 0xbe, 0xc5, 0x77, 0x14, 0x30, 0x76,
-	0xe4, 0xbf, 0xf7, 0x7b, 0xc5, 0xfd, 0xdf, 0x19,
-	0x1e, 0x0f, 0xdf, 0x1c, 0x40, 0xfa, 0x34, 0x9e,
-	0x1f, 0x42, 0x24, 0xa3, 0x2c, 0xd5, 0xc7, 0xc9,
-	0x7b, 0x47, 0x78, 0x96, 0xf1, 0x37, 0x0e, 0x88,
-	0xcb, 0xa6, 0x52, 0x29, 0xd7, 0xa8, 0x38, 0x29,
-	0x8e, 0x6e, 0x23, 0x47, 0xd4, 0x4b, 0x70, 0x3e
-};
-static const u8 pkex_resp_y_bp_p512r1[64] = {
-	0x80, 0x1f, 0x43, 0xd2, 0x17, 0x35, 0xec, 0x81,
-	0xd9, 0x4b, 0xdc, 0x81, 0x19, 0xd9, 0x5f, 0x68,
-	0x16, 0x84, 0xfe, 0x63, 0x4b, 0x8d, 0x5d, 0xaa,
-	0x88, 0x4a, 0x47, 0x48, 0xd4, 0xea, 0xab, 0x7d,
-	0x6a, 0xbf, 0xe1, 0x28, 0x99, 0x6a, 0x87, 0x1c,
-	0x30, 0xb4, 0x44, 0x2d, 0x75, 0xac, 0x35, 0x09,
-	0x73, 0x24, 0x3d, 0xb4, 0x43, 0xb1, 0xc1, 0x56,
-	0x56, 0xad, 0x30, 0x87, 0xf4, 0xc3, 0x00, 0xc7
-};
-
-
-static void dpp_debug_print_point(const char *title, const EC_GROUP *group,
-				  const EC_POINT *point)
-{
-	BIGNUM *x, *y;
-	BN_CTX *ctx;
-	char *x_str = NULL, *y_str = NULL;
-
-	if (!wpa_debug_show_keys)
-		return;
-
-	ctx = BN_CTX_new();
-	x = BN_new();
-	y = BN_new();
-	if (!ctx || !x || !y ||
-	    EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx) != 1)
-		goto fail;
-
-	x_str = BN_bn2hex(x);
-	y_str = BN_bn2hex(y);
-	if (!x_str || !y_str)
-		goto fail;
-
-	wpa_printf(MSG_DEBUG, "%s (%s,%s)", title, x_str, y_str);
-
-fail:
-	OPENSSL_free(x_str);
-	OPENSSL_free(y_str);
-	BN_free(x);
-	BN_free(y);
-	BN_CTX_free(ctx);
-}
-
-
-static int dpp_hash_vector(const struct dpp_curve_params *curve,
-			   size_t num_elem, const u8 *addr[], const size_t *len,
-			   u8 *mac)
-{
-	if (curve->hash_len == 32)
-		return sha256_vector(num_elem, addr, len, mac);
-	if (curve->hash_len == 48)
-		return sha384_vector(num_elem, addr, len, mac);
-	if (curve->hash_len == 64)
-		return sha512_vector(num_elem, addr, len, mac);
-	return -1;
-}
-
-
-static int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len,
-			   const char *label, u8 *out, size_t outlen)
-{
-	if (hash_len == 32)
-		return hmac_sha256_kdf(secret, secret_len, NULL,
-				       (const u8 *) label, os_strlen(label),
-				       out, outlen);
-	if (hash_len == 48)
-		return hmac_sha384_kdf(secret, secret_len, NULL,
-				       (const u8 *) label, os_strlen(label),
-				       out, outlen);
-	if (hash_len == 64)
-		return hmac_sha512_kdf(secret, secret_len, NULL,
-				       (const u8 *) label, os_strlen(label),
-				       out, outlen);
-	return -1;
-}
-
-
-static int dpp_hmac_vector(size_t hash_len, const u8 *key, size_t key_len,
-			   size_t num_elem, const u8 *addr[],
-			   const size_t *len, u8 *mac)
-{
-	if (hash_len == 32)
-		return hmac_sha256_vector(key, key_len, num_elem, addr, len,
-					  mac);
-	if (hash_len == 48)
-		return hmac_sha384_vector(key, key_len, num_elem, addr, len,
-					  mac);
-	if (hash_len == 64)
-		return hmac_sha512_vector(key, key_len, num_elem, addr, len,
-					  mac);
-	return -1;
-}
-
-
-static int dpp_hmac(size_t hash_len, const u8 *key, size_t key_len,
-		    const u8 *data, size_t data_len, u8 *mac)
-{
-	if (hash_len == 32)
-		return hmac_sha256(key, key_len, data, data_len, mac);
-	if (hash_len == 48)
-		return hmac_sha384(key, key_len, data, data_len, mac);
-	if (hash_len == 64)
-		return hmac_sha512(key, key_len, data, data_len, mac);
-	return -1;
-}
-
-
-#ifdef CONFIG_DPP2
-
-static int dpp_pbkdf2_f(size_t hash_len,
-			const u8 *password, size_t password_len,
-			const u8 *salt, size_t salt_len,
-			unsigned int iterations, unsigned int count, u8 *digest)
-{
-	unsigned char tmp[DPP_MAX_HASH_LEN], tmp2[DPP_MAX_HASH_LEN];
-	unsigned int i;
-	size_t j;
-	u8 count_buf[4];
-	const u8 *addr[2];
-	size_t len[2];
-
-	addr[0] = salt;
-	len[0] = salt_len;
-	addr[1] = count_buf;
-	len[1] = 4;
-
-	/* F(P, S, c, i) = U1 xor U2 xor ... Uc
-	 * U1 = PRF(P, S || i)
-	 * U2 = PRF(P, U1)
-	 * Uc = PRF(P, Uc-1)
-	 */
-
-	WPA_PUT_BE32(count_buf, count);
-	if (dpp_hmac_vector(hash_len, password, password_len, 2, addr, len,
-			    tmp))
-		return -1;
-	os_memcpy(digest, tmp, hash_len);
-
-	for (i = 1; i < iterations; i++) {
-		if (dpp_hmac(hash_len, password, password_len, tmp, hash_len,
-			     tmp2))
-			return -1;
-		os_memcpy(tmp, tmp2, hash_len);
-		for (j = 0; j < hash_len; j++)
-			digest[j] ^= tmp2[j];
-	}
-
-	return 0;
-}
-
-
-static int dpp_pbkdf2(size_t hash_len, const u8 *password, size_t password_len,
-		      const u8 *salt, size_t salt_len, unsigned int iterations,
-		      u8 *buf, size_t buflen)
-{
-	unsigned int count = 0;
-	unsigned char *pos = buf;
-	size_t left = buflen, plen;
-	unsigned char digest[DPP_MAX_HASH_LEN];
-
-	while (left > 0) {
-		count++;
-		if (dpp_pbkdf2_f(hash_len, password, password_len,
-				 salt, salt_len, iterations, count, digest))
-			return -1;
-		plen = left > hash_len ? hash_len : left;
-		os_memcpy(pos, digest, plen);
-		pos += plen;
-		left -= plen;
-	}
-
-	return 0;
-}
-
-#endif /* CONFIG_DPP2 */
-
-
-static int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len)
-{
-	int num_bytes, offset;
-
-	num_bytes = BN_num_bytes(bn);
-	if ((size_t) num_bytes > len)
-		return -1;
-	offset = len - num_bytes;
-	os_memset(pos, 0, offset);
-	BN_bn2bin(bn, pos + offset);
-	return 0;
-}
-
-
-static struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix)
-{
-	int len, res;
-	EC_KEY *eckey;
-	struct wpabuf *buf;
-	unsigned char *pos;
-
-	eckey = EVP_PKEY_get1_EC_KEY(pkey);
-	if (!eckey)
-		return NULL;
-	EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED);
-	len = i2o_ECPublicKey(eckey, NULL);
-	if (len <= 0) {
-		wpa_printf(MSG_ERROR,
-			   "DDP: Failed to determine public key encoding length");
-		EC_KEY_free(eckey);
-		return NULL;
-	}
-
-	buf = wpabuf_alloc(len);
-	if (!buf) {
-		EC_KEY_free(eckey);
-		return NULL;
-	}
-
-	pos = wpabuf_put(buf, len);
-	res = i2o_ECPublicKey(eckey, &pos);
-	EC_KEY_free(eckey);
-	if (res != len) {
-		wpa_printf(MSG_ERROR,
-			   "DDP: Failed to encode public key (res=%d/%d)",
-			   res, len);
-		wpabuf_free(buf);
-		return NULL;
-	}
-
-	if (!prefix) {
-		/* Remove 0x04 prefix to match DPP definition */
-		pos = wpabuf_mhead(buf);
-		os_memmove(pos, pos + 1, len - 1);
-		buf->used--;
-	}
-
-	return buf;
-}
-
-
-static EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group,
-					     const u8 *buf_x, const u8 *buf_y,
-					     size_t len)
-{
-	EC_KEY *eckey = NULL;
-	BN_CTX *ctx;
-	EC_POINT *point = NULL;
-	BIGNUM *x = NULL, *y = NULL;
-	EVP_PKEY *pkey = NULL;
-
-	ctx = BN_CTX_new();
-	if (!ctx) {
-		wpa_printf(MSG_ERROR, "DPP: Out of memory");
-		return NULL;
-	}
-
-	point = EC_POINT_new(group);
-	x = BN_bin2bn(buf_x, len, NULL);
-	y = BN_bin2bn(buf_y, len, NULL);
-	if (!point || !x || !y) {
-		wpa_printf(MSG_ERROR, "DPP: Out of memory");
-		goto fail;
-	}
-
-	if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) {
-		wpa_printf(MSG_ERROR,
-			   "DPP: OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
-		goto fail;
-	}
-
-	if (!EC_POINT_is_on_curve(group, point, ctx) ||
-	    EC_POINT_is_at_infinity(group, point)) {
-		wpa_printf(MSG_ERROR, "DPP: Invalid point");
-		goto fail;
-	}
-	dpp_debug_print_point("DPP: dpp_set_pubkey_point_group", group, point);
-
-	eckey = EC_KEY_new();
-	if (!eckey ||
-	    EC_KEY_set_group(eckey, group) != 1 ||
-	    EC_KEY_set_public_key(eckey, point) != 1) {
-		wpa_printf(MSG_ERROR,
-			   "DPP: Failed to set EC_KEY: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
-		goto fail;
-	}
-	EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
-
-	pkey = EVP_PKEY_new();
-	if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) {
-		wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY");
-		goto fail;
-	}
-
-out:
-	BN_free(x);
-	BN_free(y);
-	EC_KEY_free(eckey);
-	EC_POINT_free(point);
-	BN_CTX_free(ctx);
-	return pkey;
-fail:
-	EVP_PKEY_free(pkey);
-	pkey = NULL;
-	goto out;
-}
-
-
-static EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key,
-				       const u8 *buf, size_t len)
-{
-	EC_KEY *eckey;
-	const EC_GROUP *group;
-	EVP_PKEY *pkey = NULL;
-
-	if (len & 1)
-		return NULL;
-
-	eckey = EVP_PKEY_get1_EC_KEY(group_key);
-	if (!eckey) {
-		wpa_printf(MSG_ERROR,
-			   "DPP: Could not get EC_KEY from group_key");
-		return NULL;
-	}
-
-	group = EC_KEY_get0_group(eckey);
-	if (group)
-		pkey = dpp_set_pubkey_point_group(group, buf, buf + len / 2,
-						  len / 2);
-	else
-		wpa_printf(MSG_ERROR, "DPP: Could not get EC group");
-
-	EC_KEY_free(eckey);
-	return pkey;
-}
-
-
-static int dpp_ecdh(EVP_PKEY *own, EVP_PKEY *peer,
-		    u8 *secret, size_t *secret_len)
-{
-	EVP_PKEY_CTX *ctx;
-	int ret = -1;
-
-	ERR_clear_error();
-	*secret_len = 0;
-
-	ctx = EVP_PKEY_CTX_new(own, NULL);
-	if (!ctx) {
-		wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_CTX_new failed: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
-		return -1;
-	}
-
-	if (EVP_PKEY_derive_init(ctx) != 1) {
-		wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive_init failed: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
-		goto fail;
-	}
-
-	if (EVP_PKEY_derive_set_peer(ctx, peer) != 1) {
-		wpa_printf(MSG_ERROR,
-			   "DPP: EVP_PKEY_derive_set_peet failed: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
-		goto fail;
-	}
-
-	if (EVP_PKEY_derive(ctx, NULL, secret_len) != 1) {
-		wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive(NULL) failed: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
-		goto fail;
-	}
-
-	if (*secret_len > DPP_MAX_SHARED_SECRET_LEN) {
-		u8 buf[200];
-		int level = *secret_len > 200 ? MSG_ERROR : MSG_DEBUG;
-
-		/* It looks like OpenSSL can return unexpectedly large buffer
-		 * need for shared secret from EVP_PKEY_derive(NULL) in some
-		 * cases. For example, group 19 has shown cases where secret_len
-		 * is set to 72 even though the actual length ends up being
-		 * updated to 32 when EVP_PKEY_derive() is called with a buffer
-		 * for the value. Work around this by trying to fetch the value
-		 * and continue if it is within supported range even when the
-		 * initial buffer need is claimed to be larger. */
-		wpa_printf(level,
-			   "DPP: Unexpected secret_len=%d from EVP_PKEY_derive()",
-			   (int) *secret_len);
-		if (*secret_len > 200)
-			goto fail;
-		if (EVP_PKEY_derive(ctx, buf, secret_len) != 1) {
-			wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive failed: %s",
-				   ERR_error_string(ERR_get_error(), NULL));
-			goto fail;
-		}
-		if (*secret_len > DPP_MAX_SHARED_SECRET_LEN) {
-			wpa_printf(MSG_ERROR,
-				   "DPP: Unexpected secret_len=%d from EVP_PKEY_derive()",
-				   (int) *secret_len);
-			goto fail;
-		}
-		wpa_hexdump_key(MSG_DEBUG, "DPP: Unexpected secret_len change",
-				buf, *secret_len);
-		os_memcpy(secret, buf, *secret_len);
-		forced_memzero(buf, sizeof(buf));
-		goto done;
-	}
-
-	if (EVP_PKEY_derive(ctx, secret, secret_len) != 1) {
-		wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive failed: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
-		goto fail;
-	}
-
-done:
-	ret = 0;
-
-fail:
-	EVP_PKEY_CTX_free(ctx);
-	return ret;
-}
-
-
-static void dpp_auth_fail(struct dpp_authentication *auth, const char *txt)
+void dpp_auth_fail(struct dpp_authentication *auth, const char *txt)
 {
 	wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
 }
@@ -1037,57 +312,21 @@
 }
 
 
-static const struct dpp_curve_params *
-dpp_get_curve_oid(const ASN1_OBJECT *poid)
+int dpp_parse_uri_version(struct dpp_bootstrap_info *bi, const char *version)
 {
-	ASN1_OBJECT *oid;
-	int i;
+#ifdef CONFIG_DPP2
+	if (!version || DPP_VERSION < 2)
+		return 0;
 
-	for (i = 0; dpp_curves[i].name; i++) {
-		oid = OBJ_txt2obj(dpp_curves[i].name, 0);
-		if (oid && OBJ_cmp(poid, oid) == 0)
-			return &dpp_curves[i];
-	}
-	return NULL;
-}
+	if (*version == '1')
+		bi->version = 1;
+	else if (*version == '2')
+		bi->version = 2;
+	else
+		wpa_printf(MSG_DEBUG, "DPP: Unknown URI version");
 
-
-static const struct dpp_curve_params * dpp_get_curve_nid(int nid)
-{
-	int i, tmp;
-
-	if (!nid)
-		return NULL;
-	for (i = 0; dpp_curves[i].name; i++) {
-		tmp = OBJ_txt2nid(dpp_curves[i].name);
-		if (tmp == nid)
-			return &dpp_curves[i];
-	}
-	return NULL;
-}
-
-
-static int dpp_bi_pubkey_hash(struct dpp_bootstrap_info *bi,
-			      const u8 *data, size_t data_len)
-{
-	const u8 *addr[2];
-	size_t len[2];
-
-	addr[0] = data;
-	len[0] = data_len;
-	if (sha256_vector(1, addr, len, bi->pubkey_hash) < 0)
-		return -1;
-	wpa_hexdump(MSG_DEBUG, "DPP: Public key hash",
-		    bi->pubkey_hash, SHA256_MAC_LEN);
-
-	addr[0] = (const u8 *) "chirp";
-	len[0] = 5;
-	addr[1] = data;
-	len[1] = data_len;
-	if (sha256_vector(2, addr, len, bi->pubkey_hash_chirp) < 0)
-		return -1;
-	wpa_hexdump(MSG_DEBUG, "DPP: Public key hash (chirp)",
-		    bi->pubkey_hash_chirp, SHA256_MAC_LEN);
+	wpa_printf(MSG_DEBUG, "DPP: URI version: %d", bi->version);
+#endif /* CONFIG_DPP2 */
 
 	return 0;
 }
@@ -1095,28 +334,10 @@
 
 static int dpp_parse_uri_pk(struct dpp_bootstrap_info *bi, const char *info)
 {
-	const char *end;
 	u8 *data;
 	size_t data_len;
-	EVP_PKEY *pkey;
-	const unsigned char *p;
 	int res;
-	X509_PUBKEY *pub = NULL;
-	ASN1_OBJECT *ppkalg;
-	const unsigned char *pk;
-	int ppklen;
-	X509_ALGOR *pa;
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-	(defined(LIBRESSL_VERSION_NUMBER) && \
-	 LIBRESSL_VERSION_NUMBER < 0x20800000L)
-	ASN1_OBJECT *pa_oid;
-#else
-	const ASN1_OBJECT *pa_oid;
-#endif
-	const void *pval;
-	int ptype;
-	const ASN1_OBJECT *poid;
-	char buf[100];
+	const char *end;
 
 	end = os_strchr(info, ';');
 	if (!end)
@@ -1131,101 +352,9 @@
 	wpa_hexdump(MSG_DEBUG, "DPP: Base64 decoded URI public-key",
 		    data, data_len);
 
-	if (dpp_bi_pubkey_hash(bi, data, data_len) < 0) {
-		wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
-		os_free(data);
-		return -1;
-	}
-
-	/* DER encoded ASN.1 SubjectPublicKeyInfo
-	 *
-	 * SubjectPublicKeyInfo  ::=  SEQUENCE  {
-	 *      algorithm            AlgorithmIdentifier,
-	 *      subjectPublicKey     BIT STRING  }
-	 *
-	 * AlgorithmIdentifier  ::=  SEQUENCE  {
-	 *      algorithm               OBJECT IDENTIFIER,
-	 *      parameters              ANY DEFINED BY algorithm OPTIONAL  }
-	 *
-	 * subjectPublicKey = compressed format public key per ANSI X9.63
-	 * algorithm = ecPublicKey (1.2.840.10045.2.1)
-	 * parameters = shall be present and shall be OBJECT IDENTIFIER; e.g.,
-	 *       prime256v1 (1.2.840.10045.3.1.7)
-	 */
-
-	p = data;
-	pkey = d2i_PUBKEY(NULL, &p, data_len);
+	res = dpp_get_subject_public_key(bi, data, data_len);
 	os_free(data);
-
-	if (!pkey) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Could not parse URI public-key SubjectPublicKeyInfo");
-		return -1;
-	}
-
-	if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: SubjectPublicKeyInfo does not describe an EC key");
-		EVP_PKEY_free(pkey);
-		return -1;
-	}
-
-	res = X509_PUBKEY_set(&pub, pkey);
-	if (res != 1) {
-		wpa_printf(MSG_DEBUG, "DPP: Could not set pubkey");
-		goto fail;
-	}
-
-	res = X509_PUBKEY_get0_param(&ppkalg, &pk, &ppklen, &pa, pub);
-	if (res != 1) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Could not extract SubjectPublicKeyInfo parameters");
-		goto fail;
-	}
-	res = OBJ_obj2txt(buf, sizeof(buf), ppkalg, 0);
-	if (res < 0 || (size_t) res >= sizeof(buf)) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Could not extract SubjectPublicKeyInfo algorithm");
-		goto fail;
-	}
-	wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey algorithm: %s", buf);
-	if (os_strcmp(buf, "id-ecPublicKey") != 0) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Unsupported SubjectPublicKeyInfo algorithm");
-		goto fail;
-	}
-
-	X509_ALGOR_get0(&pa_oid, &ptype, (void *) &pval, pa);
-	if (ptype != V_ASN1_OBJECT) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: SubjectPublicKeyInfo parameters did not contain an OID");
-		goto fail;
-	}
-	poid = pval;
-	res = OBJ_obj2txt(buf, sizeof(buf), poid, 0);
-	if (res < 0 || (size_t) res >= sizeof(buf)) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Could not extract SubjectPublicKeyInfo parameters OID");
-		goto fail;
-	}
-	wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey parameters: %s", buf);
-	bi->curve = dpp_get_curve_oid(poid);
-	if (!bi->curve) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Unsupported SubjectPublicKeyInfo curve: %s",
-			   buf);
-		goto fail;
-	}
-
-	wpa_hexdump(MSG_DEBUG, "DPP: URI subjectPublicKey", pk, ppklen);
-
-	X509_PUBKEY_free(pub);
-	bi->pubkey = pkey;
-	return 0;
-fail:
-	X509_PUBKEY_free(pub);
-	EVP_PKEY_free(pkey);
-	return -1;
+	return res;
 }
 
 
@@ -1234,6 +363,7 @@
 	const char *pos = uri;
 	const char *end;
 	const char *chan_list = NULL, *mac = NULL, *info = NULL, *pk = NULL;
+	const char *version = NULL;
 	struct dpp_bootstrap_info *bi;
 
 	wpa_hexdump_ascii(MSG_DEBUG, "DPP: URI", uri, os_strlen(uri));
@@ -1264,6 +394,8 @@
 			info = pos + 2;
 		else if (pos[0] == 'K' && pos[1] == ':' && !pk)
 			pk = pos + 2;
+		else if (pos[0] == 'V' && pos[1] == ':' && !version)
+			version = pos + 2;
 		else
 			wpa_hexdump_ascii(MSG_DEBUG,
 					  "DPP: Ignore unrecognized URI parameter",
@@ -1284,6 +416,7 @@
 	    dpp_parse_uri_chan_list(bi, chan_list) < 0 ||
 	    dpp_parse_uri_mac(bi, mac) < 0 ||
 	    dpp_parse_uri_info(bi, info) < 0 ||
+	    dpp_parse_uri_version(bi, version) < 0 ||
 	    dpp_parse_uri_pk(bi, pk) < 0) {
 		dpp_bootstrap_info_free(bi);
 		bi = NULL;
@@ -1293,462 +426,7 @@
 }
 
 
-static void dpp_debug_print_key(const char *title, EVP_PKEY *key)
-{
-	EC_KEY *eckey;
-	BIO *out;
-	size_t rlen;
-	char *txt;
-	int res;
-	unsigned char *der = NULL;
-	int der_len;
-	const EC_GROUP *group;
-	const EC_POINT *point;
-
-	out = BIO_new(BIO_s_mem());
-	if (!out)
-		return;
-
-	EVP_PKEY_print_private(out, key, 0, NULL);
-	rlen = BIO_ctrl_pending(out);
-	txt = os_malloc(rlen + 1);
-	if (txt) {
-		res = BIO_read(out, txt, rlen);
-		if (res > 0) {
-			txt[res] = '\0';
-			wpa_printf(MSG_DEBUG, "%s: %s", title, txt);
-		}
-		os_free(txt);
-	}
-	BIO_free(out);
-
-	eckey = EVP_PKEY_get1_EC_KEY(key);
-	if (!eckey)
-		return;
-
-	group = EC_KEY_get0_group(eckey);
-	point = EC_KEY_get0_public_key(eckey);
-	if (group && point)
-		dpp_debug_print_point(title, group, point);
-
-	der_len = i2d_ECPrivateKey(eckey, &der);
-	if (der_len > 0)
-		wpa_hexdump_key(MSG_DEBUG, "DPP: ECPrivateKey", der, der_len);
-	OPENSSL_free(der);
-	if (der_len <= 0) {
-		der = NULL;
-		der_len = i2d_EC_PUBKEY(eckey, &der);
-		if (der_len > 0)
-			wpa_hexdump(MSG_DEBUG, "DPP: EC_PUBKEY", der, der_len);
-		OPENSSL_free(der);
-	}
-
-	EC_KEY_free(eckey);
-}
-
-
-static EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve)
-{
-	EVP_PKEY_CTX *kctx = NULL;
-	EC_KEY *ec_params = NULL;
-	EVP_PKEY *params = NULL, *key = NULL;
-	int nid;
-
-	wpa_printf(MSG_DEBUG, "DPP: Generating a keypair");
-
-	nid = OBJ_txt2nid(curve->name);
-	if (nid == NID_undef) {
-		wpa_printf(MSG_INFO, "DPP: Unsupported curve %s", curve->name);
-		return NULL;
-	}
-
-	ec_params = EC_KEY_new_by_curve_name(nid);
-	if (!ec_params) {
-		wpa_printf(MSG_ERROR,
-			   "DPP: Failed to generate EC_KEY parameters");
-		goto fail;
-	}
-	EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE);
-	params = EVP_PKEY_new();
-	if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) {
-		wpa_printf(MSG_ERROR,
-			   "DPP: Failed to generate EVP_PKEY parameters");
-		goto fail;
-	}
-
-	kctx = EVP_PKEY_CTX_new(params, NULL);
-	if (!kctx ||
-	    EVP_PKEY_keygen_init(kctx) != 1 ||
-	    EVP_PKEY_keygen(kctx, &key) != 1) {
-		wpa_printf(MSG_ERROR, "DPP: Failed to generate EC key");
-		key = NULL;
-		goto fail;
-	}
-
-	if (wpa_debug_show_keys)
-		dpp_debug_print_key("Own generated key", key);
-
-fail:
-	EC_KEY_free(ec_params);
-	EVP_PKEY_free(params);
-	EVP_PKEY_CTX_free(kctx);
-	return key;
-}
-
-
-static const struct dpp_curve_params *
-dpp_get_curve_name(const char *name)
-{
-	int i;
-
-	for (i = 0; dpp_curves[i].name; i++) {
-		if (os_strcmp(name, dpp_curves[i].name) == 0 ||
-		    (dpp_curves[i].jwk_crv &&
-		     os_strcmp(name, dpp_curves[i].jwk_crv) == 0))
-			return &dpp_curves[i];
-	}
-	return NULL;
-}
-
-
-static const struct dpp_curve_params *
-dpp_get_curve_jwk_crv(const char *name)
-{
-	int i;
-
-	for (i = 0; dpp_curves[i].name; i++) {
-		if (dpp_curves[i].jwk_crv &&
-		    os_strcmp(name, dpp_curves[i].jwk_crv) == 0)
-			return &dpp_curves[i];
-	}
-	return NULL;
-}
-
-
-static EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve,
-				  const u8 *privkey, size_t privkey_len)
-{
-	EVP_PKEY *pkey;
-	EC_KEY *eckey;
-	const EC_GROUP *group;
-	int nid;
-
-	pkey = EVP_PKEY_new();
-	if (!pkey)
-		return NULL;
-	eckey = d2i_ECPrivateKey(NULL, &privkey, privkey_len);
-	if (!eckey) {
-		wpa_printf(MSG_INFO,
-			   "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
-		EVP_PKEY_free(pkey);
-		return NULL;
-	}
-	group = EC_KEY_get0_group(eckey);
-	if (!group) {
-		EC_KEY_free(eckey);
-		EVP_PKEY_free(pkey);
-		return NULL;
-	}
-	nid = EC_GROUP_get_curve_name(group);
-	*curve = dpp_get_curve_nid(nid);
-	if (!*curve) {
-		wpa_printf(MSG_INFO,
-			   "DPP: Unsupported curve (nid=%d) in pre-assigned key",
-			   nid);
-		EC_KEY_free(eckey);
-		EVP_PKEY_free(pkey);
-		return NULL;
-	}
-
-	if (EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) {
-		EC_KEY_free(eckey);
-		EVP_PKEY_free(pkey);
-		return NULL;
-	}
-	return pkey;
-}
-
-
-typedef struct {
-	/* AlgorithmIdentifier ecPublicKey with optional parameters present
-	 * as an OID identifying the curve */
-	X509_ALGOR *alg;
-	/* Compressed format public key per ANSI X9.63 */
-	ASN1_BIT_STRING *pub_key;
-} DPP_BOOTSTRAPPING_KEY;
-
-ASN1_SEQUENCE(DPP_BOOTSTRAPPING_KEY) = {
-	ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, alg, X509_ALGOR),
-	ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, pub_key, ASN1_BIT_STRING)
-} ASN1_SEQUENCE_END(DPP_BOOTSTRAPPING_KEY);
-
-IMPLEMENT_ASN1_FUNCTIONS(DPP_BOOTSTRAPPING_KEY);
-
-
-static struct wpabuf * dpp_bootstrap_key_der(EVP_PKEY *key)
-{
-	unsigned char *der = NULL;
-	int der_len;
-	EC_KEY *eckey;
-	struct wpabuf *ret = NULL;
-	size_t len;
-	const EC_GROUP *group;
-	const EC_POINT *point;
-	BN_CTX *ctx;
-	DPP_BOOTSTRAPPING_KEY *bootstrap = NULL;
-	int nid;
-
-	ctx = BN_CTX_new();
-	eckey = EVP_PKEY_get1_EC_KEY(key);
-	if (!ctx || !eckey)
-		goto fail;
-
-	group = EC_KEY_get0_group(eckey);
-	point = EC_KEY_get0_public_key(eckey);
-	if (!group || !point)
-		goto fail;
-	dpp_debug_print_point("DPP: bootstrap public key", group, point);
-	nid = EC_GROUP_get_curve_name(group);
-
-	bootstrap = DPP_BOOTSTRAPPING_KEY_new();
-	if (!bootstrap ||
-	    X509_ALGOR_set0(bootstrap->alg, OBJ_nid2obj(EVP_PKEY_EC),
-			    V_ASN1_OBJECT, (void *) OBJ_nid2obj(nid)) != 1)
-		goto fail;
-
-	len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
-				 NULL, 0, ctx);
-	if (len == 0)
-		goto fail;
-
-	der = OPENSSL_malloc(len);
-	if (!der)
-		goto fail;
-	len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
-				 der, len, ctx);
-
-	OPENSSL_free(bootstrap->pub_key->data);
-	bootstrap->pub_key->data = der;
-	der = NULL;
-	bootstrap->pub_key->length = len;
-	/* No unused bits */
-	bootstrap->pub_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
-	bootstrap->pub_key->flags |= ASN1_STRING_FLAG_BITS_LEFT;
-
-	der_len = i2d_DPP_BOOTSTRAPPING_KEY(bootstrap, &der);
-	if (der_len <= 0) {
-		wpa_printf(MSG_ERROR,
-			   "DDP: Failed to build DER encoded public key");
-		goto fail;
-	}
-
-	ret = wpabuf_alloc_copy(der, der_len);
-fail:
-	DPP_BOOTSTRAPPING_KEY_free(bootstrap);
-	OPENSSL_free(der);
-	EC_KEY_free(eckey);
-	BN_CTX_free(ctx);
-	return ret;
-}
-
-
-static int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi)
-{
-	struct wpabuf *der;
-	int res;
-
-	der = dpp_bootstrap_key_der(bi->pubkey);
-	if (!der)
-		return -1;
-	wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
-			der);
-	res = dpp_bi_pubkey_hash(bi, wpabuf_head(der), wpabuf_len(der));
-	if (res < 0)
-		wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
-	wpabuf_free(der);
-	return res;
-}
-
-
-static int dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
-		      const u8 *privkey, size_t privkey_len)
-{
-	char *base64 = NULL;
-	char *pos, *end;
-	size_t len;
-	struct wpabuf *der = NULL;
-
-	if (!curve) {
-		bi->curve = &dpp_curves[0];
-	} else {
-		bi->curve = dpp_get_curve_name(curve);
-		if (!bi->curve) {
-			wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
-				   curve);
-			return -1;
-		}
-	}
-	if (privkey)
-		bi->pubkey = dpp_set_keypair(&bi->curve, privkey, privkey_len);
-	else
-		bi->pubkey = dpp_gen_keypair(bi->curve);
-	if (!bi->pubkey)
-		goto fail;
-	bi->own = 1;
-
-	der = dpp_bootstrap_key_der(bi->pubkey);
-	if (!der)
-		goto fail;
-	wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
-			der);
-
-	if (dpp_bi_pubkey_hash(bi, wpabuf_head(der), wpabuf_len(der)) < 0) {
-		wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
-		goto fail;
-	}
-
-	base64 = base64_encode(wpabuf_head(der), wpabuf_len(der), &len);
-	wpabuf_free(der);
-	der = NULL;
-	if (!base64)
-		goto fail;
-	pos = base64;
-	end = pos + len;
-	for (;;) {
-		pos = os_strchr(pos, '\n');
-		if (!pos)
-			break;
-		os_memmove(pos, pos + 1, end - pos);
-	}
-	os_free(bi->pk);
-	bi->pk = base64;
-	return 0;
-fail:
-	os_free(base64);
-	wpabuf_free(der);
-	return -1;
-}
-
-
-static int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1,
-			 unsigned int hash_len)
-{
-	u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
-	const char *info = "first intermediate key";
-	int res;
-
-	/* k1 = HKDF(<>, "first intermediate key", M.x) */
-
-	/* HKDF-Extract(<>, M.x) */
-	os_memset(salt, 0, hash_len);
-	if (dpp_hmac(hash_len, salt, hash_len, Mx, Mx_len, prk) < 0)
-		return -1;
-	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=M.x)",
-			prk, hash_len);
-
-	/* HKDF-Expand(PRK, info, L) */
-	res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k1, hash_len);
-	os_memset(prk, 0, hash_len);
-	if (res < 0)
-		return -1;
-
-	wpa_hexdump_key(MSG_DEBUG, "DPP: k1 = HKDF-Expand(PRK, info, L)",
-			k1, hash_len);
-	return 0;
-}
-
-
-static int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2,
-			 unsigned int hash_len)
-{
-	u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
-	const char *info = "second intermediate key";
-	int res;
-
-	/* k2 = HKDF(<>, "second intermediate key", N.x) */
-
-	/* HKDF-Extract(<>, N.x) */
-	os_memset(salt, 0, hash_len);
-	res = dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk);
-	if (res < 0)
-		return -1;
-	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
-			prk, hash_len);
-
-	/* HKDF-Expand(PRK, info, L) */
-	res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k2, hash_len);
-	os_memset(prk, 0, hash_len);
-	if (res < 0)
-		return -1;
-
-	wpa_hexdump_key(MSG_DEBUG, "DPP: k2 = HKDF-Expand(PRK, info, L)",
-			k2, hash_len);
-	return 0;
-}
-
-
-static int dpp_derive_ke(struct dpp_authentication *auth, u8 *ke,
-			 unsigned int hash_len)
-{
-	size_t nonce_len;
-	u8 nonces[2 * DPP_MAX_NONCE_LEN];
-	const char *info_ke = "DPP Key";
-	u8 prk[DPP_MAX_HASH_LEN];
-	int res;
-	const u8 *addr[3];
-	size_t len[3];
-	size_t num_elem = 0;
-
-	if (!auth->Mx_len || !auth->Nx_len) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Mx/Nx not available - cannot derive ke");
-		return -1;
-	}
-
-	/* ke = HKDF(I-nonce | R-nonce, "DPP Key", M.x | N.x [| L.x]) */
-
-	/* HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x]) */
-	nonce_len = auth->curve->nonce_len;
-	os_memcpy(nonces, auth->i_nonce, nonce_len);
-	os_memcpy(&nonces[nonce_len], auth->r_nonce, nonce_len);
-	addr[num_elem] = auth->Mx;
-	len[num_elem] = auth->Mx_len;
-	num_elem++;
-	addr[num_elem] = auth->Nx;
-	len[num_elem] = auth->Nx_len;
-	num_elem++;
-	if (auth->peer_bi && auth->own_bi) {
-		if (!auth->Lx_len) {
-			wpa_printf(MSG_DEBUG,
-				   "DPP: Lx not available - cannot derive ke");
-			return -1;
-		}
-		addr[num_elem] = auth->Lx;
-		len[num_elem] = auth->secret_len;
-		num_elem++;
-	}
-	res = dpp_hmac_vector(hash_len, nonces, 2 * nonce_len,
-			      num_elem, addr, len, prk);
-	if (res < 0)
-		return -1;
-	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
-			prk, hash_len);
-
-	/* HKDF-Expand(PRK, info, L) */
-	res = dpp_hkdf_expand(hash_len, prk, hash_len, info_ke, ke, hash_len);
-	os_memset(prk, 0, hash_len);
-	if (res < 0)
-		return -1;
-
-	wpa_hexdump_key(MSG_DEBUG, "DPP: ke = HKDF-Expand(PRK, info, L)",
-			ke, hash_len);
-	return 0;
-}
-
-
-static void dpp_build_attr_status(struct wpabuf *msg,
-				  enum dpp_status_error status)
+void dpp_build_attr_status(struct wpabuf *msg, enum dpp_status_error status)
 {
 	wpa_printf(MSG_DEBUG, "DPP: Status %d", status);
 	wpabuf_put_le16(msg, DPP_ATTR_STATUS);
@@ -1757,8 +435,7 @@
 }
 
 
-static void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg,
-						const u8 *hash)
+void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg, const u8 *hash)
 {
 	if (hash) {
 		wpa_printf(MSG_DEBUG, "DPP: R-Bootstrap Key Hash");
@@ -1769,374 +446,6 @@
 }
 
 
-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 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 */
-	wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
-	wpabuf_put_le16(msg, 1);
-	wpabuf_put_u8(msg, 2);
-#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, 2);
-	}
-#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_channel_ok_init(struct hostapd_hw_modes *own_modes,
 			       u16 num_modes, unsigned int freq)
 {
@@ -2255,10 +564,9 @@
 }
 
 
-static int dpp_prepare_channel_list(struct dpp_authentication *auth,
-				    unsigned int neg_freq,
-				    struct hostapd_hw_modes *own_modes,
-				    u16 num_modes)
+int dpp_prepare_channel_list(struct dpp_authentication *auth,
+			     unsigned int neg_freq,
+			     struct hostapd_hw_modes *own_modes, u16 num_modes)
 {
 	int res;
 	char freqs[DPP_BOOTSTRAP_MAX_FREQ * 6 + 10], *pos, *end;
@@ -2269,6 +577,7 @@
 			return -1;
 		auth->num_freq = 1;
 		auth->freq[0] = neg_freq;
+		auth->curr_freq = neg_freq;
 		return 0;
 	}
 
@@ -2304,7 +613,7 @@
 }
 
 
-static int dpp_gen_uri(struct dpp_bootstrap_info *bi)
+int dpp_gen_uri(struct dpp_bootstrap_info *bi)
 {
 	char macstr[ETH_ALEN * 2 + 10];
 	size_t len;
@@ -2320,50 +629,27 @@
 	len += os_strlen(macstr); /* M:...; */
 	if (bi->info)
 		len += 3 + os_strlen(bi->info); /* I:...; */
+#ifdef CONFIG_DPP2
+	len += 4; /* V:2; */
+#endif /* CONFIG_DPP2 */
 	len += 4 + os_strlen(bi->pk); /* K:...;; */
 
 	os_free(bi->uri);
 	bi->uri = os_malloc(len + 1);
 	if (!bi->uri)
 		return -1;
-	os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%sK:%s;;",
+	os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%sK:%s;;",
 		    bi->chan ? "C:" : "", bi->chan ? bi->chan : "",
 		    bi->chan ? ";" : "",
 		    macstr,
 		    bi->info ? "I:" : "", bi->info ? bi->info : "",
 		    bi->info ? ";" : "",
+		    DPP_VERSION == 2 ? "V:2;" : "",
 		    bi->pk);
 	return 0;
 }
 
 
-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_alloc_auth(struct dpp_global *dpp, void *msg_ctx)
 {
@@ -2379,151 +665,6 @@
 }
 
 
-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 struct wpabuf * dpp_build_conf_req_attr(struct dpp_authentication *auth,
 					       const char *json)
 {
@@ -2629,7 +770,7 @@
 }
 
 
-static void dpp_write_adv_proto(struct wpabuf *buf)
+void dpp_write_adv_proto(struct wpabuf *buf)
 {
 	/* Advertisement Protocol IE */
 	wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
@@ -2643,7 +784,7 @@
 }
 
 
-static void dpp_write_gas_query(struct wpabuf *buf, struct wpabuf *query)
+void dpp_write_gas_query(struct wpabuf *buf, struct wpabuf *query)
 {
 	/* GAS Query */
 	wpabuf_put_le16(buf, wpabuf_len(query));
@@ -2738,1704 +879,6 @@
 }
 
 
-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 int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth)
-{
-	struct wpabuf *pix, *prx, *bix, *brx;
-	const u8 *addr[7];
-	size_t len[7];
-	size_t i, num_elem = 0;
-	size_t nonce_len;
-	u8 zero = 0;
-	int res = -1;
-
-	/* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
-	nonce_len = auth->curve->nonce_len;
-
-	if (auth->initiator) {
-		pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
-		prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
-		if (auth->own_bi)
-			bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
-		else
-			bix = NULL;
-		brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
-	} else {
-		pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
-		prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
-		if (auth->peer_bi)
-			bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
-		else
-			bix = NULL;
-		brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
-	}
-	if (!pix || !prx || !brx)
-		goto fail;
-
-	addr[num_elem] = auth->i_nonce;
-	len[num_elem] = nonce_len;
-	num_elem++;
-
-	addr[num_elem] = auth->r_nonce;
-	len[num_elem] = nonce_len;
-	num_elem++;
-
-	addr[num_elem] = wpabuf_head(pix);
-	len[num_elem] = wpabuf_len(pix) / 2;
-	num_elem++;
-
-	addr[num_elem] = wpabuf_head(prx);
-	len[num_elem] = wpabuf_len(prx) / 2;
-	num_elem++;
-
-	if (bix) {
-		addr[num_elem] = wpabuf_head(bix);
-		len[num_elem] = wpabuf_len(bix) / 2;
-		num_elem++;
-	}
-
-	addr[num_elem] = wpabuf_head(brx);
-	len[num_elem] = wpabuf_len(brx) / 2;
-	num_elem++;
-
-	addr[num_elem] = &zero;
-	len[num_elem] = 1;
-	num_elem++;
-
-	wpa_printf(MSG_DEBUG, "DPP: R-auth hash components");
-	for (i = 0; i < num_elem; i++)
-		wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
-	res = dpp_hash_vector(auth->curve, num_elem, addr, len, r_auth);
-	if (res == 0)
-		wpa_hexdump(MSG_DEBUG, "DPP: R-auth", r_auth,
-			    auth->curve->hash_len);
-fail:
-	wpabuf_free(pix);
-	wpabuf_free(prx);
-	wpabuf_free(bix);
-	wpabuf_free(brx);
-	return res;
-}
-
-
-static int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth)
-{
-	struct wpabuf *pix = NULL, *prx = NULL, *bix = NULL, *brx = NULL;
-	const u8 *addr[7];
-	size_t len[7];
-	size_t i, num_elem = 0;
-	size_t nonce_len;
-	u8 one = 1;
-	int res = -1;
-
-	/* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
-	nonce_len = auth->curve->nonce_len;
-
-	if (auth->initiator) {
-		pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
-		prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
-		if (auth->own_bi)
-			bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
-		else
-			bix = NULL;
-		if (!auth->peer_bi)
-			goto fail;
-		brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
-	} else {
-		pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
-		prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
-		if (auth->peer_bi)
-			bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
-		else
-			bix = NULL;
-		if (!auth->own_bi)
-			goto fail;
-		brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
-	}
-	if (!pix || !prx || !brx)
-		goto fail;
-
-	addr[num_elem] = auth->r_nonce;
-	len[num_elem] = nonce_len;
-	num_elem++;
-
-	addr[num_elem] = auth->i_nonce;
-	len[num_elem] = nonce_len;
-	num_elem++;
-
-	addr[num_elem] = wpabuf_head(prx);
-	len[num_elem] = wpabuf_len(prx) / 2;
-	num_elem++;
-
-	addr[num_elem] = wpabuf_head(pix);
-	len[num_elem] = wpabuf_len(pix) / 2;
-	num_elem++;
-
-	addr[num_elem] = wpabuf_head(brx);
-	len[num_elem] = wpabuf_len(brx) / 2;
-	num_elem++;
-
-	if (bix) {
-		addr[num_elem] = wpabuf_head(bix);
-		len[num_elem] = wpabuf_len(bix) / 2;
-		num_elem++;
-	}
-
-	addr[num_elem] = &one;
-	len[num_elem] = 1;
-	num_elem++;
-
-	wpa_printf(MSG_DEBUG, "DPP: I-auth hash components");
-	for (i = 0; i < num_elem; i++)
-		wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
-	res = dpp_hash_vector(auth->curve, num_elem, addr, len, i_auth);
-	if (res == 0)
-		wpa_hexdump(MSG_DEBUG, "DPP: I-auth", i_auth,
-			    auth->curve->hash_len);
-fail:
-	wpabuf_free(pix);
-	wpabuf_free(prx);
-	wpabuf_free(bix);
-	wpabuf_free(brx);
-	return res;
-}
-
-
-static int dpp_auth_derive_l_responder(struct dpp_authentication *auth)
-{
-	const EC_GROUP *group;
-	EC_POINT *l = NULL;
-	EC_KEY *BI = NULL, *bR = NULL, *pR = NULL;
-	const EC_POINT *BI_point;
-	BN_CTX *bnctx;
-	BIGNUM *lx, *sum, *q;
-	const BIGNUM *bR_bn, *pR_bn;
-	int ret = -1;
-
-	/* L = ((bR + pR) modulo q) * BI */
-
-	bnctx = BN_CTX_new();
-	sum = BN_new();
-	q = BN_new();
-	lx = BN_new();
-	if (!bnctx || !sum || !q || !lx)
-		goto fail;
-	BI = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey);
-	if (!BI)
-		goto fail;
-	BI_point = EC_KEY_get0_public_key(BI);
-	group = EC_KEY_get0_group(BI);
-	if (!group)
-		goto fail;
-
-	bR = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey);
-	pR = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key);
-	if (!bR || !pR)
-		goto fail;
-	bR_bn = EC_KEY_get0_private_key(bR);
-	pR_bn = EC_KEY_get0_private_key(pR);
-	if (!bR_bn || !pR_bn)
-		goto fail;
-	if (EC_GROUP_get_order(group, q, bnctx) != 1 ||
-	    BN_mod_add(sum, bR_bn, pR_bn, q, bnctx) != 1)
-		goto fail;
-	l = EC_POINT_new(group);
-	if (!l ||
-	    EC_POINT_mul(group, l, NULL, BI_point, sum, bnctx) != 1 ||
-	    EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
-						bnctx) != 1) {
-		wpa_printf(MSG_ERROR,
-			   "OpenSSL: failed: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
-		goto fail;
-	}
-
-	if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
-		goto fail;
-	wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
-	auth->Lx_len = auth->secret_len;
-	ret = 0;
-fail:
-	EC_POINT_clear_free(l);
-	EC_KEY_free(BI);
-	EC_KEY_free(bR);
-	EC_KEY_free(pR);
-	BN_clear_free(lx);
-	BN_clear_free(sum);
-	BN_free(q);
-	BN_CTX_free(bnctx);
-	return ret;
-}
-
-
-static int dpp_auth_derive_l_initiator(struct dpp_authentication *auth)
-{
-	const EC_GROUP *group;
-	EC_POINT *l = NULL, *sum = NULL;
-	EC_KEY *bI = NULL, *BR = NULL, *PR = NULL;
-	const EC_POINT *BR_point, *PR_point;
-	BN_CTX *bnctx;
-	BIGNUM *lx;
-	const BIGNUM *bI_bn;
-	int ret = -1;
-
-	/* L = bI * (BR + PR) */
-
-	bnctx = BN_CTX_new();
-	lx = BN_new();
-	if (!bnctx || !lx)
-		goto fail;
-	BR = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey);
-	PR = EVP_PKEY_get1_EC_KEY(auth->peer_protocol_key);
-	if (!BR || !PR)
-		goto fail;
-	BR_point = EC_KEY_get0_public_key(BR);
-	PR_point = EC_KEY_get0_public_key(PR);
-
-	bI = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey);
-	if (!bI)
-		goto fail;
-	group = EC_KEY_get0_group(bI);
-	bI_bn = EC_KEY_get0_private_key(bI);
-	if (!group || !bI_bn)
-		goto fail;
-	sum = EC_POINT_new(group);
-	l = EC_POINT_new(group);
-	if (!sum || !l ||
-	    EC_POINT_add(group, sum, BR_point, PR_point, bnctx) != 1 ||
-	    EC_POINT_mul(group, l, NULL, sum, bI_bn, bnctx) != 1 ||
-	    EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
-						bnctx) != 1) {
-		wpa_printf(MSG_ERROR,
-			   "OpenSSL: failed: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
-		goto fail;
-	}
-
-	if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
-		goto fail;
-	wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
-	auth->Lx_len = auth->secret_len;
-	ret = 0;
-fail:
-	EC_POINT_clear_free(l);
-	EC_POINT_clear_free(sum);
-	EC_KEY_free(bI);
-	EC_KEY_free(BR);
-	EC_KEY_free(PR);
-	BN_clear_free(lx);
-	BN_CTX_free(bnctx);
-	return ret;
-}
-
-
-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_ke(auth, auth->ke, auth->curve->hash_len) < 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) {
-		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 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) {
-		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) {
-		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_ke(auth, auth->ke, auth->curve->hash_len) < 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) {
-		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;
-}
-
-
 static int bin_str_eq(const char *val, size_t len, const char *cmp)
 {
 	return os_strlen(cmp) == len && os_memcmp(val, cmp, len) == 0;
@@ -4747,7 +1190,7 @@
 	wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd);
 
 	pos = os_strstr(cmd, " configurator=");
-	if (pos) {
+	if (!auth->conf && pos) {
 		pos += 14;
 		auth->conf = dpp_configurator_get_id(auth->global, atoi(pos));
 		if (!auth->conf) {
@@ -4781,20 +1224,6 @@
 }
 
 
-static void dpp_free_asymmetric_key(struct dpp_asymmetric_key *key)
-{
-	while (key) {
-		struct dpp_asymmetric_key *next = key->next;
-
-		EVP_PKEY_free(key->csign);
-		str_clear_free(key->config_template);
-		str_clear_free(key->connector_template);
-		os_free(key);
-		key = next;
-	}
-}
-
-
 void dpp_auth_deinit(struct dpp_authentication *auth)
 {
 	unsigned int i;
@@ -4807,16 +1236,21 @@
 	dpp_configuration_free(auth->conf2_sta);
 	EVP_PKEY_free(auth->own_protocol_key);
 	EVP_PKEY_free(auth->peer_protocol_key);
+	EVP_PKEY_free(auth->reconfig_old_protocol_key);
 	wpabuf_free(auth->req_msg);
 	wpabuf_free(auth->resp_msg);
 	wpabuf_free(auth->conf_req);
+	wpabuf_free(auth->reconfig_req_msg);
+	wpabuf_free(auth->reconfig_resp_msg);
 	for (i = 0; i < auth->num_conf_obj; i++) {
 		struct dpp_config_obj *conf = &auth->conf_obj[i];
 
 		os_free(conf->connector);
 		wpabuf_free(conf->c_sign_key);
 	}
+#ifdef CONFIG_DPP2
 	dpp_free_asymmetric_key(auth->conf_key_pkg);
+#endif /* CONFIG_DPP2 */
 	wpabuf_free(auth->net_access_key);
 	dpp_bootstrap_info_free(auth->tmp_own_bi);
 #ifdef CONFIG_TESTING_OPTIONS
@@ -4876,8 +1310,8 @@
 }
 
 
-static int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key,
-			 const char *kid, const struct dpp_curve_params *curve)
+int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key,
+		  const char *kid, const struct dpp_curve_params *curve)
 {
 	struct wpabuf *pub;
 	const u8 *pos;
@@ -4948,20 +1382,10 @@
 		       struct dpp_configuration *conf)
 {
 	struct wpabuf *buf = NULL;
-	char *signed1 = NULL, *signed2 = NULL, *signed3 = NULL;
+	char *signed_conn = NULL;
 	size_t tailroom;
 	const struct dpp_curve_params *curve;
-	struct wpabuf *jws_prot_hdr;
-	size_t signed1_len, signed2_len, signed3_len;
 	struct wpabuf *dppcon = NULL;
-	unsigned char *signature = NULL;
-	const unsigned char *p;
-	size_t signature_len;
-	EVP_MD_CTX *md_ctx = NULL;
-	ECDSA_SIG *sig = NULL;
-	char *dot = ".";
-	const EVP_MD *sign_md;
-	const BIGNUM *r, *s;
 	size_t extra_len = 1000;
 	int incl_legacy;
 	enum dpp_akm akm;
@@ -4973,16 +1397,6 @@
 		goto fail;
 	}
 	curve = auth->conf->curve;
-	if (curve->hash_len == SHA256_MAC_LEN) {
-		sign_md = EVP_sha256();
-	} else if (curve->hash_len == SHA384_MAC_LEN) {
-		sign_md = EVP_sha384();
-	} else if (curve->hash_len == SHA512_MAC_LEN) {
-		sign_md = EVP_sha512();
-	} else {
-		wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm");
-		goto fail;
-	}
 
 	akm = conf->akm;
 	if (dpp_akm_ver2(akm) && auth->peer_version < 2) {
@@ -5030,7 +1444,8 @@
 #ifdef CONFIG_TESTING_OPTIONS
 skip_groups:
 #endif /* CONFIG_TESTING_OPTIONS */
-	if (dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL,
+	if (!auth->peer_protocol_key ||
+	    dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL,
 			  auth->curve) < 0) {
 		wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK");
 		goto fail;
@@ -5055,79 +1470,14 @@
 	wpa_printf(MSG_DEBUG, "DPP: dppCon: %s",
 		   (const char *) wpabuf_head(dppcon));
 
-	jws_prot_hdr = wpabuf_alloc(100);
-	if (!jws_prot_hdr)
-		goto fail;
-	json_start_object(jws_prot_hdr, NULL);
-	json_add_string(jws_prot_hdr, "typ", "dppCon");
-	json_value_sep(jws_prot_hdr);
-	json_add_string(jws_prot_hdr, "kid", auth->conf->kid);
-	json_value_sep(jws_prot_hdr);
-	json_add_string(jws_prot_hdr, "alg", curve->jws_alg);
-	json_end_object(jws_prot_hdr);
-	signed1 = base64_url_encode(wpabuf_head(jws_prot_hdr),
-				    wpabuf_len(jws_prot_hdr),
-				    &signed1_len);
-	wpabuf_free(jws_prot_hdr);
-	signed2 = base64_url_encode(wpabuf_head(dppcon), wpabuf_len(dppcon),
-				    &signed2_len);
-	if (!signed1 || !signed2)
-		goto fail;
-
-	md_ctx = EVP_MD_CTX_create();
-	if (!md_ctx)
-		goto fail;
-
-	ERR_clear_error();
-	if (EVP_DigestSignInit(md_ctx, NULL, sign_md, NULL,
-			       auth->conf->csign) != 1) {
-		wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignInit failed: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
-		goto fail;
-	}
-	if (EVP_DigestSignUpdate(md_ctx, signed1, signed1_len) != 1 ||
-	    EVP_DigestSignUpdate(md_ctx, dot, 1) != 1 ||
-	    EVP_DigestSignUpdate(md_ctx, signed2, signed2_len) != 1) {
-		wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignUpdate failed: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
-		goto fail;
-	}
-	if (EVP_DigestSignFinal(md_ctx, NULL, &signature_len) != 1) {
-		wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
-		goto fail;
-	}
-	signature = os_malloc(signature_len);
-	if (!signature)
-		goto fail;
-	if (EVP_DigestSignFinal(md_ctx, signature, &signature_len) != 1) {
-		wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
-		goto fail;
-	}
-	wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (DER)",
-		    signature, signature_len);
-	/* Convert to raw coordinates r,s */
-	p = signature;
-	sig = d2i_ECDSA_SIG(NULL, &p, signature_len);
-	if (!sig)
-		goto fail;
-	ECDSA_SIG_get0(sig, &r, &s);
-	if (dpp_bn2bin_pad(r, signature, curve->prime_len) < 0 ||
-	    dpp_bn2bin_pad(s, signature + curve->prime_len,
-			   curve->prime_len) < 0)
-		goto fail;
-	signature_len = 2 * curve->prime_len;
-	wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (raw r,s)",
-		    signature, signature_len);
-	signed3 = base64_url_encode(signature, signature_len, &signed3_len);
-	if (!signed3)
+	signed_conn = dpp_sign_connector(auth->conf, dppcon);
+	if (!signed_conn)
 		goto fail;
 
 	incl_legacy = dpp_akm_psk(akm) || dpp_akm_sae(akm);
 	tailroom = 1000;
 	tailroom += 2 * curve->prime_len * 4 / 3 + os_strlen(auth->conf->kid);
-	tailroom += signed1_len + signed2_len + signed3_len;
+	tailroom += os_strlen(signed_conn);
 	if (incl_legacy)
 		tailroom += 1000;
 	buf = dpp_build_conf_start(auth, conf, tailroom);
@@ -5146,11 +1496,7 @@
 		json_value_sep(buf);
 	}
 	wpabuf_put_str(buf, "\"signedConnector\":\"");
-	wpabuf_put_str(buf, signed1);
-	wpabuf_put_u8(buf, '.');
-	wpabuf_put_str(buf, signed2);
-	wpabuf_put_u8(buf, '.');
-	wpabuf_put_str(buf, signed3);
+	wpabuf_put_str(buf, signed_conn);
 	wpabuf_put_str(buf, "\"");
 	json_value_sep(buf);
 	if (dpp_build_jwk(buf, "csign", auth->conf->csign, auth->conf->kid,
@@ -5166,12 +1512,7 @@
 			      wpabuf_head(buf), wpabuf_len(buf));
 
 out:
-	EVP_MD_CTX_destroy(md_ctx);
-	ECDSA_SIG_free(sig);
-	os_free(signed1);
-	os_free(signed2);
-	os_free(signed3);
-	os_free(signature);
+	os_free(signed_conn);
 	wpabuf_free(dppcon);
 	return buf;
 fail:
@@ -5252,488 +1593,6 @@
 }
 
 
-#ifdef CONFIG_DPP2
-
-static struct wpabuf * dpp_build_conf_params(void)
-{
-	struct wpabuf *buf;
-	size_t len;
-	/* TODO: proper template values */
-	const char *conf_template = "{\"wi-fi_tech\":\"infra\",\"discovery\":{\"ssid\":\"test\"},\"cred\":{\"akm\":\"dpp\"}}";
-	const char *connector_template = NULL;
-
-	len = 100 + os_strlen(conf_template);
-	if (connector_template)
-		len += os_strlen(connector_template);
-	buf = wpabuf_alloc(len);
-	if (!buf)
-		return NULL;
-
-	/*
-	 * DPPConfigurationParameters ::= SEQUENCE {
-	 *    configurationTemplate	UTF8String,
-	 *    connectorTemplate		UTF8String OPTIONAL}
-	 */
-
-	asn1_put_utf8string(buf, conf_template);
-	if (connector_template)
-		asn1_put_utf8string(buf, connector_template);
-	return asn1_encaps(buf, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
-}
-
-
-static struct wpabuf * dpp_build_attribute(void)
-{
-	struct wpabuf *conf_params, *attr;
-
-	/*
-	 * aa-DPPConfigurationParameters ATTRIBUTE ::=
-	 * { TYPE DPPConfigurationParameters IDENTIFIED BY id-DPPConfigParams }
-	 *
-	 * Attribute ::= SEQUENCE {
-	 *    type OBJECT IDENTIFIER,
-	 *    values SET SIZE(1..MAX) OF Type
-	 */
-	conf_params = dpp_build_conf_params();
-	conf_params = asn1_encaps(conf_params, ASN1_CLASS_UNIVERSAL,
-				  ASN1_TAG_SET);
-	if (!conf_params)
-		return NULL;
-
-	attr = wpabuf_alloc(100 + wpabuf_len(conf_params));
-	if (!attr) {
-		wpabuf_clear_free(conf_params);
-		return NULL;
-	}
-
-	asn1_put_oid(attr, &asn1_dpp_config_params_oid);
-	wpabuf_put_buf(attr, conf_params);
-	wpabuf_clear_free(conf_params);
-
-	return asn1_encaps(attr, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
-}
-
-
-static struct wpabuf * dpp_build_key_alg(const struct dpp_curve_params *curve)
-{
-	const struct asn1_oid *oid;
-	struct wpabuf *params, *res;
-
-	switch (curve->ike_group) {
-	case 19:
-		oid = &asn1_prime256v1_oid;
-		break;
-	case 20:
-		oid = &asn1_secp384r1_oid;
-		break;
-	case 21:
-		oid = &asn1_secp521r1_oid;
-		break;
-	case 28:
-		oid = &asn1_brainpoolP256r1_oid;
-		break;
-	case 29:
-		oid = &asn1_brainpoolP384r1_oid;
-		break;
-	case 30:
-		oid = &asn1_brainpoolP512r1_oid;
-		break;
-	default:
-		return NULL;
-	}
-
-	params = wpabuf_alloc(20);
-	if (!params)
-		return NULL;
-	asn1_put_oid(params, oid); /* namedCurve */
-
-	res = asn1_build_alg_id(&asn1_ec_public_key_oid, params);
-	wpabuf_free(params);
-	return res;
-}
-
-
-static struct wpabuf * dpp_build_key_pkg(struct dpp_authentication *auth)
-{
-	struct wpabuf *key = NULL, *attr, *alg, *priv_key = NULL;
-	EC_KEY *eckey;
-	unsigned char *der = NULL;
-	int der_len;
-
-	eckey = EVP_PKEY_get0_EC_KEY(auth->conf->csign);
-	if (!eckey)
-		return NULL;
-
-	EC_KEY_set_enc_flags(eckey, EC_PKEY_NO_PUBKEY);
-	der_len = i2d_ECPrivateKey(eckey, &der);
-	if (der_len > 0)
-		priv_key = wpabuf_alloc_copy(der, der_len);
-	OPENSSL_free(der);
-
-	alg = dpp_build_key_alg(auth->conf->curve);
-
-	/* Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } } */
-	attr = dpp_build_attribute();
-	attr = asn1_encaps(attr, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SET);
-	if (!priv_key || !attr || !alg)
-		goto fail;
-
-	/*
-	 * OneAsymmetricKey ::= SEQUENCE {
-	 *    version			Version,
-	 *    privateKeyAlgorithm	PrivateKeyAlgorithmIdentifier,
-	 *    privateKey		PrivateKey,
-	 *    attributes		[0] Attributes OPTIONAL,
-	 *    ...,
-	 *    [[2: publicKey		[1] BIT STRING OPTIONAL ]],
-	 *    ...
-	 * }
-	 */
-
-	key = wpabuf_alloc(100 + wpabuf_len(alg) + wpabuf_len(priv_key) +
-			   wpabuf_len(attr));
-	if (!key)
-		goto fail;
-
-	asn1_put_integer(key, 1); /* version = v2(1) */
-
-	/* PrivateKeyAlgorithmIdentifier */
-	wpabuf_put_buf(key, alg);
-
-	/* PrivateKey ::= OCTET STRING */
-	asn1_put_octet_string(key, priv_key);
-
-	/* [0] Attributes OPTIONAL */
-	asn1_put_hdr(key, ASN1_CLASS_CONTEXT_SPECIFIC, 1, 0, wpabuf_len(attr));
-	wpabuf_put_buf(key, attr);
-
-fail:
-	wpabuf_clear_free(attr);
-	wpabuf_clear_free(priv_key);
-	wpabuf_free(alg);
-
-	/*
-	 * DPPAsymmetricKeyPackage ::= AsymmetricKeyPackage
-	 *
-	 * AsymmetricKeyPackage ::= SEQUENCE SIZE (1..MAX) OF OneAsymmetricKey
-	 *
-	 * OneAsymmetricKey ::= SEQUENCE
-	 */
-	return asn1_encaps(asn1_encaps(key,
-				       ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE),
-			   ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
-}
-
-
-static struct wpabuf * dpp_build_pbkdf2_alg_id(const struct wpabuf *salt,
-					       size_t hash_len)
-{
-	struct wpabuf *params = NULL, *buf = NULL, *prf = NULL;
-	const struct asn1_oid *oid;
-
-	/*
-	 * PBKDF2-params ::= SEQUENCE {
-	 *    salt CHOICE {
-	 *       specified OCTET STRING,
-	 *       otherSource AlgorithmIdentifier}
-	 *    iterationCount INTEGER (1..MAX),
-	 *    keyLength INTEGER (1..MAX),
-	 *    prf AlgorithmIdentifier}
-	 *
-	 * salt is an 64 octet value, iterationCount is 1000, keyLength is based
-	 * on Configurator signing key length, prf is
-	 * id-hmacWithSHA{256,384,512} based on Configurator signing key.
-	 */
-
-	if (hash_len == 32)
-		oid = &asn1_pbkdf2_hmac_sha256_oid;
-	else if (hash_len == 48)
-		oid = &asn1_pbkdf2_hmac_sha384_oid;
-	else if (hash_len == 64)
-		oid = &asn1_pbkdf2_hmac_sha512_oid;
-	else
-		goto fail;
-	prf = asn1_build_alg_id(oid, NULL);
-	if (!prf)
-		goto fail;
-	params = wpabuf_alloc(100 + wpabuf_len(salt) + wpabuf_len(prf));
-	if (!params)
-		goto fail;
-	asn1_put_octet_string(params, salt); /* salt.specified */
-	asn1_put_integer(params, 1000); /* iterationCount */
-	asn1_put_integer(params, hash_len); /* keyLength */
-	wpabuf_put_buf(params, prf);
-	params = asn1_encaps(params, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
-	if (!params)
-		goto fail;
-	buf = asn1_build_alg_id(&asn1_pbkdf2_oid, params);
-fail:
-	wpabuf_free(params);
-	wpabuf_free(prf);
-	return buf;
-}
-
-
-static struct wpabuf *
-dpp_build_pw_recipient_info(struct dpp_authentication *auth, size_t hash_len,
-			    const struct wpabuf *cont_enc_key)
-{
-	struct wpabuf *pwri = NULL, *enc_key = NULL, *key_der_alg = NULL,
-		*key_enc_alg = NULL, *salt;
-	u8 kek[DPP_MAX_HASH_LEN];
-	const u8 *key;
-	size_t key_len;
-
-	salt = wpabuf_alloc(64);
-	if (!salt || os_get_random(wpabuf_put(salt, 64), 64) < 0)
-		goto fail;
-	wpa_hexdump_buf(MSG_DEBUG, "DPP: PBKDF2 salt", salt);
-
-	/* TODO: For initial testing, use ke as the key. Replace this with a
-	 * new key once that has been defined. */
-	key = auth->ke;
-	key_len = auth->curve->hash_len;
-	wpa_hexdump_key(MSG_DEBUG, "DPP: PBKDF2 key", key, key_len);
-
-	if (dpp_pbkdf2(hash_len, key, key_len, wpabuf_head(salt), 64, 1000,
-		       kek, hash_len)) {
-		wpa_printf(MSG_DEBUG, "DPP: PBKDF2 failed");
-		goto fail;
-	}
-	wpa_hexdump_key(MSG_DEBUG, "DPP: key-encryption key from PBKDF2",
-			kek, hash_len);
-
-	enc_key = wpabuf_alloc(hash_len + AES_BLOCK_SIZE);
-	if (!enc_key ||
-	    aes_siv_encrypt(kek, hash_len, wpabuf_head(cont_enc_key),
-			    wpabuf_len(cont_enc_key), 0, NULL, NULL,
-			    wpabuf_put(enc_key, hash_len + AES_BLOCK_SIZE)) < 0)
-		goto fail;
-	wpa_hexdump_buf(MSG_DEBUG, "DPP: encryptedKey", enc_key);
-
-	/*
-	 * PasswordRecipientInfo ::= SEQUENCE {
-	 *    version			CMSVersion,
-	 *    keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier OPTIONAL,
-	 *    keyEncryptionAlgorithm	KeyEncryptionAlgorithmIdentifier,
-	 *    encryptedKey		EncryptedKey}
-	 *
-	 * version is 0, keyDerivationAlgorithm is id-PKBDF2, and the
-	 * parameters contains PBKDF2-params SEQUENCE.
-	 */
-
-	key_der_alg = dpp_build_pbkdf2_alg_id(salt, hash_len);
-	key_enc_alg = asn1_build_alg_id(&asn1_aes_siv_cmac_aead_256_oid, NULL);
-	if (!key_der_alg || !key_enc_alg)
-		goto fail;
-	pwri = wpabuf_alloc(100 + wpabuf_len(key_der_alg) +
-			    wpabuf_len(key_enc_alg) + wpabuf_len(enc_key));
-	if (!pwri)
-		goto fail;
-
-	/* version = 0 */
-	asn1_put_integer(pwri, 0);
-
-	/* [0] KeyDerivationAlgorithmIdentifier */
-	asn1_put_hdr(pwri, ASN1_CLASS_CONTEXT_SPECIFIC, 1, 0,
-		     wpabuf_len(key_der_alg));
-	wpabuf_put_buf(pwri, key_der_alg);
-
-	/* KeyEncryptionAlgorithmIdentifier */
-	wpabuf_put_buf(pwri, key_enc_alg);
-
-	/* EncryptedKey ::= OCTET STRING */
-	asn1_put_octet_string(pwri, enc_key);
-
-fail:
-	wpabuf_clear_free(key_der_alg);
-	wpabuf_free(key_enc_alg);
-	wpabuf_free(enc_key);
-	wpabuf_free(salt);
-	forced_memzero(kek, sizeof(kek));
-	return asn1_encaps(pwri, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
-}
-
-
-static struct wpabuf *
-dpp_build_recipient_info(struct dpp_authentication *auth, size_t hash_len,
-			 const struct wpabuf *cont_enc_key)
-{
-	struct wpabuf *pwri;
-
-	/*
-	 * RecipientInfo ::= CHOICE {
-	 *    ktri		KeyTransRecipientInfo,
-	 *    kari	[1]	KeyAgreeRecipientInfo,
-	 *    kekri	[2]	KEKRecipientInfo,
-	 *    pwri	[3]	PasswordRecipientInfo,
-	 *    ori	[4]	OtherRecipientInfo}
-	 *
-	 * Shall always use the pwri CHOICE.
-	 */
-
-	pwri = dpp_build_pw_recipient_info(auth, hash_len, cont_enc_key);
-	return asn1_encaps(pwri, ASN1_CLASS_CONTEXT_SPECIFIC, 3);
-}
-
-
-static struct wpabuf *
-dpp_build_enc_cont_info(struct dpp_authentication *auth, size_t hash_len,
-			const struct wpabuf *cont_enc_key)
-{
-	struct wpabuf *key_pkg, *enc_cont_info = NULL, *enc_cont = NULL,
-		*enc_alg;
-	const struct asn1_oid *oid;
-	size_t enc_cont_len;
-
-	/*
-	 * EncryptedContentInfo ::= SEQUENCE {
-	 *    contentType			ContentType,
-	 *    contentEncryptionAlgorithm  ContentEncryptionAlgorithmIdentifier,
-	 *    encryptedContent	[0] IMPLICIT	EncryptedContent OPTIONAL}
-	 */
-
-	if (hash_len == 32)
-		oid = &asn1_aes_siv_cmac_aead_256_oid;
-	else if (hash_len == 48)
-		oid = &asn1_aes_siv_cmac_aead_384_oid;
-	else if (hash_len == 64)
-		oid = &asn1_aes_siv_cmac_aead_512_oid;
-	else
-		return NULL;
-
-	key_pkg = dpp_build_key_pkg(auth);
-	enc_alg = asn1_build_alg_id(oid, NULL);
-	if (!key_pkg || !enc_alg)
-		goto fail;
-
-	wpa_hexdump_buf_key(MSG_MSGDUMP, "DPP: DPPAsymmetricKeyPackage",
-			    key_pkg);
-
-	enc_cont_len = wpabuf_len(key_pkg) + AES_BLOCK_SIZE;
-	enc_cont = wpabuf_alloc(enc_cont_len);
-	if (!enc_cont ||
-	    aes_siv_encrypt(wpabuf_head(cont_enc_key), wpabuf_len(cont_enc_key),
-			    wpabuf_head(key_pkg), wpabuf_len(key_pkg),
-			    0, NULL, NULL,
-			    wpabuf_put(enc_cont, enc_cont_len)) < 0)
-		goto fail;
-
-	enc_cont_info = wpabuf_alloc(100 + wpabuf_len(enc_alg) +
-				     wpabuf_len(enc_cont));
-	if (!enc_cont_info)
-		goto fail;
-
-	/* ContentType ::= OBJECT IDENTIFIER */
-	asn1_put_oid(enc_cont_info, &asn1_dpp_asymmetric_key_package_oid);
-
-	/* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */
-	wpabuf_put_buf(enc_cont_info, enc_alg);
-
-	/* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
-	 * EncryptedContent ::= OCTET STRING */
-	asn1_put_hdr(enc_cont_info, ASN1_CLASS_CONTEXT_SPECIFIC, 0, 0,
-		     wpabuf_len(enc_cont));
-	wpabuf_put_buf(enc_cont_info, enc_cont);
-
-fail:
-	wpabuf_clear_free(key_pkg);
-	wpabuf_free(enc_cont);
-	wpabuf_free(enc_alg);
-	return enc_cont_info;
-}
-
-
-static struct wpabuf * dpp_gen_random(size_t len)
-{
-	struct wpabuf *key;
-
-	key = wpabuf_alloc(len);
-	if (!key || os_get_random(wpabuf_put(key, len), len) < 0) {
-		wpabuf_free(key);
-		key = NULL;
-	}
-	wpa_hexdump_buf_key(MSG_DEBUG, "DPP: content-encryption key", key);
-	return key;
-}
-
-
-static struct wpabuf * dpp_build_enveloped_data(struct dpp_authentication *auth)
-{
-	struct wpabuf *env = NULL;
-	struct wpabuf *recipient_info = NULL, *enc_cont_info = NULL;
-	struct wpabuf *cont_enc_key = NULL;
-	size_t hash_len;
-
-	if (!auth->conf) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: No Configurator instance selected for the session - cannot build DPPEnvelopedData");
-		return NULL;
-	}
-
-	if (!auth->provision_configurator) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Configurator provisioning not allowed");
-		return NULL;
-	}
-
-	wpa_printf(MSG_DEBUG, "DPP: Building DPPEnvelopedData");
-
-	hash_len = auth->conf->curve->hash_len;
-	cont_enc_key = dpp_gen_random(hash_len);
-	if (!cont_enc_key)
-		goto fail;
-	recipient_info = dpp_build_recipient_info(auth, hash_len, cont_enc_key);
-	enc_cont_info = dpp_build_enc_cont_info(auth, hash_len, cont_enc_key);
-	if (!recipient_info || !enc_cont_info)
-		goto fail;
-
-	env = wpabuf_alloc(wpabuf_len(recipient_info) +
-			   wpabuf_len(enc_cont_info) +
-			   100);
-	if (!env)
-		goto fail;
-
-	/*
-	 * DPPEnvelopedData ::= EnvelopedData
-	 *
-	 * EnvelopedData ::= SEQUENCE {
-	 *    version			CMSVersion,
-	 *    originatorInfo	[0]	IMPLICIT OriginatorInfo OPTIONAL,
-	 *    recipientInfos		RecipientInfos,
-	 *    encryptedContentInfo	EncryptedContentInfo,
-	 *    unprotectedAttrs  [1] IMPLICIT	UnprotectedAttributes OPTIONAL}
-	 *
-	 * For DPP, version is 3, both originatorInfo and
-	 * unprotectedAttrs are omitted, and recipientInfos contains a single
-	 * RecipientInfo.
-	 */
-
-	/* EnvelopedData.version = 3 */
-	asn1_put_integer(env, 3);
-
-	/* RecipientInfos ::= SET SIZE (1..MAX) OF RecipientInfo */
-	asn1_put_set(env, recipient_info);
-
-	/* EncryptedContentInfo ::= SEQUENCE */
-	asn1_put_sequence(env, enc_cont_info);
-
-	env = asn1_encaps(env, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
-	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: DPPEnvelopedData", env);
-out:
-	wpabuf_clear_free(cont_enc_key);
-	wpabuf_clear_free(recipient_info);
-	wpabuf_free(enc_cont_info);
-	return env;
-fail:
-	wpabuf_free(env);
-	env = NULL;
-	goto out;
-}
-
-#endif /* CONFIG_DPP2 */
-
-
 static struct wpabuf *
 dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
 		    u16 e_nonce_len, enum dpp_netrole netrole)
@@ -6022,12 +1881,19 @@
 	}
 
 	token = json_get_member(root, "mudurl");
-	if (token && token->type == JSON_STRING)
+	if (token && token->type == JSON_STRING) {
 		wpa_printf(MSG_DEBUG, "DPP: mudurl = '%s'", token->string);
+		wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_MUD_URL "%s",
+			token->string);
+	}
 
 	token = json_get_member(root, "bandSupport");
 	auth->band_list_size = 0;
 	if (token && token->type == JSON_ARRAY) {
+		int *opclass = NULL;
+		char txt[200], *pos, *end;
+		int i, res;
+
 		memset(auth->band_list, 0, sizeof(auth->band_list));
 		wpa_printf(MSG_DEBUG, "DPP: bandSupport");
 		token = token->child;
@@ -6042,9 +1908,26 @@
 				wpa_printf(MSG_DEBUG,
 					   "DPP: Supported global operating class: %d",
 					   token->number);
+				int_array_add_unique(&opclass, token->number);
 			}
 			token = token->sibling;
 		}
+
+		txt[0] = '\0';
+		pos = txt;
+		end = txt + sizeof(txt);
+		for (i = 0; opclass && opclass[i]; i++) {
+			res = os_snprintf(pos, end - pos, "%s%d",
+					  pos == txt ? "" : ",", opclass[i]);
+			if (os_snprintf_error(end - pos, res)) {
+				*pos = '\0';
+				break;
+			}
+			pos += res;
+		}
+		os_free(opclass);
+		wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_BAND_SUPPORT "%s",
+			txt);
 	}
 
 	resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, netrole);
@@ -6056,86 +1939,6 @@
 }
 
 
-static struct wpabuf *
-dpp_parse_jws_prot_hdr(const struct dpp_curve_params *curve,
-		       const u8 *prot_hdr, u16 prot_hdr_len,
-		       const EVP_MD **ret_md)
-{
-	struct json_token *root, *token;
-	struct wpabuf *kid = NULL;
-
-	root = json_parse((const char *) prot_hdr, prot_hdr_len);
-	if (!root) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: JSON parsing failed for JWS Protected Header");
-		goto fail;
-	}
-
-	if (root->type != JSON_OBJECT) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: JWS Protected Header root is not an object");
-		goto fail;
-	}
-
-	token = json_get_member(root, "typ");
-	if (!token || token->type != JSON_STRING) {
-		wpa_printf(MSG_DEBUG, "DPP: No typ string value found");
-		goto fail;
-	}
-	wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header typ=%s",
-		   token->string);
-	if (os_strcmp(token->string, "dppCon") != 0) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Unsupported JWS Protected Header typ=%s",
-			   token->string);
-		goto fail;
-	}
-
-	token = json_get_member(root, "alg");
-	if (!token || token->type != JSON_STRING) {
-		wpa_printf(MSG_DEBUG, "DPP: No alg string value found");
-		goto fail;
-	}
-	wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header alg=%s",
-		   token->string);
-	if (os_strcmp(token->string, curve->jws_alg) != 0) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Unexpected JWS Protected Header alg=%s (expected %s based on C-sign-key)",
-			   token->string, curve->jws_alg);
-		goto fail;
-	}
-	if (os_strcmp(token->string, "ES256") == 0 ||
-	    os_strcmp(token->string, "BS256") == 0)
-		*ret_md = EVP_sha256();
-	else if (os_strcmp(token->string, "ES384") == 0 ||
-		 os_strcmp(token->string, "BS384") == 0)
-		*ret_md = EVP_sha384();
-	else if (os_strcmp(token->string, "ES512") == 0 ||
-		 os_strcmp(token->string, "BS512") == 0)
-		*ret_md = EVP_sha512();
-	else
-		*ret_md = NULL;
-	if (!*ret_md) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Unsupported JWS Protected Header alg=%s",
-			   token->string);
-		goto fail;
-	}
-
-	kid = json_get_member_base64url(root, "kid");
-	if (!kid) {
-		wpa_printf(MSG_DEBUG, "DPP: No kid string value found");
-		goto fail;
-	}
-	wpa_hexdump_buf(MSG_DEBUG, "DPP: JWS Protected Header kid (decoded)",
-			kid);
-
-fail:
-	json_free(root);
-	return kid;
-}
-
-
 static int dpp_parse_cred_legacy(struct dpp_config_obj *conf,
 				 struct json_token *cred)
 {
@@ -6183,8 +1986,8 @@
 }
 
 
-static EVP_PKEY * dpp_parse_jwk(struct json_token *jwk,
-				const struct dpp_curve_params **key_curve)
+EVP_PKEY * dpp_parse_jwk(struct json_token *jwk,
+			 const struct dpp_curve_params **key_curve)
 {
 	struct json_token *token;
 	const struct dpp_curve_params *curve;
@@ -6438,38 +2241,6 @@
 }
 
 
-static int dpp_check_pubkey_match(EVP_PKEY *pub, struct wpabuf *r_hash)
-{
-	struct wpabuf *uncomp;
-	int res;
-	u8 hash[SHA256_MAC_LEN];
-	const u8 *addr[1];
-	size_t len[1];
-
-	if (wpabuf_len(r_hash) != SHA256_MAC_LEN)
-		return -1;
-	uncomp = dpp_get_pubkey_point(pub, 1);
-	if (!uncomp)
-		return -1;
-	addr[0] = wpabuf_head(uncomp);
-	len[0] = wpabuf_len(uncomp);
-	wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed public key",
-		    addr[0], len[0]);
-	res = sha256_vector(1, addr, len, hash);
-	wpabuf_free(uncomp);
-	if (res < 0)
-		return -1;
-	if (os_memcmp(hash, wpabuf_head(r_hash), SHA256_MAC_LEN) != 0) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Received hash value does not match calculated public key hash value");
-		wpa_hexdump(MSG_DEBUG, "DPP: Calculated hash",
-			    hash, SHA256_MAC_LEN);
-		return -1;
-	}
-	return 0;
-}
-
-
 static void dpp_copy_csign(struct dpp_config_obj *conf, EVP_PKEY *csign)
 {
 	unsigned char *der = NULL;
@@ -6490,8 +2261,15 @@
 	unsigned char *der = NULL;
 	int der_len;
 	EC_KEY *eckey;
+	EVP_PKEY *own_key;
 
-	eckey = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key);
+	own_key = auth->own_protocol_key;
+#ifdef CONFIG_DPP2
+	if (auth->reconfig_connector_key == DPP_CONFIG_REUSEKEY &&
+	    auth->reconfig_old_protocol_key)
+		own_key = auth->reconfig_old_protocol_key;
+#endif /* CONFIG_DPP2 */
+	eckey = EVP_PKEY_get1_EC_KEY(own_key);
 	if (!eckey)
 		return;
 
@@ -6507,174 +2285,6 @@
 }
 
 
-struct dpp_signed_connector_info {
-	unsigned char *payload;
-	size_t payload_len;
-};
-
-static enum dpp_status_error
-dpp_process_signed_connector(struct dpp_signed_connector_info *info,
-			     EVP_PKEY *csign_pub, const char *connector)
-{
-	enum dpp_status_error ret = 255;
-	const char *pos, *end, *signed_start, *signed_end;
-	struct wpabuf *kid = NULL;
-	unsigned char *prot_hdr = NULL, *signature = NULL;
-	size_t prot_hdr_len = 0, signature_len = 0;
-	const EVP_MD *sign_md = NULL;
-	unsigned char *der = NULL;
-	int der_len;
-	int res;
-	EVP_MD_CTX *md_ctx = NULL;
-	ECDSA_SIG *sig = NULL;
-	BIGNUM *r = NULL, *s = NULL;
-	const struct dpp_curve_params *curve;
-	EC_KEY *eckey;
-	const EC_GROUP *group;
-	int nid;
-
-	eckey = EVP_PKEY_get1_EC_KEY(csign_pub);
-	if (!eckey)
-		goto fail;
-	group = EC_KEY_get0_group(eckey);
-	if (!group)
-		goto fail;
-	nid = EC_GROUP_get_curve_name(group);
-	curve = dpp_get_curve_nid(nid);
-	if (!curve)
-		goto fail;
-	wpa_printf(MSG_DEBUG, "DPP: C-sign-key group: %s", curve->jwk_crv);
-	os_memset(info, 0, sizeof(*info));
-
-	signed_start = pos = connector;
-	end = os_strchr(pos, '.');
-	if (!end) {
-		wpa_printf(MSG_DEBUG, "DPP: Missing dot(1) in signedConnector");
-		ret = DPP_STATUS_INVALID_CONNECTOR;
-		goto fail;
-	}
-	prot_hdr = base64_url_decode(pos, end - pos, &prot_hdr_len);
-	if (!prot_hdr) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Failed to base64url decode signedConnector JWS Protected Header");
-		ret = DPP_STATUS_INVALID_CONNECTOR;
-		goto fail;
-	}
-	wpa_hexdump_ascii(MSG_DEBUG,
-			  "DPP: signedConnector - JWS Protected Header",
-			  prot_hdr, prot_hdr_len);
-	kid = dpp_parse_jws_prot_hdr(curve, prot_hdr, prot_hdr_len, &sign_md);
-	if (!kid) {
-		ret = DPP_STATUS_INVALID_CONNECTOR;
-		goto fail;
-	}
-	if (wpabuf_len(kid) != SHA256_MAC_LEN) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Unexpected signedConnector JWS Protected Header kid length: %u (expected %u)",
-			   (unsigned int) wpabuf_len(kid), SHA256_MAC_LEN);
-		ret = DPP_STATUS_INVALID_CONNECTOR;
-		goto fail;
-	}
-
-	pos = end + 1;
-	end = os_strchr(pos, '.');
-	if (!end) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Missing dot(2) in signedConnector");
-		ret = DPP_STATUS_INVALID_CONNECTOR;
-		goto fail;
-	}
-	signed_end = end - 1;
-	info->payload = base64_url_decode(pos, end - pos, &info->payload_len);
-	if (!info->payload) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Failed to base64url decode signedConnector JWS Payload");
-		ret = DPP_STATUS_INVALID_CONNECTOR;
-		goto fail;
-	}
-	wpa_hexdump_ascii(MSG_DEBUG,
-			  "DPP: signedConnector - JWS Payload",
-			  info->payload, info->payload_len);
-	pos = end + 1;
-	signature = base64_url_decode(pos, os_strlen(pos), &signature_len);
-	if (!signature) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Failed to base64url decode signedConnector signature");
-		ret = DPP_STATUS_INVALID_CONNECTOR;
-		goto fail;
-		}
-	wpa_hexdump(MSG_DEBUG, "DPP: signedConnector - signature",
-		    signature, signature_len);
-
-	if (dpp_check_pubkey_match(csign_pub, kid) < 0) {
-		ret = DPP_STATUS_NO_MATCH;
-		goto fail;
-	}
-
-	if (signature_len & 0x01) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Unexpected signedConnector signature length (%d)",
-			   (int) signature_len);
-		ret = DPP_STATUS_INVALID_CONNECTOR;
-		goto fail;
-	}
-
-	/* JWS Signature encodes the signature (r,s) as two octet strings. Need
-	 * to convert that to DER encoded ECDSA_SIG for OpenSSL EVP routines. */
-	r = BN_bin2bn(signature, signature_len / 2, NULL);
-	s = BN_bin2bn(signature + signature_len / 2, signature_len / 2, NULL);
-	sig = ECDSA_SIG_new();
-	if (!r || !s || !sig || ECDSA_SIG_set0(sig, r, s) != 1)
-		goto fail;
-	r = NULL;
-	s = NULL;
-
-	der_len = i2d_ECDSA_SIG(sig, &der);
-	if (der_len <= 0) {
-		wpa_printf(MSG_DEBUG, "DPP: Could not DER encode signature");
-		goto fail;
-	}
-	wpa_hexdump(MSG_DEBUG, "DPP: DER encoded signature", der, der_len);
-	md_ctx = EVP_MD_CTX_create();
-	if (!md_ctx)
-		goto fail;
-
-	ERR_clear_error();
-	if (EVP_DigestVerifyInit(md_ctx, NULL, sign_md, NULL, csign_pub) != 1) {
-		wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyInit failed: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
-		goto fail;
-	}
-	if (EVP_DigestVerifyUpdate(md_ctx, signed_start,
-				   signed_end - signed_start + 1) != 1) {
-		wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyUpdate failed: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
-		goto fail;
-	}
-	res = EVP_DigestVerifyFinal(md_ctx, der, der_len);
-	if (res != 1) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: EVP_DigestVerifyFinal failed (res=%d): %s",
-			   res, ERR_error_string(ERR_get_error(), NULL));
-		ret = DPP_STATUS_INVALID_CONNECTOR;
-		goto fail;
-	}
-
-	ret = DPP_STATUS_OK;
-fail:
-	EC_KEY_free(eckey);
-	EVP_MD_CTX_destroy(md_ctx);
-	os_free(prot_hdr);
-	wpabuf_free(kid);
-	os_free(signature);
-	ECDSA_SIG_free(sig);
-	BN_free(r);
-	BN_free(s);
-	OPENSSL_free(der);
-	return ret;
-}
-
-
 static int dpp_parse_cred_dpp(struct dpp_authentication *auth,
 			      struct dpp_config_obj *conf,
 			      struct json_token *cred)
@@ -6974,709 +2584,6 @@
 }
 
 
-#ifdef CONFIG_DPP2
-
-struct dpp_enveloped_data {
-	const u8 *enc_cont;
-	size_t enc_cont_len;
-	const u8 *enc_key;
-	size_t enc_key_len;
-	const u8 *salt;
-	size_t pbkdf2_key_len;
-	size_t prf_hash_len;
-};
-
-
-static int dpp_parse_recipient_infos(const u8 *pos, size_t len,
-				     struct dpp_enveloped_data *data)
-{
-	struct asn1_hdr hdr;
-	const u8 *end = pos + len;
-	const u8 *next, *e_end;
-	struct asn1_oid oid;
-	int val;
-	const u8 *params;
-	size_t params_len;
-
-	wpa_hexdump(MSG_MSGDUMP, "DPP: RecipientInfos", pos, len);
-
-	/*
-	 * RecipientInfo ::= CHOICE {
-	 *    ktri		KeyTransRecipientInfo,
-	 *    kari	[1]	KeyAgreeRecipientInfo,
-	 *    kekri	[2]	KEKRecipientInfo,
-	 *    pwri	[3]	PasswordRecipientInfo,
-	 *    ori	[4]	OtherRecipientInfo}
-	 *
-	 * Shall always use the pwri CHOICE.
-	 */
-
-	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
-	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 3) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Expected CHOICE [3] (pwri) - found class %d tag 0x%x",
-			   hdr.class, hdr.tag);
-		return -1;
-	}
-	wpa_hexdump(MSG_MSGDUMP, "DPP: PasswordRecipientInfo",
-		    hdr.payload, hdr.length);
-	pos = hdr.payload;
-	end = pos + hdr.length;
-
-	/*
-	 * PasswordRecipientInfo ::= SEQUENCE {
-	 *    version			CMSVersion,
-	 *    keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier OPTIONAL,
-	 *    keyEncryptionAlgorithm	KeyEncryptionAlgorithmIdentifier,
-	 *    encryptedKey		EncryptedKey}
-	 *
-	 * version is 0, keyDerivationAlgorithm is id-PKBDF2, and the
-	 * parameters contains PBKDF2-params SEQUENCE.
-	 */
-
-	if (asn1_get_sequence(pos, end - pos, &hdr, &end) < 0)
-		return -1;
-	pos = hdr.payload;
-
-	if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
-		return -1;
-	if (val != 0) {
-		wpa_printf(MSG_DEBUG, "DPP: pwri.version != 0");
-		return -1;
-	}
-
-	wpa_hexdump(MSG_MSGDUMP, "DPP: Remaining PasswordRecipientInfo after version",
-		    pos, end - pos);
-
-	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
-	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 0) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Expected keyDerivationAlgorithm [0] - found class %d tag 0x%x",
-			   hdr.class, hdr.tag);
-		return -1;
-	}
-	pos = hdr.payload;
-	e_end = pos + hdr.length;
-
-	/* KeyDerivationAlgorithmIdentifier ::= AlgorithmIdentifier */
-	if (asn1_get_alg_id(pos, e_end - pos, &oid, &params, &params_len,
-			    &next) < 0)
-		return -1;
-	if (!asn1_oid_equal(&oid, &asn1_pbkdf2_oid)) {
-		char buf[80];
-
-		asn1_oid_to_str(&oid, buf, sizeof(buf));
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Unexpected KeyDerivationAlgorithmIdentifier %s",
-			   buf);
-		return -1;
-	}
-
-	/*
-	 * PBKDF2-params ::= SEQUENCE {
-	 *    salt CHOICE {
-	 *       specified OCTET STRING,
-	 *       otherSource AlgorithmIdentifier}
-	 *    iterationCount INTEGER (1..MAX),
-	 *    keyLength INTEGER (1..MAX),
-	 *    prf AlgorithmIdentifier}
-	 *
-	 * salt is an 64 octet value, iterationCount is 1000, keyLength is based
-	 * on Configurator signing key length, prf is
-	 * id-hmacWithSHA{256,384,512} based on Configurator signing key.
-	 */
-	if (!params ||
-	    asn1_get_sequence(params, params_len, &hdr, &e_end) < 0)
-		return -1;
-	pos = hdr.payload;
-
-	if (asn1_get_next(pos, e_end - pos, &hdr) < 0 ||
-	    hdr.class != ASN1_CLASS_UNIVERSAL ||
-	    hdr.tag != ASN1_TAG_OCTETSTRING) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Expected OCTETSTRING (salt.specified) - found class %d tag 0x%x",
-			   hdr.class, hdr.tag);
-		return -1;
-	}
-	wpa_hexdump(MSG_MSGDUMP, "DPP: salt.specified",
-		    hdr.payload, hdr.length);
-	if (hdr.length != 64) {
-		wpa_printf(MSG_DEBUG, "DPP: Unexpected salt length %u",
-			   hdr.length);
-		return -1;
-	}
-	data->salt = hdr.payload;
-	pos = hdr.payload + hdr.length;
-
-	if (asn1_get_integer(pos, e_end - pos, &val, &pos) < 0)
-		return -1;
-	if (val != 1000) {
-		wpa_printf(MSG_DEBUG, "DPP: Unexpected iterationCount %d", val);
-		return -1;
-	}
-
-	if (asn1_get_integer(pos, e_end - pos, &val, &pos) < 0)
-		return -1;
-	if (val != 32 && val != 48 && val != 64) {
-		wpa_printf(MSG_DEBUG, "DPP: Unexpected keyLength %d", val);
-		return -1;
-	}
-	data->pbkdf2_key_len = val;
-
-	if (asn1_get_sequence(pos, e_end - pos, &hdr, NULL) < 0 ||
-	    asn1_get_oid(hdr.payload, hdr.length, &oid, &pos) < 0) {
-		wpa_printf(MSG_DEBUG, "DPP: Could not parse prf");
-		return -1;
-	}
-	if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha256_oid)) {
-		data->prf_hash_len = 32;
-	} else if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha384_oid)) {
-		data->prf_hash_len = 48;
-	} else if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha512_oid)) {
-		data->prf_hash_len = 64;
-	} else {
-		char buf[80];
-
-		asn1_oid_to_str(&oid, buf, sizeof(buf));
-		wpa_printf(MSG_DEBUG, "DPP: Unexpected PBKDF2-params.prf %s",
-			   buf);
-		return -1;
-	}
-
-	pos = next;
-
-	/* keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier
-	 *
-	 * KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
-	 *
-	 * id-alg-AES-SIV-CMAC-aed-256, id-alg-AES-SIV-CMAC-aed-384, or
-	 * id-alg-AES-SIV-CMAC-aed-512. */
-	if (asn1_get_alg_id(pos, end - pos, &oid, NULL, NULL, &pos) < 0)
-		return -1;
-	if (!asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_256_oid) &&
-	    !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_384_oid) &&
-	    !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_512_oid)) {
-		char buf[80];
-
-		asn1_oid_to_str(&oid, buf, sizeof(buf));
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Unexpected KeyEncryptionAlgorithmIdentifier %s",
-			   buf);
-		return -1;
-	}
-
-	/*
-	 * encryptedKey EncryptedKey
-	 *
-	 * EncryptedKey ::= OCTET STRING
-	 */
-	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
-	    hdr.class != ASN1_CLASS_UNIVERSAL ||
-	    hdr.tag != ASN1_TAG_OCTETSTRING) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Expected OCTETSTRING (pwri.encryptedKey) - found class %d tag 0x%x",
-			   hdr.class, hdr.tag);
-		return -1;
-	}
-	wpa_hexdump(MSG_MSGDUMP, "DPP: pwri.encryptedKey",
-		    hdr.payload, hdr.length);
-	data->enc_key = hdr.payload;
-	data->enc_key_len = hdr.length;
-
-	return 0;
-}
-
-
-static int dpp_parse_encrypted_content_info(const u8 *pos, const u8 *end,
-					    struct dpp_enveloped_data *data)
-{
-	struct asn1_hdr hdr;
-	struct asn1_oid oid;
-
-	/*
-	 * EncryptedContentInfo ::= SEQUENCE {
-	 *    contentType			ContentType,
-	 *    contentEncryptionAlgorithm  ContentEncryptionAlgorithmIdentifier,
-	 *    encryptedContent	[0] IMPLICIT	EncryptedContent OPTIONAL}
-	 */
-	if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
-		return -1;
-	wpa_hexdump(MSG_MSGDUMP, "DPP: EncryptedContentInfo",
-		    hdr.payload, hdr.length);
-	if (pos < end) {
-		wpa_hexdump(MSG_DEBUG,
-			    "DPP: Unexpected extra data after EncryptedContentInfo",
-			    pos, end - pos);
-		return -1;
-	}
-
-	end = pos;
-	pos = hdr.payload;
-
-	/* ContentType ::= OBJECT IDENTIFIER */
-	if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) {
-		wpa_printf(MSG_DEBUG, "DPP: Could not parse ContentType");
-		return -1;
-	}
-	if (!asn1_oid_equal(&oid, &asn1_dpp_asymmetric_key_package_oid)) {
-		char buf[80];
-
-		asn1_oid_to_str(&oid, buf, sizeof(buf));
-		wpa_printf(MSG_DEBUG, "DPP: Unexpected ContentType %s", buf);
-		return -1;
-	}
-
-	/* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */
-	if (asn1_get_alg_id(pos, end - pos, &oid, NULL, NULL, &pos) < 0)
-		return -1;
-	if (!asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_256_oid) &&
-	    !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_384_oid) &&
-	    !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_512_oid)) {
-		char buf[80];
-
-		asn1_oid_to_str(&oid, buf, sizeof(buf));
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Unexpected ContentEncryptionAlgorithmIdentifier %s",
-			   buf);
-		return -1;
-	}
-	/* ignore optional parameters */
-
-	/* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
-	 * EncryptedContent ::= OCTET STRING */
-	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
-	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 0) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Expected [0] IMPLICIT (EncryptedContent) - found class %d tag 0x%x",
-			   hdr.class, hdr.tag);
-		return -1;
-	}
-	wpa_hexdump(MSG_MSGDUMP, "DPP: EncryptedContent",
-		    hdr.payload, hdr.length);
-	data->enc_cont = hdr.payload;
-	data->enc_cont_len = hdr.length;
-	return 0;
-}
-
-
-static int dpp_parse_enveloped_data(const u8 *env_data, size_t env_data_len,
-				    struct dpp_enveloped_data *data)
-{
-	struct asn1_hdr hdr;
-	const u8 *pos, *end;
-	int val;
-
-	os_memset(data, 0, sizeof(*data));
-
-	/*
-	 * DPPEnvelopedData ::= EnvelopedData
-	 *
-	 * EnvelopedData ::= SEQUENCE {
-	 *    version			CMSVersion,
-	 *    originatorInfo	[0]	IMPLICIT OriginatorInfo OPTIONAL,
-	 *    recipientInfos		RecipientInfos,
-	 *    encryptedContentInfo	EncryptedContentInfo,
-	 *    unprotectedAttrs  [1] IMPLICIT	UnprotectedAttributes OPTIONAL}
-	 *
-	 * CMSVersion ::= INTEGER
-	 *
-	 * RecipientInfos ::= SET SIZE (1..MAX) OF RecipientInfo
-	 *
-	 * For DPP, version is 3, both originatorInfo and
-	 * unprotectedAttrs are omitted, and recipientInfos contains a single
-	 * RecipientInfo.
-	 */
-	if (asn1_get_sequence(env_data, env_data_len, &hdr, &end) < 0)
-		return -1;
-	pos = hdr.payload;
-	if (end < env_data + env_data_len) {
-		wpa_hexdump(MSG_DEBUG,
-			    "DPP: Unexpected extra data after DPPEnvelopedData",
-			    end, env_data + env_data_len - end);
-		return -1;
-	}
-
-	if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
-		return -1;
-	if (val != 3) {
-		wpa_printf(MSG_DEBUG, "DPP: EnvelopedData.version != 3");
-		return -1;
-	}
-
-	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
-	    hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SET) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Expected SET (RecipientInfos) - found class %d tag 0x%x",
-			   hdr.class, hdr.tag);
-		return -1;
-	}
-
-	if (dpp_parse_recipient_infos(hdr.payload, hdr.length, data) < 0)
-		return -1;
-	return dpp_parse_encrypted_content_info(hdr.payload + hdr.length, end,
-						data);
-}
-
-
-static struct dpp_asymmetric_key *
-dpp_parse_one_asymmetric_key(const u8 *buf, size_t len)
-{
-	struct asn1_hdr hdr;
-	const u8 *pos = buf, *end = buf + len, *next;
-	int val;
-	const u8 *params;
-	size_t params_len;
-	struct asn1_oid oid;
-	char txt[80];
-	struct dpp_asymmetric_key *key;
-	EC_KEY *eckey;
-
-	wpa_hexdump_key(MSG_MSGDUMP, "DPP: OneAsymmetricKey", buf, len);
-
-	key = os_zalloc(sizeof(*key));
-	if (!key)
-		return NULL;
-
-	/*
-	 * OneAsymmetricKey ::= SEQUENCE {
-	 *    version			Version,
-	 *    privateKeyAlgorithm	PrivateKeyAlgorithmIdentifier,
-	 *    privateKey		PrivateKey,
-	 *    attributes		[0] Attributes OPTIONAL,
-	 *    ...,
-	 *    [[2: publicKey		[1] BIT STRING OPTIONAL ]],
-	 *    ...
-	 * }
-	 */
-	if (asn1_get_sequence(pos, end - pos, &hdr, &end) < 0)
-		goto fail;
-	pos = hdr.payload;
-
-	/* Version ::= INTEGER { v1(0), v2(1) } (v1, ..., v2) */
-	if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
-		goto fail;
-	if (val != 1) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Unsupported DPPAsymmetricKeyPackage version %d",
-			   val);
-		goto fail;
-	}
-
-	/* PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier */
-	if (asn1_get_alg_id(pos, end - pos, &oid, &params, &params_len,
-			    &pos) < 0)
-		goto fail;
-	if (!asn1_oid_equal(&oid, &asn1_ec_public_key_oid)) {
-		asn1_oid_to_str(&oid, txt, sizeof(txt));
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Unsupported PrivateKeyAlgorithmIdentifier %s",
-			   txt);
-		goto fail;
-	}
-	wpa_hexdump(MSG_MSGDUMP, "DPP: PrivateKeyAlgorithmIdentifier params",
-		    params, params_len);
-	/*
-	 * ECParameters ::= CHOICE {
-	 *    namedCurve	OBJECT IDENTIFIER
-	 *    -- implicitCurve	NULL
-	 *    -- specifiedCurve	SpecifiedECDomain}
-	 */
-	if (!params || asn1_get_oid(params, params_len, &oid, &next) < 0) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Could not parse ECParameters.namedCurve");
-		goto fail;
-	}
-	asn1_oid_to_str(&oid, txt, sizeof(txt));
-	wpa_printf(MSG_MSGDUMP, "DPP: namedCurve %s", txt);
-	/* Assume the curve is identified within ECPrivateKey, so that this
-	 * separate indication is not really needed. */
-
-	/*
-	 * PrivateKey ::= OCTET STRING
-	 *    (Contains DER encoding of ECPrivateKey)
-	 */
-	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
-	    hdr.class != ASN1_CLASS_UNIVERSAL ||
-	    hdr.tag != ASN1_TAG_OCTETSTRING) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Expected OCTETSTRING (PrivateKey) - found class %d tag 0x%x",
-			   hdr.class, hdr.tag);
-		goto fail;
-	}
-	wpa_hexdump_key(MSG_MSGDUMP, "DPP: PrivateKey",
-			hdr.payload, hdr.length);
-	pos = hdr.payload + hdr.length;
-	eckey = d2i_ECPrivateKey(NULL, &hdr.payload, hdr.length);
-	if (!eckey) {
-		wpa_printf(MSG_INFO,
-			   "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
-		goto fail;
-	}
-	key->csign = EVP_PKEY_new();
-	if (!key->csign || EVP_PKEY_assign_EC_KEY(key->csign, eckey) != 1) {
-		EC_KEY_free(eckey);
-		goto fail;
-	}
-	if (wpa_debug_show_keys)
-		dpp_debug_print_key("DPP: Received c-sign-key", key->csign);
-
-	/*
-	 * Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } }
-	 *
-	 * Exactly one instance of type Attribute in OneAsymmetricKey.
-	 */
-	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
-	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 0) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Expected [0] Attributes - found class %d tag 0x%x",
-			   hdr.class, hdr.tag);
-		goto fail;
-	}
-	wpa_hexdump_key(MSG_MSGDUMP, "DPP: Attributes",
-			hdr.payload, hdr.length);
-	if (hdr.payload + hdr.length < end) {
-		wpa_hexdump_key(MSG_MSGDUMP,
-				"DPP: Ignore additional data at the end of OneAsymmetricKey",
-				hdr.payload + hdr.length,
-				end - (hdr.payload + hdr.length));
-	}
-	pos = hdr.payload;
-	end = hdr.payload + hdr.length;
-
-	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
-	    hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SET) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Expected SET (Attributes) - found class %d tag 0x%x",
-			   hdr.class, hdr.tag);
-		goto fail;
-	}
-	if (hdr.payload + hdr.length < end) {
-		wpa_hexdump_key(MSG_MSGDUMP,
-				"DPP: Ignore additional data at the end of OneAsymmetricKey (after SET)",
-				hdr.payload + hdr.length,
-				end - (hdr.payload + hdr.length));
-	}
-	pos = hdr.payload;
-	end = hdr.payload + hdr.length;
-
-	/*
-	 * OneAsymmetricKeyAttributes ATTRIBUTE ::= {
-	 *    aa-DPPConfigurationParameters,
-	 *    ... -- For local profiles
-	 * }
-	 *
-	 * aa-DPPConfigurationParameters ATTRIBUTE ::=
-	 * { TYPE DPPConfigurationParameters IDENTIFIED BY id-DPPConfigParams }
-	 *
-	 * Attribute ::= SEQUENCE {
-	 *    type OBJECT IDENTIFIER,
-	 *    values SET SIZE(1..MAX) OF Type
-	 *
-	 * Exactly one instance of ATTRIBUTE in attrValues.
-	 */
-	if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
-		goto fail;
-	if (pos < end) {
-		wpa_hexdump_key(MSG_MSGDUMP,
-				"DPP: Ignore additional data at the end of ATTRIBUTE",
-				pos, end - pos);
-	}
-	end = pos;
-	pos = hdr.payload;
-
-	if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0)
-		goto fail;
-	if (!asn1_oid_equal(&oid, &asn1_dpp_config_params_oid)) {
-		asn1_oid_to_str(&oid, txt, sizeof(txt));
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Unexpected Attribute identifier %s", txt);
-		goto fail;
-	}
-
-	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
-	    hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SET) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Expected SET (Attribute) - found class %d tag 0x%x",
-			   hdr.class, hdr.tag);
-		goto fail;
-	}
-	pos = hdr.payload;
-	end = hdr.payload + hdr.length;
-
-	/*
-	 * DPPConfigurationParameters ::= SEQUENCE {
-	 *    configurationTemplate	UTF8String,
-	 *    connectorTemplate		UTF8String OPTIONAL}
-	 */
-
-	wpa_hexdump_key(MSG_MSGDUMP, "DPP: DPPConfigurationParameters",
-			pos, end - pos);
-	if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
-		goto fail;
-	if (pos < end) {
-		wpa_hexdump_key(MSG_MSGDUMP,
-				"DPP: Ignore additional data after DPPConfigurationParameters",
-				pos, end - pos);
-	}
-	end = pos;
-	pos = hdr.payload;
-
-	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
-	    hdr.class != ASN1_CLASS_UNIVERSAL ||
-	    hdr.tag != ASN1_TAG_UTF8STRING) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Expected UTF8STRING (configurationTemplate) - found class %d tag 0x%x",
-			   hdr.class, hdr.tag);
-		goto fail;
-	}
-	wpa_hexdump_ascii_key(MSG_MSGDUMP, "DPP: configurationTemplate",
-			      hdr.payload, hdr.length);
-	key->config_template = os_zalloc(hdr.length + 1);
-	if (!key->config_template)
-		goto fail;
-	os_memcpy(key->config_template, hdr.payload, hdr.length);
-
-	pos = hdr.payload + hdr.length;
-
-	if (pos < end) {
-		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
-		    hdr.class != ASN1_CLASS_UNIVERSAL ||
-		    hdr.tag != ASN1_TAG_UTF8STRING) {
-			wpa_printf(MSG_DEBUG,
-				   "DPP: Expected UTF8STRING (connectorTemplate) - found class %d tag 0x%x",
-				   hdr.class, hdr.tag);
-			goto fail;
-		}
-		wpa_hexdump_ascii_key(MSG_MSGDUMP, "DPP: connectorTemplate",
-				      hdr.payload, hdr.length);
-		key->connector_template = os_zalloc(hdr.length + 1);
-		if (!key->connector_template)
-			goto fail;
-		os_memcpy(key->connector_template, hdr.payload, hdr.length);
-	}
-
-	return key;
-fail:
-	wpa_printf(MSG_DEBUG, "DPP: Failed to parse OneAsymmetricKey");
-	dpp_free_asymmetric_key(key);
-	return NULL;
-}
-
-
-static struct dpp_asymmetric_key *
-dpp_parse_dpp_asymmetric_key_package(const u8 *key_pkg, size_t key_pkg_len)
-{
-	struct asn1_hdr hdr;
-	const u8 *pos = key_pkg, *end = key_pkg + key_pkg_len;
-	struct dpp_asymmetric_key *first = NULL, *last = NULL, *key;
-
-	wpa_hexdump_key(MSG_MSGDUMP, "DPP: DPPAsymmetricKeyPackage",
-			key_pkg, key_pkg_len);
-
-	/*
-	 * DPPAsymmetricKeyPackage ::= AsymmetricKeyPackage
-	 *
-	 * AsymmetricKeyPackage ::= SEQUENCE SIZE (1..MAX) OF OneAsymmetricKey
-	 */
-	while (pos < end) {
-		if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0 ||
-		    !(key = dpp_parse_one_asymmetric_key(hdr.payload,
-							 hdr.length))) {
-			dpp_free_asymmetric_key(first);
-			return NULL;
-		}
-		if (!last) {
-			first = last = key;
-		} else {
-			last->next = key;
-			last = key;
-		}
-	}
-
-	return first;
-}
-
-
-static int dpp_conf_resp_env_data(struct dpp_authentication *auth,
-				  const u8 *env_data, size_t env_data_len)
-{
-	const u8 *key;
-	size_t key_len;
-	u8 kek[DPP_MAX_HASH_LEN];
-	u8 cont_encr_key[DPP_MAX_HASH_LEN];
-	size_t cont_encr_key_len;
-	int res;
-	u8 *key_pkg;
-	size_t key_pkg_len;
-	struct dpp_enveloped_data data;
-	struct dpp_asymmetric_key *keys;
-
-	wpa_hexdump(MSG_DEBUG, "DPP: DPPEnvelopedData", env_data, env_data_len);
-
-	if (dpp_parse_enveloped_data(env_data, env_data_len, &data) < 0)
-		return -1;
-
-	/* TODO: For initial testing, use ke as the key. Replace this with a
-	 * new key once that has been defined. */
-	key = auth->ke;
-	key_len = auth->curve->hash_len;
-	wpa_hexdump_key(MSG_DEBUG, "DPP: PBKDF2 key", key, key_len);
-
-	if (dpp_pbkdf2(data.prf_hash_len, key, key_len, data.salt, 64, 1000,
-		       kek, data.pbkdf2_key_len)) {
-		wpa_printf(MSG_DEBUG, "DPP: PBKDF2 failed");
-		return -1;
-	}
-	wpa_hexdump_key(MSG_DEBUG, "DPP: key-encryption key from PBKDF2",
-			kek, data.pbkdf2_key_len);
-
-	if (data.enc_key_len < AES_BLOCK_SIZE ||
-	    data.enc_key_len > sizeof(cont_encr_key) + AES_BLOCK_SIZE) {
-		wpa_printf(MSG_DEBUG, "DPP: Invalid encryptedKey length");
-		return -1;
-	}
-	res = aes_siv_decrypt(kek, data.pbkdf2_key_len,
-			      data.enc_key, data.enc_key_len,
-			      0, NULL, NULL, cont_encr_key);
-	forced_memzero(kek, data.pbkdf2_key_len);
-	if (res < 0) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: AES-SIV decryption of encryptedKey failed");
-		return -1;
-	}
-	cont_encr_key_len = data.enc_key_len - AES_BLOCK_SIZE;
-	wpa_hexdump_key(MSG_DEBUG, "DPP: content-encryption key",
-			cont_encr_key, cont_encr_key_len);
-
-	if (data.enc_cont_len < AES_BLOCK_SIZE)
-		return -1;
-	key_pkg_len = data.enc_cont_len - AES_BLOCK_SIZE;
-	key_pkg = os_malloc(key_pkg_len);
-	if (!key_pkg)
-		return -1;
-	res = aes_siv_decrypt(cont_encr_key, cont_encr_key_len,
-			      data.enc_cont, data.enc_cont_len,
-			      0, NULL, NULL, key_pkg);
-	forced_memzero(cont_encr_key, cont_encr_key_len);
-	if (res < 0) {
-		bin_clear_free(key_pkg, key_pkg_len);
-		wpa_printf(MSG_DEBUG,
-			   "DPP: AES-SIV decryption of encryptedContent failed");
-		return -1;
-	}
-
-	keys = dpp_parse_dpp_asymmetric_key_package(key_pkg, key_pkg_len);
-	bin_clear_free(key_pkg, key_pkg_len);
-	dpp_free_asymmetric_key(auth->conf_key_pkg);
-	auth->conf_key_pkg = keys;
-
-	return keys != NULL;;
-}
-
-#endif /* CONFIG_DPP2 */
-
-
 int dpp_conf_resp_rx(struct dpp_authentication *auth,
 		     const struct wpabuf *resp)
 {
@@ -8072,6 +2979,36 @@
 }
 
 
+struct wpabuf * dpp_build_conn_status(enum dpp_status_error result,
+				      const u8 *ssid, size_t ssid_len,
+				      const char *channel_list)
+{
+	struct wpabuf *json;
+
+	json = wpabuf_alloc(1000);
+	if (!json)
+		return NULL;
+	json_start_object(json, NULL);
+	json_add_int(json, "result", result);
+	if (ssid) {
+		json_value_sep(json);
+		if (json_add_base64url(json, "ssid64", ssid, ssid_len) < 0) {
+			wpabuf_free(json);
+			return NULL;
+		}
+	}
+	if (channel_list) {
+		json_value_sep(json);
+		json_add_string(json, "channelList", channel_list);
+	}
+	json_end_object(json);
+	wpa_hexdump_ascii(MSG_DEBUG, "DPP: connStatus JSON",
+			  wpabuf_head(json), wpabuf_len(json));
+
+	return json;
+}
+
+
 struct wpabuf * dpp_build_conn_status_result(struct dpp_authentication *auth,
 					     enum dpp_status_error result,
 					     const u8 *ssid, size_t ssid_len,
@@ -8083,23 +3020,9 @@
 	size_t len[2];
 	u8 *wrapped;
 
-	json = wpabuf_alloc(1000);
+	json = dpp_build_conn_status(result, ssid, ssid_len, channel_list);
 	if (!json)
 		return NULL;
-	json_start_object(json, NULL);
-	json_add_int(json, "result", result);
-	if (ssid) {
-		json_value_sep(json);
-		if (json_add_base64url(json, "ssid64", ssid, ssid_len) < 0)
-			goto fail;
-	}
-	if (channel_list) {
-		json_value_sep(json);
-		json_add_string(json, "channelList", channel_list);
-	}
-	json_end_object(json);
-	wpa_hexdump_ascii(MSG_DEBUG, "DPP: connStatus JSON",
-			  wpabuf_head(json), wpabuf_len(json));
 
 	nonce_len = auth->curve->nonce_len;
 	clear_len = 5 + 4 + nonce_len + 4 + wpabuf_len(json);
@@ -8161,6 +3084,8 @@
 		return;
 	EVP_PKEY_free(conf->csign);
 	os_free(conf->kid);
+	os_free(conf->connector);
+	EVP_PKEY_free(conf->connector_key);
 	os_free(conf);
 }
 
@@ -8192,7 +3117,6 @@
 static int dpp_configurator_gen_kid(struct dpp_configurator *conf)
 {
 	struct wpabuf *csign_pub = NULL;
-	u8 kid_hash[SHA256_MAC_LEN];
 	const u8 *addr[1];
 	size_t len[1];
 	int res;
@@ -8206,7 +3130,7 @@
 	/* kid = SHA256(ANSI X9.63 uncompressed C-sign-key) */
 	addr[0] = wpabuf_head(csign_pub);
 	len[0] = wpabuf_len(csign_pub);
-	res = sha256_vector(1, addr, len, kid_hash);
+	res = sha256_vector(1, addr, len, conf->kid_hash);
 	wpabuf_free(csign_pub);
 	if (res < 0) {
 		wpa_printf(MSG_DEBUG,
@@ -8214,7 +3138,8 @@
 		return -1;
 	}
 
-	conf->kid = base64_url_encode(kid_hash, sizeof(kid_hash), NULL);
+	conf->kid = base64_url_encode(conf->kid_hash, sizeof(conf->kid_hash),
+				      NULL);
 	return conf->kid ? 0 : -1;
 }
 
@@ -8229,17 +3154,13 @@
 	if (!conf)
 		return NULL;
 
-	if (!curve) {
-		conf->curve = &dpp_curves[0];
-	} else {
-		conf->curve = dpp_get_curve_name(curve);
-		if (!conf->curve) {
-			wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
-				   curve);
-			os_free(conf);
-			return NULL;
-		}
+	conf->curve = dpp_get_curve_name(curve);
+	if (!conf->curve) {
+		wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", curve);
+		os_free(conf);
+		return NULL;
 	}
+
 	if (privkey)
 		conf->csign = dpp_set_keypair(&conf->curve, privkey,
 					      privkey_len);
@@ -8269,16 +3190,12 @@
 		return -1;
 	}
 
-	if (!curve) {
-		auth->curve = &dpp_curves[0];
-	} else {
-		auth->curve = dpp_get_curve_name(curve);
-		if (!auth->curve) {
-			wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
-				   curve);
-			return -1;
-		}
+	auth->curve = dpp_get_curve_name(curve);
+	if (!auth->curve) {
+		wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", curve);
+		return -1;
 	}
+
 	wpa_printf(MSG_DEBUG,
 		   "DPP: Building own configuration/connector with curve %s",
 		   auth->curve->name);
@@ -8314,7 +3231,8 @@
 
 static int dpp_connector_compatible_group(struct json_token *root,
 					  const char *group_id,
-					  const char *net_role)
+					  const char *net_role,
+					  bool reconfig)
 {
 	struct json_token *groups, *token;
 
@@ -8338,7 +3256,9 @@
 		    os_strcmp(id->string, group_id) != 0)
 			continue;
 
-		if (dpp_compatible_netrole(role->string, net_role))
+		if (reconfig && os_strcmp(net_role, "configurator") == 0)
+			return 1;
+		if (!reconfig && dpp_compatible_netrole(role->string, net_role))
 			return 1;
 	}
 
@@ -8346,8 +3266,8 @@
 }
 
 
-static int dpp_connector_match_groups(struct json_token *own_root,
-				      struct json_token *peer_root)
+int dpp_connector_match_groups(struct json_token *own_root,
+			       struct json_token *peer_root, bool reconfig)
 {
 	struct json_token *groups, *token;
 
@@ -8377,7 +3297,7 @@
 			   "DPP: peer connector group: groupId='%s' netRole='%s'",
 			   id->string, role->string);
 		if (dpp_connector_compatible_group(own_root, id->string,
-						   role->string)) {
+						   role->string, reconfig)) {
 			wpa_printf(MSG_DEBUG,
 				   "DPP: Compatible group/netRole in own connector");
 			return 1;
@@ -8388,71 +3308,37 @@
 }
 
 
-static int dpp_derive_pmk(const u8 *Nx, size_t Nx_len, u8 *pmk,
-			  unsigned int hash_len)
+struct json_token * dpp_parse_own_connector(const char *own_connector)
 {
-	u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
-	const char *info = "DPP PMK";
-	int res;
+	unsigned char *own_conn;
+	size_t own_conn_len;
+	const char *pos, *end;
+	struct json_token *own_root;
 
-	/* PMK = HKDF(<>, "DPP PMK", N.x) */
-
-	/* HKDF-Extract(<>, N.x) */
-	os_memset(salt, 0, hash_len);
-	if (dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk) < 0)
-		return -1;
-	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
-			prk, hash_len);
-
-	/* HKDF-Expand(PRK, info, L) */
-	res = dpp_hkdf_expand(hash_len, prk, hash_len, info, pmk, hash_len);
-	os_memset(prk, 0, hash_len);
-	if (res < 0)
-		return -1;
-
-	wpa_hexdump_key(MSG_DEBUG, "DPP: PMK = HKDF-Expand(PRK, info, L)",
-			pmk, hash_len);
-	return 0;
-}
-
-
-static int dpp_derive_pmkid(const struct dpp_curve_params *curve,
-			    EVP_PKEY *own_key, EVP_PKEY *peer_key, u8 *pmkid)
-{
-	struct wpabuf *nkx, *pkx;
-	int ret = -1, res;
-	const u8 *addr[2];
-	size_t len[2];
-	u8 hash[SHA256_MAC_LEN];
-
-	/* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */
-	nkx = dpp_get_pubkey_point(own_key, 0);
-	pkx = dpp_get_pubkey_point(peer_key, 0);
-	if (!nkx || !pkx)
-		goto fail;
-	addr[0] = wpabuf_head(nkx);
-	len[0] = wpabuf_len(nkx) / 2;
-	addr[1] = wpabuf_head(pkx);
-	len[1] = wpabuf_len(pkx) / 2;
-	if (len[0] != len[1])
-		goto fail;
-	if (os_memcmp(addr[0], addr[1], len[0]) > 0) {
-		addr[0] = wpabuf_head(pkx);
-		addr[1] = wpabuf_head(nkx);
+	pos = os_strchr(own_connector, '.');
+	if (!pos) {
+		wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the first dot (.)");
+		return NULL;
 	}
-	wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 1", addr[0], len[0]);
-	wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 2", addr[1], len[1]);
-	res = sha256_vector(2, addr, len, hash);
-	if (res < 0)
-		goto fail;
-	wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash output", hash, SHA256_MAC_LEN);
-	os_memcpy(pmkid, hash, PMKID_LEN);
-	wpa_hexdump(MSG_DEBUG, "DPP: PMKID", pmkid, PMKID_LEN);
-	ret = 0;
-fail:
-	wpabuf_free(nkx);
-	wpabuf_free(pkx);
-	return ret;
+	pos++;
+	end = os_strchr(pos, '.');
+	if (!end) {
+		wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the second dot (.)");
+		return NULL;
+	}
+	own_conn = base64_url_decode(pos, end - pos, &own_conn_len);
+	if (!own_conn) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to base64url decode own signedConnector JWS Payload");
+		return NULL;
+	}
+
+	own_root = json_parse((const char *) own_conn, own_conn_len);
+	os_free(own_conn);
+	if (!own_root)
+		wpa_printf(MSG_DEBUG, "DPP: Failed to parse local connector");
+
+	return own_root;
 }
 
 
@@ -8470,12 +3356,6 @@
 	struct wpabuf *own_key_pub = NULL;
 	const struct dpp_curve_params *curve, *own_curve;
 	struct dpp_signed_connector_info info;
-	const unsigned char *p;
-	EVP_PKEY *csign = NULL;
-	char *signed_connector = NULL;
-	const char *pos, *end;
-	unsigned char *own_conn = NULL;
-	size_t own_conn_len;
 	size_t Nx_len;
 	u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
 
@@ -8484,14 +3364,6 @@
 	if (expiry)
 		*expiry = 0;
 
-	p = csign_key;
-	csign = d2i_PUBKEY(NULL, &p, csign_key_len);
-	if (!csign) {
-		wpa_printf(MSG_ERROR,
-			   "DPP: Failed to parse local C-sign-key information");
-		goto fail;
-	}
-
 	own_key = dpp_set_keypair(&own_curve, net_access_key,
 				  net_access_key_len);
 	if (!own_key) {
@@ -8499,39 +3371,12 @@
 		goto fail;
 	}
 
-	pos = os_strchr(own_connector, '.');
-	if (!pos) {
-		wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the first dot (.)");
+	own_root = dpp_parse_own_connector(own_connector);
+	if (!own_root)
 		goto fail;
-	}
-	pos++;
-	end = os_strchr(pos, '.');
-	if (!end) {
-		wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the second dot (.)");
-		goto fail;
-	}
-	own_conn = base64_url_decode(pos, end - pos, &own_conn_len);
-	if (!own_conn) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Failed to base64url decode own signedConnector JWS Payload");
-		goto fail;
-	}
 
-	own_root = json_parse((const char *) own_conn, own_conn_len);
-	if (!own_root) {
-		wpa_printf(MSG_DEBUG, "DPP: Failed to parse local connector");
-		goto fail;
-	}
-
-	wpa_hexdump_ascii(MSG_DEBUG, "DPP: Peer signedConnector",
-			  peer_connector, peer_connector_len);
-	signed_connector = os_malloc(peer_connector_len + 1);
-	if (!signed_connector)
-		goto fail;
-	os_memcpy(signed_connector, peer_connector, peer_connector_len);
-	signed_connector[peer_connector_len] = '\0';
-
-	res = dpp_process_signed_connector(&info, csign, signed_connector);
+	res = dpp_check_signed_connector(&info, csign_key, csign_key_len,
+					 peer_connector, peer_connector_len);
 	if (res != DPP_STATUS_OK) {
 		ret = res;
 		goto fail;
@@ -8544,7 +3389,7 @@
 		goto fail;
 	}
 
-	if (!dpp_connector_match_groups(own_root, root)) {
+	if (!dpp_connector_match_groups(own_root, root, false)) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Peer connector does not include compatible group netrole with own connector");
 		ret = DPP_STATUS_NO_MATCH;
@@ -8612,1751 +3457,17 @@
 	if (ret != DPP_STATUS_OK)
 		os_memset(intro, 0, sizeof(*intro));
 	os_memset(Nx, 0, sizeof(Nx));
-	os_free(own_conn);
-	os_free(signed_connector);
 	os_free(info.payload);
 	EVP_PKEY_free(own_key);
 	wpabuf_free(own_key_pub);
 	EVP_PKEY_free(peer_key);
-	EVP_PKEY_free(csign);
 	json_free(root);
 	json_free(own_root);
 	return ret;
 }
 
 
-static EVP_PKEY * dpp_pkex_get_role_elem(const struct dpp_curve_params *curve,
-					 int init)
-{
-	EC_GROUP *group;
-	size_t len = curve->prime_len;
-	const u8 *x, *y;
-	EVP_PKEY *res;
-
-	switch (curve->ike_group) {
-	case 19:
-		x = init ? pkex_init_x_p256 : pkex_resp_x_p256;
-		y = init ? pkex_init_y_p256 : pkex_resp_y_p256;
-		break;
-	case 20:
-		x = init ? pkex_init_x_p384 : pkex_resp_x_p384;
-		y = init ? pkex_init_y_p384 : pkex_resp_y_p384;
-		break;
-	case 21:
-		x = init ? pkex_init_x_p521 : pkex_resp_x_p521;
-		y = init ? pkex_init_y_p521 : pkex_resp_y_p521;
-		break;
-	case 28:
-		x = init ? pkex_init_x_bp_p256r1 : pkex_resp_x_bp_p256r1;
-		y = init ? pkex_init_y_bp_p256r1 : pkex_resp_y_bp_p256r1;
-		break;
-	case 29:
-		x = init ? pkex_init_x_bp_p384r1 : pkex_resp_x_bp_p384r1;
-		y = init ? pkex_init_y_bp_p384r1 : pkex_resp_y_bp_p384r1;
-		break;
-	case 30:
-		x = init ? pkex_init_x_bp_p512r1 : pkex_resp_x_bp_p512r1;
-		y = init ? pkex_init_y_bp_p512r1 : pkex_resp_y_bp_p512r1;
-		break;
-	default:
-		return NULL;
-	}
-
-	group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
-	if (!group)
-		return NULL;
-	res = dpp_set_pubkey_point_group(group, x, y, len);
-	EC_GROUP_free(group);
-	return res;
-}
-
-
-static EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve,
-				     const u8 *mac_init, const char *code,
-				     const char *identifier, BN_CTX *bnctx,
-				     EC_GROUP **ret_group)
-{
-	u8 hash[DPP_MAX_HASH_LEN];
-	const u8 *addr[3];
-	size_t len[3];
-	unsigned int num_elem = 0;
-	EC_POINT *Qi = NULL;
-	EVP_PKEY *Pi = NULL;
-	EC_KEY *Pi_ec = NULL;
-	const EC_POINT *Pi_point;
-	BIGNUM *hash_bn = NULL;
-	const EC_GROUP *group = NULL;
-	EC_GROUP *group2 = NULL;
-
-	/* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
-
-	wpa_printf(MSG_DEBUG, "DPP: MAC-Initiator: " MACSTR, MAC2STR(mac_init));
-	addr[num_elem] = mac_init;
-	len[num_elem] = ETH_ALEN;
-	num_elem++;
-	if (identifier) {
-		wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
-			   identifier);
-		addr[num_elem] = (const u8 *) identifier;
-		len[num_elem] = os_strlen(identifier);
-		num_elem++;
-	}
-	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
-	addr[num_elem] = (const u8 *) code;
-	len[num_elem] = os_strlen(code);
-	num_elem++;
-	if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
-		goto fail;
-	wpa_hexdump_key(MSG_DEBUG,
-			"DPP: H(MAC-Initiator | [identifier |] code)",
-			hash, curve->hash_len);
-	Pi = dpp_pkex_get_role_elem(curve, 1);
-	if (!Pi)
-		goto fail;
-	dpp_debug_print_key("DPP: Pi", Pi);
-	Pi_ec = EVP_PKEY_get1_EC_KEY(Pi);
-	if (!Pi_ec)
-		goto fail;
-	Pi_point = EC_KEY_get0_public_key(Pi_ec);
-
-	group = EC_KEY_get0_group(Pi_ec);
-	if (!group)
-		goto fail;
-	group2 = EC_GROUP_dup(group);
-	if (!group2)
-		goto fail;
-	Qi = EC_POINT_new(group2);
-	if (!Qi) {
-		EC_GROUP_free(group2);
-		goto fail;
-	}
-	hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
-	if (!hash_bn ||
-	    EC_POINT_mul(group2, Qi, NULL, Pi_point, hash_bn, bnctx) != 1)
-		goto fail;
-	if (EC_POINT_is_at_infinity(group, Qi)) {
-		wpa_printf(MSG_INFO, "DPP: Qi is the point-at-infinity");
-		goto fail;
-	}
-	dpp_debug_print_point("DPP: Qi", group, Qi);
-out:
-	EC_KEY_free(Pi_ec);
-	EVP_PKEY_free(Pi);
-	BN_clear_free(hash_bn);
-	if (ret_group && Qi)
-		*ret_group = group2;
-	else
-		EC_GROUP_free(group2);
-	return Qi;
-fail:
-	EC_POINT_free(Qi);
-	Qi = NULL;
-	goto out;
-}
-
-
-static EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve,
-				     const u8 *mac_resp, const char *code,
-				     const char *identifier, BN_CTX *bnctx,
-				     EC_GROUP **ret_group)
-{
-	u8 hash[DPP_MAX_HASH_LEN];
-	const u8 *addr[3];
-	size_t len[3];
-	unsigned int num_elem = 0;
-	EC_POINT *Qr = NULL;
-	EVP_PKEY *Pr = NULL;
-	EC_KEY *Pr_ec = NULL;
-	const EC_POINT *Pr_point;
-	BIGNUM *hash_bn = NULL;
-	const EC_GROUP *group = NULL;
-	EC_GROUP *group2 = NULL;
-
-	/* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
-
-	wpa_printf(MSG_DEBUG, "DPP: MAC-Responder: " MACSTR, MAC2STR(mac_resp));
-	addr[num_elem] = mac_resp;
-	len[num_elem] = ETH_ALEN;
-	num_elem++;
-	if (identifier) {
-		wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
-			   identifier);
-		addr[num_elem] = (const u8 *) identifier;
-		len[num_elem] = os_strlen(identifier);
-		num_elem++;
-	}
-	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
-	addr[num_elem] = (const u8 *) code;
-	len[num_elem] = os_strlen(code);
-	num_elem++;
-	if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
-		goto fail;
-	wpa_hexdump_key(MSG_DEBUG,
-			"DPP: H(MAC-Responder | [identifier |] code)",
-			hash, curve->hash_len);
-	Pr = dpp_pkex_get_role_elem(curve, 0);
-	if (!Pr)
-		goto fail;
-	dpp_debug_print_key("DPP: Pr", Pr);
-	Pr_ec = EVP_PKEY_get1_EC_KEY(Pr);
-	if (!Pr_ec)
-		goto fail;
-	Pr_point = EC_KEY_get0_public_key(Pr_ec);
-
-	group = EC_KEY_get0_group(Pr_ec);
-	if (!group)
-		goto fail;
-	group2 = EC_GROUP_dup(group);
-	if (!group2)
-		goto fail;
-	Qr = EC_POINT_new(group2);
-	if (!Qr) {
-		EC_GROUP_free(group2);
-		goto fail;
-	}
-	hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
-	if (!hash_bn ||
-	    EC_POINT_mul(group2, Qr, NULL, Pr_point, hash_bn, bnctx) != 1)
-		goto fail;
-	if (EC_POINT_is_at_infinity(group, Qr)) {
-		wpa_printf(MSG_INFO, "DPP: Qr is the point-at-infinity");
-		goto fail;
-	}
-	dpp_debug_print_point("DPP: Qr", group, Qr);
-out:
-	EC_KEY_free(Pr_ec);
-	EVP_PKEY_free(Pr);
-	BN_clear_free(hash_bn);
-	if (ret_group && Qr)
-		*ret_group = group2;
-	else
-		EC_GROUP_free(group2);
-	return Qr;
-fail:
-	EC_POINT_free(Qr);
-	Qr = NULL;
-	goto out;
-}
-
-
-#ifdef CONFIG_TESTING_OPTIONS
-static int dpp_test_gen_invalid_key(struct wpabuf *msg,
-				    const struct dpp_curve_params *curve)
-{
-	BN_CTX *ctx;
-	BIGNUM *x, *y;
-	int ret = -1;
-	EC_GROUP *group;
-	EC_POINT *point;
-
-	group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
-	if (!group)
-		return -1;
-
-	ctx = BN_CTX_new();
-	point = EC_POINT_new(group);
-	x = BN_new();
-	y = BN_new();
-	if (!ctx || !point || !x || !y)
-		goto fail;
-
-	if (BN_rand(x, curve->prime_len * 8, 0, 0) != 1)
-		goto fail;
-
-	/* Generate a random y coordinate that results in a point that is not
-	 * on the curve. */
-	for (;;) {
-		if (BN_rand(y, curve->prime_len * 8, 0, 0) != 1)
-			goto fail;
-
-		if (EC_POINT_set_affine_coordinates_GFp(group, point, x, y,
-							ctx) != 1) {
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined(OPENSSL_IS_BORINGSSL)
-		/* Unlike older OpenSSL versions, OpenSSL 1.1.1 and BoringSSL
-		 * return an error from EC_POINT_set_affine_coordinates_GFp()
-		 * when the point is not on the curve. */
-			break;
-#else /* >=1.1.0 or OPENSSL_IS_BORINGSSL */
-			goto fail;
-#endif /* >= 1.1.0 or OPENSSL_IS_BORINGSSL */
-		}
-
-		if (!EC_POINT_is_on_curve(group, point, ctx))
-			break;
-	}
-
-	if (dpp_bn2bin_pad(x, wpabuf_put(msg, curve->prime_len),
-			   curve->prime_len) < 0 ||
-	    dpp_bn2bin_pad(y, wpabuf_put(msg, curve->prime_len),
-			   curve->prime_len) < 0)
-		goto fail;
-
-	ret = 0;
-fail:
-	if (ret < 0)
-		wpa_printf(MSG_INFO, "DPP: Failed to generate invalid key");
-	BN_free(x);
-	BN_free(y);
-	EC_POINT_free(point);
-	BN_CTX_free(ctx);
-	EC_GROUP_free(group);
-
-	return ret;
-}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-
-static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex)
-{
-	EC_KEY *X_ec = NULL;
-	const EC_POINT *X_point;
-	BN_CTX *bnctx = NULL;
-	EC_GROUP *group = NULL;
-	EC_POINT *Qi = NULL, *M = NULL;
-	struct wpabuf *M_buf = NULL;
-	BIGNUM *Mx = NULL, *My = NULL;
-	struct wpabuf *msg = NULL;
-	size_t attr_len;
-	const struct dpp_curve_params *curve = pkex->own_bi->curve;
-
-	wpa_printf(MSG_DEBUG, "DPP: Build PKEX Exchange Request");
-
-	/* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
-	bnctx = BN_CTX_new();
-	if (!bnctx)
-		goto fail;
-	Qi = dpp_pkex_derive_Qi(curve, pkex->own_mac, pkex->code,
-				pkex->identifier, bnctx, &group);
-	if (!Qi)
-		goto fail;
-
-	/* Generate a random ephemeral keypair x/X */
-#ifdef CONFIG_TESTING_OPTIONS
-	if (dpp_pkex_ephemeral_key_override_len) {
-		const struct dpp_curve_params *tmp_curve;
-
-		wpa_printf(MSG_INFO,
-			   "DPP: TESTING - override ephemeral key x/X");
-		pkex->x = dpp_set_keypair(&tmp_curve,
-					  dpp_pkex_ephemeral_key_override,
-					  dpp_pkex_ephemeral_key_override_len);
-	} else {
-		pkex->x = dpp_gen_keypair(curve);
-	}
-#else /* CONFIG_TESTING_OPTIONS */
-	pkex->x = dpp_gen_keypair(curve);
-#endif /* CONFIG_TESTING_OPTIONS */
-	if (!pkex->x)
-		goto fail;
-
-	/* M = X + Qi */
-	X_ec = EVP_PKEY_get1_EC_KEY(pkex->x);
-	if (!X_ec)
-		goto fail;
-	X_point = EC_KEY_get0_public_key(X_ec);
-	if (!X_point)
-		goto fail;
-	dpp_debug_print_point("DPP: X", group, X_point);
-	M = EC_POINT_new(group);
-	Mx = BN_new();
-	My = BN_new();
-	if (!M || !Mx || !My ||
-	    EC_POINT_add(group, M, X_point, Qi, bnctx) != 1 ||
-	    EC_POINT_get_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1)
-		goto fail;
-	dpp_debug_print_point("DPP: M", group, M);
-
-	/* Initiator -> Responder: group, [identifier,] M */
-	attr_len = 4 + 2;
-	if (pkex->identifier)
-		attr_len += 4 + os_strlen(pkex->identifier);
-	attr_len += 4 + 2 * curve->prime_len;
-	msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_REQ, attr_len);
-	if (!msg)
-		goto fail;
-
-#ifdef CONFIG_TESTING_OPTIONS
-	if (dpp_test == DPP_TEST_NO_FINITE_CYCLIC_GROUP_PKEX_EXCHANGE_REQ) {
-		wpa_printf(MSG_INFO, "DPP: TESTING - no Finite Cyclic Group");
-		goto skip_finite_cyclic_group;
-	}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-	/* Finite Cyclic Group attribute */
-	wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
-	wpabuf_put_le16(msg, 2);
-	wpabuf_put_le16(msg, curve->ike_group);
-
-#ifdef CONFIG_TESTING_OPTIONS
-skip_finite_cyclic_group:
-#endif /* CONFIG_TESTING_OPTIONS */
-
-	/* Code Identifier attribute */
-	if (pkex->identifier) {
-		wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
-		wpabuf_put_le16(msg, os_strlen(pkex->identifier));
-		wpabuf_put_str(msg, pkex->identifier);
-	}
-
-#ifdef CONFIG_TESTING_OPTIONS
-	if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
-		wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
-		goto out;
-	}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-	/* M in Encrypted Key attribute */
-	wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
-	wpabuf_put_le16(msg, 2 * curve->prime_len);
-
-#ifdef CONFIG_TESTING_OPTIONS
-	if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
-		wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
-		if (dpp_test_gen_invalid_key(msg, curve) < 0)
-			goto fail;
-		goto out;
-	}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-	if (dpp_bn2bin_pad(Mx, wpabuf_put(msg, curve->prime_len),
-			   curve->prime_len) < 0 ||
-	    dpp_bn2bin_pad(Mx, pkex->Mx, curve->prime_len) < 0 ||
-	    dpp_bn2bin_pad(My, wpabuf_put(msg, curve->prime_len),
-			   curve->prime_len) < 0)
-		goto fail;
-
-out:
-	wpabuf_free(M_buf);
-	EC_KEY_free(X_ec);
-	EC_POINT_free(M);
-	EC_POINT_free(Qi);
-	BN_clear_free(Mx);
-	BN_clear_free(My);
-	BN_CTX_free(bnctx);
-	EC_GROUP_free(group);
-	return msg;
-fail:
-	wpa_printf(MSG_INFO, "DPP: Failed to build PKEX Exchange Request");
-	wpabuf_free(msg);
-	msg = NULL;
-	goto out;
-}
-
-
-static void dpp_pkex_fail(struct dpp_pkex *pkex, const char *txt)
-{
-	wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
-}
-
-
-struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
-				const u8 *own_mac,
-				const char *identifier,
-				const char *code)
-{
-	struct dpp_pkex *pkex;
-
-#ifdef CONFIG_TESTING_OPTIONS
-	if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
-		wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
-			   MAC2STR(dpp_pkex_own_mac_override));
-		own_mac = dpp_pkex_own_mac_override;
-	}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-	pkex = os_zalloc(sizeof(*pkex));
-	if (!pkex)
-		return NULL;
-	pkex->msg_ctx = msg_ctx;
-	pkex->initiator = 1;
-	pkex->own_bi = bi;
-	os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
-	if (identifier) {
-		pkex->identifier = os_strdup(identifier);
-		if (!pkex->identifier)
-			goto fail;
-	}
-	pkex->code = os_strdup(code);
-	if (!pkex->code)
-		goto fail;
-	pkex->exchange_req = dpp_pkex_build_exchange_req(pkex);
-	if (!pkex->exchange_req)
-		goto fail;
-	return pkex;
-fail:
-	dpp_pkex_free(pkex);
-	return NULL;
-}
-
-
-static struct wpabuf *
-dpp_pkex_build_exchange_resp(struct dpp_pkex *pkex,
-			     enum dpp_status_error status,
-			     const BIGNUM *Nx, const BIGNUM *Ny)
-{
-	struct wpabuf *msg = NULL;
-	size_t attr_len;
-	const struct dpp_curve_params *curve = pkex->own_bi->curve;
-
-	/* Initiator -> Responder: DPP Status, [identifier,] N */
-	attr_len = 4 + 1;
-	if (pkex->identifier)
-		attr_len += 4 + os_strlen(pkex->identifier);
-	attr_len += 4 + 2 * curve->prime_len;
-	msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_RESP, attr_len);
-	if (!msg)
-		goto fail;
-
-#ifdef CONFIG_TESTING_OPTIONS
-	if (dpp_test == DPP_TEST_NO_STATUS_PKEX_EXCHANGE_RESP) {
-		wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
-		goto skip_status;
-	}
-
-	if (dpp_test == DPP_TEST_INVALID_STATUS_PKEX_EXCHANGE_RESP) {
-		wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
-		status = 255;
-	}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-	/* DPP Status */
-	dpp_build_attr_status(msg, status);
-
-#ifdef CONFIG_TESTING_OPTIONS
-skip_status:
-#endif /* CONFIG_TESTING_OPTIONS */
-
-	/* Code Identifier attribute */
-	if (pkex->identifier) {
-		wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
-		wpabuf_put_le16(msg, os_strlen(pkex->identifier));
-		wpabuf_put_str(msg, pkex->identifier);
-	}
-
-	if (status != DPP_STATUS_OK)
-		goto skip_encrypted_key;
-
-#ifdef CONFIG_TESTING_OPTIONS
-	if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
-		wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
-		goto skip_encrypted_key;
-	}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-	/* N in Encrypted Key attribute */
-	wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
-	wpabuf_put_le16(msg, 2 * curve->prime_len);
-
-#ifdef CONFIG_TESTING_OPTIONS
-	if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
-		wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
-		if (dpp_test_gen_invalid_key(msg, curve) < 0)
-			goto fail;
-		goto skip_encrypted_key;
-	}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-	if (dpp_bn2bin_pad(Nx, wpabuf_put(msg, curve->prime_len),
-			   curve->prime_len) < 0 ||
-	    dpp_bn2bin_pad(Nx, pkex->Nx, curve->prime_len) < 0 ||
-	    dpp_bn2bin_pad(Ny, wpabuf_put(msg, curve->prime_len),
-			   curve->prime_len) < 0)
-		goto fail;
-
-skip_encrypted_key:
-	if (status == DPP_STATUS_BAD_GROUP) {
-		/* Finite Cyclic Group attribute */
-		wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
-		wpabuf_put_le16(msg, 2);
-		wpabuf_put_le16(msg, curve->ike_group);
-	}
-
-	return msg;
-fail:
-	wpabuf_free(msg);
-	return NULL;
-}
-
-
-static int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp,
-			     const u8 *Mx, size_t Mx_len,
-			     const u8 *Nx, size_t Nx_len,
-			     const char *code,
-			     const u8 *Kx, size_t Kx_len,
-			     u8 *z, unsigned int hash_len)
-{
-	u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
-	int res;
-	u8 *info, *pos;
-	size_t info_len;
-
-	/* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
-	 */
-
-	/* HKDF-Extract(<>, IKM=K.x) */
-	os_memset(salt, 0, hash_len);
-	if (dpp_hmac(hash_len, salt, hash_len, Kx, Kx_len, prk) < 0)
-		return -1;
-	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
-			prk, hash_len);
-	info_len = 2 * ETH_ALEN + Mx_len + Nx_len + os_strlen(code);
-	info = os_malloc(info_len);
-	if (!info)
-		return -1;
-	pos = info;
-	os_memcpy(pos, mac_init, ETH_ALEN);
-	pos += ETH_ALEN;
-	os_memcpy(pos, mac_resp, ETH_ALEN);
-	pos += ETH_ALEN;
-	os_memcpy(pos, Mx, Mx_len);
-	pos += Mx_len;
-	os_memcpy(pos, Nx, Nx_len);
-	pos += Nx_len;
-	os_memcpy(pos, code, os_strlen(code));
-
-	/* HKDF-Expand(PRK, info, L) */
-	if (hash_len == 32)
-		res = hmac_sha256_kdf(prk, hash_len, NULL, info, info_len,
-				      z, hash_len);
-	else if (hash_len == 48)
-		res = hmac_sha384_kdf(prk, hash_len, NULL, info, info_len,
-				      z, hash_len);
-	else if (hash_len == 64)
-		res = hmac_sha512_kdf(prk, hash_len, NULL, info, info_len,
-				      z, hash_len);
-	else
-		res = -1;
-	os_free(info);
-	os_memset(prk, 0, hash_len);
-	if (res < 0)
-		return -1;
-
-	wpa_hexdump_key(MSG_DEBUG, "DPP: z = HKDF-Expand(PRK, info, L)",
-			z, hash_len);
-	return 0;
-}
-
-
-static int dpp_pkex_identifier_match(const u8 *attr_id, u16 attr_id_len,
-				     const char *identifier)
-{
-	if (!attr_id && identifier) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: No PKEX code identifier received, but expected one");
-		return 0;
-	}
-
-	if (attr_id && !identifier) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: PKEX code identifier received, but not expecting one");
-		return 0;
-	}
-
-	if (attr_id && identifier &&
-	    (os_strlen(identifier) != attr_id_len ||
-	     os_memcmp(identifier, attr_id, attr_id_len) != 0)) {
-		wpa_printf(MSG_DEBUG, "DPP: PKEX code identifier mismatch");
-		return 0;
-	}
-
-	return 1;
-}
-
-
-struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
-					   struct dpp_bootstrap_info *bi,
-					   const u8 *own_mac,
-					   const u8 *peer_mac,
-					   const char *identifier,
-					   const char *code,
-					   const u8 *buf, size_t len)
-{
-	const u8 *attr_group, *attr_id, *attr_key;
-	u16 attr_group_len, attr_id_len, attr_key_len;
-	const struct dpp_curve_params *curve = bi->curve;
-	u16 ike_group;
-	struct dpp_pkex *pkex = NULL;
-	EC_POINT *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL, *N = NULL;
-	BN_CTX *bnctx = NULL;
-	EC_GROUP *group = NULL;
-	BIGNUM *Mx = NULL, *My = NULL;
-	EC_KEY *Y_ec = NULL, *X_ec = NULL;;
-	const EC_POINT *Y_point;
-	BIGNUM *Nx = NULL, *Ny = NULL;
-	u8 Kx[DPP_MAX_SHARED_SECRET_LEN];
-	size_t Kx_len;
-	int res;
-
-	if (bi->pkex_t >= PKEX_COUNTER_T_LIMIT) {
-		wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
-			"PKEX counter t limit reached - ignore message");
-		return NULL;
-	}
-
-#ifdef CONFIG_TESTING_OPTIONS
-	if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
-		wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
-			   MAC2STR(dpp_pkex_peer_mac_override));
-		peer_mac = dpp_pkex_peer_mac_override;
-	}
-	if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
-		wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
-			   MAC2STR(dpp_pkex_own_mac_override));
-		own_mac = dpp_pkex_own_mac_override;
-	}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-	attr_id_len = 0;
-	attr_id = dpp_get_attr(buf, len, DPP_ATTR_CODE_IDENTIFIER,
-			       &attr_id_len);
-	if (!dpp_pkex_identifier_match(attr_id, attr_id_len, identifier))
-		return NULL;
-
-	attr_group = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
-				  &attr_group_len);
-	if (!attr_group || attr_group_len != 2) {
-		wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
-			"Missing or invalid Finite Cyclic Group attribute");
-		return NULL;
-	}
-	ike_group = WPA_GET_LE16(attr_group);
-	if (ike_group != curve->ike_group) {
-		wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
-			"Mismatching PKEX curve: peer=%u own=%u",
-			ike_group, curve->ike_group);
-		pkex = os_zalloc(sizeof(*pkex));
-		if (!pkex)
-			goto fail;
-		pkex->own_bi = bi;
-		pkex->failed = 1;
-		pkex->exchange_resp = dpp_pkex_build_exchange_resp(
-			pkex, DPP_STATUS_BAD_GROUP, NULL, NULL);
-		if (!pkex->exchange_resp)
-			goto fail;
-		return pkex;
-	}
-
-	/* M in Encrypted Key attribute */
-	attr_key = dpp_get_attr(buf, len, DPP_ATTR_ENCRYPTED_KEY,
-				&attr_key_len);
-	if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2 ||
-	    attr_key_len / 2 > DPP_MAX_SHARED_SECRET_LEN) {
-		wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
-			"Missing Encrypted Key attribute");
-		return NULL;
-	}
-
-	/* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
-	bnctx = BN_CTX_new();
-	if (!bnctx)
-		goto fail;
-	Qi = dpp_pkex_derive_Qi(curve, peer_mac, code, identifier, bnctx,
-				&group);
-	if (!Qi)
-		goto fail;
-
-	/* X' = M - Qi */
-	X = EC_POINT_new(group);
-	M = EC_POINT_new(group);
-	Mx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
-	My = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
-	if (!X || !M || !Mx || !My ||
-	    EC_POINT_set_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1 ||
-	    EC_POINT_is_at_infinity(group, M) ||
-	    !EC_POINT_is_on_curve(group, M, bnctx) ||
-	    EC_POINT_invert(group, Qi, bnctx) != 1 ||
-	    EC_POINT_add(group, X, M, Qi, bnctx) != 1 ||
-	    EC_POINT_is_at_infinity(group, X) ||
-	    !EC_POINT_is_on_curve(group, X, bnctx)) {
-		wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
-			"Invalid Encrypted Key value");
-		bi->pkex_t++;
-		goto fail;
-	}
-	dpp_debug_print_point("DPP: M", group, M);
-	dpp_debug_print_point("DPP: X'", group, X);
-
-	pkex = os_zalloc(sizeof(*pkex));
-	if (!pkex)
-		goto fail;
-	pkex->t = bi->pkex_t;
-	pkex->msg_ctx = msg_ctx;
-	pkex->own_bi = bi;
-	os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
-	os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
-	if (identifier) {
-		pkex->identifier = os_strdup(identifier);
-		if (!pkex->identifier)
-			goto fail;
-	}
-	pkex->code = os_strdup(code);
-	if (!pkex->code)
-		goto fail;
-
-	os_memcpy(pkex->Mx, attr_key, attr_key_len / 2);
-
-	X_ec = EC_KEY_new();
-	if (!X_ec ||
-	    EC_KEY_set_group(X_ec, group) != 1 ||
-	    EC_KEY_set_public_key(X_ec, X) != 1)
-		goto fail;
-	pkex->x = EVP_PKEY_new();
-	if (!pkex->x ||
-	    EVP_PKEY_set1_EC_KEY(pkex->x, X_ec) != 1)
-		goto fail;
-
-	/* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
-	Qr = dpp_pkex_derive_Qr(curve, own_mac, code, identifier, bnctx, NULL);
-	if (!Qr)
-		goto fail;
-
-	/* Generate a random ephemeral keypair y/Y */
-#ifdef CONFIG_TESTING_OPTIONS
-	if (dpp_pkex_ephemeral_key_override_len) {
-		const struct dpp_curve_params *tmp_curve;
-
-		wpa_printf(MSG_INFO,
-			   "DPP: TESTING - override ephemeral key y/Y");
-		pkex->y = dpp_set_keypair(&tmp_curve,
-					  dpp_pkex_ephemeral_key_override,
-					  dpp_pkex_ephemeral_key_override_len);
-	} else {
-		pkex->y = dpp_gen_keypair(curve);
-	}
-#else /* CONFIG_TESTING_OPTIONS */
-	pkex->y = dpp_gen_keypair(curve);
-#endif /* CONFIG_TESTING_OPTIONS */
-	if (!pkex->y)
-		goto fail;
-
-	/* N = Y + Qr */
-	Y_ec = EVP_PKEY_get1_EC_KEY(pkex->y);
-	if (!Y_ec)
-		goto fail;
-	Y_point = EC_KEY_get0_public_key(Y_ec);
-	if (!Y_point)
-		goto fail;
-	dpp_debug_print_point("DPP: Y", group, Y_point);
-	N = EC_POINT_new(group);
-	Nx = BN_new();
-	Ny = BN_new();
-	if (!N || !Nx || !Ny ||
-	    EC_POINT_add(group, N, Y_point, Qr, bnctx) != 1 ||
-	    EC_POINT_get_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1)
-		goto fail;
-	dpp_debug_print_point("DPP: N", group, N);
-
-	pkex->exchange_resp = dpp_pkex_build_exchange_resp(pkex, DPP_STATUS_OK,
-							   Nx, Ny);
-	if (!pkex->exchange_resp)
-		goto fail;
-
-	/* K = y * X' */
-	if (dpp_ecdh(pkex->y, pkex->x, Kx, &Kx_len) < 0)
-		goto fail;
-
-	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
-			Kx, Kx_len);
-
-	/* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
-	 */
-	res = dpp_pkex_derive_z(pkex->peer_mac, pkex->own_mac,
-				pkex->Mx, curve->prime_len,
-				pkex->Nx, curve->prime_len, pkex->code,
-				Kx, Kx_len, pkex->z, curve->hash_len);
-	os_memset(Kx, 0, Kx_len);
-	if (res < 0)
-		goto fail;
-
-	pkex->exchange_done = 1;
-
-out:
-	BN_CTX_free(bnctx);
-	EC_POINT_free(Qi);
-	EC_POINT_free(Qr);
-	BN_free(Mx);
-	BN_free(My);
-	BN_free(Nx);
-	BN_free(Ny);
-	EC_POINT_free(M);
-	EC_POINT_free(N);
-	EC_POINT_free(X);
-	EC_KEY_free(X_ec);
-	EC_KEY_free(Y_ec);
-	EC_GROUP_free(group);
-	return pkex;
-fail:
-	wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request processing failed");
-	dpp_pkex_free(pkex);
-	pkex = NULL;
-	goto out;
-}
-
-
-static struct wpabuf *
-dpp_pkex_build_commit_reveal_req(struct dpp_pkex *pkex,
-				 const struct wpabuf *A_pub, const u8 *u)
-{
-	const struct dpp_curve_params *curve = pkex->own_bi->curve;
-	struct wpabuf *msg = NULL;
-	size_t clear_len, attr_len;
-	struct wpabuf *clear = NULL;
-	u8 *wrapped;
-	u8 octet;
-	const u8 *addr[2];
-	size_t len[2];
-
-	/* {A, u, [bootstrapping info]}z */
-	clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
-	clear = wpabuf_alloc(clear_len);
-	attr_len = 4 + clear_len + AES_BLOCK_SIZE;
-#ifdef CONFIG_TESTING_OPTIONS
-	if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ)
-		attr_len += 5;
-#endif /* CONFIG_TESTING_OPTIONS */
-	msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_REQ, attr_len);
-	if (!clear || !msg)
-		goto fail;
-
-#ifdef CONFIG_TESTING_OPTIONS
-	if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_REQ) {
-		wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
-		goto skip_bootstrap_key;
-	}
-	if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_REQ) {
-		wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
-		wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
-		wpabuf_put_le16(clear, 2 * curve->prime_len);
-		if (dpp_test_gen_invalid_key(clear, curve) < 0)
-			goto fail;
-		goto skip_bootstrap_key;
-	}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-	/* A in Bootstrap Key attribute */
-	wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
-	wpabuf_put_le16(clear, wpabuf_len(A_pub));
-	wpabuf_put_buf(clear, A_pub);
-
-#ifdef CONFIG_TESTING_OPTIONS
-skip_bootstrap_key:
-	if (dpp_test == DPP_TEST_NO_I_AUTH_TAG_PKEX_CR_REQ) {
-		wpa_printf(MSG_INFO, "DPP: TESTING - no I-Auth tag");
-		goto skip_i_auth_tag;
-	}
-	if (dpp_test == DPP_TEST_I_AUTH_TAG_MISMATCH_PKEX_CR_REQ) {
-		wpa_printf(MSG_INFO, "DPP: TESTING - I-Auth tag mismatch");
-		wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
-		wpabuf_put_le16(clear, curve->hash_len);
-		wpabuf_put_data(clear, u, curve->hash_len - 1);
-		wpabuf_put_u8(clear, u[curve->hash_len - 1] ^ 0x01);
-		goto skip_i_auth_tag;
-	}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-	/* u in I-Auth tag attribute */
-	wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
-	wpabuf_put_le16(clear, curve->hash_len);
-	wpabuf_put_data(clear, u, curve->hash_len);
-
-#ifdef CONFIG_TESTING_OPTIONS
-skip_i_auth_tag:
-	if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_REQ) {
-		wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
-		goto skip_wrapped_data;
-	}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-	addr[0] = wpabuf_head_u8(msg) + 2;
-	len[0] = DPP_HDR_LEN;
-	octet = 0;
-	addr[1] = &octet;
-	len[1] = sizeof(octet);
-	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]);
-
-	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
-	wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
-	wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
-
-	wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
-	if (aes_siv_encrypt(pkex->z, curve->hash_len,
-			    wpabuf_head(clear), wpabuf_len(clear),
-			    2, addr, len, wrapped) < 0)
-		goto fail;
-	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
-		    wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
-
-#ifdef CONFIG_TESTING_OPTIONS
-	if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_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 */
-
-out:
-	wpabuf_free(clear);
-	return msg;
-
-fail:
-	wpabuf_free(msg);
-	msg = NULL;
-	goto out;
-}
-
-
-struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
-					  const u8 *peer_mac,
-					  const u8 *buf, size_t buflen)
-{
-	const u8 *attr_status, *attr_id, *attr_key, *attr_group;
-	u16 attr_status_len, attr_id_len, attr_key_len, attr_group_len;
-	EC_GROUP *group = NULL;
-	BN_CTX *bnctx = NULL;
-	struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
-	const struct dpp_curve_params *curve = pkex->own_bi->curve;
-	EC_POINT *Qr = NULL, *Y = NULL, *N = NULL;
-	BIGNUM *Nx = NULL, *Ny = NULL;
-	EC_KEY *Y_ec = NULL;
-	size_t Jx_len, Kx_len;
-	u8 Jx[DPP_MAX_SHARED_SECRET_LEN], Kx[DPP_MAX_SHARED_SECRET_LEN];
-	const u8 *addr[4];
-	size_t len[4];
-	u8 u[DPP_MAX_HASH_LEN];
-	int res;
-
-	if (pkex->failed || pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
-		return NULL;
-
-#ifdef CONFIG_TESTING_OPTIONS
-	if (dpp_test == DPP_TEST_STOP_AT_PKEX_EXCHANGE_RESP) {
-		wpa_printf(MSG_INFO,
-			   "DPP: TESTING - stop at PKEX Exchange Response");
-		pkex->failed = 1;
-		return NULL;
-	}
-
-	if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
-		wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
-			   MAC2STR(dpp_pkex_peer_mac_override));
-		peer_mac = dpp_pkex_peer_mac_override;
-	}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-	os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
-
-	attr_status = dpp_get_attr(buf, buflen, DPP_ATTR_STATUS,
-				   &attr_status_len);
-	if (!attr_status || attr_status_len != 1) {
-		dpp_pkex_fail(pkex, "No DPP Status attribute");
-		return NULL;
-	}
-	wpa_printf(MSG_DEBUG, "DPP: Status %u", attr_status[0]);
-
-	if (attr_status[0] == DPP_STATUS_BAD_GROUP) {
-		attr_group = dpp_get_attr(buf, buflen,
-					  DPP_ATTR_FINITE_CYCLIC_GROUP,
-					  &attr_group_len);
-		if (attr_group && attr_group_len == 2) {
-			wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
-				"Peer indicated mismatching PKEX group - proposed %u",
-				WPA_GET_LE16(attr_group));
-			return NULL;
-		}
-	}
-
-	if (attr_status[0] != DPP_STATUS_OK) {
-		dpp_pkex_fail(pkex, "PKEX failed (peer indicated failure)");
-		return NULL;
-	}
-
-	attr_id_len = 0;
-	attr_id = dpp_get_attr(buf, buflen, DPP_ATTR_CODE_IDENTIFIER,
-			       &attr_id_len);
-	if (!dpp_pkex_identifier_match(attr_id, attr_id_len,
-				       pkex->identifier)) {
-		dpp_pkex_fail(pkex, "PKEX code identifier mismatch");
-		return NULL;
-	}
-
-	/* N in Encrypted Key attribute */
-	attr_key = dpp_get_attr(buf, buflen, DPP_ATTR_ENCRYPTED_KEY,
-				&attr_key_len);
-	if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2) {
-		dpp_pkex_fail(pkex, "Missing Encrypted Key attribute");
-		return NULL;
-	}
-
-	/* Qr = H(MAC-Responder | [identifier |] code) * Pr */
-	bnctx = BN_CTX_new();
-	if (!bnctx)
-		goto fail;
-	Qr = dpp_pkex_derive_Qr(curve, pkex->peer_mac, pkex->code,
-				pkex->identifier, bnctx, &group);
-	if (!Qr)
-		goto fail;
-
-	/* Y' = N - Qr */
-	Y = EC_POINT_new(group);
-	N = EC_POINT_new(group);
-	Nx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
-	Ny = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
-	if (!Y || !N || !Nx || !Ny ||
-	    EC_POINT_set_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1 ||
-	    EC_POINT_is_at_infinity(group, N) ||
-	    !EC_POINT_is_on_curve(group, N, bnctx) ||
-	    EC_POINT_invert(group, Qr, bnctx) != 1 ||
-	    EC_POINT_add(group, Y, N, Qr, bnctx) != 1 ||
-	    EC_POINT_is_at_infinity(group, Y) ||
-	    !EC_POINT_is_on_curve(group, Y, bnctx)) {
-		dpp_pkex_fail(pkex, "Invalid Encrypted Key value");
-		pkex->t++;
-		goto fail;
-	}
-	dpp_debug_print_point("DPP: N", group, N);
-	dpp_debug_print_point("DPP: Y'", group, Y);
-
-	pkex->exchange_done = 1;
-
-	/* ECDH: J = a * Y’ */
-	Y_ec = EC_KEY_new();
-	if (!Y_ec ||
-	    EC_KEY_set_group(Y_ec, group) != 1 ||
-	    EC_KEY_set_public_key(Y_ec, Y) != 1)
-		goto fail;
-	pkex->y = EVP_PKEY_new();
-	if (!pkex->y ||
-	    EVP_PKEY_set1_EC_KEY(pkex->y, Y_ec) != 1)
-		goto fail;
-	if (dpp_ecdh(pkex->own_bi->pubkey, pkex->y, Jx, &Jx_len) < 0)
-		goto fail;
-
-	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
-			Jx, Jx_len);
-
-	/* u = HMAC(J.x,  MAC-Initiator | A.x | Y’.x | X.x ) */
-	A_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
-	Y_pub = dpp_get_pubkey_point(pkex->y, 0);
-	X_pub = dpp_get_pubkey_point(pkex->x, 0);
-	if (!A_pub || !Y_pub || !X_pub)
-		goto fail;
-	addr[0] = pkex->own_mac;
-	len[0] = ETH_ALEN;
-	addr[1] = wpabuf_head(A_pub);
-	len[1] = wpabuf_len(A_pub) / 2;
-	addr[2] = wpabuf_head(Y_pub);
-	len[2] = wpabuf_len(Y_pub) / 2;
-	addr[3] = wpabuf_head(X_pub);
-	len[3] = wpabuf_len(X_pub) / 2;
-	if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
-		goto fail;
-	wpa_hexdump(MSG_DEBUG, "DPP: u", u, curve->hash_len);
-
-	/* K = x * Y’ */
-	if (dpp_ecdh(pkex->x, pkex->y, Kx, &Kx_len) < 0)
-		goto fail;
-
-	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
-			Kx, Kx_len);
-
-	/* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
-	 */
-	res = dpp_pkex_derive_z(pkex->own_mac, pkex->peer_mac,
-				pkex->Mx, curve->prime_len,
-				attr_key /* N.x */, attr_key_len / 2,
-				pkex->code, Kx, Kx_len,
-				pkex->z, curve->hash_len);
-	os_memset(Kx, 0, Kx_len);
-	if (res < 0)
-		goto fail;
-
-	msg = dpp_pkex_build_commit_reveal_req(pkex, A_pub, u);
-	if (!msg)
-		goto fail;
-
-out:
-	wpabuf_free(A_pub);
-	wpabuf_free(X_pub);
-	wpabuf_free(Y_pub);
-	EC_POINT_free(Qr);
-	EC_POINT_free(Y);
-	EC_POINT_free(N);
-	BN_free(Nx);
-	BN_free(Ny);
-	EC_KEY_free(Y_ec);
-	BN_CTX_free(bnctx);
-	EC_GROUP_free(group);
-	return msg;
-fail:
-	wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response processing failed");
-	goto out;
-}
-
-
-static struct wpabuf *
-dpp_pkex_build_commit_reveal_resp(struct dpp_pkex *pkex,
-				  const struct wpabuf *B_pub, const u8 *v)
-{
-	const struct dpp_curve_params *curve = pkex->own_bi->curve;
-	struct wpabuf *msg = NULL;
-	const u8 *addr[2];
-	size_t len[2];
-	u8 octet;
-	u8 *wrapped;
-	struct wpabuf *clear = NULL;
-	size_t clear_len, attr_len;
-
-	/* {B, v [bootstrapping info]}z */
-	clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
-	clear = wpabuf_alloc(clear_len);
-	attr_len = 4 + clear_len + AES_BLOCK_SIZE;
-#ifdef CONFIG_TESTING_OPTIONS
-	if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP)
-		attr_len += 5;
-#endif /* CONFIG_TESTING_OPTIONS */
-	msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_RESP, attr_len);
-	if (!clear || !msg)
-		goto fail;
-
-#ifdef CONFIG_TESTING_OPTIONS
-	if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_RESP) {
-		wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
-		goto skip_bootstrap_key;
-	}
-	if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_RESP) {
-		wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
-		wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
-		wpabuf_put_le16(clear, 2 * curve->prime_len);
-		if (dpp_test_gen_invalid_key(clear, curve) < 0)
-			goto fail;
-		goto skip_bootstrap_key;
-	}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-	/* B in Bootstrap Key attribute */
-	wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
-	wpabuf_put_le16(clear, wpabuf_len(B_pub));
-	wpabuf_put_buf(clear, B_pub);
-
-#ifdef CONFIG_TESTING_OPTIONS
-skip_bootstrap_key:
-	if (dpp_test == DPP_TEST_NO_R_AUTH_TAG_PKEX_CR_RESP) {
-		wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth tag");
-		goto skip_r_auth_tag;
-	}
-	if (dpp_test == DPP_TEST_R_AUTH_TAG_MISMATCH_PKEX_CR_RESP) {
-		wpa_printf(MSG_INFO, "DPP: TESTING - R-Auth tag mismatch");
-		wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
-		wpabuf_put_le16(clear, curve->hash_len);
-		wpabuf_put_data(clear, v, curve->hash_len - 1);
-		wpabuf_put_u8(clear, v[curve->hash_len - 1] ^ 0x01);
-		goto skip_r_auth_tag;
-	}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-	/* v in R-Auth tag attribute */
-	wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
-	wpabuf_put_le16(clear, curve->hash_len);
-	wpabuf_put_data(clear, v, curve->hash_len);
-
-#ifdef CONFIG_TESTING_OPTIONS
-skip_r_auth_tag:
-	if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_RESP) {
-		wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
-		goto skip_wrapped_data;
-	}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-	addr[0] = wpabuf_head_u8(msg) + 2;
-	len[0] = DPP_HDR_LEN;
-	octet = 1;
-	addr[1] = &octet;
-	len[1] = sizeof(octet);
-	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]);
-
-	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
-	wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
-	wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
-
-	wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
-	if (aes_siv_encrypt(pkex->z, curve->hash_len,
-			    wpabuf_head(clear), wpabuf_len(clear),
-			    2, addr, len, wrapped) < 0)
-		goto fail;
-	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
-		    wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
-
-#ifdef CONFIG_TESTING_OPTIONS
-	if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_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 */
-
-out:
-	wpabuf_free(clear);
-	return msg;
-
-fail:
-	wpabuf_free(msg);
-	msg = NULL;
-	goto out;
-}
-
-
-struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
-					      const u8 *hdr,
-					      const u8 *buf, size_t buflen)
-{
-	const struct dpp_curve_params *curve = pkex->own_bi->curve;
-	size_t Jx_len, Lx_len;
-	u8 Jx[DPP_MAX_SHARED_SECRET_LEN];
-	u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
-	const u8 *wrapped_data, *b_key, *peer_u;
-	u16 wrapped_data_len, b_key_len, peer_u_len = 0;
-	const u8 *addr[4];
-	size_t len[4];
-	u8 octet;
-	u8 *unwrapped = NULL;
-	size_t unwrapped_len = 0;
-	struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
-	struct wpabuf *B_pub = NULL;
-	u8 u[DPP_MAX_HASH_LEN], v[DPP_MAX_HASH_LEN];
-
-#ifdef CONFIG_TESTING_OPTIONS
-	if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_REQ) {
-		wpa_printf(MSG_INFO,
-			   "DPP: TESTING - stop at PKEX CR Request");
-		pkex->failed = 1;
-		return NULL;
-	}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-	if (!pkex->exchange_done || pkex->failed ||
-	    pkex->t >= PKEX_COUNTER_T_LIMIT || pkex->initiator)
-		goto fail;
-
-	wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
-				    &wrapped_data_len);
-	if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
-		dpp_pkex_fail(pkex,
-			      "Missing or invalid required Wrapped Data attribute");
-		goto fail;
-	}
-
-	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;
-
-	addr[0] = hdr;
-	len[0] = DPP_HDR_LEN;
-	octet = 0;
-	addr[1] = &octet;
-	len[1] = sizeof(octet);
-	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]);
-
-	if (aes_siv_decrypt(pkex->z, curve->hash_len,
-			    wrapped_data, wrapped_data_len,
-			    2, addr, len, unwrapped) < 0) {
-		dpp_pkex_fail(pkex,
-			      "AES-SIV decryption failed - possible PKEX code mismatch");
-		pkex->failed = 1;
-		pkex->t++;
-		goto fail;
-	}
-	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
-		    unwrapped, unwrapped_len);
-
-	if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
-		dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
-		goto fail;
-	}
-
-	b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
-			     &b_key_len);
-	if (!b_key || b_key_len != 2 * curve->prime_len) {
-		dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
-		goto fail;
-	}
-	pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
-							b_key_len);
-	if (!pkex->peer_bootstrap_key) {
-		dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
-		goto fail;
-	}
-	dpp_debug_print_key("DPP: Peer bootstrap public key",
-			    pkex->peer_bootstrap_key);
-
-	/* ECDH: J' = y * A' */
-	if (dpp_ecdh(pkex->y, pkex->peer_bootstrap_key, Jx, &Jx_len) < 0)
-		goto fail;
-
-	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
-			Jx, Jx_len);
-
-	/* u' = HMAC(J'.x, MAC-Initiator | A'.x | Y.x | X'.x) */
-	A_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
-	Y_pub = dpp_get_pubkey_point(pkex->y, 0);
-	X_pub = dpp_get_pubkey_point(pkex->x, 0);
-	if (!A_pub || !Y_pub || !X_pub)
-		goto fail;
-	addr[0] = pkex->peer_mac;
-	len[0] = ETH_ALEN;
-	addr[1] = wpabuf_head(A_pub);
-	len[1] = wpabuf_len(A_pub) / 2;
-	addr[2] = wpabuf_head(Y_pub);
-	len[2] = wpabuf_len(Y_pub) / 2;
-	addr[3] = wpabuf_head(X_pub);
-	len[3] = wpabuf_len(X_pub) / 2;
-	if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
-		goto fail;
-
-	peer_u = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
-			      &peer_u_len);
-	if (!peer_u || peer_u_len != curve->hash_len ||
-	    os_memcmp(peer_u, u, curve->hash_len) != 0) {
-		dpp_pkex_fail(pkex, "No valid u (I-Auth tag) found");
-		wpa_hexdump(MSG_DEBUG, "DPP: Calculated u'",
-			    u, curve->hash_len);
-		wpa_hexdump(MSG_DEBUG, "DPP: Received u", peer_u, peer_u_len);
-		pkex->t++;
-		goto fail;
-	}
-	wpa_printf(MSG_DEBUG, "DPP: Valid u (I-Auth tag) received");
-
-	/* ECDH: L = b * X' */
-	if (dpp_ecdh(pkex->own_bi->pubkey, pkex->x, Lx, &Lx_len) < 0)
-		goto fail;
-
-	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
-			Lx, Lx_len);
-
-	/* v = HMAC(L.x, MAC-Responder | B.x | X'.x | Y.x) */
-	B_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
-	if (!B_pub)
-		goto fail;
-	addr[0] = pkex->own_mac;
-	len[0] = ETH_ALEN;
-	addr[1] = wpabuf_head(B_pub);
-	len[1] = wpabuf_len(B_pub) / 2;
-	addr[2] = wpabuf_head(X_pub);
-	len[2] = wpabuf_len(X_pub) / 2;
-	addr[3] = wpabuf_head(Y_pub);
-	len[3] = wpabuf_len(Y_pub) / 2;
-	if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
-		goto fail;
-	wpa_hexdump(MSG_DEBUG, "DPP: v", v, curve->hash_len);
-
-	msg = dpp_pkex_build_commit_reveal_resp(pkex, B_pub, v);
-	if (!msg)
-		goto fail;
-
-out:
-	os_free(unwrapped);
-	wpabuf_free(A_pub);
-	wpabuf_free(B_pub);
-	wpabuf_free(X_pub);
-	wpabuf_free(Y_pub);
-	return msg;
-fail:
-	wpa_printf(MSG_DEBUG,
-		   "DPP: PKEX Commit-Reveal Request processing failed");
-	goto out;
-}
-
-
-int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr,
-				   const u8 *buf, size_t buflen)
-{
-	const struct dpp_curve_params *curve = pkex->own_bi->curve;
-	const u8 *wrapped_data, *b_key, *peer_v;
-	u16 wrapped_data_len, b_key_len, peer_v_len = 0;
-	const u8 *addr[4];
-	size_t len[4];
-	u8 octet;
-	u8 *unwrapped = NULL;
-	size_t unwrapped_len = 0;
-	int ret = -1;
-	u8 v[DPP_MAX_HASH_LEN];
-	size_t Lx_len;
-	u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
-	struct wpabuf *B_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
-
-#ifdef CONFIG_TESTING_OPTIONS
-	if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_RESP) {
-		wpa_printf(MSG_INFO,
-			   "DPP: TESTING - stop at PKEX CR Response");
-		pkex->failed = 1;
-		goto fail;
-	}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-	if (!pkex->exchange_done || pkex->failed ||
-	    pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
-		goto fail;
-
-	wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
-				    &wrapped_data_len);
-	if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
-		dpp_pkex_fail(pkex,
-			      "Missing or invalid required Wrapped Data attribute");
-		goto fail;
-	}
-
-	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;
-
-	addr[0] = hdr;
-	len[0] = DPP_HDR_LEN;
-	octet = 1;
-	addr[1] = &octet;
-	len[1] = sizeof(octet);
-	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]);
-
-	if (aes_siv_decrypt(pkex->z, curve->hash_len,
-			    wrapped_data, wrapped_data_len,
-			    2, addr, len, unwrapped) < 0) {
-		dpp_pkex_fail(pkex,
-			      "AES-SIV decryption failed - possible PKEX code mismatch");
-		pkex->t++;
-		goto fail;
-	}
-	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
-		    unwrapped, unwrapped_len);
-
-	if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
-		dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
-		goto fail;
-	}
-
-	b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
-			     &b_key_len);
-	if (!b_key || b_key_len != 2 * curve->prime_len) {
-		dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
-		goto fail;
-	}
-	pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
-							b_key_len);
-	if (!pkex->peer_bootstrap_key) {
-		dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
-		goto fail;
-	}
-	dpp_debug_print_key("DPP: Peer bootstrap public key",
-			    pkex->peer_bootstrap_key);
-
-	/* ECDH: L' = x * B' */
-	if (dpp_ecdh(pkex->x, pkex->peer_bootstrap_key, Lx, &Lx_len) < 0)
-		goto fail;
-
-	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
-			Lx, Lx_len);
-
-	/* v' = HMAC(L.x, MAC-Responder | B'.x | X.x | Y'.x) */
-	B_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
-	X_pub = dpp_get_pubkey_point(pkex->x, 0);
-	Y_pub = dpp_get_pubkey_point(pkex->y, 0);
-	if (!B_pub || !X_pub || !Y_pub)
-		goto fail;
-	addr[0] = pkex->peer_mac;
-	len[0] = ETH_ALEN;
-	addr[1] = wpabuf_head(B_pub);
-	len[1] = wpabuf_len(B_pub) / 2;
-	addr[2] = wpabuf_head(X_pub);
-	len[2] = wpabuf_len(X_pub) / 2;
-	addr[3] = wpabuf_head(Y_pub);
-	len[3] = wpabuf_len(Y_pub) / 2;
-	if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
-		goto fail;
-
-	peer_v = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_AUTH_TAG,
-			      &peer_v_len);
-	if (!peer_v || peer_v_len != curve->hash_len ||
-	    os_memcmp(peer_v, v, curve->hash_len) != 0) {
-		dpp_pkex_fail(pkex, "No valid v (R-Auth tag) found");
-		wpa_hexdump(MSG_DEBUG, "DPP: Calculated v'",
-			    v, curve->hash_len);
-		wpa_hexdump(MSG_DEBUG, "DPP: Received v", peer_v, peer_v_len);
-		pkex->t++;
-		goto fail;
-	}
-	wpa_printf(MSG_DEBUG, "DPP: Valid v (R-Auth tag) received");
-
-	ret = 0;
-out:
-	wpabuf_free(B_pub);
-	wpabuf_free(X_pub);
-	wpabuf_free(Y_pub);
-	os_free(unwrapped);
-	return ret;
-fail:
-	goto out;
-}
-
-
-void dpp_pkex_free(struct dpp_pkex *pkex)
-{
-	if (!pkex)
-		return;
-
-	os_free(pkex->identifier);
-	os_free(pkex->code);
-	EVP_PKEY_free(pkex->x);
-	EVP_PKEY_free(pkex->y);
-	EVP_PKEY_free(pkex->peer_bootstrap_key);
-	wpabuf_free(pkex->exchange_req);
-	wpabuf_free(pkex->exchange_resp);
-	os_free(pkex);
-}
-
-
-#ifdef CONFIG_TESTING_OPTIONS
-char * dpp_corrupt_connector_signature(const char *connector)
-{
-	char *tmp, *pos, *signed3 = NULL;
-	unsigned char *signature = NULL;
-	size_t signature_len = 0, signed3_len;
-
-	tmp = os_zalloc(os_strlen(connector) + 5);
-	if (!tmp)
-		goto fail;
-	os_memcpy(tmp, connector, os_strlen(connector));
-
-	pos = os_strchr(tmp, '.');
-	if (!pos)
-		goto fail;
-
-	pos = os_strchr(pos + 1, '.');
-	if (!pos)
-		goto fail;
-	pos++;
-
-	wpa_printf(MSG_DEBUG, "DPP: Original base64url encoded signature: %s",
-		   pos);
-	signature = base64_url_decode(pos, os_strlen(pos), &signature_len);
-	if (!signature || signature_len == 0)
-		goto fail;
-	wpa_hexdump(MSG_DEBUG, "DPP: Original Connector signature",
-		    signature, signature_len);
-	signature[signature_len - 1] ^= 0x01;
-	wpa_hexdump(MSG_DEBUG, "DPP: Corrupted Connector signature",
-		    signature, signature_len);
-	signed3 = base64_url_encode(signature, signature_len, &signed3_len);
-	if (!signed3)
-		goto fail;
-	os_memcpy(pos, signed3, signed3_len);
-	pos[signed3_len] = '\0';
-	wpa_printf(MSG_DEBUG, "DPP: Corrupted base64url encoded signature: %s",
-		   pos);
-
-out:
-	os_free(signature);
-	os_free(signed3);
-	return tmp;
-fail:
-	os_free(tmp);
-	tmp = NULL;
-	goto out;
-}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-
-#ifdef CONFIG_DPP2
-
-struct dpp_pfs * dpp_pfs_init(const u8 *net_access_key,
-			      size_t net_access_key_len)
-{
-	struct wpabuf *pub = NULL;
-	EVP_PKEY *own_key;
-	struct dpp_pfs *pfs;
-
-	pfs = os_zalloc(sizeof(*pfs));
-	if (!pfs)
-		return NULL;
-
-	own_key = dpp_set_keypair(&pfs->curve, net_access_key,
-				  net_access_key_len);
-	if (!own_key) {
-		wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
-		goto fail;
-	}
-	EVP_PKEY_free(own_key);
-
-	pfs->ecdh = crypto_ecdh_init(pfs->curve->ike_group);
-	if (!pfs->ecdh)
-		goto fail;
-
-	pub = crypto_ecdh_get_pubkey(pfs->ecdh, 0);
-	pub = wpabuf_zeropad(pub, pfs->curve->prime_len);
-	if (!pub)
-		goto fail;
-
-	pfs->ie = wpabuf_alloc(5 + wpabuf_len(pub));
-	if (!pfs->ie)
-		goto fail;
-	wpabuf_put_u8(pfs->ie, WLAN_EID_EXTENSION);
-	wpabuf_put_u8(pfs->ie, 1 + 2 + wpabuf_len(pub));
-	wpabuf_put_u8(pfs->ie, WLAN_EID_EXT_OWE_DH_PARAM);
-	wpabuf_put_le16(pfs->ie, pfs->curve->ike_group);
-	wpabuf_put_buf(pfs->ie, pub);
-	wpabuf_free(pub);
-	wpa_hexdump_buf(MSG_DEBUG, "DPP: Diffie-Hellman Parameter element",
-			pfs->ie);
-
-	return pfs;
-fail:
-	wpabuf_free(pub);
-	dpp_pfs_free(pfs);
-	return NULL;
-}
-
-
-int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len)
-{
-	if (peer_ie_len < 2)
-		return -1;
-	if (WPA_GET_LE16(peer_ie) != pfs->curve->ike_group) {
-		wpa_printf(MSG_DEBUG, "DPP: Peer used different group for PFS");
-		return -1;
-	}
-
-	pfs->secret = crypto_ecdh_set_peerkey(pfs->ecdh, 0, peer_ie + 2,
-					      peer_ie_len - 2);
-	pfs->secret = wpabuf_zeropad(pfs->secret, pfs->curve->prime_len);
-	if (!pfs->secret) {
-		wpa_printf(MSG_DEBUG, "DPP: Invalid peer DH public key");
-		return -1;
-	}
-	wpa_hexdump_buf_key(MSG_DEBUG, "DPP: DH shared secret", pfs->secret);
-	return 0;
-}
-
-
-void dpp_pfs_free(struct dpp_pfs *pfs)
-{
-	if (!pfs)
-		return;
-	crypto_ecdh_deinit(pfs->ecdh);
-	wpabuf_free(pfs->ie);
-	wpabuf_clear_free(pfs->secret);
-	os_free(pfs);
-}
-
-#endif /* CONFIG_DPP2 */
-
-
-static unsigned int dpp_next_id(struct dpp_global *dpp)
+unsigned int dpp_next_id(struct dpp_global *dpp)
 {
 	struct dpp_bootstrap_info *bi;
 	unsigned int max_id = 0;
@@ -10527,33 +3638,6 @@
 }
 
 
-struct dpp_bootstrap_info *
-dpp_pkex_finish(struct dpp_global *dpp, struct dpp_pkex *pkex, const u8 *peer,
-		unsigned int freq)
-{
-	struct dpp_bootstrap_info *bi;
-
-	bi = os_zalloc(sizeof(*bi));
-	if (!bi)
-		return NULL;
-	bi->id = dpp_next_id(dpp);
-	bi->type = DPP_BOOTSTRAP_PKEX;
-	os_memcpy(bi->mac_addr, peer, ETH_ALEN);
-	bi->num_freq = 1;
-	bi->freq[0] = freq;
-	bi->curve = pkex->own_bi->curve;
-	bi->pubkey = pkex->peer_bootstrap_key;
-	pkex->peer_bootstrap_key = NULL;
-	if (dpp_bootstrap_key_hash(bi) < 0) {
-		dpp_bootstrap_info_free(bi);
-		return NULL;
-	}
-	dpp_pkex_free(pkex);
-	dl_list_add(&dpp->bootstrap, &bi->list);
-	return bi;
-}
-
-
 const char * dpp_bootstrap_get_uri(struct dpp_global *dpp, unsigned int id)
 {
 	struct dpp_bootstrap_info *bi;
@@ -10582,14 +3666,16 @@
 			   "num_freq=%u\n"
 			   "use_freq=%u\n"
 			   "curve=%s\n"
-			   "pkhash=%s\n",
+			   "pkhash=%s\n"
+			   "version=%d\n",
 			   dpp_bootstrap_type_txt(bi->type),
 			   MAC2STR(bi->mac_addr),
 			   bi->info ? bi->info : "",
 			   bi->num_freq,
 			   bi->num_freq == 1 ? bi->freq[0] : 0,
 			   bi->curve->name,
-			   pkhash);
+			   pkhash,
+			   bi->version);
 }
 
 
@@ -10897,68 +3983,20 @@
 }
 
 
-static void dpp_controller_conn_status_result_wait_timeout(void *eloop_ctx,
-							   void *timeout_ctx);
-
-
-static void dpp_connection_free(struct dpp_connection *conn)
+struct dpp_configurator * dpp_configurator_find_kid(struct dpp_global *dpp,
+						    const u8 *kid)
 {
-	if (conn->sock >= 0) {
-		wpa_printf(MSG_DEBUG, "DPP: Close Controller socket %d",
-			   conn->sock);
-		eloop_unregister_sock(conn->sock, EVENT_TYPE_READ);
-		eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
-		close(conn->sock);
-	}
-	eloop_cancel_timeout(dpp_controller_conn_status_result_wait_timeout,
-			     conn, NULL);
-	wpabuf_free(conn->msg);
-	wpabuf_free(conn->msg_out);
-	dpp_auth_deinit(conn->auth);
-	os_free(conn);
-}
-
-
-static void dpp_connection_remove(struct dpp_connection *conn)
-{
-	dl_list_del(&conn->list);
-	dpp_connection_free(conn);
-}
-
-
-static void dpp_tcp_init_flush(struct dpp_global *dpp)
-{
-	struct dpp_connection *conn, *tmp;
-
-	dl_list_for_each_safe(conn, tmp, &dpp->tcp_init, struct dpp_connection,
-			      list)
-		dpp_connection_remove(conn);
-}
-
-
-static void dpp_relay_controller_free(struct dpp_relay_controller *ctrl)
-{
-	struct dpp_connection *conn, *tmp;
-
-	dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection,
-			      list)
-		dpp_connection_remove(conn);
-	os_free(ctrl);
-}
-
-
-static void dpp_relay_flush_controllers(struct dpp_global *dpp)
-{
-	struct dpp_relay_controller *ctrl, *tmp;
+	struct dpp_configurator *conf;
 
 	if (!dpp)
-		return;
+		return NULL;
 
-	dl_list_for_each_safe(ctrl, tmp, &dpp->controllers,
-			      struct dpp_relay_controller, list) {
-		dl_list_del(&ctrl->list);
-		dpp_relay_controller_free(ctrl);
+	dl_list_for_each(conf, &dpp->configurator,
+			 struct dpp_configurator, list) {
+		if (os_memcmp(conf->kid_hash, kid, SHA256_MAC_LEN) == 0)
+			return conf;
 	}
+	return NULL;
 }
 
 #endif /* CONFIG_DPP2 */
@@ -11012,1301 +4050,6 @@
 
 
 #ifdef CONFIG_DPP2
-
-static void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx);
-static void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx);
-static void dpp_controller_auth_success(struct dpp_connection *conn,
-					int initiator);
-
-
-int dpp_relay_add_controller(struct dpp_global *dpp,
-			     struct dpp_relay_config *config)
-{
-	struct dpp_relay_controller *ctrl;
-
-	if (!dpp)
-		return -1;
-
-	ctrl = os_zalloc(sizeof(*ctrl));
-	if (!ctrl)
-		return -1;
-	dl_list_init(&ctrl->conn);
-	ctrl->global = dpp;
-	os_memcpy(&ctrl->ipaddr, config->ipaddr, sizeof(*config->ipaddr));
-	os_memcpy(ctrl->pkhash, config->pkhash, SHA256_MAC_LEN);
-	ctrl->cb_ctx = config->cb_ctx;
-	ctrl->tx = config->tx;
-	ctrl->gas_resp_tx = config->gas_resp_tx;
-	dl_list_add(&dpp->controllers, &ctrl->list);
-	return 0;
-}
-
-
-static struct dpp_relay_controller *
-dpp_relay_controller_get(struct dpp_global *dpp, const u8 *pkhash)
-{
-	struct dpp_relay_controller *ctrl;
-
-	if (!dpp)
-		return NULL;
-
-	dl_list_for_each(ctrl, &dpp->controllers, struct dpp_relay_controller,
-			 list) {
-		if (os_memcmp(pkhash, ctrl->pkhash, SHA256_MAC_LEN) == 0)
-			return ctrl;
-	}
-
-	return NULL;
-}
-
-
-static void dpp_controller_gas_done(struct dpp_connection *conn)
-{
-	struct dpp_authentication *auth = conn->auth;
-
-	if (auth->peer_version >= 2 &&
-	    auth->conf_resp_status == DPP_STATUS_OK) {
-		wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result");
-		auth->waiting_conf_result = 1;
-		return;
-	}
-
-	wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
-	dpp_connection_remove(conn);
-}
-
-
-static int dpp_tcp_send(struct dpp_connection *conn)
-{
-	int res;
-
-	if (!conn->msg_out) {
-		eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
-		conn->write_eloop = 0;
-		return -1;
-	}
-	res = send(conn->sock,
-		   wpabuf_head_u8(conn->msg_out) + conn->msg_out_pos,
-		   wpabuf_len(conn->msg_out) - conn->msg_out_pos, 0);
-	if (res < 0) {
-		wpa_printf(MSG_DEBUG, "DPP: Failed to send buffer: %s",
-			   strerror(errno));
-		dpp_connection_remove(conn);
-		return -1;
-	}
-
-	conn->msg_out_pos += res;
-	if (wpabuf_len(conn->msg_out) > conn->msg_out_pos) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: %u/%u bytes of message sent to Controller",
-			   (unsigned int) conn->msg_out_pos,
-			   (unsigned int) wpabuf_len(conn->msg_out));
-		if (!conn->write_eloop &&
-		    eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
-					dpp_conn_tx_ready, conn, NULL) == 0)
-			conn->write_eloop = 1;
-		return 1;
-	}
-
-	wpa_printf(MSG_DEBUG, "DPP: Full message sent over TCP");
-	wpabuf_free(conn->msg_out);
-	conn->msg_out = NULL;
-	conn->msg_out_pos = 0;
-	eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
-	conn->write_eloop = 0;
-	if (!conn->read_eloop &&
-	    eloop_register_sock(conn->sock, EVENT_TYPE_READ,
-				dpp_controller_rx, conn, NULL) == 0)
-		conn->read_eloop = 1;
-	if (conn->on_tcp_tx_complete_remove) {
-		dpp_connection_remove(conn);
-	} else if (conn->ctrl && conn->on_tcp_tx_complete_gas_done &&
-		   conn->auth) {
-		dpp_controller_gas_done(conn);
-	} else if (conn->on_tcp_tx_complete_auth_ok) {
-		conn->on_tcp_tx_complete_auth_ok = 0;
-		dpp_controller_auth_success(conn, 1);
-	}
-
-	return 0;
-}
-
-
-static int dpp_tcp_send_msg(struct dpp_connection *conn,
-			    const struct wpabuf *msg)
-{
-	wpabuf_free(conn->msg_out);
-	conn->msg_out_pos = 0;
-	conn->msg_out = wpabuf_alloc(4 + wpabuf_len(msg) - 1);
-	if (!conn->msg_out)
-		return -1;
-	wpabuf_put_be32(conn->msg_out, wpabuf_len(msg) - 1);
-	wpabuf_put_data(conn->msg_out, wpabuf_head_u8(msg) + 1,
-			wpabuf_len(msg) - 1);
-
-	if (dpp_tcp_send(conn) == 1) {
-		if (!conn->write_eloop) {
-			if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
-						dpp_conn_tx_ready,
-						conn, NULL) < 0)
-				return -1;
-			conn->write_eloop = 1;
-		}
-	}
-
-	return 0;
-}
-
-
-static void dpp_controller_start_gas_client(struct dpp_connection *conn)
-{
-	struct dpp_authentication *auth = conn->auth;
-	struct wpabuf *buf;
-	int netrole_ap = 0; /* TODO: make this configurable */
-
-	buf = dpp_build_conf_req_helper(auth, "Test", netrole_ap, NULL, NULL);
-	if (!buf) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: No configuration request data available");
-		return;
-	}
-
-	dpp_tcp_send_msg(conn, buf);
-	wpabuf_free(buf);
-}
-
-
-static void dpp_controller_auth_success(struct dpp_connection *conn,
-					int initiator)
-{
-	struct dpp_authentication *auth = conn->auth;
-
-	if (!auth)
-		return;
-
-	wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
-	wpa_msg(conn->global->msg_ctx, MSG_INFO,
-		DPP_EVENT_AUTH_SUCCESS "init=%d", initiator);
-#ifdef CONFIG_TESTING_OPTIONS
-	if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
-		wpa_printf(MSG_INFO,
-			   "DPP: TESTING - stop at Authentication Confirm");
-		if (auth->configurator) {
-			/* Prevent GAS response */
-			auth->auth_success = 0;
-		}
-		return;
-	}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-	if (!auth->configurator)
-		dpp_controller_start_gas_client(conn);
-}
-
-
-static void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx)
-{
-	struct dpp_connection *conn = eloop_ctx;
-
-	wpa_printf(MSG_DEBUG, "DPP: TCP socket %d ready for TX", sock);
-	dpp_tcp_send(conn);
-}
-
-
-static int dpp_ipaddr_to_sockaddr(struct sockaddr *addr, socklen_t *addrlen,
-				  const struct hostapd_ip_addr *ipaddr,
-				  int port)
-{
-	struct sockaddr_in *dst;
-#ifdef CONFIG_IPV6
-	struct sockaddr_in6 *dst6;
-#endif /* CONFIG_IPV6 */
-
-	switch (ipaddr->af) {
-	case AF_INET:
-		dst = (struct sockaddr_in *) addr;
-		os_memset(dst, 0, sizeof(*dst));
-		dst->sin_family = AF_INET;
-		dst->sin_addr.s_addr = ipaddr->u.v4.s_addr;
-		dst->sin_port = htons(port);
-		*addrlen = sizeof(*dst);
-		break;
-#ifdef CONFIG_IPV6
-	case AF_INET6:
-		dst6 = (struct sockaddr_in6 *) addr;
-		os_memset(dst6, 0, sizeof(*dst6));
-		dst6->sin6_family = AF_INET6;
-		os_memcpy(&dst6->sin6_addr, &ipaddr->u.v6,
-			  sizeof(struct in6_addr));
-		dst6->sin6_port = htons(port);
-		*addrlen = sizeof(*dst6);
-		break;
-#endif /* CONFIG_IPV6 */
-	default:
-		return -1;
-	}
-
-	return 0;
-}
-
-
-static struct dpp_connection *
-dpp_relay_new_conn(struct dpp_relay_controller *ctrl, const u8 *src,
-		   unsigned int freq)
-{
-	struct dpp_connection *conn;
-	struct sockaddr_storage addr;
-	socklen_t addrlen;
-	char txt[100];
-
-	if (dl_list_len(&ctrl->conn) >= 15) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Too many ongoing Relay connections to the Controller - cannot start a new one");
-		return NULL;
-	}
-
-	if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &addr, &addrlen,
-				   &ctrl->ipaddr, DPP_TCP_PORT) < 0)
-		return NULL;
-
-	conn = os_zalloc(sizeof(*conn));
-	if (!conn)
-		return NULL;
-
-	conn->global = ctrl->global;
-	conn->relay = ctrl;
-	os_memcpy(conn->mac_addr, src, ETH_ALEN);
-	conn->freq = freq;
-
-	conn->sock = socket(AF_INET, SOCK_STREAM, 0);
-	if (conn->sock < 0)
-		goto fail;
-	wpa_printf(MSG_DEBUG, "DPP: TCP relay socket %d connection to %s",
-		   conn->sock, hostapd_ip_txt(&ctrl->ipaddr, txt, sizeof(txt)));
-
-	if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
-		wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
-			   strerror(errno));
-		goto fail;
-	}
-
-	if (connect(conn->sock, (struct sockaddr *) &addr, addrlen) < 0) {
-		if (errno != EINPROGRESS) {
-			wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s",
-				   strerror(errno));
-			goto fail;
-		}
-
-		/*
-		 * Continue connecting in the background; eloop will call us
-		 * once the connection is ready (or failed).
-		 */
-	}
-
-	if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
-				dpp_conn_tx_ready, conn, NULL) < 0)
-		goto fail;
-	conn->write_eloop = 1;
-
-	/* TODO: eloop timeout to clear a connection if it does not complete
-	 * properly */
-
-	dl_list_add(&ctrl->conn, &conn->list);
-	return conn;
-fail:
-	dpp_connection_free(conn);
-	return NULL;
-}
-
-
-static struct wpabuf * dpp_tcp_encaps(const u8 *hdr, const u8 *buf, size_t len)
-{
-	struct wpabuf *msg;
-
-	msg = wpabuf_alloc(4 + 1 + DPP_HDR_LEN + len);
-	if (!msg)
-		return NULL;
-	wpabuf_put_be32(msg, 1 + DPP_HDR_LEN + len);
-	wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC);
-	wpabuf_put_data(msg, hdr, DPP_HDR_LEN);
-	wpabuf_put_data(msg, buf, len);
-	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg);
-	return msg;
-}
-
-
-static int dpp_relay_tx(struct dpp_connection *conn, const u8 *hdr,
-			const u8 *buf, size_t len)
-{
-	u8 type = hdr[DPP_HDR_LEN - 1];
-
-	wpa_printf(MSG_DEBUG,
-		   "DPP: Continue already established Relay/Controller connection for this session");
-	wpabuf_free(conn->msg_out);
-	conn->msg_out_pos = 0;
-	conn->msg_out = dpp_tcp_encaps(hdr, buf, len);
-	if (!conn->msg_out) {
-		dpp_connection_remove(conn);
-		return -1;
-	}
-
-	/* TODO: for proto ver 1, need to do remove connection based on GAS Resp
-	 * TX status */
-	if (type == DPP_PA_CONFIGURATION_RESULT)
-		conn->on_tcp_tx_complete_remove = 1;
-	dpp_tcp_send(conn);
-	return 0;
-}
-
-
-int dpp_relay_rx_action(struct dpp_global *dpp, const u8 *src, const u8 *hdr,
-			const u8 *buf, size_t len, unsigned int freq,
-			const u8 *i_bootstrap, const u8 *r_bootstrap)
-{
-	struct dpp_relay_controller *ctrl;
-	struct dpp_connection *conn;
-	u8 type = hdr[DPP_HDR_LEN - 1];
-
-	/* Check if there is an already started session for this peer and if so,
-	 * continue that session (send this over TCP) and return 0.
-	 */
-	if (type != DPP_PA_PEER_DISCOVERY_REQ &&
-	    type != DPP_PA_PEER_DISCOVERY_RESP &&
-	    type != DPP_PA_PRESENCE_ANNOUNCEMENT) {
-		dl_list_for_each(ctrl, &dpp->controllers,
-				 struct dpp_relay_controller, list) {
-			dl_list_for_each(conn, &ctrl->conn,
-					 struct dpp_connection, list) {
-				if (os_memcmp(src, conn->mac_addr,
-					      ETH_ALEN) == 0)
-					return dpp_relay_tx(conn, hdr, buf, len);
-			}
-		}
-	}
-
-	if (!r_bootstrap)
-		return -1;
-
-	if (type == DPP_PA_PRESENCE_ANNOUNCEMENT) {
-		/* TODO: Could send this to all configured Controllers. For now,
-		 * only the first Controller is supported. */
-		ctrl = dl_list_first(&dpp->controllers,
-				     struct dpp_relay_controller, list);
-	} else {
-		ctrl = dpp_relay_controller_get(dpp, r_bootstrap);
-	}
-	if (!ctrl)
-		return -1;
-
-	wpa_printf(MSG_DEBUG,
-		   "DPP: Authentication Request for a configured Controller");
-	conn = dpp_relay_new_conn(ctrl, src, freq);
-	if (!conn)
-		return -1;
-
-	conn->msg_out = dpp_tcp_encaps(hdr, buf, len);
-	if (!conn->msg_out) {
-		dpp_connection_remove(conn);
-		return -1;
-	}
-	/* Message will be sent in dpp_conn_tx_ready() */
-
-	return 0;
-}
-
-
-int dpp_relay_rx_gas_req(struct dpp_global *dpp, const u8 *src, const u8 *data,
-			 size_t data_len)
-{
-	struct dpp_relay_controller *ctrl;
-	struct dpp_connection *conn, *found = NULL;
-	struct wpabuf *msg;
-
-	/* Check if there is a successfully completed authentication for this
-	 * and if so, continue that session (send this over TCP) and return 0.
-	 */
-	dl_list_for_each(ctrl, &dpp->controllers,
-			 struct dpp_relay_controller, list) {
-		if (found)
-			break;
-		dl_list_for_each(conn, &ctrl->conn,
-				 struct dpp_connection, list) {
-			if (os_memcmp(src, conn->mac_addr,
-				      ETH_ALEN) == 0) {
-				found = conn;
-				break;
-			}
-		}
-	}
-
-	if (!found)
-		return -1;
-
-	msg = wpabuf_alloc(4 + 1 + data_len);
-	if (!msg)
-		return -1;
-	wpabuf_put_be32(msg, 1 + data_len);
-	wpabuf_put_u8(msg, WLAN_PA_GAS_INITIAL_REQ);
-	wpabuf_put_data(msg, data, data_len);
-	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg);
-
-	wpabuf_free(conn->msg_out);
-	conn->msg_out_pos = 0;
-	conn->msg_out = msg;
-	dpp_tcp_send(conn);
-	return 0;
-}
-
-
-static void dpp_controller_free(struct dpp_controller *ctrl)
-{
-	struct dpp_connection *conn, *tmp;
-
-	if (!ctrl)
-		return;
-
-	dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection,
-			      list)
-		dpp_connection_remove(conn);
-
-	if (ctrl->sock >= 0) {
-		close(ctrl->sock);
-		eloop_unregister_sock(ctrl->sock, EVENT_TYPE_READ);
-	}
-	os_free(ctrl->configurator_params);
-	os_free(ctrl);
-}
-
-
-static int dpp_controller_rx_auth_req(struct dpp_connection *conn,
-				      const u8 *hdr, const u8 *buf, size_t len)
-{
-	const u8 *r_bootstrap, *i_bootstrap;
-	u16 r_bootstrap_len, i_bootstrap_len;
-	struct dpp_bootstrap_info *own_bi = NULL, *peer_bi = NULL;
-
-	if (!conn->ctrl)
-		return 0;
-
-	wpa_printf(MSG_DEBUG, "DPP: Authentication Request");
-
-	r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
-				   &r_bootstrap_len);
-	if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
-		wpa_printf(MSG_INFO,
-			   "Missing or invalid required Responder Bootstrapping Key Hash attribute");
-		return -1;
-	}
-	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
-		    r_bootstrap, r_bootstrap_len);
-
-	i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
-				   &i_bootstrap_len);
-	if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) {
-		wpa_printf(MSG_INFO,
-			   "Missing or invalid required Initiator Bootstrapping Key Hash attribute");
-		return -1;
-	}
-	wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash",
-		    i_bootstrap, i_bootstrap_len);
-
-	/* Try to find own and peer bootstrapping key matches based on the
-	 * received hash values */
-	dpp_bootstrap_find_pair(conn->ctrl->global, i_bootstrap, r_bootstrap,
-				&own_bi, &peer_bi);
-	if (!own_bi) {
-		wpa_printf(MSG_INFO,
-			"No matching own bootstrapping key found - ignore message");
-		return -1;
-	}
-
-	if (conn->auth) {
-		wpa_printf(MSG_INFO,
-			   "Already in DPP authentication exchange - ignore new one");
-		return 0;
-	}
-
-	conn->auth = dpp_auth_req_rx(conn->ctrl->global,
-				     conn->ctrl->global->msg_ctx,
-				     conn->ctrl->allowed_roles,
-				     conn->ctrl->qr_mutual,
-				     peer_bi, own_bi, -1, hdr, buf, len);
-	if (!conn->auth) {
-		wpa_printf(MSG_DEBUG, "DPP: No response generated");
-		return -1;
-	}
-
-	if (dpp_set_configurator(conn->auth,
-				 conn->ctrl->configurator_params) < 0) {
-		dpp_connection_remove(conn);
-		return -1;
-	}
-
-	return dpp_tcp_send_msg(conn, conn->auth->resp_msg);
-}
-
-
-static int dpp_controller_rx_auth_resp(struct dpp_connection *conn,
-				       const u8 *hdr, const u8 *buf, size_t len)
-{
-	struct dpp_authentication *auth = conn->auth;
-	struct wpabuf *msg;
-	int res;
-
-	if (!auth)
-		return -1;
-
-	wpa_printf(MSG_DEBUG, "DPP: Authentication Response");
-
-	msg = dpp_auth_resp_rx(auth, hdr, buf, len);
-	if (!msg) {
-		if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
-			wpa_printf(MSG_DEBUG,
-				   "DPP: Start wait for full response");
-			return -1;
-		}
-		wpa_printf(MSG_DEBUG, "DPP: No confirm generated");
-		dpp_connection_remove(conn);
-		return -1;
-	}
-
-	conn->on_tcp_tx_complete_auth_ok = 1;
-	res = dpp_tcp_send_msg(conn, msg);
-	wpabuf_free(msg);
-	return res;
-}
-
-
-static int dpp_controller_rx_auth_conf(struct dpp_connection *conn,
-				       const u8 *hdr, const u8 *buf, size_t len)
-{
-	struct dpp_authentication *auth = conn->auth;
-
-	wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation");
-
-	if (!auth) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: No DPP Authentication in progress - drop");
-		return -1;
-	}
-
-	if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) {
-		wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
-		return -1;
-	}
-
-	dpp_controller_auth_success(conn, 0);
-	return 0;
-}
-
-
-static void dpp_controller_conn_status_result_wait_timeout(void *eloop_ctx,
-							   void *timeout_ctx)
-{
-	struct dpp_connection *conn = eloop_ctx;
-
-	if (!conn->auth->waiting_conf_result)
-		return;
-
-	wpa_printf(MSG_DEBUG,
-		   "DPP: Timeout while waiting for Connection Status Result");
-	wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
-		DPP_EVENT_CONN_STATUS_RESULT "timeout");
-	dpp_connection_remove(conn);
-}
-
-
-static int dpp_controller_rx_conf_result(struct dpp_connection *conn,
-					 const u8 *hdr, const u8 *buf,
-					 size_t len)
-{
-	struct dpp_authentication *auth = conn->auth;
-	enum dpp_status_error status;
-
-	if (!conn->ctrl)
-		return 0;
-
-	wpa_printf(MSG_DEBUG, "DPP: Configuration Result");
-
-	if (!auth || !auth->waiting_conf_result) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: No DPP Configuration waiting for result - drop");
-		return -1;
-	}
-
-	status = dpp_conf_result_rx(auth, hdr, buf, len);
-	if (status == DPP_STATUS_OK && auth->send_conn_status) {
-		wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
-			DPP_EVENT_CONF_SENT "wait_conn_status=1");
-		wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result");
-		eloop_cancel_timeout(
-			dpp_controller_conn_status_result_wait_timeout,
-			conn, NULL);
-		eloop_register_timeout(
-			16, 0, dpp_controller_conn_status_result_wait_timeout,
-			conn, NULL);
-		return 0;
-	}
-	if (status == DPP_STATUS_OK)
-		wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
-			DPP_EVENT_CONF_SENT);
-	else
-		wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
-			DPP_EVENT_CONF_FAILED);
-	return -1; /* to remove the completed connection */
-}
-
-
-static int dpp_controller_rx_conn_status_result(struct dpp_connection *conn,
-						const u8 *hdr, const u8 *buf,
-						size_t len)
-{
-	struct dpp_authentication *auth = conn->auth;
-	enum dpp_status_error status;
-	u8 ssid[SSID_MAX_LEN];
-	size_t ssid_len = 0;
-	char *channel_list = NULL;
-
-	if (!conn->ctrl)
-		return 0;
-
-	wpa_printf(MSG_DEBUG, "DPP: Connection Status Result");
-
-	if (!auth || !auth->waiting_conn_status_result) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: No DPP Configuration waiting for connection status result - drop");
-		return -1;
-	}
-
-	status = dpp_conn_status_result_rx(auth, hdr, buf, len,
-					   ssid, &ssid_len, &channel_list);
-	wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
-		DPP_EVENT_CONN_STATUS_RESULT
-		"result=%d ssid=%s channel_list=%s",
-		status, wpa_ssid_txt(ssid, ssid_len),
-		channel_list ? channel_list : "N/A");
-	os_free(channel_list);
-	return -1; /* to remove the completed connection */
-}
-
-
-static int dpp_controller_rx_presence_announcement(struct dpp_connection *conn,
-						   const u8 *hdr, const u8 *buf,
-						   size_t len)
-{
-	const u8 *r_bootstrap;
-	u16 r_bootstrap_len;
-	struct dpp_bootstrap_info *peer_bi;
-	struct dpp_authentication *auth;
-	struct dpp_global *dpp = conn->ctrl->global;
-
-	if (conn->auth) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Ignore Presence Announcement during ongoing Authentication");
-		return -1;
-	}
-
-	wpa_printf(MSG_DEBUG, "DPP: Presence Announcement");
-
-	r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
-				   &r_bootstrap_len);
-	if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
-		wpa_msg(dpp->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
-			"Missing or invalid required Responder Bootstrapping Key Hash attribute");
-		return -1;
-	}
-	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
-		    r_bootstrap, r_bootstrap_len);
-	peer_bi = dpp_bootstrap_find_chirp(dpp, r_bootstrap);
-	if (!peer_bi) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: No matching bootstrapping information found");
-		return -1;
-	}
-
-	auth = dpp_auth_init(dpp, dpp->msg_ctx, peer_bi, NULL,
-			     DPP_CAPAB_CONFIGURATOR, -1, NULL, 0);
-	if (!auth)
-		return -1;
-	if (dpp_set_configurator(conn->auth,
-				 conn->ctrl->configurator_params) < 0) {
-		dpp_auth_deinit(auth);
-		dpp_connection_remove(conn);
-		return -1;
-	}
-
-	conn->auth = auth;
-	return dpp_tcp_send_msg(conn, conn->auth->req_msg);
-}
-
-
-static int dpp_controller_rx_action(struct dpp_connection *conn, const u8 *msg,
-				    size_t len)
-{
-	const u8 *pos, *end;
-	u8 type;
-
-	wpa_printf(MSG_DEBUG, "DPP: Received DPP Action frame over TCP");
-	pos = msg;
-	end = msg + len;
-
-	if (end - pos < DPP_HDR_LEN ||
-	    WPA_GET_BE24(pos) != OUI_WFA ||
-	    pos[3] != DPP_OUI_TYPE) {
-		wpa_printf(MSG_DEBUG, "DPP: Unrecognized header");
-		return -1;
-	}
-
-	if (pos[4] != 1) {
-		wpa_printf(MSG_DEBUG, "DPP: Unsupported Crypto Suite %u",
-			   pos[4]);
-		return -1;
-	}
-	type = pos[5];
-	wpa_printf(MSG_DEBUG, "DPP: Received message type %u", type);
-	pos += DPP_HDR_LEN;
-
-	wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes",
-		    pos, end - pos);
-	if (dpp_check_attrs(pos, end - pos) < 0)
-		return -1;
-
-	if (conn->relay) {
-		wpa_printf(MSG_DEBUG, "DPP: Relay - send over WLAN");
-		conn->relay->tx(conn->relay->cb_ctx, conn->mac_addr,
-				conn->freq, msg, len);
-		return 0;
-	}
-
-	switch (type) {
-	case DPP_PA_AUTHENTICATION_REQ:
-		return dpp_controller_rx_auth_req(conn, msg, pos, end - pos);
-	case DPP_PA_AUTHENTICATION_RESP:
-		return dpp_controller_rx_auth_resp(conn, msg, pos, end - pos);
-	case DPP_PA_AUTHENTICATION_CONF:
-		return dpp_controller_rx_auth_conf(conn, msg, pos, end - pos);
-	case DPP_PA_CONFIGURATION_RESULT:
-		return dpp_controller_rx_conf_result(conn, msg, pos, end - pos);
-	case DPP_PA_CONNECTION_STATUS_RESULT:
-		return dpp_controller_rx_conn_status_result(conn, msg, pos,
-							    end - pos);
-	case DPP_PA_PRESENCE_ANNOUNCEMENT:
-		return dpp_controller_rx_presence_announcement(conn, msg, pos,
-							       end - pos);
-	default:
-		/* TODO: missing messages types */
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Unsupported frame subtype %d", type);
-		return -1;
-	}
-}
-
-
-static int dpp_controller_rx_gas_req(struct dpp_connection *conn, const u8 *msg,
-				     size_t len)
-{
-	const u8 *pos, *end, *next;
-	u8 dialog_token;
-	const u8 *adv_proto;
-	u16 slen;
-	struct wpabuf *resp, *buf;
-	struct dpp_authentication *auth = conn->auth;
-
-	if (len < 1 + 2)
-		return -1;
-
-	wpa_printf(MSG_DEBUG,
-		   "DPP: Received DPP Configuration Request over TCP");
-
-	if (!conn->ctrl || !auth || !auth->auth_success) {
-		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
-		return -1;
-	}
-
-	pos = msg;
-	end = msg + len;
-
-	dialog_token = *pos++;
-	adv_proto = pos++;
-	slen = *pos++;
-	if (*adv_proto != WLAN_EID_ADV_PROTO ||
-	    slen > end - pos || slen < 2)
-		return -1;
-
-	next = pos + slen;
-	pos++; /* skip QueryRespLenLimit and PAME-BI */
-
-	if (slen != 8 || *pos != WLAN_EID_VENDOR_SPECIFIC ||
-	    pos[1] != 5 || WPA_GET_BE24(&pos[2]) != OUI_WFA ||
-	    pos[5] != DPP_OUI_TYPE || pos[6] != 0x01)
-		return -1;
-
-	pos = next;
-	/* Query Request */
-	if (end - pos < 2)
-		return -1;
-	slen = WPA_GET_LE16(pos);
-	pos += 2;
-	if (slen > end - pos)
-		return -1;
-
-	resp = dpp_conf_req_rx(auth, pos, slen);
-	if (!resp)
-		return -1;
-
-	buf = wpabuf_alloc(4 + 18 + wpabuf_len(resp));
-	if (!buf) {
-		wpabuf_free(resp);
-		return -1;
-	}
-
-	wpabuf_put_be32(buf, 18 + wpabuf_len(resp));
-
-	wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_RESP);
-	wpabuf_put_u8(buf, dialog_token);
-	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
-	wpabuf_put_le16(buf, 0); /* GAS Comeback Delay */
-
-	dpp_write_adv_proto(buf);
-	dpp_write_gas_query(buf, resp);
-	wpabuf_free(resp);
-
-	/* Send Config Response over TCP; GAS fragmentation is taken care of by
-	 * the Relay */
-	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", buf);
-	wpabuf_free(conn->msg_out);
-	conn->msg_out_pos = 0;
-	conn->msg_out = buf;
-	conn->on_tcp_tx_complete_gas_done = 1;
-	dpp_tcp_send(conn);
-	return 0;
-}
-
-
-static int dpp_tcp_rx_gas_resp(struct dpp_connection *conn, struct wpabuf *resp)
-{
-	struct dpp_authentication *auth = conn->auth;
-	int res;
-	struct wpabuf *msg;
-	enum dpp_status_error status;
-
-	wpa_printf(MSG_DEBUG,
-		   "DPP: Configuration Response for local stack from TCP");
-
-	res = dpp_conf_resp_rx(auth, resp);
-	wpabuf_free(resp);
-	if (res < 0) {
-		wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
-		return -1;
-	}
-
-	if (conn->global->process_conf_obj)
-		res = conn->global->process_conf_obj(conn->global->cb_ctx,
-						     auth);
-	else
-		res = 0;
-
-	if (auth->peer_version < 2 || auth->conf_resp_status != DPP_STATUS_OK)
-		return -1;
-
-	wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result");
-	status = res < 0 ? DPP_STATUS_CONFIG_REJECTED : DPP_STATUS_OK;
-	msg = dpp_build_conf_result(auth, status);
-	if (!msg)
-		return -1;
-
-	conn->on_tcp_tx_complete_remove = 1;
-	res = dpp_tcp_send_msg(conn, msg);
-	wpabuf_free(msg);
-
-	/* This exchange will be terminated in the TX status handler */
-
-	return res;
-}
-
-
-static int dpp_rx_gas_resp(struct dpp_connection *conn, const u8 *msg,
-			   size_t len)
-{
-	struct wpabuf *buf;
-	u8 dialog_token;
-	const u8 *pos, *end, *next, *adv_proto;
-	u16 status, slen;
-
-	if (len < 5 + 2)
-		return -1;
-
-	wpa_printf(MSG_DEBUG,
-		   "DPP: Received DPP Configuration Response over TCP");
-
-	pos = msg;
-	end = msg + len;
-
-	dialog_token = *pos++;
-	status = WPA_GET_LE16(pos);
-	if (status != WLAN_STATUS_SUCCESS) {
-		wpa_printf(MSG_DEBUG, "DPP: Unexpected Status Code %u", status);
-		return -1;
-	}
-	pos += 2;
-	pos += 2; /* ignore GAS Comeback Delay */
-
-	adv_proto = pos++;
-	slen = *pos++;
-	if (*adv_proto != WLAN_EID_ADV_PROTO ||
-	    slen > end - pos || slen < 2)
-		return -1;
-
-	next = pos + slen;
-	pos++; /* skip QueryRespLenLimit and PAME-BI */
-
-	if (slen != 8 || *pos != WLAN_EID_VENDOR_SPECIFIC ||
-	    pos[1] != 5 || WPA_GET_BE24(&pos[2]) != OUI_WFA ||
-	    pos[5] != DPP_OUI_TYPE || pos[6] != 0x01)
-		return -1;
-
-	pos = next;
-	/* Query Response */
-	if (end - pos < 2)
-		return -1;
-	slen = WPA_GET_LE16(pos);
-	pos += 2;
-	if (slen > end - pos)
-		return -1;
-
-	buf = wpabuf_alloc(slen);
-	if (!buf)
-		return -1;
-	wpabuf_put_data(buf, pos, slen);
-
-	if (!conn->relay && !conn->ctrl)
-		return dpp_tcp_rx_gas_resp(conn, buf);
-
-	if (!conn->relay) {
-		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
-		wpabuf_free(buf);
-		return -1;
-	}
-	wpa_printf(MSG_DEBUG, "DPP: Relay - send over WLAN");
-	conn->relay->gas_resp_tx(conn->relay->cb_ctx, conn->mac_addr,
-				 dialog_token, 0, buf);
-
-	return 0;
-}
-
-
-static void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx)
-{
-	struct dpp_connection *conn = eloop_ctx;
-	int res;
-	const u8 *pos;
-
-	wpa_printf(MSG_DEBUG, "DPP: TCP data available for reading (sock %d)",
-		   sd);
-
-	if (conn->msg_len_octets < 4) {
-		u32 msglen;
-
-		res = recv(sd, &conn->msg_len[conn->msg_len_octets],
-			   4 - conn->msg_len_octets, 0);
-		if (res < 0) {
-			wpa_printf(MSG_DEBUG, "DPP: recv failed: %s",
-				   strerror(errno));
-			dpp_connection_remove(conn);
-			return;
-		}
-		if (res == 0) {
-			wpa_printf(MSG_DEBUG,
-				   "DPP: No more data available over TCP");
-			dpp_connection_remove(conn);
-			return;
-		}
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Received %d/%d octet(s) of message length field",
-			   res, (int) (4 - conn->msg_len_octets));
-		conn->msg_len_octets += res;
-
-		if (conn->msg_len_octets < 4) {
-			wpa_printf(MSG_DEBUG,
-				   "DPP: Need %d more octets of message length field",
-				   (int) (4 - conn->msg_len_octets));
-			return;
-		}
-
-		msglen = WPA_GET_BE32(conn->msg_len);
-		wpa_printf(MSG_DEBUG, "DPP: Message length: %u", msglen);
-		if (msglen > 65535) {
-			wpa_printf(MSG_INFO, "DPP: Unexpectedly long message");
-			dpp_connection_remove(conn);
-			return;
-		}
-
-		wpabuf_free(conn->msg);
-		conn->msg = wpabuf_alloc(msglen);
-	}
-
-	if (!conn->msg) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: No buffer available for receiving the message");
-		dpp_connection_remove(conn);
-		return;
-	}
-
-	wpa_printf(MSG_DEBUG, "DPP: Need %u more octets of message payload",
-		   (unsigned int) wpabuf_tailroom(conn->msg));
-
-	res = recv(sd, wpabuf_put(conn->msg, 0), wpabuf_tailroom(conn->msg), 0);
-	if (res < 0) {
-		wpa_printf(MSG_DEBUG, "DPP: recv failed: %s", strerror(errno));
-		dpp_connection_remove(conn);
-		return;
-	}
-	if (res == 0) {
-		wpa_printf(MSG_DEBUG, "DPP: No more data available over TCP");
-		dpp_connection_remove(conn);
-		return;
-	}
-	wpa_printf(MSG_DEBUG, "DPP: Received %d octets", res);
-	wpabuf_put(conn->msg, res);
-
-	if (wpabuf_tailroom(conn->msg) > 0) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Need %u more octets of message payload",
-			   (unsigned int) wpabuf_tailroom(conn->msg));
-		return;
-	}
-
-	conn->msg_len_octets = 0;
-	wpa_hexdump_buf(MSG_DEBUG, "DPP: Received TCP message", conn->msg);
-	if (wpabuf_len(conn->msg) < 1) {
-		dpp_connection_remove(conn);
-		return;
-	}
-
-	pos = wpabuf_head(conn->msg);
-	switch (*pos) {
-	case WLAN_PA_VENDOR_SPECIFIC:
-		if (dpp_controller_rx_action(conn, pos + 1,
-					     wpabuf_len(conn->msg) - 1) < 0)
-			dpp_connection_remove(conn);
-		break;
-	case WLAN_PA_GAS_INITIAL_REQ:
-		if (dpp_controller_rx_gas_req(conn, pos + 1,
-					      wpabuf_len(conn->msg) - 1) < 0)
-			dpp_connection_remove(conn);
-		break;
-	case WLAN_PA_GAS_INITIAL_RESP:
-		if (dpp_rx_gas_resp(conn, pos + 1,
-				    wpabuf_len(conn->msg) - 1) < 0)
-			dpp_connection_remove(conn);
-		break;
-	default:
-		wpa_printf(MSG_DEBUG, "DPP: Ignore unsupported message type %u",
-			   *pos);
-		break;
-	}
-}
-
-
-static void dpp_controller_tcp_cb(int sd, void *eloop_ctx, void *sock_ctx)
-{
-	struct dpp_controller *ctrl = eloop_ctx;
-	struct sockaddr_in addr;
-	socklen_t addr_len = sizeof(addr);
-	int fd;
-	struct dpp_connection *conn;
-
-	wpa_printf(MSG_DEBUG, "DPP: New TCP connection");
-
-	fd = accept(ctrl->sock, (struct sockaddr *) &addr, &addr_len);
-	if (fd < 0) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Failed to accept new connection: %s",
-			   strerror(errno));
-		return;
-	}
-	wpa_printf(MSG_DEBUG, "DPP: Connection from %s:%d",
-		   inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
-
-	conn = os_zalloc(sizeof(*conn));
-	if (!conn)
-		goto fail;
-
-	conn->global = ctrl->global;
-	conn->ctrl = ctrl;
-	conn->sock = fd;
-
-	if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
-		wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
-			   strerror(errno));
-		goto fail;
-	}
-
-	if (eloop_register_sock(conn->sock, EVENT_TYPE_READ,
-				dpp_controller_rx, conn, NULL) < 0)
-		goto fail;
-	conn->read_eloop = 1;
-
-	/* TODO: eloop timeout to expire connections that do not complete in
-	 * reasonable time */
-	dl_list_add(&ctrl->conn, &conn->list);
-	return;
-
-fail:
-	close(fd);
-	os_free(conn);
-}
-
-
-int dpp_tcp_init(struct dpp_global *dpp, struct dpp_authentication *auth,
-		 const struct hostapd_ip_addr *addr, int port)
-{
-	struct dpp_connection *conn;
-	struct sockaddr_storage saddr;
-	socklen_t addrlen;
-	const u8 *hdr, *pos, *end;
-	char txt[100];
-
-	wpa_printf(MSG_DEBUG, "DPP: Initialize TCP connection to %s port %d",
-		   hostapd_ip_txt(addr, txt, sizeof(txt)), port);
-	if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &saddr, &addrlen,
-				   addr, port) < 0) {
-		dpp_auth_deinit(auth);
-		return -1;
-	}
-
-	conn = os_zalloc(sizeof(*conn));
-	if (!conn) {
-		dpp_auth_deinit(auth);
-		return -1;
-	}
-
-	conn->global = dpp;
-	conn->auth = auth;
-	conn->sock = socket(AF_INET, SOCK_STREAM, 0);
-	if (conn->sock < 0)
-		goto fail;
-
-	if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
-		wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
-			   strerror(errno));
-		goto fail;
-	}
-
-	if (connect(conn->sock, (struct sockaddr *) &saddr, addrlen) < 0) {
-		if (errno != EINPROGRESS) {
-			wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s",
-				   strerror(errno));
-			goto fail;
-		}
-
-		/*
-		 * Continue connecting in the background; eloop will call us
-		 * once the connection is ready (or failed).
-		 */
-	}
-
-	if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
-				dpp_conn_tx_ready, conn, NULL) < 0)
-		goto fail;
-	conn->write_eloop = 1;
-
-	hdr = wpabuf_head(auth->req_msg);
-	end = hdr + wpabuf_len(auth->req_msg);
-	hdr += 2; /* skip Category and Actiom */
-	pos = hdr + DPP_HDR_LEN;
-	conn->msg_out = dpp_tcp_encaps(hdr, pos, end - pos);
-	if (!conn->msg_out)
-		goto fail;
-	/* Message will be sent in dpp_conn_tx_ready() */
-
-	/* TODO: eloop timeout to clear a connection if it does not complete
-	 * properly */
-	dl_list_add(&dpp->tcp_init, &conn->list);
-	return 0;
-fail:
-	dpp_connection_free(conn);
-	return -1;
-}
-
-
-int dpp_controller_start(struct dpp_global *dpp,
-			 struct dpp_controller_config *config)
-{
-	struct dpp_controller *ctrl;
-	int on = 1;
-	struct sockaddr_in sin;
-	int port;
-
-	if (!dpp || dpp->controller)
-		return -1;
-
-	ctrl = os_zalloc(sizeof(*ctrl));
-	if (!ctrl)
-		return -1;
-	ctrl->global = dpp;
-	if (config->configurator_params)
-		ctrl->configurator_params =
-			os_strdup(config->configurator_params);
-	dl_list_init(&ctrl->conn);
-	/* TODO: configure these somehow */
-	ctrl->allowed_roles = DPP_CAPAB_ENROLLEE | DPP_CAPAB_CONFIGURATOR;
-	ctrl->qr_mutual = 0;
-
-	ctrl->sock = socket(AF_INET, SOCK_STREAM, 0);
-	if (ctrl->sock < 0)
-		goto fail;
-
-	if (setsockopt(ctrl->sock, SOL_SOCKET, SO_REUSEADDR,
-		       &on, sizeof(on)) < 0) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: setsockopt(SO_REUSEADDR) failed: %s",
-			   strerror(errno));
-		/* try to continue anyway */
-	}
-
-	if (fcntl(ctrl->sock, F_SETFL, O_NONBLOCK) < 0) {
-		wpa_printf(MSG_INFO, "DPP: fnctl(O_NONBLOCK) failed: %s",
-			   strerror(errno));
-		goto fail;
-	}
-
-	/* TODO: IPv6 */
-	os_memset(&sin, 0, sizeof(sin));
-	sin.sin_family = AF_INET;
-	sin.sin_addr.s_addr = INADDR_ANY;
-	port = config->tcp_port ? config->tcp_port : DPP_TCP_PORT;
-	sin.sin_port = htons(port);
-	if (bind(ctrl->sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
-		wpa_printf(MSG_INFO,
-			   "DPP: Failed to bind Controller TCP port: %s",
-			   strerror(errno));
-		goto fail;
-	}
-	if (listen(ctrl->sock, 10 /* max backlog */) < 0 ||
-	    fcntl(ctrl->sock, F_SETFL, O_NONBLOCK) < 0 ||
-	    eloop_register_sock(ctrl->sock, EVENT_TYPE_READ,
-				dpp_controller_tcp_cb, ctrl, NULL))
-		goto fail;
-
-	dpp->controller = ctrl;
-	wpa_printf(MSG_DEBUG, "DPP: Controller started on TCP port %d", port);
-	return 0;
-fail:
-	dpp_controller_free(ctrl);
-	return -1;
-}
-
-
-void dpp_controller_stop(struct dpp_global *dpp)
-{
-	if (dpp) {
-		dpp_controller_free(dpp->controller);
-		dpp->controller = NULL;
-	}
-}
-
-
 struct wpabuf * dpp_build_presence_announcement(struct dpp_bootstrap_info *bi)
 {
 	struct wpabuf *msg;
@@ -12323,5 +4066,4 @@
 			"DPP: Presence Announcement frame attributes", msg);
 	return msg;
 }
-
 #endif /* CONFIG_DPP2 */
diff --git a/src/common/dpp.h b/src/common/dpp.h
index 585d398..d77762e 100644
--- a/src/common/dpp.h
+++ b/src/common/dpp.h
@@ -20,6 +20,18 @@
 struct crypto_ecdh;
 struct hostapd_ip_addr;
 struct dpp_global;
+struct json_token;
+
+#ifdef CONFIG_TESTING_OPTIONS
+#define DPP_VERSION (dpp_version_override)
+extern int dpp_version_override;
+#else /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_DPP2
+#define DPP_VERSION 2
+#else
+#define DPP_VERSION 1
+#endif
+#endif /* CONFIG_TESTING_OPTIONS */
 
 #define DPP_HDR_LEN (4 + 2) /* OUI, OUI Type, Crypto Suite, DPP frame type */
 #define DPP_TCP_PORT 7871
@@ -94,6 +106,12 @@
 	DPP_STATUS_CSR_BAD = 13,
 };
 
+/* DPP Reconfig Flags object - connectorKey values */
+enum dpp_connector_key {
+	DPP_CONFIG_REUSEKEY = 0,
+	DPP_CONFIG_REPLACEKEY = 1,
+};
+
 #define DPP_CAPAB_ENROLLEE BIT(0)
 #define DPP_CAPAB_CONFIGURATOR BIT(1)
 #define DPP_CAPAB_ROLE_MASK (BIT(0) | BIT(1))
@@ -131,6 +149,7 @@
 	char *pk;
 	unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ];
 	unsigned int num_freq;
+	u8 version;
 	int own;
 	EVP_PKEY *pubkey;
 	u8 pubkey_hash[SHA256_MAC_LEN];
@@ -224,6 +243,8 @@
 	struct dpp_bootstrap_info *tmp_own_bi;
 	u8 waiting_pubkey_hash[SHA256_MAC_LEN];
 	int response_pending;
+	int reconfig;
+	enum dpp_connector_key reconfig_connector_key;
 	enum dpp_status_error auth_resp_status;
 	enum dpp_status_error conf_resp_status;
 	u8 peer_mac_addr[ETH_ALEN];
@@ -234,8 +255,11 @@
 	u8 r_capab;
 	EVP_PKEY *own_protocol_key;
 	EVP_PKEY *peer_protocol_key;
+	EVP_PKEY *reconfig_old_protocol_key;
 	struct wpabuf *req_msg;
 	struct wpabuf *resp_msg;
+	struct wpabuf *reconfig_req_msg;
+	struct wpabuf *reconfig_resp_msg;
 	/* Intersection of possible frequencies for initiating DPP
 	 * Authentication exchange */
 	unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ];
@@ -253,6 +277,7 @@
 	u8 k1[DPP_MAX_HASH_LEN];
 	u8 k2[DPP_MAX_HASH_LEN];
 	u8 ke[DPP_MAX_HASH_LEN];
+	u8 bk[DPP_MAX_HASH_LEN];
 	int initiator;
 	int waiting_auth_resp;
 	int waiting_auth_conf;
@@ -265,6 +290,7 @@
 	int waiting_conf_result;
 	int waiting_conn_status_result;
 	int auth_success;
+	bool reconfig_success;
 	struct wpabuf *conf_req;
 	const struct wpabuf *conf_resp; /* owned by GAS server */
 	struct dpp_configuration *conf_ap;
@@ -292,6 +318,7 @@
 	int conn_status_requested;
 	int akm_use_selector;
 	int configurator_set;
+	u8 transaction_id;
 #ifdef CONFIG_TESTING_OPTIONS
 	char *config_obj_override;
 	char *discovery_override;
@@ -307,8 +334,11 @@
 	unsigned int id;
 	int own;
 	EVP_PKEY *csign;
+	u8 kid_hash[SHA256_MAC_LEN];
 	char *kid;
 	const struct dpp_curve_params *curve;
+	char *connector; /* own Connector for reconfiguration */
+	EVP_PKEY *connector_key;
 };
 
 struct dpp_introduction {
@@ -331,6 +361,7 @@
 struct dpp_controller_config {
 	const char *configurator_params;
 	int tcp_port;
+	u8 allowed_roles;
 };
 
 #ifdef CONFIG_TESTING_OPTIONS
@@ -591,6 +622,8 @@
 				char *buf, size_t buflen);
 int dpp_configurator_from_backup(struct dpp_global *dpp,
 				 struct dpp_asymmetric_key *key);
+struct dpp_configurator * dpp_configurator_find_kid(struct dpp_global *dpp,
+						    const u8 *kid);
 int dpp_relay_add_controller(struct dpp_global *dpp,
 			     struct dpp_relay_config *config);
 int dpp_relay_rx_action(struct dpp_global *dpp, const u8 *src, const u8 *hdr,
@@ -616,5 +649,25 @@
 void dpp_global_clear(struct dpp_global *dpp);
 void dpp_global_deinit(struct dpp_global *dpp);
 
+/* dpp_reconfig.c */
+
+struct wpabuf * dpp_build_reconfig_announcement(const u8 *csign_key,
+						size_t csign_key_len);
+struct dpp_authentication *
+dpp_reconfig_init(struct dpp_global *dpp, void *msg_ctx,
+		  struct dpp_configurator *conf, unsigned int freq);
+struct dpp_authentication *
+dpp_reconfig_auth_req_rx(struct dpp_global *dpp, void *msg_ctx,
+			 const char *own_connector,
+			 const u8 *net_access_key, size_t net_access_key_len,
+			 const u8 *csign_key, size_t csign_key_len,
+			 unsigned int freq, const u8 *hdr,
+			 const u8 *attr_start, size_t attr_len);
+struct wpabuf *
+dpp_reconfig_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
+			  const u8 *attr_start, size_t attr_len);
+int dpp_reconfig_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
+			      const u8 *attr_start, size_t attr_len);
+
 #endif /* CONFIG_DPP */
 #endif /* DPP_H */
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;
+}
diff --git a/src/common/dpp_backup.c b/src/common/dpp_backup.c
new file mode 100644
index 0000000..c675c42
--- /dev/null
+++ b/src/common/dpp_backup.c
@@ -0,0 +1,1227 @@
+/*
+ * DPP configurator backup
+ * Copyright (c) 2019-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 <openssl/opensslv.h>
+#include <openssl/err.h>
+
+#include "utils/common.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "tls/asn1.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+#ifdef CONFIG_DPP2
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+	(defined(LIBRESSL_VERSION_NUMBER) && \
+	 LIBRESSL_VERSION_NUMBER < 0x20700000L)
+/* Compatibility wrappers for older versions. */
+
+static EC_KEY * EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
+{
+	if (pkey->type != EVP_PKEY_EC)
+		return NULL;
+	return pkey->pkey.ec;
+}
+
+#endif
+
+
+void dpp_free_asymmetric_key(struct dpp_asymmetric_key *key)
+{
+	while (key) {
+		struct dpp_asymmetric_key *next = key->next;
+
+		EVP_PKEY_free(key->csign);
+		str_clear_free(key->config_template);
+		str_clear_free(key->connector_template);
+		os_free(key);
+		key = next;
+	}
+}
+
+
+static struct wpabuf * dpp_build_conf_params(void)
+{
+	struct wpabuf *buf;
+	size_t len;
+	/* TODO: proper template values */
+	const char *conf_template = "{\"wi-fi_tech\":\"infra\",\"discovery\":{\"ssid\":\"test\"},\"cred\":{\"akm\":\"dpp\"}}";
+	const char *connector_template = NULL;
+
+	len = 100 + os_strlen(conf_template);
+	if (connector_template)
+		len += os_strlen(connector_template);
+	buf = wpabuf_alloc(len);
+	if (!buf)
+		return NULL;
+
+	/*
+	 * DPPConfigurationParameters ::= SEQUENCE {
+	 *    configurationTemplate	UTF8String,
+	 *    connectorTemplate		UTF8String OPTIONAL}
+	 */
+
+	asn1_put_utf8string(buf, conf_template);
+	if (connector_template)
+		asn1_put_utf8string(buf, connector_template);
+	return asn1_encaps(buf, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+}
+
+
+static struct wpabuf * dpp_build_attribute(void)
+{
+	struct wpabuf *conf_params, *attr;
+
+	/*
+	 * aa-DPPConfigurationParameters ATTRIBUTE ::=
+	 * { TYPE DPPConfigurationParameters IDENTIFIED BY id-DPPConfigParams }
+	 *
+	 * Attribute ::= SEQUENCE {
+	 *    type OBJECT IDENTIFIER,
+	 *    values SET SIZE(1..MAX) OF Type
+	 */
+	conf_params = dpp_build_conf_params();
+	conf_params = asn1_encaps(conf_params, ASN1_CLASS_UNIVERSAL,
+				  ASN1_TAG_SET);
+	if (!conf_params)
+		return NULL;
+
+	attr = wpabuf_alloc(100 + wpabuf_len(conf_params));
+	if (!attr) {
+		wpabuf_clear_free(conf_params);
+		return NULL;
+	}
+
+	asn1_put_oid(attr, &asn1_dpp_config_params_oid);
+	wpabuf_put_buf(attr, conf_params);
+	wpabuf_clear_free(conf_params);
+
+	return asn1_encaps(attr, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+}
+
+
+static struct wpabuf * dpp_build_key_alg(const struct dpp_curve_params *curve)
+{
+	const struct asn1_oid *oid;
+	struct wpabuf *params, *res;
+
+	switch (curve->ike_group) {
+	case 19:
+		oid = &asn1_prime256v1_oid;
+		break;
+	case 20:
+		oid = &asn1_secp384r1_oid;
+		break;
+	case 21:
+		oid = &asn1_secp521r1_oid;
+		break;
+	case 28:
+		oid = &asn1_brainpoolP256r1_oid;
+		break;
+	case 29:
+		oid = &asn1_brainpoolP384r1_oid;
+		break;
+	case 30:
+		oid = &asn1_brainpoolP512r1_oid;
+		break;
+	default:
+		return NULL;
+	}
+
+	params = wpabuf_alloc(20);
+	if (!params)
+		return NULL;
+	asn1_put_oid(params, oid); /* namedCurve */
+
+	res = asn1_build_alg_id(&asn1_ec_public_key_oid, params);
+	wpabuf_free(params);
+	return res;
+}
+
+
+static struct wpabuf * dpp_build_key_pkg(struct dpp_authentication *auth)
+{
+	struct wpabuf *key = NULL, *attr, *alg, *priv_key = NULL;
+	EC_KEY *eckey;
+	unsigned char *der = NULL;
+	int der_len;
+
+	eckey = EVP_PKEY_get0_EC_KEY(auth->conf->csign);
+	if (!eckey)
+		return NULL;
+
+	EC_KEY_set_enc_flags(eckey, EC_PKEY_NO_PUBKEY);
+	der_len = i2d_ECPrivateKey(eckey, &der);
+	if (der_len > 0)
+		priv_key = wpabuf_alloc_copy(der, der_len);
+	OPENSSL_free(der);
+
+	alg = dpp_build_key_alg(auth->conf->curve);
+
+	/* Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } } */
+	attr = dpp_build_attribute();
+	attr = asn1_encaps(attr, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SET);
+	if (!priv_key || !attr || !alg)
+		goto fail;
+
+	/*
+	 * OneAsymmetricKey ::= SEQUENCE {
+	 *    version			Version,
+	 *    privateKeyAlgorithm	PrivateKeyAlgorithmIdentifier,
+	 *    privateKey		PrivateKey,
+	 *    attributes		[0] Attributes OPTIONAL,
+	 *    ...,
+	 *    [[2: publicKey		[1] BIT STRING OPTIONAL ]],
+	 *    ...
+	 * }
+	 */
+
+	key = wpabuf_alloc(100 + wpabuf_len(alg) + wpabuf_len(priv_key) +
+			   wpabuf_len(attr));
+	if (!key)
+		goto fail;
+
+	asn1_put_integer(key, 1); /* version = v2(1) */
+
+	/* PrivateKeyAlgorithmIdentifier */
+	wpabuf_put_buf(key, alg);
+
+	/* PrivateKey ::= OCTET STRING */
+	asn1_put_octet_string(key, priv_key);
+
+	/* [0] Attributes OPTIONAL */
+	asn1_put_hdr(key, ASN1_CLASS_CONTEXT_SPECIFIC, 1, 0, wpabuf_len(attr));
+	wpabuf_put_buf(key, attr);
+
+fail:
+	wpabuf_clear_free(attr);
+	wpabuf_clear_free(priv_key);
+	wpabuf_free(alg);
+
+	/*
+	 * DPPAsymmetricKeyPackage ::= AsymmetricKeyPackage
+	 *
+	 * AsymmetricKeyPackage ::= SEQUENCE SIZE (1..MAX) OF OneAsymmetricKey
+	 *
+	 * OneAsymmetricKey ::= SEQUENCE
+	 */
+	return asn1_encaps(asn1_encaps(key,
+				       ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE),
+			   ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+}
+
+
+static struct wpabuf * dpp_build_pbkdf2_alg_id(const struct wpabuf *salt,
+					       size_t hash_len)
+{
+	struct wpabuf *params = NULL, *buf = NULL, *prf = NULL;
+	const struct asn1_oid *oid;
+
+	/*
+	 * PBKDF2-params ::= SEQUENCE {
+	 *    salt CHOICE {
+	 *       specified OCTET STRING,
+	 *       otherSource AlgorithmIdentifier}
+	 *    iterationCount INTEGER (1..MAX),
+	 *    keyLength INTEGER (1..MAX),
+	 *    prf AlgorithmIdentifier}
+	 *
+	 * salt is an 64 octet value, iterationCount is 1000, keyLength is based
+	 * on Configurator signing key length, prf is
+	 * id-hmacWithSHA{256,384,512} based on Configurator signing key.
+	 */
+
+	if (hash_len == 32)
+		oid = &asn1_pbkdf2_hmac_sha256_oid;
+	else if (hash_len == 48)
+		oid = &asn1_pbkdf2_hmac_sha384_oid;
+	else if (hash_len == 64)
+		oid = &asn1_pbkdf2_hmac_sha512_oid;
+	else
+		goto fail;
+	prf = asn1_build_alg_id(oid, NULL);
+	if (!prf)
+		goto fail;
+	params = wpabuf_alloc(100 + wpabuf_len(salt) + wpabuf_len(prf));
+	if (!params)
+		goto fail;
+	asn1_put_octet_string(params, salt); /* salt.specified */
+	asn1_put_integer(params, 1000); /* iterationCount */
+	asn1_put_integer(params, hash_len); /* keyLength */
+	wpabuf_put_buf(params, prf);
+	params = asn1_encaps(params, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+	if (!params)
+		goto fail;
+	buf = asn1_build_alg_id(&asn1_pbkdf2_oid, params);
+fail:
+	wpabuf_free(params);
+	wpabuf_free(prf);
+	return buf;
+}
+
+
+static struct wpabuf *
+dpp_build_pw_recipient_info(struct dpp_authentication *auth, size_t hash_len,
+			    const struct wpabuf *cont_enc_key)
+{
+	struct wpabuf *pwri = NULL, *enc_key = NULL, *key_der_alg = NULL,
+		*key_enc_alg = NULL, *salt;
+	u8 kek[DPP_MAX_HASH_LEN];
+	const u8 *key;
+	size_t key_len;
+
+	salt = wpabuf_alloc(64);
+	if (!salt || os_get_random(wpabuf_put(salt, 64), 64) < 0)
+		goto fail;
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: PBKDF2 salt", salt);
+
+	/* TODO: For initial testing, use ke as the key. Replace this with a
+	 * new key once that has been defined. */
+	key = auth->ke;
+	key_len = auth->curve->hash_len;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PBKDF2 key", key, key_len);
+
+	if (dpp_pbkdf2(hash_len, key, key_len, wpabuf_head(salt), 64, 1000,
+		       kek, hash_len)) {
+		wpa_printf(MSG_DEBUG, "DPP: PBKDF2 failed");
+		goto fail;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "DPP: key-encryption key from PBKDF2",
+			kek, hash_len);
+
+	enc_key = wpabuf_alloc(hash_len + AES_BLOCK_SIZE);
+	if (!enc_key ||
+	    aes_siv_encrypt(kek, hash_len, wpabuf_head(cont_enc_key),
+			    wpabuf_len(cont_enc_key), 0, NULL, NULL,
+			    wpabuf_put(enc_key, hash_len + AES_BLOCK_SIZE)) < 0)
+		goto fail;
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: encryptedKey", enc_key);
+
+	/*
+	 * PasswordRecipientInfo ::= SEQUENCE {
+	 *    version			CMSVersion,
+	 *    keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier OPTIONAL,
+	 *    keyEncryptionAlgorithm	KeyEncryptionAlgorithmIdentifier,
+	 *    encryptedKey		EncryptedKey}
+	 *
+	 * version is 0, keyDerivationAlgorithm is id-PKBDF2, and the
+	 * parameters contains PBKDF2-params SEQUENCE.
+	 */
+
+	key_der_alg = dpp_build_pbkdf2_alg_id(salt, hash_len);
+	key_enc_alg = asn1_build_alg_id(&asn1_aes_siv_cmac_aead_256_oid, NULL);
+	if (!key_der_alg || !key_enc_alg)
+		goto fail;
+	pwri = wpabuf_alloc(100 + wpabuf_len(key_der_alg) +
+			    wpabuf_len(key_enc_alg) + wpabuf_len(enc_key));
+	if (!pwri)
+		goto fail;
+
+	/* version = 0 */
+	asn1_put_integer(pwri, 0);
+
+	/* [0] KeyDerivationAlgorithmIdentifier */
+	asn1_put_hdr(pwri, ASN1_CLASS_CONTEXT_SPECIFIC, 1, 0,
+		     wpabuf_len(key_der_alg));
+	wpabuf_put_buf(pwri, key_der_alg);
+
+	/* KeyEncryptionAlgorithmIdentifier */
+	wpabuf_put_buf(pwri, key_enc_alg);
+
+	/* EncryptedKey ::= OCTET STRING */
+	asn1_put_octet_string(pwri, enc_key);
+
+fail:
+	wpabuf_clear_free(key_der_alg);
+	wpabuf_free(key_enc_alg);
+	wpabuf_free(enc_key);
+	wpabuf_free(salt);
+	forced_memzero(kek, sizeof(kek));
+	return asn1_encaps(pwri, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+}
+
+
+static struct wpabuf *
+dpp_build_recipient_info(struct dpp_authentication *auth, size_t hash_len,
+			 const struct wpabuf *cont_enc_key)
+{
+	struct wpabuf *pwri;
+
+	/*
+	 * RecipientInfo ::= CHOICE {
+	 *    ktri		KeyTransRecipientInfo,
+	 *    kari	[1]	KeyAgreeRecipientInfo,
+	 *    kekri	[2]	KEKRecipientInfo,
+	 *    pwri	[3]	PasswordRecipientInfo,
+	 *    ori	[4]	OtherRecipientInfo}
+	 *
+	 * Shall always use the pwri CHOICE.
+	 */
+
+	pwri = dpp_build_pw_recipient_info(auth, hash_len, cont_enc_key);
+	return asn1_encaps(pwri, ASN1_CLASS_CONTEXT_SPECIFIC, 3);
+}
+
+
+static struct wpabuf *
+dpp_build_enc_cont_info(struct dpp_authentication *auth, size_t hash_len,
+			const struct wpabuf *cont_enc_key)
+{
+	struct wpabuf *key_pkg, *enc_cont_info = NULL, *enc_cont = NULL,
+		*enc_alg;
+	const struct asn1_oid *oid;
+	size_t enc_cont_len;
+
+	/*
+	 * EncryptedContentInfo ::= SEQUENCE {
+	 *    contentType			ContentType,
+	 *    contentEncryptionAlgorithm  ContentEncryptionAlgorithmIdentifier,
+	 *    encryptedContent	[0] IMPLICIT	EncryptedContent OPTIONAL}
+	 */
+
+	if (hash_len == 32)
+		oid = &asn1_aes_siv_cmac_aead_256_oid;
+	else if (hash_len == 48)
+		oid = &asn1_aes_siv_cmac_aead_384_oid;
+	else if (hash_len == 64)
+		oid = &asn1_aes_siv_cmac_aead_512_oid;
+	else
+		return NULL;
+
+	key_pkg = dpp_build_key_pkg(auth);
+	enc_alg = asn1_build_alg_id(oid, NULL);
+	if (!key_pkg || !enc_alg)
+		goto fail;
+
+	wpa_hexdump_buf_key(MSG_MSGDUMP, "DPP: DPPAsymmetricKeyPackage",
+			    key_pkg);
+
+	enc_cont_len = wpabuf_len(key_pkg) + AES_BLOCK_SIZE;
+	enc_cont = wpabuf_alloc(enc_cont_len);
+	if (!enc_cont ||
+	    aes_siv_encrypt(wpabuf_head(cont_enc_key), wpabuf_len(cont_enc_key),
+			    wpabuf_head(key_pkg), wpabuf_len(key_pkg),
+			    0, NULL, NULL,
+			    wpabuf_put(enc_cont, enc_cont_len)) < 0)
+		goto fail;
+
+	enc_cont_info = wpabuf_alloc(100 + wpabuf_len(enc_alg) +
+				     wpabuf_len(enc_cont));
+	if (!enc_cont_info)
+		goto fail;
+
+	/* ContentType ::= OBJECT IDENTIFIER */
+	asn1_put_oid(enc_cont_info, &asn1_dpp_asymmetric_key_package_oid);
+
+	/* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */
+	wpabuf_put_buf(enc_cont_info, enc_alg);
+
+	/* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
+	 * EncryptedContent ::= OCTET STRING */
+	asn1_put_hdr(enc_cont_info, ASN1_CLASS_CONTEXT_SPECIFIC, 0, 0,
+		     wpabuf_len(enc_cont));
+	wpabuf_put_buf(enc_cont_info, enc_cont);
+
+fail:
+	wpabuf_clear_free(key_pkg);
+	wpabuf_free(enc_cont);
+	wpabuf_free(enc_alg);
+	return enc_cont_info;
+}
+
+
+static struct wpabuf * dpp_gen_random(size_t len)
+{
+	struct wpabuf *key;
+
+	key = wpabuf_alloc(len);
+	if (!key || os_get_random(wpabuf_put(key, len), len) < 0) {
+		wpabuf_free(key);
+		key = NULL;
+	}
+	wpa_hexdump_buf_key(MSG_DEBUG, "DPP: content-encryption key", key);
+	return key;
+}
+
+
+struct wpabuf * dpp_build_enveloped_data(struct dpp_authentication *auth)
+{
+	struct wpabuf *env = NULL;
+	struct wpabuf *recipient_info = NULL, *enc_cont_info = NULL;
+	struct wpabuf *cont_enc_key = NULL;
+	size_t hash_len;
+
+	if (!auth->conf) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No Configurator instance selected for the session - cannot build DPPEnvelopedData");
+		return NULL;
+	}
+
+	if (!auth->provision_configurator) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Configurator provisioning not allowed");
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Building DPPEnvelopedData");
+
+	hash_len = auth->conf->curve->hash_len;
+	cont_enc_key = dpp_gen_random(hash_len);
+	if (!cont_enc_key)
+		goto fail;
+	recipient_info = dpp_build_recipient_info(auth, hash_len, cont_enc_key);
+	enc_cont_info = dpp_build_enc_cont_info(auth, hash_len, cont_enc_key);
+	if (!recipient_info || !enc_cont_info)
+		goto fail;
+
+	env = wpabuf_alloc(wpabuf_len(recipient_info) +
+			   wpabuf_len(enc_cont_info) +
+			   100);
+	if (!env)
+		goto fail;
+
+	/*
+	 * DPPEnvelopedData ::= EnvelopedData
+	 *
+	 * EnvelopedData ::= SEQUENCE {
+	 *    version			CMSVersion,
+	 *    originatorInfo	[0]	IMPLICIT OriginatorInfo OPTIONAL,
+	 *    recipientInfos		RecipientInfos,
+	 *    encryptedContentInfo	EncryptedContentInfo,
+	 *    unprotectedAttrs  [1] IMPLICIT	UnprotectedAttributes OPTIONAL}
+	 *
+	 * For DPP, version is 3, both originatorInfo and
+	 * unprotectedAttrs are omitted, and recipientInfos contains a single
+	 * RecipientInfo.
+	 */
+
+	/* EnvelopedData.version = 3 */
+	asn1_put_integer(env, 3);
+
+	/* RecipientInfos ::= SET SIZE (1..MAX) OF RecipientInfo */
+	asn1_put_set(env, recipient_info);
+
+	/* EncryptedContentInfo ::= SEQUENCE */
+	asn1_put_sequence(env, enc_cont_info);
+
+	env = asn1_encaps(env, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: DPPEnvelopedData", env);
+out:
+	wpabuf_clear_free(cont_enc_key);
+	wpabuf_clear_free(recipient_info);
+	wpabuf_free(enc_cont_info);
+	return env;
+fail:
+	wpabuf_free(env);
+	env = NULL;
+	goto out;
+}
+
+
+struct dpp_enveloped_data {
+	const u8 *enc_cont;
+	size_t enc_cont_len;
+	const u8 *enc_key;
+	size_t enc_key_len;
+	const u8 *salt;
+	size_t pbkdf2_key_len;
+	size_t prf_hash_len;
+};
+
+
+static int dpp_parse_recipient_infos(const u8 *pos, size_t len,
+				     struct dpp_enveloped_data *data)
+{
+	struct asn1_hdr hdr;
+	const u8 *end = pos + len;
+	const u8 *next, *e_end;
+	struct asn1_oid oid;
+	int val;
+	const u8 *params;
+	size_t params_len;
+
+	wpa_hexdump(MSG_MSGDUMP, "DPP: RecipientInfos", pos, len);
+
+	/*
+	 * RecipientInfo ::= CHOICE {
+	 *    ktri		KeyTransRecipientInfo,
+	 *    kari	[1]	KeyAgreeRecipientInfo,
+	 *    kekri	[2]	KEKRecipientInfo,
+	 *    pwri	[3]	PasswordRecipientInfo,
+	 *    ori	[4]	OtherRecipientInfo}
+	 *
+	 * Shall always use the pwri CHOICE.
+	 */
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 3) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Expected CHOICE [3] (pwri) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: PasswordRecipientInfo",
+		    hdr.payload, hdr.length);
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	/*
+	 * PasswordRecipientInfo ::= SEQUENCE {
+	 *    version			CMSVersion,
+	 *    keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier OPTIONAL,
+	 *    keyEncryptionAlgorithm	KeyEncryptionAlgorithmIdentifier,
+	 *    encryptedKey		EncryptedKey}
+	 *
+	 * version is 0, keyDerivationAlgorithm is id-PKBDF2, and the
+	 * parameters contains PBKDF2-params SEQUENCE.
+	 */
+
+	if (asn1_get_sequence(pos, end - pos, &hdr, &end) < 0)
+		return -1;
+	pos = hdr.payload;
+
+	if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
+		return -1;
+	if (val != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: pwri.version != 0");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Remaining PasswordRecipientInfo after version",
+		    pos, end - pos);
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Expected keyDerivationAlgorithm [0] - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	pos = hdr.payload;
+	e_end = pos + hdr.length;
+
+	/* KeyDerivationAlgorithmIdentifier ::= AlgorithmIdentifier */
+	if (asn1_get_alg_id(pos, e_end - pos, &oid, &params, &params_len,
+			    &next) < 0)
+		return -1;
+	if (!asn1_oid_equal(&oid, &asn1_pbkdf2_oid)) {
+		char buf[80];
+
+		asn1_oid_to_str(&oid, buf, sizeof(buf));
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected KeyDerivationAlgorithmIdentifier %s",
+			   buf);
+		return -1;
+	}
+
+	/*
+	 * PBKDF2-params ::= SEQUENCE {
+	 *    salt CHOICE {
+	 *       specified OCTET STRING,
+	 *       otherSource AlgorithmIdentifier}
+	 *    iterationCount INTEGER (1..MAX),
+	 *    keyLength INTEGER (1..MAX),
+	 *    prf AlgorithmIdentifier}
+	 *
+	 * salt is an 64 octet value, iterationCount is 1000, keyLength is based
+	 * on Configurator signing key length, prf is
+	 * id-hmacWithSHA{256,384,512} based on Configurator signing key.
+	 */
+	if (!params ||
+	    asn1_get_sequence(params, params_len, &hdr, &e_end) < 0)
+		return -1;
+	pos = hdr.payload;
+
+	if (asn1_get_next(pos, e_end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_OCTETSTRING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Expected OCTETSTRING (salt.specified) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: salt.specified",
+		    hdr.payload, hdr.length);
+	if (hdr.length != 64) {
+		wpa_printf(MSG_DEBUG, "DPP: Unexpected salt length %u",
+			   hdr.length);
+		return -1;
+	}
+	data->salt = hdr.payload;
+	pos = hdr.payload + hdr.length;
+
+	if (asn1_get_integer(pos, e_end - pos, &val, &pos) < 0)
+		return -1;
+	if (val != 1000) {
+		wpa_printf(MSG_DEBUG, "DPP: Unexpected iterationCount %d", val);
+		return -1;
+	}
+
+	if (asn1_get_integer(pos, e_end - pos, &val, &pos) < 0)
+		return -1;
+	if (val != 32 && val != 48 && val != 64) {
+		wpa_printf(MSG_DEBUG, "DPP: Unexpected keyLength %d", val);
+		return -1;
+	}
+	data->pbkdf2_key_len = val;
+
+	if (asn1_get_sequence(pos, e_end - pos, &hdr, NULL) < 0 ||
+	    asn1_get_oid(hdr.payload, hdr.length, &oid, &pos) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Could not parse prf");
+		return -1;
+	}
+	if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha256_oid)) {
+		data->prf_hash_len = 32;
+	} else if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha384_oid)) {
+		data->prf_hash_len = 48;
+	} else if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha512_oid)) {
+		data->prf_hash_len = 64;
+	} else {
+		char buf[80];
+
+		asn1_oid_to_str(&oid, buf, sizeof(buf));
+		wpa_printf(MSG_DEBUG, "DPP: Unexpected PBKDF2-params.prf %s",
+			   buf);
+		return -1;
+	}
+
+	pos = next;
+
+	/* keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier
+	 *
+	 * KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
+	 *
+	 * id-alg-AES-SIV-CMAC-aed-256, id-alg-AES-SIV-CMAC-aed-384, or
+	 * id-alg-AES-SIV-CMAC-aed-512. */
+	if (asn1_get_alg_id(pos, end - pos, &oid, NULL, NULL, &pos) < 0)
+		return -1;
+	if (!asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_256_oid) &&
+	    !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_384_oid) &&
+	    !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_512_oid)) {
+		char buf[80];
+
+		asn1_oid_to_str(&oid, buf, sizeof(buf));
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected KeyEncryptionAlgorithmIdentifier %s",
+			   buf);
+		return -1;
+	}
+
+	/*
+	 * encryptedKey EncryptedKey
+	 *
+	 * EncryptedKey ::= OCTET STRING
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_OCTETSTRING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Expected OCTETSTRING (pwri.encryptedKey) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: pwri.encryptedKey",
+		    hdr.payload, hdr.length);
+	data->enc_key = hdr.payload;
+	data->enc_key_len = hdr.length;
+
+	return 0;
+}
+
+
+static int dpp_parse_encrypted_content_info(const u8 *pos, const u8 *end,
+					    struct dpp_enveloped_data *data)
+{
+	struct asn1_hdr hdr;
+	struct asn1_oid oid;
+
+	/*
+	 * EncryptedContentInfo ::= SEQUENCE {
+	 *    contentType			ContentType,
+	 *    contentEncryptionAlgorithm  ContentEncryptionAlgorithmIdentifier,
+	 *    encryptedContent	[0] IMPLICIT	EncryptedContent OPTIONAL}
+	 */
+	if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
+		return -1;
+	wpa_hexdump(MSG_MSGDUMP, "DPP: EncryptedContentInfo",
+		    hdr.payload, hdr.length);
+	if (pos < end) {
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Unexpected extra data after EncryptedContentInfo",
+			    pos, end - pos);
+		return -1;
+	}
+
+	end = pos;
+	pos = hdr.payload;
+
+	/* ContentType ::= OBJECT IDENTIFIER */
+	if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Could not parse ContentType");
+		return -1;
+	}
+	if (!asn1_oid_equal(&oid, &asn1_dpp_asymmetric_key_package_oid)) {
+		char buf[80];
+
+		asn1_oid_to_str(&oid, buf, sizeof(buf));
+		wpa_printf(MSG_DEBUG, "DPP: Unexpected ContentType %s", buf);
+		return -1;
+	}
+
+	/* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */
+	if (asn1_get_alg_id(pos, end - pos, &oid, NULL, NULL, &pos) < 0)
+		return -1;
+	if (!asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_256_oid) &&
+	    !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_384_oid) &&
+	    !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_512_oid)) {
+		char buf[80];
+
+		asn1_oid_to_str(&oid, buf, sizeof(buf));
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected ContentEncryptionAlgorithmIdentifier %s",
+			   buf);
+		return -1;
+	}
+	/* ignore optional parameters */
+
+	/* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
+	 * EncryptedContent ::= OCTET STRING */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Expected [0] IMPLICIT (EncryptedContent) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: EncryptedContent",
+		    hdr.payload, hdr.length);
+	data->enc_cont = hdr.payload;
+	data->enc_cont_len = hdr.length;
+	return 0;
+}
+
+
+static int dpp_parse_enveloped_data(const u8 *env_data, size_t env_data_len,
+				    struct dpp_enveloped_data *data)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+	int val;
+
+	os_memset(data, 0, sizeof(*data));
+
+	/*
+	 * DPPEnvelopedData ::= EnvelopedData
+	 *
+	 * EnvelopedData ::= SEQUENCE {
+	 *    version			CMSVersion,
+	 *    originatorInfo	[0]	IMPLICIT OriginatorInfo OPTIONAL,
+	 *    recipientInfos		RecipientInfos,
+	 *    encryptedContentInfo	EncryptedContentInfo,
+	 *    unprotectedAttrs  [1] IMPLICIT	UnprotectedAttributes OPTIONAL}
+	 *
+	 * CMSVersion ::= INTEGER
+	 *
+	 * RecipientInfos ::= SET SIZE (1..MAX) OF RecipientInfo
+	 *
+	 * For DPP, version is 3, both originatorInfo and
+	 * unprotectedAttrs are omitted, and recipientInfos contains a single
+	 * RecipientInfo.
+	 */
+	if (asn1_get_sequence(env_data, env_data_len, &hdr, &end) < 0)
+		return -1;
+	pos = hdr.payload;
+	if (end < env_data + env_data_len) {
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Unexpected extra data after DPPEnvelopedData",
+			    end, env_data + env_data_len - end);
+		return -1;
+	}
+
+	if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
+		return -1;
+	if (val != 3) {
+		wpa_printf(MSG_DEBUG, "DPP: EnvelopedData.version != 3");
+		return -1;
+	}
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SET) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Expected SET (RecipientInfos) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	if (dpp_parse_recipient_infos(hdr.payload, hdr.length, data) < 0)
+		return -1;
+	return dpp_parse_encrypted_content_info(hdr.payload + hdr.length, end,
+						data);
+}
+
+
+static struct dpp_asymmetric_key *
+dpp_parse_one_asymmetric_key(const u8 *buf, size_t len)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos = buf, *end = buf + len, *next;
+	int val;
+	const u8 *params;
+	size_t params_len;
+	struct asn1_oid oid;
+	char txt[80];
+	struct dpp_asymmetric_key *key;
+	EC_KEY *eckey;
+
+	wpa_hexdump_key(MSG_MSGDUMP, "DPP: OneAsymmetricKey", buf, len);
+
+	key = os_zalloc(sizeof(*key));
+	if (!key)
+		return NULL;
+
+	/*
+	 * OneAsymmetricKey ::= SEQUENCE {
+	 *    version			Version,
+	 *    privateKeyAlgorithm	PrivateKeyAlgorithmIdentifier,
+	 *    privateKey		PrivateKey,
+	 *    attributes		[0] Attributes OPTIONAL,
+	 *    ...,
+	 *    [[2: publicKey		[1] BIT STRING OPTIONAL ]],
+	 *    ...
+	 * }
+	 */
+	if (asn1_get_sequence(pos, end - pos, &hdr, &end) < 0)
+		goto fail;
+	pos = hdr.payload;
+
+	/* Version ::= INTEGER { v1(0), v2(1) } (v1, ..., v2) */
+	if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
+		goto fail;
+	if (val != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unsupported DPPAsymmetricKeyPackage version %d",
+			   val);
+		goto fail;
+	}
+
+	/* PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier */
+	if (asn1_get_alg_id(pos, end - pos, &oid, &params, &params_len,
+			    &pos) < 0)
+		goto fail;
+	if (!asn1_oid_equal(&oid, &asn1_ec_public_key_oid)) {
+		asn1_oid_to_str(&oid, txt, sizeof(txt));
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unsupported PrivateKeyAlgorithmIdentifier %s",
+			   txt);
+		goto fail;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: PrivateKeyAlgorithmIdentifier params",
+		    params, params_len);
+	/*
+	 * ECParameters ::= CHOICE {
+	 *    namedCurve	OBJECT IDENTIFIER
+	 *    -- implicitCurve	NULL
+	 *    -- specifiedCurve	SpecifiedECDomain}
+	 */
+	if (!params || asn1_get_oid(params, params_len, &oid, &next) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not parse ECParameters.namedCurve");
+		goto fail;
+	}
+	asn1_oid_to_str(&oid, txt, sizeof(txt));
+	wpa_printf(MSG_MSGDUMP, "DPP: namedCurve %s", txt);
+	/* Assume the curve is identified within ECPrivateKey, so that this
+	 * separate indication is not really needed. */
+
+	/*
+	 * PrivateKey ::= OCTET STRING
+	 *    (Contains DER encoding of ECPrivateKey)
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_OCTETSTRING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Expected OCTETSTRING (PrivateKey) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		goto fail;
+	}
+	wpa_hexdump_key(MSG_MSGDUMP, "DPP: PrivateKey",
+			hdr.payload, hdr.length);
+	pos = hdr.payload + hdr.length;
+	eckey = d2i_ECPrivateKey(NULL, &hdr.payload, hdr.length);
+	if (!eckey) {
+		wpa_printf(MSG_INFO,
+			   "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	key->csign = EVP_PKEY_new();
+	if (!key->csign || EVP_PKEY_assign_EC_KEY(key->csign, eckey) != 1) {
+		EC_KEY_free(eckey);
+		goto fail;
+	}
+	if (wpa_debug_show_keys)
+		dpp_debug_print_key("DPP: Received c-sign-key", key->csign);
+
+	/*
+	 * Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } }
+	 *
+	 * Exactly one instance of type Attribute in OneAsymmetricKey.
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Expected [0] Attributes - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		goto fail;
+	}
+	wpa_hexdump_key(MSG_MSGDUMP, "DPP: Attributes",
+			hdr.payload, hdr.length);
+	if (hdr.payload + hdr.length < end) {
+		wpa_hexdump_key(MSG_MSGDUMP,
+				"DPP: Ignore additional data at the end of OneAsymmetricKey",
+				hdr.payload + hdr.length,
+				end - (hdr.payload + hdr.length));
+	}
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SET) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Expected SET (Attributes) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		goto fail;
+	}
+	if (hdr.payload + hdr.length < end) {
+		wpa_hexdump_key(MSG_MSGDUMP,
+				"DPP: Ignore additional data at the end of OneAsymmetricKey (after SET)",
+				hdr.payload + hdr.length,
+				end - (hdr.payload + hdr.length));
+	}
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	/*
+	 * OneAsymmetricKeyAttributes ATTRIBUTE ::= {
+	 *    aa-DPPConfigurationParameters,
+	 *    ... -- For local profiles
+	 * }
+	 *
+	 * aa-DPPConfigurationParameters ATTRIBUTE ::=
+	 * { TYPE DPPConfigurationParameters IDENTIFIED BY id-DPPConfigParams }
+	 *
+	 * Attribute ::= SEQUENCE {
+	 *    type OBJECT IDENTIFIER,
+	 *    values SET SIZE(1..MAX) OF Type
+	 *
+	 * Exactly one instance of ATTRIBUTE in attrValues.
+	 */
+	if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
+		goto fail;
+	if (pos < end) {
+		wpa_hexdump_key(MSG_MSGDUMP,
+				"DPP: Ignore additional data at the end of ATTRIBUTE",
+				pos, end - pos);
+	}
+	end = pos;
+	pos = hdr.payload;
+
+	if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0)
+		goto fail;
+	if (!asn1_oid_equal(&oid, &asn1_dpp_config_params_oid)) {
+		asn1_oid_to_str(&oid, txt, sizeof(txt));
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected Attribute identifier %s", txt);
+		goto fail;
+	}
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SET) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Expected SET (Attribute) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		goto fail;
+	}
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	/*
+	 * DPPConfigurationParameters ::= SEQUENCE {
+	 *    configurationTemplate	UTF8String,
+	 *    connectorTemplate		UTF8String OPTIONAL}
+	 */
+
+	wpa_hexdump_key(MSG_MSGDUMP, "DPP: DPPConfigurationParameters",
+			pos, end - pos);
+	if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
+		goto fail;
+	if (pos < end) {
+		wpa_hexdump_key(MSG_MSGDUMP,
+				"DPP: Ignore additional data after DPPConfigurationParameters",
+				pos, end - pos);
+	}
+	end = pos;
+	pos = hdr.payload;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_UTF8STRING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Expected UTF8STRING (configurationTemplate) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		goto fail;
+	}
+	wpa_hexdump_ascii_key(MSG_MSGDUMP, "DPP: configurationTemplate",
+			      hdr.payload, hdr.length);
+	key->config_template = os_zalloc(hdr.length + 1);
+	if (!key->config_template)
+		goto fail;
+	os_memcpy(key->config_template, hdr.payload, hdr.length);
+
+	pos = hdr.payload + hdr.length;
+
+	if (pos < end) {
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    hdr.class != ASN1_CLASS_UNIVERSAL ||
+		    hdr.tag != ASN1_TAG_UTF8STRING) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Expected UTF8STRING (connectorTemplate) - found class %d tag 0x%x",
+				   hdr.class, hdr.tag);
+			goto fail;
+		}
+		wpa_hexdump_ascii_key(MSG_MSGDUMP, "DPP: connectorTemplate",
+				      hdr.payload, hdr.length);
+		key->connector_template = os_zalloc(hdr.length + 1);
+		if (!key->connector_template)
+			goto fail;
+		os_memcpy(key->connector_template, hdr.payload, hdr.length);
+	}
+
+	return key;
+fail:
+	wpa_printf(MSG_DEBUG, "DPP: Failed to parse OneAsymmetricKey");
+	dpp_free_asymmetric_key(key);
+	return NULL;
+}
+
+
+static struct dpp_asymmetric_key *
+dpp_parse_dpp_asymmetric_key_package(const u8 *key_pkg, size_t key_pkg_len)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos = key_pkg, *end = key_pkg + key_pkg_len;
+	struct dpp_asymmetric_key *first = NULL, *last = NULL, *key;
+
+	wpa_hexdump_key(MSG_MSGDUMP, "DPP: DPPAsymmetricKeyPackage",
+			key_pkg, key_pkg_len);
+
+	/*
+	 * DPPAsymmetricKeyPackage ::= AsymmetricKeyPackage
+	 *
+	 * AsymmetricKeyPackage ::= SEQUENCE SIZE (1..MAX) OF OneAsymmetricKey
+	 */
+	while (pos < end) {
+		if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0 ||
+		    !(key = dpp_parse_one_asymmetric_key(hdr.payload,
+							 hdr.length))) {
+			dpp_free_asymmetric_key(first);
+			return NULL;
+		}
+		if (!last) {
+			first = last = key;
+		} else {
+			last->next = key;
+			last = key;
+		}
+	}
+
+	return first;
+}
+
+
+int dpp_conf_resp_env_data(struct dpp_authentication *auth,
+			   const u8 *env_data, size_t env_data_len)
+{
+	const u8 *key;
+	size_t key_len;
+	u8 kek[DPP_MAX_HASH_LEN];
+	u8 cont_encr_key[DPP_MAX_HASH_LEN];
+	size_t cont_encr_key_len;
+	int res;
+	u8 *key_pkg;
+	size_t key_pkg_len;
+	struct dpp_enveloped_data data;
+	struct dpp_asymmetric_key *keys;
+
+	wpa_hexdump(MSG_DEBUG, "DPP: DPPEnvelopedData", env_data, env_data_len);
+
+	if (dpp_parse_enveloped_data(env_data, env_data_len, &data) < 0)
+		return -1;
+
+	/* TODO: For initial testing, use ke as the key. Replace this with a
+	 * new key once that has been defined. */
+	key = auth->ke;
+	key_len = auth->curve->hash_len;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PBKDF2 key", key, key_len);
+
+	if (dpp_pbkdf2(data.prf_hash_len, key, key_len, data.salt, 64, 1000,
+		       kek, data.pbkdf2_key_len)) {
+		wpa_printf(MSG_DEBUG, "DPP: PBKDF2 failed");
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "DPP: key-encryption key from PBKDF2",
+			kek, data.pbkdf2_key_len);
+
+	if (data.enc_key_len < AES_BLOCK_SIZE ||
+	    data.enc_key_len > sizeof(cont_encr_key) + AES_BLOCK_SIZE) {
+		wpa_printf(MSG_DEBUG, "DPP: Invalid encryptedKey length");
+		return -1;
+	}
+	res = aes_siv_decrypt(kek, data.pbkdf2_key_len,
+			      data.enc_key, data.enc_key_len,
+			      0, NULL, NULL, cont_encr_key);
+	forced_memzero(kek, data.pbkdf2_key_len);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: AES-SIV decryption of encryptedKey failed");
+		return -1;
+	}
+	cont_encr_key_len = data.enc_key_len - AES_BLOCK_SIZE;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: content-encryption key",
+			cont_encr_key, cont_encr_key_len);
+
+	if (data.enc_cont_len < AES_BLOCK_SIZE)
+		return -1;
+	key_pkg_len = data.enc_cont_len - AES_BLOCK_SIZE;
+	key_pkg = os_malloc(key_pkg_len);
+	if (!key_pkg)
+		return -1;
+	res = aes_siv_decrypt(cont_encr_key, cont_encr_key_len,
+			      data.enc_cont, data.enc_cont_len,
+			      0, NULL, NULL, key_pkg);
+	forced_memzero(cont_encr_key, cont_encr_key_len);
+	if (res < 0) {
+		bin_clear_free(key_pkg, key_pkg_len);
+		wpa_printf(MSG_DEBUG,
+			   "DPP: AES-SIV decryption of encryptedContent failed");
+		return -1;
+	}
+
+	keys = dpp_parse_dpp_asymmetric_key_package(key_pkg, key_pkg_len);
+	bin_clear_free(key_pkg, key_pkg_len);
+	dpp_free_asymmetric_key(auth->conf_key_pkg);
+	auth->conf_key_pkg = keys;
+
+	return keys != NULL;
+}
+
+#endif /* CONFIG_DPP2 */
diff --git a/src/common/dpp_crypto.c b/src/common/dpp_crypto.c
new file mode 100644
index 0000000..8bf2a74
--- /dev/null
+++ b/src/common/dpp_crypto.c
@@ -0,0 +1,2785 @@
+/*
+ * DPP crypto functionality
+ * 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 <openssl/opensslv.h>
+#include <openssl/err.h>
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+
+#include "utils/common.h"
+#include "utils/base64.h"
+#include "utils/json.h"
+#include "common/ieee802_11_defs.h"
+#include "crypto/crypto.h"
+#include "crypto/sha384.h"
+#include "crypto/sha512.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+	(defined(LIBRESSL_VERSION_NUMBER) && \
+	 LIBRESSL_VERSION_NUMBER < 0x20700000L)
+/* Compatibility wrappers for older versions. */
+
+static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
+{
+	sig->r = r;
+	sig->s = s;
+	return 1;
+}
+
+
+static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr,
+			   const BIGNUM **ps)
+{
+	if (pr)
+		*pr = sig->r;
+	if (ps)
+		*ps = sig->s;
+}
+
+
+static EC_KEY * EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
+{
+	if (pkey->type != EVP_PKEY_EC)
+		return NULL;
+	return pkey->pkey.ec;
+}
+
+#endif
+
+static const struct dpp_curve_params dpp_curves[] = {
+	/* The mandatory to support and the default NIST P-256 curve needs to
+	 * be the first entry on this list. */
+	{ "prime256v1", 32, 32, 16, 32, "P-256", 19, "ES256" },
+	{ "secp384r1", 48, 48, 24, 48, "P-384", 20, "ES384" },
+	{ "secp521r1", 64, 64, 32, 66, "P-521", 21, "ES512" },
+	{ "brainpoolP256r1", 32, 32, 16, 32, "BP-256", 28, "BS256" },
+	{ "brainpoolP384r1", 48, 48, 24, 48, "BP-384", 29, "BS384" },
+	{ "brainpoolP512r1", 64, 64, 32, 64, "BP-512", 30, "BS512" },
+	{ NULL, 0, 0, 0, 0, NULL, 0, NULL }
+};
+
+
+const struct dpp_curve_params * dpp_get_curve_name(const char *name)
+{
+	int i;
+
+	if (!name)
+		return &dpp_curves[0];
+
+	for (i = 0; dpp_curves[i].name; i++) {
+		if (os_strcmp(name, dpp_curves[i].name) == 0 ||
+		    (dpp_curves[i].jwk_crv &&
+		     os_strcmp(name, dpp_curves[i].jwk_crv) == 0))
+			return &dpp_curves[i];
+	}
+	return NULL;
+}
+
+
+const struct dpp_curve_params * dpp_get_curve_jwk_crv(const char *name)
+{
+	int i;
+
+	for (i = 0; dpp_curves[i].name; i++) {
+		if (dpp_curves[i].jwk_crv &&
+		    os_strcmp(name, dpp_curves[i].jwk_crv) == 0)
+			return &dpp_curves[i];
+	}
+	return NULL;
+}
+
+
+static const struct dpp_curve_params *
+dpp_get_curve_oid(const ASN1_OBJECT *poid)
+{
+	ASN1_OBJECT *oid;
+	int i;
+
+	for (i = 0; dpp_curves[i].name; i++) {
+		oid = OBJ_txt2obj(dpp_curves[i].name, 0);
+		if (oid && OBJ_cmp(poid, oid) == 0)
+			return &dpp_curves[i];
+	}
+	return NULL;
+}
+
+
+const struct dpp_curve_params * dpp_get_curve_nid(int nid)
+{
+	int i, tmp;
+
+	if (!nid)
+		return NULL;
+	for (i = 0; dpp_curves[i].name; i++) {
+		tmp = OBJ_txt2nid(dpp_curves[i].name);
+		if (tmp == nid)
+			return &dpp_curves[i];
+	}
+	return NULL;
+}
+
+
+void dpp_debug_print_point(const char *title, const EC_GROUP *group,
+			   const EC_POINT *point)
+{
+	BIGNUM *x, *y;
+	BN_CTX *ctx;
+	char *x_str = NULL, *y_str = NULL;
+
+	if (!wpa_debug_show_keys)
+		return;
+
+	ctx = BN_CTX_new();
+	x = BN_new();
+	y = BN_new();
+	if (!ctx || !x || !y ||
+	    EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx) != 1)
+		goto fail;
+
+	x_str = BN_bn2hex(x);
+	y_str = BN_bn2hex(y);
+	if (!x_str || !y_str)
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "%s (%s,%s)", title, x_str, y_str);
+
+fail:
+	OPENSSL_free(x_str);
+	OPENSSL_free(y_str);
+	BN_free(x);
+	BN_free(y);
+	BN_CTX_free(ctx);
+}
+
+
+void dpp_debug_print_key(const char *title, EVP_PKEY *key)
+{
+	EC_KEY *eckey;
+	BIO *out;
+	size_t rlen;
+	char *txt;
+	int res;
+	unsigned char *der = NULL;
+	int der_len;
+	const EC_GROUP *group;
+	const EC_POINT *point;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return;
+
+	EVP_PKEY_print_private(out, key, 0, NULL);
+	rlen = BIO_ctrl_pending(out);
+	txt = os_malloc(rlen + 1);
+	if (txt) {
+		res = BIO_read(out, txt, rlen);
+		if (res > 0) {
+			txt[res] = '\0';
+			wpa_printf(MSG_DEBUG, "%s: %s", title, txt);
+		}
+		os_free(txt);
+	}
+	BIO_free(out);
+
+	eckey = EVP_PKEY_get1_EC_KEY(key);
+	if (!eckey)
+		return;
+
+	group = EC_KEY_get0_group(eckey);
+	point = EC_KEY_get0_public_key(eckey);
+	if (group && point)
+		dpp_debug_print_point(title, group, point);
+
+	der_len = i2d_ECPrivateKey(eckey, &der);
+	if (der_len > 0)
+		wpa_hexdump_key(MSG_DEBUG, "DPP: ECPrivateKey", der, der_len);
+	OPENSSL_free(der);
+	if (der_len <= 0) {
+		der = NULL;
+		der_len = i2d_EC_PUBKEY(eckey, &der);
+		if (der_len > 0)
+			wpa_hexdump(MSG_DEBUG, "DPP: EC_PUBKEY", der, der_len);
+		OPENSSL_free(der);
+	}
+
+	EC_KEY_free(eckey);
+}
+
+
+static int dpp_hash_vector(const struct dpp_curve_params *curve,
+			   size_t num_elem, const u8 *addr[], const size_t *len,
+			   u8 *mac)
+{
+	if (curve->hash_len == 32)
+		return sha256_vector(num_elem, addr, len, mac);
+	if (curve->hash_len == 48)
+		return sha384_vector(num_elem, addr, len, mac);
+	if (curve->hash_len == 64)
+		return sha512_vector(num_elem, addr, len, mac);
+	return -1;
+}
+
+
+int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len,
+		    const char *label, u8 *out, size_t outlen)
+{
+	if (hash_len == 32)
+		return hmac_sha256_kdf(secret, secret_len, NULL,
+				       (const u8 *) label, os_strlen(label),
+				       out, outlen);
+	if (hash_len == 48)
+		return hmac_sha384_kdf(secret, secret_len, NULL,
+				       (const u8 *) label, os_strlen(label),
+				       out, outlen);
+	if (hash_len == 64)
+		return hmac_sha512_kdf(secret, secret_len, NULL,
+				       (const u8 *) label, os_strlen(label),
+				       out, outlen);
+	return -1;
+}
+
+
+int dpp_hmac_vector(size_t hash_len, const u8 *key, size_t key_len,
+		    size_t num_elem, const u8 *addr[], const size_t *len,
+		    u8 *mac)
+{
+	if (hash_len == 32)
+		return hmac_sha256_vector(key, key_len, num_elem, addr, len,
+					  mac);
+	if (hash_len == 48)
+		return hmac_sha384_vector(key, key_len, num_elem, addr, len,
+					  mac);
+	if (hash_len == 64)
+		return hmac_sha512_vector(key, key_len, num_elem, addr, len,
+					  mac);
+	return -1;
+}
+
+
+static int dpp_hmac(size_t hash_len, const u8 *key, size_t key_len,
+		    const u8 *data, size_t data_len, u8 *mac)
+{
+	if (hash_len == 32)
+		return hmac_sha256(key, key_len, data, data_len, mac);
+	if (hash_len == 48)
+		return hmac_sha384(key, key_len, data, data_len, mac);
+	if (hash_len == 64)
+		return hmac_sha512(key, key_len, data, data_len, mac);
+	return -1;
+}
+
+
+#ifdef CONFIG_DPP2
+
+static int dpp_pbkdf2_f(size_t hash_len,
+			const u8 *password, size_t password_len,
+			const u8 *salt, size_t salt_len,
+			unsigned int iterations, unsigned int count, u8 *digest)
+{
+	unsigned char tmp[DPP_MAX_HASH_LEN], tmp2[DPP_MAX_HASH_LEN];
+	unsigned int i;
+	size_t j;
+	u8 count_buf[4];
+	const u8 *addr[2];
+	size_t len[2];
+
+	addr[0] = salt;
+	len[0] = salt_len;
+	addr[1] = count_buf;
+	len[1] = 4;
+
+	/* F(P, S, c, i) = U1 xor U2 xor ... Uc
+	 * U1 = PRF(P, S || i)
+	 * U2 = PRF(P, U1)
+	 * Uc = PRF(P, Uc-1)
+	 */
+
+	WPA_PUT_BE32(count_buf, count);
+	if (dpp_hmac_vector(hash_len, password, password_len, 2, addr, len,
+			    tmp))
+		return -1;
+	os_memcpy(digest, tmp, hash_len);
+
+	for (i = 1; i < iterations; i++) {
+		if (dpp_hmac(hash_len, password, password_len, tmp, hash_len,
+			     tmp2))
+			return -1;
+		os_memcpy(tmp, tmp2, hash_len);
+		for (j = 0; j < hash_len; j++)
+			digest[j] ^= tmp2[j];
+	}
+
+	return 0;
+}
+
+
+int dpp_pbkdf2(size_t hash_len, const u8 *password, size_t password_len,
+	       const u8 *salt, size_t salt_len, unsigned int iterations,
+	       u8 *buf, size_t buflen)
+{
+	unsigned int count = 0;
+	unsigned char *pos = buf;
+	size_t left = buflen, plen;
+	unsigned char digest[DPP_MAX_HASH_LEN];
+
+	while (left > 0) {
+		count++;
+		if (dpp_pbkdf2_f(hash_len, password, password_len,
+				 salt, salt_len, iterations, count, digest))
+			return -1;
+		plen = left > hash_len ? hash_len : left;
+		os_memcpy(pos, digest, plen);
+		pos += plen;
+		left -= plen;
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_DPP2 */
+
+
+int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len)
+{
+	int num_bytes, offset;
+
+	num_bytes = BN_num_bytes(bn);
+	if ((size_t) num_bytes > len)
+		return -1;
+	offset = len - num_bytes;
+	os_memset(pos, 0, offset);
+	BN_bn2bin(bn, pos + offset);
+	return 0;
+}
+
+
+struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix)
+{
+	int len, res;
+	EC_KEY *eckey;
+	struct wpabuf *buf;
+	unsigned char *pos;
+
+	eckey = EVP_PKEY_get1_EC_KEY(pkey);
+	if (!eckey)
+		return NULL;
+	EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED);
+	len = i2o_ECPublicKey(eckey, NULL);
+	if (len <= 0) {
+		wpa_printf(MSG_ERROR,
+			   "DDP: Failed to determine public key encoding length");
+		EC_KEY_free(eckey);
+		return NULL;
+	}
+
+	buf = wpabuf_alloc(len);
+	if (!buf) {
+		EC_KEY_free(eckey);
+		return NULL;
+	}
+
+	pos = wpabuf_put(buf, len);
+	res = i2o_ECPublicKey(eckey, &pos);
+	EC_KEY_free(eckey);
+	if (res != len) {
+		wpa_printf(MSG_ERROR,
+			   "DDP: Failed to encode public key (res=%d/%d)",
+			   res, len);
+		wpabuf_free(buf);
+		return NULL;
+	}
+
+	if (!prefix) {
+		/* Remove 0x04 prefix to match DPP definition */
+		pos = wpabuf_mhead(buf);
+		os_memmove(pos, pos + 1, len - 1);
+		buf->used--;
+	}
+
+	return buf;
+}
+
+
+EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group,
+				      const u8 *buf_x, const u8 *buf_y,
+				      size_t len)
+{
+	EC_KEY *eckey = NULL;
+	BN_CTX *ctx;
+	EC_POINT *point = NULL;
+	BIGNUM *x = NULL, *y = NULL;
+	EVP_PKEY *pkey = NULL;
+
+	ctx = BN_CTX_new();
+	if (!ctx) {
+		wpa_printf(MSG_ERROR, "DPP: Out of memory");
+		return NULL;
+	}
+
+	point = EC_POINT_new(group);
+	x = BN_bin2bn(buf_x, len, NULL);
+	y = BN_bin2bn(buf_y, len, NULL);
+	if (!point || !x || !y) {
+		wpa_printf(MSG_ERROR, "DPP: Out of memory");
+		goto fail;
+	}
+
+	if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	if (!EC_POINT_is_on_curve(group, point, ctx) ||
+	    EC_POINT_is_at_infinity(group, point)) {
+		wpa_printf(MSG_ERROR, "DPP: Invalid point");
+		goto fail;
+	}
+	dpp_debug_print_point("DPP: dpp_set_pubkey_point_group", group, point);
+
+	eckey = EC_KEY_new();
+	if (!eckey ||
+	    EC_KEY_set_group(eckey, group) != 1 ||
+	    EC_KEY_set_public_key(eckey, point) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to set EC_KEY: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
+
+	pkey = EVP_PKEY_new();
+	if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) {
+		wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY");
+		goto fail;
+	}
+
+out:
+	BN_free(x);
+	BN_free(y);
+	EC_KEY_free(eckey);
+	EC_POINT_free(point);
+	BN_CTX_free(ctx);
+	return pkey;
+fail:
+	EVP_PKEY_free(pkey);
+	pkey = NULL;
+	goto out;
+}
+
+
+EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key, const u8 *buf, size_t len)
+{
+	const EC_KEY *eckey;
+	const EC_GROUP *group;
+	EVP_PKEY *pkey = NULL;
+
+	if (len & 1)
+		return NULL;
+
+	eckey = EVP_PKEY_get0_EC_KEY(group_key);
+	if (!eckey) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Could not get EC_KEY from group_key");
+		return NULL;
+	}
+
+	group = EC_KEY_get0_group(eckey);
+	if (group)
+		pkey = dpp_set_pubkey_point_group(group, buf, buf + len / 2,
+						  len / 2);
+	else
+		wpa_printf(MSG_ERROR, "DPP: Could not get EC group");
+
+	return pkey;
+}
+
+
+EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve)
+{
+	EVP_PKEY_CTX *kctx = NULL;
+	EC_KEY *ec_params = NULL;
+	EVP_PKEY *params = NULL, *key = NULL;
+	int nid;
+
+	wpa_printf(MSG_DEBUG, "DPP: Generating a keypair");
+
+	nid = OBJ_txt2nid(curve->name);
+	if (nid == NID_undef) {
+		wpa_printf(MSG_INFO, "DPP: Unsupported curve %s", curve->name);
+		return NULL;
+	}
+
+	ec_params = EC_KEY_new_by_curve_name(nid);
+	if (!ec_params) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to generate EC_KEY parameters");
+		goto fail;
+	}
+	EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE);
+	params = EVP_PKEY_new();
+	if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to generate EVP_PKEY parameters");
+		goto fail;
+	}
+
+	kctx = EVP_PKEY_CTX_new(params, NULL);
+	if (!kctx ||
+	    EVP_PKEY_keygen_init(kctx) != 1 ||
+	    EVP_PKEY_keygen(kctx, &key) != 1) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to generate EC key");
+		key = NULL;
+		goto fail;
+	}
+
+	if (wpa_debug_show_keys)
+		dpp_debug_print_key("Own generated key", key);
+
+fail:
+	EC_KEY_free(ec_params);
+	EVP_PKEY_free(params);
+	EVP_PKEY_CTX_free(kctx);
+	return key;
+}
+
+
+EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve,
+			   const u8 *privkey, size_t privkey_len)
+{
+	EVP_PKEY *pkey;
+	EC_KEY *eckey;
+	const EC_GROUP *group;
+	int nid;
+
+	pkey = EVP_PKEY_new();
+	if (!pkey)
+		return NULL;
+	eckey = d2i_ECPrivateKey(NULL, &privkey, privkey_len);
+	if (!eckey) {
+		wpa_printf(MSG_INFO,
+			   "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		EVP_PKEY_free(pkey);
+		return NULL;
+	}
+	group = EC_KEY_get0_group(eckey);
+	if (!group) {
+		EC_KEY_free(eckey);
+		EVP_PKEY_free(pkey);
+		return NULL;
+	}
+	nid = EC_GROUP_get_curve_name(group);
+	*curve = dpp_get_curve_nid(nid);
+	if (!*curve) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Unsupported curve (nid=%d) in pre-assigned key",
+			   nid);
+		EC_KEY_free(eckey);
+		EVP_PKEY_free(pkey);
+		return NULL;
+	}
+
+	if (EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) {
+		EC_KEY_free(eckey);
+		EVP_PKEY_free(pkey);
+		return NULL;
+	}
+	return pkey;
+}
+
+
+typedef struct {
+	/* AlgorithmIdentifier ecPublicKey with optional parameters present
+	 * as an OID identifying the curve */
+	X509_ALGOR *alg;
+	/* Compressed format public key per ANSI X9.63 */
+	ASN1_BIT_STRING *pub_key;
+} DPP_BOOTSTRAPPING_KEY;
+
+ASN1_SEQUENCE(DPP_BOOTSTRAPPING_KEY) = {
+	ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, alg, X509_ALGOR),
+	ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, pub_key, ASN1_BIT_STRING)
+} ASN1_SEQUENCE_END(DPP_BOOTSTRAPPING_KEY);
+
+IMPLEMENT_ASN1_FUNCTIONS(DPP_BOOTSTRAPPING_KEY);
+
+
+static struct wpabuf * dpp_bootstrap_key_der(EVP_PKEY *key)
+{
+	unsigned char *der = NULL;
+	int der_len;
+	const EC_KEY *eckey;
+	struct wpabuf *ret = NULL;
+	size_t len;
+	const EC_GROUP *group;
+	const EC_POINT *point;
+	BN_CTX *ctx;
+	DPP_BOOTSTRAPPING_KEY *bootstrap = NULL;
+	int nid;
+
+	ctx = BN_CTX_new();
+	eckey = EVP_PKEY_get0_EC_KEY(key);
+	if (!ctx || !eckey)
+		goto fail;
+
+	group = EC_KEY_get0_group(eckey);
+	point = EC_KEY_get0_public_key(eckey);
+	if (!group || !point)
+		goto fail;
+	dpp_debug_print_point("DPP: bootstrap public key", group, point);
+	nid = EC_GROUP_get_curve_name(group);
+
+	bootstrap = DPP_BOOTSTRAPPING_KEY_new();
+	if (!bootstrap ||
+	    X509_ALGOR_set0(bootstrap->alg, OBJ_nid2obj(EVP_PKEY_EC),
+			    V_ASN1_OBJECT, (void *) OBJ_nid2obj(nid)) != 1)
+		goto fail;
+
+	len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
+				 NULL, 0, ctx);
+	if (len == 0)
+		goto fail;
+
+	der = OPENSSL_malloc(len);
+	if (!der)
+		goto fail;
+	len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
+				 der, len, ctx);
+
+	OPENSSL_free(bootstrap->pub_key->data);
+	bootstrap->pub_key->data = der;
+	der = NULL;
+	bootstrap->pub_key->length = len;
+	/* No unused bits */
+	bootstrap->pub_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+	bootstrap->pub_key->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+
+	der_len = i2d_DPP_BOOTSTRAPPING_KEY(bootstrap, &der);
+	if (der_len <= 0) {
+		wpa_printf(MSG_ERROR,
+			   "DDP: Failed to build DER encoded public key");
+		goto fail;
+	}
+
+	ret = wpabuf_alloc_copy(der, der_len);
+fail:
+	DPP_BOOTSTRAPPING_KEY_free(bootstrap);
+	OPENSSL_free(der);
+	BN_CTX_free(ctx);
+	return ret;
+}
+
+
+int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi)
+{
+	struct wpabuf *der;
+	int res;
+
+	der = dpp_bootstrap_key_der(bi->pubkey);
+	if (!der)
+		return -1;
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
+			der);
+	res = dpp_bi_pubkey_hash(bi, wpabuf_head(der), wpabuf_len(der));
+	if (res < 0)
+		wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+	wpabuf_free(der);
+	return res;
+}
+
+
+int dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
+	       const u8 *privkey, size_t privkey_len)
+{
+	char *base64 = NULL;
+	char *pos, *end;
+	size_t len;
+	struct wpabuf *der = NULL;
+
+	bi->curve = dpp_get_curve_name(curve);
+	if (!bi->curve) {
+		wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", curve);
+		return -1;
+	}
+
+	if (privkey)
+		bi->pubkey = dpp_set_keypair(&bi->curve, privkey, privkey_len);
+	else
+		bi->pubkey = dpp_gen_keypair(bi->curve);
+	if (!bi->pubkey)
+		goto fail;
+	bi->own = 1;
+
+	der = dpp_bootstrap_key_der(bi->pubkey);
+	if (!der)
+		goto fail;
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
+			der);
+
+	if (dpp_bi_pubkey_hash(bi, wpabuf_head(der), wpabuf_len(der)) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+		goto fail;
+	}
+
+	base64 = base64_encode(wpabuf_head(der), wpabuf_len(der), &len);
+	wpabuf_free(der);
+	der = NULL;
+	if (!base64)
+		goto fail;
+	pos = base64;
+	end = pos + len;
+	for (;;) {
+		pos = os_strchr(pos, '\n');
+		if (!pos)
+			break;
+		os_memmove(pos, pos + 1, end - pos);
+	}
+	os_free(bi->pk);
+	bi->pk = base64;
+	return 0;
+fail:
+	os_free(base64);
+	wpabuf_free(der);
+	return -1;
+}
+
+
+int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1, unsigned int hash_len)
+{
+	u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+	const char *info = "first intermediate key";
+	int res;
+
+	/* k1 = HKDF(<>, "first intermediate key", M.x) */
+
+	/* HKDF-Extract(<>, M.x) */
+	os_memset(salt, 0, hash_len);
+	if (dpp_hmac(hash_len, salt, hash_len, Mx, Mx_len, prk) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=M.x)",
+			prk, hash_len);
+
+	/* HKDF-Expand(PRK, info, L) */
+	res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k1, hash_len);
+	os_memset(prk, 0, hash_len);
+	if (res < 0)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: k1 = HKDF-Expand(PRK, info, L)",
+			k1, hash_len);
+	return 0;
+}
+
+
+int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2, unsigned int hash_len)
+{
+	u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+	const char *info = "second intermediate key";
+	int res;
+
+	/* k2 = HKDF(<>, "second intermediate key", N.x) */
+
+	/* HKDF-Extract(<>, N.x) */
+	os_memset(salt, 0, hash_len);
+	res = dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk);
+	if (res < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
+			prk, hash_len);
+
+	/* HKDF-Expand(PRK, info, L) */
+	res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k2, hash_len);
+	os_memset(prk, 0, hash_len);
+	if (res < 0)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: k2 = HKDF-Expand(PRK, info, L)",
+			k2, hash_len);
+	return 0;
+}
+
+
+int dpp_derive_bk_ke(struct dpp_authentication *auth)
+{
+	unsigned int hash_len = auth->curve->hash_len;
+	size_t nonce_len = auth->curve->nonce_len;
+	u8 nonces[2 * DPP_MAX_NONCE_LEN];
+	const char *info_ke = "DPP Key";
+	int res;
+	const u8 *addr[3];
+	size_t len[3];
+	size_t num_elem = 0;
+
+	if (!auth->Mx_len || !auth->Nx_len) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Mx/Nx not available - cannot derive ke");
+		return -1;
+	}
+
+	/* bk = HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x]) */
+	os_memcpy(nonces, auth->i_nonce, nonce_len);
+	os_memcpy(&nonces[nonce_len], auth->r_nonce, nonce_len);
+	addr[num_elem] = auth->Mx;
+	len[num_elem] = auth->Mx_len;
+	num_elem++;
+	addr[num_elem] = auth->Nx;
+	len[num_elem] = auth->Nx_len;
+	num_elem++;
+	if (auth->peer_bi && auth->own_bi) {
+		if (!auth->Lx_len) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Lx not available - cannot derive ke");
+			return -1;
+		}
+		addr[num_elem] = auth->Lx;
+		len[num_elem] = auth->secret_len;
+		num_elem++;
+	}
+	res = dpp_hmac_vector(hash_len, nonces, 2 * nonce_len,
+			      num_elem, addr, len, auth->bk);
+	if (res < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG,
+			"DPP: bk = HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x])",
+			auth->bk, hash_len);
+
+	/* ke = HKDF-Expand(bkK, "DPP Key", length) */
+	res = dpp_hkdf_expand(hash_len, auth->bk, hash_len, info_ke, auth->ke,
+			      hash_len);
+	if (res < 0)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG,
+			"DPP: ke = HKDF-Expand(bk, \"DPP Key\", length)",
+			auth->ke, hash_len);
+
+	return 0;
+}
+
+
+int dpp_ecdh(EVP_PKEY *own, EVP_PKEY *peer, u8 *secret, size_t *secret_len)
+{
+	EVP_PKEY_CTX *ctx;
+	int ret = -1;
+
+	ERR_clear_error();
+	*secret_len = 0;
+
+	ctx = EVP_PKEY_CTX_new(own, NULL);
+	if (!ctx) {
+		wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_CTX_new failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		return -1;
+	}
+
+	if (EVP_PKEY_derive_init(ctx) != 1) {
+		wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive_init failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	if (EVP_PKEY_derive_set_peer(ctx, peer) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: EVP_PKEY_derive_set_peet failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	if (EVP_PKEY_derive(ctx, NULL, secret_len) != 1) {
+		wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive(NULL) failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	if (*secret_len > DPP_MAX_SHARED_SECRET_LEN) {
+		u8 buf[200];
+		int level = *secret_len > 200 ? MSG_ERROR : MSG_DEBUG;
+
+		/* It looks like OpenSSL can return unexpectedly large buffer
+		 * need for shared secret from EVP_PKEY_derive(NULL) in some
+		 * cases. For example, group 19 has shown cases where secret_len
+		 * is set to 72 even though the actual length ends up being
+		 * updated to 32 when EVP_PKEY_derive() is called with a buffer
+		 * for the value. Work around this by trying to fetch the value
+		 * and continue if it is within supported range even when the
+		 * initial buffer need is claimed to be larger. */
+		wpa_printf(level,
+			   "DPP: Unexpected secret_len=%d from EVP_PKEY_derive()",
+			   (int) *secret_len);
+		if (*secret_len > 200)
+			goto fail;
+		if (EVP_PKEY_derive(ctx, buf, secret_len) != 1) {
+			wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive failed: %s",
+				   ERR_error_string(ERR_get_error(), NULL));
+			goto fail;
+		}
+		if (*secret_len > DPP_MAX_SHARED_SECRET_LEN) {
+			wpa_printf(MSG_ERROR,
+				   "DPP: Unexpected secret_len=%d from EVP_PKEY_derive()",
+				   (int) *secret_len);
+			goto fail;
+		}
+		wpa_hexdump_key(MSG_DEBUG, "DPP: Unexpected secret_len change",
+				buf, *secret_len);
+		os_memcpy(secret, buf, *secret_len);
+		forced_memzero(buf, sizeof(buf));
+		goto done;
+	}
+
+	if (EVP_PKEY_derive(ctx, secret, secret_len) != 1) {
+		wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+done:
+	ret = 0;
+
+fail:
+	EVP_PKEY_CTX_free(ctx);
+	return ret;
+}
+
+
+int dpp_bi_pubkey_hash(struct dpp_bootstrap_info *bi,
+		       const u8 *data, size_t data_len)
+{
+	const u8 *addr[2];
+	size_t len[2];
+
+	addr[0] = data;
+	len[0] = data_len;
+	if (sha256_vector(1, addr, len, bi->pubkey_hash) < 0)
+		return -1;
+	wpa_hexdump(MSG_DEBUG, "DPP: Public key hash",
+		    bi->pubkey_hash, SHA256_MAC_LEN);
+
+	addr[0] = (const u8 *) "chirp";
+	len[0] = 5;
+	addr[1] = data;
+	len[1] = data_len;
+	if (sha256_vector(2, addr, len, bi->pubkey_hash_chirp) < 0)
+		return -1;
+	wpa_hexdump(MSG_DEBUG, "DPP: Public key hash (chirp)",
+		    bi->pubkey_hash_chirp, SHA256_MAC_LEN);
+
+	return 0;
+}
+
+
+int dpp_get_subject_public_key(struct dpp_bootstrap_info *bi,
+			       const u8 *data, size_t data_len)
+{
+	EVP_PKEY *pkey;
+	const unsigned char *p;
+	int res;
+	X509_PUBKEY *pub = NULL;
+	ASN1_OBJECT *ppkalg;
+	const unsigned char *pk;
+	int ppklen;
+	X509_ALGOR *pa;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+	(defined(LIBRESSL_VERSION_NUMBER) && \
+	 LIBRESSL_VERSION_NUMBER < 0x20800000L)
+	ASN1_OBJECT *pa_oid;
+#else
+	const ASN1_OBJECT *pa_oid;
+#endif
+	const void *pval;
+	int ptype;
+	const ASN1_OBJECT *poid;
+	char buf[100];
+
+	if (dpp_bi_pubkey_hash(bi, data, data_len) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+		return -1;
+	}
+
+	/* DER encoded ASN.1 SubjectPublicKeyInfo
+	 *
+	 * SubjectPublicKeyInfo  ::=  SEQUENCE  {
+	 *      algorithm            AlgorithmIdentifier,
+	 *      subjectPublicKey     BIT STRING  }
+	 *
+	 * AlgorithmIdentifier  ::=  SEQUENCE  {
+	 *      algorithm               OBJECT IDENTIFIER,
+	 *      parameters              ANY DEFINED BY algorithm OPTIONAL  }
+	 *
+	 * subjectPublicKey = compressed format public key per ANSI X9.63
+	 * algorithm = ecPublicKey (1.2.840.10045.2.1)
+	 * parameters = shall be present and shall be OBJECT IDENTIFIER; e.g.,
+	 *       prime256v1 (1.2.840.10045.3.1.7)
+	 */
+
+	p = data;
+	pkey = d2i_PUBKEY(NULL, &p, data_len);
+
+	if (!pkey) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not parse URI public-key SubjectPublicKeyInfo");
+		return -1;
+	}
+
+	if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: SubjectPublicKeyInfo does not describe an EC key");
+		EVP_PKEY_free(pkey);
+		return -1;
+	}
+
+	res = X509_PUBKEY_set(&pub, pkey);
+	if (res != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: Could not set pubkey");
+		goto fail;
+	}
+
+	res = X509_PUBKEY_get0_param(&ppkalg, &pk, &ppklen, &pa, pub);
+	if (res != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not extract SubjectPublicKeyInfo parameters");
+		goto fail;
+	}
+	res = OBJ_obj2txt(buf, sizeof(buf), ppkalg, 0);
+	if (res < 0 || (size_t) res >= sizeof(buf)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not extract SubjectPublicKeyInfo algorithm");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey algorithm: %s", buf);
+	if (os_strcmp(buf, "id-ecPublicKey") != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unsupported SubjectPublicKeyInfo algorithm");
+		goto fail;
+	}
+
+	X509_ALGOR_get0(&pa_oid, &ptype, (void *) &pval, pa);
+	if (ptype != V_ASN1_OBJECT) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: SubjectPublicKeyInfo parameters did not contain an OID");
+		goto fail;
+	}
+	poid = pval;
+	res = OBJ_obj2txt(buf, sizeof(buf), poid, 0);
+	if (res < 0 || (size_t) res >= sizeof(buf)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not extract SubjectPublicKeyInfo parameters OID");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey parameters: %s", buf);
+	bi->curve = dpp_get_curve_oid(poid);
+	if (!bi->curve) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unsupported SubjectPublicKeyInfo curve: %s",
+			   buf);
+		goto fail;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "DPP: URI subjectPublicKey", pk, ppklen);
+
+	X509_PUBKEY_free(pub);
+	bi->pubkey = pkey;
+	return 0;
+fail:
+	X509_PUBKEY_free(pub);
+	EVP_PKEY_free(pkey);
+	return -1;
+}
+
+
+static struct wpabuf *
+dpp_parse_jws_prot_hdr(const struct dpp_curve_params *curve,
+		       const u8 *prot_hdr, u16 prot_hdr_len,
+		       const EVP_MD **ret_md)
+{
+	struct json_token *root, *token;
+	struct wpabuf *kid = NULL;
+
+	root = json_parse((const char *) prot_hdr, prot_hdr_len);
+	if (!root) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: JSON parsing failed for JWS Protected Header");
+		goto fail;
+	}
+
+	if (root->type != JSON_OBJECT) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: JWS Protected Header root is not an object");
+		goto fail;
+	}
+
+	token = json_get_member(root, "typ");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG, "DPP: No typ string value found");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header typ=%s",
+		   token->string);
+	if (os_strcmp(token->string, "dppCon") != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unsupported JWS Protected Header typ=%s",
+			   token->string);
+		goto fail;
+	}
+
+	token = json_get_member(root, "alg");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG, "DPP: No alg string value found");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header alg=%s",
+		   token->string);
+	if (os_strcmp(token->string, curve->jws_alg) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected JWS Protected Header alg=%s (expected %s based on C-sign-key)",
+			   token->string, curve->jws_alg);
+		goto fail;
+	}
+	if (os_strcmp(token->string, "ES256") == 0 ||
+	    os_strcmp(token->string, "BS256") == 0)
+		*ret_md = EVP_sha256();
+	else if (os_strcmp(token->string, "ES384") == 0 ||
+		 os_strcmp(token->string, "BS384") == 0)
+		*ret_md = EVP_sha384();
+	else if (os_strcmp(token->string, "ES512") == 0 ||
+		 os_strcmp(token->string, "BS512") == 0)
+		*ret_md = EVP_sha512();
+	else
+		*ret_md = NULL;
+	if (!*ret_md) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unsupported JWS Protected Header alg=%s",
+			   token->string);
+		goto fail;
+	}
+
+	kid = json_get_member_base64url(root, "kid");
+	if (!kid) {
+		wpa_printf(MSG_DEBUG, "DPP: No kid string value found");
+		goto fail;
+	}
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: JWS Protected Header kid (decoded)",
+			kid);
+
+fail:
+	json_free(root);
+	return kid;
+}
+
+
+static int dpp_check_pubkey_match(EVP_PKEY *pub, struct wpabuf *r_hash)
+{
+	struct wpabuf *uncomp;
+	int res;
+	u8 hash[SHA256_MAC_LEN];
+	const u8 *addr[1];
+	size_t len[1];
+
+	if (wpabuf_len(r_hash) != SHA256_MAC_LEN)
+		return -1;
+	uncomp = dpp_get_pubkey_point(pub, 1);
+	if (!uncomp)
+		return -1;
+	addr[0] = wpabuf_head(uncomp);
+	len[0] = wpabuf_len(uncomp);
+	wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed public key",
+		    addr[0], len[0]);
+	res = sha256_vector(1, addr, len, hash);
+	wpabuf_free(uncomp);
+	if (res < 0)
+		return -1;
+	if (os_memcmp(hash, wpabuf_head(r_hash), SHA256_MAC_LEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Received hash value does not match calculated public key hash value");
+		wpa_hexdump(MSG_DEBUG, "DPP: Calculated hash",
+			    hash, SHA256_MAC_LEN);
+		return -1;
+	}
+	return 0;
+}
+
+
+enum dpp_status_error
+dpp_process_signed_connector(struct dpp_signed_connector_info *info,
+			     EVP_PKEY *csign_pub, const char *connector)
+{
+	enum dpp_status_error ret = 255;
+	const char *pos, *end, *signed_start, *signed_end;
+	struct wpabuf *kid = NULL;
+	unsigned char *prot_hdr = NULL, *signature = NULL;
+	size_t prot_hdr_len = 0, signature_len = 0;
+	const EVP_MD *sign_md = NULL;
+	unsigned char *der = NULL;
+	int der_len;
+	int res;
+	EVP_MD_CTX *md_ctx = NULL;
+	ECDSA_SIG *sig = NULL;
+	BIGNUM *r = NULL, *s = NULL;
+	const struct dpp_curve_params *curve;
+	const EC_KEY *eckey;
+	const EC_GROUP *group;
+	int nid;
+
+	eckey = EVP_PKEY_get0_EC_KEY(csign_pub);
+	if (!eckey)
+		goto fail;
+	group = EC_KEY_get0_group(eckey);
+	if (!group)
+		goto fail;
+	nid = EC_GROUP_get_curve_name(group);
+	curve = dpp_get_curve_nid(nid);
+	if (!curve)
+		goto fail;
+	wpa_printf(MSG_DEBUG, "DPP: C-sign-key group: %s", curve->jwk_crv);
+	os_memset(info, 0, sizeof(*info));
+
+	signed_start = pos = connector;
+	end = os_strchr(pos, '.');
+	if (!end) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing dot(1) in signedConnector");
+		ret = DPP_STATUS_INVALID_CONNECTOR;
+		goto fail;
+	}
+	prot_hdr = base64_url_decode(pos, end - pos, &prot_hdr_len);
+	if (!prot_hdr) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to base64url decode signedConnector JWS Protected Header");
+		ret = DPP_STATUS_INVALID_CONNECTOR;
+		goto fail;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG,
+			  "DPP: signedConnector - JWS Protected Header",
+			  prot_hdr, prot_hdr_len);
+	kid = dpp_parse_jws_prot_hdr(curve, prot_hdr, prot_hdr_len, &sign_md);
+	if (!kid) {
+		ret = DPP_STATUS_INVALID_CONNECTOR;
+		goto fail;
+	}
+	if (wpabuf_len(kid) != SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected signedConnector JWS Protected Header kid length: %u (expected %u)",
+			   (unsigned int) wpabuf_len(kid), SHA256_MAC_LEN);
+		ret = DPP_STATUS_INVALID_CONNECTOR;
+		goto fail;
+	}
+
+	pos = end + 1;
+	end = os_strchr(pos, '.');
+	if (!end) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing dot(2) in signedConnector");
+		ret = DPP_STATUS_INVALID_CONNECTOR;
+		goto fail;
+	}
+	signed_end = end - 1;
+	info->payload = base64_url_decode(pos, end - pos, &info->payload_len);
+	if (!info->payload) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to base64url decode signedConnector JWS Payload");
+		ret = DPP_STATUS_INVALID_CONNECTOR;
+		goto fail;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG,
+			  "DPP: signedConnector - JWS Payload",
+			  info->payload, info->payload_len);
+	pos = end + 1;
+	signature = base64_url_decode(pos, os_strlen(pos), &signature_len);
+	if (!signature) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to base64url decode signedConnector signature");
+		ret = DPP_STATUS_INVALID_CONNECTOR;
+		goto fail;
+		}
+	wpa_hexdump(MSG_DEBUG, "DPP: signedConnector - signature",
+		    signature, signature_len);
+
+	if (dpp_check_pubkey_match(csign_pub, kid) < 0) {
+		ret = DPP_STATUS_NO_MATCH;
+		goto fail;
+	}
+
+	if (signature_len & 0x01) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected signedConnector signature length (%d)",
+			   (int) signature_len);
+		ret = DPP_STATUS_INVALID_CONNECTOR;
+		goto fail;
+	}
+
+	/* JWS Signature encodes the signature (r,s) as two octet strings. Need
+	 * to convert that to DER encoded ECDSA_SIG for OpenSSL EVP routines. */
+	r = BN_bin2bn(signature, signature_len / 2, NULL);
+	s = BN_bin2bn(signature + signature_len / 2, signature_len / 2, NULL);
+	sig = ECDSA_SIG_new();
+	if (!r || !s || !sig || ECDSA_SIG_set0(sig, r, s) != 1)
+		goto fail;
+	r = NULL;
+	s = NULL;
+
+	der_len = i2d_ECDSA_SIG(sig, &der);
+	if (der_len <= 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Could not DER encode signature");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: DER encoded signature", der, der_len);
+	md_ctx = EVP_MD_CTX_create();
+	if (!md_ctx)
+		goto fail;
+
+	ERR_clear_error();
+	if (EVP_DigestVerifyInit(md_ctx, NULL, sign_md, NULL, csign_pub) != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyInit failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	if (EVP_DigestVerifyUpdate(md_ctx, signed_start,
+				   signed_end - signed_start + 1) != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyUpdate failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	res = EVP_DigestVerifyFinal(md_ctx, der, der_len);
+	if (res != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: EVP_DigestVerifyFinal failed (res=%d): %s",
+			   res, ERR_error_string(ERR_get_error(), NULL));
+		ret = DPP_STATUS_INVALID_CONNECTOR;
+		goto fail;
+	}
+
+	ret = DPP_STATUS_OK;
+fail:
+	EVP_MD_CTX_destroy(md_ctx);
+	os_free(prot_hdr);
+	wpabuf_free(kid);
+	os_free(signature);
+	ECDSA_SIG_free(sig);
+	BN_free(r);
+	BN_free(s);
+	OPENSSL_free(der);
+	return ret;
+}
+
+
+enum dpp_status_error
+dpp_check_signed_connector(struct dpp_signed_connector_info *info,
+			   const u8 *csign_key, size_t csign_key_len,
+			   const u8 *peer_connector, size_t peer_connector_len)
+{
+	const unsigned char *p;
+	EVP_PKEY *csign = NULL;
+	char *signed_connector = NULL;
+	enum dpp_status_error res = DPP_STATUS_INVALID_CONNECTOR;
+
+	p = csign_key;
+	csign = d2i_PUBKEY(NULL, &p, csign_key_len);
+	if (!csign) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to parse local C-sign-key information");
+		goto fail;
+	}
+
+	wpa_hexdump_ascii(MSG_DEBUG, "DPP: Peer signedConnector",
+			  peer_connector, peer_connector_len);
+	signed_connector = os_malloc(peer_connector_len + 1);
+	if (!signed_connector)
+		goto fail;
+	os_memcpy(signed_connector, peer_connector, peer_connector_len);
+	signed_connector[peer_connector_len] = '\0';
+	res = dpp_process_signed_connector(info, csign, signed_connector);
+fail:
+	os_free(signed_connector);
+	EVP_PKEY_free(csign);
+	return res;
+}
+
+
+int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth)
+{
+	struct wpabuf *pix, *prx, *bix, *brx;
+	const u8 *addr[7];
+	size_t len[7];
+	size_t i, num_elem = 0;
+	size_t nonce_len;
+	u8 zero = 0;
+	int res = -1;
+
+	/* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+	nonce_len = auth->curve->nonce_len;
+
+	if (auth->initiator) {
+		pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+		prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+		if (auth->own_bi)
+			bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+		else
+			bix = NULL;
+		brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+	} else {
+		pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+		prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+		if (auth->peer_bi)
+			bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+		else
+			bix = NULL;
+		brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+	}
+	if (!pix || !prx || !brx)
+		goto fail;
+
+	addr[num_elem] = auth->i_nonce;
+	len[num_elem] = nonce_len;
+	num_elem++;
+
+	addr[num_elem] = auth->r_nonce;
+	len[num_elem] = nonce_len;
+	num_elem++;
+
+	addr[num_elem] = wpabuf_head(pix);
+	len[num_elem] = wpabuf_len(pix) / 2;
+	num_elem++;
+
+	addr[num_elem] = wpabuf_head(prx);
+	len[num_elem] = wpabuf_len(prx) / 2;
+	num_elem++;
+
+	if (bix) {
+		addr[num_elem] = wpabuf_head(bix);
+		len[num_elem] = wpabuf_len(bix) / 2;
+		num_elem++;
+	}
+
+	addr[num_elem] = wpabuf_head(brx);
+	len[num_elem] = wpabuf_len(brx) / 2;
+	num_elem++;
+
+	addr[num_elem] = &zero;
+	len[num_elem] = 1;
+	num_elem++;
+
+	wpa_printf(MSG_DEBUG, "DPP: R-auth hash components");
+	for (i = 0; i < num_elem; i++)
+		wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
+	res = dpp_hash_vector(auth->curve, num_elem, addr, len, r_auth);
+	if (res == 0)
+		wpa_hexdump(MSG_DEBUG, "DPP: R-auth", r_auth,
+			    auth->curve->hash_len);
+fail:
+	wpabuf_free(pix);
+	wpabuf_free(prx);
+	wpabuf_free(bix);
+	wpabuf_free(brx);
+	return res;
+}
+
+
+int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth)
+{
+	struct wpabuf *pix = NULL, *prx = NULL, *bix = NULL, *brx = NULL;
+	const u8 *addr[7];
+	size_t len[7];
+	size_t i, num_elem = 0;
+	size_t nonce_len;
+	u8 one = 1;
+	int res = -1;
+
+	/* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
+	nonce_len = auth->curve->nonce_len;
+
+	if (auth->initiator) {
+		pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+		prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+		if (auth->own_bi)
+			bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+		else
+			bix = NULL;
+		if (!auth->peer_bi)
+			goto fail;
+		brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+	} else {
+		pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+		prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+		if (auth->peer_bi)
+			bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+		else
+			bix = NULL;
+		if (!auth->own_bi)
+			goto fail;
+		brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+	}
+	if (!pix || !prx || !brx)
+		goto fail;
+
+	addr[num_elem] = auth->r_nonce;
+	len[num_elem] = nonce_len;
+	num_elem++;
+
+	addr[num_elem] = auth->i_nonce;
+	len[num_elem] = nonce_len;
+	num_elem++;
+
+	addr[num_elem] = wpabuf_head(prx);
+	len[num_elem] = wpabuf_len(prx) / 2;
+	num_elem++;
+
+	addr[num_elem] = wpabuf_head(pix);
+	len[num_elem] = wpabuf_len(pix) / 2;
+	num_elem++;
+
+	addr[num_elem] = wpabuf_head(brx);
+	len[num_elem] = wpabuf_len(brx) / 2;
+	num_elem++;
+
+	if (bix) {
+		addr[num_elem] = wpabuf_head(bix);
+		len[num_elem] = wpabuf_len(bix) / 2;
+		num_elem++;
+	}
+
+	addr[num_elem] = &one;
+	len[num_elem] = 1;
+	num_elem++;
+
+	wpa_printf(MSG_DEBUG, "DPP: I-auth hash components");
+	for (i = 0; i < num_elem; i++)
+		wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
+	res = dpp_hash_vector(auth->curve, num_elem, addr, len, i_auth);
+	if (res == 0)
+		wpa_hexdump(MSG_DEBUG, "DPP: I-auth", i_auth,
+			    auth->curve->hash_len);
+fail:
+	wpabuf_free(pix);
+	wpabuf_free(prx);
+	wpabuf_free(bix);
+	wpabuf_free(brx);
+	return res;
+}
+
+
+int dpp_auth_derive_l_responder(struct dpp_authentication *auth)
+{
+	const EC_GROUP *group;
+	EC_POINT *l = NULL;
+	const EC_KEY *BI, *bR, *pR;
+	const EC_POINT *BI_point;
+	BN_CTX *bnctx;
+	BIGNUM *lx, *sum, *q;
+	const BIGNUM *bR_bn, *pR_bn;
+	int ret = -1;
+
+	/* L = ((bR + pR) modulo q) * BI */
+
+	bnctx = BN_CTX_new();
+	sum = BN_new();
+	q = BN_new();
+	lx = BN_new();
+	if (!bnctx || !sum || !q || !lx)
+		goto fail;
+	BI = EVP_PKEY_get0_EC_KEY(auth->peer_bi->pubkey);
+	if (!BI)
+		goto fail;
+	BI_point = EC_KEY_get0_public_key(BI);
+	group = EC_KEY_get0_group(BI);
+	if (!group)
+		goto fail;
+
+	bR = EVP_PKEY_get0_EC_KEY(auth->own_bi->pubkey);
+	pR = EVP_PKEY_get0_EC_KEY(auth->own_protocol_key);
+	if (!bR || !pR)
+		goto fail;
+	bR_bn = EC_KEY_get0_private_key(bR);
+	pR_bn = EC_KEY_get0_private_key(pR);
+	if (!bR_bn || !pR_bn)
+		goto fail;
+	if (EC_GROUP_get_order(group, q, bnctx) != 1 ||
+	    BN_mod_add(sum, bR_bn, pR_bn, q, bnctx) != 1)
+		goto fail;
+	l = EC_POINT_new(group);
+	if (!l ||
+	    EC_POINT_mul(group, l, NULL, BI_point, sum, bnctx) != 1 ||
+	    EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
+						bnctx) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
+	auth->Lx_len = auth->secret_len;
+	ret = 0;
+fail:
+	EC_POINT_clear_free(l);
+	BN_clear_free(lx);
+	BN_clear_free(sum);
+	BN_free(q);
+	BN_CTX_free(bnctx);
+	return ret;
+}
+
+
+int dpp_auth_derive_l_initiator(struct dpp_authentication *auth)
+{
+	const EC_GROUP *group;
+	EC_POINT *l = NULL, *sum = NULL;
+	const EC_KEY *bI, *BR, *PR;
+	const EC_POINT *BR_point, *PR_point;
+	BN_CTX *bnctx;
+	BIGNUM *lx;
+	const BIGNUM *bI_bn;
+	int ret = -1;
+
+	/* L = bI * (BR + PR) */
+
+	bnctx = BN_CTX_new();
+	lx = BN_new();
+	if (!bnctx || !lx)
+		goto fail;
+	BR = EVP_PKEY_get0_EC_KEY(auth->peer_bi->pubkey);
+	PR = EVP_PKEY_get0_EC_KEY(auth->peer_protocol_key);
+	if (!BR || !PR)
+		goto fail;
+	BR_point = EC_KEY_get0_public_key(BR);
+	PR_point = EC_KEY_get0_public_key(PR);
+
+	bI = EVP_PKEY_get0_EC_KEY(auth->own_bi->pubkey);
+	if (!bI)
+		goto fail;
+	group = EC_KEY_get0_group(bI);
+	bI_bn = EC_KEY_get0_private_key(bI);
+	if (!group || !bI_bn)
+		goto fail;
+	sum = EC_POINT_new(group);
+	l = EC_POINT_new(group);
+	if (!sum || !l ||
+	    EC_POINT_add(group, sum, BR_point, PR_point, bnctx) != 1 ||
+	    EC_POINT_mul(group, l, NULL, sum, bI_bn, bnctx) != 1 ||
+	    EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
+						bnctx) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
+	auth->Lx_len = auth->secret_len;
+	ret = 0;
+fail:
+	EC_POINT_clear_free(l);
+	EC_POINT_clear_free(sum);
+	BN_clear_free(lx);
+	BN_CTX_free(bnctx);
+	return ret;
+}
+
+
+int dpp_derive_pmk(const u8 *Nx, size_t Nx_len, u8 *pmk, unsigned int hash_len)
+{
+	u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+	const char *info = "DPP PMK";
+	int res;
+
+	/* PMK = HKDF(<>, "DPP PMK", N.x) */
+
+	/* HKDF-Extract(<>, N.x) */
+	os_memset(salt, 0, hash_len);
+	if (dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
+			prk, hash_len);
+
+	/* HKDF-Expand(PRK, info, L) */
+	res = dpp_hkdf_expand(hash_len, prk, hash_len, info, pmk, hash_len);
+	os_memset(prk, 0, hash_len);
+	if (res < 0)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PMK = HKDF-Expand(PRK, info, L)",
+			pmk, hash_len);
+	return 0;
+}
+
+
+int dpp_derive_pmkid(const struct dpp_curve_params *curve,
+		     EVP_PKEY *own_key, EVP_PKEY *peer_key, u8 *pmkid)
+{
+	struct wpabuf *nkx, *pkx;
+	int ret = -1, res;
+	const u8 *addr[2];
+	size_t len[2];
+	u8 hash[SHA256_MAC_LEN];
+
+	/* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */
+	nkx = dpp_get_pubkey_point(own_key, 0);
+	pkx = dpp_get_pubkey_point(peer_key, 0);
+	if (!nkx || !pkx)
+		goto fail;
+	addr[0] = wpabuf_head(nkx);
+	len[0] = wpabuf_len(nkx) / 2;
+	addr[1] = wpabuf_head(pkx);
+	len[1] = wpabuf_len(pkx) / 2;
+	if (len[0] != len[1])
+		goto fail;
+	if (os_memcmp(addr[0], addr[1], len[0]) > 0) {
+		addr[0] = wpabuf_head(pkx);
+		addr[1] = wpabuf_head(nkx);
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 1", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 2", addr[1], len[1]);
+	res = sha256_vector(2, addr, len, hash);
+	if (res < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash output", hash, SHA256_MAC_LEN);
+	os_memcpy(pmkid, hash, PMKID_LEN);
+	wpa_hexdump(MSG_DEBUG, "DPP: PMKID", pmkid, PMKID_LEN);
+	ret = 0;
+fail:
+	wpabuf_free(nkx);
+	wpabuf_free(pkx);
+	return ret;
+}
+
+
+/* Role-specific elements for PKEX */
+
+/* NIST P-256 */
+static const u8 pkex_init_x_p256[32] = {
+	0x56, 0x26, 0x12, 0xcf, 0x36, 0x48, 0xfe, 0x0b,
+	0x07, 0x04, 0xbb, 0x12, 0x22, 0x50, 0xb2, 0x54,
+	0xb1, 0x94, 0x64, 0x7e, 0x54, 0xce, 0x08, 0x07,
+	0x2e, 0xec, 0xca, 0x74, 0x5b, 0x61, 0x2d, 0x25
+ };
+static const u8 pkex_init_y_p256[32] = {
+	0x3e, 0x44, 0xc7, 0xc9, 0x8c, 0x1c, 0xa1, 0x0b,
+	0x20, 0x09, 0x93, 0xb2, 0xfd, 0xe5, 0x69, 0xdc,
+	0x75, 0xbc, 0xad, 0x33, 0xc1, 0xe7, 0xc6, 0x45,
+	0x4d, 0x10, 0x1e, 0x6a, 0x3d, 0x84, 0x3c, 0xa4
+ };
+static const u8 pkex_resp_x_p256[32] = {
+	0x1e, 0xa4, 0x8a, 0xb1, 0xa4, 0xe8, 0x42, 0x39,
+	0xad, 0x73, 0x07, 0xf2, 0x34, 0xdf, 0x57, 0x4f,
+	0xc0, 0x9d, 0x54, 0xbe, 0x36, 0x1b, 0x31, 0x0f,
+	0x59, 0x91, 0x52, 0x33, 0xac, 0x19, 0x9d, 0x76
+};
+static const u8 pkex_resp_y_p256[32] = {
+	0xd9, 0xfb, 0xf6, 0xb9, 0xf5, 0xfa, 0xdf, 0x19,
+	0x58, 0xd8, 0x3e, 0xc9, 0x89, 0x7a, 0x35, 0xc1,
+	0xbd, 0xe9, 0x0b, 0x77, 0x7a, 0xcb, 0x91, 0x2a,
+	0xe8, 0x21, 0x3f, 0x47, 0x52, 0x02, 0x4d, 0x67
+};
+
+/* NIST P-384 */
+static const u8 pkex_init_x_p384[48] = {
+	0x95, 0x3f, 0x42, 0x9e, 0x50, 0x7f, 0xf9, 0xaa,
+	0xac, 0x1a, 0xf2, 0x85, 0x2e, 0x64, 0x91, 0x68,
+	0x64, 0xc4, 0x3c, 0xb7, 0x5c, 0xf8, 0xc9, 0x53,
+	0x6e, 0x58, 0x4c, 0x7f, 0xc4, 0x64, 0x61, 0xac,
+	0x51, 0x8a, 0x6f, 0xfe, 0xab, 0x74, 0xe6, 0x12,
+	0x81, 0xac, 0x38, 0x5d, 0x41, 0xe6, 0xb9, 0xa3
+};
+static const u8 pkex_init_y_p384[48] = {
+	0x76, 0x2f, 0x68, 0x84, 0xa6, 0xb0, 0x59, 0x29,
+	0x83, 0xa2, 0x6c, 0xa4, 0x6c, 0x3b, 0xf8, 0x56,
+	0x76, 0x11, 0x2a, 0x32, 0x90, 0xbd, 0x07, 0xc7,
+	0x37, 0x39, 0x9d, 0xdb, 0x96, 0xf3, 0x2b, 0xb6,
+	0x27, 0xbb, 0x29, 0x3c, 0x17, 0x33, 0x9d, 0x94,
+	0xc3, 0xda, 0xac, 0x46, 0xb0, 0x8e, 0x07, 0x18
+};
+static const u8 pkex_resp_x_p384[48] = {
+	0xad, 0xbe, 0xd7, 0x1d, 0x3a, 0x71, 0x64, 0x98,
+	0x5f, 0xb4, 0xd6, 0x4b, 0x50, 0xd0, 0x84, 0x97,
+	0x4b, 0x7e, 0x57, 0x70, 0xd2, 0xd9, 0xf4, 0x92,
+	0x2a, 0x3f, 0xce, 0x99, 0xc5, 0x77, 0x33, 0x44,
+	0x14, 0x56, 0x92, 0xcb, 0xae, 0x46, 0x64, 0xdf,
+	0xe0, 0xbb, 0xd7, 0xb1, 0x29, 0x20, 0x72, 0xdf
+};
+static const u8 pkex_resp_y_p384[48] = {
+	0xab, 0xa7, 0xdf, 0x52, 0xaa, 0xe2, 0x35, 0x0c,
+	0xe3, 0x75, 0x32, 0xe6, 0xbf, 0x06, 0xc8, 0x7c,
+	0x38, 0x29, 0x4c, 0xec, 0x82, 0xac, 0xd7, 0xa3,
+	0x09, 0xd2, 0x0e, 0x22, 0x5a, 0x74, 0x52, 0xa1,
+	0x7e, 0x54, 0x4e, 0xfe, 0xc6, 0x29, 0x33, 0x63,
+	0x15, 0xe1, 0x7b, 0xe3, 0x40, 0x1c, 0xca, 0x06
+};
+
+/* NIST P-521 */
+static const u8 pkex_init_x_p521[66] = {
+	0x00, 0x16, 0x20, 0x45, 0x19, 0x50, 0x95, 0x23,
+	0x0d, 0x24, 0xbe, 0x00, 0x87, 0xdc, 0xfa, 0xf0,
+	0x58, 0x9a, 0x01, 0x60, 0x07, 0x7a, 0xca, 0x76,
+	0x01, 0xab, 0x2d, 0x5a, 0x46, 0xcd, 0x2c, 0xb5,
+	0x11, 0x9a, 0xff, 0xaa, 0x48, 0x04, 0x91, 0x38,
+	0xcf, 0x86, 0xfc, 0xa4, 0xa5, 0x0f, 0x47, 0x01,
+	0x80, 0x1b, 0x30, 0xa3, 0xae, 0xe8, 0x1c, 0x2e,
+	0xea, 0xcc, 0xf0, 0x03, 0x9f, 0x77, 0x4c, 0x8d,
+	0x97, 0x76
+};
+static const u8 pkex_init_y_p521[66] = {
+	0x00, 0xb3, 0x8e, 0x02, 0xe4, 0x2a, 0x63, 0x59,
+	0x12, 0xc6, 0x10, 0xba, 0x3a, 0xf9, 0x02, 0x99,
+	0x3f, 0x14, 0xf0, 0x40, 0xde, 0x5c, 0xc9, 0x8b,
+	0x02, 0x55, 0xfa, 0x91, 0xb1, 0xcc, 0x6a, 0xbd,
+	0xe5, 0x62, 0xc0, 0xc5, 0xe3, 0xa1, 0x57, 0x9f,
+	0x08, 0x1a, 0xa6, 0xe2, 0xf8, 0x55, 0x90, 0xbf,
+	0xf5, 0xa6, 0xc3, 0xd8, 0x52, 0x1f, 0xb7, 0x02,
+	0x2e, 0x7c, 0xc8, 0xb3, 0x20, 0x1e, 0x79, 0x8d,
+	0x03, 0xa8
+};
+static const u8 pkex_resp_x_p521[66] = {
+	0x00, 0x79, 0xe4, 0x4d, 0x6b, 0x5e, 0x12, 0x0a,
+	0x18, 0x2c, 0xb3, 0x05, 0x77, 0x0f, 0xc3, 0x44,
+	0x1a, 0xcd, 0x78, 0x46, 0x14, 0xee, 0x46, 0x3f,
+	0xab, 0xc9, 0x59, 0x7c, 0x85, 0xa0, 0xc2, 0xfb,
+	0x02, 0x32, 0x99, 0xde, 0x5d, 0xe1, 0x0d, 0x48,
+	0x2d, 0x71, 0x7d, 0x8d, 0x3f, 0x61, 0x67, 0x9e,
+	0x2b, 0x8b, 0x12, 0xde, 0x10, 0x21, 0x55, 0x0a,
+	0x5b, 0x2d, 0xe8, 0x05, 0x09, 0xf6, 0x20, 0x97,
+	0x84, 0xb4
+};
+static const u8 pkex_resp_y_p521[66] = {
+	0x00, 0x46, 0x63, 0x39, 0xbe, 0xcd, 0xa4, 0x2d,
+	0xca, 0x27, 0x74, 0xd4, 0x1b, 0x91, 0x33, 0x20,
+	0x83, 0xc7, 0x3b, 0xa4, 0x09, 0x8b, 0x8e, 0xa3,
+	0x88, 0xe9, 0x75, 0x7f, 0x56, 0x7b, 0x38, 0x84,
+	0x62, 0x02, 0x7c, 0x90, 0x51, 0x07, 0xdb, 0xe9,
+	0xd0, 0xde, 0xda, 0x9a, 0x5d, 0xe5, 0x94, 0xd2,
+	0xcf, 0x9d, 0x4c, 0x33, 0x91, 0xa6, 0xc3, 0x80,
+	0xa7, 0x6e, 0x7e, 0x8d, 0xf8, 0x73, 0x6e, 0x53,
+	0xce, 0xe1
+};
+
+/* Brainpool P-256r1 */
+static const u8 pkex_init_x_bp_p256r1[32] = {
+	0x46, 0x98, 0x18, 0x6c, 0x27, 0xcd, 0x4b, 0x10,
+	0x7d, 0x55, 0xa3, 0xdd, 0x89, 0x1f, 0x9f, 0xca,
+	0xc7, 0x42, 0x5b, 0x8a, 0x23, 0xed, 0xf8, 0x75,
+	0xac, 0xc7, 0xe9, 0x8d, 0xc2, 0x6f, 0xec, 0xd8
+};
+static const u8 pkex_init_y_bp_p256r1[32] = {
+	0x93, 0xca, 0xef, 0xa9, 0x66, 0x3e, 0x87, 0xcd,
+	0x52, 0x6e, 0x54, 0x13, 0xef, 0x31, 0x67, 0x30,
+	0x15, 0x13, 0x9d, 0x6d, 0xc0, 0x95, 0x32, 0xbe,
+	0x4f, 0xab, 0x5d, 0xf7, 0xbf, 0x5e, 0xaa, 0x0b
+};
+static const u8 pkex_resp_x_bp_p256r1[32] = {
+	0x90, 0x18, 0x84, 0xc9, 0xdc, 0xcc, 0xb5, 0x2f,
+	0x4a, 0x3f, 0x4f, 0x18, 0x0a, 0x22, 0x56, 0x6a,
+	0xa9, 0xef, 0xd4, 0xe6, 0xc3, 0x53, 0xc2, 0x1a,
+	0x23, 0x54, 0xdd, 0x08, 0x7e, 0x10, 0xd8, 0xe3
+};
+static const u8 pkex_resp_y_bp_p256r1[32] = {
+	0x2a, 0xfa, 0x98, 0x9b, 0xe3, 0xda, 0x30, 0xfd,
+	0x32, 0x28, 0xcb, 0x66, 0xfb, 0x40, 0x7f, 0xf2,
+	0xb2, 0x25, 0x80, 0x82, 0x44, 0x85, 0x13, 0x7e,
+	0x4b, 0xb5, 0x06, 0xc0, 0x03, 0x69, 0x23, 0x64
+};
+
+/* Brainpool P-384r1 */
+static const u8 pkex_init_x_bp_p384r1[48] = {
+	0x0a, 0x2c, 0xeb, 0x49, 0x5e, 0xb7, 0x23, 0xbd,
+	0x20, 0x5b, 0xe0, 0x49, 0xdf, 0xcf, 0xcf, 0x19,
+	0x37, 0x36, 0xe1, 0x2f, 0x59, 0xdb, 0x07, 0x06,
+	0xb5, 0xeb, 0x2d, 0xae, 0xc2, 0xb2, 0x38, 0x62,
+	0xa6, 0x73, 0x09, 0xa0, 0x6c, 0x0a, 0xa2, 0x30,
+	0x99, 0xeb, 0xf7, 0x1e, 0x47, 0xb9, 0x5e, 0xbe
+};
+static const u8 pkex_init_y_bp_p384r1[48] = {
+	0x54, 0x76, 0x61, 0x65, 0x75, 0x5a, 0x2f, 0x99,
+	0x39, 0x73, 0xca, 0x6c, 0xf9, 0xf7, 0x12, 0x86,
+	0x54, 0xd5, 0xd4, 0xad, 0x45, 0x7b, 0xbf, 0x32,
+	0xee, 0x62, 0x8b, 0x9f, 0x52, 0xe8, 0xa0, 0xc9,
+	0xb7, 0x9d, 0xd1, 0x09, 0xb4, 0x79, 0x1c, 0x3e,
+	0x1a, 0xbf, 0x21, 0x45, 0x66, 0x6b, 0x02, 0x52
+};
+static const u8 pkex_resp_x_bp_p384r1[48] = {
+	0x03, 0xa2, 0x57, 0xef, 0xe8, 0x51, 0x21, 0xa0,
+	0xc8, 0x9e, 0x21, 0x02, 0xb5, 0x9a, 0x36, 0x25,
+	0x74, 0x22, 0xd1, 0xf2, 0x1b, 0xa8, 0x9a, 0x9b,
+	0x97, 0xbc, 0x5a, 0xeb, 0x26, 0x15, 0x09, 0x71,
+	0x77, 0x59, 0xec, 0x8b, 0xb7, 0xe1, 0xe8, 0xce,
+	0x65, 0xb8, 0xaf, 0xf8, 0x80, 0xae, 0x74, 0x6c
+};
+static const u8 pkex_resp_y_bp_p384r1[48] = {
+	0x2f, 0xd9, 0x6a, 0xc7, 0x3e, 0xec, 0x76, 0x65,
+	0x2d, 0x38, 0x7f, 0xec, 0x63, 0x26, 0x3f, 0x04,
+	0xd8, 0x4e, 0xff, 0xe1, 0x0a, 0x51, 0x74, 0x70,
+	0xe5, 0x46, 0x63, 0x7f, 0x5c, 0xc0, 0xd1, 0x7c,
+	0xfb, 0x2f, 0xea, 0xe2, 0xd8, 0x0f, 0x84, 0xcb,
+	0xe9, 0x39, 0x5c, 0x64, 0xfe, 0xcb, 0x2f, 0xf1
+};
+
+/* Brainpool P-512r1 */
+static const u8 pkex_init_x_bp_p512r1[64] = {
+	0x4c, 0xe9, 0xb6, 0x1c, 0xe2, 0x00, 0x3c, 0x9c,
+	0xa9, 0xc8, 0x56, 0x52, 0xaf, 0x87, 0x3e, 0x51,
+	0x9c, 0xbb, 0x15, 0x31, 0x1e, 0xc1, 0x05, 0xfc,
+	0x7c, 0x77, 0xd7, 0x37, 0x61, 0x27, 0xd0, 0x95,
+	0x98, 0xee, 0x5d, 0xa4, 0x3d, 0x09, 0xdb, 0x3d,
+	0xfa, 0x89, 0x9e, 0x7f, 0xa6, 0xa6, 0x9c, 0xff,
+	0x83, 0x5c, 0x21, 0x6c, 0x3e, 0xf2, 0xfe, 0xdc,
+	0x63, 0xe4, 0xd1, 0x0e, 0x75, 0x45, 0x69, 0x0f
+};
+static const u8 pkex_init_y_bp_p512r1[64] = {
+	0x50, 0xb5, 0x9b, 0xfa, 0x45, 0x67, 0x75, 0x94,
+	0x44, 0xe7, 0x68, 0xb0, 0xeb, 0x3e, 0xb3, 0xb8,
+	0xf9, 0x99, 0x05, 0xef, 0xae, 0x6c, 0xbc, 0xe3,
+	0xe1, 0xd2, 0x51, 0x54, 0xdf, 0x59, 0xd4, 0x45,
+	0x41, 0x3a, 0xa8, 0x0b, 0x76, 0x32, 0x44, 0x0e,
+	0x07, 0x60, 0x3a, 0x6e, 0xbe, 0xfe, 0xe0, 0x58,
+	0x52, 0xa0, 0xaa, 0x8b, 0xd8, 0x5b, 0xf2, 0x71,
+	0x11, 0x9a, 0x9e, 0x8f, 0x1a, 0xd1, 0xc9, 0x99
+};
+static const u8 pkex_resp_x_bp_p512r1[64] = {
+	0x2a, 0x60, 0x32, 0x27, 0xa1, 0xe6, 0x94, 0x72,
+	0x1c, 0x48, 0xbe, 0xc5, 0x77, 0x14, 0x30, 0x76,
+	0xe4, 0xbf, 0xf7, 0x7b, 0xc5, 0xfd, 0xdf, 0x19,
+	0x1e, 0x0f, 0xdf, 0x1c, 0x40, 0xfa, 0x34, 0x9e,
+	0x1f, 0x42, 0x24, 0xa3, 0x2c, 0xd5, 0xc7, 0xc9,
+	0x7b, 0x47, 0x78, 0x96, 0xf1, 0x37, 0x0e, 0x88,
+	0xcb, 0xa6, 0x52, 0x29, 0xd7, 0xa8, 0x38, 0x29,
+	0x8e, 0x6e, 0x23, 0x47, 0xd4, 0x4b, 0x70, 0x3e
+};
+static const u8 pkex_resp_y_bp_p512r1[64] = {
+	0x80, 0x1f, 0x43, 0xd2, 0x17, 0x35, 0xec, 0x81,
+	0xd9, 0x4b, 0xdc, 0x81, 0x19, 0xd9, 0x5f, 0x68,
+	0x16, 0x84, 0xfe, 0x63, 0x4b, 0x8d, 0x5d, 0xaa,
+	0x88, 0x4a, 0x47, 0x48, 0xd4, 0xea, 0xab, 0x7d,
+	0x6a, 0xbf, 0xe1, 0x28, 0x99, 0x6a, 0x87, 0x1c,
+	0x30, 0xb4, 0x44, 0x2d, 0x75, 0xac, 0x35, 0x09,
+	0x73, 0x24, 0x3d, 0xb4, 0x43, 0xb1, 0xc1, 0x56,
+	0x56, 0xad, 0x30, 0x87, 0xf4, 0xc3, 0x00, 0xc7
+};
+
+
+static EVP_PKEY * dpp_pkex_get_role_elem(const struct dpp_curve_params *curve,
+					 int init)
+{
+	EC_GROUP *group;
+	size_t len = curve->prime_len;
+	const u8 *x, *y;
+	EVP_PKEY *res;
+
+	switch (curve->ike_group) {
+	case 19:
+		x = init ? pkex_init_x_p256 : pkex_resp_x_p256;
+		y = init ? pkex_init_y_p256 : pkex_resp_y_p256;
+		break;
+	case 20:
+		x = init ? pkex_init_x_p384 : pkex_resp_x_p384;
+		y = init ? pkex_init_y_p384 : pkex_resp_y_p384;
+		break;
+	case 21:
+		x = init ? pkex_init_x_p521 : pkex_resp_x_p521;
+		y = init ? pkex_init_y_p521 : pkex_resp_y_p521;
+		break;
+	case 28:
+		x = init ? pkex_init_x_bp_p256r1 : pkex_resp_x_bp_p256r1;
+		y = init ? pkex_init_y_bp_p256r1 : pkex_resp_y_bp_p256r1;
+		break;
+	case 29:
+		x = init ? pkex_init_x_bp_p384r1 : pkex_resp_x_bp_p384r1;
+		y = init ? pkex_init_y_bp_p384r1 : pkex_resp_y_bp_p384r1;
+		break;
+	case 30:
+		x = init ? pkex_init_x_bp_p512r1 : pkex_resp_x_bp_p512r1;
+		y = init ? pkex_init_y_bp_p512r1 : pkex_resp_y_bp_p512r1;
+		break;
+	default:
+		return NULL;
+	}
+
+	group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
+	if (!group)
+		return NULL;
+	res = dpp_set_pubkey_point_group(group, x, y, len);
+	EC_GROUP_free(group);
+	return res;
+}
+
+
+EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve,
+			      const u8 *mac_init, const char *code,
+			      const char *identifier, BN_CTX *bnctx,
+			      EC_GROUP **ret_group)
+{
+	u8 hash[DPP_MAX_HASH_LEN];
+	const u8 *addr[3];
+	size_t len[3];
+	unsigned int num_elem = 0;
+	EC_POINT *Qi = NULL;
+	EVP_PKEY *Pi = NULL;
+	const EC_KEY *Pi_ec;
+	const EC_POINT *Pi_point;
+	BIGNUM *hash_bn = NULL;
+	const EC_GROUP *group = NULL;
+	EC_GROUP *group2 = NULL;
+
+	/* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
+
+	wpa_printf(MSG_DEBUG, "DPP: MAC-Initiator: " MACSTR, MAC2STR(mac_init));
+	addr[num_elem] = mac_init;
+	len[num_elem] = ETH_ALEN;
+	num_elem++;
+	if (identifier) {
+		wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
+			   identifier);
+		addr[num_elem] = (const u8 *) identifier;
+		len[num_elem] = os_strlen(identifier);
+		num_elem++;
+	}
+	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
+	addr[num_elem] = (const u8 *) code;
+	len[num_elem] = os_strlen(code);
+	num_elem++;
+	if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG,
+			"DPP: H(MAC-Initiator | [identifier |] code)",
+			hash, curve->hash_len);
+	Pi = dpp_pkex_get_role_elem(curve, 1);
+	if (!Pi)
+		goto fail;
+	dpp_debug_print_key("DPP: Pi", Pi);
+	Pi_ec = EVP_PKEY_get0_EC_KEY(Pi);
+	if (!Pi_ec)
+		goto fail;
+	Pi_point = EC_KEY_get0_public_key(Pi_ec);
+
+	group = EC_KEY_get0_group(Pi_ec);
+	if (!group)
+		goto fail;
+	group2 = EC_GROUP_dup(group);
+	if (!group2)
+		goto fail;
+	Qi = EC_POINT_new(group2);
+	if (!Qi) {
+		EC_GROUP_free(group2);
+		goto fail;
+	}
+	hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
+	if (!hash_bn ||
+	    EC_POINT_mul(group2, Qi, NULL, Pi_point, hash_bn, bnctx) != 1)
+		goto fail;
+	if (EC_POINT_is_at_infinity(group, Qi)) {
+		wpa_printf(MSG_INFO, "DPP: Qi is the point-at-infinity");
+		goto fail;
+	}
+	dpp_debug_print_point("DPP: Qi", group, Qi);
+out:
+	EVP_PKEY_free(Pi);
+	BN_clear_free(hash_bn);
+	if (ret_group && Qi)
+		*ret_group = group2;
+	else
+		EC_GROUP_free(group2);
+	return Qi;
+fail:
+	EC_POINT_free(Qi);
+	Qi = NULL;
+	goto out;
+}
+
+
+EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve,
+			      const u8 *mac_resp, const char *code,
+			      const char *identifier, BN_CTX *bnctx,
+			      EC_GROUP **ret_group)
+{
+	u8 hash[DPP_MAX_HASH_LEN];
+	const u8 *addr[3];
+	size_t len[3];
+	unsigned int num_elem = 0;
+	EC_POINT *Qr = NULL;
+	EVP_PKEY *Pr = NULL;
+	const EC_KEY *Pr_ec;
+	const EC_POINT *Pr_point;
+	BIGNUM *hash_bn = NULL;
+	const EC_GROUP *group = NULL;
+	EC_GROUP *group2 = NULL;
+
+	/* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
+
+	wpa_printf(MSG_DEBUG, "DPP: MAC-Responder: " MACSTR, MAC2STR(mac_resp));
+	addr[num_elem] = mac_resp;
+	len[num_elem] = ETH_ALEN;
+	num_elem++;
+	if (identifier) {
+		wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
+			   identifier);
+		addr[num_elem] = (const u8 *) identifier;
+		len[num_elem] = os_strlen(identifier);
+		num_elem++;
+	}
+	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
+	addr[num_elem] = (const u8 *) code;
+	len[num_elem] = os_strlen(code);
+	num_elem++;
+	if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG,
+			"DPP: H(MAC-Responder | [identifier |] code)",
+			hash, curve->hash_len);
+	Pr = dpp_pkex_get_role_elem(curve, 0);
+	if (!Pr)
+		goto fail;
+	dpp_debug_print_key("DPP: Pr", Pr);
+	Pr_ec = EVP_PKEY_get0_EC_KEY(Pr);
+	if (!Pr_ec)
+		goto fail;
+	Pr_point = EC_KEY_get0_public_key(Pr_ec);
+
+	group = EC_KEY_get0_group(Pr_ec);
+	if (!group)
+		goto fail;
+	group2 = EC_GROUP_dup(group);
+	if (!group2)
+		goto fail;
+	Qr = EC_POINT_new(group2);
+	if (!Qr) {
+		EC_GROUP_free(group2);
+		goto fail;
+	}
+	hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
+	if (!hash_bn ||
+	    EC_POINT_mul(group2, Qr, NULL, Pr_point, hash_bn, bnctx) != 1)
+		goto fail;
+	if (EC_POINT_is_at_infinity(group, Qr)) {
+		wpa_printf(MSG_INFO, "DPP: Qr is the point-at-infinity");
+		goto fail;
+	}
+	dpp_debug_print_point("DPP: Qr", group, Qr);
+out:
+	EVP_PKEY_free(Pr);
+	BN_clear_free(hash_bn);
+	if (ret_group && Qr)
+		*ret_group = group2;
+	else
+		EC_GROUP_free(group2);
+	return Qr;
+fail:
+	EC_POINT_free(Qr);
+	Qr = NULL;
+	goto out;
+}
+
+
+int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp,
+		      const u8 *Mx, size_t Mx_len,
+		      const u8 *Nx, size_t Nx_len,
+		      const char *code,
+		      const u8 *Kx, size_t Kx_len,
+		      u8 *z, unsigned int hash_len)
+{
+	u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+	int res;
+	u8 *info, *pos;
+	size_t info_len;
+
+	/* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
+	 */
+
+	/* HKDF-Extract(<>, IKM=K.x) */
+	os_memset(salt, 0, hash_len);
+	if (dpp_hmac(hash_len, salt, hash_len, Kx, Kx_len, prk) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
+			prk, hash_len);
+	info_len = 2 * ETH_ALEN + Mx_len + Nx_len + os_strlen(code);
+	info = os_malloc(info_len);
+	if (!info)
+		return -1;
+	pos = info;
+	os_memcpy(pos, mac_init, ETH_ALEN);
+	pos += ETH_ALEN;
+	os_memcpy(pos, mac_resp, ETH_ALEN);
+	pos += ETH_ALEN;
+	os_memcpy(pos, Mx, Mx_len);
+	pos += Mx_len;
+	os_memcpy(pos, Nx, Nx_len);
+	pos += Nx_len;
+	os_memcpy(pos, code, os_strlen(code));
+
+	/* HKDF-Expand(PRK, info, L) */
+	if (hash_len == 32)
+		res = hmac_sha256_kdf(prk, hash_len, NULL, info, info_len,
+				      z, hash_len);
+	else if (hash_len == 48)
+		res = hmac_sha384_kdf(prk, hash_len, NULL, info, info_len,
+				      z, hash_len);
+	else if (hash_len == 64)
+		res = hmac_sha512_kdf(prk, hash_len, NULL, info, info_len,
+				      z, hash_len);
+	else
+		res = -1;
+	os_free(info);
+	os_memset(prk, 0, hash_len);
+	if (res < 0)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: z = HKDF-Expand(PRK, info, L)",
+			z, hash_len);
+	return 0;
+}
+
+
+int dpp_reconfig_derive_ke_responder(struct dpp_authentication *auth,
+				     const u8 *net_access_key,
+				     size_t net_access_key_len,
+				     struct json_token *peer_net_access_key)
+{
+	BN_CTX *bnctx = NULL;
+	EVP_PKEY *own_key = NULL, *peer_key = NULL;
+	BIGNUM *sum = NULL, *q = NULL, *mx = NULL;
+	EC_POINT *m = NULL;
+	const EC_KEY *cR, *pR;
+	const EC_GROUP *group;
+	const BIGNUM *cR_bn, *pR_bn;
+	const EC_POINT *CI_point;
+	const EC_KEY *CI;
+	u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
+	u8 prk[DPP_MAX_HASH_LEN];
+	const struct dpp_curve_params *curve;
+	int res = -1;
+
+	own_key = dpp_set_keypair(&auth->curve, net_access_key,
+				  net_access_key_len);
+	if (!own_key) {
+		dpp_auth_fail(auth, "Failed to parse own netAccessKey");
+		goto fail;
+	}
+
+	peer_key = dpp_parse_jwk(peer_net_access_key, &curve);
+	if (!peer_key)
+		goto fail;
+	dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
+
+	if (auth->curve != curve) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Mismatching netAccessKey curves (%s != %s)",
+			   auth->curve->name, curve->name);
+		goto fail;
+	}
+
+	auth->own_protocol_key = dpp_gen_keypair(curve);
+	if (!auth->own_protocol_key)
+		goto fail;
+
+	/* M = { cR + pR } * CI */
+	cR = EVP_PKEY_get0_EC_KEY(own_key);
+	pR = EVP_PKEY_get0_EC_KEY(auth->own_protocol_key);
+	group = EC_KEY_get0_group(pR);
+	bnctx = BN_CTX_new();
+	sum = BN_new();
+	mx = BN_new();
+	q = BN_new();
+	m = EC_POINT_new(group);
+	if (!cR || !pR || !bnctx || !sum || !mx || !q || !m)
+		goto fail;
+	cR_bn = EC_KEY_get0_private_key(cR);
+	pR_bn = EC_KEY_get0_private_key(pR);
+	if (!cR_bn || !pR_bn)
+		goto fail;
+	CI = EVP_PKEY_get0_EC_KEY(peer_key);
+	CI_point = EC_KEY_get0_public_key(CI);
+	if (EC_GROUP_get_order(group, q, bnctx) != 1 ||
+	    BN_mod_add(sum, cR_bn, pR_bn, q, bnctx) != 1 ||
+	    EC_POINT_mul(group, m, NULL, CI_point, sum, bnctx) != 1 ||
+	    EC_POINT_get_affine_coordinates_GFp(group, m, mx, NULL,
+						bnctx) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	if (dpp_bn2bin_pad(mx, Mx, curve->prime_len) < 0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: M.x", Mx, curve->prime_len);
+
+	/* ke = HKDF(I-nonce, "dpp reconfig key", M.x) */
+
+	/* HKDF-Extract(I-nonce, M.x) */
+	if (dpp_hmac(curve->hash_len, auth->i_nonce, curve->nonce_len,
+		     Mx, curve->prime_len, prk) < 0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK", prk, curve->hash_len);
+
+	/* HKDF-Expand(PRK, "dpp reconfig key", L) */
+	if (dpp_hkdf_expand(curve->hash_len, prk, curve->hash_len,
+			    "dpp reconfig key", auth->ke, curve->hash_len) < 0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG,
+			"DPP: ke = HKDF(I-nonce, \"dpp reconfig key\", M.x)",
+			auth->ke, curve->hash_len);
+
+	res = 0;
+	EVP_PKEY_free(auth->reconfig_old_protocol_key);
+	auth->reconfig_old_protocol_key = own_key;
+	own_key = NULL;
+fail:
+	forced_memzero(prk, sizeof(prk));
+	forced_memzero(Mx, sizeof(Mx));
+	EC_POINT_clear_free(m);
+	BN_free(q);
+	BN_clear_free(mx);
+	BN_clear_free(sum);
+	EVP_PKEY_free(own_key);
+	EVP_PKEY_free(peer_key);
+	BN_CTX_free(bnctx);
+	return res;
+}
+
+
+int dpp_reconfig_derive_ke_initiator(struct dpp_authentication *auth,
+				     const u8 *r_proto, u16 r_proto_len,
+				     struct json_token *net_access_key)
+{
+	BN_CTX *bnctx = NULL;
+	EVP_PKEY *pr = NULL, *peer_key = NULL;
+	EC_POINT *sum = NULL, *m = NULL;
+	BIGNUM *mx = NULL;
+	const EC_KEY *cI, *CR, *PR;
+	const EC_GROUP *group;
+	const EC_POINT *CR_point, *PR_point;
+	const BIGNUM *cI_bn;
+	u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
+	u8 prk[DPP_MAX_HASH_LEN];
+	int res = -1;
+	const struct dpp_curve_params *curve;
+
+	pr = dpp_set_pubkey_point(auth->conf->connector_key,
+				  r_proto, r_proto_len);
+	if (!pr) {
+		dpp_auth_fail(auth, "Invalid Responder Protocol Key");
+		goto fail;
+	}
+	dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
+	EVP_PKEY_free(auth->peer_protocol_key);
+	auth->peer_protocol_key = pr;
+	pr = NULL;
+
+	peer_key = dpp_parse_jwk(net_access_key, &curve);
+	if (!peer_key)
+		goto fail;
+	dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
+	if (auth->curve != curve) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Mismatching netAccessKey curves (%s != %s)",
+			   auth->curve->name, curve->name);
+		goto fail;
+	}
+
+	/* M = cI * { CR + PR } */
+	cI = EVP_PKEY_get0_EC_KEY(auth->conf->connector_key);
+	cI_bn = EC_KEY_get0_private_key(cI);
+	group = EC_KEY_get0_group(cI);
+	bnctx = BN_CTX_new();
+	sum = EC_POINT_new(group);
+	m = EC_POINT_new(group);
+	mx = BN_new();
+	CR = EVP_PKEY_get0_EC_KEY(peer_key);
+	PR = EVP_PKEY_get0_EC_KEY(auth->peer_protocol_key);
+	CR_point = EC_KEY_get0_public_key(CR);
+	PR_point = EC_KEY_get0_public_key(PR);
+	if (!bnctx || !sum || !m || !mx ||
+	    EC_POINT_add(group, sum, CR_point, PR_point, bnctx) != 1 ||
+	    EC_POINT_mul(group, m, NULL, sum, cI_bn, bnctx) != 1 ||
+	    EC_POINT_get_affine_coordinates_GFp(group, m, mx, NULL,
+						bnctx) != 1 ||
+	    dpp_bn2bin_pad(mx, Mx, curve->prime_len) < 0)
+		goto fail;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: M.x", Mx, curve->prime_len);
+
+	/* ke = HKDF(I-nonce, "dpp reconfig key", M.x) */
+
+	/* HKDF-Extract(I-nonce, M.x) */
+	if (dpp_hmac(curve->hash_len, auth->i_nonce, curve->nonce_len,
+		     Mx, curve->prime_len, prk) < 0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK", prk, curve->hash_len);
+
+	/* HKDF-Expand(PRK, "dpp reconfig key", L) */
+	if (dpp_hkdf_expand(curve->hash_len, prk, curve->hash_len,
+			    "dpp reconfig key", auth->ke, curve->hash_len) < 0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG,
+			"DPP: ke = HKDF(I-nonce, \"dpp reconfig key\", M.x)",
+			auth->ke, curve->hash_len);
+
+	res = 0;
+fail:
+	forced_memzero(prk, sizeof(prk));
+	forced_memzero(Mx, sizeof(Mx));
+	EVP_PKEY_free(pr);
+	EVP_PKEY_free(peer_key);
+	EC_POINT_clear_free(sum);
+	EC_POINT_clear_free(m);
+	BN_clear_free(mx);
+	BN_CTX_free(bnctx);
+	return res;
+}
+
+
+static char *
+dpp_build_jws_prot_hdr(struct dpp_configurator *conf, size_t *signed1_len)
+{
+	struct wpabuf *jws_prot_hdr;
+	char *signed1;
+
+	jws_prot_hdr = wpabuf_alloc(100);
+	if (!jws_prot_hdr)
+		return NULL;
+	json_start_object(jws_prot_hdr, NULL);
+	json_add_string(jws_prot_hdr, "typ", "dppCon");
+	json_value_sep(jws_prot_hdr);
+	json_add_string(jws_prot_hdr, "kid", conf->kid);
+	json_value_sep(jws_prot_hdr);
+	json_add_string(jws_prot_hdr, "alg", conf->curve->jws_alg);
+	json_end_object(jws_prot_hdr);
+	signed1 = base64_url_encode(wpabuf_head(jws_prot_hdr),
+				    wpabuf_len(jws_prot_hdr),
+				    signed1_len);
+	wpabuf_free(jws_prot_hdr);
+	return signed1;
+}
+
+
+static char *
+dpp_build_conn_signature(struct dpp_configurator *conf,
+			 const char *signed1, size_t signed1_len,
+			 const char *signed2, size_t signed2_len,
+			 size_t *signed3_len)
+{
+	const struct dpp_curve_params *curve;
+	char *signed3 = NULL;
+	unsigned char *signature = NULL;
+	const unsigned char *p;
+	size_t signature_len;
+	EVP_MD_CTX *md_ctx = NULL;
+	ECDSA_SIG *sig = NULL;
+	char *dot = ".";
+	const EVP_MD *sign_md;
+	const BIGNUM *r, *s;
+
+	curve = conf->curve;
+	if (curve->hash_len == SHA256_MAC_LEN) {
+		sign_md = EVP_sha256();
+	} else if (curve->hash_len == SHA384_MAC_LEN) {
+		sign_md = EVP_sha384();
+	} else if (curve->hash_len == SHA512_MAC_LEN) {
+		sign_md = EVP_sha512();
+	} else {
+		wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm");
+		goto fail;
+	}
+
+	md_ctx = EVP_MD_CTX_create();
+	if (!md_ctx)
+		goto fail;
+
+	ERR_clear_error();
+	if (EVP_DigestSignInit(md_ctx, NULL, sign_md, NULL, conf->csign) != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignInit failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	if (EVP_DigestSignUpdate(md_ctx, signed1, signed1_len) != 1 ||
+	    EVP_DigestSignUpdate(md_ctx, dot, 1) != 1 ||
+	    EVP_DigestSignUpdate(md_ctx, signed2, signed2_len) != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignUpdate failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	if (EVP_DigestSignFinal(md_ctx, NULL, &signature_len) != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	signature = os_malloc(signature_len);
+	if (!signature)
+		goto fail;
+	if (EVP_DigestSignFinal(md_ctx, signature, &signature_len) != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (DER)",
+		    signature, signature_len);
+	/* Convert to raw coordinates r,s */
+	p = signature;
+	sig = d2i_ECDSA_SIG(NULL, &p, signature_len);
+	if (!sig)
+		goto fail;
+	ECDSA_SIG_get0(sig, &r, &s);
+	if (dpp_bn2bin_pad(r, signature, curve->prime_len) < 0 ||
+	    dpp_bn2bin_pad(s, signature + curve->prime_len,
+			   curve->prime_len) < 0)
+		goto fail;
+	signature_len = 2 * curve->prime_len;
+	wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (raw r,s)",
+		    signature, signature_len);
+	signed3 = base64_url_encode(signature, signature_len, signed3_len);
+fail:
+	EVP_MD_CTX_destroy(md_ctx);
+	ECDSA_SIG_free(sig);
+	os_free(signature);
+	return signed3;
+}
+
+char * dpp_sign_connector(struct dpp_configurator *conf,
+			  const struct wpabuf *dppcon)
+{
+	char *signed1 = NULL, *signed2 = NULL, *signed3 = NULL;
+	char *signed_conn = NULL, *pos;
+	size_t signed1_len, signed2_len, signed3_len;
+
+	signed1 = dpp_build_jws_prot_hdr(conf, &signed1_len);
+	signed2 = base64_url_encode(wpabuf_head(dppcon), wpabuf_len(dppcon),
+				    &signed2_len);
+	if (!signed1 || !signed2)
+		goto fail;
+
+	signed3 = dpp_build_conn_signature(conf, signed1, signed1_len,
+					   signed2, signed2_len, &signed3_len);
+	if (!signed3)
+		goto fail;
+
+	signed_conn = os_malloc(signed1_len + signed2_len + signed3_len + 3);
+	if (!signed_conn)
+		goto fail;
+	pos = signed_conn;
+	os_memcpy(pos, signed1, signed1_len);
+	pos += signed1_len;
+	*pos++ = '.';
+	os_memcpy(pos, signed2, signed2_len);
+	pos += signed2_len;
+	*pos++ = '.';
+	os_memcpy(pos, signed3, signed3_len);
+	pos += signed3_len;
+	*pos = '\0';
+
+fail:
+	os_free(signed1);
+	os_free(signed2);
+	os_free(signed3);
+	return signed_conn;
+}
+
+
+#ifdef CONFIG_DPP2
+
+struct dpp_pfs * dpp_pfs_init(const u8 *net_access_key,
+			      size_t net_access_key_len)
+{
+	struct wpabuf *pub = NULL;
+	EVP_PKEY *own_key;
+	struct dpp_pfs *pfs;
+
+	pfs = os_zalloc(sizeof(*pfs));
+	if (!pfs)
+		return NULL;
+
+	own_key = dpp_set_keypair(&pfs->curve, net_access_key,
+				  net_access_key_len);
+	if (!own_key) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
+		goto fail;
+	}
+	EVP_PKEY_free(own_key);
+
+	pfs->ecdh = crypto_ecdh_init(pfs->curve->ike_group);
+	if (!pfs->ecdh)
+		goto fail;
+
+	pub = crypto_ecdh_get_pubkey(pfs->ecdh, 0);
+	pub = wpabuf_zeropad(pub, pfs->curve->prime_len);
+	if (!pub)
+		goto fail;
+
+	pfs->ie = wpabuf_alloc(5 + wpabuf_len(pub));
+	if (!pfs->ie)
+		goto fail;
+	wpabuf_put_u8(pfs->ie, WLAN_EID_EXTENSION);
+	wpabuf_put_u8(pfs->ie, 1 + 2 + wpabuf_len(pub));
+	wpabuf_put_u8(pfs->ie, WLAN_EID_EXT_OWE_DH_PARAM);
+	wpabuf_put_le16(pfs->ie, pfs->curve->ike_group);
+	wpabuf_put_buf(pfs->ie, pub);
+	wpabuf_free(pub);
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: Diffie-Hellman Parameter element",
+			pfs->ie);
+
+	return pfs;
+fail:
+	wpabuf_free(pub);
+	dpp_pfs_free(pfs);
+	return NULL;
+}
+
+
+int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len)
+{
+	if (peer_ie_len < 2)
+		return -1;
+	if (WPA_GET_LE16(peer_ie) != pfs->curve->ike_group) {
+		wpa_printf(MSG_DEBUG, "DPP: Peer used different group for PFS");
+		return -1;
+	}
+
+	pfs->secret = crypto_ecdh_set_peerkey(pfs->ecdh, 0, peer_ie + 2,
+					      peer_ie_len - 2);
+	pfs->secret = wpabuf_zeropad(pfs->secret, pfs->curve->prime_len);
+	if (!pfs->secret) {
+		wpa_printf(MSG_DEBUG, "DPP: Invalid peer DH public key");
+		return -1;
+	}
+	wpa_hexdump_buf_key(MSG_DEBUG, "DPP: DH shared secret", pfs->secret);
+	return 0;
+}
+
+
+void dpp_pfs_free(struct dpp_pfs *pfs)
+{
+	if (!pfs)
+		return;
+	crypto_ecdh_deinit(pfs->ecdh);
+	wpabuf_free(pfs->ie);
+	wpabuf_clear_free(pfs->secret);
+	os_free(pfs);
+}
+
+#endif /* CONFIG_DPP2 */
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+
+int dpp_test_gen_invalid_key(struct wpabuf *msg,
+			     const struct dpp_curve_params *curve)
+{
+	BN_CTX *ctx;
+	BIGNUM *x, *y;
+	int ret = -1;
+	EC_GROUP *group;
+	EC_POINT *point;
+
+	group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
+	if (!group)
+		return -1;
+
+	ctx = BN_CTX_new();
+	point = EC_POINT_new(group);
+	x = BN_new();
+	y = BN_new();
+	if (!ctx || !point || !x || !y)
+		goto fail;
+
+	if (BN_rand(x, curve->prime_len * 8, 0, 0) != 1)
+		goto fail;
+
+	/* Generate a random y coordinate that results in a point that is not
+	 * on the curve. */
+	for (;;) {
+		if (BN_rand(y, curve->prime_len * 8, 0, 0) != 1)
+			goto fail;
+
+		if (EC_POINT_set_affine_coordinates_GFp(group, point, x, y,
+							ctx) != 1) {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined(OPENSSL_IS_BORINGSSL)
+		/* Unlike older OpenSSL versions, OpenSSL 1.1.1 and BoringSSL
+		 * return an error from EC_POINT_set_affine_coordinates_GFp()
+		 * when the point is not on the curve. */
+			break;
+#else /* >=1.1.0 or OPENSSL_IS_BORINGSSL */
+			goto fail;
+#endif /* >= 1.1.0 or OPENSSL_IS_BORINGSSL */
+		}
+
+		if (!EC_POINT_is_on_curve(group, point, ctx))
+			break;
+	}
+
+	if (dpp_bn2bin_pad(x, wpabuf_put(msg, curve->prime_len),
+			   curve->prime_len) < 0 ||
+	    dpp_bn2bin_pad(y, wpabuf_put(msg, curve->prime_len),
+			   curve->prime_len) < 0)
+		goto fail;
+
+	ret = 0;
+fail:
+	if (ret < 0)
+		wpa_printf(MSG_INFO, "DPP: Failed to generate invalid key");
+	BN_free(x);
+	BN_free(y);
+	EC_POINT_free(point);
+	BN_CTX_free(ctx);
+	EC_GROUP_free(group);
+
+	return ret;
+}
+
+
+char * dpp_corrupt_connector_signature(const char *connector)
+{
+	char *tmp, *pos, *signed3 = NULL;
+	unsigned char *signature = NULL;
+	size_t signature_len = 0, signed3_len;
+
+	tmp = os_zalloc(os_strlen(connector) + 5);
+	if (!tmp)
+		goto fail;
+	os_memcpy(tmp, connector, os_strlen(connector));
+
+	pos = os_strchr(tmp, '.');
+	if (!pos)
+		goto fail;
+
+	pos = os_strchr(pos + 1, '.');
+	if (!pos)
+		goto fail;
+	pos++;
+
+	wpa_printf(MSG_DEBUG, "DPP: Original base64url encoded signature: %s",
+		   pos);
+	signature = base64_url_decode(pos, os_strlen(pos), &signature_len);
+	if (!signature || signature_len == 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: Original Connector signature",
+		    signature, signature_len);
+	signature[signature_len - 1] ^= 0x01;
+	wpa_hexdump(MSG_DEBUG, "DPP: Corrupted Connector signature",
+		    signature, signature_len);
+	signed3 = base64_url_encode(signature, signature_len, &signed3_len);
+	if (!signed3)
+		goto fail;
+	os_memcpy(pos, signed3, signed3_len);
+	pos[signed3_len] = '\0';
+	wpa_printf(MSG_DEBUG, "DPP: Corrupted base64url encoded signature: %s",
+		   pos);
+
+out:
+	os_free(signature);
+	os_free(signed3);
+	return tmp;
+fail:
+	os_free(tmp);
+	tmp = NULL;
+	goto out;
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/common/dpp_i.h b/src/common/dpp_i.h
new file mode 100644
index 0000000..e66eb6c
--- /dev/null
+++ b/src/common/dpp_i.h
@@ -0,0 +1,148 @@
+/*
+ * DPP module internal definitions
+ * 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.
+ */
+
+#ifndef DPP_I_H
+#define DPP_I_H
+
+#ifdef CONFIG_DPP
+
+struct dpp_global {
+	void *msg_ctx;
+	struct dl_list bootstrap; /* struct dpp_bootstrap_info */
+	struct dl_list configurator; /* struct dpp_configurator */
+#ifdef CONFIG_DPP2
+	struct dl_list controllers; /* struct dpp_relay_controller */
+	struct dpp_controller *controller;
+	struct dl_list tcp_init; /* struct dpp_connection */
+	void *cb_ctx;
+	int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
+	void (*remove_bi)(void *ctx, struct dpp_bootstrap_info *bi);
+#endif /* CONFIG_DPP2 */
+};
+
+/* dpp.c */
+
+void dpp_build_attr_status(struct wpabuf *msg, enum dpp_status_error status);
+void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg, const u8 *hash);
+unsigned int dpp_next_id(struct dpp_global *dpp);
+struct wpabuf * dpp_build_conn_status(enum dpp_status_error result,
+				      const u8 *ssid, size_t ssid_len,
+				      const char *channel_list);
+struct json_token * dpp_parse_own_connector(const char *own_connector);
+int dpp_connector_match_groups(struct json_token *own_root,
+			       struct json_token *peer_root, bool reconfig);
+int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key,
+		  const char *kid, const struct dpp_curve_params *curve);
+EVP_PKEY * dpp_parse_jwk(struct json_token *jwk,
+			 const struct dpp_curve_params **key_curve);
+int dpp_prepare_channel_list(struct dpp_authentication *auth,
+			     unsigned int neg_freq,
+			     struct hostapd_hw_modes *own_modes, u16 num_modes);
+void dpp_auth_fail(struct dpp_authentication *auth, const char *txt);
+int dpp_gen_uri(struct dpp_bootstrap_info *bi);
+void dpp_write_adv_proto(struct wpabuf *buf);
+void dpp_write_gas_query(struct wpabuf *buf, struct wpabuf *query);
+
+/* dpp_backup.c */
+
+void dpp_free_asymmetric_key(struct dpp_asymmetric_key *key);
+struct wpabuf * dpp_build_enveloped_data(struct dpp_authentication *auth);
+int dpp_conf_resp_env_data(struct dpp_authentication *auth,
+			   const u8 *env_data, size_t env_data_len);
+
+/* dpp_crypto.c */
+
+struct dpp_signed_connector_info {
+	unsigned char *payload;
+	size_t payload_len;
+};
+
+enum dpp_status_error
+dpp_process_signed_connector(struct dpp_signed_connector_info *info,
+			     EVP_PKEY *csign_pub, const char *connector);
+enum dpp_status_error
+dpp_check_signed_connector(struct dpp_signed_connector_info *info,
+			   const u8 *csign_key, size_t csign_key_len,
+			   const u8 *peer_connector, size_t peer_connector_len);
+const struct dpp_curve_params * dpp_get_curve_name(const char *name);
+const struct dpp_curve_params * dpp_get_curve_jwk_crv(const char *name);
+const struct dpp_curve_params * dpp_get_curve_nid(int nid);
+int dpp_bi_pubkey_hash(struct dpp_bootstrap_info *bi,
+		       const u8 *data, size_t data_len);
+struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix);
+EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group,
+				      const u8 *buf_x, const u8 *buf_y,
+				      size_t len);
+EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key, const u8 *buf, size_t len);
+int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len);
+int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len,
+		    const char *label, u8 *out, size_t outlen);
+int dpp_hmac_vector(size_t hash_len, const u8 *key, size_t key_len,
+		    size_t num_elem, const u8 *addr[], const size_t *len,
+		    u8 *mac);
+int dpp_ecdh(EVP_PKEY *own, EVP_PKEY *peer, u8 *secret, size_t *secret_len);
+void dpp_debug_print_point(const char *title, const EC_GROUP *group,
+			   const EC_POINT *point);
+void dpp_debug_print_key(const char *title, EVP_PKEY *key);
+int dpp_pbkdf2(size_t hash_len, const u8 *password, size_t password_len,
+	       const u8 *salt, size_t salt_len, unsigned int iterations,
+	       u8 *buf, size_t buflen);
+int dpp_get_subject_public_key(struct dpp_bootstrap_info *bi,
+			       const u8 *data, size_t data_len);
+int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi);
+int dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
+	       const u8 *privkey, size_t privkey_len);
+EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve,
+			   const u8 *privkey, size_t privkey_len);
+EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve);
+int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1, unsigned int hash_len);
+int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2, unsigned int hash_len);
+int dpp_derive_bk_ke(struct dpp_authentication *auth);
+int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth);
+int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth);
+int dpp_auth_derive_l_responder(struct dpp_authentication *auth);
+int dpp_auth_derive_l_initiator(struct dpp_authentication *auth);
+int dpp_derive_pmk(const u8 *Nx, size_t Nx_len, u8 *pmk, unsigned int hash_len);
+int dpp_derive_pmkid(const struct dpp_curve_params *curve,
+		     EVP_PKEY *own_key, EVP_PKEY *peer_key, u8 *pmkid);
+EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve,
+			      const u8 *mac_init, const char *code,
+			      const char *identifier, BN_CTX *bnctx,
+			      EC_GROUP **ret_group);
+EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve,
+			      const u8 *mac_resp, const char *code,
+			      const char *identifier, BN_CTX *bnctx,
+			      EC_GROUP **ret_group);
+int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp,
+		      const u8 *Mx, size_t Mx_len,
+		      const u8 *Nx, size_t Nx_len,
+		      const char *code,
+		      const u8 *Kx, size_t Kx_len,
+		      u8 *z, unsigned int hash_len);
+int dpp_reconfig_derive_ke_responder(struct dpp_authentication *auth,
+				     const u8 *net_access_key,
+				     size_t net_access_key_len,
+				     struct json_token *peer_net_access_key);
+int dpp_reconfig_derive_ke_initiator(struct dpp_authentication *auth,
+				     const u8 *r_proto, u16 r_proto_len,
+				     struct json_token *net_access_key);
+char * dpp_sign_connector(struct dpp_configurator *conf,
+			  const struct wpabuf *dppcon);
+int dpp_test_gen_invalid_key(struct wpabuf *msg,
+			     const struct dpp_curve_params *curve);
+
+/* dpp_tcp.c */
+
+void dpp_controller_conn_status_result_wait_timeout(void *eloop_ctx,
+						    void *timeout_ctx);
+void dpp_tcp_init_flush(struct dpp_global *dpp);
+void dpp_relay_flush_controllers(struct dpp_global *dpp);
+
+#endif /* CONFIG_DPP */
+#endif /* DPP_I_H */
diff --git a/src/common/dpp_pkex.c b/src/common/dpp_pkex.c
new file mode 100644
index 0000000..807ab7d
--- /dev/null
+++ b/src/common/dpp_pkex.c
@@ -0,0 +1,1324 @@
+/*
+ * DPP PKEX functionality
+ * 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 <openssl/opensslv.h>
+#include <openssl/err.h>
+
+#include "utils/common.h"
+#include "common/wpa_ctrl.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "crypto/crypto.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+u8 dpp_pkex_own_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+u8 dpp_pkex_peer_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+u8 dpp_pkex_ephemeral_key_override[600];
+size_t dpp_pkex_ephemeral_key_override_len = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+	(defined(LIBRESSL_VERSION_NUMBER) && \
+	 LIBRESSL_VERSION_NUMBER < 0x20700000L)
+/* Compatibility wrappers for older versions. */
+
+static EC_KEY * EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
+{
+	if (pkey->type != EVP_PKEY_EC)
+		return NULL;
+	return pkey->pkey.ec;
+}
+
+#endif
+
+
+static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex)
+{
+	const EC_KEY *X_ec;
+	const EC_POINT *X_point;
+	BN_CTX *bnctx = NULL;
+	EC_GROUP *group = NULL;
+	EC_POINT *Qi = NULL, *M = NULL;
+	struct wpabuf *M_buf = NULL;
+	BIGNUM *Mx = NULL, *My = NULL;
+	struct wpabuf *msg = NULL;
+	size_t attr_len;
+	const struct dpp_curve_params *curve = pkex->own_bi->curve;
+
+	wpa_printf(MSG_DEBUG, "DPP: Build PKEX Exchange Request");
+
+	/* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
+	bnctx = BN_CTX_new();
+	if (!bnctx)
+		goto fail;
+	Qi = dpp_pkex_derive_Qi(curve, pkex->own_mac, pkex->code,
+				pkex->identifier, bnctx, &group);
+	if (!Qi)
+		goto fail;
+
+	/* Generate a random ephemeral keypair x/X */
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_pkex_ephemeral_key_override_len) {
+		const struct dpp_curve_params *tmp_curve;
+
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - override ephemeral key x/X");
+		pkex->x = dpp_set_keypair(&tmp_curve,
+					  dpp_pkex_ephemeral_key_override,
+					  dpp_pkex_ephemeral_key_override_len);
+	} else {
+		pkex->x = dpp_gen_keypair(curve);
+	}
+#else /* CONFIG_TESTING_OPTIONS */
+	pkex->x = dpp_gen_keypair(curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (!pkex->x)
+		goto fail;
+
+	/* M = X + Qi */
+	X_ec = EVP_PKEY_get0_EC_KEY(pkex->x);
+	if (!X_ec)
+		goto fail;
+	X_point = EC_KEY_get0_public_key(X_ec);
+	if (!X_point)
+		goto fail;
+	dpp_debug_print_point("DPP: X", group, X_point);
+	M = EC_POINT_new(group);
+	Mx = BN_new();
+	My = BN_new();
+	if (!M || !Mx || !My ||
+	    EC_POINT_add(group, M, X_point, Qi, bnctx) != 1 ||
+	    EC_POINT_get_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1)
+		goto fail;
+	dpp_debug_print_point("DPP: M", group, M);
+
+	/* Initiator -> Responder: group, [identifier,] M */
+	attr_len = 4 + 2;
+	if (pkex->identifier)
+		attr_len += 4 + os_strlen(pkex->identifier);
+	attr_len += 4 + 2 * curve->prime_len;
+	msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_REQ, attr_len);
+	if (!msg)
+		goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_NO_FINITE_CYCLIC_GROUP_PKEX_EXCHANGE_REQ) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no Finite Cyclic Group");
+		goto skip_finite_cyclic_group;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* Finite Cyclic Group attribute */
+	wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
+	wpabuf_put_le16(msg, 2);
+	wpabuf_put_le16(msg, curve->ike_group);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_finite_cyclic_group:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* Code Identifier attribute */
+	if (pkex->identifier) {
+		wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
+		wpabuf_put_le16(msg, os_strlen(pkex->identifier));
+		wpabuf_put_str(msg, pkex->identifier);
+	}
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
+		goto out;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* M in Encrypted Key attribute */
+	wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
+	wpabuf_put_le16(msg, 2 * curve->prime_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
+		if (dpp_test_gen_invalid_key(msg, curve) < 0)
+			goto fail;
+		goto out;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (dpp_bn2bin_pad(Mx, wpabuf_put(msg, curve->prime_len),
+			   curve->prime_len) < 0 ||
+	    dpp_bn2bin_pad(Mx, pkex->Mx, curve->prime_len) < 0 ||
+	    dpp_bn2bin_pad(My, wpabuf_put(msg, curve->prime_len),
+			   curve->prime_len) < 0)
+		goto fail;
+
+out:
+	wpabuf_free(M_buf);
+	EC_POINT_free(M);
+	EC_POINT_free(Qi);
+	BN_clear_free(Mx);
+	BN_clear_free(My);
+	BN_CTX_free(bnctx);
+	EC_GROUP_free(group);
+	return msg;
+fail:
+	wpa_printf(MSG_INFO, "DPP: Failed to build PKEX Exchange Request");
+	wpabuf_free(msg);
+	msg = NULL;
+	goto out;
+}
+
+
+static void dpp_pkex_fail(struct dpp_pkex *pkex, const char *txt)
+{
+	wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
+}
+
+
+struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
+				const u8 *own_mac,
+				const char *identifier,
+				const char *code)
+{
+	struct dpp_pkex *pkex;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
+			   MAC2STR(dpp_pkex_own_mac_override));
+		own_mac = dpp_pkex_own_mac_override;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	pkex = os_zalloc(sizeof(*pkex));
+	if (!pkex)
+		return NULL;
+	pkex->msg_ctx = msg_ctx;
+	pkex->initiator = 1;
+	pkex->own_bi = bi;
+	os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
+	if (identifier) {
+		pkex->identifier = os_strdup(identifier);
+		if (!pkex->identifier)
+			goto fail;
+	}
+	pkex->code = os_strdup(code);
+	if (!pkex->code)
+		goto fail;
+	pkex->exchange_req = dpp_pkex_build_exchange_req(pkex);
+	if (!pkex->exchange_req)
+		goto fail;
+	return pkex;
+fail:
+	dpp_pkex_free(pkex);
+	return NULL;
+}
+
+
+static struct wpabuf *
+dpp_pkex_build_exchange_resp(struct dpp_pkex *pkex,
+			     enum dpp_status_error status,
+			     const BIGNUM *Nx, const BIGNUM *Ny)
+{
+	struct wpabuf *msg = NULL;
+	size_t attr_len;
+	const struct dpp_curve_params *curve = pkex->own_bi->curve;
+
+	/* Initiator -> Responder: DPP Status, [identifier,] N */
+	attr_len = 4 + 1;
+	if (pkex->identifier)
+		attr_len += 4 + os_strlen(pkex->identifier);
+	attr_len += 4 + 2 * curve->prime_len;
+	msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_RESP, attr_len);
+	if (!msg)
+		goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_NO_STATUS_PKEX_EXCHANGE_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+		goto skip_status;
+	}
+
+	if (dpp_test == DPP_TEST_INVALID_STATUS_PKEX_EXCHANGE_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+		status = 255;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* DPP Status */
+	dpp_build_attr_status(msg, status);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_status:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* Code Identifier attribute */
+	if (pkex->identifier) {
+		wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
+		wpabuf_put_le16(msg, os_strlen(pkex->identifier));
+		wpabuf_put_str(msg, pkex->identifier);
+	}
+
+	if (status != DPP_STATUS_OK)
+		goto skip_encrypted_key;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
+		goto skip_encrypted_key;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* N in Encrypted Key attribute */
+	wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
+	wpabuf_put_le16(msg, 2 * curve->prime_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
+		if (dpp_test_gen_invalid_key(msg, curve) < 0)
+			goto fail;
+		goto skip_encrypted_key;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (dpp_bn2bin_pad(Nx, wpabuf_put(msg, curve->prime_len),
+			   curve->prime_len) < 0 ||
+	    dpp_bn2bin_pad(Nx, pkex->Nx, curve->prime_len) < 0 ||
+	    dpp_bn2bin_pad(Ny, wpabuf_put(msg, curve->prime_len),
+			   curve->prime_len) < 0)
+		goto fail;
+
+skip_encrypted_key:
+	if (status == DPP_STATUS_BAD_GROUP) {
+		/* Finite Cyclic Group attribute */
+		wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
+		wpabuf_put_le16(msg, 2);
+		wpabuf_put_le16(msg, curve->ike_group);
+	}
+
+	return msg;
+fail:
+	wpabuf_free(msg);
+	return NULL;
+}
+
+
+static int dpp_pkex_identifier_match(const u8 *attr_id, u16 attr_id_len,
+				     const char *identifier)
+{
+	if (!attr_id && identifier) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No PKEX code identifier received, but expected one");
+		return 0;
+	}
+
+	if (attr_id && !identifier) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: PKEX code identifier received, but not expecting one");
+		return 0;
+	}
+
+	if (attr_id && identifier &&
+	    (os_strlen(identifier) != attr_id_len ||
+	     os_memcmp(identifier, attr_id, attr_id_len) != 0)) {
+		wpa_printf(MSG_DEBUG, "DPP: PKEX code identifier mismatch");
+		return 0;
+	}
+
+	return 1;
+}
+
+
+struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
+					   struct dpp_bootstrap_info *bi,
+					   const u8 *own_mac,
+					   const u8 *peer_mac,
+					   const char *identifier,
+					   const char *code,
+					   const u8 *buf, size_t len)
+{
+	const u8 *attr_group, *attr_id, *attr_key;
+	u16 attr_group_len, attr_id_len, attr_key_len;
+	const struct dpp_curve_params *curve = bi->curve;
+	u16 ike_group;
+	struct dpp_pkex *pkex = NULL;
+	EC_POINT *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL, *N = NULL;
+	BN_CTX *bnctx = NULL;
+	EC_GROUP *group = NULL;
+	BIGNUM *Mx = NULL, *My = NULL;
+	const EC_KEY *Y_ec;
+	EC_KEY *X_ec = NULL;
+	const EC_POINT *Y_point;
+	BIGNUM *Nx = NULL, *Ny = NULL;
+	u8 Kx[DPP_MAX_SHARED_SECRET_LEN];
+	size_t Kx_len;
+	int res;
+
+	if (bi->pkex_t >= PKEX_COUNTER_T_LIMIT) {
+		wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+			"PKEX counter t limit reached - ignore message");
+		return NULL;
+	}
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
+			   MAC2STR(dpp_pkex_peer_mac_override));
+		peer_mac = dpp_pkex_peer_mac_override;
+	}
+	if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
+			   MAC2STR(dpp_pkex_own_mac_override));
+		own_mac = dpp_pkex_own_mac_override;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	attr_id_len = 0;
+	attr_id = dpp_get_attr(buf, len, DPP_ATTR_CODE_IDENTIFIER,
+			       &attr_id_len);
+	if (!dpp_pkex_identifier_match(attr_id, attr_id_len, identifier))
+		return NULL;
+
+	attr_group = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
+				  &attr_group_len);
+	if (!attr_group || attr_group_len != 2) {
+		wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+			"Missing or invalid Finite Cyclic Group attribute");
+		return NULL;
+	}
+	ike_group = WPA_GET_LE16(attr_group);
+	if (ike_group != curve->ike_group) {
+		wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+			"Mismatching PKEX curve: peer=%u own=%u",
+			ike_group, curve->ike_group);
+		pkex = os_zalloc(sizeof(*pkex));
+		if (!pkex)
+			goto fail;
+		pkex->own_bi = bi;
+		pkex->failed = 1;
+		pkex->exchange_resp = dpp_pkex_build_exchange_resp(
+			pkex, DPP_STATUS_BAD_GROUP, NULL, NULL);
+		if (!pkex->exchange_resp)
+			goto fail;
+		return pkex;
+	}
+
+	/* M in Encrypted Key attribute */
+	attr_key = dpp_get_attr(buf, len, DPP_ATTR_ENCRYPTED_KEY,
+				&attr_key_len);
+	if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2 ||
+	    attr_key_len / 2 > DPP_MAX_SHARED_SECRET_LEN) {
+		wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+			"Missing Encrypted Key attribute");
+		return NULL;
+	}
+
+	/* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
+	bnctx = BN_CTX_new();
+	if (!bnctx)
+		goto fail;
+	Qi = dpp_pkex_derive_Qi(curve, peer_mac, code, identifier, bnctx,
+				&group);
+	if (!Qi)
+		goto fail;
+
+	/* X' = M - Qi */
+	X = EC_POINT_new(group);
+	M = EC_POINT_new(group);
+	Mx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
+	My = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
+	if (!X || !M || !Mx || !My ||
+	    EC_POINT_set_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1 ||
+	    EC_POINT_is_at_infinity(group, M) ||
+	    !EC_POINT_is_on_curve(group, M, bnctx) ||
+	    EC_POINT_invert(group, Qi, bnctx) != 1 ||
+	    EC_POINT_add(group, X, M, Qi, bnctx) != 1 ||
+	    EC_POINT_is_at_infinity(group, X) ||
+	    !EC_POINT_is_on_curve(group, X, bnctx)) {
+		wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+			"Invalid Encrypted Key value");
+		bi->pkex_t++;
+		goto fail;
+	}
+	dpp_debug_print_point("DPP: M", group, M);
+	dpp_debug_print_point("DPP: X'", group, X);
+
+	pkex = os_zalloc(sizeof(*pkex));
+	if (!pkex)
+		goto fail;
+	pkex->t = bi->pkex_t;
+	pkex->msg_ctx = msg_ctx;
+	pkex->own_bi = bi;
+	os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
+	os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
+	if (identifier) {
+		pkex->identifier = os_strdup(identifier);
+		if (!pkex->identifier)
+			goto fail;
+	}
+	pkex->code = os_strdup(code);
+	if (!pkex->code)
+		goto fail;
+
+	os_memcpy(pkex->Mx, attr_key, attr_key_len / 2);
+
+	X_ec = EC_KEY_new();
+	if (!X_ec ||
+	    EC_KEY_set_group(X_ec, group) != 1 ||
+	    EC_KEY_set_public_key(X_ec, X) != 1)
+		goto fail;
+	pkex->x = EVP_PKEY_new();
+	if (!pkex->x ||
+	    EVP_PKEY_set1_EC_KEY(pkex->x, X_ec) != 1)
+		goto fail;
+
+	/* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
+	Qr = dpp_pkex_derive_Qr(curve, own_mac, code, identifier, bnctx, NULL);
+	if (!Qr)
+		goto fail;
+
+	/* Generate a random ephemeral keypair y/Y */
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_pkex_ephemeral_key_override_len) {
+		const struct dpp_curve_params *tmp_curve;
+
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - override ephemeral key y/Y");
+		pkex->y = dpp_set_keypair(&tmp_curve,
+					  dpp_pkex_ephemeral_key_override,
+					  dpp_pkex_ephemeral_key_override_len);
+	} else {
+		pkex->y = dpp_gen_keypair(curve);
+	}
+#else /* CONFIG_TESTING_OPTIONS */
+	pkex->y = dpp_gen_keypair(curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (!pkex->y)
+		goto fail;
+
+	/* N = Y + Qr */
+	Y_ec = EVP_PKEY_get0_EC_KEY(pkex->y);
+	if (!Y_ec)
+		goto fail;
+	Y_point = EC_KEY_get0_public_key(Y_ec);
+	if (!Y_point)
+		goto fail;
+	dpp_debug_print_point("DPP: Y", group, Y_point);
+	N = EC_POINT_new(group);
+	Nx = BN_new();
+	Ny = BN_new();
+	if (!N || !Nx || !Ny ||
+	    EC_POINT_add(group, N, Y_point, Qr, bnctx) != 1 ||
+	    EC_POINT_get_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1)
+		goto fail;
+	dpp_debug_print_point("DPP: N", group, N);
+
+	pkex->exchange_resp = dpp_pkex_build_exchange_resp(pkex, DPP_STATUS_OK,
+							   Nx, Ny);
+	if (!pkex->exchange_resp)
+		goto fail;
+
+	/* K = y * X' */
+	if (dpp_ecdh(pkex->y, pkex->x, Kx, &Kx_len) < 0)
+		goto fail;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
+			Kx, Kx_len);
+
+	/* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
+	 */
+	res = dpp_pkex_derive_z(pkex->peer_mac, pkex->own_mac,
+				pkex->Mx, curve->prime_len,
+				pkex->Nx, curve->prime_len, pkex->code,
+				Kx, Kx_len, pkex->z, curve->hash_len);
+	os_memset(Kx, 0, Kx_len);
+	if (res < 0)
+		goto fail;
+
+	pkex->exchange_done = 1;
+
+out:
+	BN_CTX_free(bnctx);
+	EC_POINT_free(Qi);
+	EC_POINT_free(Qr);
+	BN_free(Mx);
+	BN_free(My);
+	BN_free(Nx);
+	BN_free(Ny);
+	EC_POINT_free(M);
+	EC_POINT_free(N);
+	EC_POINT_free(X);
+	EC_KEY_free(X_ec);
+	EC_GROUP_free(group);
+	return pkex;
+fail:
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request processing failed");
+	dpp_pkex_free(pkex);
+	pkex = NULL;
+	goto out;
+}
+
+
+static struct wpabuf *
+dpp_pkex_build_commit_reveal_req(struct dpp_pkex *pkex,
+				 const struct wpabuf *A_pub, const u8 *u)
+{
+	const struct dpp_curve_params *curve = pkex->own_bi->curve;
+	struct wpabuf *msg = NULL;
+	size_t clear_len, attr_len;
+	struct wpabuf *clear = NULL;
+	u8 *wrapped;
+	u8 octet;
+	const u8 *addr[2];
+	size_t len[2];
+
+	/* {A, u, [bootstrapping info]}z */
+	clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
+	clear = wpabuf_alloc(clear_len);
+	attr_len = 4 + clear_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ)
+		attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+	msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_REQ, attr_len);
+	if (!clear || !msg)
+		goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_REQ) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
+		goto skip_bootstrap_key;
+	}
+	if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_REQ) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
+		wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+		wpabuf_put_le16(clear, 2 * curve->prime_len);
+		if (dpp_test_gen_invalid_key(clear, curve) < 0)
+			goto fail;
+		goto skip_bootstrap_key;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* A in Bootstrap Key attribute */
+	wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+	wpabuf_put_le16(clear, wpabuf_len(A_pub));
+	wpabuf_put_buf(clear, A_pub);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_bootstrap_key:
+	if (dpp_test == DPP_TEST_NO_I_AUTH_TAG_PKEX_CR_REQ) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no I-Auth tag");
+		goto skip_i_auth_tag;
+	}
+	if (dpp_test == DPP_TEST_I_AUTH_TAG_MISMATCH_PKEX_CR_REQ) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - I-Auth tag mismatch");
+		wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
+		wpabuf_put_le16(clear, curve->hash_len);
+		wpabuf_put_data(clear, u, curve->hash_len - 1);
+		wpabuf_put_u8(clear, u[curve->hash_len - 1] ^ 0x01);
+		goto skip_i_auth_tag;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* u in I-Auth tag attribute */
+	wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
+	wpabuf_put_le16(clear, curve->hash_len);
+	wpabuf_put_data(clear, u, curve->hash_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_i_auth_tag:
+	if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_REQ) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+		goto skip_wrapped_data;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	addr[0] = wpabuf_head_u8(msg) + 2;
+	len[0] = DPP_HDR_LEN;
+	octet = 0;
+	addr[1] = &octet;
+	len[1] = sizeof(octet);
+	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]);
+
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+	wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+	if (aes_siv_encrypt(pkex->z, curve->hash_len,
+			    wpabuf_head(clear), wpabuf_len(clear),
+			    2, addr, len, wrapped) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_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 */
+
+out:
+	wpabuf_free(clear);
+	return msg;
+
+fail:
+	wpabuf_free(msg);
+	msg = NULL;
+	goto out;
+}
+
+
+struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
+					  const u8 *peer_mac,
+					  const u8 *buf, size_t buflen)
+{
+	const u8 *attr_status, *attr_id, *attr_key, *attr_group;
+	u16 attr_status_len, attr_id_len, attr_key_len, attr_group_len;
+	EC_GROUP *group = NULL;
+	BN_CTX *bnctx = NULL;
+	struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
+	const struct dpp_curve_params *curve = pkex->own_bi->curve;
+	EC_POINT *Qr = NULL, *Y = NULL, *N = NULL;
+	BIGNUM *Nx = NULL, *Ny = NULL;
+	EC_KEY *Y_ec = NULL;
+	size_t Jx_len, Kx_len;
+	u8 Jx[DPP_MAX_SHARED_SECRET_LEN], Kx[DPP_MAX_SHARED_SECRET_LEN];
+	const u8 *addr[4];
+	size_t len[4];
+	u8 u[DPP_MAX_HASH_LEN];
+	int res;
+
+	if (pkex->failed || pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
+		return NULL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_STOP_AT_PKEX_EXCHANGE_RESP) {
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - stop at PKEX Exchange Response");
+		pkex->failed = 1;
+		return NULL;
+	}
+
+	if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
+			   MAC2STR(dpp_pkex_peer_mac_override));
+		peer_mac = dpp_pkex_peer_mac_override;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
+
+	attr_status = dpp_get_attr(buf, buflen, DPP_ATTR_STATUS,
+				   &attr_status_len);
+	if (!attr_status || attr_status_len != 1) {
+		dpp_pkex_fail(pkex, "No DPP Status attribute");
+		return NULL;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Status %u", attr_status[0]);
+
+	if (attr_status[0] == DPP_STATUS_BAD_GROUP) {
+		attr_group = dpp_get_attr(buf, buflen,
+					  DPP_ATTR_FINITE_CYCLIC_GROUP,
+					  &attr_group_len);
+		if (attr_group && attr_group_len == 2) {
+			wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+				"Peer indicated mismatching PKEX group - proposed %u",
+				WPA_GET_LE16(attr_group));
+			return NULL;
+		}
+	}
+
+	if (attr_status[0] != DPP_STATUS_OK) {
+		dpp_pkex_fail(pkex, "PKEX failed (peer indicated failure)");
+		return NULL;
+	}
+
+	attr_id_len = 0;
+	attr_id = dpp_get_attr(buf, buflen, DPP_ATTR_CODE_IDENTIFIER,
+			       &attr_id_len);
+	if (!dpp_pkex_identifier_match(attr_id, attr_id_len,
+				       pkex->identifier)) {
+		dpp_pkex_fail(pkex, "PKEX code identifier mismatch");
+		return NULL;
+	}
+
+	/* N in Encrypted Key attribute */
+	attr_key = dpp_get_attr(buf, buflen, DPP_ATTR_ENCRYPTED_KEY,
+				&attr_key_len);
+	if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2) {
+		dpp_pkex_fail(pkex, "Missing Encrypted Key attribute");
+		return NULL;
+	}
+
+	/* Qr = H(MAC-Responder | [identifier |] code) * Pr */
+	bnctx = BN_CTX_new();
+	if (!bnctx)
+		goto fail;
+	Qr = dpp_pkex_derive_Qr(curve, pkex->peer_mac, pkex->code,
+				pkex->identifier, bnctx, &group);
+	if (!Qr)
+		goto fail;
+
+	/* Y' = N - Qr */
+	Y = EC_POINT_new(group);
+	N = EC_POINT_new(group);
+	Nx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
+	Ny = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
+	if (!Y || !N || !Nx || !Ny ||
+	    EC_POINT_set_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1 ||
+	    EC_POINT_is_at_infinity(group, N) ||
+	    !EC_POINT_is_on_curve(group, N, bnctx) ||
+	    EC_POINT_invert(group, Qr, bnctx) != 1 ||
+	    EC_POINT_add(group, Y, N, Qr, bnctx) != 1 ||
+	    EC_POINT_is_at_infinity(group, Y) ||
+	    !EC_POINT_is_on_curve(group, Y, bnctx)) {
+		dpp_pkex_fail(pkex, "Invalid Encrypted Key value");
+		pkex->t++;
+		goto fail;
+	}
+	dpp_debug_print_point("DPP: N", group, N);
+	dpp_debug_print_point("DPP: Y'", group, Y);
+
+	pkex->exchange_done = 1;
+
+	/* ECDH: J = a * Y' */
+	Y_ec = EC_KEY_new();
+	if (!Y_ec ||
+	    EC_KEY_set_group(Y_ec, group) != 1 ||
+	    EC_KEY_set_public_key(Y_ec, Y) != 1)
+		goto fail;
+	pkex->y = EVP_PKEY_new();
+	if (!pkex->y ||
+	    EVP_PKEY_set1_EC_KEY(pkex->y, Y_ec) != 1)
+		goto fail;
+	if (dpp_ecdh(pkex->own_bi->pubkey, pkex->y, Jx, &Jx_len) < 0)
+		goto fail;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
+			Jx, Jx_len);
+
+	/* u = HMAC(J.x, MAC-Initiator | A.x | Y'.x | X.x) */
+	A_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
+	Y_pub = dpp_get_pubkey_point(pkex->y, 0);
+	X_pub = dpp_get_pubkey_point(pkex->x, 0);
+	if (!A_pub || !Y_pub || !X_pub)
+		goto fail;
+	addr[0] = pkex->own_mac;
+	len[0] = ETH_ALEN;
+	addr[1] = wpabuf_head(A_pub);
+	len[1] = wpabuf_len(A_pub) / 2;
+	addr[2] = wpabuf_head(Y_pub);
+	len[2] = wpabuf_len(Y_pub) / 2;
+	addr[3] = wpabuf_head(X_pub);
+	len[3] = wpabuf_len(X_pub) / 2;
+	if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: u", u, curve->hash_len);
+
+	/* K = x * Y' */
+	if (dpp_ecdh(pkex->x, pkex->y, Kx, &Kx_len) < 0)
+		goto fail;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
+			Kx, Kx_len);
+
+	/* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
+	 */
+	res = dpp_pkex_derive_z(pkex->own_mac, pkex->peer_mac,
+				pkex->Mx, curve->prime_len,
+				attr_key /* N.x */, attr_key_len / 2,
+				pkex->code, Kx, Kx_len,
+				pkex->z, curve->hash_len);
+	os_memset(Kx, 0, Kx_len);
+	if (res < 0)
+		goto fail;
+
+	msg = dpp_pkex_build_commit_reveal_req(pkex, A_pub, u);
+	if (!msg)
+		goto fail;
+
+out:
+	wpabuf_free(A_pub);
+	wpabuf_free(X_pub);
+	wpabuf_free(Y_pub);
+	EC_POINT_free(Qr);
+	EC_POINT_free(Y);
+	EC_POINT_free(N);
+	BN_free(Nx);
+	BN_free(Ny);
+	EC_KEY_free(Y_ec);
+	BN_CTX_free(bnctx);
+	EC_GROUP_free(group);
+	return msg;
+fail:
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response processing failed");
+	goto out;
+}
+
+
+static struct wpabuf *
+dpp_pkex_build_commit_reveal_resp(struct dpp_pkex *pkex,
+				  const struct wpabuf *B_pub, const u8 *v)
+{
+	const struct dpp_curve_params *curve = pkex->own_bi->curve;
+	struct wpabuf *msg = NULL;
+	const u8 *addr[2];
+	size_t len[2];
+	u8 octet;
+	u8 *wrapped;
+	struct wpabuf *clear = NULL;
+	size_t clear_len, attr_len;
+
+	/* {B, v [bootstrapping info]}z */
+	clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
+	clear = wpabuf_alloc(clear_len);
+	attr_len = 4 + clear_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP)
+		attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+	msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_RESP, attr_len);
+	if (!clear || !msg)
+		goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
+		goto skip_bootstrap_key;
+	}
+	if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
+		wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+		wpabuf_put_le16(clear, 2 * curve->prime_len);
+		if (dpp_test_gen_invalid_key(clear, curve) < 0)
+			goto fail;
+		goto skip_bootstrap_key;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* B in Bootstrap Key attribute */
+	wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+	wpabuf_put_le16(clear, wpabuf_len(B_pub));
+	wpabuf_put_buf(clear, B_pub);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_bootstrap_key:
+	if (dpp_test == DPP_TEST_NO_R_AUTH_TAG_PKEX_CR_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth tag");
+		goto skip_r_auth_tag;
+	}
+	if (dpp_test == DPP_TEST_R_AUTH_TAG_MISMATCH_PKEX_CR_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - R-Auth tag mismatch");
+		wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
+		wpabuf_put_le16(clear, curve->hash_len);
+		wpabuf_put_data(clear, v, curve->hash_len - 1);
+		wpabuf_put_u8(clear, v[curve->hash_len - 1] ^ 0x01);
+		goto skip_r_auth_tag;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* v in R-Auth tag attribute */
+	wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
+	wpabuf_put_le16(clear, curve->hash_len);
+	wpabuf_put_data(clear, v, curve->hash_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_r_auth_tag:
+	if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+		goto skip_wrapped_data;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	addr[0] = wpabuf_head_u8(msg) + 2;
+	len[0] = DPP_HDR_LEN;
+	octet = 1;
+	addr[1] = &octet;
+	len[1] = sizeof(octet);
+	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]);
+
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+	wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+	if (aes_siv_encrypt(pkex->z, curve->hash_len,
+			    wpabuf_head(clear), wpabuf_len(clear),
+			    2, addr, len, wrapped) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_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 */
+
+out:
+	wpabuf_free(clear);
+	return msg;
+
+fail:
+	wpabuf_free(msg);
+	msg = NULL;
+	goto out;
+}
+
+
+struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
+					      const u8 *hdr,
+					      const u8 *buf, size_t buflen)
+{
+	const struct dpp_curve_params *curve = pkex->own_bi->curve;
+	size_t Jx_len, Lx_len;
+	u8 Jx[DPP_MAX_SHARED_SECRET_LEN];
+	u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
+	const u8 *wrapped_data, *b_key, *peer_u;
+	u16 wrapped_data_len, b_key_len, peer_u_len = 0;
+	const u8 *addr[4];
+	size_t len[4];
+	u8 octet;
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
+	struct wpabuf *B_pub = NULL;
+	u8 u[DPP_MAX_HASH_LEN], v[DPP_MAX_HASH_LEN];
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_REQ) {
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - stop at PKEX CR Request");
+		pkex->failed = 1;
+		return NULL;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (!pkex->exchange_done || pkex->failed ||
+	    pkex->t >= PKEX_COUNTER_T_LIMIT || pkex->initiator)
+		goto fail;
+
+	wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+		dpp_pkex_fail(pkex,
+			      "Missing or invalid required Wrapped Data attribute");
+		goto fail;
+	}
+
+	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;
+
+	addr[0] = hdr;
+	len[0] = DPP_HDR_LEN;
+	octet = 0;
+	addr[1] = &octet;
+	len[1] = sizeof(octet);
+	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]);
+
+	if (aes_siv_decrypt(pkex->z, curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    2, addr, len, unwrapped) < 0) {
+		dpp_pkex_fail(pkex,
+			      "AES-SIV decryption failed - possible PKEX code mismatch");
+		pkex->failed = 1;
+		pkex->t++;
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped, unwrapped_len);
+
+	if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+		dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
+			     &b_key_len);
+	if (!b_key || b_key_len != 2 * curve->prime_len) {
+		dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
+		goto fail;
+	}
+	pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
+							b_key_len);
+	if (!pkex->peer_bootstrap_key) {
+		dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
+		goto fail;
+	}
+	dpp_debug_print_key("DPP: Peer bootstrap public key",
+			    pkex->peer_bootstrap_key);
+
+	/* ECDH: J' = y * A' */
+	if (dpp_ecdh(pkex->y, pkex->peer_bootstrap_key, Jx, &Jx_len) < 0)
+		goto fail;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
+			Jx, Jx_len);
+
+	/* u' = HMAC(J'.x, MAC-Initiator | A'.x | Y.x | X'.x) */
+	A_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
+	Y_pub = dpp_get_pubkey_point(pkex->y, 0);
+	X_pub = dpp_get_pubkey_point(pkex->x, 0);
+	if (!A_pub || !Y_pub || !X_pub)
+		goto fail;
+	addr[0] = pkex->peer_mac;
+	len[0] = ETH_ALEN;
+	addr[1] = wpabuf_head(A_pub);
+	len[1] = wpabuf_len(A_pub) / 2;
+	addr[2] = wpabuf_head(Y_pub);
+	len[2] = wpabuf_len(Y_pub) / 2;
+	addr[3] = wpabuf_head(X_pub);
+	len[3] = wpabuf_len(X_pub) / 2;
+	if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
+		goto fail;
+
+	peer_u = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
+			      &peer_u_len);
+	if (!peer_u || peer_u_len != curve->hash_len ||
+	    os_memcmp(peer_u, u, curve->hash_len) != 0) {
+		dpp_pkex_fail(pkex, "No valid u (I-Auth tag) found");
+		wpa_hexdump(MSG_DEBUG, "DPP: Calculated u'",
+			    u, curve->hash_len);
+		wpa_hexdump(MSG_DEBUG, "DPP: Received u", peer_u, peer_u_len);
+		pkex->t++;
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Valid u (I-Auth tag) received");
+
+	/* ECDH: L = b * X' */
+	if (dpp_ecdh(pkex->own_bi->pubkey, pkex->x, Lx, &Lx_len) < 0)
+		goto fail;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
+			Lx, Lx_len);
+
+	/* v = HMAC(L.x, MAC-Responder | B.x | X'.x | Y.x) */
+	B_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
+	if (!B_pub)
+		goto fail;
+	addr[0] = pkex->own_mac;
+	len[0] = ETH_ALEN;
+	addr[1] = wpabuf_head(B_pub);
+	len[1] = wpabuf_len(B_pub) / 2;
+	addr[2] = wpabuf_head(X_pub);
+	len[2] = wpabuf_len(X_pub) / 2;
+	addr[3] = wpabuf_head(Y_pub);
+	len[3] = wpabuf_len(Y_pub) / 2;
+	if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: v", v, curve->hash_len);
+
+	msg = dpp_pkex_build_commit_reveal_resp(pkex, B_pub, v);
+	if (!msg)
+		goto fail;
+
+out:
+	os_free(unwrapped);
+	wpabuf_free(A_pub);
+	wpabuf_free(B_pub);
+	wpabuf_free(X_pub);
+	wpabuf_free(Y_pub);
+	return msg;
+fail:
+	wpa_printf(MSG_DEBUG,
+		   "DPP: PKEX Commit-Reveal Request processing failed");
+	goto out;
+}
+
+
+int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr,
+				   const u8 *buf, size_t buflen)
+{
+	const struct dpp_curve_params *curve = pkex->own_bi->curve;
+	const u8 *wrapped_data, *b_key, *peer_v;
+	u16 wrapped_data_len, b_key_len, peer_v_len = 0;
+	const u8 *addr[4];
+	size_t len[4];
+	u8 octet;
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	int ret = -1;
+	u8 v[DPP_MAX_HASH_LEN];
+	size_t Lx_len;
+	u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
+	struct wpabuf *B_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_RESP) {
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - stop at PKEX CR Response");
+		pkex->failed = 1;
+		goto fail;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (!pkex->exchange_done || pkex->failed ||
+	    pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
+		goto fail;
+
+	wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+		dpp_pkex_fail(pkex,
+			      "Missing or invalid required Wrapped Data attribute");
+		goto fail;
+	}
+
+	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;
+
+	addr[0] = hdr;
+	len[0] = DPP_HDR_LEN;
+	octet = 1;
+	addr[1] = &octet;
+	len[1] = sizeof(octet);
+	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]);
+
+	if (aes_siv_decrypt(pkex->z, curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    2, addr, len, unwrapped) < 0) {
+		dpp_pkex_fail(pkex,
+			      "AES-SIV decryption failed - possible PKEX code mismatch");
+		pkex->t++;
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped, unwrapped_len);
+
+	if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+		dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
+			     &b_key_len);
+	if (!b_key || b_key_len != 2 * curve->prime_len) {
+		dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
+		goto fail;
+	}
+	pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
+							b_key_len);
+	if (!pkex->peer_bootstrap_key) {
+		dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
+		goto fail;
+	}
+	dpp_debug_print_key("DPP: Peer bootstrap public key",
+			    pkex->peer_bootstrap_key);
+
+	/* ECDH: L' = x * B' */
+	if (dpp_ecdh(pkex->x, pkex->peer_bootstrap_key, Lx, &Lx_len) < 0)
+		goto fail;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
+			Lx, Lx_len);
+
+	/* v' = HMAC(L.x, MAC-Responder | B'.x | X.x | Y'.x) */
+	B_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
+	X_pub = dpp_get_pubkey_point(pkex->x, 0);
+	Y_pub = dpp_get_pubkey_point(pkex->y, 0);
+	if (!B_pub || !X_pub || !Y_pub)
+		goto fail;
+	addr[0] = pkex->peer_mac;
+	len[0] = ETH_ALEN;
+	addr[1] = wpabuf_head(B_pub);
+	len[1] = wpabuf_len(B_pub) / 2;
+	addr[2] = wpabuf_head(X_pub);
+	len[2] = wpabuf_len(X_pub) / 2;
+	addr[3] = wpabuf_head(Y_pub);
+	len[3] = wpabuf_len(Y_pub) / 2;
+	if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
+		goto fail;
+
+	peer_v = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_AUTH_TAG,
+			      &peer_v_len);
+	if (!peer_v || peer_v_len != curve->hash_len ||
+	    os_memcmp(peer_v, v, curve->hash_len) != 0) {
+		dpp_pkex_fail(pkex, "No valid v (R-Auth tag) found");
+		wpa_hexdump(MSG_DEBUG, "DPP: Calculated v'",
+			    v, curve->hash_len);
+		wpa_hexdump(MSG_DEBUG, "DPP: Received v", peer_v, peer_v_len);
+		pkex->t++;
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Valid v (R-Auth tag) received");
+
+	ret = 0;
+out:
+	wpabuf_free(B_pub);
+	wpabuf_free(X_pub);
+	wpabuf_free(Y_pub);
+	os_free(unwrapped);
+	return ret;
+fail:
+	goto out;
+}
+
+
+struct dpp_bootstrap_info *
+dpp_pkex_finish(struct dpp_global *dpp, struct dpp_pkex *pkex, const u8 *peer,
+		unsigned int freq)
+{
+	struct dpp_bootstrap_info *bi;
+
+	bi = os_zalloc(sizeof(*bi));
+	if (!bi)
+		return NULL;
+	bi->id = dpp_next_id(dpp);
+	bi->type = DPP_BOOTSTRAP_PKEX;
+	os_memcpy(bi->mac_addr, peer, ETH_ALEN);
+	bi->num_freq = 1;
+	bi->freq[0] = freq;
+	bi->curve = pkex->own_bi->curve;
+	bi->pubkey = pkex->peer_bootstrap_key;
+	pkex->peer_bootstrap_key = NULL;
+	if (dpp_bootstrap_key_hash(bi) < 0) {
+		dpp_bootstrap_info_free(bi);
+		return NULL;
+	}
+	dpp_pkex_free(pkex);
+	dl_list_add(&dpp->bootstrap, &bi->list);
+	return bi;
+}
+
+
+void dpp_pkex_free(struct dpp_pkex *pkex)
+{
+	if (!pkex)
+		return;
+
+	os_free(pkex->identifier);
+	os_free(pkex->code);
+	EVP_PKEY_free(pkex->x);
+	EVP_PKEY_free(pkex->y);
+	EVP_PKEY_free(pkex->peer_bootstrap_key);
+	wpabuf_free(pkex->exchange_req);
+	wpabuf_free(pkex->exchange_resp);
+	os_free(pkex);
+}
diff --git a/src/common/dpp_reconfig.c b/src/common/dpp_reconfig.c
new file mode 100644
index 0000000..0bb00cc
--- /dev/null
+++ b/src/common/dpp_reconfig.c
@@ -0,0 +1,881 @@
+/*
+ * DPP reconfiguration
+ * Copyright (c) 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 <openssl/opensslv.h>
+#include <openssl/err.h>
+
+#include "utils/common.h"
+#include "utils/json.h"
+#include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+
+#ifdef CONFIG_DPP2
+
+static void dpp_build_attr_csign_key_hash(struct wpabuf *msg, const u8 *hash)
+{
+	if (hash) {
+		wpa_printf(MSG_DEBUG, "DPP: Configurator C-sign key Hash");
+		wpabuf_put_le16(msg, DPP_ATTR_C_SIGN_KEY_HASH);
+		wpabuf_put_le16(msg, SHA256_MAC_LEN);
+		wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
+	}
+}
+
+
+struct wpabuf * dpp_build_reconfig_announcement(const u8 *csign_key,
+						size_t csign_key_len)
+{
+	struct wpabuf *msg;
+	EVP_PKEY *csign = NULL;
+	const unsigned char *p;
+	struct wpabuf *uncomp;
+	u8 hash[SHA256_MAC_LEN];
+	const u8 *addr[1];
+	size_t len[1];
+	int res;
+
+	wpa_printf(MSG_DEBUG, "DPP: Build Reconfig Announcement frame");
+
+	p = csign_key;
+	csign = d2i_PUBKEY(NULL, &p, csign_key_len);
+	if (!csign) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to parse local C-sign-key information");
+		return NULL;
+	}
+
+	uncomp = dpp_get_pubkey_point(csign, 1);
+	EVP_PKEY_free(csign);
+	if (!uncomp)
+		return NULL;
+	addr[0] = wpabuf_head(uncomp);
+	len[0] = wpabuf_len(uncomp);
+	wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed C-sign key", addr[0], len[0]);
+	res = sha256_vector(1, addr, len, hash);
+	wpabuf_free(uncomp);
+	if (res < 0)
+		return NULL;
+	wpa_hexdump(MSG_DEBUG, "DPP: kid = SHA256(uncompressed C-sign key)",
+		    hash, SHA256_MAC_LEN);
+
+	msg = dpp_alloc_msg(DPP_PA_RECONFIG_ANNOUNCEMENT, 4 + SHA256_MAC_LEN);
+	if (!msg)
+		return NULL;
+
+	/* Configurator C-sign key Hash */
+	dpp_build_attr_csign_key_hash(msg, hash);
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Reconfig Announcement frame attributes", msg);
+	return msg;
+}
+
+
+static struct wpabuf * dpp_reconfig_build_req(struct dpp_authentication *auth)
+{
+	struct wpabuf *msg;
+	size_t attr_len;
+
+	/* Build DPP Reconfig Authentication Request frame attributes */
+	attr_len = 4 + 1 + 4 + 1 + 4 + os_strlen(auth->conf->connector) +
+		4 + auth->curve->nonce_len;
+	msg = dpp_alloc_msg(DPP_PA_RECONFIG_AUTH_REQ, attr_len);
+	if (!msg)
+		return NULL;
+
+	/* Transaction ID */
+	wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, auth->transaction_id);
+
+	/* Protocol Version */
+	wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, DPP_VERSION);
+
+	/* DPP Connector */
+	wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+	wpabuf_put_le16(msg, os_strlen(auth->conf->connector));
+	wpabuf_put_str(msg, auth->conf->connector);
+
+	/* I-nonce */
+	wpabuf_put_le16(msg, DPP_ATTR_I_NONCE);
+	wpabuf_put_le16(msg, auth->curve->nonce_len);
+	wpabuf_put_data(msg, auth->i_nonce, auth->curve->nonce_len);
+
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Reconfig Authentication Request frame attributes",
+			msg);
+
+	return msg;
+}
+
+
+static int dpp_configurator_build_own_connector(struct dpp_configurator *conf)
+{
+	struct wpabuf *dppcon = NULL;
+	int ret = -1;
+
+	if (conf->connector)
+		return 0; /* already generated */
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Sign own Configurator Connector for reconfiguration with curve %s",
+		   conf->curve->name);
+	conf->connector_key = dpp_gen_keypair(conf->curve);
+	if (!conf->connector_key)
+		goto fail;
+
+	/* Connector (JSON dppCon object) */
+	dppcon = wpabuf_alloc(1000 + 2 * conf->curve->prime_len * 4 / 3);
+	if (!dppcon)
+		goto fail;
+	json_start_object(dppcon, NULL);
+	json_start_array(dppcon, "groups");
+	json_start_object(dppcon, NULL);
+	json_add_string(dppcon, "groupId", "*");
+	json_value_sep(dppcon);
+	json_add_string(dppcon, "netRole", "configurator");
+	json_end_object(dppcon);
+	json_end_array(dppcon);
+	json_value_sep(dppcon);
+	if (dpp_build_jwk(dppcon, "netAccessKey", conf->connector_key, NULL,
+			  conf->curve) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK");
+		goto fail;
+	}
+	json_end_object(dppcon);
+	wpa_printf(MSG_DEBUG, "DPP: dppCon: %s",
+		   (const char *) wpabuf_head(dppcon));
+
+	conf->connector = dpp_sign_connector(conf, dppcon);
+	if (!conf->connector)
+		goto fail;
+	wpa_printf(MSG_DEBUG, "DPP: signedConnector: %s", conf->connector);
+
+	ret = 0;
+fail:
+	wpabuf_free(dppcon);
+	return ret;
+}
+
+
+struct dpp_authentication *
+dpp_reconfig_init(struct dpp_global *dpp, void *msg_ctx,
+		  struct dpp_configurator *conf, unsigned int freq)
+{
+	struct dpp_authentication *auth;
+
+	auth = dpp_alloc_auth(dpp, msg_ctx);
+	if (!auth)
+		return NULL;
+
+	auth->conf = conf;
+	auth->reconfig = 1;
+	auth->initiator = 1;
+	auth->waiting_auth_resp = 1;
+	auth->allowed_roles = DPP_CAPAB_CONFIGURATOR;
+	auth->configurator = 1;
+	auth->curve = conf->curve;
+	auth->transaction_id = 1;
+	if (freq && dpp_prepare_channel_list(auth, freq, NULL, 0) < 0)
+		goto fail;
+
+	if (dpp_configurator_build_own_connector(conf) < 0)
+		goto fail;
+
+	if (random_get_bytes(auth->i_nonce, auth->curve->nonce_len)) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce");
+		goto fail;
+	}
+
+	auth->reconfig_req_msg = dpp_reconfig_build_req(auth);
+	if (!auth->reconfig_req_msg)
+		goto fail;
+
+out:
+	return auth;
+fail:
+	dpp_auth_deinit(auth);
+	auth = NULL;
+	goto out;
+}
+
+
+static int dpp_reconfig_build_resp(struct dpp_authentication *auth,
+				   const char *own_connector,
+				   struct wpabuf *conn_status)
+{
+	struct wpabuf *msg = NULL, *clear, *pr = NULL;
+	u8 *attr_start, *attr_end;
+	size_t clear_len, attr_len, len[2];
+	const u8 *addr[2];
+	u8 *wrapped;
+	int res = -1;
+
+	/* Build DPP Reconfig Authentication Response frame attributes */
+	clear_len = 2 * (4 + auth->curve->nonce_len) +
+		4 + wpabuf_len(conn_status);
+	clear = wpabuf_alloc(clear_len);
+	if (!clear)
+		goto fail;
+
+	/* I-nonce (wrapped) */
+	wpabuf_put_le16(clear, DPP_ATTR_I_NONCE);
+	wpabuf_put_le16(clear, auth->curve->nonce_len);
+	wpabuf_put_data(clear, auth->i_nonce, auth->curve->nonce_len);
+
+	/* R-nonce (wrapped) */
+	wpabuf_put_le16(clear, DPP_ATTR_R_NONCE);
+	wpabuf_put_le16(clear, auth->curve->nonce_len);
+	wpabuf_put_data(clear, auth->r_nonce, auth->curve->nonce_len);
+
+	/* Connection Status (wrapped) */
+	wpabuf_put_le16(clear, DPP_ATTR_CONN_STATUS);
+	wpabuf_put_le16(clear, wpabuf_len(conn_status));
+	wpabuf_put_buf(clear, conn_status);
+
+	pr = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+	if (!pr)
+		goto fail;
+
+	attr_len = 4 + 1 + 4 + 1 +
+		4 + os_strlen(own_connector) +
+		4 + wpabuf_len(pr) +
+		4 + wpabuf_len(clear) + AES_BLOCK_SIZE;
+	msg = dpp_alloc_msg(DPP_PA_RECONFIG_AUTH_RESP, attr_len);
+	if (!msg)
+		goto fail;
+
+	attr_start = wpabuf_put(msg, 0);
+
+	/* Transaction ID */
+	wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, auth->transaction_id);
+
+	/* Protocol Version */
+	wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, DPP_VERSION);
+
+	/* R-Connector */
+	wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+	wpabuf_put_le16(msg, os_strlen(own_connector));
+	wpabuf_put_str(msg, own_connector);
+
+	/* Responder Protocol Key (Pr) */
+	wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
+	wpabuf_put_le16(msg, wpabuf_len(pr));
+	wpabuf_put_buf(msg, pr);
+
+	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]);
+
+	/* Wrapped Data: {I-nonce, R-nonce, Connection Status}ke */
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+	wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+	if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+			    wpabuf_head(clear), wpabuf_len(clear),
+			    2, addr, len, wrapped) < 0)
+		goto fail;
+
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Reconfig Authentication Response frame attributes",
+			msg);
+
+	wpabuf_free(auth->reconfig_resp_msg);
+	auth->reconfig_resp_msg = msg;
+
+	res = 0;
+out:
+	wpabuf_free(clear);
+	wpabuf_free(pr);
+	return res;
+fail:
+	wpabuf_free(msg);
+	goto out;
+}
+
+
+struct dpp_authentication *
+dpp_reconfig_auth_req_rx(struct dpp_global *dpp, void *msg_ctx,
+			 const char *own_connector,
+			 const u8 *net_access_key, size_t net_access_key_len,
+			 const u8 *csign_key, size_t csign_key_len,
+			 unsigned int freq, const u8 *hdr,
+			 const u8 *attr_start, size_t attr_len)
+{
+	struct dpp_authentication *auth = NULL;
+	const u8 *trans_id, *version, *i_connector, *i_nonce;
+	u16 trans_id_len, version_len, i_connector_len, i_nonce_len;
+	struct dpp_signed_connector_info info;
+	enum dpp_status_error res;
+	struct json_token *root = NULL, *own_root = NULL, *token;
+	unsigned char *own_conn = NULL;
+	struct wpabuf *conn_status = NULL;
+
+	os_memset(&info, 0, sizeof(info));
+
+	trans_id = dpp_get_attr(attr_start, attr_len, DPP_ATTR_TRANSACTION_ID,
+			       &trans_id_len);
+	if (!trans_id || trans_id_len != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include Transaction ID");
+		goto fail;
+	}
+
+	version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
+			       &version_len);
+	if (!version || version_len < 1 || version[0] < 2) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid Protocol Version attribute");
+		goto fail;
+	}
+
+	i_connector = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CONNECTOR,
+			       &i_connector_len);
+	if (!i_connector) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing I-Connector attribute");
+		goto fail;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "DPP: I-Connector",
+			  i_connector, i_connector_len);
+
+	i_nonce = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_NONCE,
+			       &i_nonce_len);
+	if (!i_nonce || i_nonce_len > DPP_MAX_NONCE_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid I-Nonce attribute");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: I-Nonce", i_nonce, i_nonce_len);
+
+	res = dpp_check_signed_connector(&info, csign_key, csign_key_len,
+					 i_connector, i_connector_len);
+	if (res != DPP_STATUS_OK) {
+		wpa_printf(MSG_DEBUG, "DPP: Invalid I-Connector");
+		goto fail;
+	}
+
+	root = json_parse((const char *) info.payload, info.payload_len);
+	own_root = dpp_parse_own_connector(own_connector);
+	if (!root || !own_root ||
+	    !dpp_connector_match_groups(own_root, root, true)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: I-Connector does not include compatible group netrole with own connector");
+		goto fail;
+	}
+
+	token = json_get_member(root, "expiry");
+	if (token && token->type == JSON_STRING &&
+	    dpp_key_expired(token->string, NULL)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: I-Connector (netAccessKey) has expired");
+		goto fail;
+	}
+
+	token = json_get_member(root, "netAccessKey");
+	if (!token || token->type != JSON_OBJECT) {
+		wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found");
+		goto fail;
+	}
+
+	auth = dpp_alloc_auth(dpp, msg_ctx);
+	if (!auth)
+		return NULL;
+
+	auth->reconfig = 1;
+	auth->allowed_roles = DPP_CAPAB_ENROLLEE;
+	if (dpp_prepare_channel_list(auth, freq, NULL, 0) < 0)
+		goto fail;
+
+	auth->transaction_id = trans_id[0];
+
+	auth->peer_version = version[0];
+	wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
+		   auth->peer_version);
+
+	os_memcpy(auth->i_nonce, i_nonce, i_nonce_len);
+
+	if (dpp_reconfig_derive_ke_responder(auth, net_access_key,
+					     net_access_key_len, token) < 0)
+		goto fail;
+
+	if (i_nonce_len != auth->curve->nonce_len) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected I-nonce length %u (curve nonce len %zu)",
+			   i_nonce_len, auth->curve->nonce_len);
+		goto fail;
+	}
+
+	if (random_get_bytes(auth->r_nonce, auth->curve->nonce_len)) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce");
+		goto fail;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "DPP: R-nonce",
+			auth->r_nonce, auth->curve->nonce_len);
+
+	/* Build Connection Status object */
+	/* TODO: Get appropriate result value */
+	/* TODO: ssid64 and channelList */
+	conn_status = dpp_build_conn_status(DPP_STATUS_NO_AP, NULL, 0, NULL);
+	if (!conn_status)
+		goto fail;
+
+	if (dpp_reconfig_build_resp(auth, own_connector, conn_status) < 0)
+		goto fail;
+
+out:
+	os_free(info.payload);
+	os_free(own_conn);
+	json_free(root);
+	json_free(own_root);
+	wpabuf_free(conn_status);
+	return auth;
+fail:
+	dpp_auth_deinit(auth);
+	auth = NULL;
+	goto out;
+}
+
+
+static struct wpabuf *
+dpp_build_reconfig_flags(enum dpp_connector_key connector_key)
+{
+	struct wpabuf *json;
+
+	json = wpabuf_alloc(100);
+	if (!json)
+		return NULL;
+	json_start_object(json, NULL);
+	json_add_int(json, "connectorKey", connector_key);
+	json_end_object(json);
+	wpa_hexdump_ascii(MSG_DEBUG, "DPP: Reconfig-Flags JSON",
+			  wpabuf_head(json), wpabuf_len(json));
+
+	return json;
+}
+
+
+struct wpabuf *
+dpp_reconfig_build_conf(struct dpp_authentication *auth)
+{
+	struct wpabuf *msg = NULL, *clear = NULL, *reconfig_flags;
+	u8 *attr_start, *attr_end;
+	size_t clear_len, attr_len, len[2];
+	const u8 *addr[2];
+	u8 *wrapped;
+
+	reconfig_flags = dpp_build_reconfig_flags(DPP_CONFIG_REPLACEKEY);
+	if (!reconfig_flags)
+		goto fail;
+
+	/* Build DPP Reconfig Authentication Confirm frame attributes */
+	clear_len = 4 + 1 + 4 + 1 + 2 * (4 + auth->curve->nonce_len) +
+		4 + wpabuf_len(reconfig_flags);
+	clear = wpabuf_alloc(clear_len);
+	if (!clear)
+		goto fail;
+
+	/* Transaction ID */
+	wpabuf_put_le16(clear, DPP_ATTR_TRANSACTION_ID);
+	wpabuf_put_le16(clear, 1);
+	wpabuf_put_u8(clear, auth->transaction_id);
+
+	/* Protocol Version */
+	wpabuf_put_le16(clear, DPP_ATTR_PROTOCOL_VERSION);
+	wpabuf_put_le16(clear, 1);
+	wpabuf_put_u8(clear, auth->peer_version);
+
+	/* I-nonce (wrapped) */
+	wpabuf_put_le16(clear, DPP_ATTR_I_NONCE);
+	wpabuf_put_le16(clear, auth->curve->nonce_len);
+	wpabuf_put_data(clear, auth->i_nonce, auth->curve->nonce_len);
+
+	/* R-nonce (wrapped) */
+	wpabuf_put_le16(clear, DPP_ATTR_R_NONCE);
+	wpabuf_put_le16(clear, auth->curve->nonce_len);
+	wpabuf_put_data(clear, auth->r_nonce, auth->curve->nonce_len);
+
+	/* Reconfig-Flags (wrapped) */
+	wpabuf_put_le16(clear, DPP_ATTR_RECONFIG_FLAGS);
+	wpabuf_put_le16(clear, wpabuf_len(reconfig_flags));
+	wpabuf_put_buf(clear, reconfig_flags);
+
+	attr_len = 4 + wpabuf_len(clear) + AES_BLOCK_SIZE;
+	msg = dpp_alloc_msg(DPP_PA_RECONFIG_AUTH_CONF, attr_len);
+	if (!msg)
+		goto fail;
+
+	attr_start = wpabuf_put(msg, 0);
+	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]);
+
+	/* Wrapped Data */
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+	wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+	if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+			    wpabuf_head(clear), wpabuf_len(clear),
+			    2, addr, len, wrapped) < 0)
+		goto fail;
+
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Reconfig Authentication Confirm frame attributes",
+			msg);
+
+out:
+	wpabuf_free(reconfig_flags);
+	wpabuf_free(clear);
+	return msg;
+fail:
+	wpabuf_free(msg);
+	msg = NULL;
+	goto out;
+}
+
+
+struct wpabuf *
+dpp_reconfig_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
+			 const u8 *attr_start, size_t attr_len)
+{
+	const u8 *trans_id, *version, *r_connector, *r_proto, *wrapped_data,
+		*i_nonce, *r_nonce, *conn_status;
+	u16 trans_id_len, version_len, r_connector_len, r_proto_len,
+		wrapped_data_len, i_nonce_len, r_nonce_len, conn_status_len;
+	struct wpabuf *conf = NULL;
+	char *signed_connector = NULL;
+	struct dpp_signed_connector_info info;
+	enum dpp_status_error res;
+	struct json_token *root = NULL, *token, *conn_status_json = NULL;
+	const u8 *addr[2];
+	size_t len[2];
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+
+	os_memset(&info, 0, sizeof(info));
+
+	if (!auth->reconfig || !auth->configurator)
+		goto fail;
+
+	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");
+		goto fail;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
+		    wrapped_data, wrapped_data_len);
+	attr_len = wrapped_data - 4 - attr_start;
+
+	trans_id = dpp_get_attr(attr_start, attr_len, DPP_ATTR_TRANSACTION_ID,
+			       &trans_id_len);
+	if (!trans_id || trans_id_len != 1) {
+		dpp_auth_fail(auth, "Peer did not include Transaction ID");
+		goto fail;
+	}
+	if (trans_id[0] != auth->transaction_id) {
+		dpp_auth_fail(auth, "Transaction ID mismatch");
+		goto fail;
+	}
+
+	version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
+			       &version_len);
+	if (!version || version_len < 1 || version[0] < 2) {
+		dpp_auth_fail(auth,
+			      "Missing or invalid Protocol Version attribute");
+		goto fail;
+	}
+	auth->peer_version = version[0];
+	wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
+		   auth->peer_version);
+
+	r_connector = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CONNECTOR,
+				   &r_connector_len);
+	if (!r_connector) {
+		dpp_auth_fail(auth, " Missing R-Connector attribute");
+		goto fail;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "DPP: R-Connector",
+			  r_connector, r_connector_len);
+
+	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");
+		goto fail;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key",
+		    r_proto, r_proto_len);
+
+	signed_connector = os_malloc(r_connector_len + 1);
+	if (!signed_connector)
+		goto fail;
+	os_memcpy(signed_connector, r_connector, r_connector_len);
+	signed_connector[r_connector_len] = '\0';
+
+	res = dpp_process_signed_connector(&info, auth->conf->csign,
+					   signed_connector);
+	if (res != DPP_STATUS_OK) {
+		dpp_auth_fail(auth, "Invalid R-Connector");
+		goto fail;
+	}
+
+	root = json_parse((const char *) info.payload, info.payload_len);
+	if (!root) {
+		dpp_auth_fail(auth, "Invalid Connector payload");
+		goto fail;
+	}
+
+	/* Do not check netAccessKey expiration for reconfiguration to allow
+	 * expired Connector to be updated. */
+
+	token = json_get_member(root, "netAccessKey");
+	if (!token || token->type != JSON_OBJECT) {
+		dpp_auth_fail(auth, "No netAccessKey object found");
+		goto fail;
+	}
+
+	if (dpp_reconfig_derive_ke_initiator(auth, r_proto, r_proto_len,
+					     token) < 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->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_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+			       &i_nonce_len);
+	if (!i_nonce || i_nonce_len != auth->curve->nonce_len ||
+	    os_memcmp(i_nonce, auth->i_nonce, i_nonce_len) != 0) {
+		dpp_auth_fail(auth, "Missing or invalid I-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+
+	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, "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);
+
+	conn_status = dpp_get_attr(unwrapped, unwrapped_len,
+				   DPP_ATTR_CONN_STATUS, &conn_status_len);
+	if (!conn_status) {
+		dpp_auth_fail(auth, "Missing Connection Status attribute");
+		goto fail;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "DPP: connStatus",
+			  conn_status, conn_status_len);
+
+	conn_status_json = json_parse((const char *) conn_status,
+				      conn_status_len);
+	if (!conn_status_json) {
+		dpp_auth_fail(auth, "Could not parse connStatus");
+		goto fail;
+	}
+	/* TODO: use connStatus information */
+
+	conf = dpp_reconfig_build_conf(auth);
+	if (conf)
+		auth->reconfig_success = true;
+
+out:
+	json_free(root);
+	json_free(conn_status_json);
+	bin_clear_free(unwrapped, unwrapped_len);
+	os_free(info.payload);
+	os_free(signed_connector);
+	return conf;
+fail:
+	wpabuf_free(conf);
+	conf = NULL;
+	goto out;
+}
+
+
+int dpp_reconfig_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
+			      const u8 *attr_start, size_t attr_len)
+{
+	const u8 *trans_id, *version, *wrapped_data, *i_nonce, *r_nonce,
+		*reconfig_flags;
+	u16 trans_id_len, version_len, wrapped_data_len, i_nonce_len,
+		r_nonce_len, reconfig_flags_len;
+	const u8 *addr[2];
+	size_t len[2];
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	struct json_token *root = NULL, *token;
+	int res = -1;
+
+	if (!auth->reconfig || auth->configurator)
+		goto fail;
+
+	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");
+		goto fail;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
+		    wrapped_data, wrapped_data_len);
+
+	addr[0] = hdr;
+	len[0] = DPP_HDR_LEN;
+	addr[1] = attr_start;
+	len[1] = 0;
+	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->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;
+	}
+
+	trans_id = dpp_get_attr(unwrapped, unwrapped_len,
+				DPP_ATTR_TRANSACTION_ID, &trans_id_len);
+	if (!trans_id || trans_id_len != 1 ||
+	    trans_id[0] != auth->transaction_id) {
+		dpp_auth_fail(auth,
+			      "Peer did not include valid Transaction ID");
+		goto fail;
+	}
+
+	version = dpp_get_attr(unwrapped, unwrapped_len,
+			       DPP_ATTR_PROTOCOL_VERSION, &version_len);
+	if (!version || version_len < 1 || version[0] != DPP_VERSION) {
+		dpp_auth_fail(auth,
+			      "Missing or invalid Protocol Version attribute");
+		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 ||
+	    os_memcmp(i_nonce, auth->i_nonce, i_nonce_len) != 0) {
+		dpp_auth_fail(auth, "Missing or invalid I-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+
+	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 ||
+	    os_memcmp(r_nonce, auth->r_nonce, r_nonce_len) != 0) {
+		dpp_auth_fail(auth, "Missing or invalid R-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
+
+	reconfig_flags = dpp_get_attr(unwrapped, unwrapped_len,
+				      DPP_ATTR_RECONFIG_FLAGS,
+				      &reconfig_flags_len);
+	if (!reconfig_flags) {
+		dpp_auth_fail(auth, "Missing or invalid Reconfig-Flags");
+		goto fail;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "DPP: Reconfig-Flags",
+			  reconfig_flags, reconfig_flags_len);
+	root = json_parse((const char *) reconfig_flags, reconfig_flags_len);
+	if (!root) {
+		dpp_auth_fail(auth, "Could not parse Reconfig-Flags");
+		goto fail;
+	}
+	token = json_get_member(root, "connectorKey");
+	if (!token || token->type != JSON_NUMBER) {
+		dpp_auth_fail(auth, "No connectorKey in Reconfig-Flags");
+		goto fail;
+	}
+	if (token->number != DPP_CONFIG_REUSEKEY &&
+	    token->number != DPP_CONFIG_REPLACEKEY) {
+		dpp_auth_fail(auth,
+			      "Unsupported connectorKey value in Reconfig-Flags");
+		goto fail;
+	}
+	auth->reconfig_connector_key = token->number;
+
+	auth->reconfig_success = true;
+	res = 0;
+fail:
+	json_free(root);
+	bin_clear_free(unwrapped, unwrapped_len);
+	return res;
+}
+
+#endif /* CONFIG_DPP2 */
diff --git a/src/common/dpp_tcp.c b/src/common/dpp_tcp.c
new file mode 100644
index 0000000..fc53b8a
--- /dev/null
+++ b/src/common/dpp_tcp.c
@@ -0,0 +1,1512 @@
+/*
+ * DPP over TCP
+ * Copyright (c) 2019-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 <fcntl.h>
+
+#include "utils/common.h"
+#include "utils/ip_addr.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+#ifdef CONFIG_DPP2
+
+struct dpp_connection {
+	struct dl_list list;
+	struct dpp_controller *ctrl;
+	struct dpp_relay_controller *relay;
+	struct dpp_global *global;
+	struct dpp_authentication *auth;
+	int sock;
+	u8 mac_addr[ETH_ALEN];
+	unsigned int freq;
+	u8 msg_len[4];
+	size_t msg_len_octets;
+	struct wpabuf *msg;
+	struct wpabuf *msg_out;
+	size_t msg_out_pos;
+	unsigned int read_eloop:1;
+	unsigned int write_eloop:1;
+	unsigned int on_tcp_tx_complete_gas_done:1;
+	unsigned int on_tcp_tx_complete_remove:1;
+	unsigned int on_tcp_tx_complete_auth_ok:1;
+};
+
+/* Remote Controller */
+struct dpp_relay_controller {
+	struct dl_list list;
+	struct dpp_global *global;
+	u8 pkhash[SHA256_MAC_LEN];
+	struct hostapd_ip_addr ipaddr;
+	void *cb_ctx;
+	void (*tx)(void *ctx, const u8 *addr, unsigned int freq, const u8 *msg,
+		   size_t len);
+	void (*gas_resp_tx)(void *ctx, const u8 *addr, u8 dialog_token,
+			    int prot, struct wpabuf *buf);
+	struct dl_list conn; /* struct dpp_connection */
+};
+
+/* Local Controller */
+struct dpp_controller {
+	struct dpp_global *global;
+	u8 allowed_roles;
+	int qr_mutual;
+	int sock;
+	struct dl_list conn; /* struct dpp_connection */
+	char *configurator_params;
+};
+
+static void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx);
+static void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx);
+static void dpp_controller_auth_success(struct dpp_connection *conn,
+					int initiator);
+
+
+static void dpp_connection_free(struct dpp_connection *conn)
+{
+	if (conn->sock >= 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Close Controller socket %d",
+			   conn->sock);
+		eloop_unregister_sock(conn->sock, EVENT_TYPE_READ);
+		eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
+		close(conn->sock);
+	}
+	eloop_cancel_timeout(dpp_controller_conn_status_result_wait_timeout,
+			     conn, NULL);
+	wpabuf_free(conn->msg);
+	wpabuf_free(conn->msg_out);
+	dpp_auth_deinit(conn->auth);
+	os_free(conn);
+}
+
+
+static void dpp_connection_remove(struct dpp_connection *conn)
+{
+	dl_list_del(&conn->list);
+	dpp_connection_free(conn);
+}
+
+
+int dpp_relay_add_controller(struct dpp_global *dpp,
+			     struct dpp_relay_config *config)
+{
+	struct dpp_relay_controller *ctrl;
+
+	if (!dpp)
+		return -1;
+
+	ctrl = os_zalloc(sizeof(*ctrl));
+	if (!ctrl)
+		return -1;
+	dl_list_init(&ctrl->conn);
+	ctrl->global = dpp;
+	os_memcpy(&ctrl->ipaddr, config->ipaddr, sizeof(*config->ipaddr));
+	os_memcpy(ctrl->pkhash, config->pkhash, SHA256_MAC_LEN);
+	ctrl->cb_ctx = config->cb_ctx;
+	ctrl->tx = config->tx;
+	ctrl->gas_resp_tx = config->gas_resp_tx;
+	dl_list_add(&dpp->controllers, &ctrl->list);
+	return 0;
+}
+
+
+static struct dpp_relay_controller *
+dpp_relay_controller_get(struct dpp_global *dpp, const u8 *pkhash)
+{
+	struct dpp_relay_controller *ctrl;
+
+	if (!dpp)
+		return NULL;
+
+	dl_list_for_each(ctrl, &dpp->controllers, struct dpp_relay_controller,
+			 list) {
+		if (os_memcmp(pkhash, ctrl->pkhash, SHA256_MAC_LEN) == 0)
+			return ctrl;
+	}
+
+	return NULL;
+}
+
+
+static void dpp_controller_gas_done(struct dpp_connection *conn)
+{
+	struct dpp_authentication *auth = conn->auth;
+	void *msg_ctx;
+
+	if (auth->peer_version >= 2 &&
+	    auth->conf_resp_status == DPP_STATUS_OK) {
+		wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result");
+		auth->waiting_conf_result = 1;
+		return;
+	}
+
+	if (conn->ctrl)
+		msg_ctx = conn->ctrl->global->msg_ctx;
+	else
+		msg_ctx = auth->msg_ctx;
+	wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+	dpp_connection_remove(conn);
+}
+
+
+static int dpp_tcp_send(struct dpp_connection *conn)
+{
+	int res;
+
+	if (!conn->msg_out) {
+		eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
+		conn->write_eloop = 0;
+		return -1;
+	}
+	res = send(conn->sock,
+		   wpabuf_head_u8(conn->msg_out) + conn->msg_out_pos,
+		   wpabuf_len(conn->msg_out) - conn->msg_out_pos, 0);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to send buffer: %s",
+			   strerror(errno));
+		dpp_connection_remove(conn);
+		return -1;
+	}
+
+	conn->msg_out_pos += res;
+	if (wpabuf_len(conn->msg_out) > conn->msg_out_pos) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: %u/%u bytes of message sent to Controller",
+			   (unsigned int) conn->msg_out_pos,
+			   (unsigned int) wpabuf_len(conn->msg_out));
+		if (!conn->write_eloop &&
+		    eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
+					dpp_conn_tx_ready, conn, NULL) == 0)
+			conn->write_eloop = 1;
+		return 1;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Full message sent over TCP");
+	wpabuf_free(conn->msg_out);
+	conn->msg_out = NULL;
+	conn->msg_out_pos = 0;
+	eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
+	conn->write_eloop = 0;
+	if (!conn->read_eloop &&
+	    eloop_register_sock(conn->sock, EVENT_TYPE_READ,
+				dpp_controller_rx, conn, NULL) == 0)
+		conn->read_eloop = 1;
+	if (conn->on_tcp_tx_complete_remove) {
+		dpp_connection_remove(conn);
+	} else if (conn->auth && (conn->ctrl || conn->auth->configurator) &&
+		   conn->on_tcp_tx_complete_gas_done) {
+		dpp_controller_gas_done(conn);
+	} else if (conn->on_tcp_tx_complete_auth_ok) {
+		conn->on_tcp_tx_complete_auth_ok = 0;
+		dpp_controller_auth_success(conn, 1);
+	}
+
+	return 0;
+}
+
+
+static int dpp_tcp_send_msg(struct dpp_connection *conn,
+			    const struct wpabuf *msg)
+{
+	wpabuf_free(conn->msg_out);
+	conn->msg_out_pos = 0;
+	conn->msg_out = wpabuf_alloc(4 + wpabuf_len(msg) - 1);
+	if (!conn->msg_out)
+		return -1;
+	wpabuf_put_be32(conn->msg_out, wpabuf_len(msg) - 1);
+	wpabuf_put_data(conn->msg_out, wpabuf_head_u8(msg) + 1,
+			wpabuf_len(msg) - 1);
+
+	if (dpp_tcp_send(conn) == 1) {
+		if (!conn->write_eloop) {
+			if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
+						dpp_conn_tx_ready,
+						conn, NULL) < 0)
+				return -1;
+			conn->write_eloop = 1;
+		}
+	}
+
+	return 0;
+}
+
+
+static void dpp_controller_start_gas_client(struct dpp_connection *conn)
+{
+	struct dpp_authentication *auth = conn->auth;
+	struct wpabuf *buf;
+	int netrole_ap = 0; /* TODO: make this configurable */
+
+	buf = dpp_build_conf_req_helper(auth, "Test", netrole_ap, NULL, NULL);
+	if (!buf) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No configuration request data available");
+		return;
+	}
+
+	dpp_tcp_send_msg(conn, buf);
+	wpabuf_free(buf);
+}
+
+
+static void dpp_controller_auth_success(struct dpp_connection *conn,
+					int initiator)
+{
+	struct dpp_authentication *auth = conn->auth;
+
+	if (!auth)
+		return;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
+	wpa_msg(conn->global->msg_ctx, MSG_INFO,
+		DPP_EVENT_AUTH_SUCCESS "init=%d", initiator);
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - stop at Authentication Confirm");
+		if (auth->configurator) {
+			/* Prevent GAS response */
+			auth->auth_success = 0;
+		}
+		return;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (!auth->configurator)
+		dpp_controller_start_gas_client(conn);
+}
+
+
+static void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct dpp_connection *conn = eloop_ctx;
+
+	wpa_printf(MSG_DEBUG, "DPP: TCP socket %d ready for TX", sock);
+	dpp_tcp_send(conn);
+}
+
+
+static int dpp_ipaddr_to_sockaddr(struct sockaddr *addr, socklen_t *addrlen,
+				  const struct hostapd_ip_addr *ipaddr,
+				  int port)
+{
+	struct sockaddr_in *dst;
+#ifdef CONFIG_IPV6
+	struct sockaddr_in6 *dst6;
+#endif /* CONFIG_IPV6 */
+
+	switch (ipaddr->af) {
+	case AF_INET:
+		dst = (struct sockaddr_in *) addr;
+		os_memset(dst, 0, sizeof(*dst));
+		dst->sin_family = AF_INET;
+		dst->sin_addr.s_addr = ipaddr->u.v4.s_addr;
+		dst->sin_port = htons(port);
+		*addrlen = sizeof(*dst);
+		break;
+#ifdef CONFIG_IPV6
+	case AF_INET6:
+		dst6 = (struct sockaddr_in6 *) addr;
+		os_memset(dst6, 0, sizeof(*dst6));
+		dst6->sin6_family = AF_INET6;
+		os_memcpy(&dst6->sin6_addr, &ipaddr->u.v6,
+			  sizeof(struct in6_addr));
+		dst6->sin6_port = htons(port);
+		*addrlen = sizeof(*dst6);
+		break;
+#endif /* CONFIG_IPV6 */
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static struct dpp_connection *
+dpp_relay_new_conn(struct dpp_relay_controller *ctrl, const u8 *src,
+		   unsigned int freq)
+{
+	struct dpp_connection *conn;
+	struct sockaddr_storage addr;
+	socklen_t addrlen;
+	char txt[100];
+
+	if (dl_list_len(&ctrl->conn) >= 15) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Too many ongoing Relay connections to the Controller - cannot start a new one");
+		return NULL;
+	}
+
+	if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &addr, &addrlen,
+				   &ctrl->ipaddr, DPP_TCP_PORT) < 0)
+		return NULL;
+
+	conn = os_zalloc(sizeof(*conn));
+	if (!conn)
+		return NULL;
+
+	conn->global = ctrl->global;
+	conn->relay = ctrl;
+	os_memcpy(conn->mac_addr, src, ETH_ALEN);
+	conn->freq = freq;
+
+	conn->sock = socket(AF_INET, SOCK_STREAM, 0);
+	if (conn->sock < 0)
+		goto fail;
+	wpa_printf(MSG_DEBUG, "DPP: TCP relay socket %d connection to %s",
+		   conn->sock, hostapd_ip_txt(&ctrl->ipaddr, txt, sizeof(txt)));
+
+	if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
+			   strerror(errno));
+		goto fail;
+	}
+
+	if (connect(conn->sock, (struct sockaddr *) &addr, addrlen) < 0) {
+		if (errno != EINPROGRESS) {
+			wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s",
+				   strerror(errno));
+			goto fail;
+		}
+
+		/*
+		 * Continue connecting in the background; eloop will call us
+		 * once the connection is ready (or failed).
+		 */
+	}
+
+	if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
+				dpp_conn_tx_ready, conn, NULL) < 0)
+		goto fail;
+	conn->write_eloop = 1;
+
+	/* TODO: eloop timeout to clear a connection if it does not complete
+	 * properly */
+
+	dl_list_add(&ctrl->conn, &conn->list);
+	return conn;
+fail:
+	dpp_connection_free(conn);
+	return NULL;
+}
+
+
+static struct wpabuf * dpp_tcp_encaps(const u8 *hdr, const u8 *buf, size_t len)
+{
+	struct wpabuf *msg;
+
+	msg = wpabuf_alloc(4 + 1 + DPP_HDR_LEN + len);
+	if (!msg)
+		return NULL;
+	wpabuf_put_be32(msg, 1 + DPP_HDR_LEN + len);
+	wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC);
+	wpabuf_put_data(msg, hdr, DPP_HDR_LEN);
+	wpabuf_put_data(msg, buf, len);
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg);
+	return msg;
+}
+
+
+static int dpp_relay_tx(struct dpp_connection *conn, const u8 *hdr,
+			const u8 *buf, size_t len)
+{
+	u8 type = hdr[DPP_HDR_LEN - 1];
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Continue already established Relay/Controller connection for this session");
+	wpabuf_free(conn->msg_out);
+	conn->msg_out_pos = 0;
+	conn->msg_out = dpp_tcp_encaps(hdr, buf, len);
+	if (!conn->msg_out) {
+		dpp_connection_remove(conn);
+		return -1;
+	}
+
+	/* TODO: for proto ver 1, need to do remove connection based on GAS Resp
+	 * TX status */
+	if (type == DPP_PA_CONFIGURATION_RESULT)
+		conn->on_tcp_tx_complete_remove = 1;
+	dpp_tcp_send(conn);
+	return 0;
+}
+
+
+int dpp_relay_rx_action(struct dpp_global *dpp, const u8 *src, const u8 *hdr,
+			const u8 *buf, size_t len, unsigned int freq,
+			const u8 *i_bootstrap, const u8 *r_bootstrap)
+{
+	struct dpp_relay_controller *ctrl;
+	struct dpp_connection *conn;
+	u8 type = hdr[DPP_HDR_LEN - 1];
+
+	/* Check if there is an already started session for this peer and if so,
+	 * continue that session (send this over TCP) and return 0.
+	 */
+	if (type != DPP_PA_PEER_DISCOVERY_REQ &&
+	    type != DPP_PA_PEER_DISCOVERY_RESP &&
+	    type != DPP_PA_PRESENCE_ANNOUNCEMENT &&
+	    type != DPP_PA_RECONFIG_ANNOUNCEMENT) {
+		dl_list_for_each(ctrl, &dpp->controllers,
+				 struct dpp_relay_controller, list) {
+			dl_list_for_each(conn, &ctrl->conn,
+					 struct dpp_connection, list) {
+				if (os_memcmp(src, conn->mac_addr,
+					      ETH_ALEN) == 0)
+					return dpp_relay_tx(conn, hdr, buf, len);
+			}
+		}
+	}
+
+	if (type == DPP_PA_PRESENCE_ANNOUNCEMENT ||
+	    type == DPP_PA_RECONFIG_ANNOUNCEMENT) {
+		/* TODO: Could send this to all configured Controllers. For now,
+		 * only the first Controller is supported. */
+		ctrl = dl_list_first(&dpp->controllers,
+				     struct dpp_relay_controller, list);
+	} else {
+		if (!r_bootstrap)
+			return -1;
+		ctrl = dpp_relay_controller_get(dpp, r_bootstrap);
+	}
+	if (!ctrl)
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Authentication Request for a configured Controller");
+	conn = dpp_relay_new_conn(ctrl, src, freq);
+	if (!conn)
+		return -1;
+
+	conn->msg_out = dpp_tcp_encaps(hdr, buf, len);
+	if (!conn->msg_out) {
+		dpp_connection_remove(conn);
+		return -1;
+	}
+	/* Message will be sent in dpp_conn_tx_ready() */
+
+	return 0;
+}
+
+
+int dpp_relay_rx_gas_req(struct dpp_global *dpp, const u8 *src, const u8 *data,
+			 size_t data_len)
+{
+	struct dpp_relay_controller *ctrl;
+	struct dpp_connection *conn, *found = NULL;
+	struct wpabuf *msg;
+
+	/* Check if there is a successfully completed authentication for this
+	 * and if so, continue that session (send this over TCP) and return 0.
+	 */
+	dl_list_for_each(ctrl, &dpp->controllers,
+			 struct dpp_relay_controller, list) {
+		if (found)
+			break;
+		dl_list_for_each(conn, &ctrl->conn,
+				 struct dpp_connection, list) {
+			if (os_memcmp(src, conn->mac_addr,
+				      ETH_ALEN) == 0) {
+				found = conn;
+				break;
+			}
+		}
+	}
+
+	if (!found)
+		return -1;
+
+	msg = wpabuf_alloc(4 + 1 + data_len);
+	if (!msg)
+		return -1;
+	wpabuf_put_be32(msg, 1 + data_len);
+	wpabuf_put_u8(msg, WLAN_PA_GAS_INITIAL_REQ);
+	wpabuf_put_data(msg, data, data_len);
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg);
+
+	wpabuf_free(conn->msg_out);
+	conn->msg_out_pos = 0;
+	conn->msg_out = msg;
+	dpp_tcp_send(conn);
+	return 0;
+}
+
+
+static void dpp_controller_free(struct dpp_controller *ctrl)
+{
+	struct dpp_connection *conn, *tmp;
+
+	if (!ctrl)
+		return;
+
+	dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection,
+			      list)
+		dpp_connection_remove(conn);
+
+	if (ctrl->sock >= 0) {
+		close(ctrl->sock);
+		eloop_unregister_sock(ctrl->sock, EVENT_TYPE_READ);
+	}
+	os_free(ctrl->configurator_params);
+	os_free(ctrl);
+}
+
+
+static int dpp_controller_rx_auth_req(struct dpp_connection *conn,
+				      const u8 *hdr, const u8 *buf, size_t len)
+{
+	const u8 *r_bootstrap, *i_bootstrap;
+	u16 r_bootstrap_len, i_bootstrap_len;
+	struct dpp_bootstrap_info *own_bi = NULL, *peer_bi = NULL;
+
+	if (!conn->ctrl)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication Request");
+
+	r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+				   &r_bootstrap_len);
+	if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_INFO,
+			   "Missing or invalid required Responder Bootstrapping Key Hash attribute");
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+		    r_bootstrap, r_bootstrap_len);
+
+	i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+				   &i_bootstrap_len);
+	if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_INFO,
+			   "Missing or invalid required Initiator Bootstrapping Key Hash attribute");
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash",
+		    i_bootstrap, i_bootstrap_len);
+
+	/* Try to find own and peer bootstrapping key matches based on the
+	 * received hash values */
+	dpp_bootstrap_find_pair(conn->ctrl->global, i_bootstrap, r_bootstrap,
+				&own_bi, &peer_bi);
+	if (!own_bi) {
+		wpa_printf(MSG_INFO,
+			"No matching own bootstrapping key found - ignore message");
+		return -1;
+	}
+
+	if (conn->auth) {
+		wpa_printf(MSG_INFO,
+			   "Already in DPP authentication exchange - ignore new one");
+		return 0;
+	}
+
+	conn->auth = dpp_auth_req_rx(conn->ctrl->global,
+				     conn->ctrl->global->msg_ctx,
+				     conn->ctrl->allowed_roles,
+				     conn->ctrl->qr_mutual,
+				     peer_bi, own_bi, -1, hdr, buf, len);
+	if (!conn->auth) {
+		wpa_printf(MSG_DEBUG, "DPP: No response generated");
+		return -1;
+	}
+
+	if (dpp_set_configurator(conn->auth,
+				 conn->ctrl->configurator_params) < 0) {
+		dpp_connection_remove(conn);
+		return -1;
+	}
+
+	return dpp_tcp_send_msg(conn, conn->auth->resp_msg);
+}
+
+
+static int dpp_controller_rx_auth_resp(struct dpp_connection *conn,
+				       const u8 *hdr, const u8 *buf, size_t len)
+{
+	struct dpp_authentication *auth = conn->auth;
+	struct wpabuf *msg;
+	int res;
+
+	if (!auth)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication Response");
+
+	msg = dpp_auth_resp_rx(auth, hdr, buf, len);
+	if (!msg) {
+		if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Start wait for full response");
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "DPP: No confirm generated");
+		dpp_connection_remove(conn);
+		return -1;
+	}
+
+	conn->on_tcp_tx_complete_auth_ok = 1;
+	res = dpp_tcp_send_msg(conn, msg);
+	wpabuf_free(msg);
+	return res;
+}
+
+
+static int dpp_controller_rx_auth_conf(struct dpp_connection *conn,
+				       const u8 *hdr, const u8 *buf, size_t len)
+{
+	struct dpp_authentication *auth = conn->auth;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation");
+
+	if (!auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Authentication in progress - drop");
+		return -1;
+	}
+
+	if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
+		return -1;
+	}
+
+	dpp_controller_auth_success(conn, 0);
+	return 0;
+}
+
+
+void dpp_controller_conn_status_result_wait_timeout(void *eloop_ctx,
+						    void *timeout_ctx)
+{
+	struct dpp_connection *conn = eloop_ctx;
+
+	if (!conn->auth->waiting_conf_result)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Timeout while waiting for Connection Status Result");
+	wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
+		DPP_EVENT_CONN_STATUS_RESULT "timeout");
+	dpp_connection_remove(conn);
+}
+
+
+static int dpp_controller_rx_conf_result(struct dpp_connection *conn,
+					 const u8 *hdr, const u8 *buf,
+					 size_t len)
+{
+	struct dpp_authentication *auth = conn->auth;
+	enum dpp_status_error status;
+	void *msg_ctx;
+
+	if (!conn->ctrl && (!auth || !auth->configurator))
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "DPP: Configuration Result");
+
+	if (!auth || !auth->waiting_conf_result) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Configuration waiting for result - drop");
+		return -1;
+	}
+	if (conn->ctrl)
+		msg_ctx = conn->ctrl->global->msg_ctx;
+	else
+		msg_ctx = auth->msg_ctx;
+
+	status = dpp_conf_result_rx(auth, hdr, buf, len);
+	if (status == DPP_STATUS_OK && auth->send_conn_status) {
+		wpa_msg(msg_ctx, MSG_INFO,
+			DPP_EVENT_CONF_SENT "wait_conn_status=1");
+		wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result");
+		eloop_cancel_timeout(
+			dpp_controller_conn_status_result_wait_timeout,
+			conn, NULL);
+		eloop_register_timeout(
+			16, 0, dpp_controller_conn_status_result_wait_timeout,
+			conn, NULL);
+		return 0;
+	}
+	if (status == DPP_STATUS_OK)
+		wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+	else
+		wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+	return -1; /* to remove the completed connection */
+}
+
+
+static int dpp_controller_rx_conn_status_result(struct dpp_connection *conn,
+						const u8 *hdr, const u8 *buf,
+						size_t len)
+{
+	struct dpp_authentication *auth = conn->auth;
+	enum dpp_status_error status;
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len = 0;
+	char *channel_list = NULL;
+
+	if (!conn->ctrl)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "DPP: Connection Status Result");
+
+	if (!auth || !auth->waiting_conn_status_result) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Configuration waiting for connection status result - drop");
+		return -1;
+	}
+
+	status = dpp_conn_status_result_rx(auth, hdr, buf, len,
+					   ssid, &ssid_len, &channel_list);
+	wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
+		DPP_EVENT_CONN_STATUS_RESULT
+		"result=%d ssid=%s channel_list=%s",
+		status, wpa_ssid_txt(ssid, ssid_len),
+		channel_list ? channel_list : "N/A");
+	os_free(channel_list);
+	return -1; /* to remove the completed connection */
+}
+
+
+static int dpp_controller_rx_presence_announcement(struct dpp_connection *conn,
+						   const u8 *hdr, const u8 *buf,
+						   size_t len)
+{
+	const u8 *r_bootstrap;
+	u16 r_bootstrap_len;
+	struct dpp_bootstrap_info *peer_bi;
+	struct dpp_authentication *auth;
+	struct dpp_global *dpp = conn->ctrl->global;
+
+	if (conn->auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore Presence Announcement during ongoing Authentication");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Presence Announcement");
+
+	r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+				   &r_bootstrap_len);
+	if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_msg(dpp->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+			"Missing or invalid required Responder Bootstrapping Key Hash attribute");
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+		    r_bootstrap, r_bootstrap_len);
+	peer_bi = dpp_bootstrap_find_chirp(dpp, r_bootstrap);
+	if (!peer_bi) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No matching bootstrapping information found");
+		return -1;
+	}
+
+	auth = dpp_auth_init(dpp, dpp->msg_ctx, peer_bi, NULL,
+			     DPP_CAPAB_CONFIGURATOR, -1, NULL, 0);
+	if (!auth)
+		return -1;
+	if (dpp_set_configurator(auth, conn->ctrl->configurator_params) < 0) {
+		dpp_auth_deinit(auth);
+		dpp_connection_remove(conn);
+		return -1;
+	}
+
+	conn->auth = auth;
+	return dpp_tcp_send_msg(conn, conn->auth->req_msg);
+}
+
+
+static int dpp_controller_rx_reconfig_announcement(struct dpp_connection *conn,
+						   const u8 *hdr, const u8 *buf,
+						   size_t len)
+{
+	const u8 *csign_hash;
+	u16 csign_hash_len;
+	struct dpp_configurator *conf;
+	struct dpp_global *dpp = conn->ctrl->global;
+	struct dpp_authentication *auth;
+
+	if (conn->auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore Reconfig Announcement during ongoing Authentication");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Reconfig Announcement");
+
+	csign_hash = dpp_get_attr(buf, len, DPP_ATTR_C_SIGN_KEY_HASH,
+				  &csign_hash_len);
+	if (!csign_hash || csign_hash_len != SHA256_MAC_LEN) {
+		wpa_msg(dpp->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+			"Missing or invalid required Configurator C-sign key Hash attribute");
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Configurator C-sign key Hash (kid)",
+		    csign_hash, csign_hash_len);
+	conf = dpp_configurator_find_kid(dpp, csign_hash);
+	if (!conf) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No matching Configurator information found");
+		return -1;
+	}
+
+	auth = dpp_reconfig_init(dpp, dpp->msg_ctx, conf, 0);
+	if (!auth)
+		return -1;
+	if (dpp_set_configurator(auth, conn->ctrl->configurator_params) < 0) {
+		dpp_auth_deinit(auth);
+		return -1;
+	}
+
+	conn->auth = auth;
+	return dpp_tcp_send_msg(conn, auth->reconfig_req_msg);
+}
+
+
+static int dpp_controller_rx_reconfig_auth_resp(struct dpp_connection *conn,
+						const u8 *hdr, const u8 *buf,
+						size_t len)
+{
+	struct dpp_authentication *auth = conn->auth;
+	struct wpabuf *conf;
+	int res;
+
+	wpa_printf(MSG_DEBUG, "DPP: Reconfig Authentication Response");
+
+	if (!auth || !auth->reconfig || !auth->configurator) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Reconfig Authentication in progress - drop");
+		return -1;
+	}
+
+	conf = dpp_reconfig_auth_resp_rx(auth, hdr, buf, len);
+	if (!conf)
+		return -1;
+
+	res = dpp_tcp_send_msg(conn, conf);
+	wpabuf_free(conf);
+	return res;
+}
+
+
+static int dpp_controller_rx_action(struct dpp_connection *conn, const u8 *msg,
+				    size_t len)
+{
+	const u8 *pos, *end;
+	u8 type;
+
+	wpa_printf(MSG_DEBUG, "DPP: Received DPP Action frame over TCP");
+	pos = msg;
+	end = msg + len;
+
+	if (end - pos < DPP_HDR_LEN ||
+	    WPA_GET_BE24(pos) != OUI_WFA ||
+	    pos[3] != DPP_OUI_TYPE) {
+		wpa_printf(MSG_DEBUG, "DPP: Unrecognized header");
+		return -1;
+	}
+
+	if (pos[4] != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: Unsupported Crypto Suite %u",
+			   pos[4]);
+		return -1;
+	}
+	type = pos[5];
+	wpa_printf(MSG_DEBUG, "DPP: Received message type %u", type);
+	pos += DPP_HDR_LEN;
+
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes",
+		    pos, end - pos);
+	if (dpp_check_attrs(pos, end - pos) < 0)
+		return -1;
+
+	if (conn->relay) {
+		wpa_printf(MSG_DEBUG, "DPP: Relay - send over WLAN");
+		conn->relay->tx(conn->relay->cb_ctx, conn->mac_addr,
+				conn->freq, msg, len);
+		return 0;
+	}
+
+	switch (type) {
+	case DPP_PA_AUTHENTICATION_REQ:
+		return dpp_controller_rx_auth_req(conn, msg, pos, end - pos);
+	case DPP_PA_AUTHENTICATION_RESP:
+		return dpp_controller_rx_auth_resp(conn, msg, pos, end - pos);
+	case DPP_PA_AUTHENTICATION_CONF:
+		return dpp_controller_rx_auth_conf(conn, msg, pos, end - pos);
+	case DPP_PA_CONFIGURATION_RESULT:
+		return dpp_controller_rx_conf_result(conn, msg, pos, end - pos);
+	case DPP_PA_CONNECTION_STATUS_RESULT:
+		return dpp_controller_rx_conn_status_result(conn, msg, pos,
+							    end - pos);
+	case DPP_PA_PRESENCE_ANNOUNCEMENT:
+		return dpp_controller_rx_presence_announcement(conn, msg, pos,
+							       end - pos);
+	case DPP_PA_RECONFIG_ANNOUNCEMENT:
+		return dpp_controller_rx_reconfig_announcement(conn, msg, pos,
+							       end - pos);
+	case DPP_PA_RECONFIG_AUTH_RESP:
+		return dpp_controller_rx_reconfig_auth_resp(conn, msg, pos,
+							    end - pos);
+	default:
+		/* TODO: missing messages types */
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unsupported frame subtype %d", type);
+		return -1;
+	}
+}
+
+
+static int dpp_controller_rx_gas_req(struct dpp_connection *conn, const u8 *msg,
+				     size_t len)
+{
+	const u8 *pos, *end, *next;
+	u8 dialog_token;
+	const u8 *adv_proto;
+	u16 slen;
+	struct wpabuf *resp, *buf;
+	struct dpp_authentication *auth = conn->auth;
+
+	if (len < 1 + 2)
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Received DPP Configuration Request over TCP");
+
+	if (!auth || (!conn->ctrl && !auth->configurator) ||
+	    (!auth->auth_success && !auth->reconfig_success)) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+		return -1;
+	}
+
+	pos = msg;
+	end = msg + len;
+
+	dialog_token = *pos++;
+	adv_proto = pos++;
+	slen = *pos++;
+	if (*adv_proto != WLAN_EID_ADV_PROTO ||
+	    slen > end - pos || slen < 2)
+		return -1;
+
+	next = pos + slen;
+	pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+	if (slen != 8 || *pos != WLAN_EID_VENDOR_SPECIFIC ||
+	    pos[1] != 5 || WPA_GET_BE24(&pos[2]) != OUI_WFA ||
+	    pos[5] != DPP_OUI_TYPE || pos[6] != 0x01)
+		return -1;
+
+	pos = next;
+	/* Query Request */
+	if (end - pos < 2)
+		return -1;
+	slen = WPA_GET_LE16(pos);
+	pos += 2;
+	if (slen > end - pos)
+		return -1;
+
+	resp = dpp_conf_req_rx(auth, pos, slen);
+	if (!resp)
+		return -1;
+
+	buf = wpabuf_alloc(4 + 18 + wpabuf_len(resp));
+	if (!buf) {
+		wpabuf_free(resp);
+		return -1;
+	}
+
+	wpabuf_put_be32(buf, 18 + wpabuf_len(resp));
+
+	wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_RESP);
+	wpabuf_put_u8(buf, dialog_token);
+	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+	wpabuf_put_le16(buf, 0); /* GAS Comeback Delay */
+
+	dpp_write_adv_proto(buf);
+	dpp_write_gas_query(buf, resp);
+	wpabuf_free(resp);
+
+	/* Send Config Response over TCP; GAS fragmentation is taken care of by
+	 * the Relay */
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", buf);
+	wpabuf_free(conn->msg_out);
+	conn->msg_out_pos = 0;
+	conn->msg_out = buf;
+	conn->on_tcp_tx_complete_gas_done = 1;
+	dpp_tcp_send(conn);
+	return 0;
+}
+
+
+static int dpp_tcp_rx_gas_resp(struct dpp_connection *conn, struct wpabuf *resp)
+{
+	struct dpp_authentication *auth = conn->auth;
+	int res;
+	struct wpabuf *msg;
+	enum dpp_status_error status;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Configuration Response for local stack from TCP");
+
+	if (auth)
+		res = dpp_conf_resp_rx(auth, resp);
+	else
+		res = -1;
+	wpabuf_free(resp);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
+		return -1;
+	}
+
+	if (conn->global->process_conf_obj)
+		res = conn->global->process_conf_obj(conn->global->cb_ctx,
+						     auth);
+	else
+		res = 0;
+
+	if (auth->peer_version < 2 || auth->conf_resp_status != DPP_STATUS_OK)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result");
+	status = res < 0 ? DPP_STATUS_CONFIG_REJECTED : DPP_STATUS_OK;
+	msg = dpp_build_conf_result(auth, status);
+	if (!msg)
+		return -1;
+
+	conn->on_tcp_tx_complete_remove = 1;
+	res = dpp_tcp_send_msg(conn, msg);
+	wpabuf_free(msg);
+
+	/* This exchange will be terminated in the TX status handler */
+
+	return res;
+}
+
+
+static int dpp_rx_gas_resp(struct dpp_connection *conn, const u8 *msg,
+			   size_t len)
+{
+	struct wpabuf *buf;
+	u8 dialog_token;
+	const u8 *pos, *end, *next, *adv_proto;
+	u16 status, slen;
+
+	if (len < 5 + 2)
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Received DPP Configuration Response over TCP");
+
+	pos = msg;
+	end = msg + len;
+
+	dialog_token = *pos++;
+	status = WPA_GET_LE16(pos);
+	if (status != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "DPP: Unexpected Status Code %u", status);
+		return -1;
+	}
+	pos += 2;
+	pos += 2; /* ignore GAS Comeback Delay */
+
+	adv_proto = pos++;
+	slen = *pos++;
+	if (*adv_proto != WLAN_EID_ADV_PROTO ||
+	    slen > end - pos || slen < 2)
+		return -1;
+
+	next = pos + slen;
+	pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+	if (slen != 8 || *pos != WLAN_EID_VENDOR_SPECIFIC ||
+	    pos[1] != 5 || WPA_GET_BE24(&pos[2]) != OUI_WFA ||
+	    pos[5] != DPP_OUI_TYPE || pos[6] != 0x01)
+		return -1;
+
+	pos = next;
+	/* Query Response */
+	if (end - pos < 2)
+		return -1;
+	slen = WPA_GET_LE16(pos);
+	pos += 2;
+	if (slen > end - pos)
+		return -1;
+
+	buf = wpabuf_alloc(slen);
+	if (!buf)
+		return -1;
+	wpabuf_put_data(buf, pos, slen);
+
+	if (!conn->relay &&
+	    (!conn->ctrl || (conn->ctrl->allowed_roles & DPP_CAPAB_ENROLLEE)))
+		return dpp_tcp_rx_gas_resp(conn, buf);
+
+	if (!conn->relay) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+		wpabuf_free(buf);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Relay - send over WLAN");
+	conn->relay->gas_resp_tx(conn->relay->cb_ctx, conn->mac_addr,
+				 dialog_token, 0, buf);
+
+	return 0;
+}
+
+
+static void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx)
+{
+	struct dpp_connection *conn = eloop_ctx;
+	int res;
+	const u8 *pos;
+
+	wpa_printf(MSG_DEBUG, "DPP: TCP data available for reading (sock %d)",
+		   sd);
+
+	if (conn->msg_len_octets < 4) {
+		u32 msglen;
+
+		res = recv(sd, &conn->msg_len[conn->msg_len_octets],
+			   4 - conn->msg_len_octets, 0);
+		if (res < 0) {
+			wpa_printf(MSG_DEBUG, "DPP: recv failed: %s",
+				   strerror(errno));
+			dpp_connection_remove(conn);
+			return;
+		}
+		if (res == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: No more data available over TCP");
+			dpp_connection_remove(conn);
+			return;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Received %d/%d octet(s) of message length field",
+			   res, (int) (4 - conn->msg_len_octets));
+		conn->msg_len_octets += res;
+
+		if (conn->msg_len_octets < 4) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Need %d more octets of message length field",
+				   (int) (4 - conn->msg_len_octets));
+			return;
+		}
+
+		msglen = WPA_GET_BE32(conn->msg_len);
+		wpa_printf(MSG_DEBUG, "DPP: Message length: %u", msglen);
+		if (msglen > 65535) {
+			wpa_printf(MSG_INFO, "DPP: Unexpectedly long message");
+			dpp_connection_remove(conn);
+			return;
+		}
+
+		wpabuf_free(conn->msg);
+		conn->msg = wpabuf_alloc(msglen);
+	}
+
+	if (!conn->msg) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No buffer available for receiving the message");
+		dpp_connection_remove(conn);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Need %u more octets of message payload",
+		   (unsigned int) wpabuf_tailroom(conn->msg));
+
+	res = recv(sd, wpabuf_put(conn->msg, 0), wpabuf_tailroom(conn->msg), 0);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: recv failed: %s", strerror(errno));
+		dpp_connection_remove(conn);
+		return;
+	}
+	if (res == 0) {
+		wpa_printf(MSG_DEBUG, "DPP: No more data available over TCP");
+		dpp_connection_remove(conn);
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Received %d octets", res);
+	wpabuf_put(conn->msg, res);
+
+	if (wpabuf_tailroom(conn->msg) > 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Need %u more octets of message payload",
+			   (unsigned int) wpabuf_tailroom(conn->msg));
+		return;
+	}
+
+	conn->msg_len_octets = 0;
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: Received TCP message", conn->msg);
+	if (wpabuf_len(conn->msg) < 1) {
+		dpp_connection_remove(conn);
+		return;
+	}
+
+	pos = wpabuf_head(conn->msg);
+	switch (*pos) {
+	case WLAN_PA_VENDOR_SPECIFIC:
+		if (dpp_controller_rx_action(conn, pos + 1,
+					     wpabuf_len(conn->msg) - 1) < 0)
+			dpp_connection_remove(conn);
+		break;
+	case WLAN_PA_GAS_INITIAL_REQ:
+		if (dpp_controller_rx_gas_req(conn, pos + 1,
+					      wpabuf_len(conn->msg) - 1) < 0)
+			dpp_connection_remove(conn);
+		break;
+	case WLAN_PA_GAS_INITIAL_RESP:
+		if (dpp_rx_gas_resp(conn, pos + 1,
+				    wpabuf_len(conn->msg) - 1) < 0)
+			dpp_connection_remove(conn);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "DPP: Ignore unsupported message type %u",
+			   *pos);
+		break;
+	}
+}
+
+
+static void dpp_controller_tcp_cb(int sd, void *eloop_ctx, void *sock_ctx)
+{
+	struct dpp_controller *ctrl = eloop_ctx;
+	struct sockaddr_in addr;
+	socklen_t addr_len = sizeof(addr);
+	int fd;
+	struct dpp_connection *conn;
+
+	wpa_printf(MSG_DEBUG, "DPP: New TCP connection");
+
+	fd = accept(ctrl->sock, (struct sockaddr *) &addr, &addr_len);
+	if (fd < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to accept new connection: %s",
+			   strerror(errno));
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Connection from %s:%d",
+		   inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+
+	conn = os_zalloc(sizeof(*conn));
+	if (!conn)
+		goto fail;
+
+	conn->global = ctrl->global;
+	conn->ctrl = ctrl;
+	conn->sock = fd;
+
+	if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
+			   strerror(errno));
+		goto fail;
+	}
+
+	if (eloop_register_sock(conn->sock, EVENT_TYPE_READ,
+				dpp_controller_rx, conn, NULL) < 0)
+		goto fail;
+	conn->read_eloop = 1;
+
+	/* TODO: eloop timeout to expire connections that do not complete in
+	 * reasonable time */
+	dl_list_add(&ctrl->conn, &conn->list);
+	return;
+
+fail:
+	close(fd);
+	os_free(conn);
+}
+
+
+int dpp_tcp_init(struct dpp_global *dpp, struct dpp_authentication *auth,
+		 const struct hostapd_ip_addr *addr, int port)
+{
+	struct dpp_connection *conn;
+	struct sockaddr_storage saddr;
+	socklen_t addrlen;
+	const u8 *hdr, *pos, *end;
+	char txt[100];
+
+	wpa_printf(MSG_DEBUG, "DPP: Initialize TCP connection to %s port %d",
+		   hostapd_ip_txt(addr, txt, sizeof(txt)), port);
+	if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &saddr, &addrlen,
+				   addr, port) < 0) {
+		dpp_auth_deinit(auth);
+		return -1;
+	}
+
+	conn = os_zalloc(sizeof(*conn));
+	if (!conn) {
+		dpp_auth_deinit(auth);
+		return -1;
+	}
+
+	conn->global = dpp;
+	conn->auth = auth;
+	conn->sock = socket(AF_INET, SOCK_STREAM, 0);
+	if (conn->sock < 0)
+		goto fail;
+
+	if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
+			   strerror(errno));
+		goto fail;
+	}
+
+	if (connect(conn->sock, (struct sockaddr *) &saddr, addrlen) < 0) {
+		if (errno != EINPROGRESS) {
+			wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s",
+				   strerror(errno));
+			goto fail;
+		}
+
+		/*
+		 * Continue connecting in the background; eloop will call us
+		 * once the connection is ready (or failed).
+		 */
+	}
+
+	if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
+				dpp_conn_tx_ready, conn, NULL) < 0)
+		goto fail;
+	conn->write_eloop = 1;
+
+	hdr = wpabuf_head(auth->req_msg);
+	end = hdr + wpabuf_len(auth->req_msg);
+	hdr += 2; /* skip Category and Actiom */
+	pos = hdr + DPP_HDR_LEN;
+	conn->msg_out = dpp_tcp_encaps(hdr, pos, end - pos);
+	if (!conn->msg_out)
+		goto fail;
+	/* Message will be sent in dpp_conn_tx_ready() */
+
+	/* TODO: eloop timeout to clear a connection if it does not complete
+	 * properly */
+	dl_list_add(&dpp->tcp_init, &conn->list);
+	return 0;
+fail:
+	dpp_connection_free(conn);
+	return -1;
+}
+
+
+int dpp_controller_start(struct dpp_global *dpp,
+			 struct dpp_controller_config *config)
+{
+	struct dpp_controller *ctrl;
+	int on = 1;
+	struct sockaddr_in sin;
+	int port;
+
+	if (!dpp || dpp->controller)
+		return -1;
+
+	ctrl = os_zalloc(sizeof(*ctrl));
+	if (!ctrl)
+		return -1;
+	ctrl->global = dpp;
+	if (config->configurator_params)
+		ctrl->configurator_params =
+			os_strdup(config->configurator_params);
+	dl_list_init(&ctrl->conn);
+	ctrl->allowed_roles = config->allowed_roles;
+	ctrl->qr_mutual = 0;
+
+	ctrl->sock = socket(AF_INET, SOCK_STREAM, 0);
+	if (ctrl->sock < 0)
+		goto fail;
+
+	if (setsockopt(ctrl->sock, SOL_SOCKET, SO_REUSEADDR,
+		       &on, sizeof(on)) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: setsockopt(SO_REUSEADDR) failed: %s",
+			   strerror(errno));
+		/* try to continue anyway */
+	}
+
+	if (fcntl(ctrl->sock, F_SETFL, O_NONBLOCK) < 0) {
+		wpa_printf(MSG_INFO, "DPP: fnctl(O_NONBLOCK) failed: %s",
+			   strerror(errno));
+		goto fail;
+	}
+
+	/* TODO: IPv6 */
+	os_memset(&sin, 0, sizeof(sin));
+	sin.sin_family = AF_INET;
+	sin.sin_addr.s_addr = INADDR_ANY;
+	port = config->tcp_port ? config->tcp_port : DPP_TCP_PORT;
+	sin.sin_port = htons(port);
+	if (bind(ctrl->sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Failed to bind Controller TCP port: %s",
+			   strerror(errno));
+		goto fail;
+	}
+	if (listen(ctrl->sock, 10 /* max backlog */) < 0 ||
+	    fcntl(ctrl->sock, F_SETFL, O_NONBLOCK) < 0 ||
+	    eloop_register_sock(ctrl->sock, EVENT_TYPE_READ,
+				dpp_controller_tcp_cb, ctrl, NULL))
+		goto fail;
+
+	dpp->controller = ctrl;
+	wpa_printf(MSG_DEBUG, "DPP: Controller started on TCP port %d", port);
+	return 0;
+fail:
+	dpp_controller_free(ctrl);
+	return -1;
+}
+
+
+void dpp_controller_stop(struct dpp_global *dpp)
+{
+	if (dpp) {
+		dpp_controller_free(dpp->controller);
+		dpp->controller = NULL;
+	}
+}
+
+
+void dpp_tcp_init_flush(struct dpp_global *dpp)
+{
+	struct dpp_connection *conn, *tmp;
+
+	dl_list_for_each_safe(conn, tmp, &dpp->tcp_init, struct dpp_connection,
+			      list)
+		dpp_connection_remove(conn);
+}
+
+
+static void dpp_relay_controller_free(struct dpp_relay_controller *ctrl)
+{
+	struct dpp_connection *conn, *tmp;
+
+	dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection,
+			      list)
+		dpp_connection_remove(conn);
+	os_free(ctrl);
+}
+
+
+void dpp_relay_flush_controllers(struct dpp_global *dpp)
+{
+	struct dpp_relay_controller *ctrl, *tmp;
+
+	if (!dpp)
+		return;
+
+	dl_list_for_each_safe(ctrl, tmp, &dpp->controllers,
+			      struct dpp_relay_controller, list) {
+		dl_list_del(&ctrl->list);
+		dpp_relay_controller_free(ctrl);
+	}
+}
+
+#endif /* CONFIG_DPP2 */
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index 71c54ba..92960e2 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -298,6 +298,11 @@
 		elems->short_ssid_list = pos;
 		elems->short_ssid_list_len = elen;
 		break;
+	case WLAN_EID_EXT_HE_6GHZ_BAND_CAP:
+		if (elen < sizeof(struct ieee80211_he_6ghz_band_cap))
+			break;
+		elems->he_6ghz_band_cap = pos;
+		break;
 	default:
 		if (show_errors) {
 			wpa_printf(MSG_MSGDUMP,
@@ -934,9 +939,9 @@
 		return HOSTAPD_MODE_IEEE80211A;
 	}
 
-	if (freq > 5940 && freq <= 7105) {
+	if (freq > 5950 && freq <= 7115) {
 		int bw;
-		u8 idx = (freq - 5940) / 5;
+		u8 idx = (freq - 5950) / 5;
 
 		bw = center_idx_to_bw_6ghz(idx);
 		if (bw < 0)
@@ -947,6 +952,12 @@
 		return HOSTAPD_MODE_IEEE80211A;
 	}
 
+	if (freq == 5935) {
+		*op_class = 136;
+		*channel = (freq - 5925) / 5;
+		return HOSTAPD_MODE_IEEE80211A;
+	}
+
 	/* 56.16 GHz, channel 1..6 */
 	if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 6) {
 		if (sec_channel)
@@ -1323,7 +1334,11 @@
 	case 135: /* UHB channels, 80+80 MHz: 7, 23, 39.. */
 		if (chan < 1 || chan > 233)
 			return -1;
-		return 5940 + chan * 5;
+		return 5950 + chan * 5;
+	case 136: /* UHB channels, 20 MHz: 2 */
+		if (chan == 2)
+			return 5935;
+		return -1;
 	case 180: /* 60 GHz band, channels 1..8 */
 		if (chan < 1 || chan > 8)
 			return -1;
@@ -2111,10 +2126,13 @@
 
 int is_6ghz_freq(int freq)
 {
-	if (freq < 5940 || freq > 7105)
+	if (freq < 5935 || freq > 7115)
 		return 0;
 
-	if (center_idx_to_bw_6ghz((freq - 5940) / 5) < 0)
+	if (freq == 5935)
+		return 1;
+
+	if (center_idx_to_bw_6ghz((freq - 5950) / 5) < 0)
 		return 0;
 
 	return 1;
@@ -2123,7 +2141,7 @@
 
 int is_6ghz_op_class(u8 op_class)
 {
-	return op_class >= 131 && op_class <= 135;
+	return op_class >= 131 && op_class <= 136;
 }
 
 
@@ -2131,14 +2149,14 @@
 {
 	int i;
 
-	if (!is_6ghz_freq(freq))
+	if (!is_6ghz_freq(freq) || freq == 5935)
 		return 0;
-	if ((((freq - 5940) / 5) & 0x3) != 0x1)
+	if ((((freq - 5950) / 5) & 0x3) != 0x1)
 		return 0;
 
-	i = (freq - 5940 + 55) % 80;
+	i = (freq - 5950 + 55) % 80;
 	if (i == 0)
-		i = (freq - 5940 + 55) / 80;
+		i = (freq - 5950 + 55) / 80;
 
 	if (i >= 1 && i <= 15)
 		return 1;
@@ -2374,6 +2392,8 @@
 	case 134: /* UHB channels, 160 MHz: 15, 47, 79.. */
 	case 135: /* UHB channels, 80+80 MHz: 7, 23, 39.. */
 		return 160;
+	case 136: /* UHB channels, 20 MHz: 2 */
+		return 20;
 	case 180: /* 60 GHz band, channels 1..8 */
 		return 2160;
 	case 181: /* 60 GHz band, EDMG CB2, channels 9..15 */
@@ -2434,6 +2454,8 @@
 		return CHANWIDTH_160MHZ;
 	case 135: /* UHB channels, 80+80 MHz: 7, 23, 39.. */
 		return CHANWIDTH_80P80MHZ;
+	case 136: /* UHB channels, 20 MHz: 2 */
+		return CHANWIDTH_USE_HT;
 	case 180: /* 60 GHz band, channels 1..8 */
 		return CHANWIDTH_2160MHZ;
 	case 181: /* 60 GHz band, EDMG CB2, channels 9..15 */
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index e395769..55f7aa6 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -113,6 +113,7 @@
 	const u8 *he_capabilities;
 	const u8 *he_operation;
 	const u8 *short_ssid_list;
+	const u8 *he_6ghz_band_cap;
 
 	u8 ssid_len;
 	u8 supp_rates_len;
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 4a5eb16..65cc4df 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -473,6 +473,7 @@
 #define WLAN_EID_EXT_SPATIAL_REUSE 39
 #define WLAN_EID_EXT_OCV_OCI 54
 #define WLAN_EID_EXT_SHORT_SSID_LIST 58
+#define WLAN_EID_EXT_HE_6GHZ_BAND_CAP 59
 #define WLAN_EID_EXT_EDMG_CAPABILITIES 61
 #define WLAN_EID_EXT_EDMG_OPERATION 62
 #define WLAN_EID_EXT_REJECTED_GROUPS 92
@@ -2155,12 +2156,46 @@
 	le32 he_oper_params; /* HE Operation Parameters[3] and
 			      * BSS Color Information[1] */
 	le16 he_mcs_nss_set;
-	u8 vht_op_info_chwidth;
-	u8 vht_op_info_chan_center_freq_seg0_idx;
-	u8 vht_op_info_chan_center_freq_seg1_idx;
-	/* Followed by conditional MaxBSSID Indicator subfield (u8) */
+	/* Followed by conditional VHT Operation Information (3 octets),
+	 * Max Co-Hosted BSSID Indicator subfield (1 octet), and/or 6 GHz
+	 * Operation Information subfield (5 octets). */
 } STRUCT_PACKED;
 
+/* IEEE P802.11ax/D6.0, Figure 9-787k - 6 GHz Operation Information field */
+struct ieee80211_he_6ghz_oper_info {
+	u8 primary_chan;
+	u8 control;
+	u8 chan_center_freq_seg0;
+	u8 chan_center_freq_seg1;
+	u8 min_rate;
+} STRUCT_PACKED;
+
+#define HE_6GHZ_OPER_INFO_CTRL_CHAN_WIDTH_MASK	(BIT(0) | BIT(1))
+#define HE_6GHZ_OPER_INFO_CTRL_DUP_BEACON	BIT(2)
+
+/* IEEE P802.11ax/D6.0, 9.4.2.261 HE 6 GHz Band Capabilities element */
+struct ieee80211_he_6ghz_band_cap {
+	 /* Minimum MPDU Start Spacing B0..B2
+	  * Maximum A-MPDU Length Exponent B3..B5
+	  * Maximum MPDU Length B6..B7 */
+	u8 a_mpdu_params; /* B0..B7 */
+	u8 info; /* B8..B15 */
+} STRUCT_PACKED;
+
+#define HE_6GHZ_BAND_CAP_MIN_MPDU_START_SPACE_MASK		0x7
+#define HE_6GHZ_BAND_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK	0x7
+#define HE_6GHZ_BAND_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT	3
+#define HE_6GHZ_BAND_CAP_MAX_MPDU_LENGTH_MASK			0x3
+#define HE_6GHZ_BAND_CAP_MAX_MPDU_LENGTH_SHIFT			6
+
+#define HE_6GHZ_BAND_CAP_SMPS_MASK			  (BIT(1) | BIT(2))
+#define HE_6GHZ_BAND_CAP_SMPS_STATIC			  0
+#define HE_6GHZ_BAND_CAP_SMPS_DYNAMIC			  BIT(1)
+#define HE_6GHZ_BAND_CAP_SMPS_DISABLED			  (BIT(1) | BIT(2))
+#define HE_6GHZ_BAND_CAP_RD_RESPONDER			  BIT(3)
+#define HE_6GHZ_BAND_CAP_RX_ANTENNA_PATTERN		  BIT(4)
+#define HE_6GHZ_BAND_CAP_TX_ANTENNA_PATTERN		  BIT(5)
+
 /*
  * IEEE P802.11ax/D4.0, 9.4.2.246 Spatial Reuse Parameter Set element
  */
diff --git a/src/common/sae.c b/src/common/sae.c
index 543640d..1b4ec6d 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -169,7 +169,7 @@
 	 * being smaller than prime. */
 	in_range = const_time_fill_msb((unsigned int) cmp_prime);
 	/* The algorithm description would skip the next steps if
-	 * cmp_prime >= 0 (reutnr 0 here), but go through them regardless to
+	 * cmp_prime >= 0 (return 0 here), but go through them regardless to
 	 * minimize externally observable differences in behavior. */
 
 	x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index eb1861a..1e7498a 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -2305,7 +2305,7 @@
 	case WPA_CIPHER_TKIP:
 		return WPA_ALG_TKIP;
 	case WPA_CIPHER_AES_128_CMAC:
-		return WPA_ALG_IGTK;
+		return WPA_ALG_BIP_CMAC_128;
 	case WPA_CIPHER_BIP_GMAC_128:
 		return WPA_ALG_BIP_GMAC_128;
 	case WPA_CIPHER_BIP_GMAC_256:
@@ -2801,6 +2801,15 @@
 		return 0;
 	}
 
+	if (pos[1] >= RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_DPP) {
+		ie->dpp_kde = pos + 2 + RSN_SELECTOR_LEN;
+		ie->dpp_kde_len = pos[1] - RSN_SELECTOR_LEN;
+		wpa_hexdump(MSG_DEBUG, "WPA: DPP KDE in EAPOL-Key",
+			    pos, pos[1] + 2);
+		return 0;
+	}
+
 	return 2;
 }
 
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index c0ef689..065dc71 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -124,6 +124,7 @@
 #define WFA_KEY_DATA_IP_ADDR_REQ RSN_SELECTOR(0x50, 0x6f, 0x9a, 4)
 #define WFA_KEY_DATA_IP_ADDR_ALLOC RSN_SELECTOR(0x50, 0x6f, 0x9a, 5)
 #define WFA_KEY_DATA_TRANSITION_DISABLE RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x20)
+#define WFA_KEY_DATA_DPP RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x21)
 
 #define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
 
@@ -362,6 +363,10 @@
 #define TRANSITION_DISABLE_WPA3_ENTERPRISE BIT(2)
 #define TRANSITION_DISABLE_ENHANCED_OPEN BIT(3)
 
+/* DPP KDE Flags */
+#define DPP_KDE_PFS_ALLOWED BIT(0)
+#define DPP_KDE_PFS_REQUIRED BIT(1)
+
 #ifdef _MSC_VER
 #pragma pack(pop)
 #endif /* _MSC_VER */
@@ -528,6 +533,8 @@
 	const u8 *ip_addr_alloc;
 	const u8 *transition_disable;
 	size_t transition_disable_len;
+	const u8 *dpp_kde;
+	size_t dpp_kde_len;
 	const u8 *oci;
 	size_t oci_len;
 	const u8 *osen;
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index ca1c35f..354de28 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -190,6 +190,8 @@
 #define DPP_EVENT_INTRO "DPP-INTRO "
 #define DPP_EVENT_CONF_REQ_RX "DPP-CONF-REQ-RX "
 #define DPP_EVENT_CHIRP_STOPPED "DPP-CHIRP-STOPPED "
+#define DPP_EVENT_MUD_URL "DPP-MUD-URL "
+#define DPP_EVENT_BAND_SUPPORT "DPP-BAND-SUPPORT "
 
 /* MESH events */
 #define MESH_GROUP_STARTED "MESH-GROUP-STARTED "
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 7c7515f..de2b077 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -485,7 +485,7 @@
 	     u8 *data, size_t data_len);
 
 /**
- * crypto_get_random - Generate cryptographically strong pseudy-random bytes
+ * crypto_get_random - Generate cryptographically strong pseudo-random bytes
  * @buf: Buffer for data
  * @len: Number of bytes to generate
  * Returns: 0 on success, -1 on failure
diff --git a/src/crypto/crypto_wolfssl.c b/src/crypto/crypto_wolfssl.c
index dc68bd6..2e4bf89 100644
--- a/src/crypto/crypto_wolfssl.c
+++ b/src/crypto/crypto_wolfssl.c
@@ -1104,19 +1104,21 @@
 {
 	int ret = 0;
 	WC_RNG rng;
+	size_t len;
+	u8 *buf;
 
 	if (TEST_FAIL())
 		return -1;
 	if (wc_InitRng(&rng) != 0)
 		return -1;
-	if (mp_rand_prime((mp_int *) r,
-			  (mp_count_bits((mp_int *) m) + 7) / 8 * 2,
-			  &rng, NULL) != 0)
-		ret = -1;
-	if (ret == 0 &&
+	len = (mp_count_bits((mp_int *) m) + 7) / 8;
+	buf = os_malloc(len);
+	if (!buf || wc_RNG_GenerateBlock(&rng, buf, len) != 0 ||
+	    mp_read_unsigned_bin((mp_int *) r, buf, len) != MP_OKAY ||
 	    mp_mod((mp_int *) r, (mp_int *) m, (mp_int *) r) != 0)
 		ret = -1;
 	wc_FreeRng(&rng);
+	bin_clear_free(buf, len);
 	return ret;
 }
 
diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c
index d222d14..11e6582 100644
--- a/src/crypto/tls_wolfssl.c
+++ b/src/crypto/tls_wolfssl.c
@@ -1741,7 +1741,7 @@
 	if (!conn)
 		return NULL;
 
-	wpa_printf(MSG_DEBUG, "SSL: encrypt: %ld bytes", wpabuf_len(in_data));
+	wpa_printf(MSG_DEBUG, "SSL: encrypt: %zu bytes", wpabuf_len(in_data));
 
 	wolfssl_reset_out_data(&conn->output);
 
@@ -1792,7 +1792,7 @@
 	}
 	wpabuf_put(buf, res);
 
-	wpa_printf(MSG_DEBUG, "SSL: decrypt: %ld bytes", wpabuf_len(buf));
+	wpa_printf(MSG_DEBUG, "SSL: decrypt: %zu bytes", wpabuf_len(buf));
 
 	return buf;
 }
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index e3b13bc..350c1cb 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1553,8 +1553,9 @@
 	 * alg - Encryption algorithm
 	 *
 	 * (%WPA_ALG_NONE, %WPA_ALG_WEP, %WPA_ALG_TKIP, %WPA_ALG_CCMP,
-	 * %WPA_ALG_IGTK, %WPA_ALG_GCMP, %WPA_ALG_GCMP_256, %WPA_ALG_CCMP_256,
-	 * %WPA_ALG_BIP_GMAC_128, %WPA_ALG_BIP_GMAC_256, %WPA_ALG_BIP_CMAC_256);
+	 * %WPA_ALG_BIP_AES_CMAC_128, %WPA_ALG_GCMP, %WPA_ALG_GCMP_256,
+	 * %WPA_ALG_CCMP_256, %WPA_ALG_BIP_GMAC_128, %WPA_ALG_BIP_GMAC_256,
+	 * %WPA_ALG_BIP_CMAC_256);
 	 * %WPA_ALG_NONE clears the key. */
 	enum wpa_alg alg;
 
@@ -1658,6 +1659,73 @@
 	enum key_flag key_flag;
 };
 
+enum wpa_driver_if_type {
+	/**
+	 * WPA_IF_STATION - Station mode interface
+	 */
+	WPA_IF_STATION,
+
+	/**
+	 * WPA_IF_AP_VLAN - AP mode VLAN interface
+	 *
+	 * This interface shares its address and Beacon frame with the main
+	 * BSS.
+	 */
+	WPA_IF_AP_VLAN,
+
+	/**
+	 * WPA_IF_AP_BSS - AP mode BSS interface
+	 *
+	 * This interface has its own address and Beacon frame.
+	 */
+	WPA_IF_AP_BSS,
+
+	/**
+	 * WPA_IF_P2P_GO - P2P Group Owner
+	 */
+	WPA_IF_P2P_GO,
+
+	/**
+	 * WPA_IF_P2P_CLIENT - P2P Client
+	 */
+	WPA_IF_P2P_CLIENT,
+
+	/**
+	 * WPA_IF_P2P_GROUP - P2P Group interface (will become either
+	 * WPA_IF_P2P_GO or WPA_IF_P2P_CLIENT, but the role is not yet known)
+	 */
+	WPA_IF_P2P_GROUP,
+
+	/**
+	 * WPA_IF_P2P_DEVICE - P2P Device interface is used to indentify the
+	 * abstracted P2P Device function in the driver
+	 */
+	WPA_IF_P2P_DEVICE,
+
+	/*
+	 * WPA_IF_MESH - Mesh interface
+	 */
+	WPA_IF_MESH,
+
+	/*
+	 * WPA_IF_TDLS - TDLS offchannel interface (used for pref freq only)
+	 */
+	WPA_IF_TDLS,
+
+	/*
+	 * WPA_IF_IBSS - IBSS interface (used for pref freq only)
+	 */
+	WPA_IF_IBSS,
+
+	/*
+	 * WPA_IF_NAN - NAN Device
+	 */
+	WPA_IF_NAN,
+
+	/* keep last */
+	WPA_IF_MAX
+};
+
 /**
  * struct wpa_driver_capa - Driver capability information
  */
@@ -1679,8 +1747,16 @@
 #define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 0x00004000
 #define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 0x00008000
 #define WPA_DRIVER_CAPA_KEY_MGMT_SAE 		0x00010000
+#define WPA_DRIVER_CAPA_KEY_MGMT_802_1X_SHA256	0x00020000
+#define WPA_DRIVER_CAPA_KEY_MGMT_PSK_SHA256	0x00040000
+#define WPA_DRIVER_CAPA_KEY_MGMT_TPK_HANDSHAKE	0x00080000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE		0x00100000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384	0x00200000
+#define WPA_DRIVER_CAPA_KEY_MGMT_CCKM		0x00400000
+#define WPA_DRIVER_CAPA_KEY_MGMT_OSEN		0x00800000
 	/** Bitfield of supported key management suites */
 	unsigned int key_mgmt;
+	unsigned int key_mgmt_iftype[WPA_IF_MAX];
 
 #define WPA_DRIVER_CAPA_ENC_WEP40	0x00000001
 #define WPA_DRIVER_CAPA_ENC_WEP104	0x00000002
@@ -2018,6 +2094,7 @@
 	u8 vht_opmode;
 	const struct ieee80211_he_capabilities *he_capab;
 	size_t he_capab_len;
+	const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab;
 	u32 flags; /* bitmask of WPA_STA_* flags */
 	u32 flags_mask; /* unset bits in flags */
 #ifdef CONFIG_MESH
@@ -2045,65 +2122,6 @@
 	struct mac_address mac_acl[0];
 };
 
-enum wpa_driver_if_type {
-	/**
-	 * WPA_IF_STATION - Station mode interface
-	 */
-	WPA_IF_STATION,
-
-	/**
-	 * WPA_IF_AP_VLAN - AP mode VLAN interface
-	 *
-	 * This interface shares its address and Beacon frame with the main
-	 * BSS.
-	 */
-	WPA_IF_AP_VLAN,
-
-	/**
-	 * WPA_IF_AP_BSS - AP mode BSS interface
-	 *
-	 * This interface has its own address and Beacon frame.
-	 */
-	WPA_IF_AP_BSS,
-
-	/**
-	 * WPA_IF_P2P_GO - P2P Group Owner
-	 */
-	WPA_IF_P2P_GO,
-
-	/**
-	 * WPA_IF_P2P_CLIENT - P2P Client
-	 */
-	WPA_IF_P2P_CLIENT,
-
-	/**
-	 * WPA_IF_P2P_GROUP - P2P Group interface (will become either
-	 * WPA_IF_P2P_GO or WPA_IF_P2P_CLIENT, but the role is not yet known)
-	 */
-	WPA_IF_P2P_GROUP,
-
-	/**
-	 * WPA_IF_P2P_DEVICE - P2P Device interface is used to indentify the
-	 * abstracted P2P Device function in the driver
-	 */
-	WPA_IF_P2P_DEVICE,
-
-	/*
-	 * WPA_IF_MESH - Mesh interface
-	 */
-	WPA_IF_MESH,
-
-	/*
-	 * WPA_IF_TDLS - TDLS offchannel interface (used for pref freq only)
-	 */
-	WPA_IF_TDLS,
-
-	/*
-	 * WPA_IF_IBSS - IBSS interface (used for pref freq only)
-	 */
-	WPA_IF_IBSS,
-};
-
 struct wpa_init_params {
 	void *global_priv;
 	const u8 *bssid;
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index d630c3d..9b4166d 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -532,7 +532,7 @@
 		cipher = IEEE80211_CIPHER_AES_GCM_256;
 		break;
 #endif /* ATH_GCM_SUPPORT */
-	case WPA_ALG_IGTK:
+	case WPA_ALG_BIP_CMAC_128:
 		cipher = IEEE80211_CIPHER_AES_CMAC;
 		break;
 #ifdef ATH_GCM_SUPPORT
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index b4400d7..5adee13 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -1451,6 +1451,7 @@
 #define	GETPARAM(drv, param, v) \
 	(((v) = get80211param(drv, param)) != -1)
 	struct bsd_driver_data *drv;
+	int i;
 
 	drv = os_zalloc(sizeof(*drv));
 	if (drv == NULL)
@@ -1486,6 +1487,10 @@
 	if (wpa_driver_bsd_capa(drv))
 		goto fail;
 
+	/* Update per interface supported AKMs */
+	for (i = 0; i < WPA_IF_MAX; i++)
+		drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+
 	/* Down interface during setup. */
 	if (bsd_get_iface_flags(drv) < 0)
 		goto fail;
diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c
index 529fc3b..b5fff48 100644
--- a/src/drivers/driver_ndis.c
+++ b/src/drivers/driver_ndis.c
@@ -2809,6 +2809,7 @@
 {
 	struct wpa_driver_ndis_data *drv;
 	u32 mode;
+	int i;
 
 	drv = os_zalloc(sizeof(*drv));
 	if (drv == NULL)
@@ -2855,6 +2856,11 @@
 	}
 	wpa_driver_ndis_get_capability(drv);
 
+	/* Update per interface supported AKMs */
+	for (i = 0; i < WPA_IF_MAX; i++)
+		drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+
+
 	/* Make sure that the driver does not have any obsolete PMKID entries.
 	 */
 	wpa_driver_ndis_flush_pmkid(drv);
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index d48f8cb..51674f0 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -2328,10 +2328,19 @@
 	/* FT Action frames */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0)
 		ret = -1;
-	else
+	else if (!drv->has_driver_key_mgmt) {
+		int i;
+
+		/* Update supported AKMs only if the driver doesn't advertize
+		 * any AKM capabilities. */
 		drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT |
 			WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
 
+		/* Update per interface supported AKMs */
+		for (i = 0; i < WPA_IF_MAX; i++)
+			drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+	}
+
 	/* WNM - BSS Transition Management Request */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x07", 2) < 0)
 		ret = -1;
@@ -2918,7 +2927,7 @@
 		return RSN_CIPHER_SUITE_CCMP_256;
 	case WPA_ALG_GCMP_256:
 		return RSN_CIPHER_SUITE_GCMP_256;
-	case WPA_ALG_IGTK:
+	case WPA_ALG_BIP_CMAC_128:
 		return RSN_CIPHER_SUITE_AES_128_CMAC;
 	case WPA_ALG_BIP_GMAC_128:
 		return RSN_CIPHER_SUITE_BIP_GMAC_128;
@@ -3275,10 +3284,7 @@
 		goto fail2;
 	if (!key_msg ||
 	    nla_put_u8(key_msg, NL80211_KEY_IDX, key_idx) ||
-	    nla_put_flag(key_msg, (alg == WPA_ALG_IGTK ||
-				   alg == WPA_ALG_BIP_GMAC_128 ||
-				   alg == WPA_ALG_BIP_GMAC_256 ||
-				   alg == WPA_ALG_BIP_CMAC_256) ?
+	    nla_put_flag(key_msg, wpa_alg_bip(alg) ?
 			 (key_idx == 6 || key_idx == 7 ?
 			  NL80211_KEY_DEFAULT_BEACON :
 			  NL80211_KEY_DEFAULT_MGMT) :
@@ -3347,7 +3353,7 @@
 	if (!suite)
 		return -1;
 
-	if (defkey && alg == WPA_ALG_IGTK) {
+	if (defkey && wpa_alg_bip(alg)) {
 		if (nla_put_flag(msg, NL80211_KEY_DEFAULT_MGMT))
 			return -1;
 	} else if (defkey) {
@@ -4323,7 +4329,7 @@
 	num_suites = wpa_key_mgmt_to_suites(params->key_mgmt_suites,
 					    suites, ARRAY_SIZE(suites));
 	if (num_suites > NL80211_MAX_NR_AKM_SUITES)
-		wpa_printf(MSG_WARNING,
+		wpa_printf(MSG_DEBUG,
 			   "nl80211: Not enough room for all AKM suites (num_suites=%d > NL80211_MAX_NR_AKM_SUITES)",
 			   num_suites);
 	else if (num_suites &&
@@ -5056,6 +5062,10 @@
 		return "P2P_GO";
 	case NL80211_IFTYPE_P2P_DEVICE:
 		return "P2P_DEVICE";
+	case NL80211_IFTYPE_OCB:
+		return "OCB";
+	case NL80211_IFTYPE_NAN:
+		return "NAN";
 	default:
 		return "unknown";
 	}
@@ -5683,13 +5693,13 @@
 			return -1;
 	}
 
-	wpa_printf(MSG_DEBUG, "  * FILS ERP next seq %u",
-		   params->fils_erp_next_seq_num);
-	if (nla_put_u16(msg, NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM,
-			params->fils_erp_next_seq_num))
-		return -1;
-
 	if (params->fils_erp_rrk_len) {
+		wpa_printf(MSG_DEBUG, "  * FILS ERP next seq %u",
+			   params->fils_erp_next_seq_num);
+		if (nla_put_u16(msg, NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM,
+				params->fils_erp_next_seq_num))
+			return -1;
+
 		wpa_printf(MSG_DEBUG, "  * FILS ERP rRK (len=%lu)",
 			   (unsigned long) params->fils_erp_rrk_len);
 		if (nla_put(msg, NL80211_ATTR_FILS_ERP_RRK,
@@ -6261,6 +6271,20 @@
 			}
 		}
 
+		if (i == 0 && was_ap && !is_ap_interface(nlmode) &&
+		    bss->brname[0] &&
+		    (bss->added_if_into_bridge || bss->already_in_bridge)) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Remove AP interface %s temporarily from the bridge %s to allow its mode to be set to STATION",
+				   bss->ifname, bss->brname);
+			if (linux_br_del_if(drv->global->ioctl_sock,
+					    bss->brname, bss->ifname) < 0)
+				wpa_printf(MSG_INFO,
+					   "nl80211: Failed to remove interface %s from bridge %s: %s",
+					   bss->ifname, bss->brname,
+					   strerror(errno));
+		}
+
 		/* Try to set the mode again while the interface is down */
 		mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode);
 		if (mode_switch_res == -EBUSY) {
@@ -6333,6 +6357,29 @@
 }
 
 
+void nl80211_restore_ap_mode(struct i802_bss *bss)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int was_ap = is_ap_interface(drv->nlmode);
+
+	wpa_driver_nl80211_set_mode(bss, drv->ap_scan_as_station);
+	if (!was_ap && is_ap_interface(drv->ap_scan_as_station) &&
+	    bss->brname[0] &&
+	    (bss->added_if_into_bridge || bss->already_in_bridge)) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Add AP interface %s back into the bridge %s",
+			   bss->ifname, bss->brname);
+		if (linux_br_add_if(drv->global->ioctl_sock, bss->brname,
+				    bss->ifname) < 0) {
+			wpa_printf(MSG_WARNING,
+				   "nl80211: Failed to add interface %s into bridge %s: %s",
+				   bss->ifname, bss->brname, strerror(errno));
+		}
+	}
+	drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
+}
+
+
 int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
 				enum nl80211_iftype nlmode)
 {
@@ -7742,7 +7789,8 @@
 	struct ieee80211_hdr *hdr;
 	int offchanok = 1;
 
-	if (is_ap_interface(drv->nlmode) && (int) freq == bss->freq)
+	if (is_ap_interface(drv->nlmode) && (int) freq == bss->freq &&
+	    bss->beacon_set)
 		offchanok = 0;
 
 	wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, "
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index dc80a17..895f9d7 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -111,6 +111,7 @@
 	unsigned int num_iface_ext_capa;
 
 	int has_capability;
+	int has_driver_key_mgmt;
 
 	int operstate;
 
@@ -169,7 +170,6 @@
 	unsigned int set_wifi_conf_vendor_cmd_avail:1;
 	unsigned int fetch_bss_trans_status:1;
 	unsigned int roam_vendor_cmd_avail:1;
-	unsigned int get_supported_akm_suites_avail:1;
 	unsigned int add_sta_node_vendor_cmd_avail:1;
 	unsigned int control_port_ap:1;
 	unsigned int multicast_registrations:1;
@@ -275,6 +275,8 @@
 
 const char * nl80211_iftype_str(enum nl80211_iftype mode);
 
+void nl80211_restore_ap_mode(struct i802_bss *bss);
+
 #ifdef ANDROID
 int android_nl_socket_set_nonblocking(struct nl_sock *handle);
 int android_pno_start(struct i802_bss *bss,
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index f997577..3e8dcef 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -79,6 +79,8 @@
 	unsigned int mac_addr_rand_scan_supported:1;
 	unsigned int mac_addr_rand_sched_scan_supported:1;
 	unsigned int update_ft_ies_supported:1;
+	unsigned int has_key_mgmt:1;
+	unsigned int has_key_mgmt_iftype:1;
 };
 
 
@@ -252,6 +254,179 @@
 }
 
 
+static unsigned int get_akm_suites_info(struct nlattr *tb)
+{
+	int i, num;
+	unsigned int key_mgmt = 0;
+	u32 *akms;
+
+	if (!tb)
+		return 0;
+
+	num = nla_len(tb) / sizeof(u32);
+	akms = nla_data(tb);
+	for (i = 0; i < num; i++) {
+		switch (akms[i]) {
+		case RSN_AUTH_KEY_MGMT_UNSPEC_802_1X:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+				WPA_DRIVER_CAPA_KEY_MGMT_WPA2;
+			break;
+		case RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+				WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+			break;
+		case RSN_AUTH_KEY_MGMT_FT_802_1X:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT;
+			break;
+		case RSN_AUTH_KEY_MGMT_FT_PSK:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
+			break;
+		case RSN_AUTH_KEY_MGMT_802_1X_SHA256:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_802_1X_SHA256;
+			break;
+		case RSN_AUTH_KEY_MGMT_PSK_SHA256:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_PSK_SHA256;
+			break;
+		case RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_TPK_HANDSHAKE;
+			break;
+		case RSN_AUTH_KEY_MGMT_FT_SAE:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE;
+			break;
+		case RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384;
+			break;
+		case RSN_AUTH_KEY_MGMT_CCKM:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_CCKM;
+			break;
+		case RSN_AUTH_KEY_MGMT_OSEN:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_OSEN;
+			break;
+		case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B;
+			break;
+		case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
+			break;
+		case RSN_AUTH_KEY_MGMT_OWE:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_OWE;
+			break;
+		case RSN_AUTH_KEY_MGMT_DPP:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_DPP;
+			break;
+		case RSN_AUTH_KEY_MGMT_FILS_SHA256:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256;
+			break;
+		case RSN_AUTH_KEY_MGMT_FILS_SHA384:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
+			break;
+		case RSN_AUTH_KEY_MGMT_FT_FILS_SHA256:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256;
+			break;
+		case RSN_AUTH_KEY_MGMT_FT_FILS_SHA384:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384;
+			break;
+		case RSN_AUTH_KEY_MGMT_SAE:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SAE;
+			break;
+		}
+	}
+
+	return key_mgmt;
+}
+
+
+static void get_iface_akm_suites_info(struct wiphy_info_data *info,
+					struct nlattr *nl_akms)
+{
+	struct nlattr *tb[NL80211_IFTYPE_AKM_ATTR_MAX + 1];
+	struct nlattr *nl_iftype;
+	unsigned int key_mgmt;
+	int i;
+
+	if (!nl_akms)
+		return;
+
+	nla_parse(tb, NL80211_IFTYPE_AKM_ATTR_MAX,
+		  nla_data(nl_akms), nla_len(nl_akms), NULL);
+
+	if (!tb[NL80211_IFTYPE_AKM_ATTR_IFTYPES] ||
+	    !tb[NL80211_IFTYPE_AKM_ATTR_SUITES])
+		return;
+
+	info->has_key_mgmt_iftype = 1;
+	key_mgmt = get_akm_suites_info(tb[NL80211_IFTYPE_AKM_ATTR_SUITES]);
+
+	nla_for_each_nested(nl_iftype, tb[NL80211_IFTYPE_AKM_ATTR_IFTYPES], i) {
+		switch (nla_type(nl_iftype)) {
+		case NL80211_IFTYPE_ADHOC:
+			info->drv->capa.key_mgmt_iftype[WPA_IF_IBSS] = key_mgmt;
+			break;
+		case NL80211_IFTYPE_STATION:
+			info->drv->capa.key_mgmt_iftype[WPA_IF_STATION] =
+				key_mgmt;
+			break;
+		case NL80211_IFTYPE_AP:
+			info->drv->capa.key_mgmt_iftype[WPA_IF_AP_BSS] =
+				key_mgmt;
+			break;
+		case NL80211_IFTYPE_AP_VLAN:
+			info->drv->capa.key_mgmt_iftype[WPA_IF_AP_VLAN] =
+				key_mgmt;
+			break;
+		case NL80211_IFTYPE_MESH_POINT:
+			info->drv->capa.key_mgmt_iftype[WPA_IF_MESH] = key_mgmt;
+			break;
+		case NL80211_IFTYPE_P2P_CLIENT:
+			info->drv->capa.key_mgmt_iftype[WPA_IF_P2P_CLIENT] =
+				key_mgmt;
+			break;
+		case NL80211_IFTYPE_P2P_GO:
+			info->drv->capa.key_mgmt_iftype[WPA_IF_P2P_GO] =
+				key_mgmt;
+			break;
+		case NL80211_IFTYPE_P2P_DEVICE:
+			info->drv->capa.key_mgmt_iftype[WPA_IF_P2P_DEVICE] =
+				key_mgmt;
+			break;
+		case NL80211_IFTYPE_NAN:
+			info->drv->capa.key_mgmt_iftype[WPA_IF_NAN] = key_mgmt;
+			break;
+		}
+		wpa_printf(MSG_DEBUG, "nl80211: %s supported key_mgmt 0x%x",
+			   nl80211_iftype_str(nla_type(nl_iftype)),
+			   key_mgmt);
+	}
+}
+
+
+static void wiphy_info_iftype_akm_suites(struct wiphy_info_data *info,
+					 struct nlattr *tb)
+{
+	struct nlattr *nl_if;
+	int rem_if;
+
+	if (!tb)
+		return;
+
+	nla_for_each_nested(nl_if, tb, rem_if)
+		get_iface_akm_suites_info(info, nl_if);
+}
+
+
+static void wiphy_info_akm_suites(struct wiphy_info_data *info,
+				  struct nlattr *tb)
+{
+	if (!tb)
+		return;
+
+	info->has_key_mgmt = 1;
+	info->capa->key_mgmt = get_akm_suites_info(tb);
+	wpa_printf(MSG_DEBUG, "nl80211: wiphy supported key_mgmt 0x%x",
+		   info->capa->key_mgmt);
+}
+
+
 static void wiphy_info_cipher_suites(struct wiphy_info_data *info,
 				     struct nlattr *tb)
 {
@@ -696,6 +871,8 @@
 	wiphy_info_iface_comb(info, tb[NL80211_ATTR_INTERFACE_COMBINATIONS]);
 	wiphy_info_supp_cmds(info, tb[NL80211_ATTR_SUPPORTED_COMMANDS]);
 	wiphy_info_cipher_suites(info, tb[NL80211_ATTR_CIPHER_SUITES]);
+	wiphy_info_akm_suites(info, tb[NL80211_ATTR_AKM_SUITES]);
+	wiphy_info_iftype_akm_suites(info, tb[NL80211_ATTR_IFTYPE_AKM_SUITES]);
 
 	if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) {
 		wpa_printf(MSG_DEBUG, "nl80211: Using driver-based "
@@ -809,9 +986,6 @@
 				case QCA_NL80211_VENDOR_SUBCMD_ROAM:
 					drv->roam_vendor_cmd_avail = 1;
 					break;
-				case QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS:
-					drv->get_supported_akm_suites_avail = 1;
-					break;
 				case QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE:
 					drv->add_sta_node_vendor_cmd_avail = 1;
 					break;
@@ -990,126 +1164,6 @@
 }
 
 
-static unsigned int get_akm_suites_info(struct nlattr *tb)
-{
-	int i, num;
-	unsigned int key_mgmt = 0;
-	u32 *akms;
-
-	if (!tb)
-		return 0;
-
-	num = nla_len(tb) / sizeof(u32);
-	akms = nla_data(tb);
-	for (i = 0; i < num; i++) {
-		u32 a = akms[i];
-
-		wpa_printf(MSG_DEBUG,
-			   "nl80211: Supported AKM %02x-%02x-%02x:%u",
-			   a >> 24, (a >> 16) & 0xff,
-			   (a >> 8) & 0xff, a & 0xff);
-		switch (a) {
-		case RSN_AUTH_KEY_MGMT_UNSPEC_802_1X:
-			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA |
-				WPA_DRIVER_CAPA_KEY_MGMT_WPA2;
-			break;
-		case RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X:
-			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
-				WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
-			break;
-		case RSN_AUTH_KEY_MGMT_FT_802_1X:
-			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT;
-			break;
-		case RSN_AUTH_KEY_MGMT_FT_PSK:
-			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
-			break;
-		case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B:
-			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B;
-			break;
-		case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192:
-			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
-			break;
-		case RSN_AUTH_KEY_MGMT_OWE:
-			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_OWE;
-			break;
-		case RSN_AUTH_KEY_MGMT_DPP:
-			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_DPP;
-			break;
-		case RSN_AUTH_KEY_MGMT_FILS_SHA256:
-			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256;
-			break;
-		case RSN_AUTH_KEY_MGMT_FILS_SHA384:
-			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
-			break;
-		case RSN_AUTH_KEY_MGMT_FT_FILS_SHA256:
-			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256;
-			break;
-		case RSN_AUTH_KEY_MGMT_FT_FILS_SHA384:
-			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384;
-			break;
-		case RSN_AUTH_KEY_MGMT_SAE:
-			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SAE;
-			break;
-		}
-	}
-
-	return key_mgmt;
-}
-
-
-static int get_akm_suites_handler(struct nl_msg *msg, void *arg)
-{
-	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-	unsigned int *key_mgmt = arg;
-
-	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-		  genlmsg_attrlen(gnlh, 0), NULL);
-
-	if (tb[NL80211_ATTR_VENDOR_DATA]) {
-		struct nlattr *nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
-		struct nlattr *tb_data[NL80211_ATTR_MAX + 1];
-
-		nla_parse(tb_data, NL80211_ATTR_MAX,
-			  nla_data(nl_vend), nla_len(nl_vend), NULL);
-
-		*key_mgmt =
-			get_akm_suites_info(tb_data[NL80211_ATTR_AKM_SUITES]);
-	}
-
-	return NL_SKIP;
-}
-
-
-static int qca_nl80211_get_akm_suites(struct wpa_driver_nl80211_data *drv)
-{
-	struct nl_msg *msg;
-	unsigned int key_mgmt = 0;
-	int ret;
-
-	if (!drv->get_supported_akm_suites_avail)
-		return -1;
-
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
-	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
-	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-			QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS)) {
-		nlmsg_free(msg);
-		return -1;
-	}
-
-	ret = send_and_recv_msgs(drv, msg, get_akm_suites_handler, &key_mgmt);
-	if (!ret) {
-		wpa_printf(MSG_DEBUG,
-			   "nl80211: Replace capa.key_mgmt based on driver advertised capabilities: 0x%x",
-			   key_mgmt);
-		drv->capa.key_mgmt = key_mgmt;
-	}
-
-	return ret;
-}
-
-
 struct features_info {
 	u8 *flags;
 	size_t flags_len;
@@ -1221,6 +1275,8 @@
 int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
 {
 	struct wiphy_info_data info;
+	int i;
+
 	if (wpa_driver_nl80211_get_info(drv, &info))
 		return -1;
 
@@ -1228,33 +1284,62 @@
 		return -1;
 
 	drv->has_capability = 1;
-	drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
-		WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
-		WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
-		WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK |
-		WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B |
-		WPA_DRIVER_CAPA_KEY_MGMT_OWE |
-		WPA_DRIVER_CAPA_KEY_MGMT_DPP;
+	drv->has_driver_key_mgmt = info.has_key_mgmt | info.has_key_mgmt_iftype;
 
-	if (drv->capa.enc & (WPA_DRIVER_CAPA_ENC_CCMP_256 |
-			     WPA_DRIVER_CAPA_ENC_GCMP_256))
-		drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
+	/* Fallback to hardcoded defaults if the driver does nott advertize any
+	 * AKM capabilities. */
+	if (!drv->has_driver_key_mgmt) {
+		drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+			WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+			WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+			WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK |
+			WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B |
+			WPA_DRIVER_CAPA_KEY_MGMT_OWE |
+			WPA_DRIVER_CAPA_KEY_MGMT_DPP;
 
-	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME)
-		drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
-			WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 |
-			WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 |
-			WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 |
-			WPA_DRIVER_CAPA_KEY_MGMT_SAE;
-	else if (drv->capa.flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD)
-		drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
-			WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
+		if (drv->capa.enc & (WPA_DRIVER_CAPA_ENC_CCMP_256 |
+				     WPA_DRIVER_CAPA_ENC_GCMP_256))
+			drv->capa.key_mgmt |=
+				WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
 
-#ifdef CONFIG_DRIVER_NL80211_QCA
-	/* Override drv->capa.key_mgmt based on driver advertised capability
-	 * constraints, if available. */
-	qca_nl80211_get_akm_suites(drv);
-#endif /* CONFIG_DRIVER_NL80211_QCA */
+		if (drv->capa.flags & WPA_DRIVER_FLAGS_SME)
+			drv->capa.key_mgmt |=
+				WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
+				WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 |
+				WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 |
+				WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 |
+				WPA_DRIVER_CAPA_KEY_MGMT_SAE;
+		else if (drv->capa.flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD)
+			drv->capa.key_mgmt |=
+				WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
+				WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
+	}
+
+	if (!info.has_key_mgmt_iftype) {
+		/* If the driver does not advertize per interface AKM
+		 * capabilities, consider all interfaces to support default AKMs
+		 * in key_mgmt. */
+		for (i = 0; i < WPA_IF_MAX; i++)
+			drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+	} else if (info.has_key_mgmt_iftype && !info.has_key_mgmt) {
+		/* If the driver advertizes only per interface supported AKMs
+		 * but does not advertize per wiphy AKM capabilities, consider
+		 * the default key_mgmt as a mask of per interface supported
+		 * AKMs. */
+		drv->capa.key_mgmt = 0;
+		for (i = 0; i < WPA_IF_MAX; i++)
+			drv->capa.key_mgmt |= drv->capa.key_mgmt_iftype[i];
+	} else if (info.has_key_mgmt_iftype && info.has_key_mgmt) {
+		/* If the driver advertizes AKM capabilities both per wiphy and
+		 * per interface, consider the interfaces for which per
+		 * interface AKM capabilities were not received to support the
+		 * default key_mgmt capabilities.
+		 */
+		for (i = 0; i < WPA_IF_MAX; i++)
+			if (!drv->capa.key_mgmt_iftype[i])
+				drv->capa.key_mgmt_iftype[i] =
+					drv->capa.key_mgmt;
+	}
 
 	drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
 		WPA_DRIVER_AUTH_SHARED |
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 1152312..1a5f899 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -464,6 +464,13 @@
 		event.assoc_info.fils_pmkid = nla_data(fils_pmkid);
 
 	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+
+	/* Avoid a race condition by stopping to ignore any following
+	 * disconnection events now that the driver has indicated it is
+	 * connected since that connection could have been triggered by a roam
+	 * operation that happened in parallel with the disconnection request.
+	 */
+	drv->ignore_next_local_disconnect = 0;
 }
 
 
@@ -2581,11 +2588,8 @@
 
 	if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED &&
 	    (cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
-	     cmd == NL80211_CMD_SCAN_ABORTED)) {
-		wpa_driver_nl80211_set_mode(drv->first_bss,
-					    drv->ap_scan_as_station);
-		drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
-	}
+	     cmd == NL80211_CMD_SCAN_ABORTED))
+		nl80211_restore_ap_mode(bss);
 
 	switch (cmd) {
 	case NL80211_CMD_TRIGGER_SCAN:
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index 17e8b2c..dc91a29 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -166,11 +166,8 @@
 
 	wpa_printf(MSG_DEBUG, "nl80211: Failed to abort scan");
 
-	if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED) {
-		wpa_driver_nl80211_set_mode(drv->first_bss,
-					    drv->ap_scan_as_station);
-		drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
-	}
+	if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED)
+		nl80211_restore_ap_mode(drv->first_bss);
 
 	wpa_printf(MSG_DEBUG, "nl80211: Try to get scan results");
 	wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index 978e1cf..0f0ad1f 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -968,6 +968,7 @@
 static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv)
 {
 	int send_rfkill_event = 0;
+	int i;
 
 	if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1) < 0) {
 		if (rfkill_is_blocked(drv->rfkill)) {
@@ -996,6 +997,10 @@
 
 	wpa_driver_wext_get_range(drv);
 
+	/* Update per interface supported AKMs */
+	for (i = 0; i < WPA_IF_MAX; i++)
+		drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+
 	/*
 	 * Unlock the driver's BSSID and force to a random SSID to clear any
 	 * previous association the driver might have when the supplicant
@@ -1768,7 +1773,7 @@
 		case WPA_ALG_CCMP:
 			ext->alg = IW_ENCODE_ALG_CCMP;
 			break;
-		case WPA_ALG_IGTK:
+		case WPA_ALG_BIP_CMAC_128:
 			ext->alg = IW_ENCODE_ALG_AES_CMAC;
 			break;
 		default:
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
index eaa1ad7..0986627 100644
--- a/src/eap_peer/eap_sim.c
+++ b/src/eap_peer/eap_sim.c
@@ -520,6 +520,12 @@
 	wpa_printf(MSG_DEBUG, "Generating EAP-SIM Start (id=%d)", id);
 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id,
 			       EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START);
+	if (identity) {
+		wpa_hexdump_ascii(MSG_DEBUG, "   AT_IDENTITY",
+				  identity, identity_len);
+		eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
+				identity, identity_len);
+	}
 	if (!data->reauth) {
 		wpa_hexdump(MSG_DEBUG, "   AT_NONCE_MT",
 			    data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
@@ -531,13 +537,6 @@
 				data->selected_version, NULL, 0);
 	}
 
-	if (identity) {
-		wpa_hexdump_ascii(MSG_DEBUG, "   AT_IDENTITY",
-				  identity, identity_len);
-		eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
-				identity, identity_len);
-	}
-
 	resp = eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0);
 	if (resp)
 		eap_sim_state(data, START_DONE);
diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c
index e3a57e7..1c11cb6 100644
--- a/src/eapol_auth/eapol_auth_sm.c
+++ b/src/eapol_auth/eapol_auth_sm.c
@@ -56,6 +56,7 @@
 }
 
 
+PRINTF_FORMAT(4, 5)
 static void eapol_auth_vlogger(struct eapol_authenticator *eapol,
 			       const u8 *addr, eapol_logger_level level,
 			       const char *fmt, ...)
diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h
index 6c49fa9..83faa05 100644
--- a/src/rsn_supp/pmksa_cache.h
+++ b/src/rsn_supp/pmksa_cache.h
@@ -28,6 +28,7 @@
 	 */
 	u8 fils_cache_id[2];
 	unsigned int fils_cache_id_set:1;
+	unsigned int dpp_pfs:1;
 
 	os_time_t reauth_time;
 
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 33e7f41..b9b1f0f 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -21,6 +21,7 @@
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "common/ocv.h"
+#include "common/dpp.h"
 #include "eap_common/eap_defs.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "drivers/driver.h"
@@ -737,7 +738,8 @@
 	kde_buf = os_malloc(kde_len +
 			    2 + RSN_SELECTOR_LEN + 3 +
 			    sm->assoc_rsnxe_len +
-			    2 + RSN_SELECTOR_LEN + 1);
+			    2 + RSN_SELECTOR_LEN + 1 +
+			    2 + RSN_SELECTOR_LEN + 2);
 	if (!kde_buf)
 		goto failed;
 	os_memcpy(kde_buf, kde, kde_len);
@@ -782,6 +784,27 @@
 	}
 #endif /* CONFIG_P2P */
 
+#ifdef CONFIG_DPP2
+	if (DPP_VERSION > 1 && sm->key_mgmt == WPA_KEY_MGMT_DPP) {
+		u8 *pos;
+
+		wpa_printf(MSG_DEBUG, "DPP: Add DPP KDE into EAPOL-Key 2/4");
+		pos = kde + kde_len;
+		*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+		*pos++ = RSN_SELECTOR_LEN + 2;
+		RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_DPP);
+		pos += RSN_SELECTOR_LEN;
+		*pos++ = DPP_VERSION; /* Protocol Version */
+		*pos = 0; /* Flags */
+		if (sm->dpp_pfs == 0)
+			*pos |= DPP_KDE_PFS_ALLOWED;
+		else if (sm->dpp_pfs == 1)
+			*pos |= DPP_KDE_PFS_ALLOWED | DPP_KDE_PFS_REQUIRED;
+		pos++;
+		kde_len = pos - kde;
+	}
+#endif /* CONFIG_DPP2 */
+
 	if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce,
 				       kde, kde_len, ptk) < 0)
 		goto failed;
@@ -1693,6 +1716,20 @@
 	}
 #endif /* CONFIG_OCV */
 
+#ifdef CONFIG_DPP2
+	if (DPP_VERSION > 1 && ie.dpp_kde) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: peer Protocol Version %u Flags 0x%x",
+			   ie.dpp_kde[0], ie.dpp_kde[1]);
+		if (sm->key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_pfs != 2 &&
+		    (ie.dpp_kde[1] & DPP_KDE_PFS_ALLOWED) && !sm->dpp_z) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Peer indicated it supports PFS and local configuration allows this, but PFS was not negotiated for the association");
+			goto failed;
+		}
+	}
+#endif /* CONFIG_DPP2 */
+
 	if (sm->use_ext_key_id &&
 	    wpa_supplicant_install_ptk(sm, key, KEY_FLAG_RX))
 		goto failed;
@@ -3255,6 +3292,11 @@
 		sm->ft_rsnxe_used = value;
 		break;
 #endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_DPP2
+	case WPA_PARAM_DPP_PFS:
+		sm->dpp_pfs = value;
+		break;
+#endif /* CONFIG_DPP2 */
 	default:
 		break;
 	}
@@ -3292,6 +3334,15 @@
 		return pos - buf;
 	pos += ret;
 
+#ifdef CONFIG_DPP2
+	if (sm->key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_z) {
+		ret = os_snprintf(pos, end - pos, "dpp_pfs=1\n");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_DPP2 */
+
 	if (sm->mfp != NO_MGMT_FRAME_PROTECTION && sm->ap_rsn_ie) {
 		struct wpa_ie_data rsn;
 		if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn)
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 0986c6c..dfc156b 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -107,6 +107,7 @@
 	WPA_PARAM_EXT_KEY_ID,
 	WPA_PARAM_USE_EXT_KEY_ID,
 	WPA_PARAM_FT_RSNXE_USED,
+	WPA_PARAM_DPP_PFS,
 };
 
 struct rsn_supp_config {
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 497d128..f7d9f62 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -183,6 +183,7 @@
 
 #ifdef CONFIG_DPP2
 	struct wpabuf *dpp_z;
+	int dpp_pfs;
 #endif /* CONFIG_DPP2 */
 };
 
diff --git a/src/utils/common.h b/src/utils/common.h
index 8e5cfe1..45f72bb 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -482,7 +482,8 @@
 void inc_byte_array(u8 *counter, size_t len);
 void buf_shift_right(u8 *buf, size_t len, size_t bits);
 void wpa_get_ntp_timestamp(u8 *buf);
-int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...);
+int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...)
+	PRINTF_FORMAT(3, 4);
 int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len,
 			 char sep);
 int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len);
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 37432d9..8631775 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -277,6 +277,12 @@
 ifdef CONFIG_DPP
 L_CFLAGS += -DCONFIG_DPP
 OBJS += src/common/dpp.c
+OBJS += src/common/dpp_auth.c
+OBJS += src/common/dpp_backup.c
+OBJS += src/common/dpp_crypto.c
+OBJS += src/common/dpp_pkex.c
+OBJS += src/common/dpp_reconfig.c
+OBJS += src/common/dpp_tcp.c
 OBJS += dpp_supplicant.c
 NEED_AES_SIV=y
 NEED_HMAC_SHA256_KDF=y
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 738b0bd..b35d11e 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -279,6 +279,12 @@
 ifdef CONFIG_DPP
 CFLAGS += -DCONFIG_DPP
 OBJS += ../src/common/dpp.o
+OBJS += ../src/common/dpp_auth.o
+OBJS += ../src/common/dpp_backup.o
+OBJS += ../src/common/dpp_crypto.o
+OBJS += ../src/common/dpp_pkex.o
+OBJS += ../src/common/dpp_reconfig.o
+OBJS += ../src/common/dpp_tcp.o
 OBJS += dpp_supplicant.o
 NEED_AES_SIV=y
 NEED_HMAC_SHA256_KDF=y
@@ -1075,7 +1081,7 @@
 
 ifeq ($(CONFIG_TLS), wolfssl)
 ifdef TLS_FUNCS
-CFLAGS += -DWOLFSSL_DER_LOAD -I/usr/local/include/wolfssl
+CFLAGS += -DWOLFSSL_DER_LOAD
 OBJS += ../src/crypto/tls_wolfssl.o
 endif
 OBJS += ../src/crypto/crypto_wolfssl.o
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 6241682..c89b80f 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -44,6 +44,27 @@
 #endif /* CONFIG_WPS */
 
 
+static bool is_chanwidth160_supported(struct hostapd_hw_modes *mode,
+				      struct hostapd_config *conf)
+{
+#ifdef CONFIG_IEEE80211AX
+	if (conf->ieee80211ax) {
+		struct he_capabilities *he_cap;
+
+		he_cap = &mode->he_capab[IEEE80211_MODE_AP];
+		if (he_cap->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+		    (HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G |
+		     HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G))
+			return true;
+	}
+#endif /* CONFIG_IEEE80211AX */
+	if (mode->vht_capab & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
+			       VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))
+		return true;
+	return false;
+}
+
+
 static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s,
 			     struct wpa_ssid *ssid,
 			     struct hostapd_config *conf,
@@ -98,7 +119,7 @@
 		 */
 		conf->vht_oper_chwidth = CHANWIDTH_160MHZ;
 		center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel);
-		if (center_chan) {
+		if (center_chan && is_chanwidth160_supported(mode, conf)) {
 			wpa_printf(MSG_DEBUG,
 				   "VHT center channel %u for auto-selected 160 MHz bandwidth",
 				   center_chan);
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index e1d9824..173be09 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -4305,6 +4305,7 @@
 	config->disassoc_imminent_rssi_threshold =
 		DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD;
 	config->oce = DEFAULT_OCE_SUPPORT;
+	config->btm_offload = DEFAULT_BTM_OFFLOAD;
 #endif /* CONFIG_MBO */
 
 	if (ctrl_interface)
@@ -5048,6 +5049,7 @@
 		    MBO_CELL_CAPA_NOT_SUPPORTED), 0 },
 	{ INT_RANGE(disassoc_imminent_rssi_threshold, -120, 0), 0 },
 	{ INT_RANGE(oce, 0, 3), 0 },
+	{ INT_RANGE(btm_offload, 0, 1), CFG_CHANGED_DISABLE_BTM_NOTIFY },
 #endif /* CONFIG_MBO */
 	{ INT(gas_address3), 0 },
 	{ INT_RANGE(ftm_responder, 0, 1), 0 },
@@ -5065,6 +5067,7 @@
 	{ INT_RANGE(disable_btm, 0, 1), CFG_CHANGED_DISABLE_BTM },
 	{ INT_RANGE(extended_key_id, 0, 1), 0 },
 #endif /* CONFIG_WNM */
+	{ INT_RANGE(wowlan_disconnect_on_deinit, 0, 1), 0},
 };
 
 #undef FUNC
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 0ca27cb..712e871 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -45,6 +45,7 @@
 #define DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD -75
 #define DEFAULT_OCE_SUPPORT OCE_STA
 #define DEFAULT_EXTENDED_KEY_ID 0
+#define DEFAULT_BTM_OFFLOAD 0
 
 #include "config_ssid.h"
 #include "wps/wps.h"
@@ -376,6 +377,7 @@
 #define CFG_CHANGED_SCHED_SCAN_PLANS BIT(17)
 #define CFG_CHANGED_WOWLAN_TRIGGERS BIT(18)
 #define CFG_CHANGED_DISABLE_BTM BIT(19)
+#define CFG_CHANGED_DISABLE_BTM_NOTIFY BIT(20)
 
 /**
  * struct wpa_config - wpa_supplicant configuration data
@@ -1447,6 +1449,14 @@
 	 *  - Set BIT(1) to enable OCE in STA-CFON mode
 	 */
 	unsigned int oce;
+
+	/**
+	 * btm_offload - Set where to perform roaming logic
+	 *  - Set to 0 to handle fully roaming logic in supplicant
+	 *  - Set to 1 to skip roaming logic in supplicant for firmware roaming
+	 *    just parse BTM frame and notify framework
+	 */
+        int btm_offload;
 #endif /* CONFIG_MBO */
 
 	/**
@@ -1590,6 +1600,15 @@
 	 * 1 = use Extended Key ID when possible
 	 */
 	int extended_key_id;
+
+	/**
+	 * wowlan_disconnect_on_deinit - Trigger disconnect on wpa_supplicant
+	 * interface deinit even if the driver has enabled WoWLAN.
+	 *
+	 * 0 = Do not disconnect
+	 * 1 = Trigger disconnection
+	 */
+	int wowlan_disconnect_on_deinit;
 };
 
 
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 52e1372..5faed4e 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -1607,6 +1607,9 @@
 	if (config->extended_key_id != DEFAULT_EXTENDED_KEY_ID)
 		fprintf(f, "extended_key_id=%d\n",
 			config->extended_key_id);
+	if (config->wowlan_disconnect_on_deinit)
+		fprintf(f, "wowlan_disconnect_on_deinit=%d\n",
+			config->wowlan_disconnect_on_deinit);
 }
 
 #endif /* CONFIG_NO_CONFIG_WRITE */
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index e0547f1..f2d4d01 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / Control interface (shared code for all backends)
- * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2020, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -696,6 +696,8 @@
 			ret = -1;
 		else
 			dpp_nonce_override_len = hex_len / 2;
+	} else if (os_strcasecmp(cmd, "dpp_version_override") == 0) {
+		dpp_version_override = atoi(value);
 #endif /* CONFIG_TESTING_OPTIONS */
 #endif /* CONFIG_DPP */
 #ifdef CONFIG_TESTING_OPTIONS
@@ -891,6 +893,8 @@
 
 	if (os_strcmp(cmd, "version") == 0) {
 		res = os_snprintf(buf, buflen, "%s", VERSION_STR);
+	} else if (os_strcasecmp(cmd, "max_command_len") == 0) {
+		res = os_snprintf(buf, buflen, "%u", CTRL_IFACE_MAX_LEN);
 	} else if (os_strcasecmp(cmd, "country") == 0) {
 		if (wpa_s->conf->country[0] && wpa_s->conf->country[1])
 			res = os_snprintf(buf, buflen, "%c%c",
@@ -3977,7 +3981,7 @@
 };
 
 
-static int ctrl_iface_get_capability_pairwise(int res, char *strict,
+static int ctrl_iface_get_capability_pairwise(int res, bool strict,
 					      struct wpa_driver_capa *capa,
 					      char *buf, size_t buflen)
 {
@@ -4017,7 +4021,7 @@
 }
 
 
-static int ctrl_iface_get_capability_group(int res, char *strict,
+static int ctrl_iface_get_capability_group(int res, bool strict,
 					   struct wpa_driver_capa *capa,
 					   char *buf, size_t buflen)
 {
@@ -4065,7 +4069,7 @@
 }
 
 
-static int ctrl_iface_get_capability_group_mgmt(int res, char *strict,
+static int ctrl_iface_get_capability_group_mgmt(int res, bool strict,
 						struct wpa_driver_capa *capa,
 						char *buf, size_t buflen)
 {
@@ -4094,11 +4098,49 @@
 }
 
 
-static int ctrl_iface_get_capability_key_mgmt(int res, char *strict,
+static int iftype_str_to_index(const char *iftype_str)
+{
+	if (!iftype_str)
+		return WPA_IF_MAX;
+
+	if (os_strcmp(iftype_str, "STATION") == 0)
+		return WPA_IF_STATION;
+
+	if (os_strcmp(iftype_str, "AP_VLAN") == 0)
+		return WPA_IF_AP_VLAN;
+
+	if (os_strcmp(iftype_str, "AP") == 0)
+		return WPA_IF_AP_BSS;
+
+	if (os_strcmp(iftype_str, "P2P_GO") == 0)
+		return WPA_IF_P2P_GO;
+
+	if (os_strcmp(iftype_str, "P2P_CLIENT") == 0)
+		return WPA_IF_P2P_CLIENT;
+
+	if (os_strcmp(iftype_str, "P2P_DEVICE") == 0)
+		return WPA_IF_P2P_DEVICE;
+
+	if (os_strcmp(iftype_str, "MESH") == 0)
+		return WPA_IF_MESH;
+
+	if (os_strcmp(iftype_str, "IBSS") == 0)
+		return WPA_IF_IBSS;
+
+	if (os_strcmp(iftype_str, "NAN") == 0)
+		return WPA_IF_NAN;
+
+	return WPA_IF_MAX;
+}
+
+
+static int ctrl_iface_get_capability_key_mgmt(int res, bool strict,
 					      struct wpa_driver_capa *capa,
+					      const char *iftype_str,
 					      char *buf, size_t buflen)
 {
 	int ret;
+	unsigned int key_mgmt;
 	char *pos, *end;
 	size_t len;
 
@@ -4115,36 +4157,68 @@
 		return len;
 	}
 
+	if (iftype_str) {
+		enum wpa_driver_if_type iftype;
+
+		iftype = iftype_str_to_index(iftype_str);
+		if (iftype == WPA_IF_MAX)
+			return -1;
+		key_mgmt = capa->key_mgmt_iftype[iftype];
+	} else {
+		key_mgmt = capa->key_mgmt;
+	}
+
 	ret = os_snprintf(pos, end - pos, "NONE IEEE8021X");
 	if (os_snprintf_error(end - pos, ret))
 		return pos - buf;
 	pos += ret;
 
-	if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
-			      WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) {
+	if (key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+			WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) {
 		ret = os_snprintf(pos, end - pos, " WPA-EAP");
 		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
 
-	if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
-			      WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
+	if (key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+			WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
 		ret = os_snprintf(pos, end - pos, " WPA-PSK");
 		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
 
-	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
+	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
 		ret = os_snprintf(pos, end - pos, " WPA-NONE");
 		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
 
+	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK) {
+		ret = os_snprintf(pos, end - pos, " WAPI-PSK");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_TPK_HANDSHAKE) {
+		ret = os_snprintf(pos, end - pos, " TPK-HANDSHAKE");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_CCKM) {
+		ret = os_snprintf(pos, end - pos, " CCKM");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
 #ifdef CONFIG_SUITEB
-	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B) {
+	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B) {
 		ret = os_snprintf(pos, end - pos, " WPA-EAP-SUITE-B");
 		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
@@ -4152,7 +4226,7 @@
 	}
 #endif /* CONFIG_SUITEB */
 #ifdef CONFIG_SUITEB192
-	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192) {
+	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192) {
 		ret = os_snprintf(pos, end - pos, " WPA-EAP-SUITE-B-192");
 		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
@@ -4160,7 +4234,7 @@
 	}
 #endif /* CONFIG_SUITEB192 */
 #ifdef CONFIG_OWE
-	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_OWE) {
+	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_OWE) {
 		ret = os_snprintf(pos, end - pos, " OWE");
 		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
@@ -4168,7 +4242,7 @@
 	}
 #endif /* CONFIG_OWE */
 #ifdef CONFIG_DPP
-	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_DPP) {
+	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_DPP) {
 		ret = os_snprintf(pos, end - pos, " DPP");
 		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
@@ -4176,26 +4250,26 @@
 	}
 #endif /* CONFIG_DPP */
 #ifdef CONFIG_FILS
-	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256) {
+	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256) {
 		ret = os_snprintf(pos, end - pos, " FILS-SHA256");
 		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
-	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384) {
+	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384) {
 		ret = os_snprintf(pos, end - pos, " FILS-SHA384");
 		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
 #ifdef CONFIG_IEEE80211R
-	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256) {
+	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256) {
 		ret = os_snprintf(pos, end - pos, " FT-FILS-SHA256");
 		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
-	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384) {
+	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384) {
 		ret = os_snprintf(pos, end - pos, " FT-FILS-SHA384");
 		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
@@ -4204,27 +4278,72 @@
 #endif /* CONFIG_IEEE80211R */
 #endif /* CONFIG_FILS */
 #ifdef CONFIG_IEEE80211R
-	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK) {
+	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK) {
 		ret = os_snprintf(pos, end - pos, " FT-PSK");
 		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
+	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) {
+		ret = os_snprintf(pos, end - pos, " FT-EAP");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#ifdef CONFIG_SAE
+	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE) {
+		ret = os_snprintf(pos, end - pos, " FT-SAE");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_SHA384
+	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384) {
+		ret = os_snprintf(pos, end - pos, " FT-EAP-SHA384");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_SHA384 */
 #endif /* CONFIG_IEEE80211R */
 #ifdef CONFIG_SAE
-	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SAE) {
+	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SAE) {
 		ret = os_snprintf(pos, end - pos, " SAE");
 		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
 #endif /* CONFIG_SAE */
+#ifdef CONFIG_SHA256
+	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_802_1X_SHA256) {
+		ret = os_snprintf(pos, end - pos, " WPA-EAP-SHA256");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_PSK_SHA256) {
+		ret = os_snprintf(pos, end - pos, " WPA-PSK-SHA256");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_SHA256 */
+#ifdef CONFIG_HS20
+	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_OSEN) {
+		ret = os_snprintf(pos, end - pos, " OSEN");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_HS20 */
 
 	return pos - buf;
 }
 
 
-static int ctrl_iface_get_capability_proto(int res, char *strict,
+static int ctrl_iface_get_capability_proto(int res, bool strict,
 					   struct wpa_driver_capa *capa,
 					   char *buf, size_t buflen)
 {
@@ -4267,7 +4386,7 @@
 
 
 static int ctrl_iface_get_capability_auth_alg(struct wpa_supplicant *wpa_s,
-					      int res, char *strict,
+					      int res, bool strict,
 					      struct wpa_driver_capa *capa,
 					      char *buf, size_t buflen)
 {
@@ -4345,7 +4464,7 @@
 }
 
 
-static int ctrl_iface_get_capability_modes(int res, char *strict,
+static int ctrl_iface_get_capability_modes(int res, bool strict,
 					   struct wpa_driver_capa *capa,
 					   char *buf, size_t buflen)
 {
@@ -4508,23 +4627,36 @@
 {
 	struct wpa_driver_capa capa;
 	int res;
-	char *strict;
-	char field[30];
+	char *next_param, *curr_param, *iftype = NULL;
+	bool strict = false;
+	char field[50];
 	size_t len;
 
 	/* Determine whether or not strict checking was requested */
 	len = os_strlcpy(field, _field, sizeof(field));
 	if (len >= sizeof(field))
 		return -1;
-	strict = os_strchr(field, ' ');
-	if (strict != NULL) {
-		*strict++ = '\0';
-		if (os_strcmp(strict, "strict") != 0)
+
+	next_param = os_strchr(field, ' ');
+	while (next_param) {
+		*next_param++ = '\0';
+		curr_param = next_param;
+		next_param = os_strchr(next_param, ' ');
+
+		if (next_param)
+			*next_param = '\0';
+
+		if (os_strcmp(curr_param, "strict") == 0)
+			strict = true;
+		else if (os_strncmp(curr_param, "iftype=", 7) == 0)
+			iftype = curr_param + 7;
+		else
 			return -1;
 	}
 
-	wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s' %s",
-		field, strict ? strict : "");
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s'%s%s%s",
+		   field, iftype ? " iftype=" : "", iftype ? iftype : "",
+		   strict ? " strict" : "");
 
 	if (os_strcmp(field, "eap") == 0) {
 		return eap_get_names(buf, buflen);
@@ -4546,7 +4678,7 @@
 
 	if (os_strcmp(field, "key_mgmt") == 0)
 		return ctrl_iface_get_capability_key_mgmt(res, strict, &capa,
-							  buf, buflen);
+							  iftype, buf, buflen);
 
 	if (os_strcmp(field, "proto") == 0)
 		return ctrl_iface_get_capability_proto(res, strict, &capa,
@@ -8158,6 +8290,11 @@
 	dpp_pkex_ephemeral_key_override_len = 0;
 	dpp_protocol_key_override_len = 0;
 	dpp_nonce_override_len = 0;
+#ifdef CONFIG_DPP2
+	dpp_version_override = 2;
+#else /* CONFIG_DPP2 */
+	dpp_version_override = 1;
+#endif /* CONFIG_DPP2 */
 #endif /* CONFIG_TESTING_OPTIONS */
 #endif /* CONFIG_DPP */
 
@@ -11004,6 +11141,12 @@
 			reply_len = -1;
 	} else if (os_strcmp(buf, "DPP_STOP_CHIRP") == 0) {
 		wpas_dpp_chirp_stop(wpa_s);
+	} else if (os_strncmp(buf, "DPP_RECONFIG ", 13) == 0) {
+		struct wpa_ssid *ssid;
+
+		ssid = wpa_config_get_network(wpa_s->conf, atoi(buf + 13));
+		if (!ssid || wpas_dpp_reconfig(wpa_s, ssid) < 0)
+			reply_len = -1;
 #endif /* CONFIG_DPP2 */
 #endif /* CONFIG_DPP */
 	} else {
diff --git a/wpa_supplicant/ctrl_iface.h b/wpa_supplicant/ctrl_iface.h
index d54cc07..a408288 100644
--- a/wpa_supplicant/ctrl_iface.h
+++ b/wpa_supplicant/ctrl_iface.h
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / UNIX domain socket -based control interface
- * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2020, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -11,6 +11,10 @@
 
 #ifdef CONFIG_CTRL_IFACE
 
+#ifndef CTRL_IFACE_MAX_LEN
+#define CTRL_IFACE_MAX_LEN 8192
+#endif /* CTRL_IFACE_MAX_LEN */
+
 /* Shared functions from ctrl_iface.c; to be called by ctrl_iface backends */
 
 /**
@@ -66,14 +70,17 @@
 
 /**
  * wpa_supplicant_ctrl_iface_deinit - Deinitialize control interface
+ * @wpa_s: Pointer to wpa_supplicant data
  * @priv: Pointer to private data from wpa_supplicant_ctrl_iface_init()
  *
  * Deinitialize the control interface that was initialized with
- * wpa_supplicant_ctrl_iface_init().
+ * wpa_supplicant_ctrl_iface_init() and any data related to the wpa_s instance.
+ * @priv may be %NULL if the control interface has not yet been initialized.
  *
  * Required to be implemented in each control interface backend.
  */
-void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv);
+void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s,
+				      struct ctrl_iface_priv *priv);
 
 /**
  * wpa_supplicant_ctrl_iface_wait - Wait for ctrl_iface monitor
diff --git a/wpa_supplicant/ctrl_iface_named_pipe.c b/wpa_supplicant/ctrl_iface_named_pipe.c
index 9c0a47e..bddc041 100644
--- a/wpa_supplicant/ctrl_iface_named_pipe.c
+++ b/wpa_supplicant/ctrl_iface_named_pipe.c
@@ -45,7 +45,7 @@
 
 /* Per-interface ctrl_iface */
 
-#define REQUEST_BUFSIZE 256
+#define REQUEST_BUFSIZE CTRL_IFACE_MAX_LEN
 #define REPLY_BUFSIZE 4096
 
 struct ctrl_iface_priv;
@@ -462,8 +462,11 @@
 }
 
 
-void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
+void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s,
+				      struct ctrl_iface_priv *priv)
 {
+	if (!priv)
+		return;
 	while (priv->ctrl_dst)
 		ctrl_close_pipe(priv->ctrl_dst);
 	if (priv->sec_attr_set)
diff --git a/wpa_supplicant/ctrl_iface_udp.c b/wpa_supplicant/ctrl_iface_udp.c
index 1e92b97..1cbf7fa 100644
--- a/wpa_supplicant/ctrl_iface_udp.c
+++ b/wpa_supplicant/ctrl_iface_udp.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / UDP socket -based control interface
- * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2020, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -219,7 +219,7 @@
 {
 	struct wpa_supplicant *wpa_s = eloop_ctx;
 	struct ctrl_iface_priv *priv = sock_ctx;
-	char buf[4096], *pos;
+	char *buf, *pos;
 	int res;
 #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
 	struct sockaddr_in6 from;
@@ -235,11 +235,15 @@
 	int new_attached = 0;
 	u8 cookie[COOKIE_LEN];
 
-	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+	buf = os_malloc(CTRL_IFACE_MAX_LEN + 1);
+	if (!buf)
+		return;
+	res = recvfrom(sock, buf, CTRL_IFACE_MAX_LEN, 0,
 		       (struct sockaddr *) &from, &fromlen);
 	if (res < 0) {
 		wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
 			   strerror(errno));
+		os_free(buf);
 		return;
 	}
 
@@ -249,6 +253,8 @@
 	if (os_strcmp(addr, "::1")) {
 		wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected source %s",
 			   addr);
+		os_free(buf);
+		return;
 	}
 #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 	if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
@@ -260,11 +266,17 @@
 		 */
 		wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected "
 			   "source %s", inet_ntoa(from.sin_addr));
+		os_free(buf);
 		return;
 	}
 #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 
+	if ((size_t) res > CTRL_IFACE_MAX_LEN) {
+		wpa_printf(MSG_ERROR, "recvform(ctrl_iface): input truncated");
+		os_free(buf);
+		return;
+	}
 	buf[res] = '\0';
 
 	if (os_strcmp(buf, "GET_COOKIE") == 0) {
@@ -282,18 +294,21 @@
 	if (os_strncmp(buf, "COOKIE=", 7) != 0) {
 		wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - "
 			   "drop request");
+		os_free(buf);
 		return;
 	}
 
 	if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) {
 		wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the "
 			   "request - drop request");
+		os_free(buf);
 		return;
 	}
 
 	if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - "
 			   "drop request");
+		os_free(buf);
 		return;
 	}
 
@@ -339,6 +354,8 @@
 		       fromlen);
 	}
 
+	os_free(buf);
+
 	if (new_attached)
 		eapol_sm_notify_ctrl_attached(wpa_s->eapol);
 }
@@ -473,8 +490,12 @@
 }
 
 
-void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
+void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s,
+				      struct ctrl_iface_priv *priv)
 {
+	if (!priv)
+		return;
+
 	if (priv->sock > -1) {
 		eloop_unregister_read_sock(priv->sock);
 		if (priv->ctrl_dst) {
@@ -600,10 +621,13 @@
 {
 	struct wpa_global *global = eloop_ctx;
 	struct ctrl_iface_global_priv *priv = sock_ctx;
-	char buf[4096], *pos;
+	char *buf, *pos;
 	int res;
 #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
 	struct sockaddr_in6 from;
+#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
+	char addr[INET6_ADDRSTRLEN];
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 	struct sockaddr_in from;
 #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
@@ -612,16 +636,28 @@
 	size_t reply_len;
 	u8 cookie[COOKIE_LEN];
 
-	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+	buf = os_malloc(CTRL_IFACE_MAX_LEN + 1);
+	if (!buf)
+		return;
+	res = recvfrom(sock, buf, CTRL_IFACE_MAX_LEN, 0,
 		       (struct sockaddr *) &from, &fromlen);
 	if (res < 0) {
 		wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
 			   strerror(errno));
+		os_free(buf);
 		return;
 	}
 
 #ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
-#ifndef CONFIG_CTRL_IFACE_UDP_IPV6
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+	inet_ntop(AF_INET6, &from.sin6_addr, addr, sizeof(from));
+	if (os_strcmp(addr, "::1")) {
+		wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected source %s",
+			   addr);
+		os_free(buf);
+		return;
+	}
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 	if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
 		/*
 		 * The OS networking stack is expected to drop this kind of
@@ -631,11 +667,17 @@
 		 */
 		wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected "
 			   "source %s", inet_ntoa(from.sin_addr));
+		os_free(buf);
 		return;
 	}
 #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 
+	if ((size_t) res > CTRL_IFACE_MAX_LEN) {
+		wpa_printf(MSG_ERROR, "recvform(ctrl_iface): input truncated");
+		os_free(buf);
+		return;
+	}
 	buf[res] = '\0';
 
 	if (os_strcmp(buf, "GET_COOKIE") == 0) {
@@ -646,18 +688,21 @@
 	if (os_strncmp(buf, "COOKIE=", 7) != 0) {
 		wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - "
 			   "drop request");
+		os_free(buf);
 		return;
 	}
 
 	if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) {
 		wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the "
 			   "request - drop request");
+		os_free(buf);
 		return;
 	}
 
 	if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - "
 			   "drop request");
+		os_free(buf);
 		return;
 	}
 
@@ -694,6 +739,8 @@
 		sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from,
 		       fromlen);
 	}
+
+	os_free(buf);
 }
 
 
diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c
index 71fe7ed..639573d 100644
--- a/wpa_supplicant/ctrl_iface_unix.c
+++ b/wpa_supplicant/ctrl_iface_unix.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / UNIX domain socket -based control interface
- * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2020, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -131,7 +131,7 @@
 {
 	struct wpa_supplicant *wpa_s = eloop_ctx;
 	struct ctrl_iface_priv *priv = sock_ctx;
-	char buf[4096];
+	char *buf;
 	int res;
 	struct sockaddr_storage from;
 	socklen_t fromlen = sizeof(from);
@@ -139,11 +139,20 @@
 	size_t reply_len = 0;
 	int new_attached = 0;
 
-	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+	buf = os_malloc(CTRL_IFACE_MAX_LEN + 1);
+	if (!buf)
+		return;
+	res = recvfrom(sock, buf, CTRL_IFACE_MAX_LEN + 1, 0,
 		       (struct sockaddr *) &from, &fromlen);
 	if (res < 0) {
 		wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
 			   strerror(errno));
+		os_free(buf);
+		return;
+	}
+	if ((size_t) res > CTRL_IFACE_MAX_LEN) {
+		wpa_printf(MSG_ERROR, "recvform(ctrl_iface): input truncated");
+		os_free(buf);
 		return;
 	}
 	buf[res] = '\0';
@@ -221,6 +230,7 @@
 		}
 	}
 	os_free(reply_buf);
+	os_free(buf);
 
 	if (new_attached)
 		eapol_sm_notify_ctrl_attached(wpa_s->eapol);
@@ -790,12 +800,52 @@
 }
 
 
-void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
+static void
+wpas_global_ctrl_iface_flush_queued_msg(struct wpa_global *global,
+					struct wpa_supplicant *wpa_s)
+{
+	struct ctrl_iface_global_priv *gpriv;
+	struct ctrl_iface_msg *msg, *prev_msg;
+	unsigned int count = 0;
+
+	if (!global || !global->ctrl_iface)
+		return;
+
+	gpriv = global->ctrl_iface;
+	dl_list_for_each_safe(msg, prev_msg, &gpriv->msg_queue,
+			      struct ctrl_iface_msg, list) {
+		if (msg->wpa_s == wpa_s) {
+			count++;
+			dl_list_del(&msg->list);
+			os_free(msg);
+		}
+	}
+
+	if (count) {
+		wpa_printf(MSG_DEBUG,
+			   "CTRL: Dropped %u pending message(s) for interface that is being removed",
+			count);
+	}
+}
+
+
+void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s,
+				      struct ctrl_iface_priv *priv)
 {
 	struct wpa_ctrl_dst *dst, *prev;
 	struct ctrl_iface_msg *msg, *prev_msg;
 	struct ctrl_iface_global_priv *gpriv;
 
+	if (!priv) {
+		/* Control interface has not yet been initialized, so there is
+		 * nothing to deinitialize here. However, there might be a
+		 * pending message for this interface, so get rid of any such
+		 * entry before completing interface removal. */
+		wpas_global_ctrl_iface_flush_queued_msg(wpa_s->global, wpa_s);
+		eloop_cancel_timeout(wpas_ctrl_msg_queue_timeout, wpa_s, NULL);
+		return;
+	}
+
 	if (priv->sock > -1) {
 		char *fname;
 		char *buf, *dir = NULL;
@@ -867,6 +917,7 @@
 			}
 		}
 	}
+	wpas_global_ctrl_iface_flush_queued_msg(wpa_s->global, wpa_s);
 	eloop_cancel_timeout(wpas_ctrl_msg_queue_timeout, priv->wpa_s, NULL);
 	os_free(priv);
 }
@@ -1046,18 +1097,27 @@
 {
 	struct wpa_global *global = eloop_ctx;
 	struct ctrl_iface_global_priv *priv = sock_ctx;
-	char buf[4096];
+	char *buf;
 	int res;
 	struct sockaddr_storage from;
 	socklen_t fromlen = sizeof(from);
 	char *reply = NULL, *reply_buf = NULL;
 	size_t reply_len;
 
-	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+	buf = os_malloc(CTRL_IFACE_MAX_LEN + 1);
+	if (!buf)
+		return;
+	res = recvfrom(sock, buf, CTRL_IFACE_MAX_LEN + 1, 0,
 		       (struct sockaddr *) &from, &fromlen);
 	if (res < 0) {
 		wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
 			   strerror(errno));
+		os_free(buf);
+		return;
+	}
+	if ((size_t) res > CTRL_IFACE_MAX_LEN) {
+		wpa_printf(MSG_ERROR, "recvform(ctrl_iface): input truncated");
+		os_free(buf);
 		return;
 	}
 	buf[res] = '\0';
@@ -1105,6 +1165,7 @@
 		}
 	}
 	os_free(reply_buf);
+	os_free(buf);
 }
 
 
diff --git a/wpa_supplicant/dbus/dbus_new_introspect.c b/wpa_supplicant/dbus/dbus_new_introspect.c
index aee105b..6c721bf 100644
--- a/wpa_supplicant/dbus/dbus_new_introspect.c
+++ b/wpa_supplicant/dbus/dbus_new_introspect.c
@@ -257,7 +257,7 @@
 	DBusMessage *reply;
 	struct wpabuf *xml;
 
-	xml = wpabuf_alloc(20000);
+	xml = wpabuf_alloc(30000);
 	if (xml == NULL)
 		return NULL;
 
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index c570775..7e06fdd 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -506,7 +506,7 @@
 CONFIG_P2P=y
 
 # Enable TDLS support
-#CONFIG_TDLS=y
+CONFIG_TDLS=y
 
 # Wi-Fi Display
 # This can be used to enable Wi-Fi Display extensions for P2P using an external
diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c
index 6dfa2e5..8c2e302 100644
--- a/wpa_supplicant/dpp_supplicant.c
+++ b/wpa_supplicant/dpp_supplicant.c
@@ -46,6 +46,10 @@
 			const u8 *src, const u8 *bssid,
 			const u8 *data, size_t data_len,
 			enum offchannel_send_action_result result);
+#ifdef CONFIG_DPP2
+static void wpas_dpp_reconfig_reply_wait_timeout(void *eloop_ctx,
+						 void *timeout_ctx);
+#endif /* CONFIG_DPP2 */
 
 static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
@@ -247,6 +251,35 @@
 
 #ifdef CONFIG_DPP2
 
+static void wpas_dpp_stop_listen_for_tx(struct wpa_supplicant *wpa_s,
+					unsigned int freq,
+					unsigned int wait_time)
+{
+	struct os_reltime now, res;
+	unsigned int remaining;
+
+	if (!wpa_s->dpp_listen_freq)
+		return;
+
+	os_get_reltime(&now);
+	if (os_reltime_before(&now, &wpa_s->dpp_listen_end)) {
+		os_reltime_sub(&wpa_s->dpp_listen_end, &now, &res);
+		remaining = res.sec * 1000 + res.usec / 1000;
+	} else {
+		remaining = 0;
+	}
+	if (wpa_s->dpp_listen_freq == freq && remaining > wait_time)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Stop listen on %u MHz ending in %u ms to allow immediate TX on %u MHz for %u ms",
+		   wpa_s->dpp_listen_freq, remaining, freq, wait_time);
+	wpas_dpp_listen_stop(wpa_s);
+
+	/* TODO: Restart listen in some cases after TX? */
+}
+
+
 static void wpas_dpp_conn_status_result_timeout(void *eloop_ctx,
 						void *timeout_ctx)
 {
@@ -435,6 +468,10 @@
 		eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
 		eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s,
 				     NULL);
+#ifdef CONFIG_DPP2
+		eloop_cancel_timeout(wpas_dpp_reconfig_reply_wait_timeout,
+				     wpa_s, NULL);
+#endif /* CONFIG_DPP2 */
 		offchannel_send_action_done(wpa_s);
 		dpp_auth_deinit(wpa_s->dpp_auth);
 		wpa_s->dpp_auth = NULL;
@@ -769,6 +806,10 @@
 		eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
 		eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s,
 				     NULL);
+#ifdef CONFIG_DPP2
+		eloop_cancel_timeout(wpas_dpp_reconfig_reply_wait_timeout,
+				     wpa_s, NULL);
+#endif /* CONFIG_DPP2 */
 		offchannel_send_action_done(wpa_s);
 		dpp_auth_deinit(wpa_s->dpp_auth);
 		wpa_s->dpp_auth = NULL;
@@ -941,6 +982,24 @@
 }
 
 
+void wpas_dpp_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+				   unsigned int freq, unsigned int duration)
+{
+	if (wpa_s->dpp_listen_freq != freq)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Remain-on-channel started for listen on %u MHz for %u ms",
+		   freq, duration);
+	os_get_reltime(&wpa_s->dpp_listen_end);
+	wpa_s->dpp_listen_end.usec += duration * 1000;
+	while (wpa_s->dpp_listen_end.usec >= 1000000) {
+		wpa_s->dpp_listen_end.sec++;
+		wpa_s->dpp_listen_end.usec -= 1000000;
+	}
+}
+
+
 void wpas_dpp_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
 					  unsigned int freq)
 {
@@ -1080,7 +1139,8 @@
 
 		res = wpa_drv_get_capa(wpa_s, &capa);
 		if (res == 0 &&
-		    !(capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SAE) &&
+		    !(capa.key_mgmt_iftype[WPA_IF_STATION] &
+		      WPA_DRIVER_CAPA_KEY_MGMT_SAE) &&
 		    !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) {
 			wpa_printf(MSG_DEBUG,
 				   "DPP: SAE not supported by the driver");
@@ -1204,6 +1264,20 @@
 static void wpas_dpp_post_process_config(struct wpa_supplicant *wpa_s,
 					 struct dpp_authentication *auth)
 {
+#ifdef CONFIG_DPP2
+	if (auth->reconfig && wpa_s->dpp_reconfig_ssid &&
+	    wpa_config_get_network(wpa_s->conf, wpa_s->dpp_reconfig_ssid_id) ==
+	    wpa_s->dpp_reconfig_ssid) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Remove reconfigured network profile");
+		wpas_notify_network_removed(wpa_s, wpa_s->dpp_reconfig_ssid);
+		wpa_config_remove_network(wpa_s->conf,
+					  wpa_s->dpp_reconfig_ssid_id);
+		wpa_s->dpp_reconfig_ssid = NULL;
+		wpa_s->dpp_reconfig_ssid_id = -1;
+	}
+#endif /* CONFIG_DPP2 */
+
 	if (wpa_s->conf->dpp_config_processing < 2)
 		return;
 
@@ -1321,7 +1395,8 @@
 
 	wpa_s->dpp_gas_dialog_token = -1;
 
-	if (!auth || !auth->auth_success) {
+	if (!auth || (!auth->auth_success && !auth->reconfig_success) ||
+	    os_memcmp(addr, auth->peer_mac_addr, ETH_ALEN) != 0) {
 		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
 		return;
 	}
@@ -1781,6 +1856,222 @@
 	}
 }
 
+
+static void wpas_dpp_reconfig_reply_wait_timeout(void *eloop_ctx,
+						 void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+	if (!auth)
+		return;
+
+	wpa_printf(MSG_DEBUG, "DPP: Reconfig Reply wait timeout");
+	offchannel_send_action_done(wpa_s);
+	wpas_dpp_listen_stop(wpa_s);
+	dpp_auth_deinit(auth);
+	wpa_s->dpp_auth = NULL;
+}
+
+
+static void
+wpas_dpp_rx_reconfig_announcement(struct wpa_supplicant *wpa_s, const u8 *src,
+				  const u8 *hdr, const u8 *buf, size_t len,
+				  unsigned int freq)
+{
+	const u8 *csign_hash;
+	u16 csign_hash_len;
+	struct dpp_configurator *conf;
+	struct dpp_authentication *auth;
+	unsigned int wait_time, max_wait_time;
+
+	if (!wpa_s->dpp)
+		return;
+
+	if (wpa_s->dpp_auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore Reconfig Announcement during ongoing Authentication");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Reconfig Announcement from " MACSTR,
+		   MAC2STR(src));
+
+	csign_hash = dpp_get_attr(buf, len, DPP_ATTR_C_SIGN_KEY_HASH,
+				  &csign_hash_len);
+	if (!csign_hash || csign_hash_len != SHA256_MAC_LEN) {
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_FAIL
+			"Missing or invalid required Configurator C-sign key Hash attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Configurator C-sign key Hash (kid)",
+		    csign_hash, csign_hash_len);
+	conf = dpp_configurator_find_kid(wpa_s->dpp, csign_hash);
+	if (!conf) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No matching Configurator information found");
+		return;
+	}
+
+	auth = dpp_reconfig_init(wpa_s->dpp, wpa_s, conf, freq);
+	if (!auth)
+		return;
+	wpas_dpp_set_testing_options(wpa_s, auth);
+	if (dpp_set_configurator(auth, wpa_s->dpp_configurator_params) < 0) {
+		dpp_auth_deinit(auth);
+		return;
+	}
+
+	os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
+	wpa_s->dpp_auth = auth;
+
+	wpa_s->dpp_in_response_listen = 0;
+	wpa_s->dpp_auth_ok_on_ack = 0;
+	wait_time = wpa_s->max_remain_on_chan;
+	max_wait_time = wpa_s->dpp_resp_wait_time ?
+		wpa_s->dpp_resp_wait_time : 2000;
+	if (wait_time > max_wait_time)
+		wait_time = max_wait_time;
+	wait_time += 10; /* give the driver some extra time to complete */
+	eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
+			       wpas_dpp_reconfig_reply_wait_timeout,
+			       wpa_s, NULL);
+	wait_time -= 10;
+
+	wpas_dpp_stop_listen_for_tx(wpa_s, freq, wait_time);
+
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+		MAC2STR(src), freq, DPP_PA_RECONFIG_AUTH_REQ);
+	if (offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr, broadcast,
+				   wpabuf_head(auth->reconfig_req_msg),
+				   wpabuf_len(auth->reconfig_req_msg),
+				   wait_time, wpas_dpp_tx_status, 0) < 0) {
+		dpp_auth_deinit(wpa_s->dpp_auth);
+		wpa_s->dpp_auth = NULL;
+	}
+}
+
+
+static void
+wpas_dpp_rx_reconfig_auth_req(struct wpa_supplicant *wpa_s, const u8 *src,
+			      const u8 *hdr, const u8 *buf, size_t len,
+			      unsigned int freq)
+{
+	struct wpa_ssid *ssid;
+	struct dpp_authentication *auth;
+
+	wpa_printf(MSG_DEBUG, "DPP: Reconfig Authentication Request from "
+		   MACSTR, MAC2STR(src));
+
+	if (!wpa_s->dpp || wpa_s->dpp_auth ||
+	    !wpa_s->dpp_reconfig_announcement || !wpa_s->dpp_reconfig_ssid)
+		return;
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+		if (ssid == wpa_s->dpp_reconfig_ssid &&
+		    ssid->id == wpa_s->dpp_reconfig_ssid_id)
+			break;
+	}
+	if (!ssid || !ssid->dpp_connector || !ssid->dpp_netaccesskey ||
+	    !ssid->dpp_csign)
+		return;
+
+	auth = dpp_reconfig_auth_req_rx(wpa_s->dpp, wpa_s, ssid->dpp_connector,
+					ssid->dpp_netaccesskey,
+					ssid->dpp_netaccesskey_len,
+					ssid->dpp_csign, ssid->dpp_csign_len,
+					freq, hdr, buf, len);
+	if (!auth)
+		return;
+	os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
+	wpa_s->dpp_auth = auth;
+
+	wpas_dpp_chirp_stop(wpa_s);
+
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+		MAC2STR(src), freq, DPP_PA_RECONFIG_AUTH_RESP);
+	if (offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr, broadcast,
+				   wpabuf_head(auth->reconfig_resp_msg),
+				   wpabuf_len(auth->reconfig_resp_msg),
+				   500, wpas_dpp_tx_status, 0) < 0) {
+		dpp_auth_deinit(wpa_s->dpp_auth);
+		wpa_s->dpp_auth = NULL;
+	}
+}
+
+
+static void
+wpas_dpp_rx_reconfig_auth_resp(struct wpa_supplicant *wpa_s, const u8 *src,
+			       const u8 *hdr, const u8 *buf, size_t len,
+			       unsigned int freq)
+{
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+	struct wpabuf *conf;
+
+	wpa_printf(MSG_DEBUG, "DPP: Reconfig Authentication Response from "
+		   MACSTR, MAC2STR(src));
+
+	if (!auth || !auth->reconfig || !auth->configurator) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Reconfig Authentication in progress - drop");
+		return;
+	}
+
+	if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+			   MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+		return;
+	}
+
+	conf = dpp_reconfig_auth_resp_rx(auth, hdr, buf, len);
+	if (!conf)
+		return;
+
+	eloop_cancel_timeout(wpas_dpp_reconfig_reply_wait_timeout, wpa_s, NULL);
+
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+		MAC2STR(src), freq, DPP_PA_RECONFIG_AUTH_CONF);
+	if (offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr, broadcast,
+				   wpabuf_head(conf), wpabuf_len(conf),
+				   500, wpas_dpp_tx_status, 0) < 0) {
+		wpabuf_free(conf);
+		dpp_auth_deinit(wpa_s->dpp_auth);
+		wpa_s->dpp_auth = NULL;
+		return;
+	}
+	wpabuf_free(conf);
+
+	wpas_dpp_start_gas_server(wpa_s);
+}
+
+
+static void
+wpas_dpp_rx_reconfig_auth_conf(struct wpa_supplicant *wpa_s, const u8 *src,
+			       const u8 *hdr, const u8 *buf, size_t len,
+			       unsigned int freq)
+{
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+	wpa_printf(MSG_DEBUG, "DPP: Reconfig Authentication Confirm from "
+		   MACSTR, MAC2STR(src));
+
+	if (!auth || !auth->reconfig || auth->configurator) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Reconfig Authentication in progress - drop");
+		return;
+	}
+
+	if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+			   MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+		return;
+	}
+
+	if (dpp_reconfig_auth_conf_rx(auth, hdr, buf, len) < 0)
+		return;
+
+	wpas_dpp_start_gas_client(wpa_s);
+}
+
 #endif /* CONFIG_DPP2 */
 
 
@@ -1791,6 +2082,11 @@
 	struct wpa_ssid *ssid;
 	const u8 *connector, *trans_id, *status;
 	u16 connector_len, trans_id_len, status_len;
+#ifdef CONFIG_DPP2
+	const u8 *version;
+	u16 version_len;
+#endif /* CONFIG_DPP2 */
+	u8 peer_version = 1;
 	struct dpp_introduction intro;
 	struct rsn_pmksa_cache_entry *entry;
 	struct os_time now;
@@ -1891,6 +2187,13 @@
 	os_memcpy(entry->pmk, intro.pmk, intro.pmk_len);
 	entry->pmk_len = intro.pmk_len;
 	entry->akmp = WPA_KEY_MGMT_DPP;
+#ifdef CONFIG_DPP2
+	version = dpp_get_attr(buf, len, DPP_ATTR_PROTOCOL_VERSION,
+			       &version_len);
+	if (version && version_len >= 1)
+		peer_version = version[0];
+	entry->dpp_pfs = peer_version >= 2;
+#endif /* CONFIG_DPP2 */
 	if (expiry) {
 		os_get_time(&now);
 		seconds = expiry - now.sec;
@@ -1904,7 +2207,7 @@
 	wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry);
 
 	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
-		" status=%u", MAC2STR(src), status[0]);
+		" status=%u version=%u", MAC2STR(src), status[0], peer_version);
 
 	wpa_printf(MSG_DEBUG,
 		   "DPP: Try connection again after successful network introduction");
@@ -2330,6 +2633,19 @@
 		wpas_dpp_rx_presence_announcement(wpa_s, src, hdr, buf, len,
 						  freq);
 		break;
+	case DPP_PA_RECONFIG_ANNOUNCEMENT:
+		wpas_dpp_rx_reconfig_announcement(wpa_s, src, hdr, buf, len,
+						  freq);
+		break;
+	case DPP_PA_RECONFIG_AUTH_REQ:
+		wpas_dpp_rx_reconfig_auth_req(wpa_s, src, hdr, buf, len, freq);
+		break;
+	case DPP_PA_RECONFIG_AUTH_RESP:
+		wpas_dpp_rx_reconfig_auth_resp(wpa_s, src, hdr, buf, len, freq);
+		break;
+	case DPP_PA_RECONFIG_AUTH_CONF:
+		wpas_dpp_rx_reconfig_auth_conf(wpa_s, src, hdr, buf, len, freq);
+		break;
 #endif /* CONFIG_DPP2 */
 	default:
 		wpa_printf(MSG_DEBUG,
@@ -2360,7 +2676,7 @@
 
 	wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR,
 		   MAC2STR(sa));
-	if (!auth || !auth->auth_success ||
+	if (!auth || (!auth->auth_success && !auth->reconfig_success) ||
 	    os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
 		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
 		return NULL;
@@ -2501,6 +2817,7 @@
 	unsigned int wait_time;
 	const u8 *rsn;
 	struct wpa_ie_data ied;
+	size_t len;
 
 	if (!(ssid->key_mgmt & WPA_KEY_MGMT_DPP) || !bss)
 		return 0; /* Not using DPP AKM - continue */
@@ -2534,8 +2851,11 @@
 		   "DPP: Starting network introduction protocol to derive PMKSA for "
 		   MACSTR, MAC2STR(bss->bssid));
 
-	msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_REQ,
-			    5 + 4 + os_strlen(ssid->dpp_connector));
+	len = 5 + 4 + os_strlen(ssid->dpp_connector);
+#ifdef CONFIG_DPP2
+	len += 5;
+#endif /* CONFIG_DPP2 */
+	msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_REQ, len);
 	if (!msg)
 		return -1;
 
@@ -2590,6 +2910,15 @@
 skip_connector:
 #endif /* CONFIG_TESTING_OPTIONS */
 
+#ifdef CONFIG_DPP2
+	if (DPP_VERSION > 1) {
+		/* Protocol Version */
+		wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+		wpabuf_put_le16(msg, 1);
+		wpabuf_put_u8(msg, DPP_VERSION);
+	}
+#endif /* CONFIG_DPP2 */
+
 	/* TODO: Timeout on AP response */
 	wait_time = wpa_s->max_remain_on_chan;
 	if (wait_time > 2000)
@@ -2792,6 +3121,8 @@
 	eloop_cancel_timeout(wpas_dpp_conn_status_result_wait_timeout,
 			     wpa_s, NULL);
 	eloop_cancel_timeout(wpas_dpp_conn_status_result_timeout, wpa_s, NULL);
+	eloop_cancel_timeout(wpas_dpp_reconfig_reply_wait_timeout,
+			     wpa_s, NULL);
 	dpp_pfs_free(wpa_s->dpp_pfs);
 	wpa_s->dpp_pfs = NULL;
 	wpas_dpp_chirp_stop(wpa_s);
@@ -2814,12 +3145,27 @@
 	const char *pos;
 
 	os_memset(&config, 0, sizeof(config));
+	config.allowed_roles = DPP_CAPAB_ENROLLEE | DPP_CAPAB_CONFIGURATOR;
 	if (cmd) {
 		pos = os_strstr(cmd, " tcp_port=");
 		if (pos) {
 			pos += 10;
 			config.tcp_port = atoi(pos);
 		}
+
+		pos = os_strstr(cmd, " role=");
+		if (pos) {
+			pos += 6;
+			if (os_strncmp(pos, "configurator", 12) == 0)
+				config.allowed_roles = DPP_CAPAB_CONFIGURATOR;
+			else if (os_strncmp(pos, "enrollee", 8) == 0)
+				config.allowed_roles = DPP_CAPAB_ENROLLEE;
+			else if (os_strncmp(pos, "either", 6) == 0)
+				config.allowed_roles = DPP_CAPAB_CONFIGURATOR |
+					DPP_CAPAB_ENROLLEE;
+			else
+				return -1;
+		}
 	}
 	config.configurator_params = wpa_s->dpp_configurator_params;
 	return dpp_controller_start(wpa_s->dpp, &config);
@@ -2862,15 +3208,24 @@
 
 static void wpas_dpp_chirp_start(struct wpa_supplicant *wpa_s)
 {
+	struct wpabuf *msg;
+	int type;
+
+	msg = wpa_s->dpp_presence_announcement;
+	type = DPP_PA_PRESENCE_ANNOUNCEMENT;
+	if (!msg) {
+		msg = wpa_s->dpp_reconfig_announcement;
+		if (!msg)
+			return;
+		type = DPP_PA_RECONFIG_ANNOUNCEMENT;
+	}
 	wpa_printf(MSG_DEBUG, "DPP: Chirp on %d MHz", wpa_s->dpp_chirp_freq);
 	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
-		MAC2STR(broadcast), wpa_s->dpp_chirp_freq,
-		DPP_PA_PRESENCE_ANNOUNCEMENT);
+		MAC2STR(broadcast), wpa_s->dpp_chirp_freq, type);
 	if (offchannel_send_action(
 		    wpa_s, wpa_s->dpp_chirp_freq, broadcast,
 		    wpa_s->own_addr, broadcast,
-		    wpabuf_head(wpa_s->dpp_presence_announcement),
-		    wpabuf_len(wpa_s->dpp_presence_announcement),
+		    wpabuf_head(msg), wpabuf_len(msg),
 		    2000, wpas_dpp_chirp_tx_status, 0) < 0)
 		wpas_dpp_chirp_stop(wpa_s);
 }
@@ -2885,7 +3240,7 @@
 	int c;
 	struct wpa_bss *bss;
 
-	if (!bi)
+	if (!bi && !wpa_s->dpp_reconfig_announcement)
 		return;
 
 	wpa_s->dpp_chirp_scan_done = 1;
@@ -2894,8 +3249,11 @@
 	wpa_s->dpp_chirp_freqs = NULL;
 
 	/* Channels from own bootstrapping info */
-	for (i = 0; i < bi->num_freq; i++)
-		int_array_add_unique(&wpa_s->dpp_chirp_freqs, bi->freq[i]);
+	if (bi) {
+		for (i = 0; i < bi->num_freq; i++)
+			int_array_add_unique(&wpa_s->dpp_chirp_freqs,
+					     bi->freq[i]);
+	}
 
 	/* Preferred chirping channels */
 	int_array_add_unique(&wpa_s->dpp_chirp_freqs, 2437);
@@ -3047,7 +3405,7 @@
 	pos = os_strstr(cmd, " listen=");
 	if (pos) {
 		listen_freq = atoi(pos + 8);
-		if (iter <= 0)
+		if (listen_freq <= 0)
 			return -1;
 	}
 
@@ -3069,13 +3427,16 @@
 
 void wpas_dpp_chirp_stop(struct wpa_supplicant *wpa_s)
 {
-	if (wpa_s->dpp_presence_announcement) {
+	if (wpa_s->dpp_presence_announcement ||
+	    wpa_s->dpp_reconfig_announcement) {
 		offchannel_send_action_done(wpa_s);
 		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CHIRP_STOPPED);
 	}
 	wpa_s->dpp_chirp_bi = NULL;
 	wpabuf_free(wpa_s->dpp_presence_announcement);
 	wpa_s->dpp_presence_announcement = NULL;
+	wpabuf_free(wpa_s->dpp_reconfig_announcement);
+	wpa_s->dpp_reconfig_announcement = NULL;
 	if (wpa_s->dpp_chirp_listen)
 		wpas_dpp_listen_stop(wpa_s);
 	wpa_s->dpp_chirp_listen = 0;
@@ -3090,4 +3451,29 @@
 	}
 }
 
+
+int wpas_dpp_reconfig(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+	if (!ssid->dpp_connector || !ssid->dpp_netaccesskey ||
+	    !ssid->dpp_csign)
+		return -1;
+
+	wpas_dpp_chirp_stop(wpa_s);
+	wpa_s->dpp_allowed_roles = DPP_CAPAB_ENROLLEE;
+	wpa_s->dpp_qr_mutual = 0;
+	wpa_s->dpp_reconfig_announcement =
+		dpp_build_reconfig_announcement(ssid->dpp_csign,
+						ssid->dpp_csign_len);
+	if (!wpa_s->dpp_reconfig_announcement)
+		return -1;
+	wpa_s->dpp_reconfig_ssid = ssid;
+	wpa_s->dpp_reconfig_ssid_id = ssid->id;
+	wpa_s->dpp_chirp_iter = 1;
+	wpa_s->dpp_chirp_round = 0;
+	wpa_s->dpp_chirp_scan_done = 0;
+	wpa_s->dpp_chirp_listen = 0;
+
+	return eloop_register_timeout(0, 0, wpas_dpp_chirp_next, wpa_s, NULL);
+}
+
 #endif /* CONFIG_DPP2 */
diff --git a/wpa_supplicant/dpp_supplicant.h b/wpa_supplicant/dpp_supplicant.h
index 2ce378d..2dc86e0 100644
--- a/wpa_supplicant/dpp_supplicant.h
+++ b/wpa_supplicant/dpp_supplicant.h
@@ -19,6 +19,8 @@
 int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd);
 int wpas_dpp_listen(struct wpa_supplicant *wpa_s, const char *cmd);
 void wpas_dpp_listen_stop(struct wpa_supplicant *wpa_s);
+void wpas_dpp_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+				   unsigned int freq, unsigned int duration);
 void wpas_dpp_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
 					  unsigned int freq);
 void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src,
@@ -37,5 +39,6 @@
 				      enum dpp_status_error result);
 int wpas_dpp_chirp(struct wpa_supplicant *wpa_s, const char *cmd);
 void wpas_dpp_chirp_stop(struct wpa_supplicant *wpa_s);
+int wpas_dpp_reconfig(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
 
 #endif /* DPP_SUPPLICANT_H */
diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
index 9f69736..11eee98 100644
--- a/wpa_supplicant/eapol_test.c
+++ b/wpa_supplicant/eapol_test.c
@@ -674,10 +674,8 @@
 	os_free(e->radius_conf);
 	e->radius_conf = NULL;
 	scard_deinit(wpa_s->scard);
-	if (wpa_s->ctrl_iface) {
-		wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
-		wpa_s->ctrl_iface = NULL;
-	}
+	wpa_supplicant_ctrl_iface_deinit(wpa_s, wpa_s->ctrl_iface);
+	wpa_s->ctrl_iface = NULL;
 
 	ext_password_deinit(wpa_s->ext_pw);
 	wpa_s->ext_pw = NULL;
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index f531c39..096fd02 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -2727,7 +2727,8 @@
 
 #ifdef CONFIG_DPP2
 	wpa_sm_set_dpp_z(wpa_s->wpa, NULL);
-	if (wpa_s->key_mgmt == WPA_KEY_MGMT_DPP && wpa_s->dpp_pfs) {
+	if (DPP_VERSION > 1 && wpa_s->key_mgmt == WPA_KEY_MGMT_DPP &&
+	    wpa_s->dpp_pfs) {
 		struct ieee802_11_elems elems;
 
 		if (ieee802_11_parse_elems(data->assoc_info.resp_ies,
@@ -4364,8 +4365,8 @@
 			data->assoc_reject.timeout_reason ?
 			data->assoc_reject.timeout_reason : "");
 	wpa_s->assoc_status_code = data->assoc_reject.status_code;
-	wpa_s->assoc_timed_out = data->assoc_reject.timed_out;
-	wpas_notify_assoc_status_code(wpa_s);
+	wpas_notify_assoc_status_code(wpa_s,
+				      bssid, data->assoc_reject.timed_out);
 
 #ifdef CONFIG_OWE
 	if (data->assoc_reject.status_code ==
@@ -4398,7 +4399,7 @@
 	 * the status code defined in the DPP R2 tech spec.
 	 * WLAN_STATUS_AKMP_NOT_VALID is addressed in the same manner as an
 	 * interoperability workaround with older hostapd implementation. */
-	if (wpa_s->current_ssid &&
+	if (DPP_VERSION > 1 && wpa_s->current_ssid &&
 	    wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_DPP &&
 	    wpa_s->current_ssid->dpp_pfs == 0 &&
 	    (data->assoc_reject.status_code ==
@@ -4585,6 +4586,11 @@
 			break;
 		}
 #endif /* CONFIG_TESTING_OPTIONS */
+		if (wpa_s->disconnected) {
+			wpa_printf(MSG_INFO,
+				   "Ignore unexpected EVENT_ASSOC in disconnected state");
+			break;
+		}
 		wpa_supplicant_event_assoc(wpa_s, data);
 		wpa_s->assoc_status_code = WLAN_STATUS_SUCCESS;
 		if (data &&
@@ -5029,6 +5035,11 @@
 		wpas_p2p_remain_on_channel_cb(
 			wpa_s, data->remain_on_channel.freq,
 			data->remain_on_channel.duration);
+#ifdef CONFIG_DPP
+		wpas_dpp_remain_on_channel_cb(
+			wpa_s, data->remain_on_channel.freq,
+			data->remain_on_channel.duration);
+#endif /* CONFIG_DPP */
 		break;
 	case EVENT_CANCEL_REMAIN_ON_CHANNEL:
 #ifdef CONFIG_OFFCHANNEL
diff --git a/wpa_supplicant/examples/dpp-nfc.py b/wpa_supplicant/examples/dpp-nfc.py
index f49da34..1883545 100755
--- a/wpa_supplicant/examples/dpp-nfc.py
+++ b/wpa_supplicant/examples/dpp-nfc.py
@@ -8,6 +8,7 @@
 # See README for more details.
 
 import os
+import struct
 import sys
 import time
 import threading
@@ -18,7 +19,7 @@
 
 import logging
 
-scriptsdir = os.path.dirname(os.path.realpath("dpp-nfc.py"))
+scriptsdir = os.path.dirname(os.path.realpath(sys.modules[__name__].__file__))
 sys.path.append(os.path.join(scriptsdir, '..', '..', 'wpaspy'))
 import wpaspy
 
@@ -33,12 +34,18 @@
 terminate_now = False
 summary_file = None
 success_file = None
+my_crn_ready = False
+my_crn = None
+peer_crn = None
+hs_sent = False
+mutex = threading.Lock()
 
 def summary(txt):
-    print(txt)
-    if summary_file:
-        with open(summary_file, 'a') as f:
-            f.write(txt + "\n")
+    with mutex:
+        print(txt)
+        if summary_file:
+            with open(summary_file, 'a') as f:
+                f.write(txt + "\n")
 
 def success_report(txt):
     summary(txt)
@@ -52,11 +59,11 @@
         try:
             ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
         except OSError as error:
-            print("Could not find wpa_supplicant: ", error)
+            summary("Could not find wpa_supplicant: %s", str(error))
             return None
 
     if len(ifaces) < 1:
-        print("No wpa_supplicant control interface found")
+        summary("No wpa_supplicant control interface found")
         return None
 
     for ctrl in ifaces:
@@ -64,7 +71,7 @@
             if ifname not in ctrl:
                 continue
         try:
-            print("Trying to use control interface " + ctrl)
+            summary("Trying to use control interface " + ctrl)
             wpas = wpaspy.Ctrl(ctrl)
             return wpas
         except Exception as e:
@@ -77,38 +84,46 @@
         return False
     peer_id = wpas.request("DPP_NFC_URI " + uri)
     if "FAIL" in peer_id:
-        print("Could not parse DPP URI from NFC URI record")
+        summary("Could not parse DPP URI from NFC URI record")
         return False
     peer_id = int(peer_id)
-    print("peer_id=%d" % peer_id)
+    summary("peer_id=%d for URI from NFC Tag: %s" % (peer_id, uri))
     cmd = "DPP_AUTH_INIT peer=%d" % peer_id
+    global enrollee_only, configurator_only, config_params
+    if enrollee_only:
+        cmd += " role=enrollee"
+    elif configurator_only:
+        cmd += " role=configurator"
+    if config_params:
+        cmd += " " + config_params
+    summary("Initiate DPP authentication: " + cmd)
     res = wpas.request(cmd)
     if "OK" not in res:
-        print("Failed to initiate DPP Authentication")
+        summary("Failed to initiate DPP Authentication")
         return False
-    print("DPP Authentication initiated")
+    summary("DPP Authentication initiated")
     return True
 
 def dpp_hs_tag_read(record):
     wpas = wpas_connect()
     if wpas is None:
         return False
-    print(record)
+    summary(record)
     if len(record.data) < 5:
-        print("Too short DPP HS")
+        summary("Too short DPP HS")
         return False
     if record.data[0] != 0:
-        print("Unexpected URI Identifier Code")
+        summary("Unexpected URI Identifier Code")
         return False
     uribuf = record.data[1:]
     try:
         uri = uribuf.decode()
     except:
-        print("Invalid URI payload")
+        summary("Invalid URI payload")
         return False
-    print("URI: " + uri)
+    summary("URI: " + uri)
     if not uri.startswith("DPP:"):
-        print("Not a DPP URI")
+        summary("Not a DPP URI")
         return False
     return dpp_nfc_uri_process(uri)
 
@@ -124,7 +139,7 @@
         try:
             [name, value] = l.split('=', 1)
         except ValueError:
-            logger.info("Ignore unexpected status line: " + l)
+            summary("Ignore unexpected status line: %s" % l)
             continue
         vals[name] = value
     return vals
@@ -136,7 +151,10 @@
     return None
 
 def own_addr(wpas):
-    return get_status_field(wpas, "address")
+    addr = get_status_field(wpas, "address")
+    if addr is None:
+        addr = get_status_field(wpas, "bssid[0]")
+    return addr
 
 def dpp_bootstrap_gen(wpas, type="qrcode", chan=None, mac=None, info=None,
                       curve=None, key=None):
@@ -146,7 +164,10 @@
     if mac:
         if mac is True:
             mac = own_addr(wpas)
-        cmd += " mac=" + mac.replace(':', '')
+        if mac is None:
+            summary("Could not determine local MAC address for bootstrap info")
+        else:
+            cmd += " mac=" + mac.replace(':', '')
     if info:
         cmd += " info=" + info
     if curve:
@@ -158,12 +179,23 @@
         raise Exception("Failed to generate bootstrapping info")
     return int(res)
 
-def wpas_get_nfc_uri(start_listen=True):
+def wpas_get_nfc_uri(start_listen=True, pick_channel=False):
     wpas = wpas_connect()
     if wpas is None:
         return None
     global own_id, chanlist
-    own_id = dpp_bootstrap_gen(wpas, type="nfc-uri", chan=chanlist, mac=True)
+    chan = chanlist
+    if chan is None and get_status_field(wpas, "bssid[0]"):
+        freq = get_status_field(wpas, "freq")
+        if freq:
+            freq = int(freq)
+            if freq >= 2412 and freq <= 2462:
+                chan = "81/%d" % ((freq - 2407) / 5)
+                summary("Use current AP operating channel (%d MHz) as the URI channel list (%s)" % (freq, chan))
+    if chan is None and pick_channel:
+        chan = "81/6"
+        summary("Use channel 2437 MHz since no other preference provided")
+    own_id = dpp_bootstrap_gen(wpas, type="nfc-uri", chan=chan, mac=True)
     res = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % own_id).rstrip()
     if "FAIL" in res:
         return None
@@ -189,14 +221,22 @@
 
 def dpp_handover_client(llc):
     uri = wpas_get_nfc_uri(start_listen=False)
+    if uri is None:
+        summary("Cannot start handover client - no bootstrap URI available")
+        return
     uri = ndef.UriRecord(uri)
-    print("NFC URI record for DPP: " + str(uri))
+    summary("NFC URI record for DPP: " + str(uri))
     carrier = ndef.Record('application/vnd.wfa.dpp', 'A', uri.data)
-    hr = ndef.HandoverRequestRecord(version="1.4", crn=os.urandom(2))
+    crn = os.urandom(2)
+    hr = ndef.HandoverRequestRecord(version="1.4", crn=crn)
     hr.add_alternative_carrier('active', carrier.name)
     message = [hr, carrier]
-    print("NFC Handover Request message for DPP: " + str(message))
+    summary("NFC Handover Request message for DPP: " + str(message))
 
+    global peer_crn
+    if peer_crn is not None:
+        summary("NFC handover request from peer was already received - do not send own")
+        return
     client = nfc.handover.HandoverClient(llc)
     try:
         summary("Trying to initiate NFC connection handover")
@@ -211,76 +251,108 @@
         client.close()
         return
 
+    if peer_crn is not None:
+        summary("NFC handover request from peer was already received - do not send own")
+        client.close()
+        return
+
     summary("Sending handover request")
 
+    global my_crn, my_crn_ready, hs_sent
+    my_crn_ready = True
+
     if not client.send_records(message):
+        my_crn_ready = False
         summary("Failed to send handover request")
         client.close()
         return
 
+    my_crn, = struct.unpack('>H', crn)
+
     summary("Receiving handover response")
-    message = client.recv_records(timeout=3.0)
+    try:
+        message = client.recv_records(timeout=3.0)
+    except Exception as e:
+        # This is fine if we are the handover selector
+        if hs_sent:
+            summary("Client receive failed as expected since I'm the handover server: %s" % str(e))
+        else:
+            summary("Client receive failed: %s" % str(e))
+        message = None
     if message is None:
-        summary("No response received")
+        if hs_sent:
+            summary("No response received as expected since I'm the handover server")
+        else:
+            summary("No response received")
         client.close()
         return
-    print("Received message: " + str(message))
+    summary("Received message: " + str(message))
     if len(message) < 1 or \
        not isinstance(message[0], ndef.HandoverSelectRecord):
         summary("Response was not Hs - received: " + message.type)
         client.close()
         return
 
-    print("Received message")
-    print("alternative carriers: " + str(message[0].alternative_carriers))
+    summary("Received handover select message")
+    summary("alternative carriers: " + str(message[0].alternative_carriers))
 
     dpp_found = False
     for carrier in message:
         if isinstance(carrier, ndef.HandoverSelectRecord):
             continue
-        print("Remote carrier type: " + carrier.type)
+        summary("Remote carrier type: " + carrier.type)
         if carrier.type == "application/vnd.wfa.dpp":
             if len(carrier.data) == 0 or carrier.data[0] != 0:
-                print("URI Identifier Code 'None' not seen")
+                summary("URI Identifier Code 'None' not seen")
                 continue
-            print("DPP carrier type match - send to wpa_supplicant")
+            summary("DPP carrier type match - send to wpa_supplicant")
             dpp_found = True
             uri = carrier.data[1:].decode("utf-8")
-            print("DPP URI: " + uri)
+            summary("DPP URI: " + uri)
             res = wpas_report_handover_sel(uri)
             if res is None or "FAIL" in res:
                 summary("DPP handover report rejected")
                 break
 
             success_report("DPP handover reported successfully (initiator)")
-            print("peer_id=" + res)
+            summary("peer_id=" + res)
             peer_id = int(res)
-            # TODO: Single Configurator instance
             wpas = wpas_connect()
             if wpas is None:
                 break
-            res = wpas.request("DPP_CONFIGURATOR_ADD")
-            if "FAIL" in res:
-                print("Failed to initiate Configurator")
-                break
-            conf_id = int(res)
+
+            global enrollee_only
+            global config_params
+            if enrollee_only:
+                extra = " role=enrollee"
+            elif config_params:
+                extra = " role=configurator " + config_params
+            else:
+                # TODO: Single Configurator instance
+                res = wpas.request("DPP_CONFIGURATOR_ADD")
+                if "FAIL" in res:
+                    summary("Failed to initiate Configurator")
+                    break
+                conf_id = int(res)
+                extra = " conf=sta-dpp configurator=%d" % conf_id
             global own_id
-            print("Initiate DPP authentication")
-            cmd = "DPP_AUTH_INIT peer=%d own=%d conf=sta-dpp configurator=%d" % (peer_id, own_id, conf_id)
+            summary("Initiate DPP authentication")
+            cmd = "DPP_AUTH_INIT peer=%d own=%d" % (peer_id, own_id)
+            cmd += extra
             res = wpas.request(cmd)
             if "FAIL" in res:
-                print("Failed to initiate DPP authentication")
+                summary("Failed to initiate DPP authentication")
             break
 
     if not dpp_found:
-        print("DPP carrier not seen in response - allow peer to initiate a new handover with different parameters")
+        summary("DPP carrier not seen in response - allow peer to initiate a new handover with different parameters")
         client.close()
-        print("Returning from dpp_handover_client")
+        summary("Returning from dpp_handover_client")
         return
 
-    print("Remove peer")
+    summary("Remove peer")
     client.close()
-    print("Done with handover")
+    summary("Done with handover")
     global only_one
     if only_one:
         print("only_one -> stop loop")
@@ -293,7 +365,7 @@
         global terminate_now
         terminate_now = True
 
-    print("Returning from dpp_handover_client")
+    summary("Returning from dpp_handover_client")
 
 class HandoverServer(nfc.handover.HandoverServer):
     def __init__(self, llc):
@@ -305,10 +377,45 @@
 
     def process_handover_request_message(self, records):
         self.ho_server_processing = True
+        global in_raw_mode
+        was_in_raw_mode = in_raw_mode
         clear_raw_mode()
-        print("\nHandoverServer - request received: " + str(records))
+        if was_in_raw_mode:
+            print("\n")
+        summary("HandoverServer - request received: " + str(records))
 
-        carrier = None
+        global my_crn, peer_crn, my_crn_ready
+
+        for carrier in records:
+            if not isinstance(carrier, ndef.HandoverRequestRecord):
+                continue
+            if carrier.collision_resolution_number:
+                peer_crn = carrier.collision_resolution_number
+                summary("peer_crn: %d" % peer_crn)
+
+        if my_crn is None and my_crn_ready:
+            summary("Still trying to send own handover request - wait a moment to see if that succeeds before checking crn values")
+            for i in range(10):
+                if my_crn is not None:
+                    break
+                time.sleep(0.01)
+        if my_crn is not None:
+            summary("my_crn: %d" % my_crn)
+
+        if my_crn is not None and peer_crn is not None:
+            if my_crn == peer_crn:
+                summary("Same crn used - automatic collision resolution failed")
+                # TODO: Should generate a new Handover Request message
+                return ''
+            if ((my_crn & 1) == (peer_crn & 1) and my_crn > peer_crn) or \
+               ((my_crn & 1) != (peer_crn & 1) and my_crn < peer_crn):
+                summary("I'm the Handover Selector Device")
+                pass
+            else:
+                summary("Peer is the Handover Selector device")
+                summary("Ignore the received request.")
+                return ''
+
         hs = ndef.HandoverSelectRecord('1.4')
         sel = [hs]
 
@@ -317,25 +424,24 @@
         for carrier in records:
             if isinstance(carrier, ndef.HandoverRequestRecord):
                 continue
-            print("Remote carrier type: " + carrier.type)
+            summary("Remote carrier type: " + carrier.type)
             if carrier.type == "application/vnd.wfa.dpp":
-                print("DPP carrier type match - add DPP carrier record")
+                summary("DPP carrier type match - add DPP carrier record")
                 if len(carrier.data) == 0 or carrier.data[0] != 0:
-                    print("URI Identifier Code 'None' not seen")
+                    summary("URI Identifier Code 'None' not seen")
                     continue
                 uri = carrier.data[1:].decode("utf-8")
-                print("Received DPP URI: " + uri)
+                summary("Received DPP URI: " + uri)
 
-                data = wpas_get_nfc_uri(start_listen=False)
-                print("Own URI (pre-processing): %s" % data)
+                data = wpas_get_nfc_uri(start_listen=False, pick_channel=True)
+                summary("Own URI (pre-processing): %s" % data)
 
                 res = wpas_report_handover_req(uri)
                 if res is None or "FAIL" in res:
-                    print("DPP handover request processing failed")
+                    summary("DPP handover request processing failed")
                     continue
 
                 found = True
-                self.received_carrier = carrier
 
                 wpas = wpas_connect()
                 if wpas is None:
@@ -344,25 +450,42 @@
                 data = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % own_id).rstrip()
                 if "FAIL" in data:
                     continue
-                print("Own URI (post-processing): %s" % data)
+                summary("Own URI (post-processing): %s" % data)
                 uri = ndef.UriRecord(data)
-                print("Own bootstrapping NFC URI record: " + str(uri))
+                summary("Own bootstrapping NFC URI record: " + str(uri))
 
                 info = wpas.request("DPP_BOOTSTRAP_INFO %d" % own_id)
                 freq = None
                 for line in info.splitlines():
                     if line.startswith("use_freq="):
                         freq = int(line.split('=')[1])
-                if freq is None:
-                    print("No channel negotiated over NFC - use channel 1")
-                    freq = 2412
-                res = wpas.request("DPP_LISTEN %d" % freq)
+                if freq is None or freq == 0:
+                    summary("No channel negotiated over NFC - use channel 6")
+                    freq = 2437
+                else:
+                    summary("Negotiated channel: %d MHz" % freq)
+                if get_status_field(wpas, "bssid[0]"):
+                    summary("Own AP freq: %s MHz" % str(get_status_field(wpas, "freq")))
+                    if get_status_field(wpas, "beacon_set", extra="DRIVER") is None:
+                        summary("Enable beaconing to have radio ready for RX")
+                        wpas.request("DISABLE")
+                        wpas.request("SET start_disabled 0")
+                        wpas.request("ENABLE")
+                cmd = "DPP_LISTEN %d" % freq
+                global enrollee_only
+                global configurator_only
+                if enrollee_only:
+                    cmd += " role=enrollee"
+                elif configurator_only:
+                    cmd += " role=configurator"
+                summary(cmd)
+                res = wpas.request(cmd)
                 if "OK" not in res:
-                    print("Failed to start DPP listen")
+                    summary("Failed to start DPP listen")
                     break
 
                 carrier = ndef.Record('application/vnd.wfa.dpp', 'A', uri.data)
-                print("Own DPP carrier record: " + str(carrier))
+                summary("Own DPP carrier record: " + str(carrier))
                 hs.add_alternative_carrier('active', carrier.name)
                 sel = [hs, carrier]
                 break
@@ -372,6 +495,8 @@
             self.success = True
         else:
             self.try_own = True
+        global hs_sent
+        hs_sent = True
         return sel
 
 def clear_raw_mode():
@@ -403,22 +528,22 @@
 def dpp_tag_read(tag):
     success = False
     for record in tag.ndef.records:
-        print(record)
-        print("record type " + record.type)
+        summary(record)
+        summary("record type " + record.type)
         if record.type == "application/vnd.wfa.dpp":
             summary("DPP HS tag - send to wpa_supplicant")
             success = dpp_hs_tag_read(record)
             break
         if isinstance(record, ndef.UriRecord):
-            print("URI record: uri=" + record.uri)
-            print("URI record: iri=" + record.iri)
+            summary("URI record: uri=" + record.uri)
+            summary("URI record: iri=" + record.iri)
             if record.iri.startswith("DPP:"):
-                print("DPP URI")
+                summary("DPP URI")
                 if not dpp_nfc_uri_process(record.iri):
                     break
                 success = True
             else:
-                print("Ignore unknown URI")
+                summary("Ignore unknown URI")
             break
 
     if success:
@@ -429,15 +554,15 @@
 def rdwr_connected_write_tag(tag):
     summary("Tag found - writing - " + str(tag))
     if not tag.ndef.is_writeable:
-        print("Not a writable tag")
+        summary("Not a writable tag")
         return
     global dpp_tag_data
     if tag.ndef.capacity < len(dpp_tag_data):
-        print("Not enough room for the message")
+        summary("Not enough room for the message")
         return
     tag.ndef.records = dpp_tag_data
     success_report("Tag write succeeded")
-    print("Done - remove tag")
+    summary("Done - remove tag")
     global only_one
     if only_one:
         global continue_loop
@@ -446,7 +571,7 @@
     return dpp_sel_wait_remove
 
 def write_nfc_uri(clf, wait_remove=True):
-    print("Write NFC URI record")
+    summary("Write NFC URI record")
     data = wpas_get_nfc_uri()
     if data is None:
         summary("Could not get NFC URI from wpa_supplicant")
@@ -454,17 +579,17 @@
 
     global dpp_sel_wait_remove
     dpp_sel_wait_remove = wait_remove
-    print("URI: %s" % data)
+    summary("URI: %s" % data)
     uri = ndef.UriRecord(data)
-    print(uri)
+    summary(uri)
 
-    print("Touch an NFC tag")
+    summary("Touch an NFC tag")
     global dpp_tag_data
     dpp_tag_data = [uri]
     clf.connect(rdwr={'on-connect': rdwr_connected_write_tag})
 
 def write_nfc_hs(clf, wait_remove=True):
-    print("Write NFC Handover Select record on a tag")
+    summary("Write NFC Handover Select record on a tag")
     data = wpas_get_nfc_uri()
     if data is None:
         summary("Could not get NFC URI from wpa_supplicant")
@@ -472,19 +597,19 @@
 
     global dpp_sel_wait_remove
     dpp_sel_wait_remove = wait_remove
-    print("URI: %s" % data)
+    summary("URI: %s" % data)
     uri = ndef.UriRecord(data)
-    print(uri)
+    summary(uri)
     carrier = ndef.Record('application/vnd.wfa.dpp', 'A', uri.data)
     hs = ndef.HandoverSelectRecord('1.4')
     hs.add_alternative_carrier('active', carrier.name)
-    print(hs)
-    print(carrier)
+    summary(hs)
+    summary(carrier)
 
-    print("Touch an NFC tag")
+    summary("Touch an NFC tag")
     global dpp_tag_data
     dpp_tag_data = [hs, carrier]
-    print(dpp_tag_data)
+    summary(dpp_tag_data)
     clf.connect(rdwr={'on-connect': rdwr_connected_write_tag})
 
 def rdwr_connected(tag):
@@ -492,8 +617,8 @@
     summary("Tag connected: " + str(tag))
 
     if tag.ndef:
-        print("NDEF tag: " + tag.type)
-        print(tag.ndef.records)
+        summary("NDEF tag: " + tag.type)
+        summary(tag.ndef.records)
         success = dpp_tag_read(tag)
         if only_one and success:
             global continue_loop
@@ -507,14 +632,14 @@
 def llcp_worker(llc):
     global init_on_touch
     if init_on_touch:
-        print("Starting handover client")
+        summary("Starting handover client")
         dpp_handover_client(llc)
-        print("Exiting llcp_worker thread (init_in_touch)")
+        summary("Exiting llcp_worker thread (init_in_touch)")
         return
 
     global no_input
     if no_input:
-        print("Wait for handover to complete")
+        summary("Wait for handover to complete")
     else:
         print("Wait for handover to complete - press 'i' to initiate")
     global srv
@@ -522,9 +647,9 @@
     while not wait_connection and srv.sent_carrier is None:
         if srv.try_own:
             srv.try_own = False
-            print("Try to initiate another handover with own parameters")
+            summary("Try to initiate another handover with own parameters")
             dpp_handover_client(llc)
-            print("Exiting llcp_worker thread (retry with own parameters)")
+            summary("Exiting llcp_worker thread (retry with own parameters)")
             return
         if srv.ho_server_processing:
             time.sleep(0.025)
@@ -535,34 +660,40 @@
             if res != 'i':
                 continue
             clear_raw_mode()
-            print("Starting handover client")
+            summary("Starting handover client")
             dpp_handover_client(llc)
-            print("Exiting llcp_worker thread (manual init)")
+            summary("Exiting llcp_worker thread (manual init)")
             return
 
+    global in_raw_mode
+    was_in_raw_mode = in_raw_mode
     clear_raw_mode()
-    print("\rExiting llcp_worker thread")
+    if was_in_raw_mode:
+        print("\r")
+    summary("Exiting llcp_worker thread")
 
 def llcp_startup(llc):
-    print("Start LLCP server")
+    summary("Start LLCP server")
     global srv
     srv = HandoverServer(llc)
     return llc
 
 def llcp_connected(llc):
-    print("P2P LLCP connected")
-    global wait_connection
+    summary("P2P LLCP connected")
+    global wait_connection, my_crn, peer_crn, my_crn_ready, hs_sent
     wait_connection = False
-    global init_on_touch
-    if not init_on_touch:
-        global srv
-        srv.start()
+    my_crn_ready = False
+    my_crn = None
+    peer_crn = None
+    hs_sent = False
+    global srv
+    srv.start()
     if init_on_touch or not no_input:
         threading.Thread(target=llcp_worker, args=(llc,)).start()
     return True
 
 def llcp_release(llc):
-    print("LLCP release")
+    summary("LLCP release")
     return True
 
 def terminate_loop():
@@ -592,17 +723,25 @@
                         help='tag read only (do not allow connection handover)')
     parser.add_argument('--handover-only', action='store_true',
                         help='connection handover only (do not allow tag read)')
+    parser.add_argument('--enrollee', action='store_true',
+                        help='run as Enrollee-only')
+    parser.add_argument('--configurator', action='store_true',
+                        help='run as Configurator-only')
+    parser.add_argument('--config-params', default='',
+                        help='configurator parameters')
+    parser.add_argument('--ctrl', default='/var/run/wpa_supplicant',
+                        help='wpa_supplicant/hostapd control interface')
     parser.add_argument('--summary',
                         help='summary file for writing status updates')
     parser.add_argument('--success',
                         help='success file for writing success update')
     parser.add_argument('--device', default='usb', help='NFC device to open')
-    parser.add_argument('--chan', default='81/1', help='channel list')
+    parser.add_argument('--chan', default=None, help='channel list')
     parser.add_argument('command', choices=['write-nfc-uri',
                                             'write-nfc-hs'],
                         nargs='?')
     args = parser.parse_args()
-    print(args)
+    summary(args)
 
     global only_one
     only_one = args.only_one
@@ -618,10 +757,23 @@
     global init_on_touch
     init_on_touch = args.init_on_touch
 
+    global enrollee_only
+    enrollee_only = args.enrollee
+
+    global configurator_only
+    configurator_only = args.configurator
+
+    global config_params
+    config_params = args.config_params
+
     if args.ifname:
         global ifname
         ifname = args.ifname
-        print("Selected ifname " + ifname)
+        summary("Selected ifname " + ifname)
+
+    if args.ctrl:
+        global wpas_ctrl
+        wpas_ctrl = args.ctrl
 
     if args.summary:
         global summary_file
@@ -640,7 +792,7 @@
 
     try:
         if not clf.open(args.device):
-            print("Could not open connection with an NFC device")
+            summary("Could not open connection with an NFC device")
             raise SystemExit
 
         if args.command == "write-nfc-uri":
@@ -653,8 +805,12 @@
 
         global continue_loop
         while continue_loop:
+            global in_raw_mode
+            was_in_raw_mode = in_raw_mode
             clear_raw_mode()
-            print("\rWaiting for a tag or peer to be touched")
+            if was_in_raw_mode:
+                print("\r")
+            summary("Waiting for a tag or peer to be touched")
             wait_connection = True
             try:
                 if args.tag_read_only:
@@ -674,7 +830,7 @@
                                        terminate=terminate_loop):
                         break
             except Exception as e:
-                print("clf.connect failed: " + str(e))
+                summary("clf.connect failed: " + str(e))
                 break
 
             global srv
diff --git a/wpa_supplicant/hidl/1.3/hidl.cpp b/wpa_supplicant/hidl/1.3/hidl.cpp
index 4c2d434..310e56c 100644
--- a/wpa_supplicant/hidl/1.3/hidl.cpp
+++ b/wpa_supplicant/hidl/1.3/hidl.cpp
@@ -282,7 +282,8 @@
 	hidl_manager->notifyDisconnectReason(wpa_s);
 }
 
-void wpas_hidl_notify_assoc_reject(struct wpa_supplicant *wpa_s)
+void wpas_hidl_notify_assoc_reject(struct wpa_supplicant *wpa_s,
+    const u8 *bssid, u8 timed_out)
 {
 	if (!wpa_s)
 		return;
@@ -295,7 +296,7 @@
 	if (!hidl_manager)
 		return;
 
-	hidl_manager->notifyAssocReject(wpa_s);
+	hidl_manager->notifyAssocReject(wpa_s, bssid, timed_out);
 }
 
 void wpas_hidl_notify_auth_timeout(struct wpa_supplicant *wpa_s)
diff --git a/wpa_supplicant/hidl/1.3/hidl.h b/wpa_supplicant/hidl/1.3/hidl.h
index 304a4d6..bf03bf9 100644
--- a/wpa_supplicant/hidl/1.3/hidl.h
+++ b/wpa_supplicant/hidl/1.3/hidl.h
@@ -50,7 +50,8 @@
 	    struct wpa_supplicant *wpa_s, u8 code, u16 reauth_delay,
 	    const char *url);
 	void wpas_hidl_notify_disconnect_reason(struct wpa_supplicant *wpa_s);
-	void wpas_hidl_notify_assoc_reject(struct wpa_supplicant *wpa_s);
+	void wpas_hidl_notify_assoc_reject(struct wpa_supplicant *wpa_s,
+	    const u8 *bssid, u8 timed_out);
 	void wpas_hidl_notify_auth_timeout(struct wpa_supplicant *wpa_s);
 	void wpas_hidl_notify_bssid_changed(struct wpa_supplicant *wpa_s);
 	void wpas_hidl_notify_wps_event_fail(
@@ -164,7 +165,8 @@
     struct wpa_supplicant *wpa_s, u8 code, u16 reauth_delay, const char *url)
 {}
 static void wpas_hidl_notify_disconnect_reason(struct wpa_supplicant *wpa_s) {}
-static void wpas_hidl_notify_assoc_reject(struct wpa_supplicant *wpa_s) {}
+static void wpas_hidl_notify_assoc_reject(struct wpa_supplicant *wpa_s,
+    const u8 *bssid, u8 timed_out) {}
 static void wpas_hidl_notify_auth_timeout(struct wpa_supplicant *wpa_s) {}
 static void wpas_hidl_notify_wps_event_fail(
     struct wpa_supplicant *wpa_s, uint8_t *peer_macaddr, uint16_t config_error,
diff --git a/wpa_supplicant/hidl/1.3/hidl_manager.cpp b/wpa_supplicant/hidl/1.3/hidl_manager.cpp
index e15e9fd..2734e98 100644
--- a/wpa_supplicant/hidl/1.3/hidl_manager.cpp
+++ b/wpa_supplicant/hidl/1.3/hidl_manager.cpp
@@ -969,8 +969,12 @@
  *
  * @param wpa_s |wpa_supplicant| struct corresponding to the interface on which
  * the network is present.
+ * @param bssid bssid of AP that rejected the association.
+ * @param timed_out flag to indicate failure is due to timeout
+ * (auth, assoc, ...) rather than explicit rejection response from the AP.
  */
-void HidlManager::notifyAssocReject(struct wpa_supplicant *wpa_s)
+void HidlManager::notifyAssocReject(struct wpa_supplicant *wpa_s,
+    const u8 *bssid, u8 timed_out)
 {
 	if (!wpa_s)
 		return;
@@ -979,19 +983,13 @@
 	    sta_iface_object_map_.end())
 		return;
 
-	const u8 *bssid = wpa_s->bssid;
-	if (is_zero_ether_addr(bssid)) {
-		bssid = wpa_s->pending_bssid;
-	}
-
 	callWithEachStaIfaceCallback(
 	    wpa_s->ifname,
 	    std::bind(
 		&ISupplicantStaIfaceCallback::onAssociationRejected,
 		std::placeholders::_1, bssid,
 		static_cast<ISupplicantStaIfaceCallback::StatusCode>(
-		    wpa_s->assoc_status_code),
-		wpa_s->assoc_timed_out == 1));
+		    wpa_s->assoc_status_code), timed_out == 1));
 }
 
 void HidlManager::notifyAuthTimeout(struct wpa_supplicant *wpa_s)
diff --git a/wpa_supplicant/hidl/1.3/hidl_manager.h b/wpa_supplicant/hidl/1.3/hidl_manager.h
index e49e28d..b40d303 100644
--- a/wpa_supplicant/hidl/1.3/hidl_manager.h
+++ b/wpa_supplicant/hidl/1.3/hidl_manager.h
@@ -83,7 +83,8 @@
 	    struct wpa_supplicant *wpa_s, u8 code, u16 reauth_delay,
 	    const char *url);
 	void notifyDisconnectReason(struct wpa_supplicant *wpa_s);
-	void notifyAssocReject(struct wpa_supplicant *wpa_s);
+	void notifyAssocReject(struct wpa_supplicant *wpa_s,
+	    const u8 *bssid, u8 timed_out);
 	void notifyAuthTimeout(struct wpa_supplicant *wpa_s);
 	void notifyBssidChanged(struct wpa_supplicant *wpa_s);
 	void notifyWpsEventFail(
diff --git a/wpa_supplicant/hidl/1.3/sta_network.cpp b/wpa_supplicant/hidl/1.3/sta_network.cpp
index d3b120d..9716b6e 100644
--- a/wpa_supplicant/hidl/1.3/sta_network.cpp
+++ b/wpa_supplicant/hidl/1.3/sta_network.cpp
@@ -2176,6 +2176,11 @@
 		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
 	}
 	setFastTransitionKeyMgmt(key_mgmt_mask);
+
+	if (key_mgmt_mask & WPA_KEY_MGMT_OWE) {
+		// Do not allow to connect to Open network when OWE is selected
+		wpa_ssid->owe_only = 1;
+	}
 	wpa_ssid->key_mgmt = key_mgmt_mask;
 	wpa_printf(MSG_MSGDUMP, "key_mgmt: 0x%x", wpa_ssid->key_mgmt);
 	resetInternalStateAfterParamsUpdate();
@@ -2434,6 +2439,10 @@
  */
 void StaNetwork::setFastTransitionKeyMgmt(uint32_t &key_mgmt_mask)
 {
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	int res;
+	struct wpa_driver_capa capa;
+
 	if (key_mgmt_mask & WPA_KEY_MGMT_PSK) {
 		key_mgmt_mask |= WPA_KEY_MGMT_FT_PSK;
 	}
@@ -2441,6 +2450,19 @@
 	if (key_mgmt_mask & WPA_KEY_MGMT_IEEE8021X) {
 		key_mgmt_mask |= WPA_KEY_MGMT_FT_IEEE8021X;
 	}
+
+	res = wpa_drv_get_capa(wpa_s, &capa);
+	if (res == 0) {
+#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_SAE
+		if ((key_mgmt_mask & WPA_KEY_MGMT_SAE) &&
+		    (capa.key_mgmt_iftype[WPA_IF_STATION] & WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE)) {
+			key_mgmt_mask |= WPA_KEY_MGMT_FT_SAE;
+		}
+#endif
+#endif
+	}
+
 }
 
 /**
@@ -2456,6 +2478,13 @@
 	if (key_mgmt_mask & WPA_KEY_MGMT_IEEE8021X) {
 		key_mgmt_mask &= ~WPA_KEY_MGMT_FT_IEEE8021X;
 	}
+#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_SAE
+	if (key_mgmt_mask & WPA_KEY_MGMT_SAE) {
+		key_mgmt_mask &= ~WPA_KEY_MGMT_FT_SAE;
+	}
+#endif
+#endif
 }
 
 /**
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index c16c2a9..b60f80d 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -946,7 +946,8 @@
 	struct wpa_driver_capa capa;
 
 	res = wpa_drv_get_capa(wpa_s, &capa);
-	if (res == 0 && capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) {
+	if (res == 0 && capa.key_mgmt_iftype[WPA_IF_STATION] &
+	    WPA_DRIVER_CAPA_KEY_MGMT_FT) {
 		key_mgmt = wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ?
 			"WPA-EAP WPA-EAP-SHA256 FT-EAP" :
 			"WPA-EAP FT-EAP";
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 56eb62a..16e747f 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -148,14 +148,15 @@
 }
 
 
-void wpas_notify_assoc_status_code(struct wpa_supplicant *wpa_s)
+void wpas_notify_assoc_status_code(struct wpa_supplicant *wpa_s,
+				   const u8 *bssid, u8 timed_out)
 {
 	if (wpa_s->p2p_mgmt)
 		return;
 
 	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_ASSOC_STATUS_CODE);
 
-	wpas_hidl_notify_assoc_reject(wpa_s);
+	wpas_hidl_notify_assoc_reject(wpa_s, bssid, timed_out);
 }
 
 void wpas_notify_auth_timeout(struct wpa_supplicant *wpa_s) {
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index e9e39ee..0e7991b 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -28,7 +28,8 @@
 			       enum wpa_states old_state);
 void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s);
 void wpas_notify_auth_status_code(struct wpa_supplicant *wpa_s);
-void wpas_notify_assoc_status_code(struct wpa_supplicant *wpa_s);
+void wpas_notify_assoc_status_code(struct wpa_supplicant *wpa_s,
+				   const u8 *bssid, u8 timed_out);
 void wpas_notify_auth_timeout(struct wpa_supplicant *wpa_s);
 void wpas_notify_roam_time(struct wpa_supplicant *wpa_s);
 void wpas_notify_roam_complete(struct wpa_supplicant *wpa_s);
diff --git a/wpa_supplicant/op_classes.c b/wpa_supplicant/op_classes.c
index 983801f..bd97fee 100644
--- a/wpa_supplicant/op_classes.c
+++ b/wpa_supplicant/op_classes.c
@@ -22,13 +22,13 @@
 				       unsigned int *flags)
 {
 	int i;
-	int is_6ghz = op_class >= 131 && op_class <= 135;
+	int is_6ghz = op_class >= 131 && op_class <= 136;
 
 	for (i = 0; i < mode->num_channels; i++) {
 		int chan_is_6ghz;
 
-		chan_is_6ghz = mode->channels[i].freq > 5940 &&
-			mode->channels[i].freq <= 7105;
+		chan_is_6ghz = mode->channels[i].freq >= 5935 &&
+			mode->channels[i].freq <= 7115;
 		if (is_6ghz == chan_is_6ghz && mode->channels[i].chan == chan)
 			break;
 	}
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index b0bea61..2f4682e 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -5961,8 +5961,31 @@
 		} else {
 			if (os_get_random((u8 *) &r, sizeof(r)) < 0)
 				return -1;
-			freq = 5180 + (r % 4) * 20;
-			if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
+
+			/*
+			 * most of 5G channels are DFS, only operating class 115 and 124
+			 * are available possibly, randomly pick a start to check them.
+			 */
+			int possible_5g_freqs[] = {
+				/* operating class 115 */
+				5180, 5200, 5220, 5240,
+				/* operating class 124 */
+				5745, 5765, 5785, 5805,
+			};
+			int possible_5g_freqs_num =
+			    sizeof(possible_5g_freqs)/sizeof(possible_5g_freqs[0]);
+
+			int i;
+			for (i = 0; i < possible_5g_freqs_num; i++, r++) {
+				if (p2p_supported_freq_go(
+				    wpa_s->global->p2p,
+				    possible_5g_freqs[r % possible_5g_freqs_num])) {
+					freq = possible_5g_freqs[r % possible_5g_freqs_num];
+					break;
+				}
+			}
+
+			if (i >= possible_5g_freqs_num) {
 				wpa_printf(MSG_DEBUG, "P2P: Could not select "
 					   "5 GHz channel for P2P group");
 				return -1;
diff --git a/wpa_supplicant/preauth_test.c b/wpa_supplicant/preauth_test.c
index 4a8f4ff..dc955b8 100644
--- a/wpa_supplicant/preauth_test.c
+++ b/wpa_supplicant/preauth_test.c
@@ -192,10 +192,8 @@
 	pmksa_candidate_free(wpa_s->wpa);
 	wpa_sm_deinit(wpa_s->wpa);
 	scard_deinit(wpa_s->scard);
-	if (wpa_s->ctrl_iface) {
-		wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
-		wpa_s->ctrl_iface = NULL;
-	}
+	wpa_supplicant_ctrl_iface_deinit(wpa_s, wpa_s->ctrl_iface);
+	wpa_s->ctrl_iface = NULL;
 	wpa_config_free(wpa_s->conf);
 }
 
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 4eaece0..d06f6e2 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -1646,6 +1646,7 @@
 {
 	struct wpa_driver_associate_params params;
 	struct ieee802_11_elems elems;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
 #ifdef CONFIG_FILS
 	u8 nonces[2 * FILS_NONCE_LEN];
 #endif /* CONFIG_FILS */
@@ -1755,8 +1756,8 @@
 		struct wpabuf *owe_ie;
 		u16 group;
 
-		if (wpa_s->current_ssid && wpa_s->current_ssid->owe_group) {
-			group = wpa_s->current_ssid->owe_group;
+		if (ssid && ssid->owe_group) {
+			group = ssid->owe_group;
 		} else if (wpa_s->assoc_status_code ==
 			   WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) {
 			if (wpa_s->last_owe_group == 19)
@@ -1792,11 +1793,14 @@
 #endif /* CONFIG_OWE */
 
 #ifdef CONFIG_DPP2
-	if (wpa_s->key_mgmt == WPA_KEY_MGMT_DPP && wpa_s->current_ssid &&
-	    wpa_s->current_ssid->dpp_netaccesskey &&
-	    wpa_s->current_ssid->dpp_pfs != 2 &&
-	    !wpa_s->current_ssid->dpp_pfs_fallback) {
-		struct wpa_ssid *ssid = wpa_s->current_ssid;
+	if (DPP_VERSION > 1 && wpa_s->key_mgmt == WPA_KEY_MGMT_DPP && ssid &&
+	    ssid->dpp_netaccesskey && ssid->dpp_pfs != 2 &&
+	    !ssid->dpp_pfs_fallback) {
+		struct rsn_pmksa_cache_entry *pmksa;
+
+		pmksa = pmksa_cache_get_current(wpa_s->wpa);
+		if (!pmksa || !pmksa->dpp_pfs)
+			goto pfs_fail;
 
 		dpp_pfs_free(wpa_s->dpp_pfs);
 		wpa_s->dpp_pfs = dpp_pfs_init(ssid->dpp_netaccesskey,
@@ -1823,7 +1827,7 @@
 pfs_fail:
 #endif /* CONFIG_DPP2 */
 
-	if (wpa_s->current_ssid && wpa_s->current_ssid->multi_ap_backhaul_sta) {
+	if (ssid && ssid->multi_ap_backhaul_sta) {
 		size_t multi_ap_ie_len;
 
 		multi_ap_ie_len = add_multi_ap_ie(
@@ -1843,8 +1847,7 @@
 	params.ssid = wpa_s->sme.ssid;
 	params.ssid_len = wpa_s->sme.ssid_len;
 	params.freq.freq = wpa_s->sme.freq;
-	params.bg_scan_period = wpa_s->current_ssid ?
-		wpa_s->current_ssid->bg_scan_period : -1;
+	params.bg_scan_period = ssid ? ssid->bg_scan_period : -1;
 	params.wpa_ie = wpa_s->sme.assoc_req_ie_len ?
 		wpa_s->sme.assoc_req_ie : NULL;
 	params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
@@ -1860,17 +1863,17 @@
 	os_memset(&htcaps_mask, 0, sizeof(htcaps_mask));
 	params.htcaps = (u8 *) &htcaps;
 	params.htcaps_mask = (u8 *) &htcaps_mask;
-	wpa_supplicant_apply_ht_overrides(wpa_s, wpa_s->current_ssid, &params);
+	wpa_supplicant_apply_ht_overrides(wpa_s, ssid, &params);
 #endif /* CONFIG_HT_OVERRIDES */
 #ifdef CONFIG_VHT_OVERRIDES
 	os_memset(&vhtcaps, 0, sizeof(vhtcaps));
 	os_memset(&vhtcaps_mask, 0, sizeof(vhtcaps_mask));
 	params.vhtcaps = &vhtcaps;
 	params.vhtcaps_mask = &vhtcaps_mask;
-	wpa_supplicant_apply_vht_overrides(wpa_s, wpa_s->current_ssid, &params);
+	wpa_supplicant_apply_vht_overrides(wpa_s, ssid, &params);
 #endif /* CONFIG_VHT_OVERRIDES */
 #ifdef CONFIG_HE_OVERRIDES
-	wpa_supplicant_apply_he_overrides(wpa_s, wpa_s->current_ssid, &params);
+	wpa_supplicant_apply_he_overrides(wpa_s, ssid, &params);
 #endif /* CONFIG_HE_OVERRIDES */
 #ifdef CONFIG_IEEE80211R
 	if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies &&
@@ -1992,7 +1995,7 @@
 				       elems.rsnxe_len + 2);
 	else
 		wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0);
-	if (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group)
+	if (ssid && ssid->p2p_group)
 		params.p2p = 1;
 
 	if (wpa_s->p2pdev->set_sta_uapsd)
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 2e09102..ac04383 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -1451,6 +1451,22 @@
 			wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url);
 	}
 
+#ifdef CONFIG_MBO
+	vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC);
+	if (vendor) {
+		wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]);
+		if (wpa_s->conf->btm_offload) {
+			wpa_msg(wpa_s, MSG_INFO,
+				"WNM: Notify BSS Transition Management Request frame status");
+			wpa_s->bss_tm_status = WNM_BSS_TM_ACCEPT;
+			wpas_notify_bss_tm_status(wpa_s);
+			/* since it could be referenced in the scan result logic, initialize it */
+			wpa_s->wnm_mbo_trans_reason_present = 0;
+			return;
+		}
+	}
+#endif /* CONFIG_MBO */
+
 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
 		wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - "
 			"Disassociation Timer %u", wpa_s->wnm_dissoc_timer);
@@ -1462,12 +1478,6 @@
 		}
 	}
 
-#ifdef CONFIG_MBO
-	vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC);
-	if (vendor)
-		wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]);
-#endif /* CONFIG_MBO */
-
 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
 		unsigned int valid_ms;
 
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 07d5f31..6a2d2c3 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -1706,15 +1706,25 @@
 static int wpa_cli_cmd_get_capability(struct wpa_ctrl *ctrl, int argc,
 				      char *argv[])
 {
-	if (argc < 1 || argc > 2) {
-		printf("Invalid GET_CAPABILITY command: need either one or "
-		       "two arguments\n");
+	if (argc < 1 || argc > 3) {
+		printf("Invalid GET_CAPABILITY command: need at least one argument and max three arguments\n");
 		return -1;
 	}
 
-	if ((argc == 2) && os_strcmp(argv[1], "strict") != 0) {
-		printf("Invalid GET_CAPABILITY command: second argument, "
-		       "if any, must be 'strict'\n");
+	if (argc > 1 && os_strcmp(argv[0], "key_mgmt") != 0 &&
+	    os_strncmp(argv[1], "iftype=", 7) == 0) {
+		printf("Invalid GET_CAPABILITY command: 'iftype=' param is allowed only for 'key_mgmt'\n");
+		return -1;
+	}
+
+	if (argc == 2 && os_strcmp(argv[1], "strict") != 0 &&
+	    os_strncmp(argv[1], "iftype=", 7) != 0) {
+		printf("Invalid GET_CAPABILITY command: the second argument, if any, must be 'strict' OR 'iftype=<iftype_name>'\n");
+		return -1;
+	}
+
+	if (argc == 3 && os_strcmp(argv[2], "strict") != 0) {
+		printf("Invalid GET_CAPABILITY command: the third argument, if any, must be 'strict'\n");
 		return -1;
 	}
 
@@ -1741,7 +1751,13 @@
 		"acs",
 #endif /* CONFIG_ACS */
 	};
+	const char *iftypes[] = {
+		"iftype=STATION", "iftype=AP", "iftype=P2P_CLIENT",
+		"iftype=P2P_GO", "iftype=AP_VLAN", "iftype=IBSS", "iftype=NAN",
+		"iftype=P2P_DEVICE", "iftype=MESH",
+	};
 	int i, num_fields = ARRAY_SIZE(fields);
+	int num_iftypes = ARRAY_SIZE(iftypes);
 	char **res = NULL;
 
 	if (arg == 1) {
@@ -1755,6 +1771,21 @@
 		}
 	}
 	if (arg == 2) {
+		/* the second argument can be "iftype=<iftype_name>" OR
+		 * "strict" */
+		res = os_calloc(num_iftypes + 2, sizeof(char *));
+		if (!res)
+			return NULL;
+		res[0] = os_strdup("strict");
+		if (!res[0])
+			return res;
+		for (i = 0; i < num_iftypes; i++) {
+			res[i + 1] = os_strdup(iftypes[i]);
+			if (!res[i + 1])
+				return res;
+		}
+	}
+	if (arg == 3) {
 		res = os_calloc(1 + 1, sizeof(char *));
 		if (res == NULL)
 			return NULL;
diff --git a/wpa_supplicant/wpa_gui-qt4/icons/Makefile b/wpa_supplicant/wpa_gui-qt4/icons/Makefile
index 709514c..88efc3c 100644
--- a/wpa_supplicant/wpa_gui-qt4/icons/Makefile
+++ b/wpa_supplicant/wpa_gui-qt4/icons/Makefile
@@ -5,14 +5,28 @@
 ICONS := $(addsuffix .png, $(foreach name, $(NAMES), $(foreach size, $(SIZES), $(size)/$(name))))
 ICONS += $(addsuffix .xpm, $(NAMES))
 
+ifeq (1, $(shell which inkscape; echo $$?))
+$(error "No inkscape in PATH, it is required for exporting icons.")
+else
+ifeq (0, $(shell inkscape --without-gui 2>&1 > /dev/null; echo $$?))
+# Inkscape < 1.0
+INKSCAPE_GUI_FLAG := --without-gui
+INKSCAPE_OUTPUT_FLAG := --export-png
+else
+# Inkscape >= 1.0
+INKSCAPE_GUI_FLAG :=
+INKSCAPE_OUTPUT_FLAG := --export-filename
+endif
+endif
+
 all: $(ICONS)
 
 %.png:
 	mkdir -p hicolor/$(word 1, $(subst /, ,$(@)))/apps/
-	inkscape $(subst .png,.svg, $(word 2, $(subst /, , $(@)))) --without-gui \
+	inkscape $(subst .png,.svg, $(word 2, $(subst /, , $(@)))) $(INKSCAPE_GUI_FLAG) \
 		--export-width=$(word 1, $(subst x, , $(@)))  \
 	        --export-height=$(word 2, $(subst x, , $(subst /, , $(@)))) \
-		--export-png=hicolor/$(word 1, $(subst /, ,$(@)))/apps/$(word 2, $(subst /, , $@))
+		$(INKSCAPE_OUTPUT_FLAG)=hicolor/$(word 1, $(subst /, ,$(@)))/apps/$(word 2, $(subst /, , $@))
 
 %.xpm:
 	mkdir -p pixmaps/
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index ea62e59..f928fdb 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -223,10 +223,10 @@
 	wpa_msg(wpa_s, MSG_INFO, "Authentication with " MACSTR " timed out.",
 		MAC2STR(bssid));
 	wpa_blacklist_add(wpa_s, bssid);
+	wpas_notify_auth_timeout(wpa_s);
 	wpa_sm_notify_disassoc(wpa_s->wpa);
 	wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
 	wpa_s->reassociate = 1;
-	wpas_notify_auth_timeout(wpa_s);
 
 	/*
 	 * If we timed out, the AP or the local radio may be busy.
@@ -1137,8 +1137,8 @@
 		    os_strcmp(conf->ctrl_interface,
 			      wpa_s->conf->ctrl_interface) != 0);
 
-	if (reconf_ctrl && wpa_s->ctrl_iface) {
-		wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
+	if (reconf_ctrl) {
+		wpa_supplicant_ctrl_iface_deinit(wpa_s, wpa_s->ctrl_iface);
 		wpa_s->ctrl_iface = NULL;
 	}
 
@@ -1686,6 +1686,9 @@
 	} else if (wpa_s->key_mgmt == WPA_KEY_MGMT_DPP) {
 		/* Use PMK from DPP network introduction (PMKSA entry) */
 		wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
+#ifdef CONFIG_DPP2
+		wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_DPP_PFS, ssid->dpp_pfs);
+#endif /* CONFIG_DPP2 */
 #endif /* CONFIG_DPP */
 	} else if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) {
 		int psk_set = 0;
@@ -2739,9 +2742,9 @@
 #ifdef CONFIG_MBO
 	const u8 *mbo_ie;
 #endif
-#ifdef CONFIG_SAE
-	int sae_pmksa_cached = 0;
-#endif /* CONFIG_SAE */
+#if defined(CONFIG_SAE) || defined(CONFIG_FILS)
+	int pmksa_cached = 0;
+#endif /* CONFIG_SAE || CONFIG_FILS */
 #ifdef CONFIG_FILS
 	const u8 *realm, *username, *rrk;
 	size_t realm_len, username_len, rrk_len;
@@ -2781,9 +2784,9 @@
 					    ssid, try_opportunistic,
 					    cache_id, 0) == 0) {
 			eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
-#ifdef CONFIG_SAE
-			sae_pmksa_cached = 1;
-#endif /* CONFIG_SAE */
+#if defined(CONFIG_SAE) || defined(CONFIG_FILS)
+			pmksa_cached = 1;
+#endif /* CONFIG_SAE || CONFIG_FILS */
 		}
 		wpa_ie_len = max_wpa_ie_len;
 		if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
@@ -2882,6 +2885,10 @@
 
 		if (mask)
 			*mask |= WPA_DRV_UPDATE_FILS_ERP_INFO;
+	} else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD) &&
+		   ssid->eap.erp && wpa_key_mgmt_fils(wpa_s->key_mgmt) &&
+		   pmksa_cached) {
+		algs = WPA_AUTH_ALG_FILS;
 	}
 #endif /* CONFIG_FILS */
 #endif /* IEEE8021X_EAPOL */
@@ -2898,7 +2905,7 @@
 	}
 
 #ifdef CONFIG_SAE
-	if (sae_pmksa_cached && algs == WPA_AUTH_ALG_SAE) {
+	if (pmksa_cached && algs == WPA_AUTH_ALG_SAE) {
 		wpa_dbg(wpa_s, MSG_DEBUG,
 			"SAE: Use WPA_AUTH_ALG_OPEN for PMKSA caching attempt");
 		algs = WPA_AUTH_ALG_OPEN;
@@ -3085,9 +3092,16 @@
 #endif /* CONFIG_OWE */
 
 #ifdef CONFIG_DPP2
-	if (wpa_sm_get_key_mgmt(wpa_s->wpa) == WPA_KEY_MGMT_DPP &&
+	if (DPP_VERSION > 1 &&
+	    wpa_sm_get_key_mgmt(wpa_s->wpa) == WPA_KEY_MGMT_DPP &&
 	    ssid->dpp_netaccesskey &&
 	    ssid->dpp_pfs != 2 && !ssid->dpp_pfs_fallback) {
+		struct rsn_pmksa_cache_entry *pmksa;
+
+		pmksa = pmksa_cache_get_current(wpa_s->wpa);
+		if (!pmksa || !pmksa->dpp_pfs)
+			goto pfs_fail;
+
 		dpp_pfs_free(wpa_s->dpp_pfs);
 		wpa_s->dpp_pfs = dpp_pfs_init(ssid->dpp_netaccesskey,
 					      ssid->dpp_netaccesskey_len);
@@ -6479,8 +6493,12 @@
 
 	wpa_s->disconnected = 1;
 	if (wpa_s->drv_priv) {
-		/* Don't deauthenticate if WoWLAN is enabled */
-		if (!wpa_drv_get_wowlan(wpa_s)) {
+		/*
+		 * Don't deauthenticate if WoWLAN is enabled and not explicitly
+		 * been configured to disconnect.
+		 */
+		if (!wpa_drv_get_wowlan(wpa_s) ||
+		    wpa_s->conf->wowlan_disconnect_on_deinit) {
 			wpa_supplicant_deauthenticate(
 				wpa_s, WLAN_REASON_DEAUTH_LEAVING);
 
@@ -6518,10 +6536,8 @@
 	if (terminate)
 		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TERMINATING);
 
-	if (wpa_s->ctrl_iface) {
-		wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
-		wpa_s->ctrl_iface = NULL;
-	}
+	wpa_supplicant_ctrl_iface_deinit(wpa_s, wpa_s->ctrl_iface);
+	wpa_s->ctrl_iface = NULL;
 
 #ifdef CONFIG_MESH
 	if (wpa_s->ifmsh) {
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 3b90567..f5194be 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -1584,6 +1584,12 @@
 # Set to 1 to disable BSS transition management
 #disable_btm=0
 
+# This value is used to set where to perform roaming logic
+# Set to 0 to handle roaming logic fully in supplicant
+# Set to 1 to skip roaming logic in supplicant and handle it in firmware
+# In supplicant, just parse BTM frame and notify framework
+#btm_offload=0
+
 # Enable EDMG capability in STA/AP mode, default value is false
 #enable_edmg=1
 
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 2f95eeb..98b317e 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1064,8 +1064,6 @@
 
 	/* WLAN_STATUS_* status codes from (Re)Association Response frame. */
 	u16 assoc_status_code;
-	/* Indicates if the previous association request timed out. */
-	u8 assoc_timed_out;
 
 	struct ext_password_data *ext_pw;
 
@@ -1258,6 +1256,7 @@
 	struct wpa_radio_work *dpp_listen_work;
 	unsigned int dpp_pending_listen_freq;
 	unsigned int dpp_listen_freq;
+	struct os_reltime dpp_listen_end;
 	u8 dpp_allowed_roles;
 	int dpp_qr_mutual;
 	int dpp_netrole;
@@ -1285,6 +1284,7 @@
 #ifdef CONFIG_DPP2
 	struct dpp_pfs *dpp_pfs;
 	int dpp_pfs_fallback;
+	struct wpabuf *dpp_reconfig_announcement;
 	struct wpabuf *dpp_presence_announcement;
 	struct dpp_bootstrap_info *dpp_chirp_bi;
 	int dpp_chirp_freq;
@@ -1293,6 +1293,8 @@
 	int dpp_chirp_round;
 	int dpp_chirp_scan_done;
 	int dpp_chirp_listen;
+	struct wpa_ssid *dpp_reconfig_ssid;
+	int dpp_reconfig_ssid_id;
 #endif /* CONFIG_DPP2 */
 #ifdef CONFIG_TESTING_OPTIONS
 	char *dpp_config_obj_override;
diff --git a/wpa_supplicant/wpa_supplicant_template.conf b/wpa_supplicant/wpa_supplicant_template.conf
index fce7e5e..552477d 100644
--- a/wpa_supplicant/wpa_supplicant_template.conf
+++ b/wpa_supplicant/wpa_supplicant_template.conf
@@ -6,3 +6,4 @@
 pmf=1
 p2p_add_cli_chan=1
 oce=1
+wowlan_disconnect_on_deinit=1
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 130c278..9f68b22 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -188,6 +188,7 @@
 	const u8 *ie;
 	struct wpa_ie_data adv;
 	int wpa2 = 0, ccmp = 0;
+	enum wpa_driver_if_type iftype;
 
 	/*
 	 * Many existing WPS APs do not know how to negotiate WPA2 or CCMP in
@@ -239,9 +240,12 @@
 		return;
 	}
 
+	iftype = ssid->p2p_group ? WPA_IF_P2P_CLIENT : WPA_IF_STATION;
+
 	if (ccmp && !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) &&
 	    (ssid->pairwise_cipher & WPA_CIPHER_TKIP) &&
-	    (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
+	    (capa.key_mgmt_iftype[iftype] &
+	     WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
 		wpa_printf(MSG_DEBUG, "WPS: Add CCMP into the credential "
 			   "based on scan results");
 		if (wpa_s->conf->ap_scan == 1)