[wpa_supplicant] Cumulative patch from 89bbe6f87

Bug: 130294744
Test: Device boots up and connects to WPA3/OWE wifi networks, run traffic.
Test: Able to turn on/off softap, associate wifi STA, run traffic.
Test: DPP test: act.py -c ../WifiDppConfig.json -tc WifiDppTest
Test: Regression test passed (Bug: 130312227)

89bbe6f87 EAP-pwd: Get rid of unnecessary allocation of temporary buffer
4396f74a3 EAP-pwd: Enforce 1 < rand,mask < r and rand+mask mod r > 1
72056f69a tests: Module tests for const_time_*() functions
16d4f1069 EAP-pwd: Check element x,y coordinates explicitly
8ad8585f9 EAP-pwd client: Verify received scalar and element
d63edfa90 EAP-pwd server: Detect reflection attacks
70ff850e8 EAP-pwd server: Verify received scalar and element
ac8fa9ef1 SAE: Fix confirm message validation in error cases
cff138b07 SAE: Use constant time operations in sae_test_pwd_seed_ffc()
f8f20717f SAE: Use const_time selection for PWE in FFC
90839597c SAE: Mask timing of MODP groups 22, 23, 24
362704dda SAE: Avoid branches in is_quadratic_residue_blind()
6513db3e9 SAE: Minimize timing differences in PWE derivation
aaf65feac EAP-pwd: Use constant time and memory access for finding the PWE
c93461c1d OpenSSL: Use constant time selection for crypto_bignum_legendre()
6e34f618d Add helper functions for constant time operations
d42c477cc OpenSSL: Use constant time operations for private bignums
242e85728 Extend domain_match and domain_suffix_match to allow list of values
dcc0ccd5b wolfSSL: Fix dNSName matching with domain_match and domain_suffix_match
83f13e4ff tests: Fix build without CONFIG_SAE
3580ed826 RADIUS server: Accept ERP keyName-NAI as user identity
bbde461d7 Fix a typo in the Multiple BSSID Index element ID define
1326cb765 DPP: Fix a regression in non-DPP, non-OpenSSL builds
db54db11a SAE: Reject unsuitable groups based on REVmd changes
6bb9d9a8d AP: Avoid NULL use with snprintf string
0a42f1ede scan: Use normal scans after connection failure
bbed23aee hostapd: Reduce minimum beacon interval from 15 to 10 TUs
048796715 OWE: Fix a possible memory leak on error path
277fa92b3 crypto: Fix unreachable code in tls_prf_sha1_md5()
3d93e26e6 tests: SAE test vector from IEEE P802.11-REVmd/D2.1, Annex J.10
e5711b618 Add a QCA vendor attribute to carry the reason for roaming
005585d60 nl80211: Add SAE, FT-SAE, FT-EAP-SHA384 AKMs in connect request
edcaf16f9 P2P: Enable HE for both 2G and 5G bands
822c756e8 MBO: Update connect params with new MBO attributes to driver
74f8e768f MBO: Always include Non-preferred Channel Report attribute in AssocReq
bd23daa8e DPP: Move GAS encapsulation into dpp_build_conf_req()
be609c6fc SAE: Fix commit message override with external authentication
9af1eea37 Prefer FT-SAE over FT-PSK if both are enabled
853bd19f2 Add more debug prints on suite selector selection
6d77014e7 Suite B: Prefer FT-EAP-SHA384 over WPA-EAP-SUITE-B-192
ab3aebcce SAE: Fix PMKSA cache entry search for FT-SAE case
253ce212e Add AKM info in the debug message noting PMKSA caching entry addition
6fe3f0f79 FT-SAE: Use PMK as XXKey in AP when SAE PMKSA caching is used
bcf190005 FT-SAE: Enable external auth support for FT-SAE also
6d14b98fc nl80211: Do not add WMM parameters when updating an existing STA entry
2ffd8076d FT/RRB: Pad RRB messages to at least minimum Ethernet frame length
555c93e2d FT/RRB: Add more debug prints for RRB message encryption/decryptiom
76fd782ab SAE: Reorder SAE and FT-SAE AKM selection to prefer the FT option
322d328e7 FT: Fix SAE + FT-SAE behavior in association parameter selection
87d8435cf DPP: Common configurator/bootstrapping data management
08dc8efd2 Fix memcpy regression in PMK handling
130444738 FILS: Fix KEK2 derivation for FILS+FT
bf84e78cb OpenSSL: Fix build with current BoringSSL
b750dde64 OWE: Move Association Response frame IE addition to appropriate place
10ec6a5f3 DPP2: PFS for PTK derivation
ecacd9ccd DPP2: Extend wpa_pmk_to_ptk() to support extra Z.x component in context
16a4e931f OWE: Allow Diffie-Hellman Parameter element to be included with DPP
808bdb308 Add TEST_FAIL() to aes_encrypt_init() with internal crypto
32f476066 TLS: Add support for RFC 5705 TLS exporter context with internal TLS
a916ff5cd Add support for an optional context parameter to TLS exporter
18015fc8a DPP2: Support new legacy+DPP config object credentials
dd6c59800 DPP: Support DPP and SAE in the same network profile
9305c2332 DPP: Clean up configuration parsing
f5db77504 Remove pending connect and sme-connect radio works on disconnect
c675397cc OpenSSL: Fix build with OpenSSL 1.0.2
ff5f54e15 SAE: Reduce queue wait time for pending Authentication frames
5e3a759cd SAE: Improved queuing policy for pending authentication frames
67b3bcc95 DPP2: Testing option for Config Object rejction
22f90b32f DPP2: Configuration Result message generation and processing
c98617b48 DPP2: Make DPP version number support available over control interface
673631b8a More robust timer_tick_enabled tracking
b5e57699a FILS+FT: STA mode validation of PMKR1Name in initial MD association
365366393 FILS+FT: AP mode processing of PMKR1Name in initial MD association
aabbdb818 FILS: Do not try to add PMKSA cache entry if caching is disabled
02bde9581 Vendor attribute to enable or disable TWT request support
4efade315 nl80211: Clear keys from heap memory before freeing it for get_seqnum
2b7fa0355 P2P: Fix ACS offloading behavior with p2p_no_group_iface=1
c6ec9759c nl80211: Exclude PMK when sending NL80211_CMD_DEL_PMKSA explicitly
0b4a906de DPP2: Protocol version indication
ce7effd08 DPP2: Build configuration flags for DPP version 2 support
bf0021ede Allow fragmentation/RTS threshold to be disabled explicitly
48102f65e Add a vendor subcommand QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG
f10a4af1b Add QCA vendor command/event and attributes for peer rate statistics
56a33496f Sync with mac80211-next.git include/uapi/linux/nl80211.h
841205a1c OpenSSL: Add 'check_cert_subject' support for TLS server
0173423f4 Use char pointers for EAP configuration parameters without length
cd6a5866e Remove forgotten os_strncpy() implementations
ca9efe113 roboswitch: Check some read operation results
9571f945c mesh: Check that SAE state initialization succeeded for PMKID check
fafad8527 defconfig: Enable DBus
6a8dee76d wpa_supplicant: Drop the old D-Bus interface support
954c535a5 DPP: Update wpa_supplicant configuration file after provisioning
4d379be4a Clarify AP mode Action frame handling
cc833a236 Minor cleanup to return after WNM Action frame handling
700b3f395 Move SA Query frame length check to the shared handler function
002edb630 Fix AP MLME in driver handling of FT and SA Query Action frames
1e653daa3 EAP-pwd server: Fix memory leak with salted passwords
96d6dfa8e SAE: Add Finite Cyclic Group field in status code 77 response
fda766010 EAP-pwd: Fix a memory leak in hunting-and-pecking loop
339dc8bd6 WPS: Allow SAE configuration to be added automatically for PSK
fc30f99b3 WPS: Allow AP SAE configuration to be added automatically for PSK
b9cd4f5e7 Vendor feature capability for TWT (Target Wake Time)
877502a6a Vendor attribute to indicate a set_blacklist of BSSID only as a hint
a9247bcdf Vendor attribute to configure HE testbed default capabilities
8a7510cc0 Vendor attribute to configure support to enable VHT in 2.4G band
8919ec616 Vendor attribute to configure HE OMI UL MU data disable
f21436158 SAE: Reuse previously generated PWE on a retry with the same STA
fd8308912 SAE: Reuse previously generated PWE on a retry with the same AP
a9af1da0b SAE: Enforce single use for anti-clogging tokens
ff9f40aee SAE: Process received commit message through a queue
a9fe13035 SAE: Enable only groups 19, 20, and 21 in station mode
941bad5ef SAE: Enable only group 19 by default in AP mode
b11fa98bc Add explicit checks for peer's DH public key
4a9531a75 bignum: Fix documentation for bignum_cmp_d()
611308365 defconfig: Enable IEEE 802.11w management frame protection (wpa_supplicant)
9515fa925 defconfig: enable IEEE 802.11r fast BSS transition (wpa_supplicant)
6b7a0da75 defconfig: Enable IEEE 802.11n and 802.11ac (wpa_supplicant)
467004d63 defconfig: Enable Hotspot 2.0 (wpa_supplicant)
ec52faa2b defconfig: Enable RSN on IBSS networks (wpa_supplicant)
67d99d2e0 defconfig: Remove obsolete notes about OpenSSL requirements for EAP-FAST
eafc5fec2 defconfig: Enable a handful of EAP methods (wpa_supplicant)
f64050da0 defconfig: Enable logging to file and syslog (wpa_supplicant)
ae5240db8 defconfig: Enable simple bgscan module (wpa_supplicant)
2d6d47219 defconfig: Enable AP (wpa_supplicant)
f87450a73 defconfig: Enable WPS (wpa_supplicant)
d989e67d0 defconfig: Fix typos in Wi-Fi Display description
c4eafad09 defconfig: Enable P2P and Wi-Fi Display (wpa_supplicant)
bf46c6fca defconfig: Add SAE (wpa_supplicant)
ca098ee45 defconfig: Add DPP (wpa_supplicant)
2f7bc0681 UBSan: Avoid a warning on unsigned integer overflow
bb05d0360 Fix a regression from VLAN assignment using WPA/WPA2 passphrase/PSK
429ed54a3 UBSan: Avoid a warning on signed left shift
b3957edbe UBSan: Split loop index decrementation into a separate step
5ac13f6d0 atheros: Avoid clang compiler warning on address of array check
aaa6b1498 Avoid compiler warning about potentially unaligned pointer value
cce974d36 UBSan: Define FST LLT macros without integer overflow
9140caf5f UBSan: Avoid integer overflow in a loop index counter
8fc22fdde UBSan: Avoid NULL pointer dereferences on an error path
43216777e UBSan: Avoid unsigned integer overflow in base64 encoding
fed7d8fcb UBSan: Avoid unsigned integer overflow in utf8_{,un}escape()
cc4cdefc7 UBSan: Avoid unnecessary warning
a9377bc38 UBSan: Avoid memcpy(ptr, NULL, 0)
5a23c2528 UBSan: Avoid an unsigned integer overflow warning
abde4eba4 UBSan: Pack MACsec peer id structure
c4fccfc7a UBSan: Avoid memcmp(ptr, NULL, 0)
1b85cad29 UBSan: Use typecast to avoid unsigned integer overflow
e3b5bd81b UBSan: Fix RRM beacon processing attempt without scan_info
01d01a311 UBSan: Avoid size_t variable overflow in control interface
ec2e7c4cf UBSan: Avoid unsigned integer overflow is throughput estimation
3b6b3ae58 Modify dl_list_for_each() to not use unaligned access with WPA_TRACE
1415d4b82 Multi-AP: Avoid memcpy(ptr, NULL, 0) in WPS Registrar initialization
2c129a1b7 Fix cipher suite selector default value in RSNE for DMG
239794018 Add new QCA vendor attributes for coex configuration
d939a8cb4 Add a vendor attribute for specifying ethernet protocol type
8682f384c hostapd: Add README-MULTI-AP
66819b07b hostapd: Support Multi-AP backhaul STA onboarding with WPS
83ebf5586 wpa_supplicant: Support Multi-AP backhaul STA onboarding with WPS
56a2d788f WPS: Add multi_ap_subelem to wps_build_wfa_ext()
bfcdac1c8 Multi-AP: Don't reject backhaul STA on fronthaul BSS
7ad7aa0e1 HS 2.0: Make hs20-osu-client SP and <FQDN> directories group writable
0f9632ceb mesh: More consistent checking of wpa_s->ifmsh in completion handler
2fae58fdc Fix wpa_psk_file parser error case handling
b0e91e387 SAE: VLAN assignment based on SAE Password Identifier
947b5a153 P2P: Stop listen state if Action frame TX is needed on another channel
b3e8ca65a P2P: Fix a typo in a debug message
464064c7e hostapd: Document openssl_ecdh_curves configuration parameter
31ee2992c Add QCA vendor subcmd/attribute to check wlan firmware state
3f8ceff54 Indicate wifi_generation in wpa_supplicant STATUS output
ccaf77476 Add HE Capabilities into ieee802_11_parse_elems()
5d68c0acd nl80211: (Re)Association Request frame IEs from association event
dbfa691df VLAN assignment based on used WPA/WPA2 passphrase/PSK
dd2aedeb5 HS 2.0 server: Add X-WFA-Hotspot20-Filtering header line to T&C

Change-Id: I4efde950845354673694f025ca07c3dddb6039e9
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 9611dc0..e640e99 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -200,8 +200,8 @@
 	conf->num_bss = 1;
 
 	conf->beacon_int = 100;
-	conf->rts_threshold = -1; /* use driver default: 2347 */
-	conf->fragm_threshold = -1; /* user driver default: 2346 */
+	conf->rts_threshold = -2; /* use driver default: 2347 */
+	conf->fragm_threshold = -2; /* user driver default: 2346 */
 	/* Set to invalid value means do not add Power Constraint IE */
 	conf->local_pwr_constraint = -1;
 
@@ -279,6 +279,8 @@
 	}
 
 	while (fgets(buf, sizeof(buf), f)) {
+		int vlan_id = 0;
+
 		line++;
 
 		if (buf[0] == '#')
@@ -301,11 +303,15 @@
 				break;
 			context2 = NULL;
 			name = str_token(token, "=", &context2);
+			if (!name)
+				break;
 			value = str_token(token, "", &context2);
 			if (!value)
 				value = "";
 			if (!os_strcmp(name, "keyid")) {
 				keyid = value;
+			} else if (!os_strcmp(name, "vlanid")) {
+				vlan_id = atoi(value);
 			} else {
 				wpa_printf(MSG_ERROR,
 					   "Unrecognized '%s=%s' on line %d in '%s'",
@@ -333,6 +339,7 @@
 			ret = -1;
 			break;
 		}
+		psk->vlan_id = vlan_id;
 		if (is_zero_ether_addr(addr))
 			psk->group = 1;
 		else
@@ -588,6 +595,7 @@
 	os_free(conf->server_cert);
 	os_free(conf->private_key);
 	os_free(conf->private_key_passwd);
+	os_free(conf->check_cert_subject);
 	os_free(conf->ocsp_stapling_response);
 	os_free(conf->ocsp_stapling_response_multi);
 	os_free(conf->dh_file);
@@ -637,6 +645,8 @@
 	os_free(conf->ap_pin);
 	os_free(conf->extra_cred);
 	os_free(conf->ap_settings);
+	hostapd_config_clear_wpa_psk(&conf->multi_ap_backhaul_ssid.wpa_psk);
+	str_clear_free(conf->multi_ap_backhaul_ssid.wpa_passphrase);
 	os_free(conf->upnp_iface);
 	os_free(conf->friendly_name);
 	os_free(conf->manufacturer_url);
@@ -858,11 +868,14 @@
 
 const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
 			   const u8 *addr, const u8 *p2p_dev_addr,
-			   const u8 *prev_psk)
+			   const u8 *prev_psk, int *vlan_id)
 {
 	struct hostapd_wpa_psk *psk;
 	int next_ok = prev_psk == NULL;
 
+	if (vlan_id)
+		*vlan_id = 0;
+
 	if (p2p_dev_addr && !is_zero_ether_addr(p2p_dev_addr)) {
 		wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR
 			   " p2p_dev_addr=" MACSTR " prev_psk=%p",
@@ -880,8 +893,11 @@
 		     (addr && os_memcmp(psk->addr, addr, ETH_ALEN) == 0) ||
 		     (!addr && p2p_dev_addr &&
 		      os_memcmp(psk->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) ==
-		      0)))
+		      0))) {
+			if (vlan_id)
+				*vlan_id = psk->vlan_id;
 			return psk->psk;
+		}
 
 		if (psk->psk == prev_psk)
 			next_ok = 1;
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 6963df4..509677a 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -152,6 +152,7 @@
 	u8 psk[PMK_LEN];
 	u8 addr[ETH_ALEN];
 	u8 p2p_dev_addr[ETH_ALEN];
+	int vlan_id;
 };
 
 struct hostapd_eap_user {
@@ -248,6 +249,7 @@
 	char *password;
 	char *identifier;
 	u8 peer_addr[ETH_ALEN];
+	int vlan_id;
 };
 
 /**
@@ -390,6 +392,7 @@
 	char *server_cert;
 	char *private_key;
 	char *private_key_passwd;
+	char *check_cert_subject;
 	int check_crl;
 	int check_crl_strict;
 	unsigned int crl_reload_interval;
@@ -462,9 +465,11 @@
 	u8 *extra_cred;
 	size_t extra_cred_len;
 	int wps_cred_processing;
+	int wps_cred_add_sae;
 	int force_per_enrollee_psk;
 	u8 *ap_settings;
 	size_t ap_settings_len;
+	struct hostapd_ssid multi_ap_backhaul_ssid;
 	char *upnp_iface;
 	char *friendly_name;
 	char *manufacturer_url;
@@ -872,7 +877,7 @@
 int hostapd_rate_found(int *list, int rate);
 const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
 			   const u8 *addr, const u8 *p2p_dev_addr,
-			   const u8 *prev_psk);
+			   const u8 *prev_psk, int *vlan_id);
 int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf);
 int hostapd_vlan_valid(struct hostapd_vlan *vlan,
 		       struct vlan_description *vlan_desc);
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index 1bb3d9f..eced6c7 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -233,6 +233,7 @@
 			hapd->conf->ocsp_stapling_response;
 		params.ocsp_stapling_response_multi =
 			hapd->conf->ocsp_stapling_response_multi;
+		params.check_cert_subject = hapd->conf->check_cert_subject;
 
 		if (tls_global_set_params(hapd->ssl_ctx, &params)) {
 			wpa_printf(MSG_ERROR, "Failed to set TLS parameters");
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 3128aed..c693715 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -1,6 +1,6 @@
 /*
  * Control interface for shared AP commands
- * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -451,11 +451,11 @@
 	if (stype == WLAN_FC_STYPE_DEAUTH) {
 		mgmt->u.deauth.reason_code =
 			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
-		pos = (u8 *) (&mgmt->u.deauth.reason_code + 1);
+		pos = mgmt->u.deauth.variable;
 	} else {
 		mgmt->u.disassoc.reason_code =
 			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
-		pos = (u8 *) (&mgmt->u.disassoc.reason_code + 1);
+		pos = mgmt->u.disassoc.variable;
 	}
 
 	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index 149f389..75edbc9 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -28,34 +28,6 @@
 static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
 
-static struct dpp_configurator *
-hostapd_dpp_configurator_get_id(struct hostapd_data *hapd, unsigned int id)
-{
-	struct dpp_configurator *conf;
-
-	dl_list_for_each(conf, &hapd->iface->interfaces->dpp_configurator,
-			 struct dpp_configurator, list) {
-		if (conf->id == id)
-			return conf;
-	}
-	return NULL;
-}
-
-
-static unsigned int hapd_dpp_next_id(struct hostapd_data *hapd)
-{
-	struct dpp_bootstrap_info *bi;
-	unsigned int max_id = 0;
-
-	dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap,
-			 struct dpp_bootstrap_info, list) {
-		if (bi->id > max_id)
-			max_id = bi->id;
-	}
-	return max_id + 1;
-}
-
-
 /**
  * hostapd_dpp_qr_code - Parse and add DPP bootstrapping info from a QR Code
  * @hapd: Pointer to hostapd_data
@@ -67,13 +39,10 @@
 	struct dpp_bootstrap_info *bi;
 	struct dpp_authentication *auth = hapd->dpp_auth;
 
-	bi = dpp_parse_qr_code(cmd);
+	bi = dpp_add_qr_code(hapd->iface->interfaces->dpp, cmd);
 	if (!bi)
 		return -1;
 
-	bi->id = hapd_dpp_next_id(hapd);
-	dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list);
-
 	if (auth && auth->response_pending &&
 	    dpp_notify_new_qr_code(auth, bi) == 1) {
 		wpa_printf(MSG_DEBUG,
@@ -92,195 +61,6 @@
 }
 
 
-static char * get_param(const char *cmd, const char *param)
-{
-	const char *pos, *end;
-	char *val;
-	size_t len;
-
-	pos = os_strstr(cmd, param);
-	if (!pos)
-		return NULL;
-
-	pos += os_strlen(param);
-	end = os_strchr(pos, ' ');
-	if (end)
-		len = end - pos;
-	else
-		len = os_strlen(pos);
-	val = os_malloc(len + 1);
-	if (!val)
-		return NULL;
-	os_memcpy(val, pos, len);
-	val[len] = '\0';
-	return val;
-}
-
-
-int hostapd_dpp_bootstrap_gen(struct hostapd_data *hapd, const char *cmd)
-{
-	char *chan = NULL, *mac = NULL, *info = NULL, *pk = NULL, *curve = NULL;
-	char *key = NULL;
-	u8 *privkey = NULL;
-	size_t privkey_len = 0;
-	size_t len;
-	int ret = -1;
-	struct dpp_bootstrap_info *bi;
-
-	bi = os_zalloc(sizeof(*bi));
-	if (!bi)
-		goto fail;
-
-	if (os_strstr(cmd, "type=qrcode"))
-		bi->type = DPP_BOOTSTRAP_QR_CODE;
-	else if (os_strstr(cmd, "type=pkex"))
-		bi->type = DPP_BOOTSTRAP_PKEX;
-	else
-		goto fail;
-
-	chan = get_param(cmd, " chan=");
-	mac = get_param(cmd, " mac=");
-	info = get_param(cmd, " info=");
-	curve = get_param(cmd, " curve=");
-	key = get_param(cmd, " key=");
-
-	if (key) {
-		privkey_len = os_strlen(key) / 2;
-		privkey = os_malloc(privkey_len);
-		if (!privkey ||
-		    hexstr2bin(key, privkey, privkey_len) < 0)
-			goto fail;
-	}
-
-	pk = dpp_keygen(bi, curve, privkey, privkey_len);
-	if (!pk)
-		goto fail;
-
-	len = 4; /* "DPP:" */
-	if (chan) {
-		if (dpp_parse_uri_chan_list(bi, chan) < 0)
-			goto fail;
-		len += 3 + os_strlen(chan); /* C:...; */
-	}
-	if (mac) {
-		if (dpp_parse_uri_mac(bi, mac) < 0)
-			goto fail;
-		len += 3 + os_strlen(mac); /* M:...; */
-	}
-	if (info) {
-		if (dpp_parse_uri_info(bi, info) < 0)
-			goto fail;
-		len += 3 + os_strlen(info); /* I:...; */
-	}
-	len += 4 + os_strlen(pk);
-	bi->uri = os_malloc(len + 1);
-	if (!bi->uri)
-		goto fail;
-	os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%sK:%s;;",
-		    chan ? "C:" : "", chan ? chan : "", chan ? ";" : "",
-		    mac ? "M:" : "", mac ? mac : "", mac ? ";" : "",
-		    info ? "I:" : "", info ? info : "", info ? ";" : "",
-		    pk);
-	bi->id = hapd_dpp_next_id(hapd);
-	dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list);
-	ret = bi->id;
-	bi = NULL;
-fail:
-	os_free(curve);
-	os_free(pk);
-	os_free(chan);
-	os_free(mac);
-	os_free(info);
-	str_clear_free(key);
-	bin_clear_free(privkey, privkey_len);
-	dpp_bootstrap_info_free(bi);
-	return ret;
-}
-
-
-static struct dpp_bootstrap_info *
-dpp_bootstrap_get_id(struct hostapd_data *hapd, unsigned int id)
-{
-	struct dpp_bootstrap_info *bi;
-
-	dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap,
-			 struct dpp_bootstrap_info, list) {
-		if (bi->id == id)
-			return bi;
-	}
-	return NULL;
-}
-
-
-static int dpp_bootstrap_del(struct hapd_interfaces *ifaces, unsigned int id)
-{
-	struct dpp_bootstrap_info *bi, *tmp;
-	int found = 0;
-
-	dl_list_for_each_safe(bi, tmp, &ifaces->dpp_bootstrap,
-			      struct dpp_bootstrap_info, list) {
-		if (id && bi->id != id)
-			continue;
-		found = 1;
-		dl_list_del(&bi->list);
-		dpp_bootstrap_info_free(bi);
-	}
-
-	if (id == 0)
-		return 0; /* flush succeeds regardless of entries found */
-	return found ? 0 : -1;
-}
-
-
-int hostapd_dpp_bootstrap_remove(struct hostapd_data *hapd, const char *id)
-{
-	unsigned int id_val;
-
-	if (os_strcmp(id, "*") == 0) {
-		id_val = 0;
-	} else {
-		id_val = atoi(id);
-		if (id_val == 0)
-			return -1;
-	}
-
-	return dpp_bootstrap_del(hapd->iface->interfaces, id_val);
-}
-
-
-const char * hostapd_dpp_bootstrap_get_uri(struct hostapd_data *hapd,
-					   unsigned int id)
-{
-	struct dpp_bootstrap_info *bi;
-
-	bi = dpp_bootstrap_get_id(hapd, id);
-	if (!bi)
-		return NULL;
-	return bi->uri;
-}
-
-
-int hostapd_dpp_bootstrap_info(struct hostapd_data *hapd, int id,
-			       char *reply, int reply_size)
-{
-	struct dpp_bootstrap_info *bi;
-
-	bi = dpp_bootstrap_get_id(hapd, id);
-	if (!bi)
-		return -1;
-	return os_snprintf(reply, reply_size, "type=%s\n"
-			   "mac_addr=" MACSTR "\n"
-			   "info=%s\n"
-			   "num_freq=%u\n"
-			   "curve=%s\n",
-			   dpp_bootstrap_type_txt(bi->type),
-			   MAC2STR(bi->mac_addr),
-			   bi->info ? bi->info : "",
-			   bi->num_freq,
-			   bi->curve->name);
-}
-
-
 static void hostapd_dpp_auth_resp_retry_timeout(void *eloop_ctx,
 						void *timeout_ctx)
 {
@@ -354,6 +134,16 @@
 		return;
 	}
 
+#ifdef CONFIG_DPP2
+	if (auth->connect_on_tx_status) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Complete exchange on configuration result");
+		dpp_auth_deinit(hapd->dpp_auth);
+		hapd->dpp_auth = NULL;
+		return;
+	}
+#endif /* CONFIG_DPP2 */
+
 	if (hapd->dpp_auth->remove_on_tx_status) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Terminate authentication exchange due to an earlier error");
@@ -505,178 +295,6 @@
 }
 
 
-static int hostapd_dpp_set_configurator(struct hostapd_data *hapd,
-					struct dpp_authentication *auth,
-					const char *cmd)
-{
-	const char *pos, *end;
-	struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL;
-	struct dpp_configurator *conf = NULL;
-	u8 ssid[32] = { "test" };
-	size_t ssid_len = 4;
-	char pass[64] = { };
-	size_t pass_len = 0;
-	u8 psk[PMK_LEN];
-	int psk_set = 0;
-	char *group_id = NULL;
-
-	if (!cmd)
-		return 0;
-
-	wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd);
-	pos = os_strstr(cmd, " ssid=");
-	if (pos) {
-		pos += 6;
-		end = os_strchr(pos, ' ');
-		ssid_len = end ? (size_t) (end - pos) : os_strlen(pos);
-		ssid_len /= 2;
-		if (ssid_len > sizeof(ssid) ||
-		    hexstr2bin(pos, ssid, ssid_len) < 0)
-			goto fail;
-	}
-
-	pos = os_strstr(cmd, " pass=");
-	if (pos) {
-		pos += 6;
-		end = os_strchr(pos, ' ');
-		pass_len = end ? (size_t) (end - pos) : os_strlen(pos);
-		pass_len /= 2;
-		if (pass_len > sizeof(pass) - 1 || pass_len < 8 ||
-		    hexstr2bin(pos, (u8 *) pass, pass_len) < 0)
-			goto fail;
-	}
-
-	pos = os_strstr(cmd, " psk=");
-	if (pos) {
-		pos += 5;
-		if (hexstr2bin(pos, psk, PMK_LEN) < 0)
-			goto fail;
-		psk_set = 1;
-	}
-
-	pos = os_strstr(cmd, " group_id=");
-	if (pos) {
-		size_t group_id_len;
-
-		pos += 10;
-		end = os_strchr(pos, ' ');
-		group_id_len = end ? (size_t) (end - pos) : os_strlen(pos);
-		group_id = os_malloc(group_id_len + 1);
-		if (!group_id)
-			goto fail;
-		os_memcpy(group_id, pos, group_id_len);
-		group_id[group_id_len] = '\0';
-	}
-
-	if (os_strstr(cmd, " conf=sta-")) {
-		conf_sta = os_zalloc(sizeof(struct dpp_configuration));
-		if (!conf_sta)
-			goto fail;
-		os_memcpy(conf_sta->ssid, ssid, ssid_len);
-		conf_sta->ssid_len = ssid_len;
-		if (os_strstr(cmd, " conf=sta-psk") ||
-		    os_strstr(cmd, " conf=sta-sae") ||
-		    os_strstr(cmd, " conf=sta-psk-sae")) {
-			if (os_strstr(cmd, " conf=sta-psk-sae"))
-				conf_sta->akm = DPP_AKM_PSK_SAE;
-			else if (os_strstr(cmd, " conf=sta-sae"))
-				conf_sta->akm = DPP_AKM_SAE;
-			else
-				conf_sta->akm = DPP_AKM_PSK;
-			if (psk_set) {
-				os_memcpy(conf_sta->psk, psk, PMK_LEN);
-			} else {
-				conf_sta->passphrase = os_strdup(pass);
-				if (!conf_sta->passphrase)
-					goto fail;
-			}
-		} else if (os_strstr(cmd, " conf=sta-dpp")) {
-			conf_sta->akm = DPP_AKM_DPP;
-		} else {
-			goto fail;
-		}
-		if (os_strstr(cmd, " group_id=")) {
-			conf_sta->group_id = group_id;
-			group_id = NULL;
-		}
-	}
-
-	if (os_strstr(cmd, " conf=ap-")) {
-		conf_ap = os_zalloc(sizeof(struct dpp_configuration));
-		if (!conf_ap)
-			goto fail;
-		os_memcpy(conf_ap->ssid, ssid, ssid_len);
-		conf_ap->ssid_len = ssid_len;
-		if (os_strstr(cmd, " conf=ap-psk") ||
-		    os_strstr(cmd, " conf=ap-sae") ||
-		    os_strstr(cmd, " conf=ap-psk-sae")) {
-			if (os_strstr(cmd, " conf=ap-psk-sae"))
-				conf_ap->akm = DPP_AKM_PSK_SAE;
-			else if (os_strstr(cmd, " conf=ap-sae"))
-				conf_ap->akm = DPP_AKM_SAE;
-			else
-				conf_ap->akm = DPP_AKM_PSK;
-			if (psk_set) {
-				os_memcpy(conf_ap->psk, psk, PMK_LEN);
-			} else if (pass_len > 0) {
-				conf_ap->passphrase = os_strdup(pass);
-				if (!conf_ap->passphrase)
-					goto fail;
-			} else {
-				goto fail;
-			}
-		} else if (os_strstr(cmd, " conf=ap-dpp")) {
-			conf_ap->akm = DPP_AKM_DPP;
-		} else {
-			goto fail;
-		}
-		if (os_strstr(cmd, " group_id=")) {
-			conf_ap->group_id = group_id;
-			group_id = NULL;
-		}
-	}
-
-	pos = os_strstr(cmd, " expiry=");
-	if (pos) {
-		long int val;
-
-		pos += 8;
-		val = strtol(pos, NULL, 0);
-		if (val <= 0)
-			goto fail;
-		if (conf_sta)
-			conf_sta->netaccesskey_expiry = val;
-		if (conf_ap)
-			conf_ap->netaccesskey_expiry = val;
-	}
-
-	pos = os_strstr(cmd, " configurator=");
-	if (pos) {
-		auth->configurator = 1;
-		pos += 14;
-		conf = hostapd_dpp_configurator_get_id(hapd, atoi(pos));
-		if (!conf) {
-			wpa_printf(MSG_INFO,
-				   "DPP: Could not find the specified configurator");
-			goto fail;
-		}
-	}
-	auth->conf_sta = conf_sta;
-	auth->conf_ap = conf_ap;
-	auth->conf = conf;
-	os_free(group_id);
-	return 0;
-
-fail:
-	wpa_msg(hapd->msg_ctx, MSG_INFO,
-		"DPP: Failed to set configurator parameters");
-	dpp_configuration_free(conf_sta);
-	dpp_configuration_free(conf_ap);
-	os_free(group_id);
-	return -1;
-}
-
-
 static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx)
 {
 	struct hostapd_data *hapd = eloop_ctx;
@@ -786,7 +404,7 @@
 	if (!pos)
 		return -1;
 	pos += 6;
-	peer_bi = dpp_bootstrap_get_id(hapd, atoi(pos));
+	peer_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos));
 	if (!peer_bi) {
 		wpa_printf(MSG_INFO,
 			   "DPP: Could not find bootstrapping info for the identified peer");
@@ -796,7 +414,8 @@
 	pos = os_strstr(cmd, " own=");
 	if (pos) {
 		pos += 5;
-		own_bi = dpp_bootstrap_get_id(hapd, atoi(pos));
+		own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp,
+					      atoi(pos));
 		if (!own_bi) {
 			wpa_printf(MSG_INFO,
 				   "DPP: Could not find bootstrapping info for the identified local entry");
@@ -846,7 +465,8 @@
 	if (!hapd->dpp_auth)
 		goto fail;
 	hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth);
-	if (hostapd_dpp_set_configurator(hapd, hapd->dpp_auth, cmd) < 0) {
+	if (dpp_set_configurator(hapd->iface->interfaces->dpp, hapd->msg_ctx,
+				 hapd->dpp_auth, cmd) < 0) {
 		dpp_auth_deinit(hapd->dpp_auth);
 		hapd->dpp_auth = NULL;
 		goto fail;
@@ -905,7 +525,10 @@
 {
 	const u8 *r_bootstrap, *i_bootstrap;
 	u16 r_bootstrap_len, i_bootstrap_len;
-	struct dpp_bootstrap_info *bi, *own_bi = NULL, *peer_bi = NULL;
+	struct dpp_bootstrap_info *own_bi = NULL, *peer_bi = NULL;
+
+	if (!hapd->iface->interfaces->dpp)
+		return;
 
 	wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR,
 		   MAC2STR(src));
@@ -932,28 +555,8 @@
 
 	/* Try to find own and peer bootstrapping key matches based on the
 	 * received hash values */
-	dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap,
-			 struct dpp_bootstrap_info, list) {
-		if (!own_bi && bi->own &&
-		    os_memcmp(bi->pubkey_hash, r_bootstrap,
-			      SHA256_MAC_LEN) == 0) {
-			wpa_printf(MSG_DEBUG,
-				   "DPP: Found matching own bootstrapping information");
-			own_bi = bi;
-		}
-
-		if (!peer_bi && !bi->own &&
-		    os_memcmp(bi->pubkey_hash, i_bootstrap,
-			      SHA256_MAC_LEN) == 0) {
-			wpa_printf(MSG_DEBUG,
-				   "DPP: Found matching peer bootstrapping information");
-			peer_bi = bi;
-		}
-
-		if (own_bi && peer_bi)
-			break;
-	}
-
+	dpp_bootstrap_find_pair(hapd->iface->interfaces->dpp, i_bootstrap,
+				r_bootstrap, &own_bi, &peer_bi);
 	if (!own_bi) {
 		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
 			"No matching own bootstrapping key found - ignore message");
@@ -975,8 +578,9 @@
 		return;
 	}
 	hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth);
-	if (hostapd_dpp_set_configurator(hapd, hapd->dpp_auth,
-					 hapd->dpp_configurator_params) < 0) {
+	if (dpp_set_configurator(hapd->iface->interfaces->dpp, hapd->msg_ctx,
+				 hapd->dpp_auth,
+				 hapd->dpp_configurator_params) < 0) {
 		dpp_auth_deinit(hapd->dpp_auth);
 		hapd->dpp_auth = NULL;
 		return;
@@ -1072,6 +676,7 @@
 	struct hostapd_data *hapd = ctx;
 	const u8 *pos;
 	struct dpp_authentication *auth = hapd->dpp_auth;
+	enum dpp_status_error status = DPP_STATUS_CONFIG_REJECTED;
 
 	if (!auth || !auth->auth_success) {
 		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
@@ -1107,12 +712,41 @@
 	}
 
 	hostapd_dpp_handle_config_obj(hapd, auth);
-	dpp_auth_deinit(hapd->dpp_auth);
-	hapd->dpp_auth = NULL;
-	return;
-
+	status = DPP_STATUS_OK;
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_REJECT_CONFIG) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - Reject Config Object");
+		status = DPP_STATUS_CONFIG_REJECTED;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 fail:
-	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+	if (status != DPP_STATUS_OK)
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+#ifdef CONFIG_DPP2
+	if (auth->peer_version >= 2 &&
+	    auth->conf_resp_status == DPP_STATUS_OK) {
+		struct wpabuf *msg;
+
+		wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result");
+		msg = dpp_build_conf_result(auth, status);
+		if (!msg)
+			goto fail2;
+
+		wpa_msg(hapd->msg_ctx, MSG_INFO,
+			DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+			MAC2STR(addr), auth->curr_freq,
+			DPP_PA_CONFIGURATION_RESULT);
+		hostapd_drv_send_action(hapd, auth->curr_freq, 0,
+					addr, wpabuf_head(msg),
+					wpabuf_len(msg));
+		wpabuf_free(msg);
+
+		/* This exchange will be terminated in the TX status handler */
+		auth->connect_on_tx_status = 1;
+		return;
+	}
+fail2:
+#endif /* CONFIG_DPP2 */
 	dpp_auth_deinit(hapd->dpp_auth);
 	hapd->dpp_auth = NULL;
 }
@@ -1121,7 +755,7 @@
 static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd)
 {
 	struct dpp_authentication *auth = hapd->dpp_auth;
-	struct wpabuf *buf, *conf_req;
+	struct wpabuf *buf;
 	char json[100];
 	int res;
 	int netrole_ap = 1;
@@ -1133,34 +767,13 @@
 		    netrole_ap ? "ap" : "sta");
 	wpa_printf(MSG_DEBUG, "DPP: GAS Config Attributes: %s", json);
 
-	conf_req = dpp_build_conf_req(auth, json);
-	if (!conf_req) {
+	buf = dpp_build_conf_req(auth, json);
+	if (!buf) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: No configuration request data available");
 		return;
 	}
 
-	buf = gas_build_initial_req(0, 10 + 2 + wpabuf_len(conf_req));
-	if (!buf) {
-		wpabuf_free(conf_req);
-		return;
-	}
-
-	/* Advertisement Protocol IE */
-	wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
-	wpabuf_put_u8(buf, 8); /* Length */
-	wpabuf_put_u8(buf, 0x7f);
-	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
-	wpabuf_put_u8(buf, 5);
-	wpabuf_put_be24(buf, OUI_WFA);
-	wpabuf_put_u8(buf, DPP_OUI_TYPE);
-	wpabuf_put_u8(buf, 0x01);
-
-	/* GAS Query */
-	wpabuf_put_le16(buf, wpabuf_len(conf_req));
-	wpabuf_put_buf(buf, conf_req);
-	wpabuf_free(conf_req);
-
 	wpa_printf(MSG_DEBUG, "DPP: GAS request to " MACSTR " (freq %u MHz)",
 		   MAC2STR(auth->peer_mac_addr), auth->curr_freq);
 
@@ -1281,6 +894,63 @@
 }
 
 
+#ifdef CONFIG_DPP2
+
+static void hostapd_dpp_config_result_wait_timeout(void *eloop_ctx,
+						   void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
+	if (!auth || !auth->waiting_conf_result)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Timeout while waiting for Configuration Result");
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+	dpp_auth_deinit(auth);
+	hapd->dpp_auth = NULL;
+}
+
+
+static void hostapd_dpp_rx_conf_result(struct hostapd_data *hapd, const u8 *src,
+				       const u8 *hdr, const u8 *buf, size_t len)
+{
+	struct dpp_authentication *auth = hapd->dpp_auth;
+	enum dpp_status_error status;
+
+	wpa_printf(MSG_DEBUG, "DPP: Configuration Result from " MACSTR,
+		   MAC2STR(src));
+
+	if (!auth || !auth->waiting_conf_result) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Configuration waiting for result - 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;
+	}
+
+	status = dpp_conf_result_rx(auth, hdr, buf, len);
+
+	hostapd_drv_send_action_cancel_wait(hapd);
+	hostapd_dpp_listen_stop(hapd);
+	if (status == DPP_STATUS_OK)
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+	else
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+	dpp_auth_deinit(auth);
+	hapd->dpp_auth = NULL;
+	eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd,
+			     NULL);
+}
+
+#endif /* CONFIG_DPP2 */
+
+
 static void hostapd_dpp_send_peer_disc_resp(struct hostapd_data *hapd,
 					    const u8 *src, unsigned int freq,
 					    u8 trans_id,
@@ -1596,24 +1266,10 @@
 				wpabuf_head(msg), wpabuf_len(msg));
 	wpabuf_free(msg);
 
-	bi = os_zalloc(sizeof(*bi));
+	bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq);
 	if (!bi)
 		return;
-	bi->id = hapd_dpp_next_id(hapd);
-	bi->type = DPP_BOOTSTRAP_PKEX;
-	os_memcpy(bi->mac_addr, src, 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;
-	dpp_pkex_free(pkex);
 	hapd->dpp_pkex = NULL;
-	if (dpp_bootstrap_key_hash(bi) < 0) {
-		dpp_bootstrap_info_free(bi);
-		return;
-	}
-	dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list);
 }
 
 
@@ -1623,7 +1279,7 @@
 				       unsigned int freq)
 {
 	int res;
-	struct dpp_bootstrap_info *bi, *own_bi;
+	struct dpp_bootstrap_info *bi;
 	struct dpp_pkex *pkex = hapd->dpp_pkex;
 	char cmd[500];
 
@@ -1641,26 +1297,10 @@
 		return;
 	}
 
-	own_bi = pkex->own_bi;
-
-	bi = os_zalloc(sizeof(*bi));
+	bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq);
 	if (!bi)
 		return;
-	bi->id = hapd_dpp_next_id(hapd);
-	bi->type = DPP_BOOTSTRAP_PKEX;
-	os_memcpy(bi->mac_addr, src, ETH_ALEN);
-	bi->num_freq = 1;
-	bi->freq[0] = freq;
-	bi->curve = own_bi->curve;
-	bi->pubkey = pkex->peer_bootstrap_key;
-	pkex->peer_bootstrap_key = NULL;
-	dpp_pkex_free(pkex);
 	hapd->dpp_pkex = NULL;
-	if (dpp_bootstrap_key_hash(bi) < 0) {
-		dpp_bootstrap_info_free(bi);
-		return;
-	}
-	dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list);
 
 	os_snprintf(cmd, sizeof(cmd), " peer=%u %s",
 		    bi->id,
@@ -1744,6 +1384,11 @@
 		hostapd_dpp_rx_pkex_commit_reveal_resp(hapd, src, hdr, buf, len,
 						       freq);
 		break;
+#ifdef CONFIG_DPP2
+	case DPP_PA_CONFIGURATION_RESULT:
+		hostapd_dpp_rx_conf_result(hapd, src, hdr, buf, len);
+		break;
+#endif /* CONFIG_DPP2 */
 	default:
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Ignored unsupported frame subtype %d", type);
@@ -1790,11 +1435,28 @@
 
 void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok)
 {
-	if (!hapd->dpp_auth)
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
+	if (!auth)
 		return;
 
+	wpa_printf(MSG_DEBUG, "DPP: Configuration exchange completed (ok=%d)",
+		   ok);
 	eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
 	eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+#ifdef CONFIG_DPP2
+	if (ok && 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;
+		eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout,
+				     hapd, NULL);
+		eloop_register_timeout(2, 0,
+				       hostapd_dpp_config_result_wait_timeout,
+				       hapd, NULL);
+		return;
+	}
+#endif /* CONFIG_DPP2 */
 	hostapd_drv_send_action_cancel_wait(hapd);
 
 	if (ok)
@@ -1806,93 +1468,6 @@
 }
 
 
-static unsigned int hostapd_dpp_next_configurator_id(struct hostapd_data *hapd)
-{
-	struct dpp_configurator *conf;
-	unsigned int max_id = 0;
-
-	dl_list_for_each(conf, &hapd->iface->interfaces->dpp_configurator,
-			 struct dpp_configurator, list) {
-		if (conf->id > max_id)
-			max_id = conf->id;
-	}
-	return max_id + 1;
-}
-
-
-int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd)
-{
-	char *curve = NULL;
-	char *key = NULL;
-	u8 *privkey = NULL;
-	size_t privkey_len = 0;
-	int ret = -1;
-	struct dpp_configurator *conf = NULL;
-
-	curve = get_param(cmd, " curve=");
-	key = get_param(cmd, " key=");
-
-	if (key) {
-		privkey_len = os_strlen(key) / 2;
-		privkey = os_malloc(privkey_len);
-		if (!privkey ||
-		    hexstr2bin(key, privkey, privkey_len) < 0)
-			goto fail;
-	}
-
-	conf = dpp_keygen_configurator(curve, privkey, privkey_len);
-	if (!conf)
-		goto fail;
-
-	conf->id = hostapd_dpp_next_configurator_id(hapd);
-	dl_list_add(&hapd->iface->interfaces->dpp_configurator, &conf->list);
-	ret = conf->id;
-	conf = NULL;
-fail:
-	os_free(curve);
-	str_clear_free(key);
-	bin_clear_free(privkey, privkey_len);
-	dpp_configurator_free(conf);
-	return ret;
-}
-
-
-static int dpp_configurator_del(struct hapd_interfaces *ifaces, unsigned int id)
-{
-	struct dpp_configurator *conf, *tmp;
-	int found = 0;
-
-	dl_list_for_each_safe(conf, tmp, &ifaces->dpp_configurator,
-			      struct dpp_configurator, list) {
-		if (id && conf->id != id)
-			continue;
-		found = 1;
-		dl_list_del(&conf->list);
-		dpp_configurator_free(conf);
-	}
-
-	if (id == 0)
-		return 0; /* flush succeeds regardless of entries found */
-	return found ? 0 : -1;
-}
-
-
-int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id)
-{
-	unsigned int id_val;
-
-	if (os_strcmp(id, "*") == 0) {
-		id_val = 0;
-	} else {
-		id_val = atoi(id);
-		if (id_val == 0)
-			return -1;
-	}
-
-	return dpp_configurator_del(hapd->iface->interfaces, id_val);
-}
-
-
 int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd)
 {
 	struct dpp_authentication *auth;
@@ -1905,7 +1480,8 @@
 
 	curve = get_param(cmd, " curve=");
 	hostapd_dpp_set_testing_options(hapd, auth);
-	if (hostapd_dpp_set_configurator(hapd, auth, cmd) == 0 &&
+	if (dpp_set_configurator(hapd->iface->interfaces->dpp, hapd->msg_ctx,
+				 auth, cmd) == 0 &&
 	    dpp_configurator_own_config(auth, curve, 1) == 0) {
 		hostapd_dpp_handle_config_obj(hapd, auth);
 		ret = 0;
@@ -1918,19 +1494,6 @@
 }
 
 
-int hostapd_dpp_configurator_get_key(struct hostapd_data *hapd, unsigned int id,
-				     char *buf, size_t buflen)
-{
-	struct dpp_configurator *conf;
-
-	conf = hostapd_dpp_configurator_get_id(hapd, id);
-	if (!conf)
-		return -1;
-
-	return dpp_configurator_get_key(conf, buf, buflen);
-}
-
-
 int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd)
 {
 	struct dpp_bootstrap_info *own_bi;
@@ -1940,7 +1503,7 @@
 	if (!pos)
 		return -1;
 	pos += 5;
-	own_bi = dpp_bootstrap_get_id(hapd, atoi(pos));
+	own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos));
 	if (!own_bi) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Identified bootstrap info not found");
@@ -2070,6 +1633,10 @@
 	eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
 	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_config_result_wait_timeout, hapd,
+			     NULL);
+#endif /* CONFIG_DPP2 */
 	dpp_auth_deinit(hapd->dpp_auth);
 	hapd->dpp_auth = NULL;
 	hostapd_dpp_pkex_remove(hapd, "*");
@@ -2077,20 +1644,3 @@
 	os_free(hapd->dpp_configurator_params);
 	hapd->dpp_configurator_params = NULL;
 }
-
-
-void hostapd_dpp_init_global(struct hapd_interfaces *ifaces)
-{
-	dl_list_init(&ifaces->dpp_bootstrap);
-	dl_list_init(&ifaces->dpp_configurator);
-	ifaces->dpp_init_done = 1;
-}
-
-
-void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces)
-{
-	if (!ifaces->dpp_init_done)
-		return;
-	dpp_bootstrap_del(ifaces, 0);
-	dpp_configurator_del(ifaces, 0);
-}
diff --git a/src/ap/dpp_hostapd.h b/src/ap/dpp_hostapd.h
index 3ef7c14..449ca16 100644
--- a/src/ap/dpp_hostapd.h
+++ b/src/ap/dpp_hostapd.h
@@ -10,12 +10,6 @@
 #define DPP_HOSTAPD_H
 
 int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd);
-int hostapd_dpp_bootstrap_gen(struct hostapd_data *hapd, const char *cmd);
-int hostapd_dpp_bootstrap_remove(struct hostapd_data *hapd, const char *id);
-const char * hostapd_dpp_bootstrap_get_uri(struct hostapd_data *hapd,
-					   unsigned int id);
-int hostapd_dpp_bootstrap_info(struct hostapd_data *hapd, int id,
-			       char *reply, int reply_size);
 int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd);
 int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd);
 void hostapd_dpp_listen_stop(struct hostapd_data *hapd);
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 54be3b5..8ddf754 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -15,6 +15,7 @@
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
+#include "common/dpp.h"
 #include "crypto/random.h"
 #include "p2p/p2p.h"
 #include "wps/wps.h"
@@ -305,6 +306,7 @@
 			return -1;
 		}
 		res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+					  hapd->iface->freq,
 					  ie, ielen,
 					  elems.mdie, elems.mdie_len,
 					  elems.owe_dh, elems.owe_dh_len);
@@ -564,6 +566,38 @@
 	}
 #endif /* CONFIG_OWE */
 
+#ifdef CONFIG_DPP2
+		dpp_pfs_free(sta->dpp_pfs);
+		sta->dpp_pfs = NULL;
+
+		if ((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) {
+			sta->dpp_pfs = dpp_pfs_init(
+				wpabuf_head(hapd->conf->dpp_netaccesskey),
+				wpabuf_len(hapd->conf->dpp_netaccesskey));
+			if (!sta->dpp_pfs) {
+				wpa_printf(MSG_DEBUG,
+					   "DPP: Could not initialize PFS");
+				/* Try to continue without PFS */
+				goto pfs_fail;
+			}
+
+			if (dpp_pfs_process(sta->dpp_pfs, elems.owe_dh,
+					    elems.owe_dh_len) < 0) {
+				dpp_pfs_free(sta->dpp_pfs);
+				sta->dpp_pfs = NULL;
+				reason = WLAN_REASON_UNSPECIFIED;
+				goto fail;
+			}
+		}
+
+		wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ?
+				   sta->dpp_pfs->secret : NULL);
+	pfs_fail:
+#endif /* CONFIG_DPP2 */
+
 #if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_FILS) || defined(CONFIG_OWE)
 	hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
 
@@ -1072,6 +1106,7 @@
 }
 
 
+#ifndef NEED_AP_MLME
 static void hostapd_action_rx(struct hostapd_data *hapd,
 			      struct rx_mgmt *drv_mgmt)
 {
@@ -1084,7 +1119,7 @@
 	if (drv_mgmt->frame_len < IEEE80211_HDRLEN + 2 + 1)
 		return;
 
-	plen = drv_mgmt->frame_len - IEEE80211_HDRLEN - 1;
+	plen = drv_mgmt->frame_len - IEEE80211_HDRLEN;
 
 	mgmt = (struct ieee80211_mgmt *) drv_mgmt->frame;
 	fc = le_to_host16(mgmt->frame_control);
@@ -1104,19 +1139,20 @@
 	}
 #ifdef CONFIG_IEEE80211R_AP
 	if (mgmt->u.action.category == WLAN_ACTION_FT) {
-		const u8 *payload = drv_mgmt->frame + 24 + 1;
-
-		wpa_ft_action_rx(sta->wpa_sm, payload, plen);
+		wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, plen);
+		return;
 	}
 #endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_IEEE80211W
-	if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY && plen >= 4) {
+	if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY) {
 		ieee802_11_sa_query_action(hapd, mgmt, drv_mgmt->frame_len);
+		return;
 	}
 #endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_WNM_AP
 	if (mgmt->u.action.category == WLAN_ACTION_WNM) {
 		ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len);
+		return;
 	}
 #endif /* CONFIG_WNM_AP */
 #ifdef CONFIG_FST
@@ -1126,7 +1162,7 @@
 	}
 #endif /* CONFIG_FST */
 #ifdef CONFIG_DPP
-	if (plen >= 1 + 4 &&
+	if (plen >= 2 + 4 &&
 	    mgmt->u.action.u.vs_public_action.action ==
 	    WLAN_PA_VENDOR_SPECIFIC &&
 	    WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
@@ -1143,6 +1179,7 @@
 	}
 #endif /* CONFIG_DPP */
 }
+#endif /* NEED_AP_MLME */
 
 
 #ifdef NEED_AP_MLME
@@ -1604,10 +1641,10 @@
 		if (!data->rx_mgmt.frame)
 			break;
 #ifdef NEED_AP_MLME
-		if (hostapd_mgmt_rx(hapd, &data->rx_mgmt) > 0)
-			break;
-#endif /* NEED_AP_MLME */
+		hostapd_mgmt_rx(hapd, &data->rx_mgmt);
+#else /* NEED_AP_MLME */
 		hostapd_action_rx(hapd, &data->rx_mgmt);
+#endif /* NEED_AP_MLME */
 		break;
 	case EVENT_RX_PROBE_REQ:
 		if (data->rx_probe_req.sa == NULL ||
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 342585f..77742f4 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / Initialization and configuration
- * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -348,7 +348,7 @@
 
 	if (!hapd->started) {
 		wpa_printf(MSG_ERROR, "%s: Interface %s wasn't started",
-			   __func__, hapd->conf->iface);
+			   __func__, hapd->conf ? hapd->conf->iface : "N/A");
 		return;
 	}
 	hapd->started = 0;
@@ -417,6 +417,20 @@
 
 	hostapd_clean_rrm(hapd);
 	fils_hlp_deinit(hapd);
+
+#ifdef CONFIG_SAE
+	{
+		struct hostapd_sae_commit_queue *q;
+
+		while ((q = dl_list_first(&hapd->sae_commit_queue,
+					  struct hostapd_sae_commit_queue,
+					  list))) {
+			dl_list_del(&q->list);
+			os_free(q);
+		}
+	}
+	eloop_cancel_timeout(auth_sae_process_commit, hapd, NULL);
+#endif /* CONFIG_SAE */
 }
 
 
@@ -431,7 +445,7 @@
 static void hostapd_cleanup(struct hostapd_data *hapd)
 {
 	wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s))", __func__, hapd,
-		   hapd->conf->iface);
+		   hapd->conf ? hapd->conf->iface : "N/A");
 	if (hapd->iface->interfaces &&
 	    hapd->iface->interfaces->ctrl_iface_deinit) {
 		wpa_msg(hapd->msg_ctx, MSG_INFO, WPA_EVENT_TERMINATING);
@@ -506,7 +520,7 @@
 
 static void hostapd_clear_wep(struct hostapd_data *hapd)
 {
-	if (hapd->drv_priv && !hapd->iface->driver_ap_teardown) {
+	if (hapd->drv_priv && !hapd->iface->driver_ap_teardown && hapd->conf) {
 		hostapd_set_privacy(hapd, 0);
 		hostapd_broadcast_wep_clear(hapd);
 	}
@@ -658,8 +672,10 @@
 	for (i = 5; i > 5 - j; i--)
 		mask[i] = 0;
 	j = bits % 8;
-	while (j--)
+	while (j) {
+		j--;
 		mask[i] <<= 1;
+	}
 
 skip_mask_ext:
 	wpa_printf(MSG_DEBUG, "BSS count %lu, BSSID mask " MACSTR " (%d bits)",
@@ -1867,15 +1883,17 @@
 		}
 	}
 
-	if (hapd->iconf->rts_threshold > -1 &&
-	    hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) {
+	if (hapd->iconf->rts_threshold >= -1 &&
+	    hostapd_set_rts(hapd, hapd->iconf->rts_threshold) &&
+	    hapd->iconf->rts_threshold >= -1) {
 		wpa_printf(MSG_ERROR, "Could not set RTS threshold for "
 			   "kernel driver");
 		goto fail;
 	}
 
-	if (hapd->iconf->fragm_threshold > -1 &&
-	    hostapd_set_frag(hapd, hapd->iconf->fragm_threshold)) {
+	if (hapd->iconf->fragm_threshold >= -1 &&
+	    hostapd_set_frag(hapd, hapd->iconf->fragm_threshold) &&
+	    hapd->iconf->fragm_threshold != -1) {
 		wpa_printf(MSG_ERROR, "Could not set fragmentation threshold "
 			   "for kernel driver");
 		goto fail;
@@ -1888,11 +1906,14 @@
 		if (j)
 			os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN);
 		if (hostapd_setup_bss(hapd, j == 0)) {
-			do {
+			for (;;) {
 				hapd = iface->bss[j];
 				hostapd_bss_deinit_no_free(hapd);
 				hostapd_free_hapd_data(hapd);
-			} while (j-- > 0);
+				if (j == 0)
+					break;
+				j--;
+			}
 			goto fail;
 		}
 		if (is_zero_ether_addr(hapd->conf->bssid))
@@ -2145,6 +2166,9 @@
 	dl_list_init(&hapd->l2_queue);
 	dl_list_init(&hapd->l2_oui_queue);
 #endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_SAE
+	dl_list_init(&hapd->sae_commit_queue);
+#endif /* CONFIG_SAE */
 
 	return hapd;
 }
@@ -2155,7 +2179,7 @@
 	if (!hapd)
 		return;
 	wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__,
-		   hapd->conf->iface);
+		   hapd->conf ? hapd->conf->iface : "N/A");
 	hostapd_bss_deinit_no_free(hapd);
 	wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
 	hostapd_cleanup(hapd);
@@ -2182,7 +2206,7 @@
 	}
 #endif /* CONFIG_FST */
 
-	for (j = iface->num_bss - 1; j >= 0; j--) {
+	for (j = (int) iface->num_bss - 1; j >= 0; j--) {
 		if (!iface->bss)
 			break;
 		hostapd_bss_deinit(iface->bss[j]);
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 85e63d3..607bb95 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -66,9 +66,7 @@
 	int eloop_initialized;
 
 #ifdef CONFIG_DPP
-	int dpp_init_done;
-	struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */
-	struct dl_list dpp_configurator; /* struct dpp_configurator */
+	struct dpp_global *dpp;
 #endif /* CONFIG_DPP */
 };
 
@@ -129,6 +127,13 @@
 	int stationary;
 };
 
+struct hostapd_sae_commit_queue {
+	struct dl_list list;
+	int rssi;
+	size_t len;
+	u8 msg[];
+};
+
 /**
  * struct hostapd_data - hostapd per-BSS data structure
  */
@@ -307,7 +312,10 @@
 	/** Key used for generating SAE anti-clogging tokens */
 	u8 sae_token_key[8];
 	struct os_reltime last_sae_token_key_update;
+	u16 sae_token_idx;
+	u16 sae_pending_token_idx[256];
 	int dot11RSNASAERetransPeriod; /* msec */
+	struct dl_list sae_commit_queue; /* struct hostapd_sae_commit_queue */
 #endif /* CONFIG_SAE */
 
 #ifdef CONFIG_TESTING_OPTIONS
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 376bbd8..28aca46 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -21,6 +21,7 @@
 #include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
 #include "common/sae.h"
+#include "common/dpp.h"
 #include "common/ocv.h"
 #include "radius/radius.h"
 #include "radius/radius_client.h"
@@ -62,6 +63,9 @@
 		       const u8 *msk, size_t msk_len,
 		       int *is_pub);
 #endif /* CONFIG_FILS */
+static void handle_auth(struct hostapd_data *hapd,
+			const struct ieee80211_mgmt *mgmt, size_t len,
+			int rssi, int from_queue);
 
 
 u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid)
@@ -420,6 +424,15 @@
 		return NULL;
 	}
 
+	if (pw && pw->vlan_id) {
+		if (!sta->sae->tmp) {
+			wpa_printf(MSG_INFO,
+				   "SAE: No temporary data allocated - cannot store VLAN ID");
+			return NULL;
+		}
+		sta->sae->tmp->vlan_id = pw->vlan_id;
+	}
+
 	buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN +
 			   (rx_id ? 3 + os_strlen(rx_id) : 0));
 	if (buf == NULL)
@@ -509,21 +522,57 @@
 			return 1;
 	}
 
+	/* In addition to already existing open SAE sessions, check whether
+	 * there are enough pending commit messages in the processing queue to
+	 * potentially result in too many open sessions. */
+	if (open + dl_list_len(&hapd->sae_commit_queue) >=
+	    hapd->conf->sae_anti_clogging_threshold)
+		return 1;
+
 	return 0;
 }
 
 
+static u8 sae_token_hash(struct hostapd_data *hapd, const u8 *addr)
+{
+	u8 hash[SHA256_MAC_LEN];
+
+	hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
+		    addr, ETH_ALEN, hash);
+	return hash[0];
+}
+
+
 static int check_sae_token(struct hostapd_data *hapd, const u8 *addr,
 			   const u8 *token, size_t token_len)
 {
 	u8 mac[SHA256_MAC_LEN];
+	const u8 *addrs[2];
+	size_t len[2];
+	u16 token_idx;
+	u8 idx;
 
 	if (token_len != SHA256_MAC_LEN)
 		return -1;
-	if (hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
-			addr, ETH_ALEN, mac) < 0 ||
-	    os_memcmp_const(token, mac, SHA256_MAC_LEN) != 0)
+	idx = sae_token_hash(hapd, addr);
+	token_idx = hapd->sae_pending_token_idx[idx];
+	if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) {
+		wpa_printf(MSG_DEBUG, "SAE: Invalid anti-clogging token from "
+			   MACSTR " - token_idx 0x%04x, expected 0x%04x",
+			   MAC2STR(addr), WPA_GET_BE16(token), token_idx);
 		return -1;
+	}
+
+	addrs[0] = addr;
+	len[0] = ETH_ALEN;
+	addrs[1] = token;
+	len[1] = 2;
+	if (hmac_sha256_vector(hapd->sae_token_key, sizeof(hapd->sae_token_key),
+			       2, addrs, len, mac) < 0 ||
+	    os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0)
+		return -1;
+
+	hapd->sae_pending_token_idx[idx] = 0; /* invalidate used token */
 
 	return 0;
 }
@@ -535,16 +584,25 @@
 	struct wpabuf *buf;
 	u8 *token;
 	struct os_reltime now;
+	u8 idx[2];
+	const u8 *addrs[2];
+	size_t len[2];
+	u8 p_idx;
+	u16 token_idx;
 
 	os_get_reltime(&now);
 	if (!os_reltime_initialized(&hapd->last_sae_token_key_update) ||
-	    os_reltime_expired(&now, &hapd->last_sae_token_key_update, 60)) {
+	    os_reltime_expired(&now, &hapd->last_sae_token_key_update, 60) ||
+	    hapd->sae_token_idx == 0xffff) {
 		if (random_get_bytes(hapd->sae_token_key,
 				     sizeof(hapd->sae_token_key)) < 0)
 			return NULL;
 		wpa_hexdump(MSG_DEBUG, "SAE: Updated token key",
 			    hapd->sae_token_key, sizeof(hapd->sae_token_key));
 		hapd->last_sae_token_key_update = now;
+		hapd->sae_token_idx = 0;
+		os_memset(hapd->sae_pending_token_idx, 0,
+			  sizeof(hapd->sae_pending_token_idx));
 	}
 
 	buf = wpabuf_alloc(sizeof(le16) + SHA256_MAC_LEN);
@@ -553,9 +611,25 @@
 
 	wpabuf_put_le16(buf, group); /* Finite Cyclic Group */
 
+	p_idx = sae_token_hash(hapd, addr);
+	token_idx = hapd->sae_pending_token_idx[p_idx];
+	if (!token_idx) {
+		hapd->sae_token_idx++;
+		token_idx = hapd->sae_token_idx;
+		hapd->sae_pending_token_idx[p_idx] = token_idx;
+	}
+	WPA_PUT_BE16(idx, token_idx);
 	token = wpabuf_put(buf, SHA256_MAC_LEN);
-	hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
-		    addr, ETH_ALEN, token);
+	addrs[0] = addr;
+	len[0] = ETH_ALEN;
+	addrs[1] = idx;
+	len[1] = sizeof(idx);
+	if (hmac_sha256_vector(hapd->sae_token_key, sizeof(hapd->sae_token_key),
+			       2, addrs, len, token) < 0) {
+		wpabuf_free(buf);
+		return NULL;
+	}
+	WPA_PUT_BE16(token, token_idx);
 
 	return buf;
 }
@@ -629,6 +703,35 @@
 
 void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
 {
+#ifndef CONFIG_NO_VLAN
+	struct vlan_description vlan_desc;
+
+	if (sta->sae->tmp && sta->sae->tmp->vlan_id > 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Assign STA " MACSTR
+			   " to VLAN ID %d",
+			   MAC2STR(sta->addr), sta->sae->tmp->vlan_id);
+
+		os_memset(&vlan_desc, 0, sizeof(vlan_desc));
+		vlan_desc.notempty = 1;
+		vlan_desc.untagged = sta->sae->tmp->vlan_id;
+		if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
+			wpa_printf(MSG_INFO,
+				   "Invalid VLAN ID %d in sae_password",
+				   sta->sae->tmp->vlan_id);
+			return;
+		}
+
+		if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0 ||
+		    ap_sta_bind_vlan(hapd, sta) < 0) {
+			wpa_printf(MSG_INFO,
+				   "Failed to assign VLAN ID %d from sae_password to "
+				   MACSTR, sta->sae->tmp->vlan_id,
+				   MAC2STR(sta->addr));
+			return;
+		}
+	}
+#endif /* CONFIG_NO_VLAN */
+
 	sta->flags |= WLAN_STA_AUTH;
 	sta->auth_alg = WLAN_AUTH_SAE;
 	mlme_authenticate_indication(hapd, sta);
@@ -640,7 +743,7 @@
 
 
 static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
-		       const u8 *bssid, u8 auth_transaction)
+		       const u8 *bssid, u8 auth_transaction, int allow_reuse)
 {
 	int ret;
 
@@ -653,7 +756,8 @@
 	switch (sta->sae->state) {
 	case SAE_NOTHING:
 		if (auth_transaction == 1) {
-			ret = auth_sae_send_commit(hapd, sta, bssid, 1);
+			ret = auth_sae_send_commit(hapd, sta, bssid,
+						   !allow_reuse);
 			if (ret)
 				return ret;
 			sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
@@ -742,7 +846,8 @@
 			 * step to get to Accepted without waiting for
 			 * additional events.
 			 */
-			return sae_sm_step(hapd, sta, bssid, auth_transaction);
+			return sae_sm_step(hapd, sta, bssid, auth_transaction,
+					   0);
 		}
 		break;
 	case SAE_CONFIRMED:
@@ -812,18 +917,21 @@
 {
 	struct sae_data *sae = sta->sae;
 	int i, *groups = hapd->conf->sae_groups;
+	int default_groups[] = { 19, 0 };
 
 	if (sae->state != SAE_COMMITTED)
 		return;
 
 	wpa_printf(MSG_DEBUG, "SAE: Previously selected group: %d", sae->group);
 
-	for (i = 0; groups && groups[i] > 0; i++) {
+	if (!groups)
+		groups = default_groups;
+	for (i = 0; groups[i] > 0; i++) {
 		if (sae->group == groups[i])
 			break;
 	}
 
-	if (!groups || groups[i] <= 0) {
+	if (groups[i] <= 0) {
 		wpa_printf(MSG_DEBUG,
 			   "SAE: Previously selected group not found from the current configuration");
 		return;
@@ -852,11 +960,15 @@
 {
 	int resp = WLAN_STATUS_SUCCESS;
 	struct wpabuf *data = NULL;
+	int *groups = hapd->conf->sae_groups;
+	int default_groups[] = { 19, 0 };
+	const u8 *pos, *end;
+
+	if (!groups)
+		groups = default_groups;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	if (hapd->conf->sae_reflection_attack && auth_transaction == 1) {
-		const u8 *pos, *end;
-
 		wpa_printf(MSG_DEBUG, "SAE: TESTING - reflection attack");
 		pos = mgmt->u.auth.variable;
 		end = ((const u8 *) mgmt) + len;
@@ -899,8 +1011,10 @@
 	}
 
 	if (auth_transaction == 1) {
-		const u8 *token = NULL, *pos, *end;
+		const u8 *token = NULL;
 		size_t token_len = 0;
+		int allow_reuse = 0;
+
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
 			       "start SAE authentication (RX commit, status=%u)",
@@ -917,8 +1031,7 @@
 				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 				goto reply;
 			}
-			resp = sae_group_allowed(sta->sae,
-						 hapd->conf->sae_groups,
+			resp = sae_group_allowed(sta->sae, groups,
 						 WPA_GET_LE16(pos));
 			if (resp != WLAN_STATUS_SUCCESS) {
 				wpa_printf(MSG_ERROR,
@@ -979,15 +1092,28 @@
 			 * to use a different group and that would not be
 			 * allowed if we remain in Committed state with the
 			 * previously set parameters. */
-			sae_set_state(sta, SAE_NOTHING,
-				      "Clear existing state to allow restart");
-			sae_clear_data(sta->sae);
+			pos = mgmt->u.auth.variable;
+			end = ((const u8 *) mgmt) + len;
+			if (end - pos >= (int) sizeof(le16) &&
+			    sae_group_allowed(sta->sae, groups,
+					      WPA_GET_LE16(pos)) ==
+			    WLAN_STATUS_SUCCESS) {
+				/* Do not waste resources deriving the same PWE
+				 * again since the same group is reused. */
+				sae_set_state(sta, SAE_NOTHING,
+					      "Allow previous PWE to be reused");
+				allow_reuse = 1;
+			} else {
+				sae_set_state(sta, SAE_NOTHING,
+					      "Clear existing state to allow restart");
+				sae_clear_data(sta->sae);
+			}
 		}
 
 		resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable,
 					((const u8 *) mgmt) + len -
 					mgmt->u.auth.variable, &token,
-					&token_len, hapd->conf->sae_groups);
+					&token_len, groups);
 		if (resp == SAE_SILENTLY_DISCARD) {
 			wpa_printf(MSG_DEBUG,
 				   "SAE: Drop commit message from " MACSTR " due to reflection attack",
@@ -1017,7 +1143,7 @@
 		if (resp != WLAN_STATUS_SUCCESS)
 			goto reply;
 
-		if (!token && use_sae_anti_clogging(hapd)) {
+		if (!token && use_sae_anti_clogging(hapd) && !allow_reuse) {
 			wpa_printf(MSG_DEBUG,
 				   "SAE: Request anti-clogging token from "
 				   MACSTR, MAC2STR(sta->addr));
@@ -1030,7 +1156,8 @@
 			goto reply;
 		}
 
-		resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction);
+		resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction,
+				   allow_reuse);
 	} else if (auth_transaction == 2) {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
@@ -1071,7 +1198,7 @@
 			}
 			sta->sae->rc = peer_send_confirm;
 		}
-		resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction);
+		resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction, 0);
 	} else {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
@@ -1084,6 +1211,15 @@
 
 reply:
 	if (resp != WLAN_STATUS_SUCCESS) {
+		pos = mgmt->u.auth.variable;
+		end = ((const u8 *) mgmt) + len;
+
+		/* Copy the Finite Cyclic Group field from the request if we
+		 * rejected it as unsupported group. */
+		if (resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
+		    !data && end - pos >= 2)
+			data = wpabuf_alloc_copy(pos, 2);
+
 		send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
 				auth_transaction, resp,
 				data ? wpabuf_head(data) : (u8 *) "",
@@ -1131,6 +1267,105 @@
 	return 0;
 }
 
+
+void auth_sae_process_commit(void *eloop_ctx, void *user_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct hostapd_sae_commit_queue *q;
+	unsigned int queue_len;
+
+	q = dl_list_first(&hapd->sae_commit_queue,
+			  struct hostapd_sae_commit_queue, list);
+	if (!q)
+		return;
+	wpa_printf(MSG_DEBUG,
+		   "SAE: Process next available message from queue");
+	dl_list_del(&q->list);
+	handle_auth(hapd, (const struct ieee80211_mgmt *) q->msg, q->len,
+		    q->rssi, 1);
+	os_free(q);
+
+	if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL))
+		return;
+	queue_len = dl_list_len(&hapd->sae_commit_queue);
+	eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit,
+			       hapd, NULL);
+}
+
+
+static void auth_sae_queue(struct hostapd_data *hapd,
+			   const struct ieee80211_mgmt *mgmt, size_t len,
+			   int rssi)
+{
+	struct hostapd_sae_commit_queue *q, *q2;
+	unsigned int queue_len;
+	const struct ieee80211_mgmt *mgmt2;
+
+	queue_len = dl_list_len(&hapd->sae_commit_queue);
+	if (queue_len >= 15) {
+		wpa_printf(MSG_DEBUG,
+			   "SAE: No more room in message queue - drop the new frame from "
+			   MACSTR, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "SAE: Queue Authentication message from "
+		   MACSTR " for processing (queue_len %u)", MAC2STR(mgmt->sa),
+		   queue_len);
+	q = os_zalloc(sizeof(*q) + len);
+	if (!q)
+		return;
+	q->rssi = rssi;
+	q->len = len;
+	os_memcpy(q->msg, mgmt, len);
+
+	/* Check whether there is already a queued Authentication frame from the
+	 * same station with the same transaction number and if so, replace that
+	 * queue entry with the new one. This avoids issues with a peer that
+	 * sends multiple times (e.g., due to frequent SAE retries). There is no
+	 * point in us trying to process the old attempts after a new one has
+	 * obsoleted them. */
+	dl_list_for_each(q2, &hapd->sae_commit_queue,
+			 struct hostapd_sae_commit_queue, list) {
+		mgmt2 = (const struct ieee80211_mgmt *) q2->msg;
+		if (os_memcmp(mgmt->sa, mgmt2->sa, ETH_ALEN) == 0 &&
+		    mgmt->u.auth.auth_transaction ==
+		    mgmt2->u.auth.auth_transaction) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE: Replace queued message from same STA with same transaction number");
+			dl_list_add(&q2->list, &q->list);
+			dl_list_del(&q2->list);
+			os_free(q2);
+			goto queued;
+		}
+	}
+
+	/* No pending identical entry, so add to the end of the queue */
+	dl_list_add_tail(&hapd->sae_commit_queue, &q->list);
+
+queued:
+	if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL))
+		return;
+	eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit,
+			       hapd, NULL);
+}
+
+
+static int auth_sae_queued_addr(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct hostapd_sae_commit_queue *q;
+	const struct ieee80211_mgmt *mgmt;
+
+	dl_list_for_each(q, &hapd->sae_commit_queue,
+			 struct hostapd_sae_commit_queue, list) {
+		mgmt = (const struct ieee80211_mgmt *) q->msg;
+		if (os_memcmp(addr, mgmt->sa, ETH_ALEN) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
 #endif /* CONFIG_SAE */
 
 
@@ -1290,6 +1525,7 @@
 	}
 
 	res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+				  hapd->iface->freq,
 				  elems.rsn_ie - 2, elems.rsn_ie_len + 2,
 				  elems.mdie, elems.mdie_len, NULL, 0);
 	resp = wpa_res_to_status_code(res);
@@ -1561,7 +1797,8 @@
 			}
 
 			sta->fils_erp_pmkid_set = 0;
-			if (wpa_auth_pmksa_add2(
+			if (!hapd->conf->disable_pmksa_caching &&
+			    wpa_auth_pmksa_add2(
 				    hapd->wpa_auth, sta->addr,
 				    pmk, pmk_len,
 				    sta->fils_erp_pmkid,
@@ -1761,7 +1998,7 @@
 
 static void handle_auth(struct hostapd_data *hapd,
 			const struct ieee80211_mgmt *mgmt, size_t len,
-			int rssi)
+			int rssi, int from_queue)
 {
 	u16 auth_alg, auth_transaction, status_code;
 	u16 resp = WLAN_STATUS_SUCCESS;
@@ -1808,11 +2045,12 @@
 
 	wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d "
 		   "auth_transaction=%d status_code=%d wep=%d%s "
-		   "seq_ctrl=0x%x%s",
+		   "seq_ctrl=0x%x%s%s",
 		   MAC2STR(mgmt->sa), auth_alg, auth_transaction,
 		   status_code, !!(fc & WLAN_FC_ISWEP),
 		   challenge ? " challenge" : "",
-		   seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
+		   seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "",
+		   from_queue ? " (from queue)" : "");
 
 #ifdef CONFIG_NO_RC4
 	if (auth_alg == WLAN_AUTH_SHARED_KEY) {
@@ -1940,6 +2178,22 @@
 	if (res == HOSTAPD_ACL_PENDING)
 		return;
 
+#ifdef CONFIG_SAE
+	if (auth_alg == WLAN_AUTH_SAE && !from_queue &&
+	    (auth_transaction == 1 ||
+	     (auth_transaction == 2 && auth_sae_queued_addr(hapd, mgmt->sa)))) {
+		/* Handle SAE Authentication commit message through a queue to
+		 * provide more control for postponing the needed heavy
+		 * processing under a possible DoS attack scenario. In addition,
+		 * queue SAE Authentication confirm message if there happens to
+		 * be a queued commit message from the same peer. This is needed
+		 * to avoid reordering Authentication frames within the same
+		 * SAE exchange. */
+		auth_sae_queue(hapd, mgmt, len, rssi);
+		return;
+	}
+#endif /* CONFIG_SAE */
+
 	sta = ap_get_sta(hapd, mgmt->sa);
 	if (sta) {
 		sta->flags &= ~WLAN_STA_PENDING_FILS_ERP;
@@ -2259,28 +2513,30 @@
 		}
 	}
 
-	if (multi_ap_value == MULTI_AP_BACKHAUL_STA)
-		sta->flags |= WLAN_STA_MULTI_AP;
+	if (multi_ap_value && multi_ap_value != MULTI_AP_BACKHAUL_STA)
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO,
+			       "Multi-AP IE with unexpected value 0x%02x",
+			       multi_ap_value);
 
-	if ((hapd->conf->multi_ap & BACKHAUL_BSS) &&
-	    multi_ap_value == MULTI_AP_BACKHAUL_STA)
-		return WLAN_STATUS_SUCCESS;
+	if (!(multi_ap_value & MULTI_AP_BACKHAUL_STA)) {
+		if (hapd->conf->multi_ap & FRONTHAUL_BSS)
+			return WLAN_STATUS_SUCCESS;
 
-	if (hapd->conf->multi_ap & FRONTHAUL_BSS) {
-		if (multi_ap_value == MULTI_AP_BACKHAUL_STA) {
-			hostapd_logger(hapd, sta->addr,
-				       HOSTAPD_MODULE_IEEE80211,
-				       HOSTAPD_LEVEL_INFO,
-				       "Backhaul STA tries to associate with fronthaul-only BSS");
-			return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
-		}
-		return WLAN_STATUS_SUCCESS;
+		hostapd_logger(hapd, sta->addr,
+			       HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO,
+			       "Non-Multi-AP STA tries to associate with backhaul-only BSS");
+		return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
 	}
 
-	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
-		       HOSTAPD_LEVEL_INFO,
-		       "Non-Multi-AP STA tries to associate with backhaul-only BSS");
-	return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
+	if (!(hapd->conf->multi_ap & BACKHAUL_BSS))
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "Backhaul STA tries to associate with fronthaul-only BSS");
+
+	sta->flags |= WLAN_STA_MULTI_AP;
+	return WLAN_STATUS_SUCCESS;
 }
 
 
@@ -2659,7 +2915,9 @@
 				   "state machine");
 			return WLAN_STATUS_UNSPECIFIED_FAILURE;
 		}
+		wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg);
 		res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+					  hapd->iface->freq,
 					  wpa_ie, wpa_ie_len,
 					  elems.mdie, elems.mdie_len,
 					  elems.owe_dh, elems.owe_dh_len);
@@ -2751,6 +3009,37 @@
 		}
 #endif /* CONFIG_OWE */
 
+#ifdef CONFIG_DPP2
+		dpp_pfs_free(sta->dpp_pfs);
+		sta->dpp_pfs = NULL;
+
+		if ((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) {
+			sta->dpp_pfs = dpp_pfs_init(
+				wpabuf_head(hapd->conf->dpp_netaccesskey),
+				wpabuf_len(hapd->conf->dpp_netaccesskey));
+			if (!sta->dpp_pfs) {
+				wpa_printf(MSG_DEBUG,
+					   "DPP: Could not initialize PFS");
+				/* Try to continue without PFS */
+				goto pfs_fail;
+			}
+
+			if (dpp_pfs_process(sta->dpp_pfs, elems.owe_dh,
+					    elems.owe_dh_len) < 0) {
+				dpp_pfs_free(sta->dpp_pfs);
+				sta->dpp_pfs = NULL;
+				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+			}
+		}
+
+		wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ?
+				   sta->dpp_pfs->secret : NULL);
+	pfs_fail:
+#endif /* CONFIG_DPP2 */
+
 #ifdef CONFIG_IEEE80211N
 		if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) &&
 		    wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) {
@@ -3021,6 +3310,10 @@
 	if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
 		buflen += 150;
 #endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP2
+	if (sta && sta->dpp_pfs)
+		buflen += 5 + sta->dpp_pfs->curve->prime_len;
+#endif /* CONFIG_DPP2 */
 	buf = os_zalloc(buflen);
 	if (!buf) {
 		res = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -3128,6 +3421,39 @@
 	}
 #endif /* CONFIG_FST */
 
+#ifdef CONFIG_OWE
+	if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+	    sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS &&
+	    wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE) {
+		struct wpabuf *pub;
+
+		pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
+		if (!pub) {
+			res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto done;
+		}
+		/* OWE Diffie-Hellman Parameter element */
+		*p++ = WLAN_EID_EXTENSION; /* Element ID */
+		*p++ = 1 + 2 + wpabuf_len(pub); /* Length */
+		*p++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */
+		WPA_PUT_LE16(p, sta->owe_group);
+		p += 2;
+		os_memcpy(p, wpabuf_head(pub), wpabuf_len(pub));
+		p += wpabuf_len(pub);
+		wpabuf_free(pub);
+	}
+#endif /* CONFIG_OWE */
+
+#ifdef CONFIG_DPP2
+	if ((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),
+			  wpabuf_len(sta->dpp_pfs->ie));
+		p += wpabuf_len(sta->dpp_pfs->ie);
+	}
+#endif /* CONFIG_DPP2 */
+
 #ifdef CONFIG_IEEE80211AC
 	if (sta && hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT))
 		p = hostapd_eid_vendor_vht(hapd, p);
@@ -3224,30 +3550,6 @@
 	}
 #endif /* CONFIG_FILS */
 
-#ifdef CONFIG_OWE
-	if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
-	    sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS &&
-	    wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE) {
-		struct wpabuf *pub;
-
-		pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
-		if (!pub) {
-			res = WLAN_STATUS_UNSPECIFIED_FAILURE;
-			goto done;
-		}
-		/* OWE Diffie-Hellman Parameter element */
-		*p++ = WLAN_EID_EXTENSION; /* Element ID */
-		*p++ = 1 + 2 + wpabuf_len(pub); /* Length */
-		*p++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */
-		WPA_PUT_LE16(p, sta->owe_group);
-		p += 2;
-		os_memcpy(p, wpabuf_head(pub), wpabuf_len(pub));
-		p += wpabuf_len(pub);
-		send_len += 3 + 2 + wpabuf_len(pub);
-		wpabuf_free(pub);
-	}
-#endif /* CONFIG_OWE */
-
 	if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) {
 		wpa_printf(MSG_INFO, "Failed to send assoc resp: %s",
 			   strerror(errno));
@@ -3896,26 +4198,6 @@
 
 
 #ifdef CONFIG_IEEE80211W
-
-static int hostapd_sa_query_action(struct hostapd_data *hapd,
-				   const struct ieee80211_mgmt *mgmt,
-				   size_t len)
-{
-	const u8 *end;
-
-	end = mgmt->u.action.u.sa_query_resp.trans_id +
-		WLAN_SA_QUERY_TR_ID_LEN;
-	if (((u8 *) mgmt) + len < end) {
-		wpa_printf(MSG_DEBUG, "IEEE 802.11: Too short SA Query Action "
-			   "frame (len=%lu)", (unsigned long) len);
-		return 0;
-	}
-
-	ieee802_11_sa_query_action(hapd, mgmt, len);
-	return 1;
-}
-
-
 static int robust_action_frame(u8 category)
 {
 	return category != WLAN_ACTION_PUBLIC &&
@@ -4001,7 +4283,8 @@
 		return 1;
 #ifdef CONFIG_IEEE80211W
 	case WLAN_ACTION_SA_QUERY:
-		return hostapd_sa_query_action(hapd, mgmt, len);
+		ieee802_11_sa_query_action(hapd, mgmt, len);
+		return 1;
 #endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_WNM_AP
 	case WLAN_ACTION_WNM:
@@ -4196,7 +4479,7 @@
 	switch (stype) {
 	case WLAN_FC_STYPE_AUTH:
 		wpa_printf(MSG_DEBUG, "mgmt::auth");
-		handle_auth(hapd, mgmt, len, ssi_signal);
+		handle_auth(hapd, mgmt, len, ssi_signal, 0);
 		ret = 1;
 		break;
 	case WLAN_FC_STYPE_ASSOC_REQ:
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 5082226..db7badc 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -175,4 +175,6 @@
 int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth,
 		      int ap_seg1_idx, int *bandwidth, int *seg1_idx);
 
+void auth_sae_process_commit(void *eloop_ctx, void *user_ctx);
+
 #endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index d70d6c1..707381f 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -213,6 +213,14 @@
 	const u8 action_type = mgmt->u.action.u.sa_query_resp.action;
 	const u8 *trans_id = mgmt->u.action.u.sa_query_resp.trans_id;
 
+	if (((const u8 *) mgmt) + len <
+	    mgmt->u.action.u.sa_query_resp.variable) {
+		wpa_printf(MSG_DEBUG,
+			   "IEEE 802.11: Too short SA Query Action frame (len=%lu)",
+			   (unsigned long) len);
+		return;
+	}
+
 	sta = ap_get_sta(hapd, sa);
 
 #ifdef CONFIG_OCV
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index a56c82e..870329a 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -2733,7 +2733,8 @@
 				   wpa_auth_sta_key_mgmt(sta->wpa_sm))) ?
 			  1 : 2,
 			  (unsigned int) diff.sec,
-			  sm->identity ? (char *) sm->identity : identity_buf);
+			  sm->identity ? (char *) sm->identity :
+					 (identity_buf ? identity_buf : "N/A"));
 	os_free(identity_buf);
 	if (os_snprintf_error(buflen - len, ret))
 		return len;
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 4a7722c..71f9f21 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -13,6 +13,7 @@
 #include "common/ieee802_11_defs.h"
 #include "common/wpa_ctrl.h"
 #include "common/sae.h"
+#include "common/dpp.h"
 #include "radius/radius.h"
 #include "radius/radius_client.h"
 #include "p2p/p2p.h"
@@ -362,6 +363,11 @@
 	crypto_ecdh_deinit(sta->owe_ecdh);
 #endif /* CONFIG_OWE */
 
+#ifdef CONFIG_DPP2
+	dpp_pfs_free(sta->dpp_pfs);
+	sta->dpp_pfs = NULL;
+#endif /* CONFIG_DPP2 */
+
 	os_free(sta->ext_capability);
 
 #ifdef CONFIG_WNM_AP
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index ee3f628..ece0c60 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -265,6 +265,10 @@
 	u8 *ext_capability;
 	char *ifname_wds; /* WDS ifname, if in use */
 
+#ifdef CONFIG_DPP2
+	struct dpp_pfs *dpp_pfs;
+#endif /* CONFIG_DPP2 */
+
 #ifdef CONFIG_TESTING_OPTIONS
 	enum wpa_alg last_tk_alg;
 	int last_tk_key_idx;
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 3021a8c..616b205 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.11 RSN / WPA Authenticator
- * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -114,12 +114,13 @@
 static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth,
 					  const u8 *addr,
 					  const u8 *p2p_dev_addr,
-					  const u8 *prev_psk, size_t *psk_len)
+					  const u8 *prev_psk, size_t *psk_len,
+					  int *vlan_id)
 {
 	if (wpa_auth->cb->get_psk == NULL)
 		return NULL;
 	return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr,
-				     prev_psk, psk_len);
+				     prev_psk, psk_len, vlan_id);
 }
 
 
@@ -251,6 +252,15 @@
 #endif /* CONFIG_OCV */
 
 
+static int wpa_auth_update_vlan(struct wpa_authenticator *wpa_auth,
+				const u8 *addr, int vlan_id)
+{
+	if (!wpa_auth->cb->update_vlan)
+		return -1;
+	return wpa_auth->cb->update_vlan(wpa_auth->cb_ctx, addr, vlan_id);
+}
+
+
 static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx)
 {
 	struct wpa_authenticator *wpa_auth = eloop_ctx;
@@ -686,6 +696,9 @@
 	os_free(sm->last_rx_eapol_key);
 	os_free(sm->wpa_ie);
 	wpa_group_put(sm->wpa_auth, sm->group);
+#ifdef CONFIG_DPP2
+	wpabuf_clear_free(sm->dpp_z);
+#endif /* CONFIG_DPP2 */
 	os_free(sm);
 }
 
@@ -852,13 +865,15 @@
 	int ok = 0;
 	const u8 *pmk = NULL;
 	size_t pmk_len;
+	int vlan_id = 0;
 
 	os_memset(&PTK, 0, sizeof(PTK));
 	for (;;) {
 		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
 		    !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
 			pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
-					       sm->p2p_dev_addr, pmk, &pmk_len);
+					       sm->p2p_dev_addr, pmk, &pmk_len,
+					       &vlan_id);
 			if (pmk == NULL)
 				break;
 #ifdef CONFIG_IEEE80211R_AP
@@ -877,8 +892,10 @@
 
 		if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
 				       data, data_len) == 0) {
-			os_memcpy(sm->PMK, pmk, pmk_len);
-			sm->pmk_len = pmk_len;
+			if (sm->PMK != pmk) {
+				os_memcpy(sm->PMK, pmk, pmk_len);
+				sm->pmk_len = pmk_len;
+			}
 			ok = 1;
 			break;
 		}
@@ -897,6 +914,11 @@
 	wpa_printf(MSG_DEBUG,
 		   "WPA: Earlier SNonce resulted in matching MIC");
 	sm->alt_snonce_valid = 0;
+
+	if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
+	    wpa_auth_update_vlan(sm->wpa_auth, sm->addr, vlan_id) < 0)
+		return -1;
+
 	os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN);
 	os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
 	sm->PTK_valid = TRUE;
@@ -2024,7 +2046,7 @@
 
 	SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk);
 	psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL,
-			       &psk_len);
+			       &psk_len, NULL);
 	if (psk) {
 		os_memcpy(sm->PMK, psk, psk_len);
 		sm->pmk_len = psk_len;
@@ -2038,6 +2060,10 @@
 		wpa_printf(MSG_DEBUG, "SAE: PMK from PMKSA cache");
 		os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
 		sm->pmk_len = sm->pmksa->pmk_len;
+#ifdef CONFIG_IEEE80211R_AP
+		os_memcpy(sm->xxkey, sm->pmksa->pmk, sm->pmksa->pmk_len);
+		sm->xxkey_len = sm->pmksa->pmk_len;
+#endif /* CONFIG_IEEE80211R_AP */
 	}
 #endif /* CONFIG_SAE */
 	sm->req_replay_counter_used = 0;
@@ -2136,14 +2162,24 @@
 			  const u8 *pmk, unsigned int pmk_len,
 			  struct wpa_ptk *ptk)
 {
+	const u8 *z = NULL;
+	size_t z_len = 0;
+
 #ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
 		return wpa_auth_derive_ptk_ft(sm, pmk, ptk);
 #endif /* CONFIG_IEEE80211R_AP */
 
+#ifdef CONFIG_DPP2
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_z) {
+		z = wpabuf_head(sm->dpp_z);
+		z_len = wpabuf_len(sm->dpp_z);
+	}
+#endif /* CONFIG_DPP2 */
+
 	return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion",
 			      sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce,
-			      ptk, sm->wpa_key_mgmt, sm->pairwise);
+			      ptk, sm->wpa_key_mgmt, sm->pairwise, z, z_len);
 }
 
 
@@ -2193,6 +2229,16 @@
 			    pmk_r0_name, WPA_PMK_NAME_LEN);
 		wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name);
 		os_memset(fils_ft, 0, sizeof(fils_ft));
+
+		res = wpa_derive_pmk_r1_name(pmk_r0_name, conf->r1_key_holder,
+					     sm->addr, sm->pmk_r1_name,
+					     use_sha384);
+		os_memset(pmk_r0, 0, PMK_LEN_MAX);
+		if (res < 0)
+			return -1;
+		wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", sm->pmk_r1_name,
+			    WPA_PMK_NAME_LEN);
+		sm->pmk_r1_name_valid = 1;
 	}
 #endif /* CONFIG_IEEE80211R_AP */
 
@@ -2712,6 +2758,7 @@
 	struct ieee802_1x_hdr *hdr;
 	struct wpa_eapol_key *key;
 	struct wpa_eapol_ie_parse kde;
+	int vlan_id = 0;
 
 	SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
 	sm->EAPOLKeyReceived = FALSE;
@@ -2727,7 +2774,8 @@
 		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
 		    !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
 			pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
-					       sm->p2p_dev_addr, pmk, &pmk_len);
+					       sm->p2p_dev_addr, pmk, &pmk_len,
+					       &vlan_id);
 			if (pmk == NULL)
 				break;
 			psk_found = 1;
@@ -2749,8 +2797,10 @@
 		    wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
 				       sm->last_rx_eapol_key,
 				       sm->last_rx_eapol_key_len) == 0) {
-			os_memcpy(sm->PMK, pmk, pmk_len);
-			sm->pmk_len = pmk_len;
+			if (sm->PMK != pmk) {
+				os_memcpy(sm->PMK, pmk, pmk_len);
+				sm->pmk_len = pmk_len;
+			}
 			ok = 1;
 			break;
 		}
@@ -2896,6 +2946,13 @@
 	}
 #endif /* CONFIG_IEEE80211R_AP */
 
+	if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
+	    wpa_auth_update_vlan(wpa_auth, sm->addr, vlan_id) < 0) {
+		wpa_sta_disconnect(wpa_auth, sm->addr,
+				   WLAN_REASON_PREV_AUTH_NOT_VALID);
+		return;
+	}
+
 	sm->pending_1_of_4_timeout = 0;
 	eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
 
@@ -3335,7 +3392,7 @@
 		break;
 	case WPA_PTK_INITPSK:
 		if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr,
-				     NULL, NULL)) {
+				     NULL, NULL, NULL)) {
 			SM_ENTER(WPA_PTK, PTKSTART);
 #ifdef CONFIG_SAE
 		} else if (wpa_auth_uses_sae(sm) && sm->pmksa) {
@@ -4786,6 +4843,24 @@
 #endif /* CONFIG_FILS */
 
 
+void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg)
+{
+	if (sm)
+		sm->auth_alg = auth_alg;
+}
+
+
+#ifdef CONFIG_DPP2
+void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z)
+{
+	if (sm) {
+		wpabuf_clear_free(sm->dpp_z);
+		sm->dpp_z = z ? wpabuf_dup(z) : NULL;
+	}
+}
+#endif /* CONFIG_DPP2 */
+
+
 #ifdef CONFIG_TESTING_OPTIONS
 
 int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index e61648d..484e1e5 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -254,7 +254,8 @@
 			  int value);
 	int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var);
 	const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr,
-			      const u8 *prev_psk, size_t *psk_len);
+			      const u8 *prev_psk, size_t *psk_len,
+			      int *vlan_id);
 	int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len);
 	int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg,
 		       const u8 *addr, int idx, u8 *key, size_t key_len);
@@ -270,6 +271,7 @@
 	int (*send_oui)(void *ctx, const u8 *dst, u8 oui_suffix, const u8 *data,
 			size_t data_len);
 	int (*channel_info)(void *ctx, struct wpa_channel_info *ci);
+	int (*update_vlan)(void *ctx, const u8 *addr, int vlan_id);
 	int (*get_sta_tx_params)(void *ctx, const u8 *addr,
 				 int ap_max_chanwidth, int ap_seg1_idx,
 				 int *bandwidth, int *seg1_idx);
@@ -316,7 +318,7 @@
 };
 
 int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
-			struct wpa_state_machine *sm,
+			struct wpa_state_machine *sm, int freq,
 			const u8 *wpa_ie, size_t wpa_ie_len,
 			const u8 *mdie, size_t mdie_len,
 			const u8 *owe_dh, size_t owe_dh_len);
@@ -471,6 +473,8 @@
 u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm,
 				   u8 *pos, size_t max_len,
 				   const u8 *req_ies, size_t req_ies_len);
+void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg);
+void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z);
 
 int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
 		       void (*cb)(void *ctx1, void *ctx2),
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index ac736f0..fdb7eba 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -66,7 +66,7 @@
  * Returns: 0 on success, -1 on error
  */
 static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len,
-			      const u8 *enc, const size_t enc_len,
+			      const u8 *enc, size_t enc_len,
 			      const u8 *auth, const size_t auth_len,
 			      const u8 *src_addr, u8 type,
 			      u8 **plain, size_t *plain_size)
@@ -74,7 +74,11 @@
 	const u8 *ad[3] = { src_addr, auth, &type };
 	size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) };
 
+	wpa_printf(MSG_DEBUG, "FT(RRB): src_addr=" MACSTR " type=%u",
+		   MAC2STR(src_addr), type);
 	wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypt using key", key, key_len);
+	wpa_hexdump(MSG_DEBUG, "FT(RRB): encrypted TLVs", enc, enc_len);
+	wpa_hexdump(MSG_DEBUG, "FT(RRB): authenticated TLVs", auth, auth_len);
 
 	if (!key) { /* skip decryption */
 		*plain = os_memdup(enc, enc_len);
@@ -97,8 +101,18 @@
 		goto err;
 
 	if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len,
-			    *plain) < 0)
-		goto err;
+			    *plain) < 0) {
+		if (enc_len < AES_BLOCK_SIZE + 2)
+			goto err;
+
+		/* Try to work around Ethernet devices that add extra
+		 * two octet padding even if the frame is longer than
+		 * the minimum Ethernet frame. */
+		enc_len -= 2;
+		if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len,
+				    *plain) < 0)
+			goto err;
+	}
 
 	*plain_size = enc_len - AES_BLOCK_SIZE;
 	wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypted TLVs",
@@ -463,9 +477,12 @@
 	const u8 *ad[3] = { src_addr, auth, &type };
 	size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) };
 
+	wpa_printf(MSG_DEBUG, "FT(RRB): src_addr=" MACSTR " type=%u",
+		   MAC2STR(src_addr), type);
 	wpa_hexdump_key(MSG_DEBUG, "FT(RRB): plaintext message",
 			plain, plain_len);
 	wpa_hexdump_key(MSG_DEBUG, "FT(RRB): encrypt using key", key, key_len);
+	wpa_hexdump(MSG_DEBUG, "FT(RRB): authenticated TLVs", auth, auth_len);
 
 	if (!key) {
 		/* encryption not needed, return plaintext as packet */
@@ -475,6 +492,8 @@
 		wpa_printf(MSG_ERROR, "FT: Failed to encrypt RRB-OUI message");
 		return -1;
 	}
+	wpa_hexdump(MSG_DEBUG, "FT(RRB): encrypted TLVs",
+		    enc, plain_len + AES_BLOCK_SIZE);
 
 	return 0;
 }
@@ -503,9 +522,10 @@
 			    const u8 *src_addr, u8 type,
 			    u8 **packet, size_t *packet_len)
 {
-	u8 *plain = NULL, *auth = NULL, *pos;
+	u8 *plain = NULL, *auth = NULL, *pos, *tmp;
 	size_t plain_len = 0, auth_len = 0;
 	int ret = -1;
+	size_t pad_len = 0;
 
 	*packet = NULL;
 	if (wpa_ft_rrb_lin(tlvs_enc0, tlvs_enc1, vlan, &plain, &plain_len) < 0)
@@ -517,6 +537,28 @@
 	*packet_len = sizeof(u16) + auth_len + plain_len;
 	if (key)
 		*packet_len += AES_BLOCK_SIZE;
+#define RRB_MIN_MSG_LEN 64
+	if (*packet_len < RRB_MIN_MSG_LEN) {
+		pad_len = RRB_MIN_MSG_LEN - *packet_len;
+		if (pad_len < sizeof(struct ft_rrb_tlv))
+			pad_len = sizeof(struct ft_rrb_tlv);
+		wpa_printf(MSG_DEBUG,
+			   "FT: Pad message to minimum Ethernet frame length (%d --> %d)",
+			   (int) *packet_len, (int) (*packet_len + pad_len));
+		*packet_len += pad_len;
+		tmp = os_realloc(auth, auth_len + pad_len);
+		if (!tmp)
+			goto out;
+		auth = tmp;
+		pos = auth + auth_len;
+		WPA_PUT_LE16(pos, FT_RRB_LAST_EMPTY);
+		pos += 2;
+		WPA_PUT_LE16(pos, pad_len - sizeof(struct ft_rrb_tlv));
+		pos += 2;
+		os_memset(pos, 0, pad_len - sizeof(struct ft_rrb_tlv));
+		auth_len += pad_len;
+
+	}
 	*packet = os_zalloc(*packet_len);
 	if (!*packet)
 		goto out;
@@ -529,6 +571,7 @@
 	if (wpa_ft_rrb_encrypt(key, key_len, plain, plain_len, auth,
 			       auth_len, src_addr, type, pos) < 0)
 		goto out;
+	wpa_hexdump(MSG_MSGDUMP, "FT: RRB frame payload", *packet, *packet_len);
 
 	ret = 0;
 
@@ -596,8 +639,8 @@
 {
 	if (!wpa_auth->cb->send_oui)
 		return -1;
-	wpa_printf(MSG_DEBUG, "FT: RRB-OUI type %u send to " MACSTR,
-		   oui_suffix, MAC2STR(dst));
+	wpa_printf(MSG_DEBUG, "FT: RRB-OUI type %u send to " MACSTR " (len=%u)",
+		   oui_suffix, MAC2STR(dst), (unsigned int) data_len);
 	return wpa_auth->cb->send_oui(wpa_auth->cb_ctx, dst, oui_suffix, data,
 				      data_len);
 }
@@ -620,7 +663,7 @@
 	if (wpa_auth->cb->get_psk == NULL)
 		return NULL;
 	return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr,
-				     prev_psk, NULL);
+				     prev_psk, NULL, NULL);
 }
 
 
@@ -907,6 +950,8 @@
 		goto err;
 	}
 
+	wpa_printf(MSG_DEBUG, "FT: Send out sequence number request to " MACSTR,
+		   MAC2STR(src_addr));
 	item = os_zalloc(sizeof(*item));
 	if (!item)
 		goto err;
@@ -2386,10 +2431,24 @@
 
 	end = pos + max_len;
 
-	if (auth_alg == WLAN_AUTH_FT) {
+	if (auth_alg == WLAN_AUTH_FT ||
+	    ((auth_alg == WLAN_AUTH_FILS_SK ||
+	      auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	      auth_alg == WLAN_AUTH_FILS_PK) &&
+	     (sm->wpa_key_mgmt & (WPA_KEY_MGMT_FT_FILS_SHA256 |
+				  WPA_KEY_MGMT_FT_FILS_SHA384)))) {
+		if (!sm->pmk_r1_name_valid) {
+			wpa_printf(MSG_ERROR,
+				   "FT: PMKR1Name is not valid for Assoc Resp RSNE");
+			return NULL;
+		}
+		wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name for Assoc Resp RSNE",
+			    sm->pmk_r1_name, WPA_PMK_NAME_LEN);
 		/*
 		 * RSN (only present if this is a Reassociation Response and
-		 * part of a fast BSS transition)
+		 * part of a fast BSS transition; or if this is a
+		 * (Re)Association Response frame during an FT initial mobility
+		 * domain association using FILS)
 		 */
 		res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name);
 		if (res < 0)
@@ -4373,6 +4432,7 @@
 	wpa_printf(MSG_DEBUG, "FT: RRB-OUI received frame from remote AP "
 		   MACSTR, MAC2STR(src_addr));
 	wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame - oui_suffix=%d", oui_suffix);
+	wpa_hexdump(MSG_MSGDUMP, "FT: RRB frame payload", data, data_len);
 
 	if (is_multicast_ether_addr(src_addr)) {
 		wpa_printf(MSG_DEBUG,
@@ -4401,8 +4461,10 @@
 	}
 
 	auth = data + sizeof(u16);
+	wpa_hexdump(MSG_MSGDUMP, "FT: Authenticated payload", auth, alen);
 	enc = data + sizeof(u16) + alen;
 	elen = data_len - sizeof(u16) - alen;
+	wpa_hexdump(MSG_MSGDUMP, "FT: Encrypted payload", enc, elen);
 
 	switch (oui_suffix) {
 	case FT_PACKET_R0KH_R1KH_PULL:
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 9091f43..45172c6 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -246,12 +246,15 @@
 
 static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
 					   const u8 *p2p_dev_addr,
-					   const u8 *prev_psk, size_t *psk_len)
+					   const u8 *prev_psk, size_t *psk_len,
+					   int *vlan_id)
 {
 	struct hostapd_data *hapd = ctx;
 	struct sta_info *sta = ap_get_sta(hapd, addr);
 	const u8 *psk;
 
+	if (vlan_id)
+		*vlan_id = 0;
 	if (psk_len)
 		*psk_len = PMK_LEN;
 
@@ -287,7 +290,8 @@
 	}
 #endif /* CONFIG_OWE */
 
-	psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk);
+	psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk,
+			      vlan_id);
 	/*
 	 * This is about to iterate over all psks, prev_psk gives the last
 	 * returned psk which should not be returned again.
@@ -295,6 +299,9 @@
 	 */
 	if (sta && sta->psk && !psk) {
 		struct hostapd_sta_wpa_psk_short *pos;
+
+		if (vlan_id)
+			*vlan_id = 0;
 		psk = sta->psk->psk;
 		for (pos = sta->psk; pos; pos = pos->next) {
 			if (pos->is_passphrase) {
@@ -788,6 +795,45 @@
 }
 
 
+static int hostapd_wpa_auth_update_vlan(void *ctx, const u8 *addr, int vlan_id)
+{
+#ifndef CONFIG_NO_VLAN
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+	struct vlan_description vlan_desc;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta)
+		return -1;
+
+	os_memset(&vlan_desc, 0, sizeof(vlan_desc));
+	vlan_desc.notempty = 1;
+	vlan_desc.untagged = vlan_id;
+	if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
+		wpa_printf(MSG_INFO, "Invalid VLAN ID %d in wpa_psk_file",
+			   vlan_id);
+		return -1;
+	}
+
+	if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0) {
+		wpa_printf(MSG_INFO,
+			   "Failed to assign VLAN ID %d from wpa_psk_file to "
+			   MACSTR, vlan_id, MAC2STR(sta->addr));
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO,
+		   "Assigned VLAN ID %d from wpa_psk_file to " MACSTR,
+		   vlan_id, MAC2STR(sta->addr));
+	if ((sta->flags & WLAN_STA_ASSOC) &&
+	    ap_sta_bind_vlan(hapd, sta) < 0)
+		return -1;
+#endif /* CONFIG_NO_VLAN */
+
+	return 0;
+}
+
+
 #ifdef CONFIG_OCV
 static int hostapd_get_sta_tx_params(void *ctx, const u8 *addr,
 				     int ap_max_chanwidth, int ap_seg1_idx,
@@ -1229,6 +1275,7 @@
 		.send_ether = hostapd_wpa_auth_send_ether,
 		.send_oui = hostapd_wpa_auth_send_oui,
 		.channel_info = hostapd_channel_info,
+		.update_vlan = hostapd_wpa_auth_update_vlan,
 #ifdef CONFIG_OCV
 		.get_sta_tx_params = hostapd_get_sta_tx_params,
 #endif /* CONFIG_OCV */
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index a349304..3dcf77a 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -22,6 +22,7 @@
 
 	u8 addr[ETH_ALEN];
 	u8 p2p_dev_addr[ETH_ALEN];
+	u16 auth_alg;
 
 	enum {
 		WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED,
@@ -150,6 +151,10 @@
 	unsigned int fils_completed:1;
 #endif /* CONFIG_FILS */
 
+#ifdef CONFIG_DPP2
+	struct wpabuf *dpp_z;
+#endif /* CONFIG_DPP2 */
+
 #ifdef CONFIG_TESTING_OPTIONS
 	void (*eapol_status_cb)(void *ctx1, void *ctx2);
 	void *eapol_status_cb_ctx1;
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 3bcbef7..8580a5a 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -530,7 +530,7 @@
 
 
 int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
-			struct wpa_state_machine *sm,
+			struct wpa_state_machine *sm, int freq,
 			const u8 *wpa_ie, size_t wpa_ie_len,
 			const u8 *mdie, size_t mdie_len,
 			const u8 *owe_dh, size_t owe_dh_len)
@@ -560,6 +560,10 @@
 
 	if (version == WPA_PROTO_RSN) {
 		res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data);
+		if (!data.has_pairwise)
+			data.pairwise_cipher = wpa_default_rsn_cipher(freq);
+		if (!data.has_group)
+			data.group_cipher = wpa_default_rsn_cipher(freq);
 
 		if (wpa_key_mgmt_ft(data.key_mgmt) && !mdie &&
 		    !wpa_key_mgmt_only_ft(data.key_mgmt)) {
@@ -831,6 +835,12 @@
 			   "OWE: No Diffie-Hellman Parameter element");
 		return WPA_INVALID_AKMP;
 	}
+#ifdef CONFIG_DPP
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && owe_dh) {
+		/* Diffie-Hellman Parameter element can be used with DPP as
+		 * well, so allow this to proceed. */
+	} else
+#endif /* CONFIG_DPP */
 	if (sm->wpa_key_mgmt != WPA_KEY_MGMT_OWE && owe_dh) {
 		wpa_printf(MSG_DEBUG,
 			   "OWE: Unexpected Diffie-Hellman Parameter element with non-OWE AKM");
@@ -848,6 +858,21 @@
 	else
 		sm->wpa = WPA_VERSION_WPA;
 
+#if defined(CONFIG_IEEE80211R_AP) && defined(CONFIG_FILS)
+	if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256 ||
+	     sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) &&
+	    (sm->auth_alg == WLAN_AUTH_FILS_SK ||
+	     sm->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	     sm->auth_alg == WLAN_AUTH_FILS_PK) &&
+	    (data.num_pmkid != 1 || !data.pmkid || !sm->pmk_r1_name_valid ||
+	     os_memcmp_const(data.pmkid, sm->pmk_r1_name,
+			     WPA_PMK_NAME_LEN) != 0)) {
+		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+				 "No PMKR1Name match for FILS+FT");
+		return WPA_INVALID_PMKID;
+	}
+#endif /* CONFIG_IEEE80211R_AP && CONFIG_FILS */
+
 	sm->pmksa = NULL;
 	for (i = 0; i < data.num_pmkid; i++) {
 		wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID",
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index 5ec0199..6161cdb 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -354,6 +354,18 @@
 							    bss->wpa_pairwise,
 							    bss->rsn_pairwise);
 
+		if (hapd->conf->wps_cred_add_sae &&
+		    (cred->auth_type & WPS_AUTH_WPA2PSK) &&
+		    cred->key_len != 2 * PMK_LEN) {
+			bss->wpa_key_mgmt |= WPA_KEY_MGMT_SAE;
+#ifdef CONFIG_IEEE80211W
+			if (bss->ieee80211w == NO_MGMT_FRAME_PROTECTION)
+				bss->ieee80211w =
+					MGMT_FRAME_PROTECTION_OPTIONAL;
+			bss->sae_require_mfp = 1;
+#endif /* CONFIG_IEEE80211W */
+		}
+
 		if (cred->key_len >= 8 && cred->key_len < 64) {
 			os_free(bss->ssid.wpa_passphrase);
 			bss->ssid.wpa_passphrase = os_zalloc(cred->key_len + 1);
@@ -401,6 +413,7 @@
 	char buf[1024];
 	int multi_bss;
 	int wpa;
+	int pmf_changed = 0;
 
 	if (hapd->wps == NULL)
 		return 0;
@@ -520,6 +533,10 @@
 
 	if (wpa) {
 		char *prefix;
+#ifdef CONFIG_IEEE80211W
+		int sae = 0;
+#endif /* CONFIG_IEEE80211W */
+
 		fprintf(nconf, "wpa=%d\n", wpa);
 
 		fprintf(nconf, "wpa_key_mgmt=");
@@ -528,10 +545,30 @@
 			fprintf(nconf, "WPA-EAP");
 			prefix = " ";
 		}
-		if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK))
+		if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) {
 			fprintf(nconf, "%sWPA-PSK", prefix);
+			prefix = " ";
+		}
+		if (hapd->conf->wps_cred_add_sae &&
+		    (cred->auth_type & WPS_AUTH_WPA2PSK) &&
+		    cred->key_len != 2 * PMK_LEN) {
+			fprintf(nconf, "%sSAE", prefix);
+#ifdef CONFIG_IEEE80211W
+			sae = 1;
+#endif /* CONFIG_IEEE80211W */
+		}
 		fprintf(nconf, "\n");
 
+#ifdef CONFIG_IEEE80211W
+		if (sae && hapd->conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) {
+			fprintf(nconf, "ieee80211w=%d\n",
+				MGMT_FRAME_PROTECTION_OPTIONAL);
+			pmf_changed = 1;
+		}
+		if (sae)
+			fprintf(nconf, "sae_require_mfp=1\n");
+#endif /* CONFIG_IEEE80211W */
+
 		fprintf(nconf, "wpa_pairwise=");
 		prefix = "";
 		if (cred->encr_type & WPS_ENCR_AES) {
@@ -585,6 +622,7 @@
 		     str_starts(buf, "wep_default_key=") ||
 		     str_starts(buf, "wep_key") ||
 		     str_starts(buf, "wps_state=") ||
+		     (pmf_changed && str_starts(buf, "ieee80211w=")) ||
 		     str_starts(buf, "wpa=") ||
 		     str_starts(buf, "wpa_psk=") ||
 		     str_starts(buf, "wpa_pairwise=") ||
@@ -975,6 +1013,7 @@
 {
 	struct wps_context *wps;
 	struct wps_registrar_config cfg;
+	u8 *multi_ap_netw_key = NULL;
 
 	if (conf->wps_state == 0) {
 		hostapd_wps_clear_ies(hapd, 0);
@@ -1133,6 +1172,31 @@
 		wps->encr_types_wpa = WPS_ENCR_AES | WPS_ENCR_TKIP;
 	}
 
+	if ((hapd->conf->multi_ap & FRONTHAUL_BSS) &&
+	    hapd->conf->multi_ap_backhaul_ssid.ssid_len) {
+		cfg.multi_ap_backhaul_ssid_len =
+			hapd->conf->multi_ap_backhaul_ssid.ssid_len;
+		cfg.multi_ap_backhaul_ssid =
+			hapd->conf->multi_ap_backhaul_ssid.ssid;
+
+		if (conf->multi_ap_backhaul_ssid.wpa_passphrase) {
+			cfg.multi_ap_backhaul_network_key = (const u8 *)
+				conf->multi_ap_backhaul_ssid.wpa_passphrase;
+			cfg.multi_ap_backhaul_network_key_len =
+				os_strlen(conf->multi_ap_backhaul_ssid.wpa_passphrase);
+		} else if (conf->multi_ap_backhaul_ssid.wpa_psk) {
+			multi_ap_netw_key = os_malloc(2 * PMK_LEN + 1);
+			if (!multi_ap_netw_key)
+				goto fail;
+			wpa_snprintf_hex((char *) multi_ap_netw_key,
+					 2 * PMK_LEN + 1,
+					 conf->multi_ap_backhaul_ssid.wpa_psk->psk,
+					 PMK_LEN);
+			cfg.multi_ap_backhaul_network_key = multi_ap_netw_key;
+			cfg.multi_ap_backhaul_network_key_len = 2 * PMK_LEN;
+		}
+	}
+
 	wps->ap_settings = conf->ap_settings;
 	wps->ap_settings_len = conf->ap_settings_len;
 
@@ -1174,10 +1238,12 @@
 	hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd);
 
 	hapd->wps = wps;
+	bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN);
 
 	return 0;
 
 fail:
+	bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN);
 	hostapd_free_wps(wps);
 	return -1;
 }
diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c
index 0b596bb..30c5247 100644
--- a/src/common/common_module_tests.c
+++ b/src/common/common_module_tests.c
@@ -1,6 +1,6 @@
 /*
  * common module tests
- * Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2014-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,10 +10,12 @@
 
 #include "utils/common.h"
 #include "utils/module_tests.h"
+#include "crypto/crypto.h"
 #include "ieee802_11_common.h"
 #include "ieee802_11_defs.h"
 #include "gas.h"
 #include "wpa_common.h"
+#include "sae.h"
 
 
 struct ieee802_11_parse_test_data {
@@ -248,6 +250,179 @@
 }
 
 
+static int sae_tests(void)
+{
+#ifdef CONFIG_SAE
+	struct sae_data sae;
+	int ret = -1;
+	/* IEEE P802.11-REVmd/D2.1, Annex J.10 */
+	const u8 addr1[ETH_ALEN] = { 0x82, 0x7b, 0x91, 0x9d, 0xd4, 0xb9 };
+	const u8 addr2[ETH_ALEN] = { 0x1e, 0xec, 0x49, 0xea, 0x64, 0x88 };
+	const char *pw = "mekmitasdigoat";
+	const char *pwid = "psk4internet";
+	const u8 local_rand[] = {
+		0xa9, 0x06, 0xf6, 0x1e, 0x4d, 0x3a, 0x5d, 0x4e,
+		0xb2, 0x96, 0x5f, 0xf3, 0x4c, 0xf9, 0x17, 0xdd,
+		0x04, 0x44, 0x45, 0xc8, 0x78, 0xc1, 0x7c, 0xa5,
+		0xd5, 0xb9, 0x37, 0x86, 0xda, 0x9f, 0x83, 0xcf
+	};
+	const u8 local_mask[] = {
+		0x42, 0x34, 0xb4, 0xfb, 0x17, 0xaa, 0x43, 0x5c,
+		0x52, 0xfb, 0xfd, 0xeb, 0xe6, 0x40, 0x39, 0xb4,
+		0x34, 0x78, 0x20, 0x0e, 0x54, 0xff, 0x7b, 0x6e,
+		0x07, 0xb6, 0x9c, 0xad, 0x74, 0x15, 0x3c, 0x15
+	};
+	const u8 local_commit[] = {
+		0x13, 0x00, 0xeb, 0x3b, 0xab, 0x19, 0x64, 0xe4,
+		0xa0, 0xab, 0x05, 0x92, 0x5d, 0xdf, 0x33, 0x39,
+		0x51, 0x91, 0x38, 0xbc, 0x65, 0xd6, 0xcd, 0xc0,
+		0xf8, 0x13, 0xdd, 0x6f, 0xd4, 0x34, 0x4e, 0xb4,
+		0xbf, 0xe4, 0x4b, 0x5c, 0x21, 0x59, 0x76, 0x58,
+		0xf4, 0xe3, 0xed, 0xdf, 0xb4, 0xb9, 0x9f, 0x25,
+		0xb4, 0xd6, 0x54, 0x0f, 0x32, 0xff, 0x1f, 0xd5,
+		0xc5, 0x30, 0xc6, 0x0a, 0x79, 0x44, 0x48, 0x61,
+		0x0b, 0xc6, 0xde, 0x3d, 0x92, 0xbd, 0xbb, 0xd4,
+		0x7d, 0x93, 0x59, 0x80, 0xca, 0x6c, 0xf8, 0x98,
+		0x8a, 0xb6, 0x63, 0x0b, 0xe6, 0x76, 0x4c, 0x88,
+		0x5c, 0xeb, 0x97, 0x93, 0x97, 0x0f, 0x69, 0x52,
+		0x17, 0xee, 0xff, 0x0d, 0x21, 0x70, 0x73, 0x6b,
+		0x34, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
+		0x74
+	};
+	const u8 peer_commit[] = {
+		0x13, 0x00, 0x55, 0x64, 0xf0, 0x45, 0xb2, 0xea,
+		0x1e, 0x56, 0x6c, 0xf1, 0xdd, 0x74, 0x1f, 0x70,
+		0xd9, 0xbe, 0x35, 0xd2, 0xdf, 0x5b, 0x9a, 0x55,
+		0x02, 0x94, 0x6e, 0xe0, 0x3c, 0xf8, 0xda, 0xe2,
+		0x7e, 0x1e, 0x05, 0xb8, 0x43, 0x0e, 0xb7, 0xa9,
+		0x9e, 0x24, 0x87, 0x7c, 0xe6, 0x9b, 0xaf, 0x3d,
+		0xc5, 0x80, 0xe3, 0x09, 0x63, 0x3d, 0x6b, 0x38,
+		0x5f, 0x83, 0xee, 0x1c, 0x3e, 0xc3, 0x59, 0x1f,
+		0x1a, 0x53, 0x93, 0xc0, 0x6e, 0x80, 0x5d, 0xdc,
+		0xeb, 0x2f, 0xde, 0x50, 0x93, 0x0d, 0xd7, 0xcf,
+		0xeb, 0xb9, 0x87, 0xc6, 0xff, 0x96, 0x66, 0xaf,
+		0x16, 0x4e, 0xb5, 0x18, 0x4d, 0x8e, 0x66, 0x62,
+		0xed, 0x6a, 0xff, 0x0d, 0x21, 0x70, 0x73, 0x6b,
+		0x34, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
+		0x74
+	};
+	const u8 kck[] = {
+		0x59, 0x9d, 0x6f, 0x1e, 0x27, 0x54, 0x8b, 0xe8,
+		0x49, 0x9d, 0xce, 0xed, 0x2f, 0xec, 0xcf, 0x94,
+		0x81, 0x8c, 0xe1, 0xc7, 0x9f, 0x1b, 0x4e, 0xb3,
+		0xd6, 0xa5, 0x32, 0x28, 0xa0, 0x9b, 0xf3, 0xed
+	};
+	const u8 pmk[] = {
+		0x7a, 0xea, 0xd8, 0x6f, 0xba, 0x4c, 0x32, 0x21,
+		0xfc, 0x43, 0x7f, 0x5f, 0x14, 0xd7, 0x0d, 0x85,
+		0x4e, 0xa5, 0xd5, 0xaa, 0xc1, 0x69, 0x01, 0x16,
+		0x79, 0x30, 0x81, 0xed, 0xa4, 0xd5, 0x57, 0xc5
+	};
+	const u8 pmkid[] = {
+		0x40, 0xa0, 0x9b, 0x60, 0x17, 0xce, 0xbf, 0x00,
+		0x72, 0x84, 0x3b, 0x53, 0x52, 0xaa, 0x2b, 0x4f
+	};
+	const u8 local_confirm[] = {
+		0x01, 0x00, 0x12, 0xd9, 0xd5, 0xc7, 0x8c, 0x50,
+		0x05, 0x26, 0xd3, 0x6c, 0x41, 0xdb, 0xc5, 0x6a,
+		0xed, 0xf2, 0x91, 0x4c, 0xed, 0xdd, 0xd7, 0xca,
+		0xd4, 0xa5, 0x8c, 0x48, 0xf8, 0x3d, 0xbd, 0xe9,
+		0xfc, 0x77
+	};
+	const u8 peer_confirm[] = {
+		0x01, 0x00, 0x02, 0x87, 0x1c, 0xf9, 0x06, 0x89,
+		0x8b, 0x80, 0x60, 0xec, 0x18, 0x41, 0x43, 0xbe,
+		0x77, 0xb8, 0xc0, 0x8a, 0x80, 0x19, 0xb1, 0x3e,
+		0xb6, 0xd0, 0xae, 0xf0, 0xd8, 0x38, 0x3d, 0xfa,
+		0xc2, 0xfd
+	};
+	struct wpabuf *buf = NULL;
+	struct crypto_bignum *mask = NULL;
+
+	os_memset(&sae, 0, sizeof(sae));
+	buf = wpabuf_alloc(1000);
+	if (!buf ||
+	    sae_set_group(&sae, 19) < 0 ||
+	    sae_prepare_commit(addr1, addr2, (const u8 *) pw, os_strlen(pw),
+			       pwid, &sae) < 0)
+		goto fail;
+
+	/* Override local values based on SAE test vector */
+	crypto_bignum_deinit(sae.tmp->sae_rand, 1);
+	sae.tmp->sae_rand = crypto_bignum_init_set(local_rand,
+						   sizeof(local_rand));
+	mask = crypto_bignum_init_set(local_mask, sizeof(local_mask));
+	if (!sae.tmp->sae_rand || !mask)
+		goto fail;
+
+	if (crypto_bignum_add(sae.tmp->sae_rand, mask,
+			      sae.tmp->own_commit_scalar) < 0 ||
+	    crypto_bignum_mod(sae.tmp->own_commit_scalar, sae.tmp->order,
+			      sae.tmp->own_commit_scalar) < 0 ||
+	    crypto_ec_point_mul(sae.tmp->ec, sae.tmp->pwe_ecc, mask,
+				sae.tmp->own_commit_element_ecc) < 0 ||
+	    crypto_ec_point_invert(sae.tmp->ec,
+				   sae.tmp->own_commit_element_ecc) < 0)
+		goto fail;
+
+	/* Check that output matches the test vector */
+	sae_write_commit(&sae, buf, NULL, pwid);
+	wpa_hexdump_buf(MSG_DEBUG, "SAE: Commit message", buf);
+
+	if (wpabuf_len(buf) != sizeof(local_commit) ||
+	    os_memcmp(wpabuf_head(buf), local_commit,
+		      sizeof(local_commit)) != 0) {
+		wpa_printf(MSG_ERROR, "SAE: Mismatch in local commit");
+		goto fail;
+	}
+
+	if (sae_parse_commit(&sae, peer_commit, sizeof(peer_commit), NULL, NULL,
+		    NULL) != 0 ||
+	    sae_process_commit(&sae) < 0)
+		goto fail;
+
+	if (os_memcmp(kck, sae.tmp->kck, SAE_KCK_LEN) != 0) {
+		wpa_printf(MSG_ERROR, "SAE: Mismatch in KCK");
+		goto fail;
+	}
+
+	if (os_memcmp(pmk, sae.pmk, SAE_PMK_LEN) != 0) {
+		wpa_printf(MSG_ERROR, "SAE: Mismatch in PMK");
+		goto fail;
+	}
+
+	if (os_memcmp(pmkid, sae.pmkid, SAE_PMKID_LEN) != 0) {
+		wpa_printf(MSG_ERROR, "SAE: Mismatch in PMKID");
+		goto fail;
+	}
+
+	buf->used = 0;
+	sae.send_confirm = 1;
+	sae_write_confirm(&sae, buf);
+	wpa_hexdump_buf(MSG_DEBUG, "SAE: Confirm message", buf);
+
+	if (wpabuf_len(buf) != sizeof(local_confirm) ||
+	    os_memcmp(wpabuf_head(buf), local_confirm,
+		      sizeof(local_confirm)) != 0) {
+		wpa_printf(MSG_ERROR, "SAE: Mismatch in local confirm");
+		goto fail;
+	}
+
+	if (sae_check_confirm(&sae, peer_confirm, sizeof(peer_confirm)) < 0)
+		goto fail;
+
+	ret = 0;
+fail:
+	sae_clear_data(&sae);
+	wpabuf_free(buf);
+	crypto_bignum_deinit(mask, 1);
+	return ret;
+#else /* CONFIG_SAE */
+	return 0;
+#endif /* CONFIG_SAE */
+}
+
+
 int common_module_tests(void)
 {
 	int ret = 0;
@@ -256,6 +431,7 @@
 
 	if (ieee802_11_parse_tests() < 0 ||
 	    gas_tests() < 0 ||
+	    sae_tests() < 0 ||
 	    rsn_ie_parse_tests() < 0)
 		ret = -1;
 
diff --git a/src/common/dpp.c b/src/common/dpp.c
index bcb694b..49de476 100644
--- a/src/common/dpp.c
+++ b/src/common/dpp.c
@@ -1,6 +1,7 @@
 /*
  * DPP functionality shared between hostapd and wpa_supplicant
  * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2019, The Linux Foundation
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -18,6 +19,7 @@
 #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"
@@ -68,6 +70,11 @@
 #endif
 
 
+struct dpp_global {
+	struct dl_list bootstrap; /* struct dpp_bootstrap_info */
+	struct dl_list configurator; /* struct dpp_configurator */
+};
+
 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. */
@@ -1537,6 +1544,9 @@
 		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;
@@ -1579,6 +1589,13 @@
 		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");
@@ -1705,6 +1722,9 @@
 	/* 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;
@@ -1732,6 +1752,13 @@
 		wpabuf_put_buf(msg, pr);
 	}
 
+#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 */
+
 	attr_end = wpabuf_put(msg, 0);
 
 #ifdef CONFIG_TESTING_OPTIONS
@@ -2202,8 +2229,8 @@
 }
 
 
-struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
-				   const char *json)
+static struct wpabuf * dpp_build_conf_req_attr(struct dpp_authentication *auth,
+					       const char *json)
 {
 	size_t nonce_len;
 	size_t json_len, clear_len;
@@ -2307,6 +2334,55 @@
 }
 
 
+static void dpp_write_adv_proto(struct wpabuf *buf)
+{
+	/* Advertisement Protocol IE */
+	wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
+	wpabuf_put_u8(buf, 8); /* Length */
+	wpabuf_put_u8(buf, 0x7f);
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+	wpabuf_put_u8(buf, 5);
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, DPP_OUI_TYPE);
+	wpabuf_put_u8(buf, 0x01);
+}
+
+
+static void dpp_write_gas_query(struct wpabuf *buf, struct wpabuf *query)
+{
+	/* GAS Query */
+	wpabuf_put_le16(buf, wpabuf_len(query));
+	wpabuf_put_buf(buf, query);
+}
+
+
+struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
+				   const char *json)
+{
+	struct wpabuf *buf, *conf_req;
+
+	conf_req = dpp_build_conf_req_attr(auth, json);
+	if (!conf_req) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No configuration request data available");
+		return NULL;
+	}
+
+	buf = gas_build_initial_req(0, 10 + 2 + wpabuf_len(conf_req));
+	if (!buf) {
+		wpabuf_free(conf_req);
+		return NULL;
+	}
+
+	dpp_write_adv_proto(buf);
+	dpp_write_gas_query(buf, conf_req);
+	wpabuf_free(conf_req);
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: GAS Config Request", buf);
+
+	return buf;
+}
+
+
 static void dpp_auth_success(struct dpp_authentication *auth)
 {
 	wpa_printf(MSG_DEBUG,
@@ -2893,6 +2969,10 @@
 	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) {
@@ -2922,6 +3002,22 @@
 	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) {
@@ -3450,6 +3546,10 @@
 		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) {
@@ -3524,6 +3624,22 @@
 		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) {
@@ -3987,6 +4103,99 @@
 }
 
 
+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;
+}
+
+
+struct dpp_configuration * dpp_configuration_alloc(const char *type)
+{
+	struct dpp_configuration *conf;
+	const char *end;
+	size_t len;
+
+	conf = os_zalloc(sizeof(*conf));
+	if (!conf)
+		goto fail;
+
+	end = os_strchr(type, ' ');
+	if (end)
+		len = end - type;
+	else
+		len = os_strlen(type);
+
+	if (bin_str_eq(type, len, "psk"))
+		conf->akm = DPP_AKM_PSK;
+	else if (bin_str_eq(type, len, "sae"))
+		conf->akm = DPP_AKM_SAE;
+	else if (bin_str_eq(type, len, "psk-sae") ||
+		 bin_str_eq(type, len, "psk+sae"))
+		conf->akm = DPP_AKM_PSK_SAE;
+	else if (bin_str_eq(type, len, "sae-dpp") ||
+		 bin_str_eq(type, len, "dpp+sae"))
+		conf->akm = DPP_AKM_SAE_DPP;
+	else if (bin_str_eq(type, len, "psk-sae-dpp") ||
+		 bin_str_eq(type, len, "dpp+psk+sae"))
+		conf->akm = DPP_AKM_PSK_SAE_DPP;
+	else if (bin_str_eq(type, len, "dpp"))
+		conf->akm = DPP_AKM_DPP;
+	else
+		goto fail;
+
+	return conf;
+fail:
+	dpp_configuration_free(conf);
+	return NULL;
+}
+
+
+int dpp_akm_psk(enum dpp_akm akm)
+{
+	return akm == DPP_AKM_PSK || akm == DPP_AKM_PSK_SAE ||
+		akm == DPP_AKM_PSK_SAE_DPP;
+}
+
+
+int dpp_akm_sae(enum dpp_akm akm)
+{
+	return akm == DPP_AKM_SAE || akm == DPP_AKM_PSK_SAE ||
+		akm == DPP_AKM_SAE_DPP || akm == DPP_AKM_PSK_SAE_DPP;
+}
+
+
+int dpp_akm_legacy(enum dpp_akm akm)
+{
+	return akm == DPP_AKM_PSK || akm == DPP_AKM_PSK_SAE ||
+		akm == DPP_AKM_SAE;
+}
+
+
+int dpp_akm_dpp(enum dpp_akm akm)
+{
+	return akm == DPP_AKM_DPP || akm == DPP_AKM_SAE_DPP ||
+		akm == DPP_AKM_PSK_SAE_DPP;
+}
+
+
+int dpp_akm_ver2(enum dpp_akm akm)
+{
+	return akm == DPP_AKM_SAE_DPP || akm == DPP_AKM_PSK_SAE_DPP;
+}
+
+
+int dpp_configuration_valid(const struct dpp_configuration *conf)
+{
+	if (conf->ssid_len == 0)
+		return 0;
+	if (dpp_akm_psk(conf->akm) && !conf->passphrase && !conf->psk_set)
+		return 0;
+	if (dpp_akm_sae(conf->akm) && !conf->passphrase)
+		return 0;
+	return 1;
+}
+
+
 void dpp_configuration_free(struct dpp_configuration *conf)
 {
 	if (!conf)
@@ -3997,6 +4206,162 @@
 }
 
 
+static int dpp_configuration_parse(struct dpp_authentication *auth,
+				   const char *cmd)
+{
+	const char *pos, *end;
+	struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL;
+	struct dpp_configuration *conf = NULL;
+
+	pos = os_strstr(cmd, " conf=sta-");
+	if (pos) {
+		conf_sta = dpp_configuration_alloc(pos + 10);
+		if (!conf_sta)
+			goto fail;
+		conf = conf_sta;
+	}
+
+	pos = os_strstr(cmd, " conf=ap-");
+	if (pos) {
+		conf_ap = dpp_configuration_alloc(pos + 9);
+		if (!conf_ap)
+			goto fail;
+		conf = conf_ap;
+	}
+
+	if (!conf)
+		return 0;
+
+	pos = os_strstr(cmd, " ssid=");
+	if (pos) {
+		pos += 6;
+		end = os_strchr(pos, ' ');
+		conf->ssid_len = end ? (size_t) (end - pos) : os_strlen(pos);
+		conf->ssid_len /= 2;
+		if (conf->ssid_len > sizeof(conf->ssid) ||
+		    hexstr2bin(pos, conf->ssid, conf->ssid_len) < 0)
+			goto fail;
+	} else {
+#ifdef CONFIG_TESTING_OPTIONS
+		/* use a default SSID for legacy testing reasons */
+		os_memcpy(conf->ssid, "test", 4);
+		conf->ssid_len = 4;
+#else /* CONFIG_TESTING_OPTIONS */
+		goto fail;
+#endif /* CONFIG_TESTING_OPTIONS */
+	}
+
+	pos = os_strstr(cmd, " pass=");
+	if (pos) {
+		size_t pass_len;
+
+		pos += 6;
+		end = os_strchr(pos, ' ');
+		pass_len = end ? (size_t) (end - pos) : os_strlen(pos);
+		pass_len /= 2;
+		if (pass_len > 63 || pass_len < 8)
+			goto fail;
+		conf->passphrase = os_zalloc(pass_len + 1);
+		if (!conf->passphrase ||
+		    hexstr2bin(pos, (u8 *) conf->passphrase, pass_len) < 0)
+			goto fail;
+	}
+
+	pos = os_strstr(cmd, " psk=");
+	if (pos) {
+		pos += 5;
+		if (hexstr2bin(pos, conf->psk, PMK_LEN) < 0)
+			goto fail;
+		conf->psk_set = 1;
+	}
+
+	pos = os_strstr(cmd, " group_id=");
+	if (pos) {
+		size_t group_id_len;
+
+		pos += 10;
+		end = os_strchr(pos, ' ');
+		group_id_len = end ? (size_t) (end - pos) : os_strlen(pos);
+		conf->group_id = os_malloc(group_id_len + 1);
+		if (!conf->group_id)
+			goto fail;
+		os_memcpy(conf->group_id, pos, group_id_len);
+		conf->group_id[group_id_len] = '\0';
+	}
+
+	pos = os_strstr(cmd, " expiry=");
+	if (pos) {
+		long int val;
+
+		pos += 8;
+		val = strtol(pos, NULL, 0);
+		if (val <= 0)
+			goto fail;
+		conf->netaccesskey_expiry = val;
+	}
+
+	if (!dpp_configuration_valid(conf))
+		goto fail;
+
+	auth->conf_sta = conf_sta;
+	auth->conf_ap = conf_ap;
+	return 0;
+
+fail:
+	dpp_configuration_free(conf_sta);
+	dpp_configuration_free(conf_ap);
+	return -1;
+}
+
+
+static struct dpp_configurator *
+dpp_configurator_get_id(struct dpp_global *dpp, unsigned int id)
+{
+	struct dpp_configurator *conf;
+
+	if (!dpp)
+		return NULL;
+
+	dl_list_for_each(conf, &dpp->configurator,
+			 struct dpp_configurator, list) {
+		if (conf->id == id)
+			return conf;
+	}
+	return NULL;
+}
+
+
+int dpp_set_configurator(struct dpp_global *dpp, void *msg_ctx,
+			 struct dpp_authentication *auth,
+			 const char *cmd)
+{
+	const char *pos;
+
+	if (!cmd)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd);
+
+	pos = os_strstr(cmd, " configurator=");
+	if (pos) {
+		pos += 14;
+		auth->conf = dpp_configurator_get_id(dpp, atoi(pos));
+		if (!auth->conf) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Could not find the specified configurator");
+			return -1;
+		}
+	}
+
+	if (dpp_configuration_parse(auth, cmd) < 0) {
+		wpa_msg(msg_ctx, MSG_INFO,
+			"DPP: Failed to set configurator parameters");
+		return -1;
+	}
+	return 0;
+}
+
+
 void dpp_auth_deinit(struct dpp_authentication *auth)
 {
 	if (!auth)
@@ -4096,6 +4461,31 @@
 }
 
 
+static void dpp_build_legacy_cred_params(struct wpabuf *buf,
+					 struct dpp_configuration *conf)
+{
+	if (conf->passphrase && os_strlen(conf->passphrase) < 64) {
+		char pass[63 * 6 + 1];
+
+		json_escape_string(pass, sizeof(pass), conf->passphrase,
+				   os_strlen(conf->passphrase));
+		wpabuf_put_str(buf, "\"pass\":\"");
+		wpabuf_put_str(buf, pass);
+		wpabuf_put_str(buf, "\"");
+		os_memset(pass, 0, sizeof(pass));
+	} else if (conf->psk_set) {
+		char psk[2 * sizeof(conf->psk) + 1];
+
+		wpa_snprintf_hex(psk, sizeof(psk),
+				 conf->psk, sizeof(conf->psk));
+		wpabuf_put_str(buf, "\"psk_hex\":\"");
+		wpabuf_put_str(buf, psk);
+		wpabuf_put_str(buf, "\"");
+		os_memset(psk, 0, sizeof(psk));
+	}
+}
+
+
 static struct wpabuf *
 dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap,
 		       struct dpp_configuration *conf)
@@ -4116,6 +4506,8 @@
 	const EVP_MD *sign_md;
 	const BIGNUM *r, *s;
 	size_t extra_len = 1000;
+	int incl_legacy;
+	enum dpp_akm akm;
 
 	if (!auth->conf) {
 		wpa_printf(MSG_INFO,
@@ -4134,6 +4526,13 @@
 		goto fail;
 	}
 
+	akm = conf->akm;
+	if (dpp_akm_ver2(akm) && auth->peer_version < 2) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Convert DPP+legacy credential to DPP-only for peer that does not support version 2");
+		akm = DPP_AKM_DPP;
+	}
+
 #ifdef CONFIG_TESTING_OPTIONS
 	if (auth->groups_override)
 		extra_len += os_strlen(auth->groups_override);
@@ -4251,14 +4650,22 @@
 	if (!signed3)
 		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;
+	if (incl_legacy)
+		tailroom += 1000;
 	buf = dpp_build_conf_start(auth, conf, tailroom);
 	if (!buf)
 		goto fail;
 
-	wpabuf_put_str(buf, "\"cred\":{\"akm\":\"dpp\",\"signedConnector\":\"");
+	wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(akm));
+	if (incl_legacy) {
+		dpp_build_legacy_cred_params(buf, conf);
+		wpabuf_put_str(buf, ",");
+	}
+	wpabuf_put_str(buf, "\"signedConnector\":\"");
 	wpabuf_put_str(buf, signed1);
 	wpabuf_put_u8(buf, '.');
 	wpabuf_put_str(buf, signed2);
@@ -4304,28 +4711,7 @@
 		return NULL;
 
 	wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(conf->akm));
-	if (conf->passphrase) {
-		char pass[63 * 6 + 1];
-
-		if (os_strlen(conf->passphrase) > 63) {
-			wpabuf_free(buf);
-			return NULL;
-		}
-
-		json_escape_string(pass, sizeof(pass), conf->passphrase,
-				   os_strlen(conf->passphrase));
-		wpabuf_put_str(buf, "\"pass\":\"");
-		wpabuf_put_str(buf, pass);
-		wpabuf_put_str(buf, "\"");
-	} else {
-		char psk[2 * sizeof(conf->psk) + 1];
-
-		wpa_snprintf_hex(psk, sizeof(psk),
-				 conf->psk, sizeof(conf->psk));
-		wpabuf_put_str(buf, "\"psk_hex\":\"");
-		wpabuf_put_str(buf, psk);
-		wpabuf_put_str(buf, "\"");
-	}
+	dpp_build_legacy_cred_params(buf, conf);
 	wpabuf_put_str(buf, "}}");
 
 	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)",
@@ -4356,7 +4742,7 @@
 		return NULL;
 	}
 
-	if (conf->akm == DPP_AKM_DPP)
+	if (dpp_akm_dpp(conf->akm))
 		return dpp_build_conf_obj_dpp(auth, ap, conf);
 	return dpp_build_conf_obj_legacy(auth, ap, conf);
 }
@@ -4380,6 +4766,7 @@
 				  wpabuf_head(conf), wpabuf_len(conf));
 	}
 	status = conf ? DPP_STATUS_OK : DPP_STATUS_CONFIGURE_FAILURE;
+	auth->conf_resp_status = status;
 
 	/* { E-nonce, configurationObject}ke */
 	clear_len = 4 + e_nonce_len;
@@ -4552,6 +4939,7 @@
 		goto fail;
 	}
 	wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
+	os_memcpy(auth->e_nonce, e_nonce, e_nonce_len);
 
 	config_attr = dpp_get_attr(unwrapped, unwrapped_len,
 				   DPP_ATTR_CONFIG_ATTR_OBJ,
@@ -4716,7 +5104,7 @@
 		os_strlcpy(auth->passphrase, pass->string,
 			   sizeof(auth->passphrase));
 	} else if (psk_hex && psk_hex->type == JSON_STRING) {
-		if (auth->akm == DPP_AKM_SAE) {
+		if (dpp_akm_sae(auth->akm) && !dpp_akm_psk(auth->akm)) {
 			wpa_printf(MSG_DEBUG,
 				   "DPP: Unexpected psk_hex with akm=sae");
 			return -1;
@@ -4734,8 +5122,7 @@
 		return -1;
 	}
 
-	if ((auth->akm == DPP_AKM_SAE || auth->akm == DPP_AKM_PSK_SAE) &&
-	    !auth->passphrase[0]) {
+	if (dpp_akm_sae(auth->akm) && !auth->passphrase[0]) {
 		wpa_printf(MSG_DEBUG, "DPP: No pass for sae found");
 		return -1;
 	}
@@ -5248,6 +5635,13 @@
 
 	os_memset(&info, 0, sizeof(info));
 
+	if (dpp_akm_psk(auth->akm) || dpp_akm_sae(auth->akm)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Legacy credential included in Connector credential");
+		if (dpp_parse_cred_legacy(auth, cred) < 0)
+			return -1;
+	}
+
 	wpa_printf(MSG_DEBUG, "DPP: Connector credential");
 
 	csign = json_get_member(cred, "csign");
@@ -5313,6 +5707,10 @@
 		return "sae";
 	case DPP_AKM_PSK_SAE:
 		return "psk+sae";
+	case DPP_AKM_SAE_DPP:
+		return "dpp+sae";
+	case DPP_AKM_PSK_SAE_DPP:
+		return "dpp+psk+sae";
 	default:
 		return "??";
 	}
@@ -5329,6 +5727,10 @@
 		return DPP_AKM_PSK_SAE;
 	if (os_strcmp(akm, "dpp") == 0)
 		return DPP_AKM_DPP;
+	if (os_strcmp(akm, "dpp+sae") == 0)
+		return DPP_AKM_SAE_DPP;
+	if (os_strcmp(akm, "dpp+psk+sae") == 0)
+		return DPP_AKM_PSK_SAE_DPP;
 	return DPP_AKM_UNKNOWN;
 }
 
@@ -5392,11 +5794,10 @@
 	}
 	auth->akm = dpp_akm_from_str(token->string);
 
-	if (auth->akm == DPP_AKM_PSK || auth->akm == DPP_AKM_SAE ||
-	    auth->akm == DPP_AKM_PSK_SAE) {
+	if (dpp_akm_legacy(auth->akm)) {
 		if (dpp_parse_cred_legacy(auth, cred) < 0)
 			goto fail;
-	} else if (auth->akm == DPP_AKM_DPP) {
+	} else if (dpp_akm_dpp(auth->akm)) {
 		if (dpp_parse_cred_dpp(auth, cred) < 0)
 			goto fail;
 	} else {
@@ -5425,6 +5826,8 @@
 	size_t unwrapped_len = 0;
 	int ret = -1;
 
+	auth->conf_resp_status = 255;
+
 	if (dpp_check_attrs(wpabuf_head(resp), wpabuf_len(resp)) < 0) {
 		dpp_auth_fail(auth, "Invalid attribute in config response");
 		return -1;
@@ -5485,6 +5888,7 @@
 			      "Missing or invalid required DPP Status attribute");
 		goto fail;
 	}
+	auth->conf_resp_status = status[0];
 	wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
 	if (status[0] != DPP_STATUS_OK) {
 		dpp_auth_fail(auth, "Configurator rejected configuration");
@@ -5511,6 +5915,146 @@
 }
 
 
+#ifdef CONFIG_DPP2
+enum dpp_status_error dpp_conf_result_rx(struct dpp_authentication *auth,
+					 const u8 *hdr,
+					 const u8 *attr_start, size_t attr_len)
+{
+	const u8 *wrapped_data, *status, *e_nonce;
+	u16 wrapped_data_len, status_len, e_nonce_len;
+	const u8 *addr[2];
+	size_t len[2];
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	enum dpp_status_error ret = 256;
+
+	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_DEBUG, "DPP: Wrapped data",
+		    wrapped_data, wrapped_data_len);
+
+	attr_len = wrapped_data - 4 - attr_start;
+
+	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;
+	}
+
+	e_nonce = dpp_get_attr(unwrapped, unwrapped_len,
+			       DPP_ATTR_ENROLLEE_NONCE,
+			       &e_nonce_len);
+	if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
+		dpp_auth_fail(auth,
+			      "Missing or invalid Enrollee Nonce attribute");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
+	if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) {
+		dpp_auth_fail(auth, "Enrollee Nonce mismatch");
+		wpa_hexdump(MSG_DEBUG, "DPP: Expected Enrollee Nonce",
+			    auth->e_nonce, e_nonce_len);
+		goto fail;
+	}
+
+	status = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_STATUS,
+			      &status_len);
+	if (!status || status_len < 1) {
+		dpp_auth_fail(auth,
+			      "Missing or invalid required DPP Status attribute");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+	ret = status[0];
+
+fail:
+	bin_clear_free(unwrapped, unwrapped_len);
+	return ret;
+}
+#endif /* CONFIG_DPP2 */
+
+
+struct wpabuf * dpp_build_conf_result(struct dpp_authentication *auth,
+				      enum dpp_status_error status)
+{
+	struct wpabuf *msg, *clear;
+	size_t nonce_len, clear_len, attr_len;
+	const u8 *addr[2];
+	size_t len[2];
+	u8 *wrapped;
+
+	nonce_len = auth->curve->nonce_len;
+	clear_len = 5 + 4 + nonce_len;
+	attr_len = 4 + clear_len + AES_BLOCK_SIZE;
+	clear = wpabuf_alloc(clear_len);
+	msg = dpp_alloc_msg(DPP_PA_CONFIGURATION_RESULT, attr_len);
+	if (!clear || !msg)
+		return NULL;
+
+	/* DPP Status */
+	dpp_build_attr_status(clear, status);
+
+	/* E-nonce */
+	wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
+	wpabuf_put_le16(clear, nonce_len);
+	wpabuf_put_data(clear, auth->e_nonce, nonce_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 (none) */
+	addr[1] = wpabuf_put(msg, 0);
+	len[1] = 0;
+	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: Configuration Result attributes", msg);
+	wpabuf_free(clear);
+	return msg;
+fail:
+	wpabuf_free(clear);
+	wpabuf_free(msg);
+	return NULL;
+}
+
+
 void dpp_configurator_free(struct dpp_configurator *conf)
 {
 	if (!conf)
@@ -7691,3 +8235,487 @@
 	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)
+{
+	struct dpp_bootstrap_info *bi;
+	unsigned int max_id = 0;
+
+	dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) {
+		if (bi->id > max_id)
+			max_id = bi->id;
+	}
+	return max_id + 1;
+}
+
+
+static int dpp_bootstrap_del(struct dpp_global *dpp, unsigned int id)
+{
+	struct dpp_bootstrap_info *bi, *tmp;
+	int found = 0;
+
+	if (!dpp)
+		return -1;
+
+	dl_list_for_each_safe(bi, tmp, &dpp->bootstrap,
+			      struct dpp_bootstrap_info, list) {
+		if (id && bi->id != id)
+			continue;
+		found = 1;
+		dl_list_del(&bi->list);
+		dpp_bootstrap_info_free(bi);
+	}
+
+	if (id == 0)
+		return 0; /* flush succeeds regardless of entries found */
+	return found ? 0 : -1;
+}
+
+
+struct dpp_bootstrap_info * dpp_add_qr_code(struct dpp_global *dpp,
+					    const char *uri)
+{
+	struct dpp_bootstrap_info *bi;
+
+	if (!dpp)
+		return NULL;
+
+	bi = dpp_parse_qr_code(uri);
+	if (!bi)
+		return NULL;
+
+	bi->id = dpp_next_id(dpp);
+	dl_list_add(&dpp->bootstrap, &bi->list);
+	return bi;
+}
+
+
+int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd)
+{
+	char *chan = NULL, *mac = NULL, *info = NULL, *pk = NULL, *curve = NULL;
+	char *key = NULL;
+	u8 *privkey = NULL;
+	size_t privkey_len = 0;
+	size_t len;
+	int ret = -1;
+	struct dpp_bootstrap_info *bi;
+
+	if (!dpp)
+		return -1;
+
+	bi = os_zalloc(sizeof(*bi));
+	if (!bi)
+		goto fail;
+
+	if (os_strstr(cmd, "type=qrcode"))
+		bi->type = DPP_BOOTSTRAP_QR_CODE;
+	else if (os_strstr(cmd, "type=pkex"))
+		bi->type = DPP_BOOTSTRAP_PKEX;
+	else
+		goto fail;
+
+	chan = get_param(cmd, " chan=");
+	mac = get_param(cmd, " mac=");
+	info = get_param(cmd, " info=");
+	curve = get_param(cmd, " curve=");
+	key = get_param(cmd, " key=");
+
+	if (key) {
+		privkey_len = os_strlen(key) / 2;
+		privkey = os_malloc(privkey_len);
+		if (!privkey ||
+		    hexstr2bin(key, privkey, privkey_len) < 0)
+			goto fail;
+	}
+
+	pk = dpp_keygen(bi, curve, privkey, privkey_len);
+	if (!pk)
+		goto fail;
+
+	len = 4; /* "DPP:" */
+	if (chan) {
+		if (dpp_parse_uri_chan_list(bi, chan) < 0)
+			goto fail;
+		len += 3 + os_strlen(chan); /* C:...; */
+	}
+	if (mac) {
+		if (dpp_parse_uri_mac(bi, mac) < 0)
+			goto fail;
+		len += 3 + os_strlen(mac); /* M:...; */
+	}
+	if (info) {
+		if (dpp_parse_uri_info(bi, info) < 0)
+			goto fail;
+		len += 3 + os_strlen(info); /* I:...; */
+	}
+	len += 4 + os_strlen(pk);
+	bi->uri = os_malloc(len + 1);
+	if (!bi->uri)
+		goto fail;
+	os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%sK:%s;;",
+		    chan ? "C:" : "", chan ? chan : "", chan ? ";" : "",
+		    mac ? "M:" : "", mac ? mac : "", mac ? ";" : "",
+		    info ? "I:" : "", info ? info : "", info ? ";" : "",
+		    pk);
+	bi->id = dpp_next_id(dpp);
+	dl_list_add(&dpp->bootstrap, &bi->list);
+	ret = bi->id;
+	bi = NULL;
+fail:
+	os_free(curve);
+	os_free(pk);
+	os_free(chan);
+	os_free(mac);
+	os_free(info);
+	str_clear_free(key);
+	bin_clear_free(privkey, privkey_len);
+	dpp_bootstrap_info_free(bi);
+	return ret;
+}
+
+
+struct dpp_bootstrap_info *
+dpp_bootstrap_get_id(struct dpp_global *dpp, unsigned int id)
+{
+	struct dpp_bootstrap_info *bi;
+
+	if (!dpp)
+		return NULL;
+
+	dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) {
+		if (bi->id == id)
+			return bi;
+	}
+	return NULL;
+}
+
+
+int dpp_bootstrap_remove(struct dpp_global *dpp, const char *id)
+{
+	unsigned int id_val;
+
+	if (os_strcmp(id, "*") == 0) {
+		id_val = 0;
+	} else {
+		id_val = atoi(id);
+		if (id_val == 0)
+			return -1;
+	}
+
+	return dpp_bootstrap_del(dpp, id_val);
+}
+
+
+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;
+
+	bi = dpp_bootstrap_get_id(dpp, id);
+	if (!bi)
+		return NULL;
+	return bi->uri;
+}
+
+
+int dpp_bootstrap_info(struct dpp_global *dpp, int id,
+		       char *reply, int reply_size)
+{
+	struct dpp_bootstrap_info *bi;
+
+	bi = dpp_bootstrap_get_id(dpp, id);
+	if (!bi)
+		return -1;
+	return os_snprintf(reply, reply_size, "type=%s\n"
+			   "mac_addr=" MACSTR "\n"
+			   "info=%s\n"
+			   "num_freq=%u\n"
+			   "curve=%s\n",
+			   dpp_bootstrap_type_txt(bi->type),
+			   MAC2STR(bi->mac_addr),
+			   bi->info ? bi->info : "",
+			   bi->num_freq,
+			   bi->curve->name);
+}
+
+
+void dpp_bootstrap_find_pair(struct dpp_global *dpp, const u8 *i_bootstrap,
+			     const u8 *r_bootstrap,
+			     struct dpp_bootstrap_info **own_bi,
+			     struct dpp_bootstrap_info **peer_bi)
+{
+	struct dpp_bootstrap_info *bi;
+
+	*own_bi = NULL;
+	*peer_bi = NULL;
+	if (!dpp)
+		return;
+
+	dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) {
+		if (!*own_bi && bi->own &&
+		    os_memcmp(bi->pubkey_hash, r_bootstrap,
+			      SHA256_MAC_LEN) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Found matching own bootstrapping information");
+			*own_bi = bi;
+		}
+
+		if (!*peer_bi && !bi->own &&
+		    os_memcmp(bi->pubkey_hash, i_bootstrap,
+			      SHA256_MAC_LEN) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Found matching peer bootstrapping information");
+			*peer_bi = bi;
+		}
+
+		if (*own_bi && *peer_bi)
+			break;
+	}
+
+}
+
+
+static unsigned int dpp_next_configurator_id(struct dpp_global *dpp)
+{
+	struct dpp_configurator *conf;
+	unsigned int max_id = 0;
+
+	dl_list_for_each(conf, &dpp->configurator, struct dpp_configurator,
+			 list) {
+		if (conf->id > max_id)
+			max_id = conf->id;
+	}
+	return max_id + 1;
+}
+
+
+int dpp_configurator_add(struct dpp_global *dpp, const char *cmd)
+{
+	char *curve = NULL;
+	char *key = NULL;
+	u8 *privkey = NULL;
+	size_t privkey_len = 0;
+	int ret = -1;
+	struct dpp_configurator *conf = NULL;
+
+	curve = get_param(cmd, " curve=");
+	key = get_param(cmd, " key=");
+
+	if (key) {
+		privkey_len = os_strlen(key) / 2;
+		privkey = os_malloc(privkey_len);
+		if (!privkey ||
+		    hexstr2bin(key, privkey, privkey_len) < 0)
+			goto fail;
+	}
+
+	conf = dpp_keygen_configurator(curve, privkey, privkey_len);
+	if (!conf)
+		goto fail;
+
+	conf->id = dpp_next_configurator_id(dpp);
+	dl_list_add(&dpp->configurator, &conf->list);
+	ret = conf->id;
+	conf = NULL;
+fail:
+	os_free(curve);
+	str_clear_free(key);
+	bin_clear_free(privkey, privkey_len);
+	dpp_configurator_free(conf);
+	return ret;
+}
+
+
+static int dpp_configurator_del(struct dpp_global *dpp, unsigned int id)
+{
+	struct dpp_configurator *conf, *tmp;
+	int found = 0;
+
+	if (!dpp)
+		return -1;
+
+	dl_list_for_each_safe(conf, tmp, &dpp->configurator,
+			      struct dpp_configurator, list) {
+		if (id && conf->id != id)
+			continue;
+		found = 1;
+		dl_list_del(&conf->list);
+		dpp_configurator_free(conf);
+	}
+
+	if (id == 0)
+		return 0; /* flush succeeds regardless of entries found */
+	return found ? 0 : -1;
+}
+
+
+int dpp_configurator_remove(struct dpp_global *dpp, const char *id)
+{
+	unsigned int id_val;
+
+	if (os_strcmp(id, "*") == 0) {
+		id_val = 0;
+	} else {
+		id_val = atoi(id);
+		if (id_val == 0)
+			return -1;
+	}
+
+	return dpp_configurator_del(dpp, id_val);
+}
+
+
+int dpp_configurator_get_key_id(struct dpp_global *dpp, unsigned int id,
+				char *buf, size_t buflen)
+{
+	struct dpp_configurator *conf;
+
+	conf = dpp_configurator_get_id(dpp, id);
+	if (!conf)
+		return -1;
+
+	return dpp_configurator_get_key(conf, buf, buflen);
+}
+
+
+struct dpp_global * dpp_global_init(void)
+{
+	struct dpp_global *dpp;
+
+	dpp = os_zalloc(sizeof(*dpp));
+	if (!dpp)
+		return NULL;
+
+	dl_list_init(&dpp->bootstrap);
+	dl_list_init(&dpp->configurator);
+
+	return dpp;
+}
+
+
+void dpp_global_clear(struct dpp_global *dpp)
+{
+	if (!dpp)
+		return;
+
+	dpp_bootstrap_del(dpp, 0);
+	dpp_configurator_del(dpp, 0);
+}
+
+
+void dpp_global_deinit(struct dpp_global *dpp)
+{
+	dpp_global_clear(dpp);
+	os_free(dpp);
+}
diff --git a/src/common/dpp.h b/src/common/dpp.h
index 2575908..5a6d8cc 100644
--- a/src/common/dpp.h
+++ b/src/common/dpp.h
@@ -1,6 +1,7 @@
 /*
  * DPP functionality shared between hostapd and wpa_supplicant
  * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2019, The Linux Foundation
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -9,12 +10,16 @@
 #ifndef DPP_H
 #define DPP_H
 
+#ifdef CONFIG_DPP
 #include <openssl/x509.h>
 
 #include "utils/list.h"
 #include "common/wpa_common.h"
 #include "crypto/sha256.h"
 
+struct crypto_ecdh;
+struct dpp_global;
+
 #define DPP_HDR_LEN (4 + 2) /* OUI, OUI Type, Crypto Suite, DPP frame type */
 
 enum dpp_public_action_frame_type {
@@ -27,6 +32,7 @@
 	DPP_PA_PKEX_EXCHANGE_RESP = 8,
 	DPP_PA_PKEX_COMMIT_REVEAL_REQ = 9,
 	DPP_PA_PKEX_COMMIT_REVEAL_RESP = 10,
+	DPP_PA_CONFIGURATION_RESULT = 11,
 };
 
 enum dpp_attribute_id {
@@ -54,6 +60,8 @@
 	DPP_ATTR_TRANSACTION_ID = 0x1016,
 	DPP_ATTR_BOOTSTRAP_INFO = 0x1017,
 	DPP_ATTR_CHANNEL = 0x1018,
+	DPP_ATTR_PROTOCOL_VERSION = 0x1019,
+	DPP_ATTR_ENVELOPED_DATA = 0x101A,
 };
 
 enum dpp_status_error {
@@ -66,6 +74,7 @@
 	DPP_STATUS_RESPONSE_PENDING = 6,
 	DPP_STATUS_INVALID_CONNECTOR = 7,
 	DPP_STATUS_NO_MATCH = 8,
+	DPP_STATUS_CONFIG_REJECTED = 9,
 };
 
 #define DPP_CAPAB_ENROLLEE BIT(0)
@@ -141,7 +150,9 @@
 	DPP_AKM_DPP,
 	DPP_AKM_PSK,
 	DPP_AKM_SAE,
-	DPP_AKM_PSK_SAE
+	DPP_AKM_PSK_SAE,
+	DPP_AKM_SAE_DPP,
+	DPP_AKM_PSK_SAE_DPP,
 };
 
 struct dpp_configuration {
@@ -158,10 +169,12 @@
 	/* For legacy configuration */
 	char *passphrase;
 	u8 psk[32];
+	int psk_set;
 };
 
 struct dpp_authentication {
 	void *msg_ctx;
+	u8 peer_version;
 	const struct dpp_curve_params *curve;
 	struct dpp_bootstrap_info *peer_bi;
 	struct dpp_bootstrap_info *own_bi;
@@ -169,6 +182,7 @@
 	u8 waiting_pubkey_hash[SHA256_MAC_LEN];
 	int response_pending;
 	enum dpp_status_error auth_resp_status;
+	enum dpp_status_error conf_resp_status;
 	u8 peer_mac_addr[ETH_ALEN];
 	u8 i_nonce[DPP_MAX_NONCE_LEN];
 	u8 r_nonce[DPP_MAX_NONCE_LEN];
@@ -204,6 +218,8 @@
 	u8 allowed_roles;
 	int configurator;
 	int remove_on_tx_status;
+	int connect_on_tx_status;
+	int waiting_conf_result;
 	int auth_success;
 	struct wpabuf *conf_req;
 	const struct wpabuf *conf_resp; /* owned by GAS server */
@@ -336,6 +352,7 @@
 	DPP_TEST_STOP_AT_AUTH_RESP = 88,
 	DPP_TEST_STOP_AT_AUTH_CONF = 89,
 	DPP_TEST_STOP_AT_CONF_REQ = 90,
+	DPP_TEST_REJECT_CONFIG = 91,
 };
 
 extern enum dpp_test_behavior dpp_test;
@@ -382,13 +399,28 @@
 		     const u8 *attr_start, size_t attr_len);
 int dpp_notify_new_qr_code(struct dpp_authentication *auth,
 			   struct dpp_bootstrap_info *peer_bi);
+struct dpp_configuration * dpp_configuration_alloc(const char *type);
+int dpp_akm_psk(enum dpp_akm akm);
+int dpp_akm_sae(enum dpp_akm akm);
+int dpp_akm_legacy(enum dpp_akm akm);
+int dpp_akm_dpp(enum dpp_akm akm);
+int dpp_akm_ver2(enum dpp_akm akm);
+int dpp_configuration_valid(const struct dpp_configuration *conf);
 void dpp_configuration_free(struct dpp_configuration *conf);
+int dpp_set_configurator(struct dpp_global *dpp, void *msg_ctx,
+			 struct dpp_authentication *auth,
+			 const char *cmd);
 void dpp_auth_deinit(struct dpp_authentication *auth);
 struct wpabuf *
 dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
 		size_t attr_len);
 int dpp_conf_resp_rx(struct dpp_authentication *auth,
 		     const struct wpabuf *resp);
+enum dpp_status_error dpp_conf_result_rx(struct dpp_authentication *auth,
+					 const u8 *hdr,
+					 const u8 *attr_start, size_t attr_len);
+struct wpabuf * dpp_build_conf_result(struct dpp_authentication *auth,
+				      enum dpp_status_error status);
 struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type,
 			      size_t len);
 const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len);
@@ -432,4 +464,42 @@
 
 char * dpp_corrupt_connector_signature(const char *connector);
 
+
+struct dpp_pfs {
+	struct crypto_ecdh *ecdh;
+	const struct dpp_curve_params *curve;
+	struct wpabuf *ie;
+	struct wpabuf *secret;
+};
+
+struct dpp_pfs * dpp_pfs_init(const u8 *net_access_key,
+			      size_t net_access_key_len);
+int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len);
+void dpp_pfs_free(struct dpp_pfs *pfs);
+
+struct dpp_bootstrap_info * dpp_add_qr_code(struct dpp_global *dpp,
+					    const char *uri);
+int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd);
+struct dpp_bootstrap_info *
+dpp_bootstrap_get_id(struct dpp_global *dpp, unsigned int id);
+int dpp_bootstrap_remove(struct dpp_global *dpp, const char *id);
+struct dpp_bootstrap_info *
+dpp_pkex_finish(struct dpp_global *dpp, struct dpp_pkex *pkex, const u8 *peer,
+		unsigned int freq);
+const char * dpp_bootstrap_get_uri(struct dpp_global *dpp, unsigned int id);
+int dpp_bootstrap_info(struct dpp_global *dpp, int id,
+		       char *reply, int reply_size);
+void dpp_bootstrap_find_pair(struct dpp_global *dpp, const u8 *i_bootstrap,
+			     const u8 *r_bootstrap,
+			     struct dpp_bootstrap_info **own_bi,
+			     struct dpp_bootstrap_info **peer_bi);
+int dpp_configurator_add(struct dpp_global *dpp, const char *cmd);
+int dpp_configurator_remove(struct dpp_global *dpp, const char *id);
+int dpp_configurator_get_key_id(struct dpp_global *dpp, unsigned int id,
+				char *buf, size_t buflen);
+struct dpp_global * dpp_global_init(void);
+void dpp_global_clear(struct dpp_global *dpp);
+void dpp_global_deinit(struct dpp_global *dpp);
+
+#endif /* CONFIG_DPP */
 #endif /* DPP_H */
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index 51e4e0c..f886551 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -270,6 +270,10 @@
 		elems->password_id = pos;
 		elems->password_id_len = elen;
 		break;
+	case WLAN_EID_EXT_HE_CAPABILITIES:
+		elems->he_capabilities = pos;
+		elems->he_capabilities_len = elen;
+		break;
 	case WLAN_EID_EXT_OCV_OCI:
 		elems->oci = pos;
 		elems->oci_len = elen;
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 4e35b79..d41bd39 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -93,6 +93,7 @@
 	const u8 *password_id;
 	const u8 *oci;
 	const u8 *multi_ap;
+	const u8 *he_capabilities;
 
 	u8 ssid_len;
 	u8 supp_rates_len;
@@ -141,6 +142,7 @@
 	u8 password_id_len;
 	u8 oci_len;
 	u8 multi_ap_len;
+	u8 he_capabilities_len;
 
 	struct mb_ies_info mb_ies;
 };
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index b285d13..adaa893 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -334,7 +334,7 @@
 #define WLAN_EID_LOCATION_PARAMETERS 82
 #define WLAN_EID_NONTRANSMITTED_BSSID_CAPA 83
 #define WLAN_EID_SSID_LIST 84
-#define WLAN_EID_MLTIPLE_BSSID_INDEX 85
+#define WLAN_EID_MULTIPLE_BSSID_INDEX 85
 #define WLAN_EID_FMS_DESCRIPTOR 86
 #define WLAN_EID_FMS_REQUEST 87
 #define WLAN_EID_FMS_RESPONSE 88
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index 6f5b87e..c34a3bc 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -507,6 +507,23 @@
  * @QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS: This command is used to query
  *	the supported AKM suite selectorss from the driver. It returns the list
  *	of supported AKMs in the attribute NL80211_ATTR_AKM_SUITES.
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE: This command is used to get firmware
+ *	state from the driver. It returns the firmware state in the attribute
+ *	QCA_WLAN_VENDOR_ATTR_FW_STATE.
+ * @QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH: This vendor subcommand
+ *	is used by the driver to flush per-peer cached statistics to user space
+ *	application. This interface is used as an event from the driver to
+ *	user space application. Attributes for this event are specified in
+ *	enum qca_wlan_vendor_attr_peer_stats_cache_params.
+ *	QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA attribute is expected to be
+ *	sent in the event.
+ * @QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG: This sub command is used to
+ *	improve the success rate of Zigbee joining network.
+ *	Due to PTA master limitation, Zigbee joining network success rate is
+ *	low while WLAN is working. The WLAN driver needs to configure some
+ *	parameters including Zigbee state and specific WLAN periods to enhance
+ *	PTA master. All these parameters are delivered by the attributes
+ *	defined in enum qca_mpta_helper_vendor_attr.
  */
 enum qca_nl80211_vendor_subcmds {
 	QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -672,6 +689,9 @@
 	QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT = 174,
 	QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG = 175,
 	QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS = 176,
+	QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE = 177,
+	QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH = 178,
+	QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG = 179,
 };
 
 enum qca_wlan_vendor_attr {
@@ -858,6 +878,12 @@
 	 * for each antenna ID, and application extract them in user space.
 	 */
 	QCA_WLAN_VENDOR_ATTR_CHAIN_EVM = 41,
+	/*
+	 * Used in QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE command to report
+	 * wlan firmware current state. FW state is an unsigned 8 bit value,
+	 * one of the values in enum qca_wlan_vendor_attr_fw_state.
+	 */
+	QCA_WLAN_VENDOR_ATTR_FW_STATE = 42,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
@@ -869,6 +895,49 @@
 	QCA_ROAMING_ALLOWED_WITHIN_ESS,
 };
 
+/**
+ * enum qca_roam_reason - Represents the reason codes for roaming. Used by
+ * QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REASON.
+ *
+ * @QCA_ROAM_REASON_UNKNOWN: Any reason that do not classify under the below
+ * reasons.
+ *
+ * @QCA_ROAM_REASON_PER: Roam triggered when packet error rates (PER) breached
+ * the configured threshold.
+ *
+ * @QCA_ROAM_REASON_BEACON_MISS: Roam triggered due to the continuous configured
+ * beacon misses from the then connected AP.
+ *
+ * @QCA_ROAM_REASON_POOR_RSSI: Roam triggered due to the poor RSSI reported
+ * by the connected AP.
+ *
+ * @QCA_ROAM_REASON_BETTER_RSSI: Roam triggered for finding a BSS with a better
+ * RSSI than the connected BSS. Here the RSSI of the current BSS is not poor.
+ *
+ * @QCA_ROAM_REASON_CONGESTION: Roam triggered considering the connected channel
+ * or environment being very noisy or congested.
+ *
+ * @QCA_ROAM_REASON_EXPLICIT_REQUEST: Roam triggered due to an explicit request
+ * from the user (user space).
+ *
+ * @QCA_ROAM_REASON_BTM: Roam triggered due to BTM Request frame received from
+ * the connected AP.
+ *
+ * @QCA_ROAM_REASON_BSS_LOAD: Roam triggered due to the channel utilization
+ * breaching out the configured threshold.
+ */
+enum qca_roam_reason {
+	QCA_ROAM_REASON_UNKNOWN,
+	QCA_ROAM_REASON_PER,
+	QCA_ROAM_REASON_BEACON_MISS,
+	QCA_ROAM_REASON_POOR_RSSI,
+	QCA_ROAM_REASON_BETTER_RSSI,
+	QCA_ROAM_REASON_CONGESTION,
+	QCA_ROAM_REASON_USER_TRIGGER,
+	QCA_ROAM_REASON_BTM,
+	QCA_ROAM_REASON_BSS_LOAD,
+};
+
 enum qca_wlan_vendor_attr_roam_auth {
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_INVALID = 0,
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID,
@@ -911,6 +980,11 @@
 	 * doing subsequent ERP based connections in the same realm.
 	 */
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM = 13,
+	/* A 16-bit unsigned value representing the reasons for the roaming.
+	 * Defined by enum qca_roam_reason.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REASON = 14,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX =
@@ -1009,6 +1083,7 @@
  *	only OCE STA-CFON functionalities.
  * @QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY: Device supports self
  *	managed regulatory.
+ * @QCA_WLAN_VENDOR_FEATURE_TWT: Device supports TWT (Target Wake Time).
  * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
  */
 enum qca_wlan_vendor_features {
@@ -1020,6 +1095,7 @@
 	QCA_WLAN_VENDOR_FEATURE_OCE_AP                  = 5,
 	QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON            = 6,
 	QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY = 7,
+	QCA_WLAN_VENDOR_FEATURE_TWT 			= 8,
 	NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
 };
 
@@ -2453,6 +2529,18 @@
 };
 
 /**
+ * enum qca_wlan_vendor_attr_fw_state - State of firmware
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_STATE_ERROR: FW is in bad state
+ * @QCA_WLAN_VENDOR_ATTR_FW_STATE_ACTIVE: FW is active
+ */
+enum qca_wlan_vendor_attr_fw_state {
+	QCA_WLAN_VENDOR_ATTR_FW_STATE_ERROR,
+	QCA_WLAN_VENDOR_ATTR_FW_STATE_ACTIVE,
+	QCA_WLAN_VENDOR_ATTR_FW_STATE_MAX
+};
+
+/**
  * BRP antenna limit mode
  *
  * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_DISABLE: Disable BRP force
@@ -3196,6 +3284,8 @@
 	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS = 18,
 	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID = 19,
 	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID = 20,
+	/* Flag attribute indicates this BSSID blacklist as a hint */
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_HINT = 21,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_AFTER_LAST,
@@ -4886,6 +4976,10 @@
 	QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_DST_MAC_ADDR,
 	/* Unsigned 32-bit value, in milli seconds */
 	QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_PERIOD,
+	/* This optional unsigned 16-bit attribute is used for specifying
+	 * ethernet protocol type. If not specified ethertype defaults to IPv4.
+	 */
+	QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_ETHER_PROTO_TYPE,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_AFTER_LAST,
@@ -5530,6 +5624,16 @@
  * @QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS: Mandatory 8-bit unsigned value
  * indicates the maximum number of space-time streams, NSTS, that
  * the STA supports in transmission and is set to NSTS - 1.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE: 8-bit unsigned value
+ * combined with the UL MU Disable subfield and the recipient's setting
+ * of the OM Control UL MU Data Disable RX Support subfield in the HE MAC
+ * capabilities to determine which HE TB PPDUs are possible by the
+ * STA to transmit.
+ * 0 - UL MU data operations are enabled by the STA.
+ * 1 - Determine which HE TB PPDU types are allowed by the STA if UL MU disable
+ * bit is not set, else UL MU Tx is suspended.
+ *
  */
 enum qca_wlan_vendor_attr_he_omi_tx {
 	QCA_WLAN_VENDOR_ATTR_HE_OMI_INVALID = 0,
@@ -5537,6 +5641,7 @@
 	QCA_WLAN_VENDOR_ATTR_HE_OMI_CH_BW = 2,
 	QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DISABLE = 3,
 	QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS = 4,
+	QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE = 5,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_HE_OMI_AFTER_LAST,
@@ -5808,6 +5913,25 @@
 	 */
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_HTC_HE_SUPP = 34,
 
+	/* 8-bit unsigned value to configure VHT support in 2.4G band.
+	 * This attribute is used to configure the testbed device.
+	 * 1-enable, 0-disable
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_2G_VHT = 35,
+
+	/* 8-bit unsigned value to configure HE testbed defaults.
+	 * This attribute is used to configure the testbed device.
+	 * 1-set the device HE capabilities to testbed defaults.
+	 * 0-reset the device HE capabilities to supported config.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SET_HE_TESTBED_DEFAULTS = 36,
+
+	/* 8-bit unsigned value to configure TWT request support.
+	 * This attribute is used to configure the testbed device.
+	 * 1-enable, 0-disable.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TWT_REQ_SUPPORT = 37,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX =
@@ -6290,13 +6414,35 @@
 	QCA_WIFI_SAP_CLASS_3_MGMT = 7,
 	QCA_WIFI_SAP_DATA = 8,
 	QCA_WIFI_SAP_ALL = 9,
+	QCA_WIFI_CASE_MAX = 31,
 	/* 32 - 63 corresponds to BT */
 	QCA_BT_A2DP = 32,
 	QCA_BT_BLE = 33,
 	QCA_BT_SCO = 34,
+	QCA_BT_CASE_MAX = 63,
 	/* 64 - 95 corresponds to Zigbee */
 	QCA_ZB_LOW = 64,
-	QCA_ZB_HIGH = 65
+	QCA_ZB_HIGH = 65,
+	QCA_ZB_CASE_MAX = 95,
+	/* 0xff is default value if the u8 profile value is not set. */
+	QCA_COEX_CONFIG_PROFILE_DEFAULT_VALUE = 255
+};
+
+/**
+ * enum qca_vendor_attr_coex_config_types - Coex configurations types.
+ * This enum defines the valid set of values of coex configuration types. These
+ * values may used by attribute
+ * %QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_RESET: Reset all the
+ *	weights to default values.
+ * @QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_START: Start to config
+ *	weights with configurability value.
+ */
+enum qca_vendor_attr_coex_config_types {
+	QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_RESET = 1,
+	QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_START = 2,
 };
 
 /**
@@ -6328,6 +6474,80 @@
 };
 
 /**
+ * enum qca_vendor_attr_coex_config_three_way - Specifies vendor coex config
+ * attributes
+ * Attributes for data used by QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG
+ *
+ * QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE: u32 attribute.
+ * Indicate config type.
+ * The config types are 32-bit values from qca_vendor_attr_coex_config_types
+ *
+ * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_1: u32 attribute.
+ *	Indicate the Priority 1 profiles.
+ *	The profiles are 8-bit values from enum qca_coex_config_profiles.
+ *	In same priority level, maximum to 4 profiles can be set here.
+ * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_2: u32 attribute.
+ *	Indicate the Priority 2 profiles.
+ *	The profiles are 8-bit values from enum qca_coex_config_profiles.
+ *	In same priority level, maximum to 4 profiles can be set here.
+ * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_3: u32 attribute.
+ *	Indicate the Priority 3 profiles.
+ *	The profiles are 8-bit values from enum qca_coex_config_profiles.
+ *	In same priority level, maximum to 4 profiles can be set here.
+ * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_4: u32 attribute.
+ *	Indicate the Priority 4 profiles.
+ *	The profiles are 8-bit values from enum qca_coex_config_profiles.
+ *	In same priority level, maximum to 4 profiles can be set here.
+ * NOTE:
+ * Limitations for QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_x priority
+ * arrangement:
+ *	1: In the same u32 attribute (priority x), the profiles enum values own
+ *	same priority level.
+ *	2: 0xff is default value if the u8 profile value is not set.
+ *	3: max to 4 rules/profiles in same priority level.
+ *	4: max to 4 priority level (priority 1 - priority 4)
+ *	5: one priority level only supports one scenario from WLAN/BT/ZB,
+ *	hybrid rules not support.
+ *	6: if WMI_COEX_CONFIG_THREE_WAY_COEX_RESET called, priority x will
+ *	remain blank to reset all parameters.
+ * For example:
+ *
+ *	If the attributes as follow:
+ *	priority 1:
+ *	------------------------------------
+ *	|  0xff  |    0   |   1   |    2   |
+ *	------------------------------------
+ *	priority 2:
+ *	-------------------------------------
+ *	|  0xff  |  0xff  |  0xff  |   32   |
+ *	-------------------------------------
+ *	priority 3:
+ *	-------------------------------------
+ *	|  0xff  |  0xff  |  0xff  |   65   |
+ *	-------------------------------------
+ *	then it means:
+ *	1: WIFI_STA_DISCOVERY, WIFI_STA_CLASS_3_MGMT and WIFI_STA_CONNECTION
+ *		owns same priority level.
+ *	2: WIFI_STA_DISCOVERY, WIFI_STA_CLASS_3_MGMT and WIFI_STA_CONNECTION
+ *		has priority over BT_A2DP and ZB_HIGH.
+ *	3: BT_A2DP has priority over ZB_HIGH.
+ */
+
+enum qca_vendor_attr_coex_config_three_way {
+	QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_INVALID = 0,
+	QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE = 1,
+	QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_1 = 2,
+	QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_2 = 3,
+	QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_3 = 4,
+	QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_4 = 5,
+
+	/* Keep last */
+	QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_AFTER_LAST,
+	QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_MAX =
+	QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_AFTER_LAST - 1,
+};
+
+/**
  * enum qca_wlan_vendor_attr_link_properties - Represent the link properties.
  *
  * @QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR: MAC address of the peer
@@ -6349,4 +6569,144 @@
 	QCA_VENDOR_ATTR_LINK_PROPERTIES_AFTER_LAST - 1,
 };
 
+/**
+ * enum qca_vendor_attr_peer_stats_cache_type - Represents peer stats cache type
+ * This enum defines the valid set of values of peer stats cache types. These
+ * values are used by attribute
+ * %QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_TX_RATE_STATS: Represents peer TX rate statistics
+ * @QCA_WLAN_VENDOR_ATTR_PEER_RX_RATE_STATS: Represents peer RX rate statistics
+ * @QCA_WLAN_VENDOR_ATTR_PEER_TX_SOJOURN_STATS: Represents peer TX sojourn
+ * statistics
+ */
+enum qca_vendor_attr_peer_stats_cache_type {
+	QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE_INVALID = 0,
+
+	QCA_WLAN_VENDOR_ATTR_PEER_TX_RATE_STATS,
+	QCA_WLAN_VENDOR_ATTR_PEER_RX_RATE_STATS,
+	QCA_WLAN_VENDOR_ATTR_PEER_TX_SOJOURN_STATS,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_peer_stats_cache_params - This enum defines
+ * attributes required for QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH
+ * Information in these attributes is used to flush peer rate statistics from
+ * the driver to user application.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE: Unsigned 32-bit attribute
+ * Indicate peer statistics cache type.
+ * The statistics types are 32-bit values from
+ * enum qca_vendor_attr_peer_stats_cache_type.
+ * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_MAC: Unsigned 8-bit array
+ * of size 6 octets, representing the peer MAC address.
+ * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA: Opaque data attribute
+ * containing buffer of statistics to send to application layer entity.
+ * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_COOKIE: Unsigned 64-bit attribute
+ * representing a cookie for peer unique session.
+ */
+enum qca_wlan_vendor_attr_peer_stats_cache_params {
+	QCA_WLAN_VENDOR_ATTR_PEER_STATS_INVALID = 0,
+
+	QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE = 1,
+	QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_MAC = 2,
+	QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA = 3,
+	QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_COOKIE = 4,
+
+	/* Keep last */
+	QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_LAST,
+	QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_MAX =
+		QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_LAST - 1
+};
+
+/**
+ * enum qca_mpta_helper_attr_zigbee_state - Current Zigbee state
+ * This enum defines all the possible states of Zigbee, which can be
+ * delivered in the QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_STATE attribute.
+ *
+ * @ZIGBEE_IDLE: Zigbee in idle state
+ * @ZIGBEE_FORM_NETWORK: Zigbee forming network
+ * @ZIGBEE_WAIT_JOIN: Zigbee waiting for joining network
+ * @ZIGBEE_JOIN: Zigbee joining network
+ * @ZIGBEE_NETWORK_UP: Zigbee network is up
+ * @ZIGBEE_HMI: Zigbee in HMI mode
+ */
+enum qca_mpta_helper_attr_zigbee_state {
+	ZIGBEE_IDLE = 0,
+	ZIGBEE_FORM_NETWORK = 1,
+	ZIGBEE_WAIT_JOIN = 2,
+	ZIGBEE_JOIN = 3,
+	ZIGBEE_NETWORK_UP = 4,
+	ZIGBEE_HMI = 5,
+};
+
+/*
+ * enum qca_mpta_helper_vendor_attr - Attributes used in vendor sub-command
+ * QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG.
+ */
+enum qca_mpta_helper_vendor_attr {
+	QCA_MPTA_HELPER_VENDOR_ATTR_INVALID = 0,
+	/* Optional attribute used to update Zigbee state.
+	 * enum qca_mpta_helper_attr_zigbee_state.
+	 * NLA_U32 attribute.
+	 */
+	QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_STATE = 1,
+	/* Optional attribute used to configure WLAN duration for Shape-OCS
+	 * during interrupt.
+	 * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_NON_WLAN_DURATION.
+	 * Value range 0 ~ 300 (ms).
+	 * NLA_U32 attribute.
+	 */
+	QCA_MPTA_HELPER_VENDOR_ATTR_INT_WLAN_DURATION = 2,
+	/* Optional attribute used to configure non-WLAN duration for Shape-OCS
+	 * during interrupt.
+	 * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_WLAN_DURATION.
+	 * Value range 0 ~ 300 (ms).
+	 * NLA_U32 attribute.
+	 */
+	QCA_MPTA_HELPER_VENDOR_ATTR_INT_NON_WLAN_DURATION  = 3,
+	/* Optional attribute used to configure WLAN duration for Shape-OCS
+	 * monitor period.
+	 * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_NON_WLAN_DURATION.
+	 * Value range 0 ~ 300 (ms)
+	 * NLA_U32 attribute
+	 */
+	QCA_MPTA_HELPER_VENDOR_ATTR_MON_WLAN_DURATION = 4,
+	/* Optional attribute used to configure non-WLAN duration for Shape-OCS
+	 * monitor period.
+	 * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_WLAN_DURATION.
+	 * Value range 0 ~ 300 (ms)
+	 * NLA_U32 attribute
+	 */
+	QCA_MPTA_HELPER_VENDOR_ATTR_MON_NON_WLAN_DURATION  = 5,
+	/* Optional attribute used to configure OCS interrupt duration.
+	 * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_OCS_DURATION.
+	 * Value range 1000 ~ 20000 (ms)
+	 * NLA_U32 attribute
+	 */
+	QCA_MPTA_HELPER_VENDOR_ATTR_INT_OCS_DURATION  = 6,
+	/* Optional attribute used to configure OCS monitor duration.
+	 * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_OCS_DURATION.
+	 * Value range 1000 ~ 20000 (ms)
+	 * NLA_U32 attribute
+	 */
+	QCA_MPTA_HELPER_VENDOR_ATTR_MON_OCS_DURATION  = 7,
+	/* Optional attribute used to notify WLAN firmware the current Zigbee
+	 * channel.
+	 * Value range 11 ~ 26
+	 * NLA_U32 attribute
+	 */
+	QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_CHAN = 8,
+	/* Optional attribute used to configure WLAN mute duration.
+	 * Value range 0 ~ 400 (ms)
+	 * NLA_U32 attribute
+	 */
+	QCA_MPTA_HELPER_VENDOR_ATTR_WLAN_MUTE_DURATION	= 9,
+
+	/* keep last */
+	QCA_MPTA_HELPER_VENDOR_ATTR_AFTER_LAST,
+	QCA_MPTA_HELPER_VENDOR_ATTR_MAX =
+		QCA_MPTA_HELPER_VENDOR_ATTR_AFTER_LAST - 1
+};
+
 #endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index 981e788..5a50294 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -9,6 +9,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "utils/const_time.h"
 #include "crypto/crypto.h"
 #include "crypto/sha256.h"
 #include "crypto/random.h"
@@ -17,10 +18,33 @@
 #include "sae.h"
 
 
+static int sae_suitable_group(int group)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	/* Allow all groups for testing purposes in non-production builds. */
+	return 1;
+#else /* CONFIG_TESTING_OPTIONS */
+	/* Enforce REVmd rules on which SAE groups are suitable for production
+	 * purposes: FFC groups whose prime is >= 3072 bits and ECC groups
+	 * defined over a prime field whose prime is >= 256 bits. Furthermore,
+	 * ECC groups defined over a characteristic 2 finite field and ECC
+	 * groups with a co-factor greater than 1 are not suitable. */
+	return group == 19 || group == 20 || group == 21 ||
+		group == 28 || group == 29 || group == 30 ||
+		group == 15 || group == 16 || group == 17 || group == 18;
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
 int sae_set_group(struct sae_data *sae, int group)
 {
 	struct sae_temporary_data *tmp;
 
+	if (!sae_suitable_group(group)) {
+		wpa_printf(MSG_DEBUG, "SAE: Reject unsuitable group %d", group);
+		return -1;
+	}
+
 	sae_clear_data(sae);
 	tmp = sae->tmp = os_zalloc(sizeof(*tmp));
 	if (tmp == NULL)
@@ -208,12 +232,14 @@
 
 static int is_quadratic_residue_blind(struct sae_data *sae,
 				      const u8 *prime, size_t bits,
-				      const struct crypto_bignum *qr,
-				      const struct crypto_bignum *qnr,
+				      const u8 *qr, const u8 *qnr,
 				      const struct crypto_bignum *y_sqr)
 {
-	struct crypto_bignum *r, *num;
+	struct crypto_bignum *r, *num, *qr_or_qnr = NULL;
 	int r_odd, check, res = -1;
+	u8 qr_or_qnr_bin[SAE_MAX_ECC_PRIME_LEN];
+	size_t prime_len = sae->tmp->prime_len;
+	unsigned int mask;
 
 	/*
 	 * Use the blinding technique to mask y_sqr while determining
@@ -224,7 +250,7 @@
 	 * r = a random number between 1 and p-1, inclusive
 	 * num = (v * r * r) modulo p
 	 */
-	r = get_rand_1_to_p_1(prime, sae->tmp->prime_len, bits, &r_odd);
+	r = get_rand_1_to_p_1(prime, prime_len, bits, &r_odd);
 	if (!r)
 		return -1;
 
@@ -234,50 +260,51 @@
 	    crypto_bignum_mulmod(num, r, sae->tmp->prime, num) < 0)
 		goto fail;
 
-	if (r_odd) {
-		/*
-		 * num = (num * qr) module p
-		 * LGR(num, p) = 1 ==> quadratic residue
-		 */
-		if (crypto_bignum_mulmod(num, qr, sae->tmp->prime, num) < 0)
-			goto fail;
-		check = 1;
-	} else {
-		/*
-		 * num = (num * qnr) module p
-		 * LGR(num, p) = -1 ==> quadratic residue
-		 */
-		if (crypto_bignum_mulmod(num, qnr, sae->tmp->prime, num) < 0)
-			goto fail;
-		check = -1;
-	}
+	/*
+	 * Need to minimize differences in handling different cases, so try to
+	 * avoid branches and timing differences.
+	 *
+	 * If r_odd:
+	 * num = (num * qr) module p
+	 * LGR(num, p) = 1 ==> quadratic residue
+	 * else:
+	 * num = (num * qnr) module p
+	 * LGR(num, p) = -1 ==> quadratic residue
+	 */
+	mask = const_time_is_zero(r_odd);
+	const_time_select_bin(mask, qnr, qr, prime_len, qr_or_qnr_bin);
+	qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, prime_len);
+	if (!qr_or_qnr ||
+	    crypto_bignum_mulmod(num, qr_or_qnr, sae->tmp->prime, num) < 0)
+		goto fail;
+	/* r_odd is 0 or 1; branchless version of check = r_odd ? 1 : -1, */
+	check = const_time_select_int(mask, -1, 1);
 
 	res = crypto_bignum_legendre(num, sae->tmp->prime);
 	if (res == -2) {
 		res = -1;
 		goto fail;
 	}
-	res = res == check;
+	/* branchless version of res = res == check
+	 * (res is -1, 0, or 1; check is -1 or 1) */
+	mask = const_time_eq(res, check);
+	res = const_time_select_int(mask, 1, 0);
 fail:
 	crypto_bignum_deinit(num, 1);
 	crypto_bignum_deinit(r, 1);
+	crypto_bignum_deinit(qr_or_qnr, 1);
 	return res;
 }
 
 
 static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
-				 const u8 *prime,
-				 const struct crypto_bignum *qr,
-				 const struct crypto_bignum *qnr,
-				 struct crypto_bignum **ret_x_cand)
+				 const u8 *prime, const u8 *qr, const u8 *qnr,
+				 u8 *pwd_value)
 {
-	u8 pwd_value[SAE_MAX_ECC_PRIME_LEN];
 	struct crypto_bignum *y_sqr, *x_cand;
 	int res;
 	size_t bits;
 
-	*ret_x_cand = NULL;
-
 	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
 
 	/* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
@@ -286,7 +313,7 @@
 			    prime, sae->tmp->prime_len, pwd_value, bits) < 0)
 		return -1;
 	if (bits % 8)
-		buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8);
+		buf_shift_right(pwd_value, sae->tmp->prime_len, 8 - bits % 8);
 	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
 			pwd_value, sae->tmp->prime_len);
 
@@ -297,31 +324,27 @@
 	if (!x_cand)
 		return -1;
 	y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand);
-	if (!y_sqr) {
-		crypto_bignum_deinit(x_cand, 1);
+	crypto_bignum_deinit(x_cand, 1);
+	if (!y_sqr)
 		return -1;
-	}
 
 	res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr);
 	crypto_bignum_deinit(y_sqr, 1);
-	if (res <= 0) {
-		crypto_bignum_deinit(x_cand, 1);
-		return res;
-	}
-
-	*ret_x_cand = x_cand;
-	return 1;
+	return res;
 }
 
 
+/* Returns -1 on fatal failure, 0 if PWE cannot be derived from the provided
+ * pwd-seed, or 1 if a valid PWE was derived from pwd-seed. */
 static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
 				 struct crypto_bignum *pwe)
 {
 	u8 pwd_value[SAE_MAX_PRIME_LEN];
 	size_t bits = sae->tmp->prime_len * 8;
 	u8 exp[1];
-	struct crypto_bignum *a, *b;
-	int res;
+	struct crypto_bignum *a, *b = NULL;
+	int res, is_val;
+	u8 pwd_value_valid;
 
 	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
 
@@ -333,16 +356,29 @@
 	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value,
 			sae->tmp->prime_len);
 
-	if (os_memcmp(pwd_value, sae->tmp->dh->prime, sae->tmp->prime_len) >= 0)
-	{
-		wpa_printf(MSG_DEBUG, "SAE: pwd-value >= p");
-		return 0;
-	}
+	/* Check whether pwd-value < p */
+	res = const_time_memcmp(pwd_value, sae->tmp->dh->prime,
+				sae->tmp->prime_len);
+	/* pwd-value >= p is invalid, so res is < 0 for the valid cases and
+	 * the negative sign can be used to fill the mask for constant time
+	 * selection */
+	pwd_value_valid = const_time_fill_msb(res);
+
+	/* If pwd-value >= p, force pwd-value to be < p and perform the
+	 * calculations anyway to hide timing difference. The derived PWE will
+	 * be ignored in that case. */
+	pwd_value[0] = const_time_select_u8(pwd_value_valid, pwd_value[0], 0);
 
 	/* PWE = pwd-value^((p-1)/r) modulo p */
 
+	res = -1;
 	a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
+	if (!a)
+		goto fail;
 
+	/* This is an optimization based on the used group that does not depend
+	 * on the password in any way, so it is fine to use separate branches
+	 * for this step without constant time operations. */
 	if (sae->tmp->dh->safe_prime) {
 		/*
 		 * r = (p-1)/2 for the group used here, so this becomes:
@@ -356,33 +392,34 @@
 		b = crypto_bignum_init_set(exp, sizeof(exp));
 		if (b == NULL ||
 		    crypto_bignum_sub(sae->tmp->prime, b, b) < 0 ||
-		    crypto_bignum_div(b, sae->tmp->order, b) < 0) {
-			crypto_bignum_deinit(b, 0);
-			b = NULL;
-		}
+		    crypto_bignum_div(b, sae->tmp->order, b) < 0)
+			goto fail;
 	}
 
-	if (a == NULL || b == NULL)
-		res = -1;
-	else
-		res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
+	if (!b)
+		goto fail;
 
-	crypto_bignum_deinit(a, 0);
-	crypto_bignum_deinit(b, 0);
+	res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
+	if (res < 0)
+		goto fail;
 
-	if (res < 0) {
-		wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE");
-		return -1;
-	}
+	/* There were no fatal errors in calculations, so determine the return
+	 * value using constant time operations. We get here for number of
+	 * invalid cases which are cleared here after having performed all the
+	 * computation. PWE is valid if pwd-value was less than prime and
+	 * PWE > 1. Start with pwd-value check first and then use constant time
+	 * operations to clear res to 0 if PWE is 0 or 1.
+	 */
+	res = const_time_select_u8(pwd_value_valid, 1, 0);
+	is_val = crypto_bignum_is_zero(pwe);
+	res = const_time_select_u8(const_time_is_zero(is_val), res, 0);
+	is_val = crypto_bignum_is_one(pwe);
+	res = const_time_select_u8(const_time_is_zero(is_val), res, 0);
 
-	/* if (PWE > 1) --> found */
-	if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) {
-		wpa_printf(MSG_DEBUG, "SAE: PWE <= 1");
-		return 0;
-	}
-
-	wpa_printf(MSG_DEBUG, "SAE: PWE found");
-	return 1;
+fail:
+	crypto_bignum_deinit(a, 1);
+	crypto_bignum_deinit(b, 1);
+	return res;
 }
 
 
@@ -431,25 +468,32 @@
 	const u8 *addr[3];
 	size_t len[3];
 	size_t num_elem;
-	u8 dummy_password[32];
-	size_t dummy_password_len;
+	u8 *dummy_password, *tmp_password;
 	int pwd_seed_odd = 0;
 	u8 prime[SAE_MAX_ECC_PRIME_LEN];
 	size_t prime_len;
-	struct crypto_bignum *x = NULL, *qr, *qnr;
+	struct crypto_bignum *x = NULL, *qr = NULL, *qnr = NULL;
+	u8 x_bin[SAE_MAX_ECC_PRIME_LEN];
+	u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN];
+	u8 qr_bin[SAE_MAX_ECC_PRIME_LEN];
+	u8 qnr_bin[SAE_MAX_ECC_PRIME_LEN];
 	size_t bits;
-	int res;
+	int res = -1;
+	u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
+		       * mask */
 
-	dummy_password_len = password_len;
-	if (dummy_password_len > sizeof(dummy_password))
-		dummy_password_len = sizeof(dummy_password);
-	if (random_get_bytes(dummy_password, dummy_password_len) < 0)
-		return -1;
+	os_memset(x_bin, 0, sizeof(x_bin));
+
+	dummy_password = os_malloc(password_len);
+	tmp_password = os_malloc(password_len);
+	if (!dummy_password || !tmp_password ||
+	    random_get_bytes(dummy_password, password_len) < 0)
+		goto fail;
 
 	prime_len = sae->tmp->prime_len;
 	if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
 				 prime_len) < 0)
-		return -1;
+		goto fail;
 	bits = crypto_ec_prime_len_bits(sae->tmp->ec);
 
 	/*
@@ -457,8 +501,10 @@
 	 * (qnr) modulo p for blinding purposes during the loop.
 	 */
 	if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits,
-			      &qr, &qnr) < 0)
-		return -1;
+			      &qr, &qnr) < 0 ||
+	    crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin), prime_len) < 0 ||
+	    crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin), prime_len) < 0)
+		goto fail;
 
 	wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
 			      password, password_len);
@@ -474,7 +520,7 @@
 	 */
 	sae_pwd_seed_key(addr1, addr2, addrs);
 
-	addr[0] = password;
+	addr[0] = tmp_password;
 	len[0] = password_len;
 	num_elem = 1;
 	if (identifier) {
@@ -491,9 +537,8 @@
 	 * attacks that attempt to determine the number of iterations required
 	 * in the loop.
 	 */
-	for (counter = 1; counter <= k || !x; counter++) {
+	for (counter = 1; counter <= k || !found; counter++) {
 		u8 pwd_seed[SHA256_MAC_LEN];
-		struct crypto_bignum *x_cand;
 
 		if (counter > 200) {
 			/* This should not happen in practice */
@@ -501,40 +546,49 @@
 			break;
 		}
 
-		wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
+		wpa_printf(MSG_DEBUG, "SAE: counter = %03u", counter);
+		const_time_select_bin(found, dummy_password, password,
+				      password_len, tmp_password);
 		if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
 				       addr, len, pwd_seed) < 0)
 			break;
 
 		res = sae_test_pwd_seed_ecc(sae, pwd_seed,
-					    prime, qr, qnr, &x_cand);
+					    prime, qr_bin, qnr_bin, x_cand_bin);
+		const_time_select_bin(found, x_bin, x_cand_bin, prime_len,
+				      x_bin);
+		pwd_seed_odd = const_time_select_u8(
+			found, pwd_seed_odd,
+			pwd_seed[SHA256_MAC_LEN - 1] & 0x01);
+		os_memset(pwd_seed, 0, sizeof(pwd_seed));
 		if (res < 0)
 			goto fail;
-		if (res > 0 && !x) {
-			wpa_printf(MSG_DEBUG,
-				   "SAE: Selected pwd-seed with counter %u",
-				   counter);
-			x = x_cand;
-			pwd_seed_odd = pwd_seed[SHA256_MAC_LEN - 1] & 0x01;
-			os_memset(pwd_seed, 0, sizeof(pwd_seed));
+		/* Need to minimize differences in handling res == 0 and 1 here
+		 * to avoid differences in timing and instruction cache access,
+		 * so use const_time_select_*() to make local copies of the
+		 * values based on whether this loop iteration was the one that
+		 * found the pwd-seed/x. */
 
-			/*
-			 * Use a dummy password for the following rounds, if
-			 * any.
-			 */
-			addr[0] = dummy_password;
-			len[0] = dummy_password_len;
-		} else if (res > 0) {
-			crypto_bignum_deinit(x_cand, 1);
-		}
+		/* found is 0 or 0xff here and res is 0 or 1. Bitwise OR of them
+		 * (with res converted to 0/0xff) handles this in constant time.
+		 */
+		found |= res * 0xff;
+		wpa_printf(MSG_DEBUG, "SAE: pwd-seed result %d found=0x%02x",
+			   res, found);
 	}
 
-	if (!x) {
+	if (!found) {
 		wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
 		res = -1;
 		goto fail;
 	}
 
+	x = crypto_bignum_init_set(x_bin, prime_len);
+	if (!x) {
+		res = -1;
+		goto fail;
+	}
+
 	if (!sae->tmp->pwe_ecc)
 		sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec);
 	if (!sae->tmp->pwe_ecc)
@@ -543,7 +597,6 @@
 		res = crypto_ec_point_solve_y_coord(sae->tmp->ec,
 						    sae->tmp->pwe_ecc, x,
 						    pwd_seed_odd);
-	crypto_bignum_deinit(x, 1);
 	if (res < 0) {
 		/*
 		 * This should not happen since we already checked that there
@@ -555,27 +608,48 @@
 fail:
 	crypto_bignum_deinit(qr, 0);
 	crypto_bignum_deinit(qnr, 0);
+	os_free(dummy_password);
+	bin_clear_free(tmp_password, password_len);
+	crypto_bignum_deinit(x, 1);
+	os_memset(x_bin, 0, sizeof(x_bin));
+	os_memset(x_cand_bin, 0, sizeof(x_cand_bin));
 
 	return res;
 }
 
 
+static int sae_modp_group_require_masking(int group)
+{
+	/* Groups for which pwd-value is likely to be >= p frequently */
+	return group == 22 || group == 23 || group == 24;
+}
+
+
 static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
 			      const u8 *addr2, const u8 *password,
 			      size_t password_len, const char *identifier)
 {
-	u8 counter;
+	u8 counter, k, sel_counter = 0;
 	u8 addrs[2 * ETH_ALEN];
 	const u8 *addr[3];
 	size_t len[3];
 	size_t num_elem;
-	int found = 0;
+	u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
+		       * mask */
+	u8 mask;
+	struct crypto_bignum *pwe;
+	size_t prime_len = sae->tmp->prime_len * 8;
+	u8 *pwe_buf;
 
-	if (sae->tmp->pwe_ffc == NULL) {
-		sae->tmp->pwe_ffc = crypto_bignum_init();
-		if (sae->tmp->pwe_ffc == NULL)
-			return -1;
-	}
+	crypto_bignum_deinit(sae->tmp->pwe_ffc, 1);
+	sae->tmp->pwe_ffc = NULL;
+
+	/* Allocate a buffer to maintain selected and candidate PWE for constant
+	 * time selection. */
+	pwe_buf = os_zalloc(prime_len * 2);
+	pwe = crypto_bignum_init();
+	if (!pwe_buf || !pwe)
+		goto fail;
 
 	wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
 			      password, password_len);
@@ -599,7 +673,9 @@
 	len[num_elem] = sizeof(counter);
 	num_elem++;
 
-	for (counter = 1; !found; counter++) {
+	k = sae_modp_group_require_masking(sae->group) ? 40 : 1;
+
+	for (counter = 1; counter <= k || !found; counter++) {
 		u8 pwd_seed[SHA256_MAC_LEN];
 		int res;
 
@@ -609,20 +685,37 @@
 			break;
 		}
 
-		wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
+		wpa_printf(MSG_DEBUG, "SAE: counter = %02u", counter);
 		if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
 				       addr, len, pwd_seed) < 0)
 			break;
-		res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc);
+		res = sae_test_pwd_seed_ffc(sae, pwd_seed, pwe);
+		/* res is -1 for fatal failure, 0 if a valid PWE was not found,
+		 * or 1 if a valid PWE was found. */
 		if (res < 0)
 			break;
-		if (res > 0) {
-			wpa_printf(MSG_DEBUG, "SAE: Use this PWE");
-			found = 1;
-		}
+		/* Store the candidate PWE into the second half of pwe_buf and
+		 * the selected PWE in the beginning of pwe_buf using constant
+		 * time selection. */
+		if (crypto_bignum_to_bin(pwe, pwe_buf + prime_len, prime_len,
+					 prime_len) < 0)
+			break;
+		const_time_select_bin(found, pwe_buf, pwe_buf + prime_len,
+				      prime_len, pwe_buf);
+		sel_counter = const_time_select_u8(found, sel_counter, counter);
+		mask = const_time_eq_u8(res, 1);
+		found = const_time_select_u8(found, found, mask);
 	}
 
-	return found ? 0 : -1;
+	if (!found)
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "SAE: Use PWE from counter = %02u", sel_counter);
+	sae->tmp->pwe_ffc = crypto_bignum_init_set(pwe_buf, prime_len);
+fail:
+	crypto_bignum_deinit(pwe, 1);
+	bin_clear_free(pwe_buf, prime_len * 2);
+	return sae->tmp->pwe_ffc ? 0 : -1;
 }
 
 
@@ -1394,23 +1487,31 @@
 
 	wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data));
 
-	if (sae->tmp == NULL) {
+	if (!sae->tmp || !sae->peer_commit_scalar ||
+	    !sae->tmp->own_commit_scalar) {
 		wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available");
 		return -1;
 	}
 
-	if (sae->tmp->ec)
+	if (sae->tmp->ec) {
+		if (!sae->tmp->peer_commit_element_ecc ||
+		    !sae->tmp->own_commit_element_ecc)
+			return -1;
 		sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar,
 				   sae->tmp->peer_commit_element_ecc,
 				   sae->tmp->own_commit_scalar,
 				   sae->tmp->own_commit_element_ecc,
 				   verifier);
-	else
+	} else {
+		if (!sae->tmp->peer_commit_element_ffc ||
+		    !sae->tmp->own_commit_element_ffc)
+			return -1;
 		sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar,
 				   sae->tmp->peer_commit_element_ffc,
 				   sae->tmp->own_commit_scalar,
 				   sae->tmp->own_commit_element_ffc,
 				   verifier);
+	}
 
 	if (os_memcmp_const(verifier, data + 2, SHA256_MAC_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch");
diff --git a/src/common/sae.h b/src/common/sae.h
index 3fbcb58..3eb6e32 100644
--- a/src/common/sae.h
+++ b/src/common/sae.h
@@ -40,6 +40,8 @@
 	struct crypto_bignum *order_buf;
 	struct wpabuf *anti_clogging_token;
 	char *pw_id;
+	int vlan_id;
+	u8 bssid[ETH_ALEN];
 };
 
 enum sae_state {
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index b47f632..ed2d1c2 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -340,14 +340,21 @@
  * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
  * PTK = PRF-X(PMK, "Pairwise key expansion",
  *             Min(AA, SA) || Max(AA, SA) ||
- *             Min(ANonce, SNonce) || Max(ANonce, SNonce))
+ *             Min(ANonce, SNonce) || Max(ANonce, SNonce)
+ *             [ || Z.x ])
+ *
+ * The optional Z.x component is used only with DPP and that part is not defined
+ * in IEEE 802.11.
  */
 int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
 		   const u8 *addr1, const u8 *addr2,
 		   const u8 *nonce1, const u8 *nonce2,
-		   struct wpa_ptk *ptk, int akmp, int cipher)
+		   struct wpa_ptk *ptk, int akmp, int cipher,
+		   const u8 *z, size_t z_len)
 {
-	u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN];
+#define MAX_Z_LEN 66 /* with NIST P-521 */
+	u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN + MAX_Z_LEN];
+	size_t data_len = 2 * ETH_ALEN + 2 * WPA_NONCE_LEN;
 	u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
 	size_t ptk_len;
 
@@ -356,6 +363,9 @@
 		return -1;
 	}
 
+	if (z_len > MAX_Z_LEN)
+		return -1;
+
 	if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) {
 		os_memcpy(data, addr1, ETH_ALEN);
 		os_memcpy(data + ETH_ALEN, addr2, ETH_ALEN);
@@ -374,6 +384,11 @@
 			  WPA_NONCE_LEN);
 	}
 
+	if (z && z_len) {
+		os_memcpy(data + 2 * ETH_ALEN + 2 * WPA_NONCE_LEN, z, z_len);
+		data_len += z_len;
+	}
+
 	ptk->kck_len = wpa_kck_len(akmp, pmk_len);
 	ptk->kek_len = wpa_kek_len(akmp, pmk_len);
 	ptk->tk_len = wpa_cipher_key_len(cipher);
@@ -388,7 +403,7 @@
 	if (wpa_key_mgmt_sha384(akmp)) {
 #if defined(CONFIG_SUITEB192) || defined(CONFIG_FILS)
 		wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)");
-		if (sha384_prf(pmk, pmk_len, label, data, sizeof(data),
+		if (sha384_prf(pmk, pmk_len, label, data, data_len,
 			       tmp, ptk_len) < 0)
 			return -1;
 #else /* CONFIG_SUITEB192 || CONFIG_FILS */
@@ -397,7 +412,7 @@
 	} else if (wpa_key_mgmt_sha256(akmp) || akmp == WPA_KEY_MGMT_OWE) {
 #if defined(CONFIG_IEEE80211W) || defined(CONFIG_SAE) || defined(CONFIG_FILS)
 		wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)");
-		if (sha256_prf(pmk, pmk_len, label, data, sizeof(data),
+		if (sha256_prf(pmk, pmk_len, label, data, data_len,
 			       tmp, ptk_len) < 0)
 			return -1;
 #else /* CONFIG_IEEE80211W or CONFIG_SAE or CONFIG_FILS */
@@ -406,17 +421,17 @@
 #ifdef CONFIG_DPP
 	} else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 32) {
 		wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)");
-		if (sha256_prf(pmk, pmk_len, label, data, sizeof(data),
+		if (sha256_prf(pmk, pmk_len, label, data, data_len,
 			       tmp, ptk_len) < 0)
 			return -1;
 	} else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 48) {
 		wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)");
-		if (sha384_prf(pmk, pmk_len, label, data, sizeof(data),
+		if (sha384_prf(pmk, pmk_len, label, data, data_len,
 			       tmp, ptk_len) < 0)
 			return -1;
 	} else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 64) {
 		wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA512)");
-		if (sha512_prf(pmk, pmk_len, label, data, sizeof(data),
+		if (sha512_prf(pmk, pmk_len, label, data, data_len,
 			       tmp, ptk_len) < 0)
 			return -1;
 	} else if (akmp == WPA_KEY_MGMT_DPP) {
@@ -426,7 +441,7 @@
 #endif /* CONFIG_DPP */
 	} else {
 		wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA1)");
-		if (sha1_prf(pmk, pmk_len, label, data, sizeof(data), tmp,
+		if (sha1_prf(pmk, pmk_len, label, data, data_len, tmp,
 			     ptk_len) < 0)
 			return -1;
 	}
@@ -435,6 +450,8 @@
 		   MAC2STR(addr1), MAC2STR(addr2));
 	wpa_hexdump(MSG_DEBUG, "WPA: Nonce1", nonce1, WPA_NONCE_LEN);
 	wpa_hexdump(MSG_DEBUG, "WPA: Nonce2", nonce2, WPA_NONCE_LEN);
+	if (z && z_len)
+		wpa_hexdump_key(MSG_DEBUG, "WPA: Z.x", z, z_len);
 	wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len);
 	wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", tmp, ptk_len);
 
@@ -451,6 +468,7 @@
 	ptk->kck2_len = 0;
 
 	os_memset(tmp, 0, sizeof(tmp));
+	os_memset(data, 0, data_len);
 	return 0;
 }
 
@@ -1209,6 +1227,7 @@
 		left = rsn_ie_len - 6;
 
 		data->group_cipher = WPA_CIPHER_GTK_NOT_USED;
+		data->has_group = 1;
 		data->key_mgmt = WPA_KEY_MGMT_OSEN;
 		data->proto = WPA_PROTO_OSEN;
 	} else {
@@ -1230,6 +1249,7 @@
 
 	if (left >= RSN_SELECTOR_LEN) {
 		data->group_cipher = rsn_selector_to_bitfield(pos);
+		data->has_group = 1;
 		if (!wpa_cipher_valid_group(data->group_cipher)) {
 			wpa_printf(MSG_DEBUG,
 				   "%s: invalid group cipher 0x%x (%08x)",
@@ -1255,6 +1275,8 @@
 				   "count %u left %u", __func__, count, left);
 			return -4;
 		}
+		if (count)
+			data->has_pairwise = 1;
 		for (i = 0; i < count; i++) {
 			data->pairwise_cipher |= rsn_selector_to_bitfield(pos);
 			pos += RSN_SELECTOR_LEN;
@@ -1473,6 +1495,15 @@
 }
 
 
+int wpa_default_rsn_cipher(int freq)
+{
+	if (freq > 56160)
+		return WPA_CIPHER_GCMP; /* DMG */
+
+	return WPA_CIPHER_CCMP;
+}
+
+
 #ifdef CONFIG_IEEE80211R
 
 /**
@@ -1760,7 +1791,7 @@
 	os_memcpy(ptk->tk, tmp + offset, ptk->tk_len);
 	offset += ptk->tk_len;
 	os_memcpy(ptk->kck2, tmp + offset, ptk->kck2_len);
-	offset = ptk->kck2_len;
+	offset += ptk->kck2_len;
 	os_memcpy(ptk->kek2, tmp + offset, ptk->kek2_len);
 
 	wpa_hexdump_key(MSG_DEBUG, "FT: KCK", ptk->kck, ptk->kck_len);
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 37b5834..e83d688 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -347,7 +347,8 @@
 int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
 		   const u8 *addr1, const u8 *addr2,
 		   const u8 *nonce1, const u8 *nonce2,
-		   struct wpa_ptk *ptk, int akmp, int cipher);
+		   struct wpa_ptk *ptk, int akmp, int cipher,
+		   const u8 *z, size_t z_len);
 int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len,
 		     const u8 *snonce, const u8 *anonce, const u8 *dh_ss,
 		     size_t dh_ss_len, u8 *pmk, size_t *pmk_len);
@@ -392,7 +393,9 @@
 struct wpa_ie_data {
 	int proto;
 	int pairwise_cipher;
+	int has_pairwise;
 	int group_cipher;
+	int has_group;
 	int key_mgmt;
 	int capabilities;
 	size_t num_pmkid;
@@ -405,6 +408,7 @@
 			 struct wpa_ie_data *data);
 int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
 			 struct wpa_ie_data *data);
+int wpa_default_rsn_cipher(int freq);
 
 void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
 	       u8 *pmkid, int akmp);
diff --git a/src/crypto/aes-internal-enc.c b/src/crypto/aes-internal-enc.c
index 9fdb4f3..baeffca 100644
--- a/src/crypto/aes-internal-enc.c
+++ b/src/crypto/aes-internal-enc.c
@@ -99,6 +99,10 @@
 {
 	u32 *rk;
 	int res;
+
+	if (TEST_FAIL())
+		return NULL;
+
 	rk = os_malloc(AES_PRIV_SIZE);
 	if (rk == NULL)
 		return NULL;
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 507b7ca..a28ddbd 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -420,6 +420,7 @@
 int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
 		   u8 *pubkey);
 int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+			    const u8 *order, size_t order_len,
 			    const u8 *privkey, size_t privkey_len,
 			    const u8 *pubkey, size_t pubkey_len,
 			    u8 *secret, size_t *len);
diff --git a/src/crypto/crypto_gnutls.c b/src/crypto/crypto_gnutls.c
index 7a797b5..4ef1146 100644
--- a/src/crypto/crypto_gnutls.c
+++ b/src/crypto/crypto_gnutls.c
@@ -310,12 +310,51 @@
 
 
 int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+			    const u8 *order, size_t order_len,
 			    const u8 *privkey, size_t privkey_len,
 			    const u8 *pubkey, size_t pubkey_len,
 			    u8 *secret, size_t *len)
 {
-	return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
-			      prime, prime_len, secret, len);
+	gcry_mpi_t pub = NULL;
+	int res = -1;
+
+	if (pubkey_len > prime_len ||
+	    (pubkey_len == prime_len &&
+	     os_memcmp(pubkey, prime, prime_len) >= 0))
+		return -1;
+
+	if (gcry_mpi_scan(&pub, GCRYMPI_FMT_USG, pubkey, pubkey_len, NULL) !=
+	    GPG_ERR_NO_ERROR ||
+	    gcry_mpi_cmp_ui(pub, 1) <= 0)
+		goto fail;
+
+	if (order) {
+		gcry_mpi_t p = NULL, q = NULL, tmp;
+		int failed;
+
+		/* verify: pubkey^q == 1 mod p */
+		tmp = gcry_mpi_new(prime_len * 8);
+		failed = !tmp ||
+			gcry_mpi_scan(&p, GCRYMPI_FMT_USG, prime, prime_len,
+				      NULL) != GPG_ERR_NO_ERROR ||
+			gcry_mpi_scan(&q, GCRYMPI_FMT_USG, order, order_len,
+				      NULL) != GPG_ERR_NO_ERROR;
+		if (!failed) {
+			gcry_mpi_powm(tmp, pub, q, p);
+			failed = gcry_mpi_cmp_ui(tmp, 1) != 0;
+		}
+		gcry_mpi_release(p);
+		gcry_mpi_release(q);
+		gcry_mpi_release(tmp);
+		if (failed)
+			goto fail;
+	}
+
+	res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+			     prime, prime_len, secret, len);
+fail:
+	gcry_mpi_release(pub);
+	return res;
 }
 
 
diff --git a/src/crypto/crypto_internal-modexp.c b/src/crypto/crypto_internal-modexp.c
index 92581ac..6819f1a 100644
--- a/src/crypto/crypto_internal-modexp.c
+++ b/src/crypto/crypto_internal-modexp.c
@@ -40,12 +40,49 @@
 
 
 int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+			    const u8 *order, size_t order_len,
 			    const u8 *privkey, size_t privkey_len,
 			    const u8 *pubkey, size_t pubkey_len,
 			    u8 *secret, size_t *len)
 {
-	return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
-			      prime, prime_len, secret, len);
+	struct bignum *pub;
+	int res = -1;
+
+	if (pubkey_len > prime_len ||
+	    (pubkey_len == prime_len &&
+	     os_memcmp(pubkey, prime, prime_len) >= 0))
+		return -1;
+
+	pub = bignum_init();
+	if (!pub || bignum_set_unsigned_bin(pub, pubkey, pubkey_len) < 0 ||
+	    bignum_cmp_d(pub, 1) <= 0)
+		goto fail;
+
+	if (order) {
+		struct bignum *p, *q, *tmp;
+		int failed;
+
+		/* verify: pubkey^q == 1 mod p */
+		p = bignum_init();
+		q = bignum_init();
+		tmp = bignum_init();
+		failed = !p || !q || !tmp ||
+			bignum_set_unsigned_bin(p, prime, prime_len) < 0 ||
+			bignum_set_unsigned_bin(q, order, order_len) < 0 ||
+			bignum_exptmod(pub, q, p, tmp) < 0 ||
+			bignum_cmp_d(tmp, 1) != 0;
+		bignum_deinit(p);
+		bignum_deinit(q);
+		bignum_deinit(tmp);
+		if (failed)
+			goto fail;
+	}
+
+	res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+			     prime, prime_len, secret, len);
+fail:
+	bignum_deinit(pub);
+	return res;
 }
 
 
diff --git a/src/crypto/crypto_libtomcrypt.c b/src/crypto/crypto_libtomcrypt.c
index 259f995..980fa42 100644
--- a/src/crypto/crypto_libtomcrypt.c
+++ b/src/crypto/crypto_libtomcrypt.c
@@ -721,10 +721,12 @@
 
 
 int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+			    const u8 *order, size_t order_len,
 			    const u8 *privkey, size_t privkey_len,
 			    const u8 *pubkey, size_t pubkey_len,
 			    u8 *secret, size_t *len)
 {
+	/* TODO: check pubkey */
 	return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
 			      prime, prime_len, secret, len);
 }
diff --git a/src/crypto/crypto_nettle.c b/src/crypto/crypto_nettle.c
index 4e31bc8..f85d365 100644
--- a/src/crypto/crypto_nettle.c
+++ b/src/crypto/crypto_nettle.c
@@ -331,12 +331,44 @@
 
 
 int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+			    const u8 *order, size_t order_len,
 			    const u8 *privkey, size_t privkey_len,
 			    const u8 *pubkey, size_t pubkey_len,
 			    u8 *secret, size_t *len)
 {
-	return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
-			      prime, prime_len, secret, len);
+	mpz_t pub;
+	int res = -1;
+
+	if (pubkey_len > prime_len ||
+	    (pubkey_len == prime_len &&
+	     os_memcmp(pubkey, prime, prime_len) >= 0))
+		return -1;
+
+	mpz_init(pub);
+	mpz_import(pub, pubkey_len, 1, 1, 1, 0, pubkey);
+	if (mpz_cmp_d(pub, 1) <= 0)
+		goto fail;
+
+	if (order) {
+		mpz_t p, q, tmp;
+		int failed;
+
+		/* verify: pubkey^q == 1 mod p */
+		mpz_inits(p, q, tmp, NULL);
+		mpz_import(p, prime_len, 1, 1, 1, 0, prime);
+		mpz_import(q, order_len, 1, 1, 1, 0, order);
+		mpz_powm(tmp, pub, q, p);
+		failed = mpz_cmp_d(tmp, 1) != 0;
+		mpz_clears(p, q, tmp, NULL);
+		if (failed)
+			goto fail;
+	}
+
+	res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+			     prime, prime_len, secret, len);
+fail:
+	mpz_clear(pub);
+	return res;
 }
 
 
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index f89053a..0f52101 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -24,6 +24,7 @@
 #endif /* CONFIG_ECC */
 
 #include "common.h"
+#include "utils/const_time.h"
 #include "wpabuf.h"
 #include "dh_group5.h"
 #include "sha1.h"
@@ -111,6 +112,31 @@
 #endif
 }
 
+
+static BIGNUM * get_group5_order(void)
+{
+	static const unsigned char RFC3526_ORDER_1536[] = {
+		0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE4,0x87,0xED,0x51,
+		0x10,0xB4,0x61,0x1A,0x62,0x63,0x31,0x45,0xC0,0x6E,0x0E,0x68,
+		0x94,0x81,0x27,0x04,0x45,0x33,0xE6,0x3A,0x01,0x05,0xDF,0x53,
+		0x1D,0x89,0xCD,0x91,0x28,0xA5,0x04,0x3C,0xC7,0x1A,0x02,0x6E,
+		0xF7,0xCA,0x8C,0xD9,0xE6,0x9D,0x21,0x8D,0x98,0x15,0x85,0x36,
+		0xF9,0x2F,0x8A,0x1B,0xA7,0xF0,0x9A,0xB6,0xB6,0xA8,0xE1,0x22,
+		0xF2,0x42,0xDA,0xBB,0x31,0x2F,0x3F,0x63,0x7A,0x26,0x21,0x74,
+		0xD3,0x1B,0xF6,0xB5,0x85,0xFF,0xAE,0x5B,0x7A,0x03,0x5B,0xF6,
+		0xF7,0x1C,0x35,0xFD,0xAD,0x44,0xCF,0xD2,0xD7,0x4F,0x92,0x08,
+		0xBE,0x25,0x8F,0xF3,0x24,0x94,0x33,0x28,0xF6,0x72,0x2D,0x9E,
+		0xE1,0x00,0x3E,0x5C,0x50,0xB1,0xDF,0x82,0xCC,0x6D,0x24,0x1B,
+		0x0E,0x2A,0xE9,0xCD,0x34,0x8B,0x1F,0xD4,0x7E,0x92,0x67,0xAF,
+		0xC1,0xB2,0xAE,0x91,0xEE,0x51,0xD6,0xCB,0x0E,0x31,0x79,0xAB,
+		0x10,0x42,0xA9,0x5D,0xCF,0x6A,0x94,0x83,0xB8,0x4B,0x4B,0x36,
+		0xB3,0x86,0x1A,0xA7,0x25,0x5E,0x4C,0x02,0x78,0xBA,0x36,0x04,
+		0x65,0x11,0xB9,0x93,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
+	};
+	return BN_bin2bn(RFC3526_ORDER_1536, sizeof(RFC3526_ORDER_1536), NULL);
+}
+
+
 #ifdef OPENSSL_NO_SHA256
 #define NO_SHA256_WRAPPER
 #endif
@@ -518,12 +544,45 @@
 
 
 int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+			    const u8 *order, size_t order_len,
 			    const u8 *privkey, size_t privkey_len,
 			    const u8 *pubkey, size_t pubkey_len,
 			    u8 *secret, size_t *len)
 {
-	return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
-			      prime, prime_len, secret, len);
+	BIGNUM *pub, *p;
+	int res = -1;
+
+	pub = BN_bin2bn(pubkey, pubkey_len, NULL);
+	p = BN_bin2bn(prime, prime_len, NULL);
+	if (!pub || !p || BN_is_zero(pub) || BN_is_one(pub) ||
+	    BN_cmp(pub, p) >= 0)
+		goto fail;
+
+	if (order) {
+		BN_CTX *ctx;
+		BIGNUM *q, *tmp;
+		int failed;
+
+		/* verify: pubkey^q == 1 mod p */
+		q = BN_bin2bn(order, order_len, NULL);
+		ctx = BN_CTX_new();
+		tmp = BN_new();
+		failed = !q || !ctx || !tmp ||
+			!BN_mod_exp(tmp, pub, q, p, ctx) ||
+			!BN_is_one(tmp);
+		BN_clear(q);
+		BN_clear(tmp);
+		BN_CTX_free(ctx);
+		if (failed)
+			goto fail;
+	}
+
+	res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+			     prime, prime_len, secret, len);
+fail:
+	BN_clear(pub);
+	BN_clear(p);
+	return res;
 }
 
 
@@ -549,7 +608,8 @@
 	    bn_result == NULL)
 		goto error;
 
-	if (BN_mod_exp(bn_result, bn_base, bn_exp, bn_modulus, ctx) != 1)
+	if (BN_mod_exp_mont_consttime(bn_result, bn_base, bn_exp, bn_modulus,
+				      ctx, NULL) != 1)
 		goto error;
 
 	*result_len = BN_bn2bin(bn_result, result);
@@ -709,6 +769,10 @@
 	if (dh->p == NULL)
 		goto err;
 
+	dh->q = get_group5_order();
+	if (!dh->q)
+		goto err;
+
 	if (DH_generate_key(dh) != 1)
 		goto err;
 
@@ -737,7 +801,7 @@
 	DH *dh;
 	struct wpabuf *pubkey = NULL, *privkey = NULL;
 	size_t publen, privlen;
-	BIGNUM *p = NULL, *g;
+	BIGNUM *p, *g, *q;
 	const BIGNUM *priv_key = NULL, *pub_key = NULL;
 
 	*priv = NULL;
@@ -750,10 +814,12 @@
 
 	g = BN_new();
 	p = get_group5_prime();
-	if (!g || BN_set_word(g, 2) != 1 || !p ||
-	    DH_set0_pqg(dh, p, NULL, g) != 1)
+	q = get_group5_order();
+	if (!g || BN_set_word(g, 2) != 1 || !p || !q ||
+	    DH_set0_pqg(dh, p, q, g) != 1)
 		goto err;
 	p = NULL;
+	q = NULL;
 	g = NULL;
 
 	if (DH_generate_key(dh) != 1)
@@ -778,6 +844,7 @@
 
 err:
 	BN_free(p);
+	BN_free(q);
 	BN_free(g);
 	wpabuf_clear_free(pubkey);
 	wpabuf_clear_free(privkey);
@@ -1295,8 +1362,9 @@
 	bnctx = BN_CTX_new();
 	if (bnctx == NULL)
 		return -1;
-	res = BN_mod_exp((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b,
-			 (const BIGNUM *) c, bnctx);
+	res = BN_mod_exp_mont_consttime((BIGNUM *) d, (const BIGNUM *) a,
+					(const BIGNUM *) b, (const BIGNUM *) c,
+					bnctx, NULL);
 	BN_CTX_free(bnctx);
 
 	return res ? 0 : -1;
@@ -1315,6 +1383,11 @@
 	bnctx = BN_CTX_new();
 	if (bnctx == NULL)
 		return -1;
+#ifdef OPENSSL_IS_BORINGSSL
+	/* TODO: use BN_mod_inverse_blinded() ? */
+#else /* OPENSSL_IS_BORINGSSL */
+	BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME);
+#endif /* OPENSSL_IS_BORINGSSL */
 	res = BN_mod_inverse((BIGNUM *) c, (const BIGNUM *) a,
 			     (const BIGNUM *) b, bnctx);
 	BN_CTX_free(bnctx);
@@ -1348,6 +1421,9 @@
 	bnctx = BN_CTX_new();
 	if (bnctx == NULL)
 		return -1;
+#ifndef OPENSSL_IS_BORINGSSL
+	BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME);
+#endif /* OPENSSL_IS_BORINGSSL */
 	res = BN_div((BIGNUM *) c, NULL, (const BIGNUM *) a,
 		     (const BIGNUM *) b, bnctx);
 	BN_CTX_free(bnctx);
@@ -1425,6 +1501,7 @@
 	BN_CTX *bnctx;
 	BIGNUM *exp = NULL, *tmp = NULL;
 	int res = -2;
+	unsigned int mask;
 
 	if (TEST_FAIL())
 		return -2;
@@ -1439,16 +1516,17 @@
 	    /* exp = (p-1) / 2 */
 	    !BN_sub(exp, (const BIGNUM *) p, BN_value_one()) ||
 	    !BN_rshift1(exp, exp) ||
-	    !BN_mod_exp(tmp, (const BIGNUM *) a, exp, (const BIGNUM *) p,
-			bnctx))
+	    !BN_mod_exp_mont_consttime(tmp, (const BIGNUM *) a, exp,
+				       (const BIGNUM *) p, bnctx, NULL))
 		goto fail;
 
-	if (BN_is_word(tmp, 1))
-		res = 1;
-	else if (BN_is_zero(tmp))
-		res = 0;
-	else
-		res = -1;
+	/* Return 1 if tmp == 1, 0 if tmp == 0, or -1 otherwise. Need to use
+	 * constant time selection to avoid branches here. */
+	res = -1;
+	mask = const_time_eq(BN_is_word(tmp, 1), 1);
+	res = const_time_select_int(mask, 1, res);
+	mask = const_time_eq(BN_is_zero(tmp), 1);
+	res = const_time_select_int(mask, 0, res);
 
 fail:
 	BN_clear_free(tmp);
diff --git a/src/crypto/crypto_wolfssl.c b/src/crypto/crypto_wolfssl.c
index b5a1e3f..10cdae6 100644
--- a/src/crypto/crypto_wolfssl.c
+++ b/src/crypto/crypto_wolfssl.c
@@ -826,6 +826,7 @@
 
 
 int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+			    const u8 *order, size_t order_len,
 			    const u8 *privkey, size_t privkey_len,
 			    const u8 *pubkey, size_t pubkey_len,
 			    u8 *secret, size_t *len)
diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c
index a9b770e..5e421b2 100644
--- a/src/crypto/dh_groups.c
+++ b/src/crypto/dh_groups.c
@@ -1249,6 +1249,7 @@
 	if (shared == NULL)
 		return NULL;
 	if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len,
+				    dh->order, dh->order_len,
 				    wpabuf_head(own_private),
 				    wpabuf_len(own_private),
 				    wpabuf_head(peer_public),
diff --git a/src/crypto/sha1-tlsprf.c b/src/crypto/sha1-tlsprf.c
index f9bc0eb..a11649a 100644
--- a/src/crypto/sha1-tlsprf.c
+++ b/src/crypto/sha1-tlsprf.c
@@ -40,9 +40,6 @@
 	const unsigned char *SHA1_addr[3];
 	size_t SHA1_len[3];
 
-	if (secret_len & 1)
-		return -1;
-
 	MD5_addr[0] = A_MD5;
 	MD5_len[0] = MD5_MAC_LEN;
 	MD5_addr[1] = (unsigned char *) label;
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index 413cccd..8bdb91f 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -42,6 +42,7 @@
 	TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9,
 	TLS_FAIL_DOMAIN_MISMATCH = 10,
 	TLS_FAIL_INSUFFICIENT_KEY_LEN = 11,
+	TLS_FAIL_DN_MISMATCH = 12,
 };
 
 
@@ -119,12 +120,19 @@
  * %NULL to allow all subjects
  * @altsubject_match: String to match in the alternative subject of the peer
  * certificate or %NULL to allow all alternative subjects
- * @suffix_match: String to suffix match in the dNSName or CN of the peer
- * certificate or %NULL to allow all domain names. This may allow subdomains an
- * wildcard certificates. Each domain name label must have a full match.
+ * @suffix_match: Semicolon deliminated string of values to suffix match against
+ * the dNSName or CN of the peer certificate or %NULL to allow all domain names.
+ * This may allow subdomains and wildcard certificates. Each domain name label
+ * must have a full case-insensitive match.
  * @domain_match: String to match in the dNSName or CN of the peer
  * certificate or %NULL to allow all domain names. This requires a full,
  * case-insensitive match.
+ *
+ * More than one match string can be provided by using semicolons to
+ * separate the strings (e.g., example.org;example.com). When multiple
+ * strings are specified, a match with any one of the values is
+ * considered a sufficient match for the certificate, i.e., the
+ * conditions are ORed together.
  * @client_cert: File or reference name for client X.509 certificate in PEM or
  * DER format
  * @client_cert_blob: client_cert as inlined data or %NULL if not used
@@ -156,6 +164,7 @@
  * @ocsp_stapling_response_multi: DER encoded file with cached OCSP stapling
  *	response list (OCSPResponseList for ocsp_multi in RFC 6961) or %NULL if
  *	ocsp_multi is not enabled
+ * @check_cert_subject: Client certificate subject name matching string
  *
  * TLS connection parameters to be configured with tls_connection_set_params()
  * and tls_global_set_params().
@@ -198,6 +207,7 @@
 	unsigned int flags;
 	const char *ocsp_stapling_response;
 	const char *ocsp_stapling_response_multi;
+	const char *check_cert_subject;
 };
 
 
@@ -367,15 +377,21 @@
  * @tls_ctx: TLS context data from tls_init()
  * @conn: Connection context data from tls_connection_init()
  * @label: Label (e.g., description of the key) for PRF
+ * @context: Optional extra upper-layer context (max len 2^16)
+ * @context_len: The length of the context value
  * @out: Buffer for output data from TLS-PRF
  * @out_len: Length of the output buffer
  * Returns: 0 on success, -1 on failure
  *
- * Exports keying material using the mechanism described in RFC 5705.
+ * Exports keying material using the mechanism described in RFC 5705. If
+ * context is %NULL, context is not provided; otherwise, context is provided
+ * (including the case of empty context with context_len == 0).
  */
 int __must_check tls_connection_export_key(void *tls_ctx,
 					   struct tls_connection *conn,
 					   const char *label,
+					   const u8 *context,
+					   size_t context_len,
 					   u8 *out, size_t out_len);
 
 /**
diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c
index 527d01e..daa01d9 100644
--- a/src/crypto/tls_gnutls.c
+++ b/src/crypto/tls_gnutls.c
@@ -739,6 +739,9 @@
 	struct tls_global *global = tls_ctx;
 	int ret;
 
+	if (params->check_cert_subject)
+		return -1; /* not yet supported */
+
 	/* Currently, global parameters are only set when running in server
 	 * mode. */
 	global->server = 1;
@@ -895,14 +898,23 @@
 
 
 int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
-			      const char *label, u8 *out, size_t out_len)
+			      const char *label, const u8 *context,
+			      size_t context_len, u8 *out, size_t out_len)
 {
 	if (conn == NULL || conn->session == NULL)
 		return -1;
 
+#if GNUTLS_VERSION_NUMBER >= 0x030404
+	return gnutls_prf_rfc5705(conn->session, os_strlen(label), label,
+				  context_len, (const char *) context,
+				  out_len, (char *) out);
+#else /* 3.4.4 */
+	if (context)
+		return -1;
 	return gnutls_prf(conn->session, os_strlen(label), label,
 			  0 /* client_random first */, 0, NULL, out_len,
 			  (char *) out);
+#endif /* 3.4.4 */
 }
 
 
@@ -1074,6 +1086,52 @@
 }
 
 
+static int tls_match_suffix_helper(gnutls_x509_crt_t cert, const char *match,
+				   int full)
+{
+	int res = -1;
+
+#if GNUTLS_VERSION_NUMBER >= 0x030300
+	if (full)
+		res = gnutls_x509_crt_check_hostname2(
+			cert, match,
+			GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS);
+#endif /* >= 3.3.0 */
+	if (res == -1)
+		res = gnutls_x509_crt_check_hostname(cert, match);
+
+	wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s --> res=%d",
+		   full ? "": "suffix ", match, res);
+	return res;
+}
+
+
+static int tls_match_suffix(gnutls_x509_crt_t cert, const char *match,
+			    int full)
+{
+	char *values, *token, *context = NULL;
+	int ret = 0;
+
+	if (!os_strchr(match, ';'))
+		return tls_match_suffix_helper(cert, match, full);
+
+	values = os_strdup(match);
+	if (!values)
+		return 0;
+
+	/* Process each match alternative separately until a match is found */
+	while ((token = str_token(values, ";", &context))) {
+		if (tls_match_suffix_helper(cert, token, full)) {
+			ret = 1;
+			break;
+		}
+	}
+
+	os_free(values);
+	return ret;
+}
+
+
 static int tls_connection_verify_peer(gnutls_session_t session)
 {
 	struct tls_connection *conn;
@@ -1269,8 +1327,7 @@
 
 		if (i == 0) {
 			if (conn->suffix_match &&
-			    !gnutls_x509_crt_check_hostname(
-				    cert, conn->suffix_match)) {
+			    !tls_match_suffix(cert, conn->suffix_match, 0)) {
 				wpa_printf(MSG_WARNING,
 					   "TLS: Domain suffix match '%s' not found",
 					   conn->suffix_match);
@@ -1286,9 +1343,7 @@
 
 #if GNUTLS_VERSION_NUMBER >= 0x030300
 			if (conn->domain_match &&
-			    !gnutls_x509_crt_check_hostname2(
-				    cert, conn->domain_match,
-				    GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS)) {
+			    !tls_match_suffix(cert, conn->domain_match, 1)) {
 				wpa_printf(MSG_WARNING,
 					   "TLS: Domain match '%s' not found",
 					   conn->domain_match);
diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c
index 57b3e63..8095b43 100644
--- a/src/crypto/tls_internal.c
+++ b/src/crypto/tls_internal.c
@@ -1,6 +1,6 @@
 /*
  * TLS interface functions and an internal TLS implementation
- * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -309,6 +309,9 @@
 	struct tls_global *global = tls_ctx;
 	struct tlsv1_credentials *cred;
 
+	if (params->check_cert_subject)
+		return -1; /* not yet supported */
+
 	/* Currently, global parameters are only set when running in server
 	 * mode. */
 	global->server = 1;
@@ -409,7 +412,8 @@
 
 
 static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
-			      const char *label, int server_random_first,
+			      const char *label, const u8 *context,
+			      size_t context_len, int server_random_first,
 			      int skip_keyblock, u8 *out, size_t out_len)
 {
 	int ret = -1, skip = 0;
@@ -428,15 +432,15 @@
 
 #ifdef CONFIG_TLS_INTERNAL_CLIENT
 	if (conn->client) {
-		ret = tlsv1_client_prf(conn->client, label,
-				       server_random_first,
+		ret = tlsv1_client_prf(conn->client, label, context,
+				       context_len, server_random_first,
 				       _out, skip + out_len);
 	}
 #endif /* CONFIG_TLS_INTERNAL_CLIENT */
 #ifdef CONFIG_TLS_INTERNAL_SERVER
 	if (conn->server) {
-		ret = tlsv1_server_prf(conn->server, label,
-				       server_random_first,
+		ret = tlsv1_server_prf(conn->server, label, context,
+				       context_len, server_random_first,
 				       _out, skip + out_len);
 	}
 #endif /* CONFIG_TLS_INTERNAL_SERVER */
@@ -449,17 +453,19 @@
 
 
 int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
-			      const char *label, u8 *out, size_t out_len)
+			      const char *label, const u8 *context,
+			      size_t context_len, u8 *out, size_t out_len)
 {
-	return tls_connection_prf(tls_ctx, conn, label, 0, 0, out, out_len);
+	return tls_connection_prf(tls_ctx, conn, label, context, context_len,
+				  0, 0, out, out_len);
 }
 
 
 int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
 				    u8 *out, size_t out_len)
 {
-	return tls_connection_prf(tls_ctx, conn, "key expansion", 1, 1, out,
-				  out_len);
+	return tls_connection_prf(tls_ctx, conn, "key expansion", NULL, 0,
+				  1, 1, out, out_len);
 }
 
 
diff --git a/src/crypto/tls_none.c b/src/crypto/tls_none.c
index 108e9aa..6d6fb0c 100644
--- a/src/crypto/tls_none.c
+++ b/src/crypto/tls_none.c
@@ -94,7 +94,8 @@
 
 
 int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
-			      const char *label, u8 *out, size_t out_len)
+			      const char *label, const u8 *context,
+			      size_t context_len, u8 *out, size_t out_len)
 {
 	return -1;
 }
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index 095c096..7fde21c 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -233,6 +233,7 @@
 	char *ca_cert;
 	unsigned int crl_reload_interval;
 	struct os_reltime crl_last_reload;
+	char *check_cert_subject;
 };
 
 struct tls_connection {
@@ -246,6 +247,7 @@
 	EVP_PKEY *private_key; /* the private key if using engine */
 #endif /* OPENSSL_NO_ENGINE */
 	char *subject_match, *altsubject_match, *suffix_match, *domain_match;
+	char *check_cert_subject;
 	int read_alerts, write_alerts, failed;
 
 	tls_session_ticket_cb session_ticket_cb;
@@ -1148,6 +1150,7 @@
 		tls_global = NULL;
 	}
 
+	os_free(data->check_cert_subject);
 	os_free(data);
 }
 
@@ -1625,6 +1628,7 @@
 	os_free(conn->altsubject_match);
 	os_free(conn->suffix_match);
 	os_free(conn->domain_match);
+	os_free(conn->check_cert_subject);
 	os_free(conn->session_ticket);
 	os_free(conn);
 }
@@ -1745,9 +1749,9 @@
 
 #ifndef CONFIG_NATIVE_WINDOWS
 static int domain_suffix_match(const u8 *val, size_t len, const char *match,
-			       int full)
+			       size_t match_len, int full)
 {
-	size_t i, match_len;
+	size_t i;
 
 	/* Check for embedded nuls that could mess up suffix matching */
 	for (i = 0; i < len; i++) {
@@ -1757,7 +1761,6 @@
 		}
 	}
 
-	match_len = os_strlen(match);
 	if (match_len > len || (full && match_len != len))
 		return 0;
 
@@ -1777,12 +1780,223 @@
 #endif /* CONFIG_NATIVE_WINDOWS */
 
 
-static int tls_match_suffix(X509 *cert, const char *match, int full)
+struct tls_dn_field_order_cnt {
+	u8 cn;
+	u8 c;
+	u8 l;
+	u8 st;
+	u8 o;
+	u8 ou;
+	u8 email;
+};
+
+
+static int get_dn_field_index(const struct tls_dn_field_order_cnt *dn_cnt,
+			      int nid)
 {
-#ifdef CONFIG_NATIVE_WINDOWS
-	/* wincrypt.h has conflicting X509_NAME definition */
-	return -1;
-#else /* CONFIG_NATIVE_WINDOWS */
+	switch (nid) {
+	case NID_commonName:
+		return dn_cnt->cn;
+	case NID_countryName:
+		return dn_cnt->c;
+	case NID_localityName:
+		return dn_cnt->l;
+	case NID_stateOrProvinceName:
+		return dn_cnt->st;
+	case NID_organizationName:
+		return dn_cnt->o;
+	case NID_organizationalUnitName:
+		return dn_cnt->ou;
+	case NID_pkcs9_emailAddress:
+		return dn_cnt->email;
+	default:
+		wpa_printf(MSG_ERROR,
+			   "TLS: Unknown NID '%d' in check_cert_subject",
+			   nid);
+		return -1;
+	}
+}
+
+
+/**
+ * match_dn_field - Match configuration DN field against Certificate DN field
+ * @cert: Certificate
+ * @nid: NID of DN field
+ * @field: Field name
+ * @value DN field value which is passed from configuration
+ *	e.g., if configuration have C=US and this argument will point to US.
+ * @dn_cnt: DN matching context
+ * Returns: 1 on success and 0 on failure
+ */
+static int match_dn_field(const X509 *cert, int nid, const char *field,
+			  const char *value,
+			  const struct tls_dn_field_order_cnt *dn_cnt)
+{
+	int i, ret = 0, len, config_dn_field_index, match_index = 0;
+	X509_NAME *name;
+
+	len = os_strlen(value);
+	name = X509_get_subject_name((X509 *) cert);
+
+	/* Assign incremented cnt for every field of DN to check DN field in
+	 * right order */
+	config_dn_field_index = get_dn_field_index(dn_cnt, nid);
+	if (config_dn_field_index < 0)
+		return 0;
+
+	/* Fetch value based on NID */
+	for (i = -1; (i = X509_NAME_get_index_by_NID(name, nid, i)) > -1;) {
+		X509_NAME_ENTRY *e;
+		ASN1_STRING *cn;
+
+		e = X509_NAME_get_entry(name, i);
+		if (!e)
+			continue;
+
+		cn = X509_NAME_ENTRY_get_data(e);
+		if (!cn)
+			continue;
+
+		match_index++;
+
+		/* check for more than one DN field with same name */
+		if (match_index != config_dn_field_index)
+			continue;
+
+		/* Check wildcard at the right end side */
+		/* E.g., if OU=develop* mentioned in configuration, allow 'OU'
+		 * of the subject in the client certificate to start with
+		 * 'develop' */
+		if (len > 0 && value[len - 1] == '*') {
+			/* Compare actual certificate DN field value with
+			 * configuration DN field value up to the specified
+			 * length. */
+			ret = ASN1_STRING_length(cn) >= len - 1 &&
+				os_memcmp(ASN1_STRING_get0_data(cn), value,
+					  len - 1) == 0;
+		} else {
+			/* Compare actual certificate DN field value with
+			 * configuration DN field value */
+			ret = ASN1_STRING_length(cn) == len &&
+				os_memcmp(ASN1_STRING_get0_data(cn), value,
+					  len) == 0;
+		}
+		if (!ret) {
+			wpa_printf(MSG_ERROR,
+				   "OpenSSL: Failed to match %s '%s' with certificate DN field value '%s'",
+				   field, value, ASN1_STRING_get0_data(cn));
+		}
+		break;
+	}
+
+	return ret;
+}
+
+
+/**
+ * get_value_from_field - Get value from DN field
+ * @cert: Certificate
+ * @field_str: DN field string which is passed from configuration file (e.g.,
+ *	 C=US)
+ * @dn_cnt: DN matching context
+ * Returns: 1 on success and 0 on failure
+ */
+static int get_value_from_field(const X509 *cert, char *field_str,
+				struct tls_dn_field_order_cnt *dn_cnt)
+{
+	int nid;
+	char *context = NULL, *name, *value;
+
+	if (os_strcmp(field_str, "*") == 0)
+		return 1; /* wildcard matches everything */
+
+	name = str_token(field_str, "=", &context);
+	if (!name)
+		return 0;
+
+	/* Compare all configured DN fields and assign nid based on that to
+	 * fetch correct value from certificate subject */
+	if (os_strcmp(name, "CN") == 0) {
+		nid = NID_commonName;
+		dn_cnt->cn++;
+	} else if(os_strcmp(name, "C") == 0) {
+		nid = NID_countryName;
+		dn_cnt->c++;
+	} else if (os_strcmp(name, "L") == 0) {
+		nid = NID_localityName;
+		dn_cnt->l++;
+	} else if (os_strcmp(name, "ST") == 0) {
+		nid = NID_stateOrProvinceName;
+		dn_cnt->st++;
+	} else if (os_strcmp(name, "O") == 0) {
+		nid = NID_organizationName;
+		dn_cnt->o++;
+	} else if (os_strcmp(name, "OU") == 0) {
+		nid = NID_organizationalUnitName;
+		dn_cnt->ou++;
+	} else if (os_strcmp(name, "emailAddress") == 0) {
+		nid = NID_pkcs9_emailAddress;
+		dn_cnt->email++;
+	} else {
+		wpa_printf(MSG_ERROR,
+			"TLS: Unknown field '%s' in check_cert_subject", name);
+		return 0;
+	}
+
+	value = str_token(field_str, "=", &context);
+	if (!value) {
+		wpa_printf(MSG_ERROR,
+			   "TLS: Distinguished Name field '%s' value is not defined in check_cert_subject",
+			   name);
+		return 0;
+	}
+
+	return match_dn_field(cert, nid, name, value, dn_cnt);
+}
+
+
+/**
+ * tls_match_dn_field - Match subject DN field with check_cert_subject
+ * @cert: Certificate
+ * @match: check_cert_subject string
+ * Returns: Return 1 on success and 0 on failure
+*/
+static int tls_match_dn_field(X509 *cert, const char *match)
+{
+	const char *token, *last = NULL;
+	char field[256];
+	struct tls_dn_field_order_cnt dn_cnt;
+
+	os_memset(&dn_cnt, 0, sizeof(dn_cnt));
+
+	/* Maximum length of each DN field is 255 characters */
+
+	/* Process each '/' delimited field */
+	while ((token = cstr_token(match, "/", &last))) {
+		if (last - token >= (int) sizeof(field)) {
+			wpa_printf(MSG_ERROR,
+				   "OpenSSL: Too long DN matching field value in '%s'",
+				   match);
+			return 0;
+		}
+		os_memcpy(field, token, last - token);
+		field[last - token] = '\0';
+
+		if (!get_value_from_field(cert, field, &dn_cnt)) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: No match for DN '%s'",
+				   field);
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+
+#ifndef CONFIG_NATIVE_WINDOWS
+static int tls_match_suffix_helper(X509 *cert, const char *match,
+				   size_t match_len, int full)
+{
 	GENERAL_NAME *gen;
 	void *ext;
 	int i;
@@ -1804,8 +2018,8 @@
 				  gen->d.dNSName->data,
 				  gen->d.dNSName->length);
 		if (domain_suffix_match(gen->d.dNSName->data,
-					gen->d.dNSName->length, match, full) ==
-		    1) {
+					gen->d.dNSName->length,
+					match, match_len, full) == 1) {
 			wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found",
 				   full ? "Match" : "Suffix match");
 			sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
@@ -1836,8 +2050,8 @@
 			continue;
 		wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName",
 				  cn->data, cn->length);
-		if (domain_suffix_match(cn->data, cn->length, match, full) == 1)
-		{
+		if (domain_suffix_match(cn->data, cn->length,
+					match, match_len, full) == 1) {
 			wpa_printf(MSG_DEBUG, "TLS: %s in commonName found",
 				   full ? "Match" : "Suffix match");
 			return 1;
@@ -1847,6 +2061,25 @@
 	wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found",
 		   full ? "": "suffix ");
 	return 0;
+}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+static int tls_match_suffix(X509 *cert, const char *match, int full)
+{
+#ifdef CONFIG_NATIVE_WINDOWS
+	/* wincrypt.h has conflicting X509_NAME definition */
+	return -1;
+#else /* CONFIG_NATIVE_WINDOWS */
+	const char *token, *last = NULL;
+
+	/* Process each match alternative separately until a match is found */
+	while ((token = cstr_token(match, ";", &last))) {
+		if (tls_match_suffix_helper(cert, token, last - token, full))
+			return 1;
+	}
+
+	return 0;
 #endif /* CONFIG_NATIVE_WINDOWS */
 }
 
@@ -2045,6 +2278,7 @@
 	struct tls_connection *conn;
 	struct tls_context *context;
 	char *match, *altmatch, *suffix_match, *domain_match;
+	const char *check_cert_subject;
 	const char *err_str;
 
 	err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
@@ -2145,6 +2379,18 @@
 		   "err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'",
 		   preverify_ok, err, err_str,
 		   conn->ca_cert_verify, depth, buf);
+	check_cert_subject = conn->check_cert_subject;
+	if (!check_cert_subject)
+		check_cert_subject = conn->data->check_cert_subject;
+	if (check_cert_subject) {
+		if (depth == 0 &&
+		    !tls_match_dn_field(err_cert, check_cert_subject)) {
+			preverify_ok = 0;
+			openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+					       "Distinguished Name",
+					       TLS_FAIL_DN_MISMATCH);
+		}
+	}
 	if (depth == 0 && match && os_strstr(buf, match) == NULL) {
 		wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not "
 			   "match with '%s'", buf, match);
@@ -2521,7 +2767,8 @@
 					    const char *subject_match,
 					    const char *altsubject_match,
 					    const char *suffix_match,
-					    const char *domain_match)
+					    const char *domain_match,
+					    const char *check_cert_subject)
 {
 	os_free(conn->subject_match);
 	conn->subject_match = NULL;
@@ -2555,6 +2802,14 @@
 			return -1;
 	}
 
+	os_free(conn->check_cert_subject);
+	conn->check_cert_subject = NULL;
+	if (check_cert_subject) {
+		conn->check_cert_subject = os_strdup(check_cert_subject);
+		if (!conn->check_cert_subject)
+			return -1;
+	}
+
 	return 0;
 }
 
@@ -2898,8 +3153,8 @@
 		return 0;
 	}
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)\
-	     && !defined(ANDROID)
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
+	!defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL)
 	if (SSL_use_certificate_chain_file(conn->ssl, client_cert) == 1) {
 		ERR_clear_error();
 		wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_chain_file"
@@ -3688,11 +3943,13 @@
 
 
 int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
-			      const char *label, u8 *out, size_t out_len)
+			      const char *label, const u8 *context,
+			      size_t context_len, u8 *out, size_t out_len)
 {
 	if (!conn ||
 	    SSL_export_keying_material(conn->ssl, out, out_len, label,
-				       os_strlen(label), NULL, 0, 0) != 1)
+				       os_strlen(label), context, context_len,
+				       context != NULL) != 1)
 		return -1;
 	return 0;
 }
@@ -4610,7 +4867,8 @@
 					     params->subject_match,
 					     params->altsubject_match,
 					     params->suffix_match,
-					     params->domain_match))
+					     params->domain_match,
+					     params->check_cert_subject))
 		return -1;
 
 	if (engine_id && ca_cert_id) {
@@ -4751,6 +5009,15 @@
 			   __func__, ERR_error_string(err, NULL));
 	}
 
+	os_free(data->check_cert_subject);
+	data->check_cert_subject = NULL;
+	if (params->check_cert_subject) {
+		data->check_cert_subject =
+			os_strdup(params->check_cert_subject);
+		if (!data->check_cert_subject)
+			return -1;
+	}
+
 	if (tls_global_ca_cert(data, params->ca_cert) ||
 	    tls_global_client_cert(data, params->client_cert) ||
 	    tls_global_private_key(data, params->private_key,
diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c
index b59622e..e9cb425 100644
--- a/src/crypto/tls_wolfssl.c
+++ b/src/crypto/tls_wolfssl.c
@@ -643,9 +643,9 @@
 
 
 static int domain_suffix_match(const char *val, size_t len, const char *match,
-			       int full)
+			       size_t match_len, int full)
 {
-	size_t i, match_len;
+	size_t i;
 
 	/* Check for embedded nuls that could mess up suffix matching */
 	for (i = 0; i < len; i++) {
@@ -656,7 +656,6 @@
 		}
 	}
 
-	match_len = os_strlen(match);
 	if (match_len > len || (full && match_len != len))
 		return 0;
 
@@ -674,7 +673,8 @@
 }
 
 
-static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full)
+static int tls_match_suffix_helper(WOLFSSL_X509 *cert, const char *match,
+				   size_t match_len, int full)
 {
 	WOLFSSL_ASN1_OBJECT *gen;
 	void *ext;
@@ -690,14 +690,14 @@
 
 	for (j = 0; ext && j < wolfSSL_sk_num(ext); j++) {
 		gen = wolfSSL_sk_value(ext, j);
-		if (gen->type != ALT_NAMES_OID)
+		if (gen->type != ASN_DNS_TYPE)
 			continue;
 		dns_name++;
 		wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName",
 				  gen->obj, os_strlen((char *)gen->obj));
 		if (domain_suffix_match((const char *) gen->obj,
 					os_strlen((char *) gen->obj), match,
-					full) == 1) {
+					match_len, full) == 1) {
 			wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found",
 				   full ? "Match" : "Suffix match");
 			wolfSSL_sk_ASN1_OBJECT_free(ext);
@@ -729,8 +729,8 @@
 			continue;
 		wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName",
 				  cn->data, cn->length);
-		if (domain_suffix_match(cn->data, cn->length, match, full) == 1)
-		{
+		if (domain_suffix_match(cn->data, cn->length,
+					match, match_len, full) == 1) {
 			wpa_printf(MSG_DEBUG, "TLS: %s in commonName found",
 				   full ? "Match" : "Suffix match");
 			return 1;
@@ -743,6 +743,20 @@
 }
 
 
+static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full)
+{
+	const char *token, *last = NULL;
+
+	/* Process each match alternative separately until a match is found */
+	while ((token = cstr_token(match, ";", &last))) {
+		if (tls_match_suffix_helper(cert, token, last - token, full))
+			return 1;
+	}
+
+	return 0;
+}
+
+
 static enum tls_fail_reason wolfssl_tls_fail_reason(int err)
 {
 	switch (err) {
@@ -1487,6 +1501,9 @@
 {
 	wpa_printf(MSG_DEBUG, "SSL: global set params");
 
+	if (params->check_cert_subject)
+		return -1; /* not yet supported */
+
 	if (tls_global_ca_cert(tls_ctx, params->ca_cert) < 0) {
 		wpa_printf(MSG_INFO, "SSL: Failed to load ca cert file '%s'",
 			   params->ca_cert);
@@ -1970,8 +1987,11 @@
 
 
 int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
-			      const char *label, u8 *out, size_t out_len)
+			      const char *label, const u8 *context,
+			      size_t context_len, u8 *out, size_t out_len)
 {
+	if (context)
+		return -1;
 	if (!conn || wolfSSL_make_eap_keys(conn->ssl, out, out_len, label) != 0)
 		return -1;
 	return 0;
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index 62f5baa..807cd94 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -1218,8 +1218,7 @@
 
 #ifdef ATH_WPS_IE
 	/* if WPS IE is present, preference is given to WPS */
-	if (ie.wps_ie &&
-	    (ie.wps_ie[1] > 0 && (ie.wps_ie[0] == WLAN_EID_VENDOR_SPECIFIC))) {
+	if (ie.wps_ie[0] == WLAN_EID_VENDOR_SPECIFIC && ie.wps_ie[1] > 0) {
 		iebuf = ie.wps_ie;
 		ielen = ie.wps_ie[1];
 	}
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index e0e6fe5..661e34e 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -4679,7 +4679,8 @@
 		goto fail;
 #endif /* CONFIG_MESH */
 
-	if (params->flags & WPA_STA_WMM) {
+	if ((!params->set || FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags)) &&
+	    (params->flags & WPA_STA_WMM)) {
 		struct nlattr *wme = nla_nest_start(msg, NL80211_ATTR_STA_WME);
 
 		wpa_printf(MSG_DEBUG, "  * qosinfo=0x%x", params->qosinfo);
@@ -5510,8 +5511,11 @@
 	    params->key_mgmt_suite == WPA_KEY_MGMT_OSEN ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_SAE ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_FT_IEEE8021X_SHA384 ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA256 ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA384 ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA256 ||
@@ -5542,12 +5546,21 @@
 		case WPA_KEY_MGMT_OSEN:
 			mgmt = RSN_AUTH_KEY_MGMT_OSEN;
 			break;
+		case WPA_KEY_MGMT_SAE:
+			mgmt = RSN_AUTH_KEY_MGMT_SAE;
+			break;
+		case WPA_KEY_MGMT_FT_SAE:
+			mgmt = RSN_AUTH_KEY_MGMT_FT_SAE;
+			break;
 		case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
 			mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
 			break;
 		case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
 			mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
 			break;
+		case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+			mgmt = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384;
+			break;
 		case WPA_KEY_MGMT_FILS_SHA256:
 			mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA256;
 			break;
@@ -6132,6 +6145,7 @@
 	if (tb[NL80211_ATTR_KEY_SEQ])
 		memcpy(arg, nla_data(tb[NL80211_ATTR_KEY_SEQ]),
 		       min_int(nla_len(tb[NL80211_ATTR_KEY_SEQ]), 6));
+	nl80211_nlmsg_clear(msg);
 	return NL_SKIP;
 }
 
@@ -6166,7 +6180,7 @@
 	int ret;
 	u32 val;
 
-	if (rts >= 2347)
+	if (rts >= 2347 || rts == -1)
 		val = (u32) -1;
 	else
 		val = rts;
@@ -6194,7 +6208,7 @@
 	int ret;
 	u32 val;
 
-	if (frag >= 2346)
+	if (frag >= 2346 || frag == -1)
 		val = (u32) -1;
 	else
 		val = frag;
@@ -7927,7 +7941,8 @@
 	    (params->fils_cache_id &&
 	     nla_put(msg, NL80211_ATTR_FILS_CACHE_ID, 2,
 		     params->fils_cache_id)) ||
-	    (params->pmk_len && params->pmk_len <= PMK_MAX_LEN &&
+	    (cmd != NL80211_CMD_DEL_PMKSA &&
+	     params->pmk_len && params->pmk_len <= PMK_MAX_LEN &&
 	     nla_put(msg, NL80211_ATTR_PMK, params->pmk_len, params->pmk))) {
 		nl80211_nlmsg_clear(msg);
 		nlmsg_free(msg);
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 06e619b..ffddd94 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -206,7 +206,8 @@
 
 
 static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
-			    const u8 *frame, size_t len, struct nlattr *wmm)
+			     const u8 *frame, size_t len, struct nlattr *wmm,
+			     struct nlattr *req_ie)
 {
 	const struct ieee80211_mgmt *mgmt;
 	union wpa_event_data event;
@@ -261,6 +262,11 @@
 			len - 24 - sizeof(mgmt->u.assoc_resp);
 	}
 
+	if (req_ie) {
+		event.assoc_info.req_ies = nla_data(req_ie);
+		event.assoc_info.req_ies_len = nla_len(req_ie);
+	}
+
 	event.assoc_info.freq = drv->assoc_freq;
 
 	/* When this association was initiated outside of wpa_supplicant,
@@ -868,7 +874,7 @@
 		       struct nlattr *addr, struct nlattr *timed_out,
 		       struct nlattr *freq, struct nlattr *ack,
 		       struct nlattr *cookie, struct nlattr *sig,
-		       struct nlattr *wmm)
+		       struct nlattr *wmm, struct nlattr *req_ie)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	const u8 *data;
@@ -917,7 +923,8 @@
 		mlme_event_auth(drv, nla_data(frame), nla_len(frame));
 		break;
 	case NL80211_CMD_ASSOCIATE:
-		mlme_event_assoc(drv, nla_data(frame), nla_len(frame), wmm);
+		mlme_event_assoc(drv, nla_data(frame), nla_len(frame), wmm,
+				 req_ie);
 		break;
 	case NL80211_CMD_DEAUTHENTICATE:
 		mlme_event_deauth_disassoc(drv, EVENT_DEAUTH,
@@ -2475,7 +2482,8 @@
 			   tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
 			   tb[NL80211_ATTR_COOKIE],
 			   tb[NL80211_ATTR_RX_SIGNAL_DBM],
-			   tb[NL80211_ATTR_STA_WME]);
+			   tb[NL80211_ATTR_STA_WME],
+			   tb[NL80211_ATTR_REQ_IE]);
 		break;
 	case NL80211_CMD_CONNECT:
 	case NL80211_CMD_ROAM:
@@ -2648,7 +2656,7 @@
 			   tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
 			   tb[NL80211_ATTR_COOKIE],
 			   tb[NL80211_ATTR_RX_SIGNAL_DBM],
-			   tb[NL80211_ATTR_STA_WME]);
+			   tb[NL80211_ATTR_STA_WME], NULL);
 		break;
 	case NL80211_CMD_UNEXPECTED_FRAME:
 		nl80211_spurious_frame(bss, tb, 0);
diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c
index e8a5135..9beb6c4 100644
--- a/src/drivers/driver_roboswitch.c
+++ b/src/drivers/driver_roboswitch.c
@@ -290,21 +290,26 @@
 
 	wpa_driver_roboswitch_addr_be16(addr, addr_be16);
 
-	wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_CONF,
-				   &_read, 1);
+	if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+				       ROBO_ARLCTRL_CONF, &_read, 1) < 0)
+		return -1;
 	/* If ARL control is disabled, there is nothing to leave. */
 	if (!(_read & (1 << 4))) return -1;
 
-	wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
-				   ROBO_ARLCTRL_ADDR_1, addr_read, 3);
-	wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1,
-				   &ports_read, 1);
+	if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+				       ROBO_ARLCTRL_ADDR_1, addr_read, 3) < 0 ||
+	    wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+				       ROBO_ARLCTRL_VEC_1, &ports_read, 1) < 0)
+		return -1;
 	/* check if we occupy multiport address 1 */
 	if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) {
-		wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
-					   ROBO_ARLCTRL_ADDR_2, addr_read, 3);
-		wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
-					   ROBO_ARLCTRL_VEC_2, &ports_read, 1);
+		if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+					       ROBO_ARLCTRL_ADDR_2, addr_read,
+					       3) < 0 ||
+		    wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+					       ROBO_ARLCTRL_VEC_2, &ports_read,
+					       1) < 0)
+			return -1;
 		/* and multiport address 2 */
 		if (os_memcmp(addr_read, addr_be16, 6) == 0 &&
 		    ports_read == ports) {
@@ -327,10 +332,13 @@
 						    &ports_read, 1);
 		}
 	} else {
-		wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
-					   ROBO_ARLCTRL_ADDR_2, addr_read, 3);
-		wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
-					   ROBO_ARLCTRL_VEC_2, &ports_read, 1);
+		if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+					       ROBO_ARLCTRL_ADDR_2, addr_read,
+					       3) < 0 ||
+		    wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+					       ROBO_ARLCTRL_VEC_2, &ports_read,
+					       1) < 0)
+			return -1;
 		/* or multiport address 2 */
 		if (os_memcmp(addr_read, addr_be16, 6) == 0 &&
 		    ports_read == ports) {
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index 31ae5c7..dd4f86e 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -1565,6 +1565,12 @@
  *	(a u32 with flags from &enum nl80211_wpa_versions).
  * @NL80211_ATTR_AKM_SUITES: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
  *	indicate which key management algorithm(s) to use (an array of u32).
+ *	This attribute is also sent in response to @NL80211_CMD_GET_WIPHY,
+ *	indicating the supported AKM suites, intended for specific drivers which
+ *	implement SME and have constraints on which AKMs are supported and also
+ *	the cases where an AKM support is offloaded to the driver/firmware.
+ *	If there is no such notification from the driver, user space should
+ *	assume the driver supports all the AKM suites.
  *
  * @NL80211_ATTR_REQ_IE: (Re)association request information elements as
  *	sent out by the card, for ROAM and successful CONNECT events.
@@ -2260,10 +2266,10 @@
  *     &enum nl80211_external_auth_action value). This is used with the
  *     %NL80211_CMD_EXTERNAL_AUTH request event.
  * @NL80211_ATTR_EXTERNAL_AUTH_SUPPORT: Flag attribute indicating that the user
- *     space supports external authentication. This attribute shall be used
- *     only with %NL80211_CMD_CONNECT request. The driver may offload
- *     authentication processing to user space if this capability is indicated
- *     in NL80211_CMD_CONNECT requests from the user space.
+ *	space supports external authentication. This attribute shall be used
+ *	with %NL80211_CMD_CONNECT and %NL80211_CMD_START_AP request. The driver
+ *	may offload authentication processing to user space if this capability
+ *	is indicated in the respective requests from the user space.
  *
  * @NL80211_ATTR_NSS: Station's New/updated  RX_NSS value notified using this
  *	u8 attribute. This is used with %NL80211_CMD_STA_OPMODE_CHANGED.
@@ -2299,6 +2305,9 @@
  *	This is also used for capability advertisement in the wiphy information,
  *	with the appropriate sub-attributes.
  *
+ * @NL80211_ATTR_AIRTIME_WEIGHT: Station's weight when scheduled by the airtime
+ *	scheduler.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2748,6 +2757,8 @@
 
 	NL80211_ATTR_PEER_MEASUREMENTS,
 
+	NL80211_ATTR_AIRTIME_WEIGHT,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -3125,6 +3136,9 @@
  *	might not be fully accurate.
  * @NL80211_STA_INFO_CONNECTED_TO_GATE: set to true if STA has a path to a
  *	mesh gate (u8, 0 or 1)
+ * @NL80211_STA_INFO_TX_DURATION: aggregate PPDU duration for all frames
+ *	sent to the station (u64, usec)
+ * @NL80211_STA_INFO_AIRTIME_WEIGHT: current airtime weight for station (u16)
  * @__NL80211_STA_INFO_AFTER_LAST: internal
  * @NL80211_STA_INFO_MAX: highest possible station info attribute
  */
@@ -3168,6 +3182,8 @@
 	NL80211_STA_INFO_RX_MPDUS,
 	NL80211_STA_INFO_FCS_ERROR_COUNT,
 	NL80211_STA_INFO_CONNECTED_TO_GATE,
+	NL80211_STA_INFO_TX_DURATION,
+	NL80211_STA_INFO_AIRTIME_WEIGHT,
 
 	/* keep last */
 	__NL80211_STA_INFO_AFTER_LAST,
@@ -3277,8 +3293,10 @@
  * 	&enum nl80211_mpath_flags;
  * @NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: total path discovery timeout, in msec
  * @NL80211_MPATH_INFO_DISCOVERY_RETRIES: mesh path discovery retries
+ * @NL80211_MPATH_INFO_HOP_COUNT: hop count to destination
+ * @NL80211_MPATH_INFO_PATH_CHANGE: total number of path changes to destination
  * @NL80211_MPATH_INFO_MAX: highest mesh path information attribute number
- *	currently defind
+ *	currently defined
  * @__NL80211_MPATH_INFO_AFTER_LAST: internal use
  */
 enum nl80211_mpath_info {
@@ -3290,6 +3308,8 @@
 	NL80211_MPATH_INFO_FLAGS,
 	NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
 	NL80211_MPATH_INFO_DISCOVERY_RETRIES,
+	NL80211_MPATH_INFO_HOP_COUNT,
+	NL80211_MPATH_INFO_PATH_CHANGE,
 
 	/* keep last */
 	__NL80211_MPATH_INFO_AFTER_LAST,
@@ -5316,6 +5336,13 @@
  *      if this flag is not set. Ignoring this can leak clear text packets and/or
  *      freeze the connection.
  *
+ * @NL80211_EXT_FEATURE_AIRTIME_FAIRNESS: Driver supports getting airtime
+ *	fairness for transmitted packets and has enabled airtime fairness
+ *	scheduling.
+ *
+ * @NL80211_EXT_FEATURE_AP_PMKSA_CACHING: Driver/device supports PMKSA caching
+ *	(set/del PMKSA operations) in AP mode.
+ *
  * @NUM_NL80211_EXT_FEATURES: number of extended features.
  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
  */
@@ -5355,6 +5382,8 @@
 	NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT,
 	NL80211_EXT_FEATURE_CAN_REPLACE_PTK0,
 	NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER,
+	NL80211_EXT_FEATURE_AIRTIME_FAIRNESS,
+	NL80211_EXT_FEATURE_AP_PMKSA_CACHING,
 
 	/* add new features before the definition below */
 	NUM_NL80211_EXT_FEATURES,
@@ -5606,9 +5635,14 @@
  * Used by cfg80211_rx_mgmt()
  *
  * @NL80211_RXMGMT_FLAG_ANSWERED: frame was answered by device/driver.
+ * @NL80211_RXMGMT_FLAG_EXTERNAL_AUTH: Host driver intends to offload
+ *	the authentication. Exclusively defined for host drivers that
+ *	advertises the SME functionality but would like the userspace
+ *	to handle certain authentication algorithms (e.g. SAE).
  */
 enum nl80211_rxmgmt_flags {
 	NL80211_RXMGMT_FLAG_ANSWERED = 1 << 0,
+	NL80211_RXMGMT_FLAG_EXTERNAL_AUTH = 1 << 1,
 };
 
 /*
diff --git a/src/eap_common/eap_eke_common.c b/src/eap_common/eap_eke_common.c
index bfe8811..438baf1 100644
--- a/src/eap_common/eap_eke_common.c
+++ b/src/eap_common/eap_eke_common.c
@@ -399,7 +399,7 @@
 	/* SharedSecret = prf(0+, g ^ (x_s * x_p) (mod p)) */
 	len = dh->prime_len;
 	if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len,
-				    dhpriv, dh->prime_len, peer_pub,
+				    NULL, 0, dhpriv, dh->prime_len, peer_pub,
 				    dh->prime_len, modexp, &len) < 0)
 		return -1;
 	if (len < dh->prime_len) {
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
index 88c6595..4288b52 100644
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -8,11 +8,15 @@
 
 #include "includes.h"
 #include "common.h"
+#include "utils/const_time.h"
 #include "crypto/sha256.h"
 #include "crypto/crypto.h"
 #include "eap_defs.h"
 #include "eap_pwd_common.h"
 
+#define MAX_ECC_PRIME_LEN 66
+
+
 /* The random function H(x) = HMAC-SHA256(0^32, x) */
 struct crypto_hash * eap_pwd_h_init(void)
 {
@@ -102,6 +106,15 @@
 }
 
 
+static void buf_shift_right(u8 *buf, size_t len, size_t bits)
+{
+	size_t i;
+	for (i = len - 1; i > 0; i--)
+		buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits);
+	buf[0] >>= bits;
+}
+
+
 /*
  * compute a "random" secret point on an elliptic curve based
  * on the password and identities.
@@ -113,17 +126,27 @@
 			     const u8 *token)
 {
 	struct crypto_bignum *qr = NULL, *qnr = NULL, *one = NULL;
+	struct crypto_bignum *qr_or_qnr = NULL;
+	u8 qr_bin[MAX_ECC_PRIME_LEN];
+	u8 qnr_bin[MAX_ECC_PRIME_LEN];
+	u8 qr_or_qnr_bin[MAX_ECC_PRIME_LEN];
+	u8 x_bin[MAX_ECC_PRIME_LEN];
 	struct crypto_bignum *tmp1 = NULL, *tmp2 = NULL, *pm1 = NULL;
 	struct crypto_hash *hash;
 	unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr;
-	int is_odd, ret = 0, check, found = 0;
-	size_t primebytelen, primebitlen;
-	struct crypto_bignum *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
+	int ret = 0, check, res;
+	u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
+		       * mask */
+	size_t primebytelen = 0, primebitlen;
+	struct crypto_bignum *x_candidate = NULL, *cofactor = NULL;
 	const struct crypto_bignum *prime;
+	u8 mask, found_ctr = 0, is_odd = 0;
 
 	if (grp->pwe)
 		return -1;
 
+	os_memset(x_bin, 0, sizeof(x_bin));
+
 	prime = crypto_ec_get_prime(grp->group);
 	cofactor = crypto_bignum_init();
 	grp->pwe = crypto_ec_point_init(grp->group);
@@ -152,8 +175,6 @@
 
 	/* get a random quadratic residue and nonresidue */
 	while (!qr || !qnr) {
-		int res;
-
 		if (crypto_bignum_rand(tmp1, prime) < 0)
 			goto fail;
 		res = crypto_bignum_legendre(tmp1, prime);
@@ -167,6 +188,11 @@
 		if (!tmp1)
 			goto fail;
 	}
+	if (crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin),
+				 primebytelen) < 0 ||
+	    crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin),
+				 primebytelen) < 0)
+		goto fail;
 
 	os_memset(prfbuf, 0, primebytelen);
 	ctr = 0;
@@ -194,17 +220,16 @@
 		eap_pwd_h_update(hash, &ctr, sizeof(ctr));
 		eap_pwd_h_final(hash, pwe_digest);
 
-		crypto_bignum_deinit(rnd, 1);
-		rnd = crypto_bignum_init_set(pwe_digest, SHA256_MAC_LEN);
-		if (!rnd) {
-			wpa_printf(MSG_INFO, "EAP-pwd: unable to create rnd");
-			goto fail;
-		}
+		is_odd = const_time_select_u8(
+			found, is_odd, pwe_digest[SHA256_MAC_LEN - 1] & 0x01);
 		if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN,
 				(u8 *) "EAP-pwd Hunting And Pecking",
 				os_strlen("EAP-pwd Hunting And Pecking"),
 				prfbuf, primebitlen) < 0)
 			goto fail;
+		if (primebitlen % 8)
+			buf_shift_right(prfbuf, primebytelen,
+					8 - primebitlen % 8);
 
 		crypto_bignum_deinit(x_candidate, 1);
 		x_candidate = crypto_bignum_init_set(prfbuf, primebytelen);
@@ -214,30 +239,20 @@
 			goto fail;
 		}
 
-		/*
-		 * eap_pwd_kdf() returns a string of bits 0..primebitlen but
-		 * BN_bin2bn will treat that string of bits as a big endian
-		 * number. If the primebitlen is not an even multiple of 8
-		 * then excessive bits-- those _after_ primebitlen-- so now
-		 * we have to shift right the amount we masked off.
-		 */
-		if ((primebitlen % 8) &&
-		    crypto_bignum_rshift(x_candidate,
-					 (8 - (primebitlen % 8)),
-					 x_candidate) < 0)
-			goto fail;
-
 		if (crypto_bignum_cmp(x_candidate, prime) >= 0)
 			continue;
 
-		wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate",
-			    prfbuf, primebytelen);
+		wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: x_candidate",
+				prfbuf, primebytelen);
+		const_time_select_bin(found, x_bin, prfbuf, primebytelen,
+				      x_bin);
 
 		/*
 		 * compute y^2 using the equation of the curve
 		 *
 		 *      y^2 = x^3 + ax + b
 		 */
+		crypto_bignum_deinit(tmp2, 1);
 		tmp2 = crypto_ec_point_compute_y_sqr(grp->group, x_candidate);
 		if (!tmp2)
 			goto fail;
@@ -260,13 +275,15 @@
 		 * Flip a coin, multiply by the random quadratic residue or the
 		 * random quadratic nonresidue and record heads or tails.
 		 */
-		if (crypto_bignum_is_odd(tmp1)) {
-			crypto_bignum_mulmod(tmp2, qr, prime, tmp2);
-			check = 1;
-		} else {
-			crypto_bignum_mulmod(tmp2, qnr, prime, tmp2);
-			check = -1;
-		}
+		mask = const_time_eq_u8(crypto_bignum_is_odd(tmp1), 1);
+		check = const_time_select_s8(mask, 1, -1);
+		const_time_select_bin(mask, qr_bin, qnr_bin, primebytelen,
+				      qr_or_qnr_bin);
+		crypto_bignum_deinit(qr_or_qnr, 1);
+		qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, primebytelen);
+		if (!qr_or_qnr ||
+		    crypto_bignum_mulmod(tmp2, qr_or_qnr, prime, tmp2) < 0)
+			goto fail;
 
 		/*
 		 * Now it's safe to do legendre, if check is 1 then it's
@@ -274,59 +291,12 @@
 		 * change result), if check is -1 then it's the opposite test
 		 * (multiplying a qr by qnr would make a qnr).
 		 */
-		if (crypto_bignum_legendre(tmp2, prime) == check) {
-			if (found == 1)
-				continue;
-
-			/* need to unambiguously identify the solution */
-			is_odd = crypto_bignum_is_odd(rnd);
-
-			/*
-			 * We know x_candidate is a quadratic residue so set
-			 * it here.
-			 */
-			if (crypto_ec_point_solve_y_coord(grp->group, grp->pwe,
-							  x_candidate,
-							  is_odd) != 0) {
-				wpa_printf(MSG_INFO,
-					   "EAP-pwd: Could not solve for y");
-				continue;
-			}
-
-			/*
-			 * If there's a solution to the equation then the point
-			 * must be on the curve so why check again explicitly?
-			 * OpenSSL code says this is required by X9.62. We're
-			 * not X9.62 but it can't hurt just to be sure.
-			 */
-			if (!crypto_ec_point_is_on_curve(grp->group,
-							 grp->pwe)) {
-				wpa_printf(MSG_INFO,
-					   "EAP-pwd: point is not on curve");
-				continue;
-			}
-
-			if (!crypto_bignum_is_one(cofactor)) {
-				/* make sure the point is not in a small
-				 * sub-group */
-				if (crypto_ec_point_mul(grp->group, grp->pwe,
-							cofactor,
-							grp->pwe) != 0) {
-					wpa_printf(MSG_INFO,
-						   "EAP-pwd: cannot multiply generator by order");
-					continue;
-				}
-				if (crypto_ec_point_is_at_infinity(grp->group,
-								   grp->pwe)) {
-					wpa_printf(MSG_INFO,
-						   "EAP-pwd: point is at infinity");
-					continue;
-				}
-			}
-			wpa_printf(MSG_DEBUG,
-				   "EAP-pwd: found a PWE in %d tries", ctr);
-			found = 1;
-		}
+		res = crypto_bignum_legendre(tmp2, prime);
+		if (res == -2)
+			goto fail;
+		mask = const_time_eq(res, check);
+		found_ctr = const_time_select_u8(found, found_ctr, ctr);
+		found |= mask;
 	}
 	if (found == 0) {
 		wpa_printf(MSG_INFO,
@@ -334,6 +304,44 @@
 			   num);
 		goto fail;
 	}
+
+	/*
+	 * We know x_candidate is a quadratic residue so set it here.
+	 */
+	crypto_bignum_deinit(x_candidate, 1);
+	x_candidate = crypto_bignum_init_set(x_bin, primebytelen);
+	if (!x_candidate ||
+	    crypto_ec_point_solve_y_coord(grp->group, grp->pwe, x_candidate,
+					  is_odd) != 0) {
+		wpa_printf(MSG_INFO, "EAP-pwd: Could not solve for y");
+		goto fail;
+	}
+
+	/*
+	 * If there's a solution to the equation then the point must be on the
+	 * curve so why check again explicitly? OpenSSL code says this is
+	 * required by X9.62. We're not X9.62 but it can't hurt just to be sure.
+	 */
+	if (!crypto_ec_point_is_on_curve(grp->group, grp->pwe)) {
+		wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve");
+		goto fail;
+	}
+
+	if (!crypto_bignum_is_one(cofactor)) {
+		/* make sure the point is not in a small sub-group */
+		if (crypto_ec_point_mul(grp->group, grp->pwe, cofactor,
+					grp->pwe) != 0) {
+			wpa_printf(MSG_INFO,
+				   "EAP-pwd: cannot multiply generator by order");
+			goto fail;
+		}
+		if (crypto_ec_point_is_at_infinity(grp->group, grp->pwe)) {
+			wpa_printf(MSG_INFO, "EAP-pwd: point is at infinity");
+			goto fail;
+		}
+	}
+	wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %02d tries", found_ctr);
+
 	if (0) {
  fail:
 		crypto_ec_point_deinit(grp->pwe, 1);
@@ -343,14 +351,18 @@
 	/* cleanliness and order.... */
 	crypto_bignum_deinit(cofactor, 1);
 	crypto_bignum_deinit(x_candidate, 1);
-	crypto_bignum_deinit(rnd, 1);
 	crypto_bignum_deinit(pm1, 0);
 	crypto_bignum_deinit(tmp1, 1);
 	crypto_bignum_deinit(tmp2, 1);
 	crypto_bignum_deinit(qr, 1);
 	crypto_bignum_deinit(qnr, 1);
+	crypto_bignum_deinit(qr_or_qnr, 1);
 	crypto_bignum_deinit(one, 0);
-	os_free(prfbuf);
+	bin_clear_free(prfbuf, primebytelen);
+	os_memset(qr_bin, 0, sizeof(qr_bin));
+	os_memset(qnr_bin, 0, sizeof(qnr_bin));
+	os_memset(qr_or_qnr_bin, 0, sizeof(qr_or_qnr_bin));
+	os_memset(pwe_digest, 0, sizeof(pwe_digest));
 
 	return ret;
 }
@@ -416,3 +428,137 @@
 
 	return 1;
 }
+
+
+static int eap_pwd_element_coord_ok(const struct crypto_bignum *prime,
+				    const u8 *buf, size_t len)
+{
+	struct crypto_bignum *val;
+	int ok = 1;
+
+	val = crypto_bignum_init_set(buf, len);
+	if (!val || crypto_bignum_is_zero(val) ||
+	    crypto_bignum_cmp(val, prime) >= 0)
+		ok = 0;
+	crypto_bignum_deinit(val, 0);
+	return ok;
+}
+
+
+struct crypto_ec_point * eap_pwd_get_element(EAP_PWD_group *group,
+					     const u8 *buf)
+{
+	struct crypto_ec_point *element;
+	const struct crypto_bignum *prime;
+	size_t prime_len;
+	struct crypto_bignum *cofactor = NULL;
+
+	prime = crypto_ec_get_prime(group->group);
+	prime_len = crypto_ec_prime_len(group->group);
+
+	/* RFC 5931, 2.8.5.2.2: 0 < x,y < p */
+	if (!eap_pwd_element_coord_ok(prime, buf, prime_len) ||
+	    !eap_pwd_element_coord_ok(prime, buf + prime_len, prime_len)) {
+		wpa_printf(MSG_INFO, "EAP-pwd: Invalid coordinate in element");
+		return NULL;
+	}
+
+	element = crypto_ec_point_from_bin(group->group, buf);
+	if (!element) {
+		wpa_printf(MSG_INFO, "EAP-pwd: EC point from element failed");
+		return NULL;
+	}
+
+	/* RFC 5931, 2.8.5.2.2: on curve and not the point at infinity */
+	if (!crypto_ec_point_is_on_curve(group->group, element) ||
+	    crypto_ec_point_is_at_infinity(group->group, element)) {
+		wpa_printf(MSG_INFO, "EAP-pwd: Invalid element");
+		goto fail;
+	}
+
+	cofactor = crypto_bignum_init();
+	if (!cofactor || crypto_ec_cofactor(group->group, cofactor) < 0) {
+		wpa_printf(MSG_INFO,
+			   "EAP-pwd: Unable to get cofactor for curve");
+		goto fail;
+	}
+
+	if (!crypto_bignum_is_one(cofactor)) {
+		struct crypto_ec_point *point;
+		int ok = 1;
+
+		/* check to ensure peer's element is not in a small sub-group */
+		point = crypto_ec_point_init(group->group);
+		if (!point ||
+		    crypto_ec_point_mul(group->group, element,
+					cofactor, point) != 0 ||
+		    crypto_ec_point_is_at_infinity(group->group, point))
+			ok = 0;
+		crypto_ec_point_deinit(point, 0);
+
+		if (!ok) {
+			wpa_printf(MSG_INFO,
+				   "EAP-pwd: Small sub-group check on peer element failed");
+			goto fail;
+		}
+	}
+
+out:
+	crypto_bignum_deinit(cofactor, 0);
+	return element;
+fail:
+	crypto_ec_point_deinit(element, 0);
+	element = NULL;
+	goto out;
+}
+
+
+struct crypto_bignum * eap_pwd_get_scalar(EAP_PWD_group *group, const u8 *buf)
+{
+	struct crypto_bignum *scalar;
+	const struct crypto_bignum *order;
+	size_t order_len;
+
+	order = crypto_ec_get_order(group->group);
+	order_len = crypto_ec_order_len(group->group);
+
+	/* RFC 5931, 2.8.5.2: 1 < scalar < r */
+	scalar = crypto_bignum_init_set(buf, order_len);
+	if (!scalar || crypto_bignum_is_zero(scalar) ||
+	    crypto_bignum_is_one(scalar) ||
+	    crypto_bignum_cmp(scalar, order) >= 0) {
+		wpa_printf(MSG_INFO, "EAP-pwd: received scalar is invalid");
+		crypto_bignum_deinit(scalar, 0);
+		scalar = NULL;
+	}
+
+	return scalar;
+}
+
+
+int eap_pwd_get_rand_mask(EAP_PWD_group *group, struct crypto_bignum *_rand,
+			  struct crypto_bignum *_mask,
+			  struct crypto_bignum *scalar)
+{
+	const struct crypto_bignum *order;
+	int count;
+
+	order = crypto_ec_get_order(group->group);
+
+	/* Select two random values rand,mask such that 1 < rand,mask < r and
+	 * rand + mask mod r > 1. */
+	for (count = 0; count < 100; count++) {
+		if (crypto_bignum_rand(_rand, order) == 0 &&
+		    !crypto_bignum_is_zero(_rand) &&
+		    crypto_bignum_rand(_mask, order) == 0 &&
+		    !crypto_bignum_is_zero(_mask) &&
+		    crypto_bignum_add(_rand, _mask, scalar) == 0 &&
+		    crypto_bignum_mod(scalar, order, scalar) == 0 &&
+		    !crypto_bignum_is_zero(scalar) &&
+		    !crypto_bignum_is_one(scalar))
+			return 0;
+	}
+
+	wpa_printf(MSG_INFO, "EAP-pwd: unable to get randomness");
+	return -1;
+}
diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h
index 6b07cf8..c48acee 100644
--- a/src/eap_common/eap_pwd_common.h
+++ b/src/eap_common/eap_pwd_common.h
@@ -67,5 +67,11 @@
 struct crypto_hash * eap_pwd_h_init(void);
 void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len);
 void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest);
+struct crypto_ec_point * eap_pwd_get_element(EAP_PWD_group *group,
+					     const u8 *buf);
+struct crypto_bignum * eap_pwd_get_scalar(EAP_PWD_group *group, const u8 *buf);
+int eap_pwd_get_rand_mask(EAP_PWD_group *group, struct crypto_bignum *_rand,
+			  struct crypto_bignum *_mask,
+			  struct crypto_bignum *scalar);
 
 #endif  /* EAP_PWD_COMMON_H */
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
index d416afd..3a88f2a 100644
--- a/src/eap_peer/eap_config.h
+++ b/src/eap_peer/eap_config.h
@@ -101,7 +101,7 @@
 	 * certificate store (My user account) is used, whereas computer store
 	 * (Computer account) is used when running wpasvc as a service.
 	 */
-	u8 *ca_cert;
+	char *ca_cert;
 
 	/**
 	 * ca_path - Directory path for CA certificate files (PEM)
@@ -112,7 +112,7 @@
 	 * these certificates are added to the list of trusted CAs. ca_cert
 	 * may also be included in that case, but it is not required.
 	 */
-	u8 *ca_path;
+	char *ca_path;
 
 	/**
 	 * client_cert - File path to client certificate file (PEM/DER)
@@ -126,7 +126,7 @@
 	 * Alternatively, a named configuration blob can be used by setting
 	 * this to blob://blob_name.
 	 */
-	u8 *client_cert;
+	char *client_cert;
 
 	/**
 	 * private_key - File path to client private key file (PEM/DER/PFX)
@@ -153,7 +153,7 @@
 	 * Alternatively, a named configuration blob can be used by setting
 	 * this to blob://blob_name.
 	 */
-	u8 *private_key;
+	char *private_key;
 
 	/**
 	 * private_key_passwd - Password for private key file
@@ -178,7 +178,7 @@
 	 * Alternatively, a named configuration blob can be used by setting
 	 * this to blob://blob_name.
 	 */
-	u8 *dh_file;
+	char *dh_file;
 
 	/**
 	 * subject_match - Constraint for server certificate subject
@@ -194,7 +194,49 @@
 	 * to do a suffix match against a possible domain name in the CN entry.
 	 * For such a use case, domain_suffix_match should be used instead.
 	 */
-	u8 *subject_match;
+	char *subject_match;
+
+	/**
+	 * check_cert_subject - Constraint for server certificate subject fields
+	 *
+	 * If check_cert_subject is set, the value of every field will be
+	 * checked against the DN of the subject in the authentication server
+	 * certificate. If the values do not match, the certificate verification
+	 * will fail, rejecting the server. This option allows wpa_supplicant to
+	 * match every individual field in the right order against the DN of the
+	 * subject in the server certificate.
+	 *
+	 * For example, check_cert_subject=C=US/O=XX/OU=ABC/OU=XYZ/CN=1234 will
+	 * check every individual DN field of the subject in the server
+	 * certificate. If OU=XYZ comes first in terms of the order in the
+	 * server certificate (DN field of server certificate
+	 * C=US/O=XX/OU=XYZ/OU=ABC/CN=1234), wpa_supplicant will reject the
+	 * server because the order of 'OU' is not matching the specified string
+	 * in check_cert_subject.
+	 *
+	 * This option also allows '*' as a wildcard. This option has some
+	 * limitation.
+	 * It can only be used as per the following example.
+	 *
+	 * For example, check_cert_subject=C=US/O=XX/OU=Production* and we have
+	 * two servers and DN of the subject in the first server certificate is
+	 * (C=US/O=XX/OU=Production Unit) and DN of the subject in the second
+	 * server is (C=US/O=XX/OU=Production Factory). In this case,
+	 * wpa_supplicant will allow both servers because the value of 'OU'
+	 * field in both server certificates matches 'OU' value in
+	 * 'check_cert_subject' up to 'wildcard'.
+	 *
+	 * (Allow all servers, e.g., check_cert_subject=*)
+	 */
+	char *check_cert_subject;
+
+	/**
+	 * check_cert_subject2 - Constraint for server certificate subject fields
+	 *
+	 * This field is like check_cert_subject, but used for phase 2 (inside
+	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
+	 */
+	char *check_cert_subject2;
 
 	/**
 	 * altsubject_match - Constraint for server certificate alt. subject
@@ -212,23 +254,26 @@
 	 *
 	 * Following types are supported: EMAIL, DNS, URI
 	 */
-	u8 *altsubject_match;
+	char *altsubject_match;
 
 	/**
 	 * domain_suffix_match - Constraint for server domain name
 	 *
-	 * If set, this FQDN is used as a suffix match requirement for the
-	 * server certificate in SubjectAltName dNSName element(s). If a
-	 * matching dNSName is found, this constraint is met. If no dNSName
-	 * values are present, this constraint is matched against SubjectName CN
-	 * using same suffix match comparison. Suffix match here means that the
-	 * host/domain name is compared one label at a time starting from the
-	 * top-level domain and all the labels in domain_suffix_match shall be
-	 * included in the certificate. The certificate may include additional
-	 * sub-level labels in addition to the required labels.
+	 * If set, this semicolon deliminated list of FQDNs is used as suffix
+	 * match requirements for the server certificate in SubjectAltName
+	 * dNSName element(s). If a matching dNSName is found against any of the
+	 * specified values, this constraint is met. If no dNSName values are
+	 * present, this constraint is matched against SubjectName CN using same
+	 * suffix match comparison. Suffix match here means that the host/domain
+	 * name is compared case-insentively one label at a time starting from
+	 * the top-level domain and all the labels in domain_suffix_match shall
+	 * be included in the certificate. The certificate may include
+	 * additional sub-level labels in addition to the required labels.
 	 *
 	 * For example, domain_suffix_match=example.com would match
-	 * test.example.com but would not match test-example.com.
+	 * test.example.com but would not match test-example.com. Multiple
+	 * match options can be specified in following manner:
+	 * example.org;example.com.
 	 */
 	char *domain_suffix_match;
 
@@ -244,6 +289,12 @@
 	 * no subdomains or wildcard matches are allowed. Case-insensitive
 	 * comparison is used, so "Example.com" matches "example.com", but would
 	 * not match "test.Example.com".
+	 *
+	 * More than one match string can be provided by using semicolons to
+	 * separate the strings (e.g., example.org;example.com). When multiple
+	 * strings are specified, a match with any one of the values is
+	 * considered a sufficient match for the certificate, i.e., the
+	 * conditions are ORed together.
 	 */
 	char *domain_match;
 
@@ -263,7 +314,7 @@
 	 * Alternatively, a named configuration blob can be used by setting
 	 * this to blob://blob_name.
 	 */
-	u8 *ca_cert2;
+	char *ca_cert2;
 
 	/**
 	 * ca_path2 - Directory path for CA certificate files (PEM) (Phase 2)
@@ -277,7 +328,7 @@
 	 * This field is like ca_path, but used for phase 2 (inside
 	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
 	 */
-	u8 *ca_path2;
+	char *ca_path2;
 
 	/**
 	 * client_cert2 - File path to client certificate file
@@ -290,7 +341,7 @@
 	 * Alternatively, a named configuration blob can be used by setting
 	 * this to blob://blob_name.
 	 */
-	u8 *client_cert2;
+	char *client_cert2;
 
 	/**
 	 * private_key2 - File path to client private key file
@@ -303,7 +354,7 @@
 	 * Alternatively, a named configuration blob can be used by setting
 	 * this to blob://blob_name.
 	 */
-	u8 *private_key2;
+	char *private_key2;
 
 	/**
 	 * private_key2_passwd -  Password for private key file
@@ -324,7 +375,7 @@
 	 * Alternatively, a named configuration blob can be used by setting
 	 * this to blob://blob_name.
 	 */
-	u8 *dh_file2;
+	char *dh_file2;
 
 	/**
 	 * subject_match2 - Constraint for server certificate subject
@@ -332,7 +383,7 @@
 	 * This field is like subject_match, but used for phase 2 (inside
 	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
 	 */
-	u8 *subject_match2;
+	char *subject_match2;
 
 	/**
 	 * altsubject_match2 - Constraint for server certificate alt. subject
@@ -340,7 +391,7 @@
 	 * This field is like altsubject_match, but used for phase 2 (inside
 	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
 	 */
-	u8 *altsubject_match2;
+	char *altsubject_match2;
 
 	/**
 	 * domain_suffix_match2 - Constraint for server domain name
diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c
index 650bea6..8dcf7cc 100644
--- a/src/eap_peer/eap_peap.c
+++ b/src/eap_peer/eap_peap.c
@@ -1084,6 +1084,7 @@
 				   "key derivation", label);
 			data->key_data =
 				eap_peer_tls_derive_key(sm, &data->ssl, label,
+							NULL, 0,
 							EAP_TLS_KEY_LEN +
 							EAP_EMSK_LEN);
 			if (data->key_data) {
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
index 761c16a..4be4fcf 100644
--- a/src/eap_peer/eap_pwd.c
+++ b/src/eap_peer/eap_pwd.c
@@ -308,10 +308,10 @@
 				const struct wpabuf *reqData,
 				const u8 *payload, size_t payload_len)
 {
-	struct crypto_ec_point *K = NULL, *point = NULL;
+	struct crypto_ec_point *K = NULL;
 	struct crypto_bignum *mask = NULL, *cofactor = NULL;
 	const u8 *ptr = payload;
-	u8 *scalar = NULL, *element = NULL;
+	u8 *scalar, *element;
 	size_t prime_len, order_len;
 	const u8 *password;
 	size_t password_len;
@@ -542,19 +542,9 @@
 		goto fin;
 	}
 
-	if (crypto_bignum_rand(data->private_value,
-			       crypto_ec_get_order(data->grp->group)) < 0 ||
-	    crypto_bignum_rand(mask,
-			       crypto_ec_get_order(data->grp->group)) < 0 ||
-	    crypto_bignum_add(data->private_value, mask,
-			      data->my_scalar) < 0 ||
-	    crypto_bignum_mod(data->my_scalar,
-			      crypto_ec_get_order(data->grp->group),
-			      data->my_scalar) < 0) {
-		wpa_printf(MSG_INFO,
-			   "EAP-pwd (peer): unable to get randomness");
+	if (eap_pwd_get_rand_mask(data->grp, data->private_value, mask,
+				  data->my_scalar) < 0)
 		goto fin;
-	}
 
 	if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, mask,
 				data->my_element) < 0) {
@@ -572,43 +562,27 @@
 	/* process the request */
 	data->k = crypto_bignum_init();
 	K = crypto_ec_point_init(data->grp->group);
-	point = crypto_ec_point_init(data->grp->group);
-	if (!data->k || !K || !point) {
+	if (!data->k || !K) {
 		wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation "
 			   "fail");
 		goto fin;
 	}
 
 	/* element, x then y, followed by scalar */
-	data->server_element = crypto_ec_point_from_bin(data->grp->group, ptr);
+	data->server_element = eap_pwd_get_element(data->grp, ptr);
 	if (!data->server_element) {
 		wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element "
 			   "fail");
 		goto fin;
 	}
 	ptr += prime_len * 2;
-	data->server_scalar = crypto_bignum_init_set(ptr, order_len);
+	data->server_scalar = eap_pwd_get_scalar(data->grp, ptr);
 	if (!data->server_scalar) {
 		wpa_printf(MSG_INFO,
 			   "EAP-PWD (peer): setting peer scalar fail");
 		goto fin;
 	}
 
-	/* check to ensure server's element is not in a small sub-group */
-	if (!crypto_bignum_is_one(cofactor)) {
-		if (crypto_ec_point_mul(data->grp->group, data->server_element,
-					cofactor, point) < 0) {
-			wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
-				   "server element by order!\n");
-			goto fin;
-		}
-		if (crypto_ec_point_is_at_infinity(data->grp->group, point)) {
-			wpa_printf(MSG_INFO, "EAP-PWD (peer): server element "
-				   "is at infinity!\n");
-			goto fin;
-		}
-	}
-
 	/* compute the shared key, k */
 	if (crypto_ec_point_mul(data->grp->group, data->grp->pwe,
 				data->server_scalar, K) < 0 ||
@@ -649,12 +623,12 @@
 	}
 
 	/* now do the response */
-	scalar = os_zalloc(order_len);
-	element = os_zalloc(prime_len * 2);
-	if (!scalar || !element) {
-		wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail");
+	data->outbuf = wpabuf_alloc(2 * prime_len + order_len);
+	if (data->outbuf == NULL)
 		goto fin;
-	}
+	/* We send the element as (x,y) followed by the scalar */
+	element = wpabuf_put(data->outbuf, 2 * prime_len);
+	scalar = wpabuf_put(data->outbuf, order_len);
 
 	/*
 	 * bignums occupy as little memory as possible so one that is
@@ -668,21 +642,10 @@
 		goto fin;
 	}
 
-	data->outbuf = wpabuf_alloc(order_len + 2 * prime_len);
-	if (data->outbuf == NULL)
-		goto fin;
-
-	/* we send the element as (x,y) follwed by the scalar */
-	wpabuf_put_data(data->outbuf, element, 2 * prime_len);
-	wpabuf_put_data(data->outbuf, scalar, order_len);
-
 fin:
-	os_free(scalar);
-	os_free(element);
 	crypto_bignum_deinit(mask, 1);
 	crypto_bignum_deinit(cofactor, 1);
 	crypto_ec_point_deinit(K, 1);
-	crypto_ec_point_deinit(point, 1);
 	if (data->outbuf == NULL)
 		eap_pwd_state(data, FAILURE);
 	else
diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c
index cb74702..ffea9d2 100644
--- a/src/eap_peer/eap_tls.c
+++ b/src/eap_peer/eap_tls.c
@@ -198,6 +198,7 @@
 
 	eap_tls_free_key(data);
 	data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, label,
+						 NULL, 0,
 						 EAP_TLS_KEY_LEN +
 						 EAP_EMSK_LEN);
 	if (data->key_data) {
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
index 7dbd364..cb94c45 100644
--- a/src/eap_peer/eap_tls_common.c
+++ b/src/eap_peer/eap_tls_common.c
@@ -108,14 +108,15 @@
 static void eap_tls_params_from_conf1(struct tls_connection_params *params,
 				      struct eap_peer_config *config)
 {
-	params->ca_cert = (char *) config->ca_cert;
-	params->ca_path = (char *) config->ca_path;
-	params->client_cert = (char *) config->client_cert;
-	params->private_key = (char *) config->private_key;
-	params->private_key_passwd = (char *) config->private_key_passwd;
-	params->dh_file = (char *) config->dh_file;
-	params->subject_match = (char *) config->subject_match;
-	params->altsubject_match = (char *) config->altsubject_match;
+	params->ca_cert = config->ca_cert;
+	params->ca_path = config->ca_path;
+	params->client_cert = config->client_cert;
+	params->private_key = config->private_key;
+	params->private_key_passwd = config->private_key_passwd;
+	params->dh_file = config->dh_file;
+	params->subject_match = config->subject_match;
+	params->altsubject_match = config->altsubject_match;
+	params->check_cert_subject = config->check_cert_subject;
 	params->suffix_match = config->domain_suffix_match;
 	params->domain_match = config->domain_match;
 	params->engine = config->engine;
@@ -131,14 +132,15 @@
 static void eap_tls_params_from_conf2(struct tls_connection_params *params,
 				      struct eap_peer_config *config)
 {
-	params->ca_cert = (char *) config->ca_cert2;
-	params->ca_path = (char *) config->ca_path2;
-	params->client_cert = (char *) config->client_cert2;
-	params->private_key = (char *) config->private_key2;
-	params->private_key_passwd = (char *) config->private_key2_passwd;
-	params->dh_file = (char *) config->dh_file2;
-	params->subject_match = (char *) config->subject_match2;
-	params->altsubject_match = (char *) config->altsubject_match2;
+	params->ca_cert = config->ca_cert2;
+	params->ca_path = config->ca_path2;
+	params->client_cert = config->client_cert2;
+	params->private_key = config->private_key2;
+	params->private_key_passwd = config->private_key2_passwd;
+	params->dh_file = config->dh_file2;
+	params->subject_match = config->subject_match2;
+	params->altsubject_match = config->altsubject_match2;
+	params->check_cert_subject = config->check_cert_subject2;
 	params->suffix_match = config->domain_suffix_match2;
 	params->domain_match = config->domain_match2;
 	params->engine = config->engine2;
@@ -347,6 +349,8 @@
  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
  * @data: Data for TLS processing
  * @label: Label string for deriving the keys, e.g., "client EAP encryption"
+ * @context: Optional extra upper-layer context (max len 2^16)
+ * @context_len: The length of the context value
  * @len: Length of the key material to generate (usually 64 for MSK)
  * Returns: Pointer to allocated key on success or %NULL on failure
  *
@@ -355,9 +359,12 @@
  * different label to bind the key usage into the generated material.
  *
  * The caller is responsible for freeing the returned buffer.
+ *
+ * Note: To provide the RFC 5705 context, the context variable must be non-NULL.
  */
 u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
-			     const char *label, size_t len)
+			     const char *label, const u8 *context,
+			     size_t context_len, size_t len)
 {
 	u8 *out;
 
@@ -365,8 +372,8 @@
 	if (out == NULL)
 		return NULL;
 
-	if (tls_connection_export_key(data->ssl_ctx, data->conn, label, out,
-				      len)) {
+	if (tls_connection_export_key(data->ssl_ctx, data->conn, label,
+				      context, context_len, out, len)) {
 		os_free(out);
 		return NULL;
 	}
@@ -407,7 +414,7 @@
 		if (!id)
 			return NULL;
 		method_id = eap_peer_tls_derive_key(
-			sm, data, "EXPORTER_EAP_TLS_Method-Id", 64);
+			sm, data, "EXPORTER_EAP_TLS_Method-Id", NULL, 0, 64);
 		if (!method_id) {
 			os_free(id);
 			return NULL;
diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h
index 306e6a9..5f82529 100644
--- a/src/eap_peer/eap_tls_common.h
+++ b/src/eap_peer/eap_tls_common.h
@@ -99,7 +99,8 @@
 			  struct eap_peer_config *config, u8 eap_type);
 void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
 u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
-			     const char *label, size_t len);
+			     const char *label, const u8 *context,
+			     size_t context_len, size_t len);
 u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm,
 				    struct eap_ssl_data *data, u8 eap_type,
 				    size_t *len);
diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c
index 5d26701..1c8dbe2 100644
--- a/src/eap_peer/eap_ttls.c
+++ b/src/eap_peer/eap_ttls.c
@@ -271,6 +271,7 @@
 	eap_ttls_free_key(data);
 	data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
 						 "ttls keying material",
+						 NULL, 0,
 						 EAP_TLS_KEY_LEN +
 						 EAP_EMSK_LEN);
 	if (!data->key_data) {
@@ -303,7 +304,8 @@
 static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
 					struct eap_ttls_data *data, size_t len)
 {
-	return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len);
+	return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge",
+				       NULL, 0, len);
 }
 #endif /* CONFIG_FIPS */
 
diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c
index d140c88..92d5a02 100644
--- a/src/eap_peer/eap_wsc.c
+++ b/src/eap_peer/eap_wsc.c
@@ -255,6 +255,9 @@
 		cfg.new_ap_settings = &new_ap_settings;
 	}
 
+	if (os_strstr(phase1, "multi_ap=1"))
+		cfg.multi_ap_backhaul_sta = 1;
+
 	data->wps = wps_init(&cfg);
 	if (data->wps == NULL) {
 		os_free(data);
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index 45e1212..b130368 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -161,5 +161,6 @@
 				   const u8 *username, size_t username_len,
 				   const u8 *challenge, const u8 *response);
 void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len);
+void eap_user_free(struct eap_user *user);
 
 #endif /* EAP_H */
diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c
index b33f632..e8b36e1 100644
--- a/src/eap_server/eap_server.c
+++ b/src/eap_server/eap_server.c
@@ -25,9 +25,6 @@
 
 #define EAP_MAX_AUTH_ROUNDS 50
 
-static void eap_user_free(struct eap_user *user);
-
-
 /* EAP state machines are described in RFC 4137 */
 
 static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
@@ -1814,7 +1811,7 @@
 }
 
 
-static void eap_user_free(struct eap_user *user)
+void eap_user_free(struct eap_user *user)
 {
 	if (user == NULL)
 		return;
diff --git a/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c
index 3d334a0..92c0e5e 100644
--- a/src/eap_server/eap_server_peap.c
+++ b/src/eap_server/eap_server_peap.c
@@ -331,7 +331,7 @@
 	 * phase 1 of PEAP (based on TLS).
 	 */
 	tk = eap_server_tls_derive_key(sm, &data->ssl, "client EAP encryption",
-				       EAP_TLS_KEY_LEN);
+				       NULL, 0, EAP_TLS_KEY_LEN);
 	if (tk == NULL)
 		return -1;
 	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60);
@@ -1333,7 +1333,7 @@
 
 	/* TODO: PEAPv1 - different label in some cases */
 	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
-					       "client EAP encryption",
+					       "client EAP encryption", NULL, 0,
 					       EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
 	if (eapKeyData) {
 		os_memset(eapKeyData + EAP_TLS_KEY_LEN, 0, EAP_EMSK_LEN);
@@ -1363,7 +1363,7 @@
 
 	/* TODO: PEAPv1 - different label in some cases */
 	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
-					       "client EAP encryption",
+					       "client EAP encryption", NULL, 0,
 					       EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
 	if (eapKeyData) {
 		emsk = os_memdup(eapKeyData + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
index d0fa54a..9799c81 100644
--- a/src/eap_server/eap_server_pwd.c
+++ b/src/eap_server/eap_server_pwd.c
@@ -236,7 +236,7 @@
 				     struct eap_pwd_data *data, u8 id)
 {
 	struct crypto_bignum *mask = NULL;
-	u8 *scalar = NULL, *element = NULL;
+	u8 *scalar, *element;
 	size_t prime_len, order_len;
 
 	wpa_printf(MSG_DEBUG, "EAP-pwd: Commit/Request");
@@ -261,18 +261,9 @@
 		goto fin;
 	}
 
-	if (crypto_bignum_rand(data->private_value,
-			       crypto_ec_get_order(data->grp->group)) < 0 ||
-	    crypto_bignum_rand(mask,
-			       crypto_ec_get_order(data->grp->group)) < 0 ||
-	    crypto_bignum_add(data->private_value, mask, data->my_scalar) < 0 ||
-	    crypto_bignum_mod(data->my_scalar,
-			      crypto_ec_get_order(data->grp->group),
-			      data->my_scalar) < 0) {
-		wpa_printf(MSG_INFO,
-			   "EAP-pwd (server): unable to get randomness");
+	if (eap_pwd_get_rand_mask(data->grp, data->private_value, mask,
+				  data->my_scalar) < 0)
 		goto fin;
-	}
 
 	if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, mask,
 				data->my_element) < 0) {
@@ -288,22 +279,6 @@
 		goto fin;
 	}
 
-	scalar = os_malloc(order_len);
-	element = os_malloc(prime_len * 2);
-	if (!scalar || !element) {
-		wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail");
-		goto fin;
-	}
-
-	if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element,
-				   element + prime_len) < 0) {
-		wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment "
-			   "fail");
-		goto fin;
-	}
-
-	crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len);
-
 	data->outbuf = wpabuf_alloc(2 * prime_len + order_len +
 				    (data->salt ? 1 + data->salt_len : 0));
 	if (data->outbuf == NULL)
@@ -316,13 +291,18 @@
 	}
 
 	/* We send the element as (x,y) followed by the scalar */
-	wpabuf_put_data(data->outbuf, element, 2 * prime_len);
-	wpabuf_put_data(data->outbuf, scalar, order_len);
+	element = wpabuf_put(data->outbuf, 2 * prime_len);
+	scalar = wpabuf_put(data->outbuf, order_len);
+	crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len);
+	if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element,
+				   element + prime_len) < 0) {
+		wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment "
+			   "fail");
+		goto fin;
+	}
 
 fin:
 	crypto_bignum_deinit(mask, 1);
-	os_free(scalar);
-	os_free(element);
 	if (data->outbuf == NULL)
 		eap_pwd_state(data, FAILURE);
 }
@@ -669,7 +649,7 @@
 {
 	const u8 *ptr;
 	struct crypto_bignum *cofactor = NULL;
-	struct crypto_ec_point *K = NULL, *point = NULL;
+	struct crypto_ec_point *K = NULL;
 	int res = 0;
 	size_t prime_len, order_len;
 
@@ -688,9 +668,8 @@
 
 	data->k = crypto_bignum_init();
 	cofactor = crypto_bignum_init();
-	point = crypto_ec_point_init(data->grp->group);
 	K = crypto_ec_point_init(data->grp->group);
-	if (!data->k || !cofactor || !point || !K) {
+	if (!data->k || !cofactor || !K) {
 		wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation "
 			   "fail");
 		goto fin;
@@ -704,33 +683,27 @@
 
 	/* element, x then y, followed by scalar */
 	ptr = payload;
-	data->peer_element = crypto_ec_point_from_bin(data->grp->group, ptr);
+	data->peer_element = eap_pwd_get_element(data->grp, ptr);
 	if (!data->peer_element) {
 		wpa_printf(MSG_INFO, "EAP-PWD (server): setting peer element "
 			   "fail");
 		goto fin;
 	}
 	ptr += prime_len * 2;
-	data->peer_scalar = crypto_bignum_init_set(ptr, order_len);
+	data->peer_scalar = eap_pwd_get_scalar(data->grp, ptr);
 	if (!data->peer_scalar) {
 		wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation "
 			   "fail");
 		goto fin;
 	}
 
-	/* check to ensure peer's element is not in a small sub-group */
-	if (!crypto_bignum_is_one(cofactor)) {
-		if (crypto_ec_point_mul(data->grp->group, data->peer_element,
-					cofactor, point) != 0) {
-			wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
-				   "multiply peer element by order");
-			goto fin;
-		}
-		if (crypto_ec_point_is_at_infinity(data->grp->group, point)) {
-			wpa_printf(MSG_INFO, "EAP-PWD (server): peer element "
-				   "is at infinity!\n");
-			goto fin;
-		}
+	/* detect reflection attacks */
+	if (crypto_bignum_cmp(data->my_scalar, data->peer_scalar) == 0 ||
+	    crypto_ec_point_cmp(data->grp->group, data->my_element,
+				data->peer_element) == 0) {
+		wpa_printf(MSG_INFO,
+			   "EAP-PWD (server): detected reflection attack!");
+		goto fin;
 	}
 
 	/* compute the shared key, k */
@@ -775,7 +748,6 @@
 
 fin:
 	crypto_ec_point_deinit(K, 1);
-	crypto_ec_point_deinit(point, 1);
 	crypto_bignum_deinit(cofactor, 1);
 
 	if (res)
diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c
index 13d2349..357e72a 100644
--- a/src/eap_server/eap_server_tls.c
+++ b/src/eap_server/eap_server_tls.c
@@ -331,6 +331,7 @@
 	else
 		label = "client EAP encryption";
 	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label,
+					       NULL, 0,
 					       EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
 	if (eapKeyData) {
 		*len = EAP_TLS_KEY_LEN;
@@ -359,6 +360,7 @@
 	else
 		label = "client EAP encryption";
 	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label,
+					       NULL, 0,
 					       EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
 	if (eapKeyData) {
 		emsk = os_malloc(EAP_EMSK_LEN);
diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c
index 4ba7c24..0eca0ff 100644
--- a/src/eap_server/eap_server_tls_common.c
+++ b/src/eap_server/eap_server_tls_common.c
@@ -107,7 +107,8 @@
 
 
 u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
-			       const char *label, size_t len)
+			       const char *label, const u8 *context,
+			       size_t context_len, size_t len)
 {
 	u8 *out;
 
@@ -115,8 +116,8 @@
 	if (out == NULL)
 		return NULL;
 
-	if (tls_connection_export_key(sm->ssl_ctx, data->conn, label, out,
-				      len)) {
+	if (tls_connection_export_key(sm->ssl_ctx, data->conn, label,
+				      context, context_len, out, len)) {
 		os_free(out);
 		return NULL;
 	}
@@ -157,7 +158,7 @@
 		if (!id)
 			return NULL;
 		method_id = eap_server_tls_derive_key(
-			sm, data, "EXPORTER_EAP_TLS_Method-Id", 64);
+			sm, data, "EXPORTER_EAP_TLS_Method-Id", NULL, 0, 64);
 		if (!method_id) {
 			os_free(id);
 			return NULL;
diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c
index b14996b..52bff8a 100644
--- a/src/eap_server/eap_server_ttls.c
+++ b/src/eap_server/eap_server_ttls.c
@@ -332,7 +332,7 @@
 					struct eap_ttls_data *data, size_t len)
 {
 	return eap_server_tls_derive_key(sm, &data->ssl, "ttls challenge",
-					 len);
+					 NULL, 0, len);
 }
 
 
@@ -1268,7 +1268,7 @@
 		return NULL;
 
 	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
-					       "ttls keying material",
+					       "ttls keying material", NULL, 0,
 					       EAP_TLS_KEY_LEN);
 	if (eapKeyData) {
 		*len = EAP_TLS_KEY_LEN;
@@ -1310,7 +1310,7 @@
 		return NULL;
 
 	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
-					       "ttls keying material",
+					       "ttls keying material", NULL, 0,
 					       EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
 	if (eapKeyData) {
 		emsk = os_malloc(EAP_EMSK_LEN);
diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h
index 31f6e72..0b04983 100644
--- a/src/eap_server/eap_tls_common.h
+++ b/src/eap_server/eap_tls_common.h
@@ -78,7 +78,8 @@
 			    int verify_peer, int eap_type);
 void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
 u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
-			       const char *label, size_t len);
+			       const char *label, const u8 *context,
+			       size_t context_len, size_t len);
 u8 * eap_server_tls_derive_session_id(struct eap_sm *sm,
 				      struct eap_ssl_data *data, u8 eap_type,
 				      size_t *len);
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index 9f029b0..a0f27fd 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -189,8 +189,9 @@
 	}
 
 	if (sm->authWhile | sm->heldWhile | sm->startWhen | sm->idleWhile) {
-		eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx,
-				       sm);
+		if (eloop_register_timeout(1, 0, eapol_port_timers_tick,
+					   eloop_ctx, sm) < 0)
+			sm->timer_tick_enabled = 0;
 	} else {
 		wpa_printf(MSG_DEBUG, "EAPOL: disable timer tick");
 		sm->timer_tick_enabled = 0;
@@ -204,9 +205,9 @@
 	if (sm->timer_tick_enabled)
 		return;
 	wpa_printf(MSG_DEBUG, "EAPOL: enable timer tick");
-	sm->timer_tick_enabled = 1;
 	eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
-	eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
+	if (eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm) == 0)
+		sm->timer_tick_enabled = 1;
 }
 
 
@@ -2141,8 +2142,8 @@
 	sm->initialize = FALSE;
 	eapol_sm_step(sm);
 
-	sm->timer_tick_enabled = 1;
-	eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
+	if (eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm) == 0)
+		sm->timer_tick_enabled = 1;
 
 	return sm;
 }
diff --git a/src/fst/fst.h b/src/fst/fst.h
index 0c0e435..2967491 100644
--- a/src/fst/fst.h
+++ b/src/fst/fst.h
@@ -19,10 +19,18 @@
 #define US_IN_MS           1000
 #define LLT_UNIT_US        32 /* See 10.32.2.2  Transitioning between states */
 
-#define FST_LLT_MS_TO_VAL(m) (((u32) (m)) * US_IN_MS / LLT_UNIT_US)
-#define FST_LLT_VAL_TO_MS(v) (((u32) (v)) * LLT_UNIT_US / US_IN_MS)
-
-#define FST_MAX_LLT_MS       FST_LLT_VAL_TO_MS(-1)
+/*
+ * These were originally
+ * #define FST_LLT_MS_TO_VAL(m) (((u32) (m)) * US_IN_MS / LLT_UNIT_US)
+ * #define FST_LLT_VAL_TO_MS(v) (((u32) (v)) * LLT_UNIT_US / US_IN_MS)
+ * #define FST_MAX_LLT_MS FST_LLT_VAL_TO_MS(-1)
+ * but those can overflow 32-bit unsigned integer, so use alternative defines
+ * to avoid undefined behavior with such overflow.
+ * LLT_UNIT_US/US_IN_MS = 32/1000 = 4/125
+ */
+#define FST_LLT_MS_TO_VAL(m) (((u32) (m)) * 125 / 4)
+#define FST_LLT_VAL_TO_MS(v) (((u32) (v)) * 4 / 125)
+#define FST_MAX_LLT_MS       (((u32) -1) / 4)
 #define FST_MAX_PRIO_VALUE   ((u8) -1)
 #define FST_MAX_GROUP_ID_LEN IFNAMSIZ
 
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index 5115239..6107b14 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -1076,7 +1076,7 @@
 				      p2p->after_scan_tx->bssid,
 				      (u8 *) (p2p->after_scan_tx + 1),
 				      p2p->after_scan_tx->len,
-				      p2p->after_scan_tx->wait_time);
+				      p2p->after_scan_tx->wait_time, NULL);
 		os_free(p2p->after_scan_tx);
 		p2p->after_scan_tx = NULL;
 		return 1;
@@ -2466,7 +2466,7 @@
 	if (msg.wps_attributes &&
 	    !p2p_match_dev_type(p2p, msg.wps_attributes)) {
 		/* No match with Requested Device Type */
-		p2p_dbg(p2p, "Probe Req requestred Device Type did not match - ignore it");
+		p2p_dbg(p2p, "Probe Req requested Device Type did not match - ignore it");
 		p2p_parse_free(&msg);
 		return P2P_PREQ_NOT_PROCESSED;
 	}
@@ -4978,6 +4978,8 @@
 		    const u8 *src, const u8 *bssid, const u8 *buf,
 		    size_t len, unsigned int wait_time)
 {
+	int res, scheduled;
+
 	if (p2p->p2p_scan_running) {
 		p2p_dbg(p2p, "Delay Action frame TX until p2p_scan completes");
 		if (p2p->after_scan_tx) {
@@ -4998,8 +5000,16 @@
 		return 0;
 	}
 
-	return p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid,
-				     buf, len, wait_time);
+	res = p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid,
+				    buf, len, wait_time, &scheduled);
+	if (res == 0 && scheduled && p2p->in_listen && freq > 0 &&
+	    (unsigned int) p2p->drv_in_listen != freq) {
+		p2p_dbg(p2p,
+			"Stop listen on %d MHz to allow a frame to be sent immediately on %d MHz",
+			p2p->drv_in_listen, freq);
+		p2p_stop_listen_for_freq(p2p, freq);
+	}
+	return res;
 }
 
 
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index f2969ee..425b037 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -665,6 +665,8 @@
 	 * @buf: Frame body (starting from Category field)
 	 * @len: Length of buf in octets
 	 * @wait_time: How many msec to wait for a response frame
+	 * @scheduled: Return value indicating whether the transmissions was
+	 *	scheduled to happen once the radio is available
 	 * Returns: 0 on success, -1 on failure
 	 *
 	 * The Action frame may not be transmitted immediately and the status
@@ -675,7 +677,7 @@
 	 */
 	int (*send_action)(void *ctx, unsigned int freq, const u8 *dst,
 			   const u8 *src, const u8 *bssid, const u8 *buf,
-			   size_t len, unsigned int wait_time);
+			   size_t len, unsigned int wait_time, int *scheduled);
 
 	/**
 	 * send_action_done - Notify that Action frame sequence was completed
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index 2882c6a..63eb2e8 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -802,7 +802,7 @@
 		wpabuf_put_be16(buf, p2p->cfg->config_methods);
 	}
 
-	if (wps_build_wfa_ext(buf, 0, NULL, 0) < 0)
+	if (wps_build_wfa_ext(buf, 0, NULL, 0, 0) < 0)
 		return -1;
 
 	if (all_attr && p2p->cfg->num_sec_dev_types) {
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index 16c28a0..aa18af6 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -941,7 +941,8 @@
 	if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, m->addr,
 				  group->cfg->interface_addr,
 				  group->cfg->interface_addr,
-				  wpabuf_head(req), wpabuf_len(req), 200) < 0)
+				  wpabuf_head(req), wpabuf_len(req), 200, NULL)
+	    < 0)
 	{
 		p2p_dbg(p2p, "Failed to send Action frame");
 	}
diff --git a/src/pae/ieee802_1x_kay_i.h b/src/pae/ieee802_1x_kay_i.h
index 1d1589c..f9cd3f4 100644
--- a/src/pae/ieee802_1x_kay_i.h
+++ b/src/pae/ieee802_1x_kay_i.h
@@ -39,7 +39,7 @@
 struct ieee802_1x_mka_peer_id {
 	u8 mi[MI_LEN];
 	be32 mn;
-};
+} STRUCT_PACKED;
 
 struct ieee802_1x_kay_peer {
 	struct ieee802_1x_mka_sci sci;
diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
index aa78cba..b621ada 100644
--- a/src/radius/radius_server.c
+++ b/src/radius/radius_server.c
@@ -1,6 +1,6 @@
 /*
  * RADIUS authentication server
- * Copyright (c) 2005-2009, 2011-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2009, 2011-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -676,6 +676,23 @@
 }
 
 
+#ifdef CONFIG_ERP
+static struct eap_server_erp_key *
+radius_server_erp_find_key(struct radius_server_data *data, const char *keyname)
+{
+	struct eap_server_erp_key *erp;
+
+	dl_list_for_each(erp, &data->erp_keys, struct eap_server_erp_key,
+			 list) {
+		if (os_strcmp(erp->keyname_nai, keyname) == 0)
+			return erp;
+	}
+
+	return NULL;
+}
+#endif /* CONFIG_ERP */
+
+
 static struct radius_session *
 radius_server_get_new_session(struct radius_server_data *data,
 			      struct radius_client *client,
@@ -686,7 +703,7 @@
 	int res;
 	struct radius_session *sess;
 	struct eap_config eap_conf;
-	struct eap_user tmp;
+	struct eap_user *tmp;
 
 	RADIUS_DEBUG("Creating a new session");
 
@@ -697,12 +714,27 @@
 	}
 	RADIUS_DUMP_ASCII("User-Name", user, user_len);
 
-	os_memset(&tmp, 0, sizeof(tmp));
-	res = data->get_eap_user(data->conf_ctx, user, user_len, 0, &tmp);
-	bin_clear_free(tmp.password, tmp.password_len);
+	tmp = os_zalloc(sizeof(*tmp));
+	if (!tmp)
+		return NULL;
 
+	res = data->get_eap_user(data->conf_ctx, user, user_len, 0, tmp);
+#ifdef CONFIG_ERP
+	if (res != 0 && data->erp) {
+		char *username;
+
+		username = os_zalloc(user_len + 1);
+		if (username) {
+			os_memcpy(username, user, user_len);
+			if (radius_server_erp_find_key(data, username))
+				res = 0;
+			os_free(username);
+		}
+	}
+#endif /* CONFIG_ERP */
 	if (res != 0) {
 		RADIUS_DEBUG("User-Name not found from user database");
+		eap_user_free(tmp);
 		return NULL;
 	}
 
@@ -710,10 +742,12 @@
 	sess = radius_server_new_session(data, client);
 	if (sess == NULL) {
 		RADIUS_DEBUG("Failed to create a new session");
+		eap_user_free(tmp);
 		return NULL;
 	}
-	sess->accept_attr = tmp.accept_attr;
-	sess->macacl = tmp.macacl;
+	sess->accept_attr = tmp->accept_attr;
+	sess->macacl = tmp->macacl;
+	eap_user_free(tmp);
 
 	sess->username = os_malloc(user_len * 4 + 1);
 	if (sess->username == NULL) {
@@ -2251,7 +2285,7 @@
 			entry->addr.s_addr = addr.s_addr;
 			val = 0;
 			for (i = 0; i < mask; i++)
-				val |= 1 << (31 - i);
+				val |= 1U << (31 - i);
 			entry->mask.s_addr = htonl(val);
 		}
 #ifdef CONFIG_IPV6
@@ -2702,15 +2736,8 @@
 {
 	struct radius_session *sess = ctx;
 	struct radius_server_data *data = sess->server;
-	struct eap_server_erp_key *erp;
 
-	dl_list_for_each(erp, &data->erp_keys, struct eap_server_erp_key,
-			 list) {
-		if (os_strcmp(erp->keyname_nai, keyname) == 0)
-			return erp;
-	}
-
-	return NULL;
+	return radius_server_erp_find_key(data, keyname);
 }
 
 
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index fdd5220..d720f7b 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -263,7 +263,8 @@
 	}
 	pmksa->pmksa_count++;
 	wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR
-		   " network_ctx=%p", MAC2STR(entry->aa), entry->network_ctx);
+		   " network_ctx=%p akmp=0x%x", MAC2STR(entry->aa),
+		   entry->network_ctx, entry->akmp);
 	wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa, entry->pmkid,
 			 entry->fils_cache_id_set ? entry->fils_cache_id : NULL,
 			 entry->pmk, entry->pmk_len);
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index 345b0c8..704c95e 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -1594,7 +1594,7 @@
 		peer->supp_rates, sizeof(peer->supp_rates),
 		kde->supp_rates + 2, kde->supp_rates_len - 2,
 		kde->ext_supp_rates ? kde->ext_supp_rates + 2 : NULL,
-		kde->ext_supp_rates_len - 2);
+		kde->ext_supp_rates ? kde->ext_supp_rates_len - 2 : 0);
 	return 0;
 }
 
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 52aa766..a7adbad 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -534,15 +534,25 @@
 static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr,
 			  const struct wpa_eapol_key *key, struct wpa_ptk *ptk)
 {
+	const u8 *z = NULL;
+	size_t z_len = 0;
+
 #ifdef CONFIG_IEEE80211R
 	if (wpa_key_mgmt_ft(sm->key_mgmt))
 		return wpa_derive_ptk_ft(sm, src_addr, key, ptk);
 #endif /* CONFIG_IEEE80211R */
 
+#ifdef CONFIG_DPP2
+	if (sm->key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_z) {
+		z = wpabuf_head(sm->dpp_z);
+		z_len = wpabuf_len(sm->dpp_z);
+	}
+#endif /* CONFIG_DPP2 */
+
 	return wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion",
 			      sm->own_addr, sm->bssid, sm->snonce,
 			      key->key_nonce, ptk, sm->key_mgmt,
-			      sm->pairwise_cipher);
+			      sm->pairwise_cipher, z, z_len);
 }
 
 
@@ -2637,6 +2647,9 @@
 #ifdef CONFIG_OWE
 	crypto_ecdh_deinit(sm->owe_ecdh);
 #endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP2
+	wpabuf_clear_free(sm->dpp_z);
+#endif /* CONFIG_DPP2 */
 	os_free(sm);
 }
 
@@ -3988,11 +4001,13 @@
 		   MAC2STR(sm->r1kh_id));
 	pos = wpabuf_put(buf, WPA_PMK_NAME_LEN);
 	if (wpa_derive_pmk_r1_name(sm->pmk_r0_name, sm->r1kh_id, sm->own_addr,
-				   pos, use_sha384) < 0) {
+				   sm->pmk_r1_name, use_sha384) < 0) {
 		wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMKR1Name");
 		return -1;
 	}
-	wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", pos, WPA_PMK_NAME_LEN);
+	wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", sm->pmk_r1_name,
+		    WPA_PMK_NAME_LEN);
+	os_memcpy(pos, sm->pmk_r1_name, WPA_PMK_NAME_LEN);
 
 #ifdef CONFIG_IEEE80211W
 	if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
@@ -4295,6 +4310,24 @@
 	}
 #endif /* CONFIG_OCV */
 
+#ifdef CONFIG_IEEE80211R
+	if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->fils_ft_ies) {
+		struct wpa_ie_data rsn;
+
+		/* Check that PMKR1Name derived by the AP matches */
+		if (!elems.rsn_ie ||
+		    wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+					 &rsn) < 0 ||
+		    !rsn.pmkid || rsn.num_pmkid != 1 ||
+		    os_memcmp(rsn.pmkid, sm->pmk_r1_name,
+			      WPA_PMK_NAME_LEN) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS+FT: No RSNE[PMKR1Name] match in AssocResp");
+			goto fail;
+		}
+	}
+#endif /* CONFIG_IEEE80211R */
+
 	/* Key Delivery */
 	if (!elems.key_delivery) {
 		wpa_printf(MSG_DEBUG, "FILS: No Key Delivery element");
@@ -4616,3 +4649,14 @@
 	}
 #endif /* CONFIG_FILS */
 }
+
+
+#ifdef CONFIG_DPP2
+void wpa_sm_set_dpp_z(struct wpa_sm *sm, const struct wpabuf *z)
+{
+	if (sm) {
+		wpabuf_clear_free(sm->dpp_z);
+		sm->dpp_z = z ? wpabuf_dup(z) : NULL;
+	}
+}
+#endif /* CONFIG_DPP2 */
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 9eee383..8903f8e 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -465,5 +465,6 @@
 
 void wpa_sm_set_reset_fils_completed(struct wpa_sm *sm, int set);
 void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id);
+void wpa_sm_set_dpp_z(struct wpa_sm *sm, const struct wpabuf *z);
 
 #endif /* WPA_H */
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index e1a213b..5dc9f2e 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -168,6 +168,10 @@
 	struct crypto_ecdh *owe_ecdh;
 	u16 owe_group;
 #endif /* CONFIG_OWE */
+
+#ifdef CONFIG_DPP2
+	struct wpabuf *dpp_z;
+#endif /* CONFIG_DPP2 */
 };
 
 
diff --git a/src/tls/bignum.c b/src/tls/bignum.c
index f3baafe..1a87c82 100644
--- a/src/tls/bignum.c
+++ b/src/tls/bignum.c
@@ -119,10 +119,10 @@
 
 
 /**
- * bignum_cmd_d - Compare bignum to standard integer
+ * bignum_cmp_d - Compare bignum to standard integer
  * @a: Bignum from bignum_init()
  * @b: Small integer
- * Returns: 0 on success, -1 on failure
+ * Returns: -1 if a < b, 0 if a == b, 1 if a > b
  */
 int bignum_cmp_d(const struct bignum *a, unsigned long b)
 {
diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c
index 76e1974..a147a54 100644
--- a/src/tls/tlsv1_client.c
+++ b/src/tls/tlsv1_client.c
@@ -1,6 +1,6 @@
 /*
  * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
- * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -514,6 +514,8 @@
  * tlsv1_client_prf - Use TLS-PRF to derive keying material
  * @conn: TLSv1 client connection data from tlsv1_client_init()
  * @label: Label (e.g., description of the key) for PRF
+ * @context: Optional extra upper-layer context (max len 2^16)
+ * @context_len: The length of the context value
  * @server_random_first: seed is 0 = client_random|server_random,
  * 1 = server_random|client_random
  * @out: Buffer for output data from TLS-PRF
@@ -521,13 +523,26 @@
  * Returns: 0 on success, -1 on failure
  */
 int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
+		     const u8 *context, size_t context_len,
 		     int server_random_first, u8 *out, size_t out_len)
 {
-	u8 seed[2 * TLS_RANDOM_LEN];
+	u8 *seed, *pos;
+	size_t seed_len = 2 * TLS_RANDOM_LEN;
+	int res;
 
 	if (conn->state != ESTABLISHED)
 		return -1;
 
+	if (context_len > 65535)
+		return -1;
+
+	if (context)
+		seed_len += 2 + context_len;
+
+	seed = os_malloc(seed_len);
+	if (!seed)
+		return -1;
+
 	if (server_random_first) {
 		os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
 		os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random,
@@ -538,9 +553,18 @@
 			  TLS_RANDOM_LEN);
 	}
 
-	return tls_prf(conn->rl.tls_version,
-		       conn->master_secret, TLS_MASTER_SECRET_LEN,
-		       label, seed, 2 * TLS_RANDOM_LEN, out, out_len);
+	if (context) {
+		pos = seed + 2 * TLS_RANDOM_LEN;
+		WPA_PUT_BE16(pos, context_len);
+		pos += 2;
+		os_memcpy(pos, context, context_len);
+	}
+
+	res = tls_prf(conn->rl.tls_version,
+		      conn->master_secret, TLS_MASTER_SECRET_LEN,
+		      label, seed, seed_len, out, out_len);
+	os_free(seed);
+	return res;
 }
 
 
diff --git a/src/tls/tlsv1_client.h b/src/tls/tlsv1_client.h
index 40fa6c7..7fcc256 100644
--- a/src/tls/tlsv1_client.h
+++ b/src/tls/tlsv1_client.h
@@ -1,6 +1,6 @@
 /*
  * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -19,6 +19,7 @@
 void tlsv1_client_deinit(struct tlsv1_client *conn);
 int tlsv1_client_established(struct tlsv1_client *conn);
 int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
+		     const u8 *context, size_t context_len,
 		     int server_random_first, u8 *out, size_t out_len);
 u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
 			    const u8 *in_data, size_t in_len,
diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c
index 4759509..12dcc85 100644
--- a/src/tls/tlsv1_server.c
+++ b/src/tls/tlsv1_server.c
@@ -1,6 +1,6 @@
 /*
  * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246)
- * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -462,6 +462,8 @@
  * tlsv1_server_prf - Use TLS-PRF to derive keying material
  * @conn: TLSv1 server connection data from tlsv1_server_init()
  * @label: Label (e.g., description of the key) for PRF
+ * @context: Optional extra upper-layer context (max len 2^16)
+ * @context_len: The length of the context value
  * @server_random_first: seed is 0 = client_random|server_random,
  * 1 = server_random|client_random
  * @out: Buffer for output data from TLS-PRF
@@ -469,13 +471,26 @@
  * Returns: 0 on success, -1 on failure
  */
 int tlsv1_server_prf(struct tlsv1_server *conn, const char *label,
+		     const u8 *context, size_t context_len,
 		     int server_random_first, u8 *out, size_t out_len)
 {
-	u8 seed[2 * TLS_RANDOM_LEN];
+	u8 *seed, *pos;
+	size_t seed_len = 2 * TLS_RANDOM_LEN;
+	int res;
 
 	if (conn->state != ESTABLISHED)
 		return -1;
 
+	if (context_len > 65535)
+		return -1;
+
+	if (context)
+		seed_len += 2 + context_len;
+
+	seed = os_malloc(seed_len);
+	if (!seed)
+		return -1;
+
 	if (server_random_first) {
 		os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
 		os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random,
@@ -486,9 +501,18 @@
 			  TLS_RANDOM_LEN);
 	}
 
-	return tls_prf(conn->rl.tls_version,
-		       conn->master_secret, TLS_MASTER_SECRET_LEN,
-		       label, seed, 2 * TLS_RANDOM_LEN, out, out_len);
+	if (context) {
+		pos = seed + 2 * TLS_RANDOM_LEN;
+		WPA_PUT_BE16(pos, context_len);
+		pos += 2;
+		os_memcpy(pos, context, context_len);
+	}
+
+	res = tls_prf(conn->rl.tls_version,
+		      conn->master_secret, TLS_MASTER_SECRET_LEN,
+		      label, seed, seed_len, out, out_len);
+	os_free(seed);
+	return res;
 }
 
 
diff --git a/src/tls/tlsv1_server.h b/src/tls/tlsv1_server.h
index c3fd37e..c9c0875 100644
--- a/src/tls/tlsv1_server.h
+++ b/src/tls/tlsv1_server.h
@@ -1,6 +1,6 @@
 /*
  * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246)
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -19,6 +19,7 @@
 void tlsv1_server_deinit(struct tlsv1_server *conn);
 int tlsv1_server_established(struct tlsv1_server *conn);
 int tlsv1_server_prf(struct tlsv1_server *conn, const char *label,
+		     const u8 *context, size_t context_len,
 		     int server_random_first, u8 *out, size_t out_len);
 u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
 			    const u8 *in_data, size_t in_len, size_t *out_len);
diff --git a/src/utils/base64.c b/src/utils/base64.c
index 8eb4ba1..53a92f4 100644
--- a/src/utils/base64.c
+++ b/src/utils/base64.c
@@ -1,12 +1,13 @@
 /*
  * Base64 encoding/decoding (RFC1341)
- * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
  */
 
 #include "includes.h"
+#include <stdint.h>
 
 #include "os.h"
 #include "base64.h"
@@ -27,6 +28,8 @@
 	size_t olen;
 	int line_len;
 
+	if (len >= SIZE_MAX / 4)
+		return NULL;
 	olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
 	if (add_pad)
 		olen += olen / 72; /* line feeds */
diff --git a/src/utils/common.c b/src/utils/common.c
index 1eb3370..b9c8bfd 100644
--- a/src/utils/common.c
+++ b/src/utils/common.c
@@ -1,6 +1,6 @@
 /*
  * wpa_supplicant/hostapd / common helper functions, etc.
- * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -1073,7 +1073,8 @@
 		in_size--;
 	}
 
-	while (in_size--) {
+	while (in_size) {
+		in_size--;
 		if (res_size >= out_size)
 			return 0;
 
@@ -1084,8 +1085,9 @@
 			return res_size;
 
 		case '\\':
-			if (!in_size--)
+			if (!in_size)
 				return 0;
+			in_size--;
 			inp++;
 			/* fall through */
 
@@ -1116,7 +1118,8 @@
 	if (!in_size)
 		in_size = os_strlen(inp);
 
-	while (in_size--) {
+	while (in_size) {
+		in_size--;
 		if (res_size++ >= out_size)
 			return 0;
 
@@ -1221,3 +1224,28 @@
 		return 220;
 	return (rssi + 110) * 2;
 }
+
+
+char * get_param(const char *cmd, const char *param)
+{
+	const char *pos, *end;
+	char *val;
+	size_t len;
+
+	pos = os_strstr(cmd, param);
+	if (!pos)
+		return NULL;
+
+	pos += os_strlen(param);
+	end = os_strchr(pos, ' ');
+	if (end)
+		len = end - pos;
+	else
+		len = os_strlen(pos);
+	val = os_malloc(len + 1);
+	if (!val)
+		return NULL;
+	os_memcpy(val, pos, len);
+	val[len] = '\0';
+	return val;
+}
diff --git a/src/utils/common.h b/src/utils/common.h
index f824d00..792a30a 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -567,6 +567,7 @@
 int str_starts(const char *str, const char *start);
 
 u8 rssi_to_rcpi(int rssi);
+char * get_param(const char *cmd, const char *param);
 
 /*
  * gcc 4.4 ends up generating strict-aliasing warnings about some very common
diff --git a/src/utils/const_time.h b/src/utils/const_time.h
new file mode 100644
index 0000000..ab8f611
--- /dev/null
+++ b/src/utils/const_time.h
@@ -0,0 +1,191 @@
+/*
+ * Helper functions for constant time operations
+ * Copyright (c) 2019, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * These helper functions can be used to implement logic that needs to minimize
+ * externally visible differences in execution path by avoiding use of branches,
+ * avoiding early termination or other time differences, and forcing same memory
+ * access pattern regardless of values.
+ */
+
+#ifndef CONST_TIME_H
+#define CONST_TIME_H
+
+
+#if defined(__clang__)
+#define NO_UBSAN_UINT_OVERFLOW \
+	__attribute__((no_sanitize("unsigned-integer-overflow")))
+#else
+#define NO_UBSAN_UINT_OVERFLOW
+#endif
+
+
+/**
+ * const_time_fill_msb - Fill all bits with MSB value
+ * @val: Input value
+ * Returns: Value with all the bits set to the MSB of the input val
+ */
+static inline unsigned int const_time_fill_msb(unsigned int val)
+{
+	/* Move the MSB to LSB and multiple by -1 to fill in all bits. */
+	return (val >> (sizeof(val) * 8 - 1)) * ~0U;
+}
+
+
+/* Returns: -1 if val is zero; 0 if val is not zero */
+static inline unsigned int const_time_is_zero(unsigned int val)
+	NO_UBSAN_UINT_OVERFLOW
+{
+	/* Set MSB to 1 for 0 and fill rest of bits with the MSB value */
+	return const_time_fill_msb(~val & (val - 1));
+}
+
+
+/* Returns: -1 if a == b; 0 if a != b */
+static inline unsigned int const_time_eq(unsigned int a, unsigned int b)
+{
+	return const_time_is_zero(a ^ b);
+}
+
+
+/* Returns: -1 if a == b; 0 if a != b */
+static inline u8 const_time_eq_u8(unsigned int a, unsigned int b)
+{
+	return (u8) const_time_eq(a, b);
+}
+
+
+/**
+ * const_time_eq_bin - Constant time memory comparison
+ * @a: First buffer to compare
+ * @b: Second buffer to compare
+ * @len: Number of octets to compare
+ * Returns: -1 if buffers are equal, 0 if not
+ *
+ * This function is meant for comparing passwords or hash values where
+ * difference in execution time or memory access pattern could provide external
+ * observer information about the location of the difference in the memory
+ * buffers. The return value does not behave like memcmp(), i.e.,
+ * const_time_eq_bin() cannot be used to sort items into a defined order. Unlike
+ * memcmp(), the execution time of const_time_eq_bin() does not depend on the
+ * contents of the compared memory buffers, but only on the total compared
+ * length.
+ */
+static inline unsigned int const_time_eq_bin(const void *a, const void *b,
+					     size_t len)
+{
+	const u8 *aa = a;
+	const u8 *bb = b;
+	size_t i;
+	u8 res = 0;
+
+	for (i = 0; i < len; i++)
+		res |= aa[i] ^ bb[i];
+
+	return const_time_is_zero(res);
+}
+
+
+/**
+ * const_time_select - Constant time unsigned int selection
+ * @mask: 0 (false) or -1 (true) to identify which value to select
+ * @true_val: Value to select for the true case
+ * @false_val: Value to select for the false case
+ * Returns: true_val if mask == -1, false_val if mask == 0
+ */
+static inline unsigned int const_time_select(unsigned int mask,
+					     unsigned int true_val,
+					     unsigned int false_val)
+{
+	return (mask & true_val) | (~mask & false_val);
+}
+
+
+/**
+ * const_time_select_int - Constant time int selection
+ * @mask: 0 (false) or -1 (true) to identify which value to select
+ * @true_val: Value to select for the true case
+ * @false_val: Value to select for the false case
+ * Returns: true_val if mask == -1, false_val if mask == 0
+ */
+static inline int const_time_select_int(unsigned int mask, int true_val,
+					int false_val)
+{
+	return (int) const_time_select(mask, (unsigned int) true_val,
+				       (unsigned int) false_val);
+}
+
+
+/**
+ * const_time_select_u8 - Constant time u8 selection
+ * @mask: 0 (false) or -1 (true) to identify which value to select
+ * @true_val: Value to select for the true case
+ * @false_val: Value to select for the false case
+ * Returns: true_val if mask == -1, false_val if mask == 0
+ */
+static inline u8 const_time_select_u8(u8 mask, u8 true_val, u8 false_val)
+{
+	return (u8) const_time_select(mask, true_val, false_val);
+}
+
+
+/**
+ * const_time_select_s8 - Constant time s8 selection
+ * @mask: 0 (false) or -1 (true) to identify which value to select
+ * @true_val: Value to select for the true case
+ * @false_val: Value to select for the false case
+ * Returns: true_val if mask == -1, false_val if mask == 0
+ */
+static inline s8 const_time_select_s8(u8 mask, s8 true_val, s8 false_val)
+{
+	return (s8) const_time_select(mask, (unsigned int) true_val,
+				      (unsigned int) false_val);
+}
+
+
+/**
+ * const_time_select_bin - Constant time binary buffer selection copy
+ * @mask: 0 (false) or -1 (true) to identify which value to copy
+ * @true_val: Buffer to copy for the true case
+ * @false_val: Buffer to copy for the false case
+ * @len: Number of octets to copy
+ * @dst: Destination buffer for the copy
+ *
+ * This function copies the specified buffer into the destination buffer using
+ * operations with identical memory access pattern regardless of which buffer
+ * is being copied.
+ */
+static inline void const_time_select_bin(u8 mask, const u8 *true_val,
+					 const u8 *false_val, size_t len,
+					 u8 *dst)
+{
+	size_t i;
+
+	for (i = 0; i < len; i++)
+		dst[i] = const_time_select_u8(mask, true_val[i], false_val[i]);
+}
+
+
+static inline int const_time_memcmp(const void *a, const void *b, size_t len)
+{
+	const u8 *aa = a;
+	const u8 *bb = b;
+	int diff, res = 0;
+	unsigned int mask;
+
+	if (len == 0)
+		return 0;
+	do {
+		len--;
+		diff = (int) aa[len] - (int) bb[len];
+		mask = const_time_is_zero((unsigned int) diff);
+		res = const_time_select_int(mask, res, diff);
+	} while (len);
+
+	return res;
+}
+
+#endif /* CONST_TIME_H */
diff --git a/src/utils/list.h b/src/utils/list.h
index ee2f485..85aa5e3 100644
--- a/src/utils/list.h
+++ b/src/utils/list.h
@@ -1,6 +1,6 @@
 /*
  * Doubly-linked list
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -76,8 +76,8 @@
 	 dl_list_entry((list)->prev, type, member))
 
 #define dl_list_for_each(item, list, type, member) \
-	for (item = dl_list_entry((list)->next, type, member); \
-	     &item->member != (list); \
+	for (item = dl_list_first((list), type, member); \
+	     item && item != dl_list_entry((list), type, member); \
 	     item = dl_list_entry(item->member.next, type, member))
 
 #define dl_list_for_each_safe(item, n, list, type, member) \
diff --git a/src/utils/os_internal.c b/src/utils/os_internal.c
index ed6eb3c..474c8a3 100644
--- a/src/utils/os_internal.c
+++ b/src/utils/os_internal.c
@@ -430,22 +430,6 @@
 }
 
 
-char * os_strncpy(char *dest, const char *src, size_t n)
-{
-	char *d = dest;
-
-	while (n--) {
-		*d = *src;
-		if (*src == '\0')
-			break;
-		d++;
-		src++;
-	}
-
-	return dest;
-}
-
-
 size_t os_strlcpy(char *dest, const char *src, size_t siz)
 {
 	const char *s = src;
diff --git a/src/utils/os_none.c b/src/utils/os_none.c
index e74f206..5e0a3ad 100644
--- a/src/utils/os_none.c
+++ b/src/utils/os_none.c
@@ -218,12 +218,6 @@
 }
 
 
-char * os_strncpy(char *dest, const char *src, size_t n)
-{
-	return dest;
-}
-
-
 size_t os_strlcpy(char *dest, const char *src, size_t size)
 {
 	return 0;
diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c
index b79a70d..b56bab2 100644
--- a/src/utils/os_unix.c
+++ b/src/utils/os_unix.c
@@ -1,6 +1,6 @@
 /*
  * OS specific functions for UNIX/POSIX systems
- * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -546,7 +546,7 @@
 {
 	void *r = os_malloc(len);
 
-	if (r)
+	if (r && src)
 		os_memcpy(r, src, len);
 	return r;
 }
diff --git a/src/utils/utils_module_tests.c b/src/utils/utils_module_tests.c
index 1b8ff82..3af4fcd 100644
--- a/src/utils/utils_module_tests.c
+++ b/src/utils/utils_module_tests.c
@@ -9,6 +9,7 @@
 #include "utils/includes.h"
 
 #include "utils/common.h"
+#include "utils/const_time.h"
 #include "common/ieee802_11_defs.h"
 #include "utils/bitfield.h"
 #include "utils/ext_password.h"
@@ -919,6 +920,294 @@
 }
 
 
+static int const_time_tests(void)
+{
+	struct const_time_fill_msb_test {
+		unsigned int val;
+		unsigned int expected;
+	} const_time_fill_msb_tests[] = {
+		{ 0, 0 },
+		{ 1, 0 },
+		{ 2, 0 },
+		{ 1 << (sizeof(unsigned int) * 8 - 1), ~0 },
+		{ ~0 - 1, ~0 },
+		{ ~0, ~0 }
+	};
+	struct const_time_is_zero_test {
+		unsigned int val;
+		unsigned int expected;
+	} const_time_is_zero_tests[] = {
+		{ 0, ~0 },
+		{ 1, 0 },
+		{ 2, 0 },
+		{ 1 << (sizeof(unsigned int) * 8 - 1), 0 },
+		{ ~0 - 1, 0 },
+		{ ~0, 0 }
+	};
+	struct const_time_eq_test {
+		unsigned int a;
+		unsigned int b;
+		unsigned int expected;
+		unsigned int expected_u8;
+	} const_time_eq_tests[] = {
+		{ 0, 1, 0, 0 },
+		{ 1, 2, 0, 0 },
+		{ 1, 1, ~0, 0xff },
+		{ ~0, ~0, ~0, 0xff },
+		{ ~0, ~0 - 1, 0, 0 },
+		{ 0, 0, ~0, 0xff }
+	};
+	struct const_time_eq_bin_test {
+		u8 *a;
+		u8 *b;
+		size_t len;
+		unsigned int expected;
+	} const_time_eq_bin_tests[] = {
+		{ (u8 *) "", (u8 *) "", 0, ~0 },
+		{ (u8 *) "abcde", (u8 *) "abcde", 5, ~0 },
+		{ (u8 *) "abcde", (u8 *) "Abcde", 5, 0 },
+		{ (u8 *) "abcde", (u8 *) "aBcde", 5, 0 },
+		{ (u8 *) "abcde", (u8 *) "abCde", 5, 0 },
+		{ (u8 *) "abcde", (u8 *) "abcDe", 5, 0 },
+		{ (u8 *) "abcde", (u8 *) "abcdE", 5, 0 },
+		{ (u8 *) "\x00", (u8 *) "\x01", 1, 0 },
+		{ (u8 *) "\x00", (u8 *) "\x80", 1, 0 },
+		{ (u8 *) "\x00", (u8 *) "\x00", 1, ~0 }
+	};
+	struct const_time_select_test {
+		unsigned int mask;
+		unsigned int true_val;
+		unsigned int false_val;
+		unsigned int expected;
+	} const_time_select_tests[] = {
+		{ ~0, ~0, ~0, ~0 },
+		{ 0, ~0, ~0, ~0 },
+		{ ~0, ~0, 0, ~0 },
+		{ 0, ~0, 0, 0 },
+		{ ~0, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa },
+		{ 0, 0xaaaaaaaa, 0x55555555, 0x55555555 },
+		{ ~0, 3, 3, 3 },
+		{ 0, 3, 3, 3 },
+		{ ~0, 1, 2, 1 },
+		{ 0, 1, 2, 2 }
+	};
+	struct const_time_select_int_test {
+		unsigned int mask;
+		int true_val;
+		int false_val;
+		int expected;
+	} const_time_select_int_tests[] = {
+		{ ~0, -128, 127, -128 },
+		{ 0, -128, 127, 127 },
+		{ ~0, -2147483648, 2147483647, -2147483648 },
+		{ 0, -2147483648, 2147483647, 2147483647 },
+		{ ~0, 0, 0, 0 },
+		{ 0, 0, 0, 0 },
+		{ ~0, -1, 1, -1 },
+		{ 0, -1, 1, 1 }
+	};
+	struct const_time_select_u8_test {
+		u8 mask;
+		u8 true_val;
+		u8 false_val;
+		u8 expected;
+	} const_time_select_u8_tests[] = {
+		{ ~0, ~0, ~0, ~0 },
+		{ 0, ~0, ~0, ~0 },
+		{ ~0, ~0, 0, ~0 },
+		{ 0, ~0, 0, 0 },
+		{ ~0, 0xaa, 0x55, 0xaa },
+		{ 0, 0xaa, 0x55, 0x55 },
+		{ ~0, 1, 2, 1 },
+		{ 0, 1, 2, 2 }
+	};
+	struct const_time_select_s8_test {
+		u8 mask;
+		s8 true_val;
+		s8 false_val;
+		s8 expected;
+	} const_time_select_s8_tests[] = {
+		{ ~0, -128, 127, -128 },
+		{ 0, -128, 127, 127 },
+		{ ~0, 0, 0, 0 },
+		{ 0, 0, 0, 0 },
+		{ ~0, -1, 1, -1 },
+		{ 0, -1, 1, 1 }
+	};
+	struct const_time_select_bin_test {
+		u8 mask;
+		u8 *true_val;
+		u8 *false_val;
+		size_t len;
+		u8 *expected;
+	} const_time_select_bin_tests[] = {
+		{ ~0, (u8 *) "abcde", (u8 *) "ABCDE", 5, (u8 *) "abcde" },
+		{ 0, (u8 *) "abcde", (u8 *) "ABCDE", 5, (u8 *) "ABCDE" },
+		{ ~0, (u8 *) "", (u8 *) "", 0, (u8 *) "" },
+		{ 0, (u8 *) "", (u8 *) "", 0, (u8 *) "" }
+	};
+	struct const_time_memcmp_test {
+		char *a;
+		char *b;
+		size_t len;
+		int expected;
+	} const_time_memcmp_tests[] = {
+		{ "abcde", "abcde", 5, 0 },
+		{ "abcde", "bbcde", 5, -1 },
+		{ "bbcde", "abcde", 5, 1 },
+		{ "accde", "abcde", 5, 1 },
+		{ "abcee", "abcde", 5, 1 },
+		{ "abcdf", "abcde", 5, 1 },
+		{ "cbcde", "aXXXX", 5, 2 },
+		{ "a", "d", 1, -3 },
+		{ "", "", 0, 0 }
+	};
+	unsigned int i;
+	int ret = 0;
+
+	wpa_printf(MSG_INFO, "constant time tests");
+
+	for (i = 0; i < ARRAY_SIZE(const_time_fill_msb_tests); i++) {
+		struct const_time_fill_msb_test *test;
+
+		test = &const_time_fill_msb_tests[i];
+		if (const_time_fill_msb(test->val) != test->expected) {
+			wpa_printf(MSG_ERROR,
+				   "const_time_fill_msb(0x%x) test failed",
+				   test->val);
+			ret = -1;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(const_time_is_zero_tests); i++) {
+		struct const_time_is_zero_test *test;
+
+		test = &const_time_is_zero_tests[i];
+		if (const_time_is_zero(test->val) != test->expected) {
+			wpa_printf(MSG_ERROR,
+				   "const_time_is_zero(0x%x) test failed",
+				   test->val);
+			ret = -1;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(const_time_eq_tests); i++) {
+		struct const_time_eq_test *test;
+
+		test = &const_time_eq_tests[i];
+		if (const_time_eq(test->a, test->b) != test->expected) {
+			wpa_printf(MSG_ERROR,
+				   "const_time_eq(0x%x,0x%x) test failed",
+				   test->a, test->b);
+			ret = -1;
+		}
+		if (const_time_eq_u8(test->a, test->b) != test->expected_u8) {
+			wpa_printf(MSG_ERROR,
+				   "const_time_eq_u8(0x%x,0x%x) test failed",
+				   test->a, test->b);
+			ret = -1;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(const_time_eq_bin_tests); i++) {
+		struct const_time_eq_bin_test *test;
+
+		test = &const_time_eq_bin_tests[i];
+		if (const_time_eq_bin(test->a, test->b, test->len) !=
+		    test->expected) {
+			wpa_printf(MSG_ERROR,
+				   "const_time_eq_bin(len=%u) test failed",
+				   (unsigned int) test->len);
+			ret = -1;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(const_time_select_tests); i++) {
+		struct const_time_select_test *test;
+
+		test = &const_time_select_tests[i];
+		if (const_time_select(test->mask, test->true_val,
+				      test->false_val) != test->expected) {
+			wpa_printf(MSG_ERROR,
+				   "const_time_select(0x%x,0x%x,0x%x) test failed",
+				   test->mask, test->true_val, test->false_val);
+			ret = -1;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(const_time_select_int_tests); i++) {
+		struct const_time_select_int_test *test;
+
+		test = &const_time_select_int_tests[i];
+		if (const_time_select_int(test->mask, test->true_val,
+					  test->false_val) != test->expected) {
+			wpa_printf(MSG_ERROR,
+				   "const_time_select_int(0x%x,%d,%d) test failed",
+				   test->mask, test->true_val, test->false_val);
+			ret = -1;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(const_time_select_u8_tests); i++) {
+		struct const_time_select_u8_test *test;
+
+		test = &const_time_select_u8_tests[i];
+		if (const_time_select_u8(test->mask, test->true_val,
+					 test->false_val) != test->expected) {
+			wpa_printf(MSG_ERROR,
+				   "const_time_select_u8(0x%x,0x%x,0x%x) test failed",
+				   test->mask, test->true_val, test->false_val);
+			ret = -1;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(const_time_select_s8_tests); i++) {
+		struct const_time_select_s8_test *test;
+
+		test = &const_time_select_s8_tests[i];
+		if (const_time_select_s8(test->mask, test->true_val,
+					 test->false_val) != test->expected) {
+			wpa_printf(MSG_ERROR,
+				   "const_time_select_s8(0x%x,0x%x,0x%x) test failed",
+				   test->mask, test->true_val, test->false_val);
+			ret = -1;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(const_time_select_bin_tests); i++) {
+		struct const_time_select_bin_test *test;
+		u8 dst[100];
+
+		test = &const_time_select_bin_tests[i];
+		const_time_select_bin(test->mask, test->true_val,
+				      test->false_val, test->len, dst);
+		if (os_memcmp(dst, test->expected, test->len) != 0) {
+			wpa_printf(MSG_ERROR,
+				   "const_time_select_bin(0x%x,%u) test failed",
+				   test->mask, (unsigned int) test->len);
+			ret = -1;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(const_time_memcmp_tests); i++) {
+		struct const_time_memcmp_test *test;
+		int res;
+
+		test = &const_time_memcmp_tests[i];
+		res = const_time_memcmp(test->a, test->b, test->len);
+		if (res != test->expected) {
+			wpa_printf(MSG_ERROR,
+				   "const_time_memcmp(%s,%s,%d) test failed (%d != %d)",
+				   test->a, test->b, (int) test->len,
+				   res, test->expected);
+			ret = -1;
+		}
+	}
+
+	return ret;
+}
+
+
 int utils_module_tests(void)
 {
 	int ret = 0;
@@ -936,6 +1225,7 @@
 	    ip_addr_tests() < 0 ||
 	    eloop_tests() < 0 ||
 	    json_tests() < 0 ||
+	    const_time_tests() < 0 ||
 	    int_array_tests() < 0)
 		ret = -1;
 
diff --git a/src/wps/wps.c b/src/wps/wps.c
index 8d22827..484df26 100644
--- a/src/wps/wps.c
+++ b/src/wps/wps.c
@@ -145,6 +145,8 @@
 		data->peer_pubkey_hash_set = 1;
 	}
 
+	data->multi_ap_backhaul_sta = cfg->multi_ap_backhaul_sta;
+
 	return data;
 }
 
@@ -430,7 +432,7 @@
 
 	if (wps_build_version(ie) ||
 	    wps_build_req_type(ie, req_type) ||
-	    wps_build_wfa_ext(ie, 0, NULL, 0)) {
+	    wps_build_wfa_ext(ie, 0, NULL, 0, 0)) {
 		wpabuf_free(ie);
 		return NULL;
 	}
@@ -464,7 +466,7 @@
 
 	if (wps_build_version(ie) ||
 	    wps_build_resp_type(ie, WPS_RESP_AP) ||
-	    wps_build_wfa_ext(ie, 0, NULL, 0)) {
+	    wps_build_wfa_ext(ie, 0, NULL, 0, 0)) {
 		wpabuf_free(ie);
 		return NULL;
 	}
@@ -516,7 +518,7 @@
 	    wps_build_model_name(dev, ie) ||
 	    wps_build_model_number(dev, ie) ||
 	    wps_build_dev_name(dev, ie) ||
-	    wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0) ||
+	    wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0, 0) ||
 	    wps_build_req_dev_type(dev, ie, num_req_dev_types, req_dev_types)
 	    ||
 	    wps_build_secondary_dev_type(dev, ie)
diff --git a/src/wps/wps.h b/src/wps/wps.h
index 2505d2d..14ce863 100644
--- a/src/wps/wps.h
+++ b/src/wps/wps.h
@@ -100,6 +100,7 @@
 	struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
 
 	int p2p;
+	u8 multi_ap_ext;
 };
 
 /**
@@ -187,6 +188,12 @@
 	 * peer_pubkey_hash - Peer public key hash or %NULL if not known
 	 */
 	const u8 *peer_pubkey_hash;
+
+	/**
+	 * multi_ap_backhaul_sta - Whether this is a Multi-AP backhaul STA
+	 * enrollee
+	 */
+	int multi_ap_backhaul_sta;
 };
 
 struct wps_data * wps_init(const struct wps_config *cfg);
@@ -395,6 +402,37 @@
 	 * PSK is set for a network.
 	 */
 	int force_per_enrollee_psk;
+
+	/**
+	 * multi_ap_backhaul_ssid - SSID to supply to a Multi-AP backhaul
+	 * enrollee
+	 *
+	 * This SSID is used by the Registrar to fill in information for
+	 * Credentials when the enrollee advertises it is a Multi-AP backhaul
+	 * STA.
+	 */
+	const u8 *multi_ap_backhaul_ssid;
+
+	/**
+	 * multi_ap_backhaul_ssid_len - Length of multi_ap_backhaul_ssid in
+	 * octets
+	 */
+	size_t multi_ap_backhaul_ssid_len;
+
+	/**
+	 * multi_ap_backhaul_network_key - The Network Key (PSK) for the
+	 * Multi-AP backhaul enrollee.
+	 *
+	 * This key can be either the ASCII passphrase (8..63 characters) or the
+	 * 32-octet PSK (64 hex characters).
+	 */
+	const u8 *multi_ap_backhaul_network_key;
+
+	/**
+	 * multi_ap_backhaul_network_key_len - Length of
+	 * multi_ap_backhaul_network_key in octets
+	 */
+	size_t multi_ap_backhaul_network_key_len;
 };
 
 
diff --git a/src/wps/wps_attr_build.c b/src/wps/wps_attr_build.c
index 7dfa95b..4e872f3 100644
--- a/src/wps/wps_attr_build.c
+++ b/src/wps/wps_attr_build.c
@@ -204,7 +204,8 @@
 
 
 int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
-		      const u8 *auth_macs, size_t auth_macs_count)
+		      const u8 *auth_macs, size_t auth_macs_count,
+		      u8 multi_ap_subelem)
 {
 	u8 *len;
 
@@ -245,6 +246,14 @@
 				   MAC2STR(&auth_macs[i * ETH_ALEN]));
 	}
 
+	if (multi_ap_subelem) {
+		wpa_printf(MSG_DEBUG, "WPS:  * Multi-AP (0x%x)",
+			   multi_ap_subelem);
+		wpabuf_put_u8(msg, WFA_ELEM_MULTI_AP);
+		wpabuf_put_u8(msg, 1); /* length */
+		wpabuf_put_u8(msg, multi_ap_subelem);
+	}
+
 	WPA_PUT_BE16(len, (u8 *) wpabuf_put(msg, 0) - len - 2);
 
 #ifdef CONFIG_WPS_TESTING
diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c
index 756d57e..fd51635 100644
--- a/src/wps/wps_attr_parse.c
+++ b/src/wps/wps_attr_parse.c
@@ -67,6 +67,17 @@
 		}
 		attr->registrar_configuration_methods = pos;
 		break;
+	case WFA_ELEM_MULTI_AP:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG,
+				   "WPS: Invalid Multi-AP Extension length %u",
+				   len);
+			return -1;
+		}
+		attr->multi_ap_ext = *pos;
+		wpa_printf(MSG_DEBUG, "WPS: Multi-AP Extension 0x%02x",
+			   attr->multi_ap_ext);
+		break;
 	default:
 		wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor "
 			   "Extension subelement %u", id);
diff --git a/src/wps/wps_attr_parse.h b/src/wps/wps_attr_parse.h
index 8188fe9..4de27b2 100644
--- a/src/wps/wps_attr_parse.h
+++ b/src/wps/wps_attr_parse.h
@@ -97,6 +97,7 @@
 	const u8 *cred[MAX_CRED_COUNT];
 	const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
 	const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT];
+	u8 multi_ap_ext;
 };
 
 int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c
index bcae1ba..747dc47 100644
--- a/src/wps/wps_common.c
+++ b/src/wps/wps_common.c
@@ -374,7 +374,7 @@
 	    (rf_band && wps_build_rf_bands_attr(plain, rf_band)) ||
 	    (channel && wps_build_ap_channel(plain, channel)) ||
 	    wps_build_mac_addr(plain, wps->dev.mac_addr) ||
-	    wps_build_wfa_ext(plain, 0, NULL, 0)) {
+	    wps_build_wfa_ext(plain, 0, NULL, 0, 0)) {
 		os_free(data.new_psk);
 		wpabuf_clear_free(plain);
 		return NULL;
@@ -421,7 +421,7 @@
 
 	if (wps_build_oob_dev_pw(data, dev_pw_id, pubkey,
 				 wpabuf_head(dev_pw), wpabuf_len(dev_pw)) ||
-	    wps_build_wfa_ext(data, 0, NULL, 0)) {
+	    wps_build_wfa_ext(data, 0, NULL, 0, 0)) {
 		wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password "
 			   "token");
 		wpabuf_clear_free(data);
@@ -586,7 +586,7 @@
 	    wps_build_msg_type(msg, WPS_WSC_ACK) ||
 	    wps_build_enrollee_nonce(wps, msg) ||
 	    wps_build_registrar_nonce(wps, msg) ||
-	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+	    wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
 		wpabuf_free(msg);
 		return NULL;
 	}
@@ -610,7 +610,7 @@
 	    wps_build_enrollee_nonce(wps, msg) ||
 	    wps_build_registrar_nonce(wps, msg) ||
 	    wps_build_config_error(msg, wps->config_error) ||
-	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+	    wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
 		wpabuf_free(msg);
 		return NULL;
 	}
@@ -726,7 +726,7 @@
 	if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER,
 				 nfc_dh_pubkey, NULL, 0) ||
 	    wps_build_uuid_e(msg, ctx->uuid) ||
-	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+	    wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
 		wpabuf_free(msg);
 		return NULL;
 	}
@@ -809,7 +809,7 @@
 	    wps_build_ssid(msg, ctx) ||
 	    wps_build_ap_freq(msg, freq) ||
 	    (bssid && wps_build_mac_addr(msg, bssid)) ||
-	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+	    wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
 		wpabuf_free(msg);
 		return NULL;
 	}
@@ -848,7 +848,7 @@
 	    wps_build_rf_bands(&ctx->dev, msg, 0) ||
 	    wps_build_serial_number(&ctx->dev, msg) ||
 	    wps_build_uuid_e(msg, ctx->uuid) ||
-	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+	    wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
 		wpabuf_free(msg);
 		return NULL;
 	}
@@ -900,7 +900,7 @@
 	    wps_build_rf_bands(&ctx->dev, msg, 0) ||
 	    wps_build_serial_number(&ctx->dev, msg) ||
 	    wps_build_uuid_e(msg, ctx->uuid) ||
-	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+	    wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
 		wpabuf_free(msg);
 		return NULL;
 	}
diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h
index 301864d..9fccb4e 100644
--- a/src/wps/wps_defs.h
+++ b/src/wps/wps_defs.h
@@ -152,7 +152,8 @@
 	WFA_ELEM_NETWORK_KEY_SHAREABLE = 0x02,
 	WFA_ELEM_REQUEST_TO_ENROLL = 0x03,
 	WFA_ELEM_SETTINGS_DELAY_TIME = 0x04,
-	WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS = 0x05
+	WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS = 0x05,
+	WFA_ELEM_MULTI_AP = 0x06
 };
 
 /* Device Password ID */
diff --git a/src/wps/wps_dev_attr.c b/src/wps/wps_dev_attr.c
index 0d01211..b209fea 100644
--- a/src/wps/wps_dev_attr.c
+++ b/src/wps/wps_dev_attr.c
@@ -390,6 +390,14 @@
 }
 
 
+void wps_process_vendor_ext_m1(struct wps_device_data *dev, const u8 ext)
+{
+	dev->multi_ap_ext = ext;
+	wpa_printf(MSG_DEBUG, "WPS: Multi-AP extension value %02x",
+		   dev->multi_ap_ext);
+}
+
+
 int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands)
 {
 	if (bands == NULL) {
diff --git a/src/wps/wps_dev_attr.h b/src/wps/wps_dev_attr.h
index c9034ad..a4b4173 100644
--- a/src/wps/wps_dev_attr.h
+++ b/src/wps/wps_dev_attr.h
@@ -29,6 +29,7 @@
 int wps_process_device_attrs(struct wps_device_data *dev,
 			     struct wps_parse_attr *attr);
 int wps_process_os_version(struct wps_device_data *dev, const u8 *ver);
+void wps_process_vendor_ext_m1(struct wps_device_data *dev, const u8 ext);
 int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands);
 void wps_device_data_free(struct wps_device_data *dev);
 int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg);
diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c
index 4175077..80ed603 100644
--- a/src/wps/wps_enrollee.c
+++ b/src/wps/wps_enrollee.c
@@ -105,6 +105,7 @@
 {
 	struct wpabuf *msg;
 	u16 config_methods;
+	u8 multi_ap_backhaul_sta = 0;
 
 	if (random_get_bytes(wps->nonce_e, WPS_NONCE_LEN) < 0)
 		return NULL;
@@ -134,6 +135,9 @@
 				    WPS_CONFIG_PHY_PUSHBUTTON);
 	}
 
+	if (wps->multi_ap_backhaul_sta)
+		multi_ap_backhaul_sta = MULTI_AP_BACKHAUL_STA;
+
 	if (wps_build_version(msg) ||
 	    wps_build_msg_type(msg, WPS_M1) ||
 	    wps_build_uuid_e(msg, wps->uuid_e) ||
@@ -152,7 +156,7 @@
 	    wps_build_dev_password_id(msg, wps->dev_pw_id) ||
 	    wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
 	    wps_build_os_version(&wps->wps->dev, msg) ||
-	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0, multi_ap_backhaul_sta) ||
 	    wps_build_vendor_ext_m1(&wps->wps->dev, msg)) {
 		wpabuf_free(msg);
 		return NULL;
@@ -190,7 +194,7 @@
 	    wps_build_msg_type(msg, WPS_M3) ||
 	    wps_build_registrar_nonce(wps, msg) ||
 	    wps_build_e_hash(wps, msg) ||
-	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
 	    wps_build_authenticator(wps, msg)) {
 		wpabuf_free(msg);
 		return NULL;
@@ -223,7 +227,7 @@
 	    wps_build_e_snonce1(wps, plain) ||
 	    wps_build_key_wrap_auth(wps, plain) ||
 	    wps_build_encr_settings(wps, msg, plain) ||
-	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
 	    wps_build_authenticator(wps, msg)) {
 		wpabuf_clear_free(plain);
 		wpabuf_free(msg);
@@ -393,7 +397,7 @@
 	    (wps->wps->ap && wps_build_ap_settings(wps, plain)) ||
 	    wps_build_key_wrap_auth(wps, plain) ||
 	    wps_build_encr_settings(wps, msg, plain) ||
-	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
 	    wps_build_authenticator(wps, msg)) {
 		wpabuf_clear_free(plain);
 		wpabuf_free(msg);
@@ -430,7 +434,7 @@
 	    wps_build_msg_type(msg, WPS_WSC_DONE) ||
 	    wps_build_enrollee_nonce(wps, msg) ||
 	    wps_build_registrar_nonce(wps, msg) ||
-	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+	    wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
 		wpabuf_free(msg);
 		return NULL;
 	}
diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c
index affd6a4..06a8fda 100644
--- a/src/wps/wps_er.c
+++ b/src/wps/wps_er.c
@@ -1530,7 +1530,7 @@
 	    wps_er_build_selected_registrar(msg, sel_reg) ||
 	    wps_er_build_dev_password_id(msg, dev_passwd_id) ||
 	    wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods) ||
-	    wps_build_wfa_ext(msg, 0, auth_macs, count) ||
+	    wps_build_wfa_ext(msg, 0, auth_macs, count, 0) ||
 	    wps_er_build_uuid_r(msg, er->wps->uuid)) {
 		wpabuf_free(msg);
 		return;
@@ -2048,7 +2048,7 @@
 	data.wps = wps;
 	data.use_cred = cred;
 	if (wps_build_cred(&data, ret) ||
-	    wps_build_wfa_ext(ret, 0, NULL, 0)) {
+	    wps_build_wfa_ext(ret, 0, NULL, 0, 0)) {
 		wpabuf_free(ret);
 		return NULL;
 	}
diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h
index fe0c60b..2cf22d4 100644
--- a/src/wps/wps_i.h
+++ b/src/wps/wps_i.h
@@ -125,6 +125,8 @@
 	int pbc_in_m1;
 
 	struct wps_nfc_pw_token *nfc_pw_token;
+
+	int multi_ap_backhaul_sta;
 };
 
 
@@ -163,7 +165,8 @@
 			    struct wpabuf *plain);
 int wps_build_version(struct wpabuf *msg);
 int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
-		      const u8 *auth_macs, size_t auth_macs_count);
+		      const u8 *auth_macs, size_t auth_macs_count,
+		      u8 multi_ap_subelem);
 int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type);
 int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg);
 int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg);
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index 379925e..0ac5b28 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -188,6 +188,37 @@
 #ifdef WPS_WORKAROUNDS
 	struct os_reltime pbc_ignore_start;
 #endif /* WPS_WORKAROUNDS */
+
+	/**
+	 * multi_ap_backhaul_ssid - SSID to supply to a Multi-AP backhaul
+	 * enrollee
+	 *
+	 * This SSID is used by the Registrar to fill in information for
+	 * Credentials when the enrollee advertises it is a Multi-AP backhaul
+	 * STA.
+	 */
+	u8 multi_ap_backhaul_ssid[SSID_MAX_LEN];
+
+	/**
+	 * multi_ap_backhaul_ssid_len - Length of multi_ap_backhaul_ssid in
+	 * octets
+	 */
+	size_t multi_ap_backhaul_ssid_len;
+
+	/**
+	 * multi_ap_backhaul_network_key - The Network Key (PSK) for the
+	 * Multi-AP backhaul enrollee.
+	 *
+	 * This key can be either the ASCII passphrase (8..63 characters) or the
+	 * 32-octet PSK (64 hex characters).
+	 */
+	u8 *multi_ap_backhaul_network_key;
+
+	/**
+	 * multi_ap_backhaul_network_key_len - Length of
+	 * multi_ap_backhaul_network_key in octets
+	 */
+	size_t multi_ap_backhaul_network_key_len;
 };
 
 
@@ -667,6 +698,22 @@
 	reg->dualband = cfg->dualband;
 	reg->force_per_enrollee_psk = cfg->force_per_enrollee_psk;
 
+	if (cfg->multi_ap_backhaul_ssid) {
+		os_memcpy(reg->multi_ap_backhaul_ssid,
+			  cfg->multi_ap_backhaul_ssid,
+			  cfg->multi_ap_backhaul_ssid_len);
+		reg->multi_ap_backhaul_ssid_len =
+			cfg->multi_ap_backhaul_ssid_len;
+	}
+	if (cfg->multi_ap_backhaul_network_key) {
+		reg->multi_ap_backhaul_network_key =
+			os_memdup(cfg->multi_ap_backhaul_network_key,
+				  cfg->multi_ap_backhaul_network_key_len);
+		if (reg->multi_ap_backhaul_network_key)
+			reg->multi_ap_backhaul_network_key_len =
+				cfg->multi_ap_backhaul_network_key_len;
+	}
+
 	if (wps_set_ie(reg)) {
 		wps_registrar_deinit(reg);
 		return NULL;
@@ -704,6 +751,8 @@
 	eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
 	wps_registrar_flush(reg);
 	wpabuf_clear_free(reg->extra_cred);
+	bin_clear_free(reg->multi_ap_backhaul_network_key,
+		       reg->multi_ap_backhaul_network_key_len);
 	os_free(reg);
 }
 
@@ -1281,7 +1330,7 @@
 	    wps_build_sel_reg_config_methods(reg, beacon) ||
 	    wps_build_sel_pbc_reg_uuid_e(reg, beacon) ||
 	    (reg->dualband && wps_build_rf_bands(&reg->wps->dev, beacon, 0)) ||
-	    wps_build_wfa_ext(beacon, 0, auth_macs, count) ||
+	    wps_build_wfa_ext(beacon, 0, auth_macs, count, 0) ||
 	    wps_build_vendor_ext(&reg->wps->dev, beacon)) {
 		wpabuf_free(beacon);
 		wpabuf_free(probe);
@@ -1311,7 +1360,7 @@
 	    wps_build_device_attrs(&reg->wps->dev, probe) ||
 	    wps_build_probe_config_methods(reg, probe) ||
 	    (reg->dualband && wps_build_rf_bands(&reg->wps->dev, probe, 0)) ||
-	    wps_build_wfa_ext(probe, 0, auth_macs, count) ||
+	    wps_build_wfa_ext(probe, 0, auth_macs, count, 0) ||
 	    wps_build_vendor_ext(&reg->wps->dev, probe)) {
 		wpabuf_free(beacon);
 		wpabuf_free(probe);
@@ -1592,6 +1641,7 @@
 int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
 {
 	struct wpabuf *cred;
+	struct wps_registrar *reg = wps->wps->registrar;
 
 	if (wps->wps->registrar->skip_cred_build)
 		goto skip_cred_build;
@@ -1603,6 +1653,29 @@
 	}
 	os_memset(&wps->cred, 0, sizeof(wps->cred));
 
+	if (wps->peer_dev.multi_ap_ext == MULTI_AP_BACKHAUL_STA &&
+	    reg->multi_ap_backhaul_ssid_len) {
+		wpa_printf(MSG_DEBUG, "WPS: Use backhaul STA credentials");
+		os_memcpy(wps->cred.ssid, reg->multi_ap_backhaul_ssid,
+			  reg->multi_ap_backhaul_ssid_len);
+		wps->cred.ssid_len = reg->multi_ap_backhaul_ssid_len;
+		/* Backhaul is always WPA2PSK */
+		wps->cred.auth_type = WPS_AUTH_WPA2PSK;
+		wps->cred.encr_type = WPS_ENCR_AES;
+		/* Set MAC address in the Credential to be the Enrollee's MAC
+		 * address
+		 */
+		os_memcpy(wps->cred.mac_addr, wps->mac_addr_e, ETH_ALEN);
+		if (reg->multi_ap_backhaul_network_key) {
+			os_memcpy(wps->cred.key,
+				  reg->multi_ap_backhaul_network_key,
+				  reg->multi_ap_backhaul_network_key_len);
+			wps->cred.key_len =
+				reg->multi_ap_backhaul_network_key_len;
+		}
+		goto use_provided;
+	}
+
 	os_memcpy(wps->cred.ssid, wps->wps->ssid, wps->wps->ssid_len);
 	wps->cred.ssid_len = wps->wps->ssid_len;
 
@@ -1845,7 +1918,7 @@
 	    wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
 	    wps_build_dev_password_id(msg, wps->dev_pw_id) ||
 	    wps_build_os_version(&wps->wps->dev, msg) ||
-	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+	    wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
 		wpabuf_free(msg);
 		return NULL;
 	}
@@ -1913,7 +1986,7 @@
 	    wps_build_assoc_state(wps, msg) ||
 	    wps_build_config_error(msg, err) ||
 	    wps_build_os_version(&wps->wps->dev, msg) ||
-	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+	    wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
 		wpabuf_free(msg);
 		return NULL;
 	}
@@ -1949,7 +2022,7 @@
 	    wps_build_r_snonce1(wps, plain) ||
 	    wps_build_key_wrap_auth(wps, plain) ||
 	    wps_build_encr_settings(wps, msg, plain) ||
-	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
 	    wps_build_authenticator(wps, msg)) {
 		wpabuf_clear_free(plain);
 		wpabuf_free(msg);
@@ -1984,7 +2057,7 @@
 	    wps_build_r_snonce2(wps, plain) ||
 	    wps_build_key_wrap_auth(wps, plain) ||
 	    wps_build_encr_settings(wps, msg, plain) ||
-	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
 	    wps_build_authenticator(wps, msg)) {
 		wpabuf_clear_free(plain);
 		wpabuf_free(msg);
@@ -2021,7 +2094,7 @@
 	    (!wps->wps->ap && !wps->er && wps_build_ap_settings(wps, plain)) ||
 	    wps_build_key_wrap_auth(wps, plain) ||
 	    wps_build_encr_settings(wps, msg, plain) ||
-	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
 	    wps_build_authenticator(wps, msg)) {
 		wpabuf_clear_free(plain);
 		wpabuf_clear_free(msg);
@@ -2705,6 +2778,7 @@
 		wps->use_psk_key = 1;
 	}
 #endif /* WPS_WORKAROUNDS */
+	wps_process_vendor_ext_m1(&wps->peer_dev, attr->multi_ap_ext);
 
 	wps->state = SEND_M2;
 	return WPS_CONTINUE;
diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c
index 0c458c6..ca893a4 100644
--- a/src/wps/wps_upnp.c
+++ b/src/wps/wps_upnp.c
@@ -599,7 +599,7 @@
 	wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
 	wpabuf_put_be16(msg, WPS_NONCE_LEN);
 	wpabuf_put(msg, WPS_NONCE_LEN);
-	if (wps_build_wfa_ext(msg, 0, NULL, 0)) {
+	if (wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
 		wpabuf_free(msg);
 		return NULL;
 	}