Merge commit '7a0d95540cf1cc4d71910a0a9b0403a8b0e40679' into HEAD

Change-Id: Ic3b480ebc162f4f69530261527ca349cf1a6aa47
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index d9c4b97..bbc9c0b 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -24,9 +24,14 @@
 # Set Android log name
 L_CFLAGS += -DANDROID_LOG_NAME=\"hostapd\"
 
+L_CFLAGS += -Wall -Werror
+
 # Disable unused parameter warnings
 L_CFLAGS += -Wno-unused-parameter
 
+# Disable unused variable warnings
+L_CFLAGS += -Wno-unused-variable
+
 # Disable macro redefined warnings
 L_CFLAGS += -Wno-macro-redefined
 
@@ -155,6 +160,7 @@
 
 OBJS += src/eapol_auth/eapol_auth_sm.c
 
+
 ifndef CONFIG_NO_DUMP_STATE
 # define HOSTAPD_DUMP_STATE to include support for dumping internal state
 # through control interface commands (undefine it, if you want to save in
@@ -217,11 +223,6 @@
 CONFIG_L2_PACKET=y
 endif
 
-ifdef CONFIG_PEERKEY
-L_CFLAGS += -DCONFIG_PEERKEY
-OBJS += src/ap/peerkey_auth.c
-endif
-
 ifdef CONFIG_HS20
 NEED_AES_OMAC1=y
 CONFIG_PROXYARP=y
@@ -254,6 +255,15 @@
 NEED_SHA256=y
 NEED_AES_OMAC1=y
 NEED_AES_UNWRAP=y
+NEED_AES_SIV=y
+NEED_ETH_P_OUI=y
+NEED_SHA256=y
+NEED_HMAC_SHA256_KDF=y
+endif
+
+ifdef NEED_ETH_P_OUI
+L_CFLAGS += -DCONFIG_ETH_P_OUI
+OBJS += src/ap/eth_p_oui.c
 endif
 
 ifdef CONFIG_SAE
@@ -263,15 +273,30 @@
 NEED_DH_GROUPS=y
 endif
 
+ifdef CONFIG_OWE
+L_CFLAGS += -DCONFIG_OWE
+NEED_ECC=y
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_HMAC_SHA512_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+NEED_SHA512=y
+endif
+
 ifdef CONFIG_FILS
 L_CFLAGS += -DCONFIG_FILS
 OBJS += src/ap/fils_hlp.c
 NEED_SHA384=y
 NEED_AES_SIV=y
+ifdef CONFIG_FILS_SK_PFS
+L_CFLAGS += -DCONFIG_FILS_SK_PFS
+NEED_ECC=y
+endif
 endif
 
 ifdef CONFIG_WNM
-L_CFLAGS += -DCONFIG_WNM
+L_CFLAGS += -DCONFIG_WNM -DCONFIG_WNM_AP
 OBJS += src/ap/wnm_ap.c
 endif
 
@@ -515,6 +540,23 @@
 
 endif
 
+ifdef CONFIG_DPP
+L_CFLAGS += -DCONFIG_DPP
+OBJS += src/common/dpp.c
+OBJS += src/ap/dpp_hostapd.c
+OBJS += src/ap/gas_query_ap.c
+NEED_AES_SIV=y
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_HMAC_SHA512_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+NEED_SHA512=y
+NEED_JSON=y
+NEED_GAS=y
+NEED_BASE64=y
+endif
+
 ifdef CONFIG_EAP_IKEV2
 L_CFLAGS += -DEAP_SERVER_IKEV2
 OBJS += src/eap_server/eap_server_ikev2.c src/eap_server/ikev2.c
@@ -597,6 +639,10 @@
 NEED_TLS_PRF_SHA256=y
 LIBS += -lcrypto
 LIBS_h += -lcrypto
+ifndef CONFIG_TLS_DEFAULT_CIPHERS
+CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW"
+endif
+L_CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
 endif
 
 ifeq ($(CONFIG_TLS), gnutls)
@@ -731,6 +777,12 @@
 ifdef NEED_AES_EAX
 AESOBJS += src/crypto/aes-eax.c
 NEED_AES_CTR=y
+NEED_AES_OMAC1=y
+endif
+ifdef NEED_AES_SIV
+AESOBJS += src/crypto/aes-siv.c
+NEED_AES_CTR=y
+NEED_AES_OMAC1=y
 endif
 ifdef NEED_AES_CTR
 AESOBJS += src/crypto/aes-ctr.c
@@ -753,9 +805,6 @@
 AESOBJS += src/crypto/aes-cbc.c
 endif
 endif
-ifdef NEED_AES_SIV
-AESOBJS += src/crypto/aes-siv.c
-endif
 ifdef NEED_AES_DEC
 ifdef CONFIG_INTERNAL_AES
 AESOBJS += src/crypto/aes-internal-dec.c
@@ -839,6 +888,15 @@
 ifdef NEED_TLS_PRF_SHA256
 OBJS += src/crypto/sha256-tlsprf.c
 endif
+ifdef NEED_HMAC_SHA256_KDF
+OBJS += src/crypto/sha256-kdf.c
+endif
+ifdef NEED_HMAC_SHA384_KDF
+OBJS += src/crypto/sha384-kdf.c
+endif
+ifdef NEED_HMAC_SHA512_KDF
+OBJS += src/crypto/sha512-kdf.c
+endif
 endif
 ifdef NEED_SHA384
 L_CFLAGS += -DCONFIG_SHA384
@@ -847,6 +905,15 @@
 endif
 OBJS += src/crypto/sha384-prf.c
 endif
+ifdef NEED_SHA512
+L_CFLAGS += -DCONFIG_SHA512
+ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
+OBJS += src/crypto/sha512.c
+endif
+endif
+OBJS += src/crypto/sha512-prf.c
+endif
 
 ifdef CONFIG_INTERNAL_SHA384
 L_CFLAGS += -DCONFIG_INTERNAL_SHA384
@@ -903,6 +970,11 @@
 OBJS += src/utils/base64.c
 endif
 
+ifdef NEED_JSON
+OBJS += src/utils/json.c
+L_CFLAGS += -DCONFIG_JSON
+endif
+
 ifdef NEED_AP_MLME
 OBJS += src/ap/wmm.c
 OBJS += src/ap/ap_list.c
@@ -936,6 +1008,10 @@
 
 ifdef CONFIG_INTERWORKING
 L_CFLAGS += -DCONFIG_INTERWORKING
+NEED_GAS=y
+endif
+
+ifdef NEED_GAS
 OBJS += src/common/gas.c
 OBJS += src/ap/gas_serv.c
 endif
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 3160d0d..eb35672 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -258,11 +258,6 @@
 CONFIG_L2_PACKET=y
 endif
 
-ifdef CONFIG_PEERKEY
-CFLAGS += -DCONFIG_PEERKEY
-OBJS += ../src/ap/peerkey_auth.o
-endif
-
 ifdef CONFIG_HS20
 NEED_AES_OMAC1=y
 CONFIG_PROXYARP=y
@@ -295,6 +290,15 @@
 NEED_SHA256=y
 NEED_AES_OMAC1=y
 NEED_AES_UNWRAP=y
+NEED_AES_SIV=y
+NEED_ETH_P_OUI=y
+NEED_SHA256=y
+NEED_HMAC_SHA256_KDF=y
+endif
+
+ifdef NEED_ETH_P_OUI
+CFLAGS += -DCONFIG_ETH_P_OUI
+OBJS += ../src/ap/eth_p_oui.o
 endif
 
 ifdef CONFIG_SAE
@@ -305,15 +309,30 @@
 NEED_AP_MLME=y
 endif
 
+ifdef CONFIG_OWE
+CFLAGS += -DCONFIG_OWE
+NEED_ECC=y
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_HMAC_SHA512_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+NEED_SHA512=y
+endif
+
 ifdef CONFIG_FILS
 CFLAGS += -DCONFIG_FILS
 OBJS += ../src/ap/fils_hlp.o
 NEED_SHA384=y
 NEED_AES_SIV=y
+ifdef CONFIG_FILS_SK_PFS
+CFLAGS += -DCONFIG_FILS_SK_PFS
+NEED_ECC=y
+endif
 endif
 
 ifdef CONFIG_WNM
-CFLAGS += -DCONFIG_WNM
+CFLAGS += -DCONFIG_WNM -DCONFIG_WNM_AP
 OBJS += ../src/ap/wnm_ap.o
 endif
 
@@ -547,6 +566,23 @@
 
 endif
 
+ifdef CONFIG_DPP
+CFLAGS += -DCONFIG_DPP
+OBJS += ../src/common/dpp.o
+OBJS += ../src/ap/dpp_hostapd.o
+OBJS += ../src/ap/gas_query_ap.o
+NEED_AES_SIV=y
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_HMAC_SHA512_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+NEED_SHA512=y
+NEED_JSON=y
+NEED_GAS=y
+NEED_BASE64=y
+endif
+
 ifdef CONFIG_EAP_IKEV2
 CFLAGS += -DEAP_SERVER_IKEV2
 OBJS += ../src/eap_server/eap_server_ikev2.o ../src/eap_server/ikev2.o
@@ -633,6 +669,10 @@
 LIBS += -ldl
 LIBS_h += -ldl
 endif
+ifndef CONFIG_TLS_DEFAULT_CIPHERS
+CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW"
+endif
+CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
 endif
 
 ifeq ($(CONFIG_TLS), gnutls)
@@ -732,6 +772,47 @@
 endif
 endif
 
+ifeq ($(CONFIG_TLS), linux)
+OBJS += ../src/crypto/crypto_linux.o
+ifdef TLS_FUNCS
+OBJS += ../src/crypto/crypto_internal-rsa.o
+OBJS += ../src/crypto/tls_internal.o
+OBJS += ../src/tls/tlsv1_common.o
+OBJS += ../src/tls/tlsv1_record.o
+OBJS += ../src/tls/tlsv1_cred.o
+OBJS += ../src/tls/tlsv1_server.o
+OBJS += ../src/tls/tlsv1_server_write.o
+OBJS += ../src/tls/tlsv1_server_read.o
+OBJS += ../src/tls/asn1.o
+OBJS += ../src/tls/rsa.o
+OBJS += ../src/tls/x509v3.o
+OBJS += ../src/tls/pkcs1.o
+OBJS += ../src/tls/pkcs5.o
+OBJS += ../src/tls/pkcs8.o
+NEED_SHA256=y
+NEED_BASE64=y
+NEED_TLS_PRF=y
+ifdef CONFIG_TLSV12
+NEED_TLS_PRF_SHA256=y
+endif
+NEED_MODEXP=y
+NEED_CIPHER=y
+CFLAGS += -DCONFIG_TLS_INTERNAL
+CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
+endif
+ifdef NEED_MODEXP
+OBJS += ../src/crypto/crypto_internal-modexp.o
+OBJS += ../src/tls/bignum.o
+CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+CFLAGS += -DLTM_FAST
+endif
+CONFIG_INTERNAL_DH_GROUP5=y
+ifdef NEED_FIPS186_2_PRF
+OBJS += ../src/crypto/fips_prf_internal.o
+OBJS += ../src/crypto/sha1-internal.o
+endif
+endif
+
 ifeq ($(CONFIG_TLS), none)
 ifdef TLS_FUNCS
 OBJS += ../src/crypto/tls_none.o
@@ -767,6 +848,12 @@
 ifdef NEED_AES_EAX
 AESOBJS += ../src/crypto/aes-eax.o
 NEED_AES_CTR=y
+NEED_AES_OMAC1=y
+endif
+ifdef NEED_AES_SIV
+AESOBJS += ../src/crypto/aes-siv.o
+NEED_AES_CTR=y
+NEED_AES_OMAC1=y
 endif
 ifdef NEED_AES_CTR
 AESOBJS += ../src/crypto/aes-ctr.o
@@ -775,22 +862,25 @@
 AESOBJS += ../src/crypto/aes-encblock.o
 endif
 ifdef NEED_AES_OMAC1
+ifneq ($(CONFIG_TLS), linux)
 AESOBJS += ../src/crypto/aes-omac1.o
 endif
+endif
 ifdef NEED_AES_UNWRAP
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 NEED_AES_DEC=y
 AESOBJS += ../src/crypto/aes-unwrap.o
 endif
 endif
+endif
 ifdef NEED_AES_CBC
 NEED_AES_DEC=y
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 AESOBJS += ../src/crypto/aes-cbc.o
 endif
 endif
-ifdef NEED_AES_SIV
-AESOBJS += ../src/crypto/aes-siv.o
 endif
 ifdef NEED_AES_DEC
 ifdef CONFIG_INTERNAL_AES
@@ -803,8 +893,10 @@
 
 ifdef NEED_SHA1
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 SHA1OBJS += ../src/crypto/sha1.o
 endif
+endif
 SHA1OBJS += ../src/crypto/sha1-prf.o
 ifdef CONFIG_INTERNAL_SHA1
 SHA1OBJS += ../src/crypto/sha1-internal.o
@@ -828,8 +920,10 @@
 endif
 
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 OBJS += ../src/crypto/md5.o
 endif
+endif
 
 ifdef NEED_MD5
 ifdef CONFIG_INTERNAL_MD5
@@ -865,8 +959,10 @@
 ifdef NEED_SHA256
 CFLAGS += -DCONFIG_SHA256
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 OBJS += ../src/crypto/sha256.o
 endif
+endif
 OBJS += ../src/crypto/sha256-prf.o
 ifdef CONFIG_INTERNAL_SHA256
 OBJS += ../src/crypto/sha256-internal.o
@@ -877,14 +973,31 @@
 ifdef NEED_HMAC_SHA256_KDF
 OBJS += ../src/crypto/sha256-kdf.o
 endif
+ifdef NEED_HMAC_SHA384_KDF
+OBJS += ../src/crypto/sha384-kdf.o
+endif
+ifdef NEED_HMAC_SHA512_KDF
+OBJS += ../src/crypto/sha512-kdf.o
+endif
 endif
 ifdef NEED_SHA384
 CFLAGS += -DCONFIG_SHA384
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 OBJS += ../src/crypto/sha384.o
 endif
+endif
 OBJS += ../src/crypto/sha384-prf.o
 endif
+ifdef NEED_SHA512
+CFLAGS += -DCONFIG_SHA512
+ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
+OBJS += ../src/crypto/sha512.o
+endif
+endif
+OBJS += ../src/crypto/sha512-prf.o
+endif
 
 ifdef CONFIG_INTERNAL_SHA384
 CFLAGS += -DCONFIG_INTERNAL_SHA384
@@ -920,9 +1033,11 @@
 HOBJS += ../src/utils/eloop.o
 HOBJS += $(SHA1OBJS)
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 HOBJS += ../src/crypto/md5.o
 endif
 endif
+endif
 
 ifdef CONFIG_RADIUS_SERVER
 CFLAGS += -DRADIUS_SERVER
@@ -941,6 +1056,11 @@
 OBJS += ../src/utils/base64.o
 endif
 
+ifdef NEED_JSON
+OBJS += ../src/utils/json.o
+CFLAGS += -DCONFIG_JSON
+endif
+
 ifdef NEED_AP_MLME
 OBJS += ../src/ap/wmm.o
 OBJS += ../src/ap/ap_list.o
@@ -970,6 +1090,10 @@
 
 ifdef CONFIG_INTERWORKING
 CFLAGS += -DCONFIG_INTERWORKING
+NEED_GAS=y
+endif
+
+ifdef NEED_GAS
 OBJS += ../src/common/gas.o
 OBJS += ../src/ap/gas_serv.o
 endif
@@ -1121,6 +1245,9 @@
 HOBJS += ../src/crypto/aes-internal.o
 HOBJS += ../src/crypto/aes-internal-enc.o
 endif
+ifeq ($(CONFIG_TLS), linux)
+HOBJS += ../src/crypto/crypto_linux.o
+endif
 
 nt_password_hash: $(NOBJS)
 	$(Q)$(CC) $(LDFLAGS) -o nt_password_hash $(NOBJS) $(LIBS_n)
diff --git a/hostapd/README b/hostapd/README
index cb37c8e..298391b 100644
--- a/hostapd/README
+++ b/hostapd/README
@@ -70,7 +70,7 @@
 Current hardware/software requirements:
 - drivers:
 	Host AP driver for Prism2/2.5/3.
-	(http://hostap.epitest.fi/)
+	(http://w1.fi/hostap-driver.html)
 	Please note that station firmware version needs to be 1.7.0 or newer
 	to work in WPA mode.
 
@@ -81,8 +81,7 @@
 	Any wired Ethernet driver for wired IEEE 802.1X authentication
 	(experimental code)
 
-	FreeBSD -current (with some kernel mods that have not yet been
-	committed when hostapd v0.3.0 was released)
+	FreeBSD -current
 	BSD net80211 layer (e.g., Atheros driver)
 
 
@@ -186,23 +185,13 @@
 the Authentication Server. Other than this, the functionality is similar
 to the case with the co-located Authentication Server.
 
-Authentication Server and Supplicant
-------------------------------------
+Authentication Server
+---------------------
 
 Any RADIUS server supporting EAP should be usable as an IEEE 802.1X
 Authentication Server with hostapd Authenticator. FreeRADIUS
 (http://www.freeradius.org/) has been successfully tested with hostapd
-Authenticator and both Xsupplicant (http://www.open1x.org) and Windows
-XP Supplicants. EAP/TLS was used with Xsupplicant and
-EAP/MD5-Challenge with Windows XP.
-
-http://www.missl.cs.umd.edu/wireless/eaptls/ has useful information
-about using EAP/TLS with FreeRADIUS and Xsupplicant (just replace
-Cisco access point with Host AP driver, hostapd daemon, and a Prism2
-card ;-). http://www.freeradius.org/doc/EAP-MD5.html has information
-about using EAP/MD5 with FreeRADIUS, including instructions for WinXP
-configuration. http://www.denobula.com/EAPTLS.pdf has a HOWTO on
-EAP/TLS use with WinXP Supplicant.
+Authenticator.
 
 Automatic WEP key configuration
 -------------------------------
@@ -243,16 +232,15 @@
 of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked
 to address the flaws of the base standard and has in practice
 completed its work in May 2004. The IEEE 802.11i amendment to the IEEE
-802.11 standard was approved in June 2004 and this amendment is likely
-to be published in July 2004.
+802.11 standard was approved in June 2004 and this amendment was
+published in July 2004.
 
 Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version of the
 IEEE 802.11i work (draft 3.0) to define a subset of the security
 enhancements that can be implemented with existing wlan hardware. This
 is called Wi-Fi Protected Access<TM> (WPA). This has now become a
 mandatory component of interoperability testing and certification done
-by Wi-Fi Alliance. Wi-Fi provides information about WPA at its web
-site (http://www.wi-fi.org/OpenSection/protected_access.asp).
+by Wi-Fi Alliance.
 
 IEEE 802.11 standard defined wired equivalent privacy (WEP) algorithm
 for protecting wireless networks. WEP uses RC4 with 40-bit keys,
diff --git a/hostapd/android.config b/hostapd/android.config
index 1715cc8..b293828 100644
--- a/hostapd/android.config
+++ b/hostapd/android.config
@@ -44,9 +44,6 @@
 # WPA2/IEEE 802.11i RSN pre-authentication
 #CONFIG_RSN_PREAUTH=y
 
-# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
-#CONFIG_PEERKEY=y
-
 # IEEE 802.11w (management frame protection)
 # This version is an experimental implementation based on IEEE 802.11w/D1.0
 # draft and is subject to change since the standard has not yet been finalized.
@@ -202,3 +199,7 @@
 
 # Include internal line edit mode in hostapd_cli.
 CONFIG_WPA_CLI_EDIT=y
+
+# Opportunistic Wireless Encryption (OWE)
+# Experimental implementation of draft-harkins-owe-07.txt
+#CONFIG_OWE=y
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 9e95440..e2a470c 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -14,6 +14,8 @@
 #include "utils/common.h"
 #include "utils/uuid.h"
 #include "common/ieee802_11_defs.h"
+#include "crypto/sha256.h"
+#include "crypto/tls.h"
 #include "drivers/driver.h"
 #include "eap_server/eap.h"
 #include "radius/radius_client.h"
@@ -307,13 +309,12 @@
 				goto failed;
 			}
 
-			user->identity = os_malloc(pos - start);
+			user->identity = os_memdup(start, pos - start);
 			if (user->identity == NULL) {
 				wpa_printf(MSG_ERROR, "Failed to allocate "
 					   "memory for EAP identity");
 				goto failed;
 			}
-			os_memcpy(user->identity, start, pos - start);
 			user->identity_len = pos - start;
 
 			if (pos[0] == '"' && pos[1] == '*') {
@@ -431,13 +432,12 @@
 				goto failed;
 			}
 
-			user->password = os_malloc(pos - start);
+			user->password = os_memdup(start, pos - start);
 			if (user->password == NULL) {
 				wpa_printf(MSG_ERROR, "Failed to allocate "
 					   "memory for EAP password");
 				goto failed;
 			}
-			os_memcpy(user->password, start, pos - start);
 			user->password_len = pos - start;
 
 			pos++;
@@ -712,6 +712,14 @@
 			val |= WPA_KEY_MGMT_FT_FILS_SHA384;
 #endif /* CONFIG_IEEE80211R_AP */
 #endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+		else if (os_strcmp(start, "OWE") == 0)
+			val |= WPA_KEY_MGMT_OWE;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+		else if (os_strcmp(start, "DPP") == 0)
+			val |= WPA_KEY_MGMT_DPP;
+#endif /* CONFIG_DPP */
 		else {
 			wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
 				   line, start);
@@ -782,10 +790,9 @@
 		if (len < 2 || val[len - 1] != '"')
 			return -1;
 		len -= 2;
-		wep->key[keyidx] = os_malloc(len);
+		wep->key[keyidx] = os_memdup(val + 1, len);
 		if (wep->key[keyidx] == NULL)
 			return -1;
-		os_memcpy(wep->key[keyidx], val + 1, len);
 		wep->len[keyidx] = len;
 	} else {
 		if (len & 1)
@@ -999,6 +1006,26 @@
 
 
 #ifdef CONFIG_IEEE80211R_AP
+
+static int rkh_derive_key(const char *pos, u8 *key, size_t key_len)
+{
+	u8 oldkey[16];
+	int ret;
+
+	if (!hexstr2bin(pos, key, key_len))
+		return 0;
+
+	/* Try to use old short key for backwards compatibility */
+	if (hexstr2bin(pos, oldkey, sizeof(oldkey)))
+		return -1;
+
+	ret = hmac_sha256_kdf(oldkey, sizeof(oldkey), "FT OLDKEY", NULL, 0,
+			      key, key_len);
+	os_memset(oldkey, 0, sizeof(oldkey));
+	return ret;
+}
+
+
 static int add_r0kh(struct hostapd_bss_config *bss, char *value)
 {
 	struct ft_remote_r0kh *r0kh;
@@ -1032,7 +1059,7 @@
 	os_memcpy(r0kh->id, pos, r0kh->id_len);
 
 	pos = next;
-	if (hexstr2bin(pos, r0kh->key, sizeof(r0kh->key))) {
+	if (rkh_derive_key(pos, r0kh->key, sizeof(r0kh->key)) < 0) {
 		wpa_printf(MSG_ERROR, "Invalid R0KH key: '%s'", pos);
 		os_free(r0kh);
 		return -1;
@@ -1077,7 +1104,7 @@
 	}
 
 	pos = next;
-	if (hexstr2bin(pos, r1kh->key, sizeof(r1kh->key))) {
+	if (rkh_derive_key(pos, r1kh->key, sizeof(r1kh->key)) < 0) {
 		wpa_printf(MSG_ERROR, "Invalid R1KH key: '%s'", pos);
 		os_free(r1kh);
 		return -1;
@@ -1105,6 +1132,10 @@
 		conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
 		conf->secondary_channel = 1;
 	}
+	if (os_strstr(capab, "[HT40+]") && os_strstr(capab, "[HT40-]")) {
+		conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+		conf->ht40_plus_minus_allowed = 1;
+	}
 	if (os_strstr(capab, "[SMPS-STATIC]")) {
 		conf->ht_capab &= ~HT_CAP_INFO_SMPS_MASK;
 		conf->ht_capab |= HT_CAP_INFO_SMPS_STATIC;
@@ -2029,6 +2060,31 @@
 #endif /* CONFIG_FILS */
 
 
+#ifdef EAP_SERVER
+static unsigned int parse_tls_flags(const char *val)
+{
+	unsigned int flags = 0;
+
+	if (os_strstr(val, "[ALLOW-SIGN-RSA-MD5]"))
+		flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5;
+	if (os_strstr(val, "[DISABLE-TIME-CHECKS]"))
+		flags |= TLS_CONN_DISABLE_TIME_CHECKS;
+	if (os_strstr(val, "[DISABLE-TLSv1.0]"))
+		flags |= TLS_CONN_DISABLE_TLSv1_0;
+	if (os_strstr(val, "[DISABLE-TLSv1.1]"))
+		flags |= TLS_CONN_DISABLE_TLSv1_1;
+	if (os_strstr(val, "[DISABLE-TLSv1.2]"))
+		flags |= TLS_CONN_DISABLE_TLSv1_2;
+	if (os_strstr(val, "[SUITEB]"))
+		flags |= TLS_CONN_SUITEB;
+	if (os_strstr(val, "[SUITEB-NO-ECDH]"))
+		flags |= TLS_CONN_SUITEB_NO_ECDH | TLS_CONN_SUITEB;
+
+	return flags;
+}
+#endif /* EAP_SERVER */
+
+
 static int hostapd_config_fill(struct hostapd_config *conf,
 			       struct hostapd_bss_config *bss,
 			       const char *buf, char *pos, int line)
@@ -2138,8 +2194,8 @@
 		bss->skip_inactivity_poll = atoi(pos);
 	} else if (os_strcmp(buf, "country_code") == 0) {
 		os_memcpy(conf->country, pos, 2);
-		/* FIX: make this configurable */
-		conf->country[2] = ' ';
+	} else if (os_strcmp(buf, "country3") == 0) {
+		conf->country[2] = strtol(pos, NULL, 16);
 	} else if (os_strcmp(buf, "ieee80211d") == 0) {
 		conf->ieee80211d = atoi(pos);
 	} else if (os_strcmp(buf, "ieee80211h") == 0) {
@@ -2182,6 +2238,8 @@
 		bss->check_crl = atoi(pos);
 	} else if (os_strcmp(buf, "tls_session_lifetime") == 0) {
 		bss->tls_session_lifetime = atoi(pos);
+	} else if (os_strcmp(buf, "tls_flags") == 0) {
+		bss->tls_flags = parse_tls_flags(pos);
 	} else if (os_strcmp(buf, "ocsp_stapling_response") == 0) {
 		os_free(bss->ocsp_stapling_response);
 		bss->ocsp_stapling_response = os_strdup(pos);
@@ -2483,6 +2541,7 @@
 		bss->wpa = atoi(pos);
 	} else if (os_strcmp(buf, "wpa_group_rekey") == 0) {
 		bss->wpa_group_rekey = atoi(pos);
+		bss->wpa_group_rekey_set = 1;
 	} else if (os_strcmp(buf, "wpa_strict_rekey") == 0) {
 		bss->wpa_strict_rekey = atoi(pos);
 	} else if (os_strcmp(buf, "wpa_gmk_rekey") == 0) {
@@ -2511,6 +2570,8 @@
 			return 1;
 		}
 		bss->wpa_pairwise_update_count = (u32) val;
+	} else if (os_strcmp(buf, "wpa_disable_eapol_key_retries") == 0) {
+		bss->wpa_disable_eapol_key_retries = atoi(pos);
 	} else if (os_strcmp(buf, "wpa_passphrase") == 0) {
 		int len = os_strlen(pos);
 		if (len < 8 || len > 63) {
@@ -2569,7 +2630,7 @@
 		if (bss->wpa_pairwise &
 		    (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) {
 			wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'",
-				   bss->wpa_pairwise, pos);
+				   line, pos);
 			return 1;
 		}
 	} else if (os_strcmp(buf, "rsn_pairwise") == 0) {
@@ -2579,7 +2640,7 @@
 		if (bss->rsn_pairwise &
 		    (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) {
 			wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'",
-				   bss->rsn_pairwise, pos);
+				   line, pos);
 			return 1;
 		}
 #ifdef CONFIG_RSN_PREAUTH
@@ -2589,10 +2650,9 @@
 		os_free(bss->rsn_preauth_interfaces);
 		bss->rsn_preauth_interfaces = os_strdup(pos);
 #endif /* CONFIG_RSN_PREAUTH */
-#ifdef CONFIG_PEERKEY
 	} else if (os_strcmp(buf, "peerkey") == 0) {
-		bss->peerkey = atoi(pos);
-#endif /* CONFIG_PEERKEY */
+		wpa_printf(MSG_INFO,
+			   "Line %d: Obsolete peerkey parameter ignored", line);
 #ifdef CONFIG_IEEE80211R_AP
 	} else if (os_strcmp(buf, "mobility_domain") == 0) {
 		if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN ||
@@ -2615,6 +2675,14 @@
 		bss->r0_key_lifetime = atoi(pos);
 	} else if (os_strcmp(buf, "reassociation_deadline") == 0) {
 		bss->reassociation_deadline = atoi(pos);
+	} else if (os_strcmp(buf, "rkh_pos_timeout") == 0) {
+		bss->rkh_pos_timeout = atoi(pos);
+	} else if (os_strcmp(buf, "rkh_neg_timeout") == 0) {
+		bss->rkh_neg_timeout = atoi(pos);
+	} else if (os_strcmp(buf, "rkh_pull_timeout") == 0) {
+		bss->rkh_pull_timeout = atoi(pos);
+	} else if (os_strcmp(buf, "rkh_pull_retries") == 0) {
+		bss->rkh_pull_retries = atoi(pos);
 	} else if (os_strcmp(buf, "r0kh") == 0) {
 		if (add_r0kh(bss, pos) < 0) {
 			wpa_printf(MSG_DEBUG, "Line %d: Invalid r0kh '%s'",
@@ -2711,6 +2779,8 @@
 				   line, pos);
 			return 1;
 		}
+	} else if (os_strcmp(buf, "acs_exclude_dfs") == 0) {
+		conf->acs_exclude_dfs = atoi(pos);
 	} else if (os_strcmp(buf, "channel") == 0) {
 		if (os_strcmp(pos, "acs_survey") == 0) {
 #ifndef CONFIG_ACS
@@ -3106,7 +3176,10 @@
 		}
 	} else if (os_strcmp(buf, "ap_pin") == 0) {
 		os_free(bss->ap_pin);
-		bss->ap_pin = os_strdup(pos);
+		if (*pos == '\0')
+			bss->ap_pin = NULL;
+		else
+			bss->ap_pin = os_strdup(pos);
 	} else if (os_strcmp(buf, "skip_cred_build") == 0) {
 		bss->skip_cred_build = atoi(pos);
 	} else if (os_strcmp(buf, "extra_cred") == 0) {
@@ -3217,12 +3290,12 @@
 		bss->time_zone = os_strdup(pos);
 		if (bss->time_zone == NULL)
 			return 1;
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
 	} else if (os_strcmp(buf, "wnm_sleep_mode") == 0) {
 		bss->wnm_sleep_mode = atoi(pos);
 	} else if (os_strcmp(buf, "bss_transition") == 0) {
 		bss->bss_transition = atoi(pos);
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
 #ifdef CONFIG_INTERWORKING
 	} else if (os_strcmp(buf, "interworking") == 0) {
 		bss->interworking = atoi(pos);
@@ -3445,6 +3518,10 @@
 #ifdef CONFIG_MBO
 	} else if (os_strcmp(buf, "mbo") == 0) {
 		bss->mbo_enabled = atoi(pos);
+	} else if (os_strcmp(buf, "mbo_cell_data_conn_pref") == 0) {
+		bss->mbo_cell_data_conn_pref = atoi(pos);
+	} else if (os_strcmp(buf, "oce") == 0) {
+		bss->oce = atoi(pos);
 #endif /* CONFIG_MBO */
 #ifdef CONFIG_TESTING_OPTIONS
 #define PARSE_TEST_PROBABILITY(_val)				\
@@ -3513,6 +3590,14 @@
 
 		wpabuf_free(bss->own_ie_override);
 		bss->own_ie_override = tmp;
+	} else if (os_strcmp(buf, "sae_reflection_attack") == 0) {
+		bss->sae_reflection_attack = atoi(pos);
+	} else if (os_strcmp(buf, "sae_commit_override") == 0) {
+		wpabuf_free(bss->sae_commit_override);
+		bss->sae_commit_override = wpabuf_parse_bin(pos);
+	} else if (os_strcmp(buf, "sae_password") == 0) {
+		os_free(bss->sae_password);
+		bss->sae_password = os_strdup(pos);
 #endif /* CONFIG_TESTING_OPTIONS */
 	} else if (os_strcmp(buf, "vendor_elements") == 0) {
 		if (parse_wpabuf_hex(line, buf, &bss->vendor_elements, pos))
@@ -3655,6 +3740,8 @@
 	} else if (os_strcmp(buf, "fils_realm") == 0) {
 		if (parse_fils_realm(bss, pos) < 0)
 			return 1;
+	} else if (os_strcmp(buf, "fils_dh_group") == 0) {
+		bss->fils_dh_group = atoi(pos);
 	} else if (os_strcmp(buf, "dhcp_server") == 0) {
 		if (hostapd_parse_ip_addr(pos, &bss->dhcp_server)) {
 			wpa_printf(MSG_ERROR,
@@ -3673,6 +3760,53 @@
 #endif /* CONFIG_FILS */
 	} else if (os_strcmp(buf, "multicast_to_unicast") == 0) {
 		bss->multicast_to_unicast = atoi(pos);
+	} else if (os_strcmp(buf, "broadcast_deauth") == 0) {
+		bss->broadcast_deauth = atoi(pos);
+#ifdef CONFIG_DPP
+	} else if (os_strcmp(buf, "dpp_connector") == 0) {
+		os_free(bss->dpp_connector);
+		bss->dpp_connector = os_strdup(pos);
+	} else if (os_strcmp(buf, "dpp_netaccesskey") == 0) {
+		if (parse_wpabuf_hex(line, buf, &bss->dpp_netaccesskey, pos))
+			return 1;
+	} else if (os_strcmp(buf, "dpp_netaccesskey_expiry") == 0) {
+		bss->dpp_netaccesskey_expiry = strtol(pos, NULL, 0);
+	} else if (os_strcmp(buf, "dpp_csign") == 0) {
+		if (parse_wpabuf_hex(line, buf, &bss->dpp_csign, pos))
+			return 1;
+#endif /* CONFIG_DPP */
+#ifdef CONFIG_OWE
+	} else if (os_strcmp(buf, "owe_transition_bssid") == 0) {
+		if (hwaddr_aton(pos, bss->owe_transition_bssid)) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid owe_transition_bssid",
+				   line);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "owe_transition_ssid") == 0) {
+		size_t slen;
+		char *str = wpa_config_parse_string(pos, &slen);
+
+		if (!str || slen < 1 || slen > SSID_MAX_LEN) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'",
+				   line, pos);
+			os_free(str);
+			return 1;
+		}
+		os_memcpy(bss->owe_transition_ssid, str, slen);
+		bss->owe_transition_ssid_len = slen;
+		os_free(str);
+	} else if (os_strcmp(buf, "owe_transition_ifname") == 0) {
+		os_strlcpy(bss->owe_transition_ifname, pos,
+			   sizeof(bss->owe_transition_ifname));
+	} else if (os_strcmp(buf, "owe_groups") == 0) {
+		if (hostapd_parse_intlist(&bss->owe_groups, pos)) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid owe_groups value '%s'",
+				   line, pos);
+			return 1;
+		}
+#endif /* CONFIG_OWE */
 	} else {
 		wpa_printf(MSG_ERROR,
 			   "Line %d: unknown configuration item '%s'",
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index 62feaa4..b3ef8d3 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -50,6 +50,7 @@
 #include "ap/beacon.h"
 #include "ap/neighbor_db.h"
 #include "ap/rrm.h"
+#include "ap/dpp_hostapd.h"
 #include "wps/wps_defs.h"
 #include "wps/wps.h"
 #include "fst/fst_ctrl_iface.h"
@@ -763,7 +764,7 @@
 #endif /* CONFIG_INTERWORKING */
 
 
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
 
 static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd,
 						const char *cmd)
@@ -838,7 +839,7 @@
 	char *url = NULL;
 	int ret;
 	u8 nei_rep[1000];
-	u8 *nei_pos = nei_rep;
+	int nei_len;
 	u8 mbo[10];
 	size_t mbo_len = 0;
 
@@ -888,99 +889,10 @@
 		WPA_PUT_LE16(&bss_term_dur[10], atoi(end));
 	}
 
-
-	/*
-	 * BSS Transition Candidate List Entries - Neighbor Report elements
-	 * neighbor=<BSSID>,<BSSID Information>,<Operating Class>,
-	 * <Channel Number>,<PHY Type>[,<hexdump of Optional Subelements>]
-	 */
-	pos = cmd;
-	while (pos) {
-		u8 *nei_start;
-		long int val;
-		char *endptr, *tmp;
-
-		pos = os_strstr(pos, " neighbor=");
-		if (!pos)
-			break;
-		if (nei_pos + 15 > nei_rep + sizeof(nei_rep)) {
-			wpa_printf(MSG_DEBUG,
-				   "Not enough room for additional neighbor");
-			return -1;
-		}
-		pos += 10;
-
-		nei_start = nei_pos;
-		*nei_pos++ = WLAN_EID_NEIGHBOR_REPORT;
-		nei_pos++; /* length to be filled in */
-
-		if (hwaddr_aton(pos, nei_pos)) {
-			wpa_printf(MSG_DEBUG, "Invalid BSSID");
-			return -1;
-		}
-		nei_pos += ETH_ALEN;
-		pos += 17;
-		if (*pos != ',') {
-			wpa_printf(MSG_DEBUG, "Missing BSSID Information");
-			return -1;
-		}
-		pos++;
-
-		val = strtol(pos, &endptr, 0);
-		WPA_PUT_LE32(nei_pos, val);
-		nei_pos += 4;
-		if (*endptr != ',') {
-			wpa_printf(MSG_DEBUG, "Missing Operating Class");
-			return -1;
-		}
-		pos = endptr + 1;
-
-		*nei_pos++ = atoi(pos); /* Operating Class */
-		pos = os_strchr(pos, ',');
-		if (pos == NULL) {
-			wpa_printf(MSG_DEBUG, "Missing Channel Number");
-			return -1;
-		}
-		pos++;
-
-		*nei_pos++ = atoi(pos); /* Channel Number */
-		pos = os_strchr(pos, ',');
-		if (pos == NULL) {
-			wpa_printf(MSG_DEBUG, "Missing PHY Type");
-			return -1;
-		}
-		pos++;
-
-		*nei_pos++ = atoi(pos); /* PHY Type */
-		end = os_strchr(pos, ' ');
-		tmp = os_strchr(pos, ',');
-		if (tmp && (!end || tmp < end)) {
-			/* Optional Subelements (hexdump) */
-			size_t len;
-
-			pos = tmp + 1;
-			end = os_strchr(pos, ' ');
-			if (end)
-				len = end - pos;
-			else
-				len = os_strlen(pos);
-			if (nei_pos + len / 2 > nei_rep + sizeof(nei_rep)) {
-				wpa_printf(MSG_DEBUG,
-					   "Not enough room for neighbor subelements");
-				return -1;
-			}
-			if (len & 0x01 ||
-			    hexstr2bin(pos, nei_pos, len / 2) < 0) {
-				wpa_printf(MSG_DEBUG,
-					   "Invalid neighbor subelement info");
-				return -1;
-			}
-			nei_pos += len / 2;
-			pos = end;
-		}
-
-		nei_start[1] = nei_pos - nei_start - 2;
-	}
+	nei_len = ieee802_11_parse_candidate_list(cmd, nei_rep,
+						  sizeof(nei_rep));
+	if (nei_len < 0)
+		return -1;
 
 	pos = os_strstr(cmd, " url=");
 	if (pos) {
@@ -1067,9 +979,8 @@
 
 	ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer,
 				  valid_int, bss_term_dur, url,
-				  nei_pos > nei_rep ? nei_rep : NULL,
-				  nei_pos - nei_rep, mbo_len ? mbo : NULL,
-				  mbo_len);
+				  nei_len ? nei_rep : NULL, nei_len,
+				  mbo_len ? mbo : NULL, mbo_len);
 #ifdef CONFIG_MBO
 fail:
 #endif /* CONFIG_MBO */
@@ -1077,7 +988,7 @@
 	return ret;
 }
 
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
 
 
 static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd,
@@ -1190,6 +1101,24 @@
 	}
 #endif /* CONFIG_FILS */
 
+#ifdef CONFIG_OWE
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) {
+		ret = os_snprintf(pos, end - pos, "OWE ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_OWE */
+
+#ifdef CONFIG_DPP
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) {
+		ret = os_snprintf(pos, end - pos, "DPP ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_DPP */
+
 	if (pos > buf && *(pos - 1) == ' ') {
 		*(pos - 1) = '\0';
 		pos--;
@@ -1359,6 +1288,20 @@
 		hapd->ext_mgmt_frame_handling = atoi(value);
 	} else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) {
 		hapd->ext_eapol_frame_io = atoi(value);
+#ifdef CONFIG_DPP
+	} else if (os_strcasecmp(cmd, "dpp_config_obj_override") == 0) {
+		os_free(hapd->dpp_config_obj_override);
+		hapd->dpp_config_obj_override = os_strdup(value);
+	} else if (os_strcasecmp(cmd, "dpp_discovery_override") == 0) {
+		os_free(hapd->dpp_discovery_override);
+		hapd->dpp_discovery_override = os_strdup(value);
+	} else if (os_strcasecmp(cmd, "dpp_groups_override") == 0) {
+		os_free(hapd->dpp_groups_override);
+		hapd->dpp_groups_override = os_strdup(value);
+	} else if (os_strcasecmp(cmd,
+				 "dpp_ignore_netaccesskey_mismatch") == 0) {
+		hapd->dpp_ignore_netaccesskey_mismatch = atoi(value);
+#endif /* CONFIG_DPP */
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifdef CONFIG_MBO
 	} else if (os_strcasecmp(cmd, "mbo_assoc_disallow") == 0) {
@@ -1379,6 +1322,11 @@
 		 * disallowing station logic.
 		 */
 #endif /* CONFIG_MBO */
+#ifdef CONFIG_DPP
+	} else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) {
+		os_free(hapd->dpp_configurator_params);
+		hapd->dpp_configurator_params = os_strdup(value);
+#endif /* CONFIG_DPP */
 	} else {
 		struct sta_info *sta;
 		struct vlan_description vlan_id;
@@ -1561,6 +1509,67 @@
 }
 
 
+static int hostapd_ctrl_iface_mgmt_tx_status_process(struct hostapd_data *hapd,
+						     char *cmd)
+{
+	char *pos, *param;
+	size_t len;
+	u8 *buf;
+	int stype = 0, ok = 0;
+	union wpa_event_data event;
+
+	if (!hapd->ext_mgmt_frame_handling)
+		return -1;
+
+	/* stype=<val> ok=<0/1> buf=<frame hexdump> */
+
+	wpa_printf(MSG_DEBUG, "External MGMT TX status process: %s", cmd);
+
+	pos = cmd;
+	param = os_strstr(pos, "stype=");
+	if (param) {
+		param += 6;
+		stype = atoi(param);
+	}
+
+	param = os_strstr(pos, " ok=");
+	if (param) {
+		param += 4;
+		ok = atoi(param);
+	}
+
+	param = os_strstr(pos, " buf=");
+	if (!param)
+		return -1;
+	param += 5;
+
+	len = os_strlen(param);
+	if (len & 1)
+		return -1;
+	len /= 2;
+
+	buf = os_malloc(len);
+	if (!buf || hexstr2bin(param, buf, len) < 0) {
+		os_free(buf);
+		return -1;
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	event.tx_status.type = WLAN_FC_TYPE_MGMT;
+	event.tx_status.data = buf;
+	event.tx_status.data_len = len;
+	event.tx_status.stype = stype;
+	event.tx_status.ack = ok;
+	hapd->ext_mgmt_frame_handling = 0;
+	wpa_supplicant_event(hapd, EVENT_TX_STATUS, &event);
+	hapd->ext_mgmt_frame_handling = 1;
+
+	os_free(buf);
+
+	return 0;
+}
+
+
 static int hostapd_ctrl_iface_mgmt_rx_process(struct hostapd_data *hapd,
 					      char *cmd)
 {
@@ -1940,6 +1949,245 @@
 #endif /* WPA_TRACE_BFD */
 }
 
+
+static int hostapd_ctrl_reset_pn(struct hostapd_data *hapd, const char *cmd)
+{
+	struct sta_info *sta;
+	u8 addr[ETH_ALEN];
+	u8 zero[WPA_TK_MAX_LEN];
+
+	os_memset(zero, 0, sizeof(zero));
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+#ifdef CONFIG_IEEE80211W
+	if (is_broadcast_ether_addr(addr) && os_strstr(cmd, "IGTK")) {
+		if (hapd->last_igtk_alg == WPA_ALG_NONE)
+			return -1;
+
+		wpa_printf(MSG_INFO, "TESTING: Reset IPN for IGTK");
+
+		/* First, use a zero key to avoid any possible duplicate key
+		 * avoidance in the driver. */
+		if (hostapd_drv_set_key(hapd->conf->iface, hapd,
+					hapd->last_igtk_alg,
+					broadcast_ether_addr,
+					hapd->last_igtk_key_idx, 1, NULL, 0,
+					zero, hapd->last_igtk_len) < 0)
+			return -1;
+
+		/* Set the previously configured key to reset its TSC */
+		return hostapd_drv_set_key(hapd->conf->iface, hapd,
+					   hapd->last_igtk_alg,
+					   broadcast_ether_addr,
+					   hapd->last_igtk_key_idx, 1, NULL, 0,
+					   hapd->last_igtk,
+					   hapd->last_igtk_len);
+	}
+#endif /* CONFIG_IEEE80211W */
+
+	if (is_broadcast_ether_addr(addr)) {
+		if (hapd->last_gtk_alg == WPA_ALG_NONE)
+			return -1;
+
+		wpa_printf(MSG_INFO, "TESTING: Reset PN for GTK");
+
+		/* First, use a zero key to avoid any possible duplicate key
+		 * avoidance in the driver. */
+		if (hostapd_drv_set_key(hapd->conf->iface, hapd,
+					hapd->last_gtk_alg,
+					broadcast_ether_addr,
+					hapd->last_gtk_key_idx, 1, NULL, 0,
+					zero, hapd->last_gtk_len) < 0)
+			return -1;
+
+		/* Set the previously configured key to reset its TSC */
+		return hostapd_drv_set_key(hapd->conf->iface, hapd,
+					   hapd->last_gtk_alg,
+					   broadcast_ether_addr,
+					   hapd->last_gtk_key_idx, 1, NULL, 0,
+					   hapd->last_gtk, hapd->last_gtk_len);
+	}
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta)
+		return -1;
+
+	if (sta->last_tk_alg == WPA_ALG_NONE)
+		return -1;
+
+	wpa_printf(MSG_INFO, "TESTING: Reset PN for " MACSTR,
+		   MAC2STR(sta->addr));
+
+	/* First, use a zero key to avoid any possible duplicate key avoidance
+	 * in the driver. */
+	if (hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg,
+				sta->addr, sta->last_tk_key_idx, 1, NULL, 0,
+				zero, sta->last_tk_len) < 0)
+		return -1;
+
+	/* Set the previously configured key to reset its TSC/RSC */
+	return hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg,
+				   sta->addr, sta->last_tk_key_idx, 1, NULL, 0,
+				   sta->last_tk, sta->last_tk_len);
+}
+
+
+static int hostapd_ctrl_set_key(struct hostapd_data *hapd, const char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	const char *pos = cmd;
+	enum wpa_alg alg;
+	int idx, set_tx;
+	u8 seq[6], key[WPA_TK_MAX_LEN];
+	size_t key_len;
+
+	/* parameters: alg addr idx set_tx seq key */
+
+	alg = atoi(pos);
+	pos = os_strchr(pos, ' ');
+	if (!pos)
+		return -1;
+	pos++;
+	if (hwaddr_aton(pos, addr))
+		return -1;
+	pos += 17;
+	if (*pos != ' ')
+		return -1;
+	pos++;
+	idx = atoi(pos);
+	pos = os_strchr(pos, ' ');
+	if (!pos)
+		return -1;
+	pos++;
+	set_tx = atoi(pos);
+	pos = os_strchr(pos, ' ');
+	if (!pos)
+		return -1;
+	pos++;
+	if (hexstr2bin(pos, seq, sizeof(6)) < 0)
+		return -1;
+	pos += 2 * 6;
+	if (*pos != ' ')
+		return -1;
+	pos++;
+	key_len = os_strlen(pos) / 2;
+	if (hexstr2bin(pos, key, key_len) < 0)
+		return -1;
+
+	wpa_printf(MSG_INFO, "TESTING: Set key");
+	return hostapd_drv_set_key(hapd->conf->iface, hapd, alg, addr, idx,
+				   set_tx, seq, 6, key, key_len);
+}
+
+
+static void restore_tk(void *ctx1, void *ctx2)
+{
+	struct hostapd_data *hapd = ctx1;
+	struct sta_info *sta = ctx2;
+
+	wpa_printf(MSG_INFO, "TESTING: Restore TK for " MACSTR,
+		   MAC2STR(sta->addr));
+	/* This does not really restore the TSC properly, so this will result
+	 * in replay protection issues for now since there is no clean way of
+	 * preventing encryption of a single EAPOL frame. */
+	hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg,
+			    sta->addr, sta->last_tk_key_idx, 1, NULL, 0,
+			    sta->last_tk, sta->last_tk_len);
+}
+
+
+static int hostapd_ctrl_resend_m1(struct hostapd_data *hapd, const char *cmd)
+{
+	struct sta_info *sta;
+	u8 addr[ETH_ALEN];
+	int plain = os_strstr(cmd, "plaintext") != NULL;
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta || !sta->wpa_sm)
+		return -1;
+
+	if (plain && sta->last_tk_alg == WPA_ALG_NONE)
+		plain = 0; /* no need for special processing */
+	if (plain) {
+		wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR,
+			   MAC2STR(sta->addr));
+		hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE,
+				    sta->addr, sta->last_tk_key_idx, 0, NULL, 0,
+				    NULL, 0);
+	}
+
+	wpa_printf(MSG_INFO, "TESTING: Send M1 to " MACSTR, MAC2STR(sta->addr));
+	return wpa_auth_resend_m1(sta->wpa_sm,
+				  os_strstr(cmd, "change-anonce") != NULL,
+				  plain ? restore_tk : NULL, hapd, sta);
+}
+
+
+static int hostapd_ctrl_resend_m3(struct hostapd_data *hapd, const char *cmd)
+{
+	struct sta_info *sta;
+	u8 addr[ETH_ALEN];
+	int plain = os_strstr(cmd, "plaintext") != NULL;
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta || !sta->wpa_sm)
+		return -1;
+
+	if (plain && sta->last_tk_alg == WPA_ALG_NONE)
+		plain = 0; /* no need for special processing */
+	if (plain) {
+		wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR,
+			   MAC2STR(sta->addr));
+		hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE,
+				    sta->addr, sta->last_tk_key_idx, 0, NULL, 0,
+				    NULL, 0);
+	}
+
+	wpa_printf(MSG_INFO, "TESTING: Send M3 to " MACSTR, MAC2STR(sta->addr));
+	return wpa_auth_resend_m3(sta->wpa_sm,
+				  plain ? restore_tk : NULL, hapd, sta);
+}
+
+
+static int hostapd_ctrl_resend_group_m1(struct hostapd_data *hapd,
+					const char *cmd)
+{
+	struct sta_info *sta;
+	u8 addr[ETH_ALEN];
+	int plain = os_strstr(cmd, "plaintext") != NULL;
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta || !sta->wpa_sm)
+		return -1;
+
+	if (plain && sta->last_tk_alg == WPA_ALG_NONE)
+		plain = 0; /* no need for special processing */
+	if (plain) {
+		wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR,
+			   MAC2STR(sta->addr));
+		hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE,
+				    sta->addr, sta->last_tk_key_idx, 0, NULL, 0,
+				    NULL, 0);
+	}
+
+	wpa_printf(MSG_INFO,
+		   "TESTING: Send group M1 for the same GTK and zero RSC to "
+		   MACSTR, MAC2STR(sta->addr));
+	return wpa_auth_resend_group_m1(sta->wpa_sm,
+					plain ? restore_tk : NULL, hapd, sta);
+}
+
 #endif /* CONFIG_TESTING_OPTIONS */
 
 
@@ -2587,7 +2835,7 @@
 		if (hostapd_ctrl_iface_hs20_deauth_req(hapd, buf + 16))
 			reply_len = -1;
 #endif /* CONFIG_HS20 */
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
 	} else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) {
 		if (hostapd_ctrl_iface_disassoc_imminent(hapd, buf + 18))
 			reply_len = -1;
@@ -2597,7 +2845,7 @@
 	} else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) {
 		if (hostapd_ctrl_iface_bss_tm_req(hapd, buf + 11))
 			reply_len = -1;
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
 	} else if (os_strcmp(buf, "GET_CONFIG") == 0) {
 		reply_len = hostapd_ctrl_iface_get_config(hapd, reply,
 							  reply_size);
@@ -2626,6 +2874,10 @@
 	} else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) {
 		if (hostapd_ctrl_iface_mgmt_tx(hapd, buf + 8))
 			reply_len = -1;
+	} else if (os_strncmp(buf, "MGMT_TX_STATUS_PROCESS ", 23) == 0) {
+		if (hostapd_ctrl_iface_mgmt_tx_status_process(hapd,
+							      buf + 23) < 0)
+			reply_len = -1;
 	} else if (os_strncmp(buf, "MGMT_RX_PROCESS ", 16) == 0) {
 		if (hostapd_ctrl_iface_mgmt_rx_process(hapd, buf + 16) < 0)
 			reply_len = -1;
@@ -2652,6 +2904,21 @@
 			reply_len = -1;
 	} else if (os_strcmp(buf, "GET_FAIL") == 0) {
 		reply_len = hostapd_ctrl_get_fail(hapd, reply, reply_size);
+	} else if (os_strncmp(buf, "RESET_PN ", 9) == 0) {
+		if (hostapd_ctrl_reset_pn(hapd, buf + 9) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "SET_KEY ", 8) == 0) {
+		if (hostapd_ctrl_set_key(hapd, buf + 8) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "RESEND_M1 ", 10) == 0) {
+		if (hostapd_ctrl_resend_m1(hapd, buf + 10) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "RESEND_M3 ", 10) == 0) {
+		if (hostapd_ctrl_resend_m3(hapd, buf + 10) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "RESEND_GROUP_M1 ", 16) == 0) {
+		if (hostapd_ctrl_resend_group_m1(hapd, buf + 16) < 0)
+			reply_len = -1;
 #endif /* CONFIG_TESTING_OPTIONS */
 	} else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
 		if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12))
@@ -2683,6 +2950,9 @@
 							  reply_size);
 	} else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) {
 		hostapd_ctrl_iface_pmksa_flush(hapd);
+	} else if (os_strncmp(buf, "PMKSA_ADD ", 10) == 0) {
+		if (hostapd_ctrl_iface_pmksa_add(hapd, buf + 10) < 0)
+			reply_len = -1;
 	} else if (os_strncmp(buf, "SET_NEIGHBOR ", 13) == 0) {
 		if (hostapd_ctrl_iface_set_neighbor(hapd, buf + 13))
 			reply_len = -1;
@@ -2703,6 +2973,70 @@
 						      reply_size);
 	} else if (os_strcmp(buf, "TERMINATE") == 0) {
 		eloop_terminate();
+#ifdef CONFIG_DPP
+	} else if (os_strncmp(buf, "DPP_QR_CODE ", 12) == 0) {
+		res = hostapd_dpp_qr_code(hapd, buf + 12);
+		if (res < 0) {
+			reply_len = -1;
+		} else {
+			reply_len = os_snprintf(reply, reply_size, "%d", res);
+			if (os_snprintf_error(reply_size, reply_len))
+				reply_len = -1;
+		}
+	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_GEN ", 18) == 0) {
+		res = hostapd_dpp_bootstrap_gen(hapd, buf + 18);
+		if (res < 0) {
+			reply_len = -1;
+		} else {
+			reply_len = os_snprintf(reply, reply_size, "%d", res);
+			if (os_snprintf_error(reply_size, reply_len))
+				reply_len = -1;
+		}
+	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_REMOVE ", 21) == 0) {
+		if (hostapd_dpp_bootstrap_remove(hapd, buf + 21) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_GET_URI ", 22) == 0) {
+		const char *uri;
+
+		uri = hostapd_dpp_bootstrap_get_uri(hapd, atoi(buf + 22));
+		if (!uri) {
+			reply_len = -1;
+		} else {
+			reply_len = os_snprintf(reply, reply_size, "%s", uri);
+			if (os_snprintf_error(reply_size, reply_len))
+				reply_len = -1;
+		}
+	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_INFO ", 19) == 0) {
+		reply_len = hostapd_dpp_bootstrap_info(hapd, atoi(buf + 19),
+						       reply, reply_size);
+	} else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) {
+		if (hostapd_dpp_auth_init(hapd, buf + 13) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_ADD", 20) == 0) {
+		res = hostapd_dpp_configurator_add(hapd, buf + 20);
+		if (res < 0) {
+			reply_len = -1;
+		} else {
+			reply_len = os_snprintf(reply, reply_size, "%d", res);
+			if (os_snprintf_error(reply_size, reply_len))
+				reply_len = -1;
+		}
+	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_REMOVE ", 24) == 0) {
+		if (hostapd_dpp_configurator_remove(hapd, buf + 24) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) {
+		res = hostapd_dpp_pkex_add(hapd, buf + 12);
+		if (res < 0) {
+			reply_len = -1;
+		} else {
+			reply_len = os_snprintf(reply, reply_size, "%d", res);
+			if (os_snprintf_error(reply_size, reply_len))
+				reply_len = -1;
+		}
+	} else if (os_strncmp(buf, "DPP_PKEX_REMOVE ", 16) == 0) {
+		if (hostapd_dpp_pkex_remove(hapd, buf + 16) < 0)
+			reply_len = -1;
+#endif /* CONFIG_DPP */
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
diff --git a/hostapd/defconfig b/hostapd/defconfig
index 9ade580..c67c662 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -50,9 +50,6 @@
 # WPA2/IEEE 802.11i RSN pre-authentication
 CONFIG_RSN_PREAUTH=y
 
-# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
-CONFIG_PEERKEY=y
-
 # IEEE 802.11w (management frame protection)
 CONFIG_IEEE80211W=y
 
@@ -265,6 +262,7 @@
 # openssl = OpenSSL (default)
 # gnutls = GnuTLS
 # internal = Internal TLSv1 implementation (experimental)
+# linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
 # none = Empty template
 #CONFIG_TLS=openssl
 
@@ -277,6 +275,10 @@
 # can be enabled to enable use of stronger crypto algorithms.
 #CONFIG_TLSV12=y
 
+# Select which ciphers to use by default with OpenSSL if the user does not
+# specify them.
+#CONFIG_TLS_DEFAULT_CIPHERS="DEFAULT:!EXP:!LOW"
+
 # If CONFIG_TLS=internal is used, additional library and include paths are
 # needed for LibTomMath. Alternatively, an integrated, minimal version of
 # LibTomMath can be used. See beginning of libtommath.c for details on benefits
@@ -357,7 +359,17 @@
 # Note: This is an experimental and not yet complete implementation. This
 # should not be enabled for production use.
 #CONFIG_FILS=y
+# FILS shared key authentication with PFS
+#CONFIG_FILS_SK_PFS=y
 
 # Include internal line edit mode in hostapd_cli. This can be used to provide
 # limited command line editing and history support.
 #CONFIG_WPA_CLI_EDIT=y
+
+# Opportunistic Wireless Encryption (OWE)
+# Experimental implementation of draft-harkins-owe-07.txt
+#CONFIG_OWE=y
+
+# Override default value for the wpa_disable_eapol_key_retries configuration
+# parameter. See that parameter in hostapd.conf for more details.
+#CFLAGS += -DDEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES=1
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index c4eebff..f558855 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -98,8 +98,25 @@
 # Country code (ISO/IEC 3166-1). Used to set regulatory domain.
 # Set as needed to indicate country in which device is operating.
 # This can limit available channels and transmit power.
+# These two octets are used as the first two octets of the Country String
+# (dot11CountryString)
 #country_code=US
 
+# The third octet of the Country String (dot11CountryString)
+# This parameter is used to set the third octet of the country string.
+#
+# All environments of the current frequency band and country (default)
+#country3=0x20
+# Outdoor environment only
+#country3=0x4f
+# Indoor environment only
+#country3=0x49
+# Noncountry entity (country_code=XX)
+#country3=0x58
+# IEEE 802.11 standard Annex E table indication: 0x01 .. 0x1f
+# Annex E, Table E-4 (Global operating classes)
+#country3=0x04
+
 # Enable IEEE 802.11d. This advertises the country_code and the set of allowed
 # channels and transmit power levels based on the regulatory limits. The
 # country_code setting must be configured with the correct country for
@@ -182,6 +199,11 @@
 #chanlist=100 104 108 112 116
 #chanlist=1 6 11-13
 
+# Exclude DFS channels from ACS
+# This option can be used to exclude all DFS channels from the ACS channel list
+# in cases where the driver supports DFS channels.
+#acs_exclude_dfs=1
+
 # Beacon interval in kus (1.024 ms) (default: 100; range 15..65535)
 beacon_int=100
 
@@ -511,6 +533,10 @@
 #
 #multicast_to_unicast=0
 
+# Send broadcast Deauthentication frame on AP start/stop
+# Default: 1 (enabled)
+#broadcast_deauth=1
+
 ##### IEEE 802.11n related configuration ######################################
 
 # ieee80211n: Whether IEEE 802.11n (HT) is enabled
@@ -905,7 +931,8 @@
 # OpenSSL cipher string
 #
 # This is an OpenSSL specific configuration option for configuring the default
-# ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the default.
+# ciphers. If not set, the value configured at build time ("DEFAULT:!EXP:!LOW"
+# by default) is used.
 # See https://www.openssl.org/docs/apps/ciphers.html for OpenSSL documentation
 # on cipher suite configuration. This is applicable only if hostapd is built to
 # use OpenSSL.
@@ -1256,7 +1283,10 @@
 
 # Time interval for rekeying GTK (broadcast/multicast encryption keys) in
 # seconds. (dot11RSNAConfigGroupRekeyTime)
-#wpa_group_rekey=600
+# This defaults to 86400 seconds (once per day) when using CCMP/GCMP as the
+# group cipher and 600 seconds (once per 10 minutes) when using TKIP as the
+# group cipher.
+#wpa_group_rekey=86400
 
 # Rekey GTK when any STA that possesses the current GTK is leaving the BSS.
 # (dot11RSNAConfigGroupRekeyStrict)
@@ -1285,6 +1315,30 @@
 # Range 1..4294967295; default: 4
 #wpa_pairwise_update_count=4
 
+# Workaround for key reinstallation attacks
+#
+# This parameter can be used to disable retransmission of EAPOL-Key frames that
+# are used to install keys (EAPOL-Key message 3/4 and group message 1/2). This
+# is similar to setting wpa_group_update_count=1 and
+# wpa_pairwise_update_count=1, but with no impact to message 1/4 and with
+# extended timeout on the response to avoid causing issues with stations that
+# may use aggressive power saving have very long time in replying to the
+# EAPOL-Key messages.
+#
+# This option can be used to work around key reinstallation attacks on the
+# station (supplicant) side in cases those station devices cannot be updated
+# for some reason. By removing the retransmissions the attacker cannot cause
+# key reinstallation with a delayed frame transmission. This is related to the
+# station side vulnerabilities CVE-2017-13077, CVE-2017-13078, CVE-2017-13079,
+# CVE-2017-13080, and CVE-2017-13081.
+#
+# This workaround might cause interoperability issues and reduced robustness of
+# key negotiation especially in environments with heavy traffic load due to the
+# number of attempts to perform the key exchange is reduced significantly. As
+# such, this workaround is disabled by default (unless overridden in build
+# configuration). To enable this, set the parameter to 1.
+#wpa_disable_eapol_key_retries=1
+
 # Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up
 # roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN
 # authentication and key handshake before actually associating with a new AP.
@@ -1300,12 +1354,6 @@
 # one.
 #rsn_preauth_interfaces=eth0
 
-# peerkey: Whether PeerKey negotiation for direct links (IEEE 802.11e) is
-# allowed. This is only used with RSN/WPA2.
-# 0 = disabled (default)
-# 1 = enabled
-#peerkey=1
-
 # ieee80211w: Whether management frame protection (MFP) is enabled
 # 0 = disabled (default)
 # 1 = optional
@@ -1348,6 +1396,15 @@
 # 1 = enabled
 #okc=1
 
+# SAE password
+# This parameter can be used to set a password for SAE. By default, the
+# wpa_passphrase value is used if this separate parameter is not used, but
+# wpa_passphrase follows the WPA-PSK constraints (8..63 characters) even though
+# SAE passwords do not have such constraints. If the BSS enabled both SAE and
+# WPA-PSK and both values are set, SAE uses the sae_password value and WPA-PSK
+# uses the wpa_passphrase value.
+#sae_password=secret
+
 # SAE threshold for anti-clogging mechanism (dot11RSNASAEAntiCloggingThreshold)
 # This parameter defines how many open SAE instances can be in progress at the
 # same time before the anti-clogging mechanism is taken into use.
@@ -1372,6 +1429,29 @@
 #fils_realm=example.com
 #fils_realm=example.org
 
+# FILS DH Group for PFS
+# 0 = PFS disabled with FILS shared key authentication (default)
+# 1-65535 DH Group to use for FILS PFS
+#fils_dh_group=0
+
+# OWE DH groups
+# OWE implementations are required to support group 19 (NIST P-256). All groups
+# that are supported by the implementation (e.g., groups 19, 20, and 21 when
+# using OpenSSL) are enabled by default. This configuration parameter can be
+# used to specify a limited set of allowed groups. The group values are listed
+# in the IANA registry:
+# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-10
+#owe_groups=19 20 21
+
+# OWE transition mode configuration
+# Pointer to the matching open/OWE BSS
+#owe_transition_bssid=<bssid>
+# SSID in same format as ssid2 described above.
+#owe_transition_ssid=<SSID>
+# Alternatively, OWE transition mode BSSID/SSID can be configured with a
+# reference to a BSS operated by this hostapd process.
+#owe_transition_ifname=<ifname>
+
 # DHCP server for FILS HLP
 # If configured, hostapd will act as a DHCP relay for all FILS HLP requests
 # that include a DHCPDISCOVER message and send them to the specific DHCP
@@ -1426,22 +1506,52 @@
 #reassociation_deadline=1000
 
 # List of R0KHs in the same Mobility Domain
-# format: <MAC address> <NAS Identifier> <128-bit key as hex string>
+# format: <MAC address> <NAS Identifier> <256-bit key as hex string>
 # This list is used to map R0KH-ID (NAS Identifier) to a destination MAC
 # address when requesting PMK-R1 key from the R0KH that the STA used during the
 # Initial Mobility Domain Association.
-#r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f
-#r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff
+#r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
+#r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff
 # And so on.. One line per R0KH.
+# Wildcard entry:
+# Upon receiving a response from R0KH, it will be added to this list, so
+# subsequent requests won't be broadcast. If R0KH does not reply, it will be
+# blacklisted.
+#r0kh=ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff
 
 # List of R1KHs in the same Mobility Domain
-# format: <MAC address> <R1KH-ID> <128-bit key as hex string>
+# format: <MAC address> <R1KH-ID> <256-bit key as hex string>
 # This list is used to map R1KH-ID to a destination MAC address when sending
 # PMK-R1 key from the R0KH. This is also the list of authorized R1KHs in the MD
 # that can request PMK-R1 keys.
-#r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f
-#r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff
+#r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
+#r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff
 # And so on.. One line per R1KH.
+# Wildcard entry:
+# Upon receiving a request from an R1KH not yet known, it will be added to this
+# list and thus will receive push notifications.
+#r1kh=00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff
+
+# Timeout (seconds) for newly discovered R0KH/R1KH (see wildcard entries above)
+# Special values: 0 -> do not expire
+# Warning: do not cache implies no sequence number validation with wildcards
+#rkh_pos_timeout=86400 (default = 1 day)
+
+# Timeout (milliseconds) for requesting PMK-R1 from R0KH using PULL request
+# and number of retries.
+#rkh_pull_timeout=1000 (default = 1 second)
+#rkh_pull_retries=4 (default)
+
+# Timeout (seconds) for non replying R0KH (see wildcard entries above)
+# Special values: 0 -> do not cache
+# default: 60 seconds
+#rkh_neg_timeout=60
+
+# Note: The R0KH/R1KH keys used to be 128-bit in length before the message
+# format was changed. That shorter key length is still supported for backwards
+# compatibility of the configuration files. If such a shorter key is used, a
+# 256-bit key is derived from it. For new deployments, configuring the 256-bit
+# key is recommended.
 
 # Whether PMK-R1 push is enabled at R0KH
 # 0 = do not push PMK-R1 to all configured R1KHs (default)
@@ -2014,6 +2124,28 @@
 #
 #osu_server_uri=...
 
+##### Multiband Operation (MBO) ###############################################
+#
+# MBO enabled
+# 0 = disabled (default)
+# 1 = enabled
+#mbo=1
+#
+# Cellular data connection preference
+# 0 = Excluded - AP does not want STA to use the cellular data connection
+# 1 = AP prefers the STA not to use cellular data connection
+# 255 = AP prefers the STA to use cellular data connection
+#mbo_cell_data_conn_pref=1
+
+##### Optimized Connectivity Experience (OCE) #################################
+#
+# Enable OCE specific features (bitmap)
+# BIT(0) - Reserved
+# Set BIT(1) (= 2) to enable OCE in STA-CFON mode
+# Set BIT(2) (= 4) to enable OCE in AP mode
+# Default is 0 = OCE disabled
+#oce=0
+
 ##### Fast Session Transfer (FST) support #####################################
 #
 # The options in this section are only available when the build configuration
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index f8d1eda..5b0882a 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -1024,7 +1024,7 @@
 
 static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	char cmd[256];
+	char cmd[2048];
 	int res;
 
 	if (argc != 2) {
@@ -1364,6 +1364,80 @@
 }
 
 
+#ifdef CONFIG_DPP
+
+static int hostapd_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	return hostapd_cli_cmd(ctrl, "DPP_QR_CODE", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_bootstrap_gen(struct wpa_ctrl *ctrl, int argc,
+					     char *argv[])
+{
+	return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_GEN", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_bootstrap_remove(struct wpa_ctrl *ctrl, int argc,
+						char *argv[])
+{
+	return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_REMOVE", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_bootstrap_get_uri(struct wpa_ctrl *ctrl,
+						 int argc, char *argv[])
+{
+	return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_GET_URI", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_bootstrap_info(struct wpa_ctrl *ctrl, int argc,
+					      char *argv[])
+{
+	return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_INFO", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_auth_init(struct wpa_ctrl *ctrl, int argc,
+					 char *argv[])
+{
+	return hostapd_cli_cmd(ctrl, "DPP_AUTH_INIT", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_configurator_add(struct wpa_ctrl *ctrl, int argc,
+						char *argv[])
+{
+	return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_ADD", 0, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_configurator_remove(struct wpa_ctrl *ctrl,
+						   int argc, char *argv[])
+{
+	return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_REMOVE", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_pkex_add(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	return hostapd_cli_cmd(ctrl, "DPP_PKEX_ADD", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_pkex_remove(struct wpa_ctrl *ctrl, int argc,
+					   char *argv[])
+{
+	return hostapd_cli_cmd(ctrl, "DPP_PKEX_REMOVE", 1, argc, argv);
+}
+
+#endif /* CONFIG_DPP */
+
+
 struct hostapd_cli_cmd {
 	const char *cmd;
 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
@@ -1499,6 +1573,29 @@
 	  " = send FTM range request"},
 	{ "driver_flags", hostapd_cli_cmd_driver_flags, NULL,
 	  " = show supported driver flags"},
+#ifdef CONFIG_DPP
+	{ "dpp_qr_code", hostapd_cli_cmd_dpp_qr_code, NULL,
+	  "report a scanned DPP URI from a QR Code" },
+	{ "dpp_bootstrap_gen", hostapd_cli_cmd_dpp_bootstrap_gen, NULL,
+	  "type=<qrcode> [chan=..] [mac=..] [info=..] [curve=..] [key=..] = generate DPP bootstrap information" },
+	{ "dpp_bootstrap_remove", hostapd_cli_cmd_dpp_bootstrap_remove, NULL,
+	  "*|<id> = remove DPP bootstrap information" },
+	{ "dpp_bootstrap_get_uri", hostapd_cli_cmd_dpp_bootstrap_get_uri, NULL,
+	  "<id> = get DPP bootstrap URI" },
+	{ "dpp_bootstrap_info", hostapd_cli_cmd_dpp_bootstrap_info, NULL,
+	  "<id> = show DPP bootstrap information" },
+	{ "dpp_auth_init", hostapd_cli_cmd_dpp_auth_init, NULL,
+	  "peer=<id> [own=<id>] = initiate DPP bootstrapping" },
+	{ "dpp_configurator_add", hostapd_cli_cmd_dpp_configurator_add, NULL,
+	  "[curve=..] [key=..] = add DPP configurator" },
+	{ "dpp_configurator_remove", hostapd_cli_cmd_dpp_configurator_remove,
+	  NULL,
+	  "*|<id> = remove DPP configurator" },
+	{ "dpp_pkex_add", hostapd_cli_cmd_dpp_pkex_add, NULL,
+	  "add PKEX code" },
+	{ "dpp_pkex_remove", hostapd_cli_cmd_dpp_pkex_remove, NULL,
+	  "*|<id> = remove DPP pkex information" },
+#endif /* CONFIG_DPP */
 	{ NULL, NULL, NULL, NULL }
 };
 
@@ -1610,7 +1707,7 @@
 	if (ctrl_conn == NULL)
 		return;
 	while (wpa_ctrl_pending(ctrl)) {
-		char buf[256];
+		char buf[4096];
 		size_t len = sizeof(buf) - 1;
 		if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
 			buf[len] = '\0';
diff --git a/hostapd/main.c b/hostapd/main.c
index 593267c..ce94d05 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -666,6 +666,9 @@
 	interfaces.global_iface_name = NULL;
 	interfaces.global_ctrl_sock = -1;
 	dl_list_init(&interfaces.global_ctrl_dst);
+#ifdef CONFIG_ETH_P_OUI
+	dl_list_init(&interfaces.eth_p_oui);
+#endif /* CONFIG_ETH_P_OUI */
 
 	for (;;) {
 		c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:");
@@ -866,8 +869,27 @@
 	 */
 	interfaces.terminate_on_error = interfaces.count;
 	for (i = 0; i < interfaces.count; i++) {
-		if (hostapd_driver_init(interfaces.iface[i]) ||
-		    hostapd_setup_interface(interfaces.iface[i]))
+		if (hostapd_driver_init(interfaces.iface[i]))
+			goto out;
+#ifdef CONFIG_MBO
+		for (j = 0; j < interfaces.iface[i]->num_bss; j++) {
+			struct hostapd_data *hapd = interfaces.iface[i]->bss[j];
+
+			if (hapd && (hapd->conf->oce & OCE_STA_CFON) &&
+			    (interfaces.iface[i]->drv_flags &
+			     WPA_DRIVER_FLAGS_OCE_STA_CFON))
+				hapd->enable_oce = OCE_STA_CFON;
+
+			if (hapd && (hapd->conf->oce & OCE_AP) &&
+			    (interfaces.iface[i]->drv_flags &
+			     WPA_DRIVER_FLAGS_OCE_STA_CFON)) {
+				/* TODO: Need to add OCE-AP support */
+				wpa_printf(MSG_ERROR,
+					   "OCE-AP feature is not yet supported");
+			}
+		}
+#endif /* CONFIG_MBO */
+		if (hostapd_setup_interface(interfaces.iface[i]))
 			goto out;
 	}
 
diff --git a/src/ap/Makefile b/src/ap/Makefile
index 1c65bd6..b8c167c 100644
--- a/src/ap/Makefile
+++ b/src/ap/Makefile
@@ -48,7 +48,6 @@
 	neighbor_db.o \
 	ndisc_snoop.o \
 	p2p_hostapd.o \
-	peerkey_auth.o \
 	pmksa_cache_auth.o \
 	preauth_auth.o \
 	rrm.o \
diff --git a/src/ap/acs.c b/src/ap/acs.c
index 5e83805..aa59058 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -260,7 +260,7 @@
 }
 
 
-static void acs_cleanup(struct hostapd_iface *iface)
+void acs_cleanup(struct hostapd_iface *iface)
 {
 	int i;
 	struct hostapd_channel_data *chan;
@@ -331,10 +331,8 @@
 	long double int_factor = 0;
 	unsigned count = 0;
 
-	if (dl_list_empty(&chan->survey_list))
-		return;
-
-	if (chan->flag & HOSTAPD_CHAN_DISABLED)
+	if (dl_list_empty(&chan->survey_list) ||
+	    (chan->flag & HOSTAPD_CHAN_DISABLED))
 		return;
 
 	chan->interference_factor = 0;
@@ -359,9 +357,8 @@
 			   (unsigned long) survey->channel_time_rx);
 	}
 
-	if (!count)
-		return;
-	chan->interference_factor /= count;
+	if (count)
+		chan->interference_factor /= count;
 }
 
 
@@ -450,13 +447,9 @@
 
 	for (i = 0; i < iface->current_mode->num_channels; i++) {
 		chan = &iface->current_mode->channels[i];
-		if (chan->flag & HOSTAPD_CHAN_DISABLED)
-			continue;
-
-		if (!acs_survey_list_is_sufficient(chan))
-			continue;
-
-		valid++;
+		if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+		    acs_survey_list_is_sufficient(chan))
+			valid++;
 	}
 
 	/* We need at least survey data for one channel */
@@ -466,13 +459,9 @@
 
 static int acs_usable_chan(struct hostapd_channel_data *chan)
 {
-	if (dl_list_empty(&chan->survey_list))
-		return 0;
-	if (chan->flag & HOSTAPD_CHAN_DISABLED)
-		return 0;
-	if (!acs_survey_list_is_sufficient(chan))
-		return 0;
-	return 1;
+	return !dl_list_empty(&chan->survey_list) &&
+		!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+		acs_survey_list_is_sufficient(chan);
 }
 
 
@@ -788,10 +777,7 @@
 
 static int acs_study_options(struct hostapd_iface *iface)
 {
-	int err;
-
-	err = acs_study_survey_based(iface);
-	if (err == 0)
+	if (acs_study_survey_based(iface) == 0)
 		return 0;
 
 	/* TODO: If no surveys are available/sufficient this is a good
@@ -920,14 +906,11 @@
 
 enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
 {
-	int err;
-
 	wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit");
 
 	if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) {
 		wpa_printf(MSG_INFO, "ACS: Offloading to driver");
-		err = hostapd_drv_do_acs(iface->bss[0]);
-		if (err)
+		if (hostapd_drv_do_acs(iface->bss[0]))
 			return HOSTAPD_CHAN_INVALID;
 		return HOSTAPD_CHAN_ACS;
 	}
@@ -937,8 +920,7 @@
 
 	acs_cleanup(iface);
 
-	err = acs_request_scan(iface);
-	if (err < 0)
+	if (acs_request_scan(iface) < 0)
 		return HOSTAPD_CHAN_INVALID;
 
 	hostapd_set_state(iface, HAPD_IFACE_ACS);
diff --git a/src/ap/acs.h b/src/ap/acs.h
index fc85259..ec84f0e 100644
--- a/src/ap/acs.h
+++ b/src/ap/acs.h
@@ -13,6 +13,7 @@
 #ifdef CONFIG_ACS
 
 enum hostapd_chan_status acs_init(struct hostapd_iface *iface);
+void acs_cleanup(struct hostapd_iface *iface);
 
 #else /* CONFIG_ACS */
 
@@ -22,6 +23,10 @@
 	return HOSTAPD_CHAN_INVALID;
 }
 
+static inline void acs_cleanup(struct hostapd_iface *iface)
+{
+}
+
 #endif /* CONFIG_ACS */
 
 #endif /* ACS_H */
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 9abcab7..07310f9 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -37,6 +37,10 @@
 }
 
 
+#ifndef DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES
+#define DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES 0
+#endif /* DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES */
+
 void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
 {
 	dl_list_init(&bss->anqp_elem);
@@ -58,6 +62,8 @@
 	bss->wpa_gmk_rekey = 86400;
 	bss->wpa_group_update_count = 4;
 	bss->wpa_pairwise_update_count = 4;
+	bss->wpa_disable_eapol_key_retries =
+		DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES;
 	bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
 	bss->wpa_pairwise = WPA_CIPHER_TKIP;
 	bss->wpa_group = WPA_CIPHER_TKIP;
@@ -93,6 +99,10 @@
 
 #ifdef CONFIG_IEEE80211R_AP
 	bss->ft_over_ds = 1;
+	bss->rkh_pos_timeout = 86400;
+	bss->rkh_neg_timeout = 60;
+	bss->rkh_pull_timeout = 1000;
+	bss->rkh_pull_retries = 4;
 #endif /* CONFIG_IEEE80211R_AP */
 
 	bss->radius_das_time_window = 300;
@@ -107,6 +117,12 @@
 	bss->dhcp_server_port = DHCP_SERVER_PORT;
 	bss->dhcp_relay_port = DHCP_SERVER_PORT;
 #endif /* CONFIG_FILS */
+
+	bss->broadcast_deauth = 1;
+
+#ifdef CONFIG_MBO
+	bss->mbo_cell_data_conn_pref = -1;
+#endif /* CONFIG_MBO */
 }
 
 
@@ -204,6 +220,11 @@
 	conf->acs_num_scans = 5;
 #endif /* CONFIG_ACS */
 
+	/* The third octet of the country string uses an ASCII space character
+	 * by default to indicate that the regulations encompass all
+	 * environments for the current frequency band in the country. */
+	conf->country[2] = ' ';
+
 	return conf;
 }
 
@@ -595,6 +616,9 @@
 	wpabuf_free(conf->assocresp_elements);
 
 	os_free(conf->sae_groups);
+#ifdef CONFIG_OWE
+	os_free(conf->owe_groups);
+#endif /* CONFIG_OWE */
 
 	os_free(conf->wowlan_triggers);
 
@@ -602,6 +626,7 @@
 
 #ifdef CONFIG_TESTING_OPTIONS
 	wpabuf_free(conf->own_ie_override);
+	wpabuf_free(conf->sae_commit_override);
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	os_free(conf->no_probe_resp_if_seen_on);
@@ -609,6 +634,14 @@
 
 	hostapd_config_free_fils_realms(conf);
 
+#ifdef CONFIG_DPP
+	os_free(conf->dpp_connector);
+	wpabuf_free(conf->dpp_netaccesskey);
+	wpabuf_free(conf->dpp_csign);
+#endif /* CONFIG_DPP */
+
+	os_free(conf->sae_password);
+
 	os_free(conf);
 }
 
@@ -1015,6 +1048,9 @@
 		bss->rsn_pairwise = bss->wpa_pairwise;
 	bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise,
 						    bss->rsn_pairwise);
+	if (!bss->wpa_group_rekey_set)
+		bss->wpa_group_rekey = bss->wpa_group == WPA_CIPHER_TKIP ?
+			600 : 86400;
 
 	if (full_config) {
 		bss->radius->auth_server = bss->radius->auth_servers;
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index fdd5a1a..89bf289 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -327,21 +327,26 @@
 	int wpa_pairwise;
 	int wpa_group;
 	int wpa_group_rekey;
+	int wpa_group_rekey_set;
 	int wpa_strict_rekey;
 	int wpa_gmk_rekey;
 	int wpa_ptk_rekey;
 	u32 wpa_group_update_count;
 	u32 wpa_pairwise_update_count;
+	int wpa_disable_eapol_key_retries;
 	int rsn_pairwise;
 	int rsn_preauth;
 	char *rsn_preauth_interfaces;
-	int peerkey;
 
 #ifdef CONFIG_IEEE80211R_AP
 	/* IEEE 802.11r - Fast BSS Transition */
 	u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
 	u8 r1_key_holder[FT_R1KH_ID_LEN];
 	u32 r0_key_lifetime;
+	int rkh_pos_timeout;
+	int rkh_neg_timeout;
+	int rkh_pull_timeout; /* ms */
+	int rkh_pull_retries;
 	u32 reassociation_deadline;
 	struct ft_remote_r0kh *r0kh_list;
 	struct ft_remote_r1kh *r1kh_list;
@@ -362,6 +367,7 @@
 	char *private_key_passwd;
 	int check_crl;
 	unsigned int tls_session_lifetime;
+	unsigned int tls_flags;
 	char *ocsp_stapling_response;
 	char *ocsp_stapling_response_multi;
 	char *dh_file;
@@ -576,6 +582,7 @@
 
 	unsigned int sae_anti_clogging_threshold;
 	int *sae_groups;
+	char *sae_password;
 
 	char *wowlan_triggers; /* Wake-on-WLAN triggers */
 
@@ -583,6 +590,8 @@
 	u8 bss_load_test[5];
 	u8 bss_load_test_set;
 	struct wpabuf *own_ie_override;
+	int sae_reflection_attack;
+	struct wpabuf *sae_commit_override;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 #define MESH_ENABLED BIT(0)
@@ -600,6 +609,14 @@
 
 #ifdef CONFIG_MBO
 	int mbo_enabled;
+	/**
+	 * oce - Enable OCE in AP and/or STA-CFON mode
+	 *  - BIT(0) is Reserved
+	 *  - Set BIT(1) to enable OCE in STA-CFON mode
+	 *  - Set BIT(2) to enable OCE in AP mode
+	 */
+	unsigned int oce;
+	int mbo_cell_data_conn_pref;
 #endif /* CONFIG_MBO */
 
 	int ftm_responder;
@@ -609,6 +626,7 @@
 	u8 fils_cache_id[FILS_CACHE_ID_LEN];
 	int fils_cache_id_set;
 	struct dl_list fils_realms; /* list of struct fils_realm */
+	int fils_dh_group;
 	struct hostapd_ip_addr dhcp_server;
 	int dhcp_rapid_commit_proxy;
 	unsigned int fils_hlp_wait_time;
@@ -617,6 +635,23 @@
 #endif /* CONFIG_FILS */
 
 	int multicast_to_unicast;
+
+	int broadcast_deauth;
+
+#ifdef CONFIG_DPP
+	char *dpp_connector;
+	struct wpabuf *dpp_netaccesskey;
+	unsigned int dpp_netaccesskey_expiry;
+	struct wpabuf *dpp_csign;
+#endif /* CONFIG_DPP */
+
+#ifdef CONFIG_OWE
+	macaddr owe_transition_bssid;
+	u8 owe_transition_ssid[SSID_MAX_LEN];
+	size_t owe_transition_ssid_len;
+	char owe_transition_ifname[IFNAMSIZ + 1];
+	int *owe_groups;
+#endif /* CONFIG_OWE */
 };
 
 /**
@@ -652,6 +687,7 @@
 	u8 channel;
 	u8 acs;
 	struct wpa_freq_range_list acs_ch_list;
+	int acs_exclude_dfs;
 	enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */
 	enum {
 		LONG_PREAMBLE = 0,
@@ -677,6 +713,9 @@
 			  * ' ' (ascii 32): all environments
 			  * 'O': Outdoor environemnt only
 			  * 'I': Indoor environment only
+			  * 'X': Used with noncountry entity ("XXX")
+			  * 0x00..0x31: identifying IEEE 802.11 standard
+			  *	Annex E table (0x04 = global table)
 			  */
 
 	int ieee80211d;
@@ -717,6 +756,7 @@
 	u8 vht_oper_chwidth;
 	u8 vht_oper_centr_freq_seg0_idx;
 	u8 vht_oper_centr_freq_seg1_idx;
+	u8 ht40_plus_minus_allowed;
 
 	/* Use driver-generated interface addresses when adding multiple BSSs */
 	u8 use_driver_iface_addr;
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index f139465..8f4d839 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -19,6 +19,7 @@
 #include "ap_config.h"
 #include "p2p_hostapd.h"
 #include "hs20.h"
+#include "wpa_auth.h"
 #include "ap_drv_ops.h"
 
 
@@ -99,6 +100,13 @@
 		goto fail;
 #endif /* CONFIG_FST */
 
+#ifdef CONFIG_FILS
+	pos = hostapd_eid_fils_indic(hapd, buf, 0);
+	if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
+	    add_buf_data(&proberesp, buf, pos - buf) < 0)
+		goto fail;
+#endif /* CONFIG_FILS */
+
 	if (add_buf(&beacon, hapd->wps_beacon_ie) < 0 ||
 	    add_buf(&proberesp, hapd->wps_probe_resp_ie) < 0)
 		goto fail;
@@ -168,7 +176,7 @@
 #endif /* CONFIG_HS20 */
 
 #ifdef CONFIG_MBO
-	if (hapd->conf->mbo_enabled) {
+	if (hapd->conf->mbo_enabled || hapd->enable_oce) {
 		pos = hostapd_eid_mbo(hapd, buf, sizeof(buf));
 		if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
 		    add_buf_data(&proberesp, buf, pos - buf) < 0 ||
@@ -340,10 +348,44 @@
 int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
 		     u16 seq, u16 status, const u8 *ie, size_t len)
 {
+	struct wpa_driver_sta_auth_params params;
+#ifdef CONFIG_FILS
+	struct sta_info *sta;
+#endif /* CONFIG_FILS */
+
 	if (hapd->driver == NULL || hapd->driver->sta_auth == NULL)
 		return 0;
-	return hapd->driver->sta_auth(hapd->drv_priv, hapd->own_addr, addr,
-				      seq, status, ie, len);
+
+	os_memset(&params, 0, sizeof(params));
+
+#ifdef CONFIG_FILS
+	sta = ap_get_sta(hapd, addr);
+	if (!sta) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR
+			   " not found for sta_auth processing",
+			   MAC2STR(addr));
+		return 0;
+	}
+
+	if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	    sta->auth_alg == WLAN_AUTH_FILS_PK) {
+		params.fils_auth = 1;
+		wpa_auth_get_fils_aead_params(sta->wpa_sm, params.fils_anonce,
+					      params.fils_snonce,
+					      params.fils_kek,
+					      &params.fils_kek_len);
+	}
+#endif /* CONFIG_FILS */
+
+	params.own_addr = hapd->own_addr;
+	params.addr = addr;
+	params.seq = seq;
+	params.status = status;
+	params.ie = ie;
+	params.len = len;
+
+	return hapd->driver->sta_auth(hapd->drv_priv, &params);
 }
 
 
@@ -554,13 +596,13 @@
 
 struct hostapd_hw_modes *
 hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
-			    u16 *flags)
+			    u16 *flags, u8 *dfs_domain)
 {
 	if (hapd->driver == NULL ||
 	    hapd->driver->get_hw_feature_data == NULL)
 		return NULL;
 	return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes,
-						 flags);
+						 flags, dfs_domain);
 }
 
 
@@ -774,7 +816,9 @@
 		if ((acs_ch_list_all ||
 		     freq_range_list_includes(&hapd->iface->conf->acs_ch_list,
 					      chan->chan)) &&
-		    !(chan->flag & HOSTAPD_CHAN_DISABLED))
+		    !(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+		    !(hapd->iface->conf->acs_exclude_dfs &&
+		      (chan->flag & HOSTAPD_CHAN_RADAR)))
 			int_array_add_unique(freq_list, chan->freq);
 	}
 }
@@ -829,6 +873,9 @@
 				    &hapd->iface->conf->acs_ch_list,
 				    chan->chan))
 				continue;
+			if (hapd->iface->conf->acs_exclude_dfs &&
+			    (chan->flag & HOSTAPD_CHAN_RADAR))
+				continue;
 			if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) {
 				channels[num_channels++] = chan->chan;
 				int_array_add_unique(&freq_list, chan->freq);
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 0bb7954..bf8169d 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -72,7 +72,7 @@
 				int cw_min, int cw_max, int burst_time);
 struct hostapd_hw_modes *
 hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
-			    u16 *flags);
+			    u16 *flags, u8 *dfs_domain);
 int hostapd_driver_commit(struct hostapd_data *hapd);
 int hostapd_drv_none(struct hostapd_data *hapd);
 int hostapd_driver_scan(struct hostapd_data *hapd,
@@ -274,8 +274,9 @@
 static inline int hostapd_drv_switch_channel(struct hostapd_data *hapd,
 					     struct csa_settings *settings)
 {
-	if (hapd->driver == NULL || hapd->driver->switch_channel == NULL)
-		return -ENOTSUP;
+	if (hapd->driver == NULL || hapd->driver->switch_channel == NULL ||
+	    hapd->drv_priv == NULL)
+		return -1;
 
 	return hapd->driver->switch_channel(hapd->drv_priv, settings);
 }
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index cdb49cd..a20f49f 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -71,11 +71,10 @@
 	}
 
 	if (eap_user->password) {
-		user->password = os_malloc(eap_user->password_len);
+		user->password = os_memdup(eap_user->password,
+					   eap_user->password_len);
 		if (user->password == NULL)
 			goto out;
-		os_memcpy(user->password, eap_user->password,
-			  eap_user->password_len);
 		user->password_len = eap_user->password_len;
 		user->password_hash = eap_user->password_hash;
 	}
@@ -133,6 +132,7 @@
 	srv.erp = conf->eap_server_erp;
 	srv.erp_domain = conf->erp_domain;
 	srv.tls_session_lifetime = conf->tls_session_lifetime;
+	srv.tls_flags = conf->tls_flags;
 
 	hapd->radius_srv = radius_server_init(&srv);
 	if (hapd->radius_srv == NULL) {
@@ -157,6 +157,7 @@
 
 		os_memset(&conf, 0, sizeof(conf));
 		conf.tls_session_lifetime = hapd->conf->tls_session_lifetime;
+		conf.tls_flags = hapd->conf->tls_flags;
 		hapd->ssl_ctx = tls_init(&conf);
 		if (hapd->ssl_ctx == NULL) {
 			wpa_printf(MSG_ERROR, "Failed to initialize TLS");
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index c6bbda3..3ea28a7 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -363,6 +363,67 @@
 }
 
 
+#ifdef CONFIG_OWE
+static int hostapd_eid_owe_trans_enabled(struct hostapd_data *hapd)
+{
+	return hapd->conf->owe_transition_ssid_len > 0 &&
+		!is_zero_ether_addr(hapd->conf->owe_transition_bssid);
+}
+#endif /* CONFIG_OWE */
+
+
+static size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_OWE
+	if (!hostapd_eid_owe_trans_enabled(hapd))
+		return 0;
+	return 6 + ETH_ALEN + 1 + hapd->conf->owe_transition_ssid_len;
+#else /* CONFIG_OWE */
+	return 0;
+#endif /* CONFIG_OWE */
+}
+
+
+static u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid,
+				  size_t len)
+{
+#ifdef CONFIG_OWE
+	u8 *pos = eid;
+	size_t elen;
+
+	if (hapd->conf->owe_transition_ifname[0] &&
+	    !hostapd_eid_owe_trans_enabled(hapd))
+		hostapd_owe_trans_get_info(hapd);
+
+	if (!hostapd_eid_owe_trans_enabled(hapd))
+		return pos;
+
+	elen = hostapd_eid_owe_trans_len(hapd);
+	if (len < elen) {
+		wpa_printf(MSG_DEBUG,
+			   "OWE: Not enough room in the buffer for OWE IE");
+		return pos;
+	}
+
+	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+	*pos++ = elen - 2;
+	WPA_PUT_BE24(pos, OUI_WFA);
+	pos += 3;
+	*pos++ = OWE_OUI_TYPE;
+	os_memcpy(pos, hapd->conf->owe_transition_bssid, ETH_ALEN);
+	pos += ETH_ALEN;
+	*pos++ = hapd->conf->owe_transition_ssid_len;
+	os_memcpy(pos, hapd->conf->owe_transition_ssid,
+		  hapd->conf->owe_transition_ssid_len);
+	pos += hapd->conf->owe_transition_ssid_len;
+
+	return pos;
+#else /* CONFIG_OWE */
+	return eid;
+#endif /* CONFIG_OWE */
+}
+
+
 static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
 				   const struct ieee80211_mgmt *req,
 				   int is_p2p, size_t *resp_len)
@@ -394,12 +455,13 @@
 
 #ifdef CONFIG_IEEE80211AX
 	if (hapd->iconf->ieee80211ax) {
-		buflen += 4 + sizeof (struct ieee80211_he_capabilities) +
-			4 + sizeof (struct ieee80211_he_operation);
+		buflen += 3 + sizeof(struct ieee80211_he_capabilities) +
+			3 + sizeof(struct ieee80211_he_operation);
 	}
-#endif
+#endif /* CONFIG_IEEE80211AX */
 
 	buflen += hostapd_mbo_ie_len(hapd);
+	buflen += hostapd_eid_owe_trans_len(hapd);
 
 	resp = os_zalloc(buflen);
 	if (resp == NULL)
@@ -502,18 +564,18 @@
 
 	pos = hostapd_eid_fils_indic(hapd, pos, 0);
 
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax) {
+		pos = hostapd_eid_he_capab(hapd, pos);
+		pos = hostapd_eid_he_operation(hapd, pos);
+	}
+#endif /* CONFIG_IEEE80211AX */
+
 #ifdef CONFIG_IEEE80211AC
 	if (hapd->conf->vendor_vht)
 		pos = hostapd_eid_vendor_vht(hapd, pos);
 #endif /* CONFIG_IEEE80211AC */
 
-#ifdef CONFIG_IEEE80211AX
-	if (hapd->iconf->ieee80211ax) {
-		pos = hostapd_eid_vendor_he_capab(hapd, pos);
-		pos = hostapd_eid_vendor_he_operation(hapd, pos);
-	}
-#endif /* CONFIG_IEEE80211AX */
-
 	/* Wi-Fi Alliance WMM */
 	pos = hostapd_eid_wmm(hapd, pos);
 
@@ -545,6 +607,7 @@
 #endif /* CONFIG_HS20 */
 
 	pos = hostapd_eid_mbo(hapd, pos, (u8 *) resp + buflen - pos);
+	pos = hostapd_eid_owe_trans(hapd, pos, (u8 *) resp + buflen - pos);
 
 	if (hapd->conf->vendor_elements) {
 		os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements),
@@ -1056,12 +1119,13 @@
 
 #ifdef CONFIG_IEEE80211AX
 	if (hapd->iconf->ieee80211ax) {
-		tail_len += 4 + sizeof (struct ieee80211_he_capabilities) +
-			4 + sizeof (struct ieee80211_he_operation);
+		tail_len += 3 + sizeof(struct ieee80211_he_capabilities) +
+			3 + sizeof(struct ieee80211_he_operation);
 	}
-#endif
+#endif /* CONFIG_IEEE80211AX */
 
 	tail_len += hostapd_mbo_ie_len(hapd);
+	tail_len += hostapd_eid_owe_trans_len(hapd);
 
 	tailpos = tail = os_malloc(tail_len);
 	if (head == NULL || tail == NULL) {
@@ -1187,18 +1251,18 @@
 
 	tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
 
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax) {
+		tailpos = hostapd_eid_he_capab(hapd, tailpos);
+		tailpos = hostapd_eid_he_operation(hapd, tailpos);
+	}
+#endif /* CONFIG_IEEE80211AX */
+
 #ifdef CONFIG_IEEE80211AC
 	if (hapd->conf->vendor_vht)
 		tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
 #endif /* CONFIG_IEEE80211AC */
 
-#ifdef CONFIG_IEEE80211AX
-	if (hapd->iconf->ieee80211ax) {
-		tailpos = hostapd_eid_vendor_he_capab(hapd, tailpos);
-		tailpos = hostapd_eid_vendor_he_operation(hapd, tailpos);
-	}
-#endif /* CONFIG_IEEE80211AX */
-
 	/* Wi-Fi Alliance WMM */
 	tailpos = hostapd_eid_wmm(hapd, tailpos);
 
@@ -1229,6 +1293,8 @@
 #endif /* CONFIG_HS20 */
 
 	tailpos = hostapd_eid_mbo(hapd, tailpos, tail + tail_len - tailpos);
+	tailpos = hostapd_eid_owe_trans(hapd, tailpos,
+					tail + tail_len - tailpos);
 
 	if (hapd->conf->vendor_elements) {
 		os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements),
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index a760e3a..1a2b4e5 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -32,17 +32,86 @@
 {
 	struct hostap_sta_driver_data data;
 	int ret;
+	int len = 0;
 
 	if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0)
 		return 0;
 
 	ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n"
-			  "rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n",
+			  "rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n"
+			  "signal=%d\n",
 			  data.rx_packets, data.tx_packets,
-			  data.rx_bytes, data.tx_bytes, data.inactive_msec);
+			  data.rx_bytes, data.tx_bytes, data.inactive_msec,
+			  data.signal);
 	if (os_snprintf_error(buflen, ret))
 		return 0;
-	return ret;
+	len += ret;
+
+	ret = os_snprintf(buf + len, buflen - len, "rx_rate_info=%lu",
+			  data.current_rx_rate);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+	if (data.flags & STA_DRV_DATA_RX_MCS) {
+		ret = os_snprintf(buf + len, buflen - len, " mcs %u",
+				  data.rx_mcs);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+	if (data.flags & STA_DRV_DATA_RX_VHT_MCS) {
+		ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u",
+				  data.rx_vhtmcs);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+	if (data.flags & STA_DRV_DATA_RX_VHT_NSS) {
+		ret = os_snprintf(buf + len, buflen - len, " vhtnss %u",
+				  data.rx_vht_nss);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+	if (data.flags & STA_DRV_DATA_RX_SHORT_GI) {
+		ret = os_snprintf(buf + len, buflen - len, " shortGI");
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+	ret = os_snprintf(buf + len, buflen - len, "\n");
+	if (!os_snprintf_error(buflen - len, ret))
+		len += ret;
+
+	ret = os_snprintf(buf + len, buflen - len, "tx_rate_info=%lu",
+			  data.current_tx_rate);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+	if (data.flags & STA_DRV_DATA_TX_MCS) {
+		ret = os_snprintf(buf + len, buflen - len, " mcs %u",
+				  data.tx_mcs);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+	if (data.flags & STA_DRV_DATA_TX_VHT_MCS) {
+		ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u",
+				  data.tx_vhtmcs);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+	if (data.flags & STA_DRV_DATA_TX_VHT_NSS) {
+		ret = os_snprintf(buf + len, buflen - len, " vhtnss %u",
+				  data.tx_vht_nss);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+	if (data.flags & STA_DRV_DATA_TX_SHORT_GI) {
+		ret = os_snprintf(buf + len, buflen - len, " shortGI");
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+	ret = os_snprintf(buf + len, buflen - len, "\n");
+	if (!os_snprintf_error(buflen - len, ret))
+		len += ret;
+
+	return len;
 }
 
 
@@ -641,6 +710,54 @@
 }
 
 
+int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd)
+{
+	u8 spa[ETH_ALEN];
+	u8 pmkid[PMKID_LEN];
+	u8 pmk[PMK_LEN_MAX];
+	size_t pmk_len;
+	char *pos, *pos2;
+	int akmp = 0, expiration = 0;
+
+	/*
+	 * Entry format:
+	 * <STA addr> <PMKID> <PMK> <expiration in seconds> <akmp>
+	 */
+
+	if (hwaddr_aton(cmd, spa))
+		return -1;
+
+	pos = os_strchr(cmd, ' ');
+	if (!pos)
+		return -1;
+	pos++;
+
+	if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0)
+		return -1;
+
+	pos = os_strchr(pos, ' ');
+	if (!pos)
+		return -1;
+	pos++;
+
+	pos2 = os_strchr(pos, ' ');
+	if (!pos2)
+		return -1;
+	pmk_len = (pos2 - pos) / 2;
+	if (pmk_len < PMK_LEN || pmk_len > PMK_LEN_MAX ||
+	    hexstr2bin(pos, pmk, pmk_len) < 0)
+		return -1;
+
+	pos = pos2 + 1;
+
+	if (sscanf(pos, "%d %d", &expiration, &akmp) != 2)
+		return -1;
+
+	return wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len,
+				   pmkid, expiration, akmp);
+}
+
+
 #ifdef CONFIG_PMKSA_CACHE_EXTERNAL
 #ifdef CONFIG_MESH
 
diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h
index 3b61cac..d1dcebf 100644
--- a/src/ap/ctrl_iface_ap.h
+++ b/src/ap/ctrl_iface_ap.h
@@ -32,6 +32,7 @@
 int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf,
 				  size_t len);
 void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd);
+int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd);
 int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd,
 				       const u8 *addr, char *buf, size_t len);
 void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd);
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 47adba7..5a0d781 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -1,7 +1,7 @@
 /*
  * DFS - Dynamic Frequency Selection
  * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2013-2015, Qualcomm Atheros, Inc.
+ * Copyright (c) 2013-2017, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -747,6 +747,23 @@
 }
 
 
+static int hostapd_config_dfs_chan_available(struct hostapd_iface *iface)
+{
+	int n_chans, n_chans1, start_chan_idx, start_chan_idx1;
+
+	/* Get the start (first) channel for current configuration */
+	start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1);
+	if (start_chan_idx < 0)
+		return 0;
+
+	/* Get the number of used channels, depending on width */
+	n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+	/* Check if all channels are DFS available */
+	return dfs_check_chans_available(iface, start_chan_idx, n_chans);
+}
+
+
 int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
 			     int ht_enabled, int chan_offset, int chan_width,
 			     int cf1, int cf2)
@@ -767,8 +784,21 @@
 			set_dfs_state(iface, freq, ht_enabled, chan_offset,
 				      chan_width, cf1, cf2,
 				      HOSTAPD_CHAN_DFS_AVAILABLE);
-			iface->cac_started = 0;
-			hostapd_setup_interface_complete(iface, 0);
+			/*
+			 * Just mark the channel available when CAC completion
+			 * event is received in enabled state. CAC result could
+			 * have been propagated from another radio having the
+			 * same regulatory configuration. When CAC completion is
+			 * received during non-HAPD_IFACE_ENABLED state, make
+			 * sure the configured channel is available because this
+			 * CAC completion event could have been propagated from
+			 * another radio.
+			 */
+			if (iface->state != HAPD_IFACE_ENABLED &&
+			    hostapd_config_dfs_chan_available(iface)) {
+				hostapd_setup_interface_complete(iface, 0);
+				iface->cac_started = 0;
+			}
 		}
 	}
 
@@ -776,6 +806,25 @@
 }
 
 
+int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
+				int ht_enabled, int chan_offset, int chan_width,
+				int cf1, int cf2)
+{
+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_PRE_CAC_EXPIRED
+		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
+		freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+
+	/* Proceed only if DFS is not offloaded to the driver */
+	if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+		return 0;
+
+	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
+		      cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
+
+	return 0;
+}
+
+
 static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
 {
 	struct hostapd_channel_data *channel;
@@ -840,6 +889,13 @@
 	if (iface->cac_started)
 		return hostapd_dfs_start_channel_switch_cac(iface);
 
+	/*
+	 * Allow selection of DFS channel in ETSI to comply with
+	 * uniform spreading.
+	 */
+	if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
+		skip_radar = 0;
+
 	/* Perform channel switch/CSA */
 	channel = dfs_get_valid_channel(iface, &secondary_channel,
 					&vht_oper_centr_freq_seg0_idx,
diff --git a/src/ap/dfs.h b/src/ap/dfs.h
index be8c0e6..f0fa6f6 100644
--- a/src/ap/dfs.h
+++ b/src/ap/dfs.h
@@ -1,7 +1,7 @@
 /*
  * DFS - Dynamic Frequency Selection
  * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ * Copyright (c) 2013-2017, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -14,6 +14,9 @@
 int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
 			     int ht_enabled, int chan_offset, int chan_width,
 			     int cf1, int cf2);
+int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
+				int ht_enabled, int chan_offset, int chan_width,
+				int cf1, int cf2);
 int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
 			       int ht_enabled,
 			       int chan_offset, int chan_width,
diff --git a/src/ap/dhcp_snoop.c b/src/ap/dhcp_snoop.c
index b9a36d7..6d8c2f4 100644
--- a/src/ap/dhcp_snoop.c
+++ b/src/ap/dhcp_snoop.c
@@ -154,4 +154,5 @@
 void dhcp_snoop_deinit(struct hostapd_data *hapd)
 {
 	l2_packet_deinit(hapd->sock_dhcp);
+	hapd->sock_dhcp = NULL;
 }
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
new file mode 100644
index 0000000..aae2910
--- /dev/null
+++ b/src/ap/dpp_hostapd.c
@@ -0,0 +1,1483 @@
+/*
+ * hostapd / DPP integration
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/dpp.h"
+#include "common/gas.h"
+#include "common/wpa_ctrl.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "gas_query_ap.h"
+#include "wpa_auth.h"
+#include "dpp_hostapd.h"
+
+
+static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator);
+
+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->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->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
+ * @cmd: DPP URI read from a QR Code
+ * Returns: Identifier of the stored info or -1 on failure
+ */
+int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd)
+{
+	struct dpp_bootstrap_info *bi;
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
+	bi = dpp_parse_qr_code(cmd);
+	if (!bi)
+		return -1;
+
+	bi->id = hapd_dpp_next_id(hapd);
+	dl_list_add(&hapd->dpp_bootstrap, &bi->list);
+
+	if (auth && auth->response_pending &&
+	    dpp_notify_new_qr_code(auth, bi) == 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Sending out pending authentication response");
+		hostapd_drv_send_action(hapd, auth->curr_freq, 0,
+					auth->peer_mac_addr,
+					wpabuf_head(hapd->dpp_auth->resp_msg),
+					wpabuf_len(hapd->dpp_auth->resp_msg));
+	}
+
+	return bi->id;
+}
+
+
+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->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->dpp_bootstrap, struct dpp_bootstrap_info,
+			 list) {
+		if (bi->id == id)
+			return bi;
+	}
+	return NULL;
+}
+
+
+static int dpp_bootstrap_del(struct hostapd_data *hapd, unsigned int id)
+{
+	struct dpp_bootstrap_info *bi, *tmp;
+	int found = 0;
+
+	dl_list_for_each_safe(bi, tmp, &hapd->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, 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);
+}
+
+
+void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
+			   const u8 *data, size_t data_len, int ok)
+{
+	wpa_printf(MSG_DEBUG, "DPP: TX status: dst=" MACSTR " ok=%d",
+		   MAC2STR(dst), ok);
+
+	if (!hapd->dpp_auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore TX status since there is no ongoing authentication exchange");
+		return;
+	}
+
+	if (hapd->dpp_auth->remove_on_tx_status) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Terminate authentication exchange due to an earlier error");
+		dpp_auth_deinit(hapd->dpp_auth);
+		hapd->dpp_auth = NULL;
+		return;
+	}
+
+	if (hapd->dpp_auth_ok_on_ack)
+		hostapd_dpp_auth_success(hapd, 1);
+}
+
+
+static void hostapd_dpp_set_testing_options(struct hostapd_data *hapd,
+					    struct dpp_authentication *auth)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->dpp_config_obj_override)
+		auth->config_obj_override =
+			os_strdup(hapd->dpp_config_obj_override);
+	if (hapd->dpp_discovery_override)
+		auth->discovery_override =
+			os_strdup(hapd->dpp_discovery_override);
+	if (hapd->dpp_groups_override)
+		auth->groups_override = os_strdup(hapd->dpp_groups_override);
+	auth->ignore_netaccesskey_mismatch =
+		hapd->dpp_ignore_netaccesskey_mismatch;
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
+static void 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;
+
+	if (!cmd)
+		return;
+
+	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;
+	}
+
+	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")) {
+			conf_sta->dpp = 0;
+			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->dpp = 1;
+		} else {
+			goto fail;
+		}
+	}
+
+	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")) {
+			conf_ap->dpp = 0;
+			if (psk_set) {
+				os_memcpy(conf_ap->psk, psk, PMK_LEN);
+			} else {
+				conf_ap->passphrase = os_strdup(pass);
+				if (!conf_ap->passphrase)
+					goto fail;
+			}
+		} else if (os_strstr(cmd, " conf=ap-dpp")) {
+			conf_ap->dpp = 1;
+		} else {
+			goto fail;
+		}
+	}
+
+	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;
+	return;
+
+fail:
+	wpa_printf(MSG_DEBUG, "DPP: Failed to set configurator parameters");
+	dpp_configuration_free(conf_sta);
+	dpp_configuration_free(conf_ap);
+}
+
+
+int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd)
+{
+	const char *pos;
+	struct dpp_bootstrap_info *peer_bi, *own_bi = NULL;
+	const u8 *dst;
+	int res;
+	int configurator = 1;
+	struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL;
+
+	pos = os_strstr(cmd, " peer=");
+	if (!pos)
+		return -1;
+	pos += 6;
+	peer_bi = dpp_bootstrap_get_id(hapd, atoi(pos));
+	if (!peer_bi) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Could not find bootstrapping info for the identified peer");
+		return -1;
+	}
+
+	pos = os_strstr(cmd, " own=");
+	if (pos) {
+		pos += 5;
+		own_bi = dpp_bootstrap_get_id(hapd, atoi(pos));
+		if (!own_bi) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Could not find bootstrapping info for the identified local entry");
+			return -1;
+		}
+
+		if (peer_bi->curve != own_bi->curve) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Mismatching curves in bootstrapping info (peer=%s own=%s)",
+				   peer_bi->curve->name, own_bi->curve->name);
+			return -1;
+		}
+	}
+
+	pos = os_strstr(cmd, " role=");
+	if (pos) {
+		pos += 6;
+		if (os_strncmp(pos, "configurator", 12) == 0)
+			configurator = 1;
+		else if (os_strncmp(pos, "enrollee", 8) == 0)
+			configurator = 0;
+		else
+			goto fail;
+	}
+
+	if (hapd->dpp_auth)
+		dpp_auth_deinit(hapd->dpp_auth);
+	hapd->dpp_auth = dpp_auth_init(hapd, peer_bi, own_bi, configurator);
+	if (!hapd->dpp_auth)
+		goto fail;
+	hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth);
+	hostapd_dpp_set_configurator(hapd, hapd->dpp_auth, cmd);
+
+	/* TODO: Support iteration over all frequencies and filtering of
+	 * frequencies based on locally enabled channels that allow initiation
+	 * of transmission. */
+	if (peer_bi->num_freq > 0)
+		hapd->dpp_auth->curr_freq = peer_bi->freq[0];
+	else
+		hapd->dpp_auth->curr_freq = 2412;
+
+	if (is_zero_ether_addr(peer_bi->mac_addr)) {
+		dst = broadcast;
+	} else {
+		dst = peer_bi->mac_addr;
+		os_memcpy(hapd->dpp_auth->peer_mac_addr, peer_bi->mac_addr,
+			  ETH_ALEN);
+	}
+	hapd->dpp_auth_ok_on_ack = 0;
+
+	res = hostapd_drv_send_action(hapd, hapd->dpp_auth->curr_freq, 0,
+				      dst, wpabuf_head(hapd->dpp_auth->req_msg),
+				      wpabuf_len(hapd->dpp_auth->req_msg));
+
+	return res;
+fail:
+	dpp_configuration_free(conf_sta);
+	dpp_configuration_free(conf_ap);
+	return -1;
+}
+
+
+static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src,
+				    const u8 *hdr, const u8 *buf, size_t len,
+				    unsigned int freq)
+{
+	const u8 *r_bootstrap, *i_bootstrap, *wrapped_data;
+	u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len;
+	struct dpp_bootstrap_info *bi, *own_bi = NULL, *peer_bi = NULL;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR,
+		   MAC2STR(src));
+
+	wrapped_data = dpp_get_attr(buf, len, DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing required Wrapped data attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped data",
+		    wrapped_data, wrapped_data_len);
+
+	r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+				   &r_bootstrap_len);
+	if (!r_bootstrap || r_bootstrap > wrapped_data ||
+	    r_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+		    r_bootstrap, r_bootstrap_len);
+
+	i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+				   &i_bootstrap_len);
+	if (!i_bootstrap || i_bootstrap > wrapped_data ||
+	    i_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Initiator Bootstrapping Key Hash attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash",
+		    i_bootstrap, i_bootstrap_len);
+
+	/* Try to find own and peer bootstrapping key matches based on the
+	 * received hash values */
+	dl_list_for_each(bi, &hapd->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;
+	}
+
+	if (!own_bi) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No matching own bootstrapping key found - ignore message");
+		return;
+	}
+
+	if (hapd->dpp_auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Already in DPP authentication exchange - ignore new one");
+		return;
+	}
+
+	hapd->dpp_auth_ok_on_ack = 0;
+	hapd->dpp_auth = dpp_auth_req_rx(hapd->msg_ctx, hapd->dpp_allowed_roles,
+					 hapd->dpp_qr_mutual,
+					 peer_bi, own_bi, freq, hdr, buf,
+					 wrapped_data, wrapped_data_len);
+	if (!hapd->dpp_auth) {
+		wpa_printf(MSG_DEBUG, "DPP: No response generated");
+		return;
+	}
+	hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth);
+	hostapd_dpp_set_configurator(hapd, hapd->dpp_auth,
+				     hapd->dpp_configurator_params);
+	os_memcpy(hapd->dpp_auth->peer_mac_addr, src, ETH_ALEN);
+
+	hostapd_drv_send_action(hapd, hapd->dpp_auth->curr_freq, 0,
+				src, wpabuf_head(hapd->dpp_auth->resp_msg),
+				wpabuf_len(hapd->dpp_auth->resp_msg));
+}
+
+
+static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
+				    enum gas_query_ap_result result,
+				    const struct wpabuf *adv_proto,
+				    const struct wpabuf *resp, u16 status_code)
+{
+	struct hostapd_data *hapd = ctx;
+	const u8 *pos;
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
+	if (!auth || !auth->auth_success) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+		return;
+	}
+	if (!resp || status_code != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "DPP: GAS query did not succeed");
+		goto fail;
+	}
+
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response adv_proto",
+			adv_proto);
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response (GAS response)",
+			resp);
+
+	if (wpabuf_len(adv_proto) != 10 ||
+	    !(pos = wpabuf_head(adv_proto)) ||
+	    pos[0] != WLAN_EID_ADV_PROTO ||
+	    pos[1] != 8 ||
+	    pos[3] != WLAN_EID_VENDOR_SPECIFIC ||
+	    pos[4] != 5 ||
+	    WPA_GET_BE24(&pos[5]) != OUI_WFA ||
+	    pos[8] != 0x1a ||
+	    pos[9] != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Not a DPP Advertisement Protocol ID");
+		goto fail;
+	}
+
+	if (dpp_conf_resp_rx(auth, resp) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
+		goto fail;
+	}
+
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_RECEIVED);
+	if (auth->ssid_len)
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_SSID "%s",
+			wpa_ssid_txt(auth->ssid, auth->ssid_len));
+	if (auth->connector) {
+		/* TODO: Save the Connector and consider using a command
+		 * to fetch the value instead of sending an event with
+		 * it. The Connector could end up being larger than what
+		 * most clients are ready to receive as an event
+		 * message. */
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONNECTOR "%s",
+			auth->connector);
+	} else if (auth->passphrase[0]) {
+		char hex[64 * 2 + 1];
+
+		wpa_snprintf_hex(hex, sizeof(hex),
+				 (const u8 *) auth->passphrase,
+				 os_strlen(auth->passphrase));
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PASS "%s",
+			hex);
+	} else if (auth->psk_set) {
+		char hex[PMK_LEN * 2 + 1];
+
+		wpa_snprintf_hex(hex, sizeof(hex), auth->psk, PMK_LEN);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PSK "%s",
+			hex);
+	}
+	if (auth->c_sign_key) {
+		char *hex;
+		size_t hexlen;
+
+		hexlen = 2 * wpabuf_len(auth->c_sign_key) + 1;
+		hex = os_malloc(hexlen);
+		if (hex) {
+			wpa_snprintf_hex(hex, hexlen,
+					 wpabuf_head(auth->c_sign_key),
+					 wpabuf_len(auth->c_sign_key));
+			wpa_msg(hapd->msg_ctx, MSG_INFO,
+				DPP_EVENT_C_SIGN_KEY "%s", hex);
+			os_free(hex);
+		}
+	}
+	if (auth->net_access_key) {
+		char *hex;
+		size_t hexlen;
+
+		hexlen = 2 * wpabuf_len(auth->net_access_key) + 1;
+		hex = os_malloc(hexlen);
+		if (hex) {
+			wpa_snprintf_hex(hex, hexlen,
+					 wpabuf_head(auth->net_access_key),
+					 wpabuf_len(auth->net_access_key));
+			if (auth->net_access_key_expiry)
+				wpa_msg(hapd->msg_ctx, MSG_INFO,
+					DPP_EVENT_NET_ACCESS_KEY "%s %lu", hex,
+					(unsigned long)
+					auth->net_access_key_expiry);
+			else
+				wpa_msg(hapd->msg_ctx, MSG_INFO,
+					DPP_EVENT_NET_ACCESS_KEY "%s", hex);
+			os_free(hex);
+		}
+	}
+	dpp_auth_deinit(hapd->dpp_auth);
+	hapd->dpp_auth = NULL;
+	return;
+
+fail:
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+	dpp_auth_deinit(hapd->dpp_auth);
+	hapd->dpp_auth = NULL;
+}
+
+
+static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd)
+{
+	struct dpp_authentication *auth = hapd->dpp_auth;
+	struct wpabuf *buf, *conf_req;
+	char json[100];
+	int res;
+	int netrole_ap = 1;
+
+	os_snprintf(json, sizeof(json),
+		    "{\"name\":\"Test\","
+		    "\"wi-fi_tech\":\"infra\","
+		    "\"netRole\":\"%s\"}",
+		    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) {
+		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);
+
+	res = gas_query_ap_req(hapd->gas, auth->peer_mac_addr, auth->curr_freq,
+			       buf, hostapd_dpp_gas_resp_cb, hapd);
+	if (res < 0) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+			"GAS: Failed to send Query Request");
+		wpabuf_free(buf);
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: GAS query started with dialog token %u", res);
+	}
+}
+
+
+static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator)
+{
+	wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=%d",
+		initiator);
+
+	if (!hapd->dpp_auth->configurator)
+		hostapd_dpp_start_gas_client(hapd);
+}
+
+
+static void hostapd_dpp_rx_auth_resp(struct hostapd_data *hapd, const u8 *src,
+				     const u8 *hdr, const u8 *buf, size_t len)
+{
+	struct dpp_authentication *auth = hapd->dpp_auth;
+	struct wpabuf *msg;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication Response from " MACSTR,
+		   MAC2STR(src));
+
+	if (!auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Authentication in progress - drop");
+		return;
+	}
+
+	if (!is_zero_ether_addr(auth->peer_mac_addr) &&
+	    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;
+	}
+
+	msg = dpp_auth_resp_rx(auth, hdr, buf, len);
+	if (!msg) {
+		if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
+			wpa_printf(MSG_DEBUG, "DPP: Wait for full response");
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "DPP: No confirm generated");
+		return;
+	}
+	os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
+
+	hostapd_drv_send_action(hapd, auth->curr_freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+	wpabuf_free(msg);
+	hapd->dpp_auth_ok_on_ack = 1;
+}
+
+
+static void hostapd_dpp_rx_auth_conf(struct hostapd_data *hapd, const u8 *src,
+				     const u8 *hdr, const u8 *buf, size_t len)
+{
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation from " MACSTR,
+		   MAC2STR(src));
+
+	if (!auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Authentication in progress - drop");
+		return;
+	}
+
+	if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+			   MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+		return;
+	}
+
+	if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
+		return;
+	}
+
+	hostapd_dpp_auth_success(hapd, 0);
+}
+
+
+static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd,
+					 const u8 *src,
+					 const u8 *buf, size_t len,
+					 unsigned int freq)
+{
+	const u8 *connector, *trans_id;
+	u16 connector_len, trans_id_len;
+	struct os_time now;
+	struct dpp_introduction intro;
+	os_time_t expire;
+	int expiration;
+	struct wpabuf *msg;
+
+	wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Request from " MACSTR,
+		   MAC2STR(src));
+	if (!hapd->wpa_auth ||
+	    !(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) ||
+	    !(hapd->conf->wpa & WPA_PROTO_RSN)) {
+		wpa_printf(MSG_DEBUG, "DPP: DPP AKM not in use");
+		return;
+	}
+
+	if (!hapd->conf->dpp_connector || !hapd->conf->dpp_netaccesskey ||
+	    !hapd->conf->dpp_csign) {
+		wpa_printf(MSG_DEBUG, "DPP: No own Connector/keys set");
+		return;
+	}
+
+	os_get_time(&now);
+
+	if (hapd->conf->dpp_netaccesskey_expiry &&
+	    hapd->conf->dpp_netaccesskey_expiry < now.sec) {
+		wpa_printf(MSG_INFO, "DPP: Own netAccessKey expired");
+		return;
+	}
+
+	trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
+			       &trans_id_len);
+	if (!trans_id || trans_id_len != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include Transaction ID");
+		return;
+	}
+
+	connector = dpp_get_attr(buf, len, DPP_ATTR_CONNECTOR, &connector_len);
+	if (!connector) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include its Connector");
+		return;
+	}
+
+	if (dpp_peer_intro(&intro, hapd->conf->dpp_connector,
+			   wpabuf_head(hapd->conf->dpp_netaccesskey),
+			   wpabuf_len(hapd->conf->dpp_netaccesskey),
+			   wpabuf_head(hapd->conf->dpp_csign),
+			   wpabuf_len(hapd->conf->dpp_csign),
+			   connector, connector_len, &expire) < 0) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Network Introduction protocol resulted in failure");
+		return;
+	}
+
+	if (!expire || hapd->conf->dpp_netaccesskey_expiry < expire)
+		expire = hapd->conf->dpp_netaccesskey_expiry;
+	if (expire)
+		expiration = expire - now.sec;
+	else
+		expiration = 0;
+
+	if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+				intro.pmkid, expiration,
+				WPA_KEY_MGMT_DPP) < 0) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
+		return;
+	}
+
+	msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_RESP,
+			    5 + 4 + os_strlen(hapd->conf->dpp_connector));
+	if (!msg)
+		return;
+
+	/* Transaction ID */
+	wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, trans_id[0]);
+
+	/* DPP Connector */
+	wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+	wpabuf_put_le16(msg, os_strlen(hapd->conf->dpp_connector));
+	wpabuf_put_str(msg, hapd->conf->dpp_connector);
+
+	wpa_printf(MSG_DEBUG, "DPP: Send Peer Discovery Response to " MACSTR,
+		   MAC2STR(src));
+	hostapd_drv_send_action(hapd, freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+	wpabuf_free(msg);
+}
+
+
+static void
+hostapd_dpp_rx_pkex_exchange_req(struct hostapd_data *hapd, const u8 *src,
+				 const u8 *buf, size_t len,
+				 unsigned int freq)
+{
+	struct wpabuf *msg;
+
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request from " MACSTR,
+		   MAC2STR(src));
+
+	/* TODO: Support multiple PKEX codes by iterating over all the enabled
+	 * values here */
+
+	if (!hapd->dpp_pkex_code || !hapd->dpp_pkex_bi) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No PKEX code configured - ignore request");
+		return;
+	}
+
+	if (hapd->dpp_pkex) {
+		/* TODO: Support parallel operations */
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Already in PKEX session - ignore new request");
+		return;
+	}
+
+	hapd->dpp_pkex = dpp_pkex_rx_exchange_req(hapd->dpp_pkex_bi,
+						  hapd->own_addr, src,
+						  hapd->dpp_pkex_identifier,
+						  hapd->dpp_pkex_code,
+						  buf, len);
+	if (!hapd->dpp_pkex) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to process the request - ignore it");
+		return;
+	}
+
+	msg = hapd->dpp_pkex->exchange_resp;
+	hostapd_drv_send_action(hapd, freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+}
+
+
+static void
+hostapd_dpp_rx_pkex_exchange_resp(struct hostapd_data *hapd, const u8 *src,
+				  const u8 *buf, size_t len, unsigned int freq)
+{
+	struct wpabuf *msg;
+
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response from " MACSTR,
+		   MAC2STR(src));
+
+	/* TODO: Support multiple PKEX codes by iterating over all the enabled
+	 * values here */
+
+	if (!hapd->dpp_pkex || !hapd->dpp_pkex->initiator ||
+	    hapd->dpp_pkex->exchange_done) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
+		return;
+	}
+
+	os_memcpy(hapd->dpp_pkex->peer_mac, src, ETH_ALEN);
+	msg = dpp_pkex_rx_exchange_resp(hapd->dpp_pkex, buf, len);
+	if (!msg) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Request to " MACSTR,
+		   MAC2STR(src));
+
+	hostapd_drv_send_action(hapd, freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+	wpabuf_free(msg);
+}
+
+
+static void
+hostapd_dpp_rx_pkex_commit_reveal_req(struct hostapd_data *hapd, const u8 *src,
+				      const u8 *hdr, const u8 *buf, size_t len,
+				      unsigned int freq)
+{
+	struct wpabuf *msg;
+	struct dpp_pkex *pkex = hapd->dpp_pkex;
+	struct dpp_bootstrap_info *bi;
+
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Request from " MACSTR,
+		   MAC2STR(src));
+
+	if (!pkex || pkex->initiator || !pkex->exchange_done) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
+		return;
+	}
+
+	msg = dpp_pkex_rx_commit_reveal_req(pkex, hdr, buf, len);
+	if (!msg) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to process the request");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Response to "
+		   MACSTR, MAC2STR(src));
+
+	hostapd_drv_send_action(hapd, freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+	wpabuf_free(msg);
+
+	bi = os_zalloc(sizeof(*bi));
+	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->dpp_bootstrap, &bi->list);
+}
+
+
+static void
+hostapd_dpp_rx_pkex_commit_reveal_resp(struct hostapd_data *hapd, const u8 *src,
+				       const u8 *hdr, const u8 *buf, size_t len,
+				       unsigned int freq)
+{
+	int res;
+	struct dpp_bootstrap_info *bi, *own_bi;
+	struct dpp_pkex *pkex = hapd->dpp_pkex;
+	char cmd[500];
+
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Response from " MACSTR,
+		   MAC2STR(src));
+
+	if (!pkex || !pkex->initiator || !pkex->exchange_done) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
+		return;
+	}
+
+	res = dpp_pkex_rx_commit_reveal_resp(pkex, hdr, buf, len);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
+		return;
+	}
+
+	own_bi = pkex->own_bi;
+
+	bi = os_zalloc(sizeof(*bi));
+	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->dpp_bootstrap, &bi->list);
+
+	os_snprintf(cmd, sizeof(cmd), " peer=%u %s",
+		    bi->id,
+		    hapd->dpp_pkex_auth_cmd ? hapd->dpp_pkex_auth_cmd : "");
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Start authentication after PKEX with parameters: %s",
+		   cmd);
+	if (hostapd_dpp_auth_init(hapd, cmd) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Authentication initialization failed");
+		return;
+	}
+}
+
+
+void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
+			   const u8 *buf, size_t len, unsigned int freq)
+{
+	u8 crypto_suite;
+	enum dpp_public_action_frame_type type;
+	const u8 *hdr;
+
+	if (len < DPP_HDR_LEN)
+		return;
+	if (WPA_GET_BE24(buf) != OUI_WFA || buf[3] != DPP_OUI_TYPE)
+		return;
+	hdr = buf;
+	buf += 4;
+	len -= 4;
+	crypto_suite = *buf++;
+	type = *buf++;
+	len -= 2;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Received DPP Public Action frame crypto suite %u type %d from "
+		   MACSTR " freq=%u",
+		   crypto_suite, type, MAC2STR(src), freq);
+	if (crypto_suite != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: Unsupported crypto suite %u",
+			   crypto_suite);
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes", buf, len);
+	if (dpp_check_attrs(buf, len) < 0)
+		return;
+
+	switch (type) {
+	case DPP_PA_AUTHENTICATION_REQ:
+		hostapd_dpp_rx_auth_req(hapd, src, hdr, buf, len, freq);
+		break;
+	case DPP_PA_AUTHENTICATION_RESP:
+		hostapd_dpp_rx_auth_resp(hapd, src, hdr, buf, len);
+		break;
+	case DPP_PA_AUTHENTICATION_CONF:
+		hostapd_dpp_rx_auth_conf(hapd, src, hdr, buf, len);
+		break;
+	case DPP_PA_PEER_DISCOVERY_REQ:
+		hostapd_dpp_rx_peer_disc_req(hapd, src, buf, len, freq);
+		break;
+	case DPP_PA_PKEX_EXCHANGE_REQ:
+		hostapd_dpp_rx_pkex_exchange_req(hapd, src, buf, len, freq);
+		break;
+	case DPP_PA_PKEX_EXCHANGE_RESP:
+		hostapd_dpp_rx_pkex_exchange_resp(hapd, src, buf, len, freq);
+		break;
+	case DPP_PA_PKEX_COMMIT_REVEAL_REQ:
+		hostapd_dpp_rx_pkex_commit_reveal_req(hapd, src, hdr, buf, len,
+						      freq);
+		break;
+	case DPP_PA_PKEX_COMMIT_REVEAL_RESP:
+		hostapd_dpp_rx_pkex_commit_reveal_resp(hapd, src, hdr, buf, len,
+						       freq);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignored unsupported frame subtype %d", type);
+		break;
+	}
+}
+
+
+struct wpabuf *
+hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
+			    const u8 *query, size_t query_len)
+{
+	struct dpp_authentication *auth = hapd->dpp_auth;
+	struct wpabuf *resp;
+
+	wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa));
+	if (!auth || !auth->auth_success ||
+	    os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+		return NULL;
+	}
+	wpa_hexdump(MSG_DEBUG,
+		    "DPP: Received Configuration Request (GAS Query Request)",
+		    query, query_len);
+	resp = dpp_conf_req_rx(auth, query, query_len);
+	if (!resp)
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+	return resp;
+}
+
+
+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->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->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 hostapd_data *hapd, unsigned int id)
+{
+	struct dpp_configurator *conf, *tmp;
+	int found = 0;
+
+	dl_list_for_each_safe(conf, tmp, &hapd->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, id_val);
+}
+
+
+int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd)
+{
+	struct dpp_bootstrap_info *own_bi;
+	const char *pos, *end;
+
+	pos = os_strstr(cmd, " own=");
+	if (!pos)
+		return -1;
+	pos += 5;
+	own_bi = dpp_bootstrap_get_id(hapd, atoi(pos));
+	if (!own_bi) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Identified bootstrap info not found");
+		return -1;
+	}
+	if (own_bi->type != DPP_BOOTSTRAP_PKEX) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Identified bootstrap info not for PKEX");
+		return -1;
+	}
+	hapd->dpp_pkex_bi = own_bi;
+
+	os_free(hapd->dpp_pkex_identifier);
+	hapd->dpp_pkex_identifier = NULL;
+	pos = os_strstr(cmd, " identifier=");
+	if (pos) {
+		pos += 12;
+		end = os_strchr(pos, ' ');
+		if (!end)
+			return -1;
+		hapd->dpp_pkex_identifier = os_malloc(end - pos + 1);
+		if (!hapd->dpp_pkex_identifier)
+			return -1;
+		os_memcpy(hapd->dpp_pkex_identifier, pos, end - pos);
+		hapd->dpp_pkex_identifier[end - pos] = '\0';
+	}
+
+	pos = os_strstr(cmd, " code=");
+	if (!pos)
+		return -1;
+	os_free(hapd->dpp_pkex_code);
+	hapd->dpp_pkex_code = os_strdup(pos + 6);
+	if (!hapd->dpp_pkex_code)
+		return -1;
+
+	if (os_strstr(cmd, " init=1")) {
+		struct wpabuf *msg;
+
+		wpa_printf(MSG_DEBUG, "DPP: Initiating PKEX");
+		dpp_pkex_free(hapd->dpp_pkex);
+		hapd->dpp_pkex = dpp_pkex_init(own_bi, hapd->own_addr,
+					       hapd->dpp_pkex_identifier,
+					       hapd->dpp_pkex_code);
+		if (!hapd->dpp_pkex)
+			return -1;
+
+		msg = hapd->dpp_pkex->exchange_req;
+		/* TODO: Which channel to use? */
+		hostapd_drv_send_action(hapd, 2437, 0, broadcast,
+					wpabuf_head(msg), wpabuf_len(msg));
+	}
+
+	/* TODO: Support multiple PKEX info entries */
+
+	os_free(hapd->dpp_pkex_auth_cmd);
+	hapd->dpp_pkex_auth_cmd = os_strdup(cmd);
+
+	return 1;
+}
+
+
+int hostapd_dpp_pkex_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;
+	}
+
+	if ((id_val != 0 && id_val != 1) || !hapd->dpp_pkex_code)
+		return -1;
+
+	/* TODO: Support multiple PKEX entries */
+	os_free(hapd->dpp_pkex_code);
+	hapd->dpp_pkex_code = NULL;
+	os_free(hapd->dpp_pkex_identifier);
+	hapd->dpp_pkex_identifier = NULL;
+	os_free(hapd->dpp_pkex_auth_cmd);
+	hapd->dpp_pkex_auth_cmd = NULL;
+	hapd->dpp_pkex_bi = NULL;
+	/* TODO: Remove dpp_pkex only if it is for the identified PKEX code */
+	dpp_pkex_free(hapd->dpp_pkex);
+	hapd->dpp_pkex = NULL;
+	return 0;
+}
+
+
+int hostapd_dpp_init(struct hostapd_data *hapd)
+{
+	dl_list_init(&hapd->dpp_bootstrap);
+	dl_list_init(&hapd->dpp_configurator);
+	hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE;
+	hapd->dpp_init_done = 1;
+	return 0;
+}
+
+
+void hostapd_dpp_deinit(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	os_free(hapd->dpp_config_obj_override);
+	hapd->dpp_config_obj_override = NULL;
+	os_free(hapd->dpp_discovery_override);
+	hapd->dpp_discovery_override = NULL;
+	os_free(hapd->dpp_groups_override);
+	hapd->dpp_groups_override = NULL;
+	hapd->dpp_ignore_netaccesskey_mismatch = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (!hapd->dpp_init_done)
+		return;
+	dpp_bootstrap_del(hapd, 0);
+	dpp_configurator_del(hapd, 0);
+	dpp_auth_deinit(hapd->dpp_auth);
+	hapd->dpp_auth = NULL;
+	hostapd_dpp_pkex_remove(hapd, "*");
+	hapd->dpp_pkex = NULL;
+	os_free(hapd->dpp_configurator_params);
+	hapd->dpp_configurator_params = NULL;
+}
diff --git a/src/ap/dpp_hostapd.h b/src/ap/dpp_hostapd.h
new file mode 100644
index 0000000..d870b20
--- /dev/null
+++ b/src/ap/dpp_hostapd.h
@@ -0,0 +1,34 @@
+/*
+ * hostapd / DPP integration
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DPP_HOSTAPD_H
+#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);
+void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
+			   const u8 *buf, size_t len, unsigned int freq);
+void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
+			   const u8 *data, size_t data_len, int ok);
+struct wpabuf *
+hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
+			    const u8 *query, size_t query_len);
+int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id);
+int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id);
+int hostapd_dpp_init(struct hostapd_data *hapd);
+void hostapd_dpp_deinit(struct hostapd_data *hapd);
+
+#endif /* DPP_HOSTAPD_H */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index f69c655..648f20e 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -31,10 +31,74 @@
 #include "wps_hostapd.h"
 #include "ap_drv_ops.h"
 #include "ap_config.h"
+#include "ap_mlme.h"
 #include "hw_features.h"
 #include "dfs.h"
 #include "beacon.h"
 #include "mbo_ap.h"
+#include "dpp_hostapd.h"
+#include "fils_hlp.h"
+
+
+#ifdef CONFIG_FILS
+void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd,
+				      struct sta_info *sta)
+{
+	u16 reply_res = WLAN_STATUS_SUCCESS;
+	struct ieee802_11_elems elems;
+	u8 buf[IEEE80211_MAX_MMPDU_SIZE], *p = buf;
+	int new_assoc;
+
+	wpa_printf(MSG_DEBUG, "%s FILS: Finish association with " MACSTR,
+		   __func__, MAC2STR(sta->addr));
+	eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+	if (!sta->fils_pending_assoc_req)
+		return;
+
+	ieee802_11_parse_elems(sta->fils_pending_assoc_req,
+			       sta->fils_pending_assoc_req_len, &elems, 0);
+	if (!elems.fils_session) {
+		wpa_printf(MSG_DEBUG, "%s failed to find FILS Session element",
+			   __func__);
+		return;
+	}
+
+	p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p,
+					   elems.fils_session,
+					   sta->fils_hlp_resp);
+
+	reply_res = hostapd_sta_assoc(hapd, sta->addr,
+				      sta->fils_pending_assoc_is_reassoc,
+				      WLAN_STATUS_SUCCESS,
+				      buf, p - buf);
+	ap_sta_set_authorized(hapd, sta, 1);
+	new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
+	sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
+	sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+	hostapd_set_sta_flags(hapd, sta);
+	wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS);
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
+	hostapd_new_assoc_sta(hapd, sta, !new_assoc);
+	os_free(sta->fils_pending_assoc_req);
+	sta->fils_pending_assoc_req = NULL;
+	sta->fils_pending_assoc_req_len = 0;
+	wpabuf_free(sta->fils_hlp_resp);
+	sta->fils_hlp_resp = NULL;
+	wpabuf_free(sta->hlp_dhcp_discover);
+	sta->hlp_dhcp_discover = NULL;
+	fils_hlp_deinit(hapd);
+
+	/*
+	 * Remove the station in case transmission of a success response fails
+	 * (the STA was added associated to the driver) or if the station was
+	 * previously added unassociated.
+	 */
+	if (reply_res != WLAN_STATUS_SUCCESS || sta->added_unassoc) {
+		hostapd_drv_sta_remove(hapd, sta->addr);
+		sta->added_unassoc = 0;
+	}
+}
+#endif /* CONFIG_FILS */
 
 
 int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
@@ -45,10 +109,10 @@
 	struct ieee802_11_elems elems;
 	const u8 *ie;
 	size_t ielen;
-#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_IEEE80211W)
+#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS)
 	u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
 	u8 *p = buf;
-#endif /* CONFIG_IEEE80211R_AP || CONFIG_IEEE80211W */
+#endif /* CONFIG_IEEE80211R_AP || CONFIG_IEEE80211W || CONFIG_FILS */
 	u16 reason = WLAN_REASON_UNSPECIFIED;
 	u16 status = WLAN_STATUS_SUCCESS;
 	const u8 *p2p_dev_addr = NULL;
@@ -231,7 +295,8 @@
 		}
 		res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
 					  ie, ielen,
-					  elems.mdie, elems.mdie_len);
+					  elems.mdie, elems.mdie_len,
+					  elems.owe_dh, elems.owe_dh_len);
 		if (res != WPA_IE_OK) {
 			wpa_printf(MSG_DEBUG,
 				   "WPA/RSN information element rejected? (res %u)",
@@ -378,16 +443,96 @@
 #ifdef CONFIG_IEEE80211R_AP
 	p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf),
 					sta->auth_alg, req_ies, req_ies_len);
+#endif /* CONFIG_IEEE80211R_AP */
 
+#ifdef CONFIG_FILS
+	if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	    sta->auth_alg == WLAN_AUTH_FILS_PK) {
+		int delay_assoc = 0;
+
+		if (!req_ies)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+		if (!wpa_fils_validate_fils_session(sta->wpa_sm, req_ies,
+						    req_ies_len,
+						    sta->fils_session)) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Session validation failed");
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		}
+
+		res = wpa_fils_validate_key_confirm(sta->wpa_sm, req_ies,
+						    req_ies_len);
+		if (res < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Key Confirm validation failed");
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		}
+
+		if (fils_process_hlp(hapd, sta, req_ies, req_ies_len) > 0) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Delaying Assoc Response (HLP)");
+			delay_assoc = 1;
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Going ahead with Assoc Response (no HLP)");
+		}
+
+		if (sta) {
+			wpa_printf(MSG_DEBUG, "FILS: HLP callback cleanup");
+			eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+			os_free(sta->fils_pending_assoc_req);
+			sta->fils_pending_assoc_req = NULL;
+			sta->fils_pending_assoc_req_len = 0;
+			wpabuf_free(sta->fils_hlp_resp);
+			sta->fils_hlp_resp = NULL;
+			sta->fils_drv_assoc_finish = 0;
+		}
+
+		if (sta && delay_assoc && status == WLAN_STATUS_SUCCESS) {
+			u8 *req_tmp;
+
+			req_tmp = os_malloc(req_ies_len);
+			if (!req_tmp) {
+				wpa_printf(MSG_DEBUG,
+					   "FILS: buffer allocation failed for assoc req");
+				goto fail;
+			}
+			os_memcpy(req_tmp, req_ies, req_ies_len);
+			sta->fils_pending_assoc_req = req_tmp;
+			sta->fils_pending_assoc_req_len = req_ies_len;
+			sta->fils_pending_assoc_is_reassoc = reassoc;
+			sta->fils_drv_assoc_finish = 1;
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Waiting for HLP processing before sending (Re)Association Response frame to "
+				   MACSTR, MAC2STR(sta->addr));
+			eloop_register_timeout(
+				0, hapd->conf->fils_hlp_wait_time * 1024,
+				fils_hlp_timeout, hapd, sta);
+			return 0;
+		}
+		p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p,
+						   elems.fils_session,
+						   sta->fils_hlp_resp);
+		wpa_hexdump(MSG_DEBUG, "FILS Assoc Resp BUF (IEs)",
+			    buf, p - buf);
+	}
+#endif /* CONFIG_FILS */
+
+#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_FILS)
 	hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
 
-	if (sta->auth_alg == WLAN_AUTH_FT)
+	if (sta->auth_alg == WLAN_AUTH_FT ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	    sta->auth_alg == WLAN_AUTH_FILS_PK)
 		ap_sta_set_authorized(hapd, sta, 1);
-#else /* CONFIG_IEEE80211R_AP */
+#else /* CONFIG_IEEE80211R_AP || CONFIG_FILS */
 	/* Keep compiler silent about unused variables */
 	if (status) {
 	}
-#endif /* CONFIG_IEEE80211R_AP */
+#endif /* CONFIG_IEEE80211R_AP || CONFIG_FILS */
 
 	new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
 	sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
@@ -397,6 +542,12 @@
 
 	if (reassoc && (sta->auth_alg == WLAN_AUTH_FT))
 		wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
+#ifdef CONFIG_FILS
+	else if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+		 sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+		 sta->auth_alg == WLAN_AUTH_FILS_PK)
+		wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS);
+#endif /* CONFIG_FILS */
 	else
 		wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
 
@@ -711,6 +862,32 @@
 #endif /* CONFIG_IEEE80211R_AP */
 
 
+#ifdef CONFIG_FILS
+static void hostapd_notify_auth_fils_finish(struct hostapd_data *hapd,
+					    struct sta_info *sta, u16 resp,
+					    struct wpabuf *data, int pub)
+{
+	if (resp == WLAN_STATUS_SUCCESS) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG, "authentication OK (FILS)");
+		sta->flags |= WLAN_STA_AUTH;
+		wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+		sta->auth_alg = WLAN_AUTH_FILS_SK;
+		mlme_authenticate_indication(hapd, sta);
+	} else {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "authentication failed (FILS)");
+	}
+
+	hostapd_sta_auth(hapd, sta->addr, 2, resp,
+			 data ? wpabuf_head(data) : NULL,
+			 data ? wpabuf_len(data) : 0);
+	wpabuf_free(data);
+}
+#endif /* CONFIG_FILS */
+
+
 static void hostapd_notif_auth(struct hostapd_data *hapd,
 			       struct auth_info *rx_auth)
 {
@@ -748,6 +925,18 @@
 		return;
 	}
 #endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_FILS
+	if (rx_auth->auth_type == WLAN_AUTH_FILS_SK) {
+		sta->auth_alg = WLAN_AUTH_FILS_SK;
+		handle_auth_fils(hapd, sta, rx_auth->ies, rx_auth->ies_len,
+				 rx_auth->auth_type, rx_auth->auth_transaction,
+				 rx_auth->status_code,
+				 hostapd_notify_auth_fils_finish);
+		return;
+	}
+#endif /* CONFIG_FILS */
+
 fail:
 	hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1,
 			 status, resp_ies, resp_ies_len);
@@ -795,18 +984,34 @@
 			mgmt->u.action.u.sa_query_resp.trans_id);
 	}
 #endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
 	if (mgmt->u.action.category == WLAN_ACTION_WNM) {
 		ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len);
 	}
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
 #ifdef CONFIG_FST
 	if (mgmt->u.action.category == WLAN_ACTION_FST && hapd->iface->fst) {
 		fst_rx_action(hapd->iface->fst, mgmt, drv_mgmt->frame_len);
 		return;
 	}
 #endif /* CONFIG_FST */
+#ifdef CONFIG_DPP
+	if (plen >= 1 + 4 &&
+	    mgmt->u.action.u.vs_public_action.action ==
+	    WLAN_PA_VENDOR_SPECIFIC &&
+	    WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
+	    OUI_WFA &&
+	    mgmt->u.action.u.vs_public_action.variable[0] ==
+	    DPP_OUI_TYPE) {
+		const u8 *pos, *end;
 
+		pos = mgmt->u.action.u.vs_public_action.oui;
+		end = drv_mgmt->frame + drv_mgmt->frame_len;
+		hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos,
+				      drv_mgmt->freq);
+		return;
+	}
+#endif /* CONFIG_DPP */
 }
 
 
@@ -1121,6 +1326,16 @@
 }
 
 
+static void hostapd_event_dfs_pre_cac_expired(struct hostapd_data *hapd,
+					      struct dfs_event *radar)
+{
+	wpa_printf(MSG_DEBUG, "DFS Pre-CAC expired on %d MHz", radar->freq);
+	hostapd_dfs_pre_cac_expired(hapd->iface, radar->freq, radar->ht_enabled,
+				    radar->chan_offset, radar->chan_width,
+				    radar->cf1, radar->cf2);
+}
+
+
 static void hostapd_event_dfs_cac_finished(struct hostapd_data *hapd,
 					   struct dfs_event *radar)
 {
@@ -1313,6 +1528,11 @@
 			break;
 		hostapd_event_dfs_radar_detected(hapd, &data->dfs_event);
 		break;
+	case EVENT_DFS_PRE_CAC_EXPIRED:
+		if (!data)
+			break;
+		hostapd_event_dfs_pre_cac_expired(hapd, &data->dfs_event);
+		break;
 	case EVENT_DFS_CAC_FINISHED:
 		if (!data)
 			break;
diff --git a/src/ap/eth_p_oui.c b/src/ap/eth_p_oui.c
new file mode 100644
index 0000000..aba901e
--- /dev/null
+++ b/src/ap/eth_p_oui.c
@@ -0,0 +1,191 @@
+/*
+ * hostapd / IEEE 802 OUI Extended EtherType 88-B7
+ * Copyright (c) 2016, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "l2_packet/l2_packet.h"
+#include "hostapd.h"
+#include "eth_p_oui.h"
+
+/*
+ * See IEEE Std 802-2014, Clause 9.2.4 for the definition of the OUI Extended
+ * EtherType 88-B7. This file implements this with OUI 00:13:74 and
+ * vendor-specific subtype 0x0001.
+ */
+static const u8 global_oui[] = { 0x00, 0x13, 0x74, 0x00, 0x01 };
+
+struct eth_p_oui_iface {
+	struct dl_list list;
+	char ifname[IFNAMSIZ + 1];
+	struct l2_packet_data *l2;
+	struct dl_list receiver;
+};
+
+struct eth_p_oui_ctx {
+	struct dl_list list;
+	struct eth_p_oui_iface *iface;
+	/* all data needed to deliver and unregister */
+	u8 oui_suffix; /* last byte of OUI */
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *dst_addr, u8 oui_suffix,
+			    const u8 *buf, size_t len);
+	void *rx_callback_ctx;
+};
+
+
+void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
+		       const u8 *dst_addr, const u8 *buf, size_t len)
+{
+	ctx->rx_callback(ctx->rx_callback_ctx, src_addr, dst_addr,
+			 ctx->oui_suffix, buf, len);
+}
+
+
+static void eth_p_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+	struct eth_p_oui_iface *iface = ctx;
+	struct eth_p_oui_ctx *receiver;
+	const struct l2_ethhdr *ethhdr;
+
+	if (len < sizeof(*ethhdr) + sizeof(global_oui) + 1) {
+		/* too short packet */
+		return;
+	}
+
+	ethhdr = (struct l2_ethhdr *) buf;
+	/* trim eth_hdr from buf and len */
+	buf += sizeof(*ethhdr);
+	len -= sizeof(*ethhdr);
+
+	/* verify OUI and vendor-specific subtype match */
+	if (os_memcmp(buf, global_oui, sizeof(global_oui)) != 0)
+		return;
+	buf += sizeof(global_oui);
+	len -= sizeof(global_oui);
+
+	dl_list_for_each(receiver, &iface->receiver,
+			 struct eth_p_oui_ctx, list) {
+		if (buf[0] != receiver->oui_suffix)
+			continue;
+
+		eth_p_oui_deliver(receiver, ethhdr->h_source, ethhdr->h_dest,
+				  buf + 1, len - 1);
+	}
+}
+
+
+struct eth_p_oui_ctx *
+eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix,
+		   void (*rx_callback)(void *ctx, const u8 *src_addr,
+				       const u8 *dst_addr, u8 oui_suffix,
+				       const u8 *buf, size_t len),
+		   void *rx_callback_ctx)
+{
+	struct eth_p_oui_iface *iface;
+	struct eth_p_oui_ctx *receiver;
+	int found = 0;
+	struct hapd_interfaces *interfaces;
+
+	receiver = os_zalloc(sizeof(*receiver));
+	if (!receiver)
+		goto err;
+
+	receiver->oui_suffix = oui_suffix;
+	receiver->rx_callback = rx_callback;
+	receiver->rx_callback_ctx = rx_callback_ctx;
+
+	interfaces = hapd->iface->interfaces;
+
+	dl_list_for_each(iface, &interfaces->eth_p_oui, struct eth_p_oui_iface,
+			 list) {
+		if (os_strcmp(iface->ifname, ifname) != 0)
+			continue;
+		found = 1;
+		break;
+	}
+
+	if (!found) {
+		iface = os_zalloc(sizeof(*iface));
+		if (!iface)
+			goto err;
+
+		os_strlcpy(iface->ifname, ifname, sizeof(iface->ifname));
+		iface->l2 = l2_packet_init(ifname, NULL, ETH_P_OUI, eth_p_rx,
+					   iface, 1);
+		if (!iface->l2) {
+			os_free(iface);
+			goto err;
+		}
+		dl_list_init(&iface->receiver);
+
+		dl_list_add_tail(&interfaces->eth_p_oui, &iface->list);
+	}
+
+	dl_list_add_tail(&iface->receiver, &receiver->list);
+	receiver->iface = iface;
+
+	return receiver;
+err:
+	os_free(receiver);
+	return NULL;
+}
+
+
+void eth_p_oui_unregister(struct eth_p_oui_ctx *ctx)
+{
+	struct eth_p_oui_iface *iface;
+
+	if (!ctx)
+		return;
+
+	iface = ctx->iface;
+
+	dl_list_del(&ctx->list);
+	os_free(ctx);
+
+	if (dl_list_empty(&iface->receiver)) {
+		dl_list_del(&iface->list);
+		l2_packet_deinit(iface->l2);
+		os_free(iface);
+	}
+}
+
+
+int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
+		   const u8 *dst_addr, const u8 *buf, size_t len)
+{
+	struct eth_p_oui_iface *iface = ctx->iface;
+	u8 *packet, *p;
+	size_t packet_len;
+	int ret;
+	struct l2_ethhdr *ethhdr;
+
+	packet_len = sizeof(*ethhdr) + sizeof(global_oui) + 1 + len;
+	packet = os_zalloc(packet_len);
+	if (!packet)
+		return -1;
+	p = packet;
+
+	ethhdr = (struct l2_ethhdr *) packet;
+	os_memcpy(ethhdr->h_source, src_addr, ETH_ALEN);
+	os_memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN);
+	ethhdr->h_proto = host_to_be16(ETH_P_OUI);
+	p += sizeof(*ethhdr);
+
+	os_memcpy(p, global_oui, sizeof(global_oui));
+	p[sizeof(global_oui)] = ctx->oui_suffix;
+	p += sizeof(global_oui) + 1;
+
+	os_memcpy(p, buf, len);
+
+	ret = l2_packet_send(iface->l2, NULL, 0, packet, packet_len);
+	os_free(packet);
+	return ret;
+}
diff --git a/src/ap/eth_p_oui.h b/src/ap/eth_p_oui.h
new file mode 100644
index 0000000..466fdc3
--- /dev/null
+++ b/src/ap/eth_p_oui.h
@@ -0,0 +1,28 @@
+/*
+ * hostapd / IEEE 802 OUI Extended Ethertype
+ * Copyright (c) 2016, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef ETH_P_OUI_H
+#define ETH_P_OUI_H
+
+struct eth_p_oui_ctx;
+struct hostapd_data;
+
+/* rx_callback only gets payload after OUI passed as buf */
+struct eth_p_oui_ctx *
+eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix,
+		   void (*rx_callback)(void *ctx, const u8 *src_addr,
+				       const u8 *dst_addr, u8 oui_suffix,
+				       const u8 *buf, size_t len),
+		   void *rx_callback_ctx);
+void eth_p_oui_unregister(struct eth_p_oui_ctx *eth_p_oui);
+int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
+		   const u8 *dst_addr, const u8 *buf, size_t len);
+void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
+		       const u8 *dst_addr, const u8 *buf, size_t len);
+
+#endif /* ETH_P_OUI_H */
diff --git a/src/ap/fils_hlp.c b/src/ap/fils_hlp.c
index 40d9be1..2a359ab 100644
--- a/src/ap/fils_hlp.c
+++ b/src/ap/fils_hlp.c
@@ -232,7 +232,7 @@
 	sta = ap_get_sta(hapd, dhcp->hw_addr);
 	if (!sta || !sta->fils_pending_assoc_req) {
 		wpa_printf(MSG_DEBUG,
-			   "FILS: No pending HLP DHCP exchange with hw_addr"
+			   "FILS: No pending HLP DHCP exchange with hw_addr "
 			   MACSTR, MAC2STR(dhcp->hw_addr));
 		return;
 	}
@@ -263,14 +263,15 @@
 	iph->ihl = sizeof(*iph) / 4;
 	iph->tot_len = htons(sizeof(*iph) + sizeof(*udph) + (end - pos));
 	iph->ttl = 1;
+	iph->protocol = 17; /* UDP */
 	iph->saddr = hapd->conf->dhcp_server.u.v4.s_addr;
 	iph->daddr = dhcp->client_ip;
 	iph->check = ip_checksum(iph, sizeof(*iph));
 	udph = wpabuf_put(resp, sizeof(*udph));
 	udph->uh_sport = htons(DHCP_SERVER_PORT);
 	udph->uh_dport = htons(DHCP_CLIENT_PORT);
-	udph->len = htons(sizeof(*udph) + (end - pos));
-	udph->check = htons(0x0000); /* TODO: calculate checksum */
+	udph->uh_ulen = htons(sizeof(*udph) + (end - pos));
+	udph->uh_sum = htons(0x0000); /* TODO: calculate checksum */
 	if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPACK &&
 	    !rapid_commit && sta->fils_dhcp_rapid_commit_proxy && end_opt) {
 		/* Add rapid commit option */
@@ -313,7 +314,11 @@
 		left -= len;
 	}
 	wpabuf_free(resp);
-	fils_hlp_finish_assoc(hapd, sta);
+
+	if (sta->fils_drv_assoc_finish)
+		hostapd_notify_assoc_fils_finish(hapd, sta);
+	else
+		fils_hlp_finish_assoc(hapd, sta);
 }
 
 
diff --git a/src/ap/gas_query_ap.c b/src/ap/gas_query_ap.c
new file mode 100644
index 0000000..fdb3cad
--- /dev/null
+++ b/src/ap/gas_query_ap.c
@@ -0,0 +1,714 @@
+/*
+ * Generic advertisement service (GAS) query (hostapd)
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011-2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, 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 "common.h"
+#include "utils/eloop.h"
+#include "utils/list.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "common/wpa_ctrl.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "gas_query_ap.h"
+
+
+/** GAS query timeout in seconds */
+#define GAS_QUERY_TIMEOUT_PERIOD 2
+
+/* GAS query wait-time / duration in ms */
+#define GAS_QUERY_WAIT_TIME_INITIAL 1000
+#define GAS_QUERY_WAIT_TIME_COMEBACK 150
+
+/**
+ * struct gas_query_pending - Pending GAS query
+ */
+struct gas_query_pending {
+	struct dl_list list;
+	struct gas_query_ap *gas;
+	u8 addr[ETH_ALEN];
+	u8 dialog_token;
+	u8 next_frag_id;
+	unsigned int wait_comeback:1;
+	unsigned int offchannel_tx_started:1;
+	unsigned int retry:1;
+	int freq;
+	u16 status_code;
+	struct wpabuf *req;
+	struct wpabuf *adv_proto;
+	struct wpabuf *resp;
+	struct os_reltime last_oper;
+	void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+		   enum gas_query_ap_result result,
+		   const struct wpabuf *adv_proto,
+		   const struct wpabuf *resp, u16 status_code);
+	void *ctx;
+	u8 sa[ETH_ALEN];
+};
+
+/**
+ * struct gas_query_ap - Internal GAS query data
+ */
+struct gas_query_ap {
+	struct hostapd_data *hapd;
+	void *msg_ctx;
+	struct dl_list pending; /* struct gas_query_pending */
+	struct gas_query_pending *current;
+};
+
+
+static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx);
+static void gas_query_timeout(void *eloop_data, void *user_ctx);
+static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx);
+static void gas_query_tx_initial_req(struct gas_query_ap *gas,
+				     struct gas_query_pending *query);
+static int gas_query_new_dialog_token(struct gas_query_ap *gas, const u8 *dst);
+
+
+static int ms_from_time(struct os_reltime *last)
+{
+	struct os_reltime now, res;
+
+	os_get_reltime(&now);
+	os_reltime_sub(&now, last, &res);
+	return res.sec * 1000 + res.usec / 1000;
+}
+
+
+/**
+ * gas_query_ap_init - Initialize GAS query component
+ * @hapd: Pointer to hostapd data
+ * Returns: Pointer to GAS query data or %NULL on failure
+ */
+struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd,
+					void *msg_ctx)
+{
+	struct gas_query_ap *gas;
+
+	gas = os_zalloc(sizeof(*gas));
+	if (!gas)
+		return NULL;
+
+	gas->hapd = hapd;
+	gas->msg_ctx = msg_ctx;
+	dl_list_init(&gas->pending);
+
+	return gas;
+}
+
+
+static const char * gas_result_txt(enum gas_query_ap_result result)
+{
+	switch (result) {
+	case GAS_QUERY_AP_SUCCESS:
+		return "SUCCESS";
+	case GAS_QUERY_AP_FAILURE:
+		return "FAILURE";
+	case GAS_QUERY_AP_TIMEOUT:
+		return "TIMEOUT";
+	case GAS_QUERY_AP_PEER_ERROR:
+		return "PEER_ERROR";
+	case GAS_QUERY_AP_INTERNAL_ERROR:
+		return "INTERNAL_ERROR";
+	case GAS_QUERY_AP_DELETED_AT_DEINIT:
+		return "DELETED_AT_DEINIT";
+	}
+
+	return "N/A";
+}
+
+
+static void gas_query_free(struct gas_query_pending *query, int del_list)
+{
+	if (del_list)
+		dl_list_del(&query->list);
+
+	wpabuf_free(query->req);
+	wpabuf_free(query->adv_proto);
+	wpabuf_free(query->resp);
+	os_free(query);
+}
+
+
+static void gas_query_done(struct gas_query_ap *gas,
+			   struct gas_query_pending *query,
+			   enum gas_query_ap_result result)
+{
+	wpa_msg(gas->msg_ctx, MSG_INFO, GAS_QUERY_DONE "addr=" MACSTR
+		" dialog_token=%u freq=%d status_code=%u result=%s",
+		MAC2STR(query->addr), query->dialog_token, query->freq,
+		query->status_code, gas_result_txt(result));
+	if (gas->current == query)
+		gas->current = NULL;
+	eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+	eloop_cancel_timeout(gas_query_timeout, gas, query);
+	eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
+	dl_list_del(&query->list);
+	query->cb(query->ctx, query->addr, query->dialog_token, result,
+		  query->adv_proto, query->resp, query->status_code);
+	gas_query_free(query, 0);
+}
+
+
+/**
+ * gas_query_ap_deinit - Deinitialize GAS query component
+ * @gas: GAS query data from gas_query_init()
+ */
+void gas_query_ap_deinit(struct gas_query_ap *gas)
+{
+	struct gas_query_pending *query, *next;
+
+	if (gas == NULL)
+		return;
+
+	dl_list_for_each_safe(query, next, &gas->pending,
+			      struct gas_query_pending, list)
+		gas_query_done(gas, query, GAS_QUERY_AP_DELETED_AT_DEINIT);
+
+	os_free(gas);
+}
+
+
+static struct gas_query_pending *
+gas_query_get_pending(struct gas_query_ap *gas, const u8 *addr, u8 dialog_token)
+{
+	struct gas_query_pending *q;
+	dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
+		if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 &&
+		    q->dialog_token == dialog_token)
+			return q;
+	}
+	return NULL;
+}
+
+
+static int gas_query_append(struct gas_query_pending *query, const u8 *data,
+			    size_t len)
+{
+	if (wpabuf_resize(&query->resp, len) < 0) {
+		wpa_printf(MSG_DEBUG, "GAS: No memory to store the response");
+		return -1;
+	}
+	wpabuf_put_data(query->resp, data, len);
+	return 0;
+}
+
+
+void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst,
+			    const u8 *data, size_t data_len, int ok)
+{
+	struct gas_query_pending *query;
+	int dur;
+
+	if (!gas || !gas->current) {
+		wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: dst=" MACSTR
+			   " ok=%d - no query in progress", MAC2STR(dst), ok);
+		return;
+	}
+
+	query = gas->current;
+
+	dur = ms_from_time(&query->last_oper);
+	wpa_printf(MSG_DEBUG, "GAS: TX status: dst=" MACSTR
+		   " ok=%d query=%p dialog_token=%u dur=%d ms",
+		   MAC2STR(dst), ok, query, query->dialog_token, dur);
+	if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination");
+		return;
+	}
+	os_get_reltime(&query->last_oper);
+
+	eloop_cancel_timeout(gas_query_timeout, gas, query);
+	if (!ok) {
+		wpa_printf(MSG_DEBUG, "GAS: No ACK to GAS request");
+		eloop_register_timeout(0, 250000, gas_query_timeout,
+				       gas, query);
+	} else {
+		eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
+				       gas_query_timeout, gas, query);
+	}
+	if (query->wait_comeback && !query->retry) {
+		eloop_cancel_timeout(gas_query_rx_comeback_timeout,
+				     gas, query);
+		eloop_register_timeout(
+			0, (GAS_QUERY_WAIT_TIME_COMEBACK + 10) * 1000,
+			gas_query_rx_comeback_timeout, gas, query);
+	}
+}
+
+
+static int pmf_in_use(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, addr);
+	return sta && (sta->flags & WLAN_STA_MFP);
+}
+
+
+static int gas_query_tx(struct gas_query_ap *gas,
+			struct gas_query_pending *query,
+			struct wpabuf *req, unsigned int wait_time)
+{
+	int res, prot = pmf_in_use(gas->hapd, query->addr);
+
+	wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
+		   "freq=%d prot=%d using src addr " MACSTR,
+		   MAC2STR(query->addr), (unsigned int) wpabuf_len(req),
+		   query->freq, prot, MAC2STR(query->sa));
+	if (prot) {
+		u8 *categ = wpabuf_mhead_u8(req);
+		*categ = WLAN_ACTION_PROTECTED_DUAL;
+	}
+	os_get_reltime(&query->last_oper);
+	res = hostapd_drv_send_action(gas->hapd, query->freq, wait_time,
+				      query->addr, wpabuf_head(req),
+				      wpabuf_len(req));
+	return res;
+}
+
+
+static void gas_query_tx_comeback_req(struct gas_query_ap *gas,
+				      struct gas_query_pending *query)
+{
+	struct wpabuf *req;
+	unsigned int wait_time;
+
+	req = gas_build_comeback_req(query->dialog_token);
+	if (req == NULL) {
+		gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+		return;
+	}
+
+	wait_time = (query->retry || !query->offchannel_tx_started) ?
+		GAS_QUERY_WAIT_TIME_INITIAL : GAS_QUERY_WAIT_TIME_COMEBACK;
+
+	if (gas_query_tx(gas, query, req, wait_time) < 0) {
+		wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
+			   MACSTR, MAC2STR(query->addr));
+		gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+	}
+
+	wpabuf_free(req);
+}
+
+
+static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx)
+{
+	struct gas_query_ap *gas = eloop_data;
+	struct gas_query_pending *query = user_ctx;
+	int dialog_token;
+
+	wpa_printf(MSG_DEBUG,
+		   "GAS: No response to comeback request received (retry=%u)",
+		   query->retry);
+	if (gas->current != query || query->retry)
+		return;
+	dialog_token = gas_query_new_dialog_token(gas, query->addr);
+	if (dialog_token < 0)
+		return;
+	wpa_printf(MSG_DEBUG,
+		   "GAS: Retry GAS query due to comeback response timeout");
+	query->retry = 1;
+	query->dialog_token = dialog_token;
+	*(wpabuf_mhead_u8(query->req) + 2) = dialog_token;
+	query->wait_comeback = 0;
+	query->next_frag_id = 0;
+	wpabuf_free(query->adv_proto);
+	query->adv_proto = NULL;
+	eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+	eloop_cancel_timeout(gas_query_timeout, gas, query);
+	gas_query_tx_initial_req(gas, query);
+}
+
+
+static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx)
+{
+	struct gas_query_ap *gas = eloop_data;
+	struct gas_query_pending *query = user_ctx;
+
+	wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR,
+		   MAC2STR(query->addr));
+	gas_query_tx_comeback_req(gas, query);
+}
+
+
+static void gas_query_tx_comeback_req_delay(struct gas_query_ap *gas,
+					    struct gas_query_pending *query,
+					    u16 comeback_delay)
+{
+	unsigned int secs, usecs;
+
+	secs = (comeback_delay * 1024) / 1000000;
+	usecs = comeback_delay * 1024 - secs * 1000000;
+	wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR
+		   " in %u secs %u usecs", MAC2STR(query->addr), secs, usecs);
+	eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+	eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout,
+			       gas, query);
+}
+
+
+static void gas_query_rx_initial(struct gas_query_ap *gas,
+				 struct gas_query_pending *query,
+				 const u8 *adv_proto, const u8 *resp,
+				 size_t len, u16 comeback_delay)
+{
+	wpa_printf(MSG_DEBUG, "GAS: Received initial response from "
+		   MACSTR " (dialog_token=%u comeback_delay=%u)",
+		   MAC2STR(query->addr), query->dialog_token, comeback_delay);
+
+	query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]);
+	if (query->adv_proto == NULL) {
+		gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+		return;
+	}
+
+	if (comeback_delay) {
+		eloop_cancel_timeout(gas_query_timeout, gas, query);
+		query->wait_comeback = 1;
+		gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
+		return;
+	}
+
+	/* Query was completed without comeback mechanism */
+	if (gas_query_append(query, resp, len) < 0) {
+		gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+		return;
+	}
+
+	gas_query_done(gas, query, GAS_QUERY_AP_SUCCESS);
+}
+
+
+static void gas_query_rx_comeback(struct gas_query_ap *gas,
+				  struct gas_query_pending *query,
+				  const u8 *adv_proto, const u8 *resp,
+				  size_t len, u8 frag_id, u8 more_frags,
+				  u16 comeback_delay)
+{
+	wpa_printf(MSG_DEBUG, "GAS: Received comeback response from "
+		   MACSTR " (dialog_token=%u frag_id=%u more_frags=%u "
+		   "comeback_delay=%u)",
+		   MAC2STR(query->addr), query->dialog_token, frag_id,
+		   more_frags, comeback_delay);
+	eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
+
+	if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) ||
+	    os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
+		      wpabuf_len(query->adv_proto)) != 0) {
+		wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed "
+			   "between initial and comeback response from "
+			   MACSTR, MAC2STR(query->addr));
+		gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR);
+		return;
+	}
+
+	if (comeback_delay) {
+		if (frag_id) {
+			wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response "
+				   "with non-zero frag_id and comeback_delay "
+				   "from " MACSTR, MAC2STR(query->addr));
+			gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR);
+			return;
+		}
+		gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
+		return;
+	}
+
+	if (frag_id != query->next_frag_id) {
+		wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response "
+			   "from " MACSTR, MAC2STR(query->addr));
+		if (frag_id + 1 == query->next_frag_id) {
+			wpa_printf(MSG_DEBUG, "GAS: Drop frame as possible "
+				   "retry of previous fragment");
+			return;
+		}
+		gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR);
+		return;
+	}
+	query->next_frag_id++;
+
+	if (gas_query_append(query, resp, len) < 0) {
+		gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+		return;
+	}
+
+	if (more_frags) {
+		gas_query_tx_comeback_req(gas, query);
+		return;
+	}
+
+	gas_query_done(gas, query, GAS_QUERY_AP_SUCCESS);
+}
+
+
+/**
+ * gas_query_ap_rx - Indicate reception of a Public Action or Protected Dual
+ *	frame
+ * @gas: GAS query data from gas_query_init()
+ * @sa: Source MAC address of the Action frame
+ * @categ: Category of the Action frame
+ * @data: Payload of the Action frame
+ * @len: Length of @data
+ * @freq: Frequency (in MHz) on which the frame was received
+ * Returns: 0 if the Public Action frame was a GAS frame or -1 if not
+ */
+int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ,
+		    const u8 *data, size_t len, int freq)
+{
+	struct gas_query_pending *query;
+	u8 action, dialog_token, frag_id = 0, more_frags = 0;
+	u16 comeback_delay, resp_len;
+	const u8 *pos, *adv_proto;
+	int prot, pmf;
+	unsigned int left;
+
+	if (!gas || len < 4)
+		return -1;
+
+	pos = data;
+	action = *pos++;
+	dialog_token = *pos++;
+
+	if (action != WLAN_PA_GAS_INITIAL_RESP &&
+	    action != WLAN_PA_GAS_COMEBACK_RESP)
+		return -1; /* Not a GAS response */
+
+	prot = categ == WLAN_ACTION_PROTECTED_DUAL;
+	pmf = pmf_in_use(gas->hapd, sa);
+	if (prot && !pmf) {
+		wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled");
+		return 0;
+	}
+	if (!prot && pmf) {
+		wpa_printf(MSG_DEBUG, "GAS: Drop unexpected unprotected GAS frame when PMF is enabled");
+		return 0;
+	}
+
+	query = gas_query_get_pending(gas, sa, dialog_token);
+	if (query == NULL) {
+		wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR
+			   " dialog token %u", MAC2STR(sa), dialog_token);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR,
+		   ms_from_time(&query->last_oper), MAC2STR(sa));
+
+	if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
+		wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
+			   MACSTR " dialog token %u when waiting for comeback "
+			   "response", MAC2STR(sa), dialog_token);
+		return 0;
+	}
+
+	if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) {
+		wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from "
+			   MACSTR " dialog token %u when waiting for initial "
+			   "response", MAC2STR(sa), dialog_token);
+		return 0;
+	}
+
+	query->status_code = WPA_GET_LE16(pos);
+	pos += 2;
+
+	if (query->status_code == WLAN_STATUS_QUERY_RESP_OUTSTANDING &&
+	    action == WLAN_PA_GAS_COMEBACK_RESP) {
+		wpa_printf(MSG_DEBUG, "GAS: Allow non-zero status for outstanding comeback response");
+	} else if (query->status_code != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
+			   "%u failed - status code %u",
+			   MAC2STR(sa), dialog_token, query->status_code);
+		gas_query_done(gas, query, GAS_QUERY_AP_FAILURE);
+		return 0;
+	}
+
+	if (action == WLAN_PA_GAS_COMEBACK_RESP) {
+		if (pos + 1 > data + len)
+			return 0;
+		frag_id = *pos & 0x7f;
+		more_frags = (*pos & 0x80) >> 7;
+		pos++;
+	}
+
+	/* Comeback Delay */
+	if (pos + 2 > data + len)
+		return 0;
+	comeback_delay = WPA_GET_LE16(pos);
+	pos += 2;
+
+	/* Advertisement Protocol element */
+	if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) {
+		wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement "
+			   "Protocol element in the response from " MACSTR,
+			   MAC2STR(sa));
+		return 0;
+	}
+
+	if (*pos != WLAN_EID_ADV_PROTO) {
+		wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
+			   "Protocol element ID %u in response from " MACSTR,
+			   *pos, MAC2STR(sa));
+		return 0;
+	}
+
+	adv_proto = pos;
+	pos += 2 + pos[1];
+
+	/* Query Response Length */
+	if (pos + 2 > data + len) {
+		wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length");
+		return 0;
+	}
+	resp_len = WPA_GET_LE16(pos);
+	pos += 2;
+
+	left = data + len - pos;
+	if (resp_len > left) {
+		wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
+			   "response from " MACSTR, MAC2STR(sa));
+		return 0;
+	}
+
+	if (resp_len < left) {
+		wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data "
+			   "after Query Response from " MACSTR,
+			   left - resp_len, MAC2STR(sa));
+	}
+
+	if (action == WLAN_PA_GAS_COMEBACK_RESP)
+		gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len,
+				      frag_id, more_frags, comeback_delay);
+	else
+		gas_query_rx_initial(gas, query, adv_proto, pos, resp_len,
+				     comeback_delay);
+
+	return 0;
+}
+
+
+static void gas_query_timeout(void *eloop_data, void *user_ctx)
+{
+	struct gas_query_ap *gas = eloop_data;
+	struct gas_query_pending *query = user_ctx;
+
+	wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR
+		   " dialog token %u",
+		   MAC2STR(query->addr), query->dialog_token);
+	gas_query_done(gas, query, GAS_QUERY_AP_TIMEOUT);
+}
+
+
+static int gas_query_dialog_token_available(struct gas_query_ap *gas,
+					    const u8 *dst, u8 dialog_token)
+{
+	struct gas_query_pending *q;
+	dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
+		if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 &&
+		    dialog_token == q->dialog_token)
+			return 0;
+	}
+
+	return 1;
+}
+
+
+static void gas_query_tx_initial_req(struct gas_query_ap *gas,
+				     struct gas_query_pending *query)
+{
+	if (gas_query_tx(gas, query, query->req,
+			 GAS_QUERY_WAIT_TIME_INITIAL) < 0) {
+		wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
+			   MACSTR, MAC2STR(query->addr));
+		gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+		return;
+	}
+	gas->current = query;
+
+	wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u",
+		   query->dialog_token);
+	eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
+			       gas_query_timeout, gas, query);
+}
+
+
+static int gas_query_new_dialog_token(struct gas_query_ap *gas, const u8 *dst)
+{
+	static int next_start = 0;
+	int dialog_token;
+
+	for (dialog_token = 0; dialog_token < 256; dialog_token++) {
+		if (gas_query_dialog_token_available(
+			    gas, dst, (next_start + dialog_token) % 256))
+			break;
+	}
+	if (dialog_token == 256)
+		return -1; /* Too many pending queries */
+	dialog_token = (next_start + dialog_token) % 256;
+	next_start = (dialog_token + 1) % 256;
+	return dialog_token;
+}
+
+
+/**
+ * gas_query_ap_req - Request a GAS query
+ * @gas: GAS query data from gas_query_init()
+ * @dst: Destination MAC address for the query
+ * @freq: Frequency (in MHz) for the channel on which to send the query
+ * @req: GAS query payload (to be freed by gas_query module in case of success
+ *	return)
+ * @cb: Callback function for reporting GAS query result and response
+ * @ctx: Context pointer to use with the @cb call
+ * Returns: dialog token (>= 0) on success or -1 on failure
+ */
+int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq,
+		     struct wpabuf *req,
+		     void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+				enum gas_query_ap_result result,
+				const struct wpabuf *adv_proto,
+				const struct wpabuf *resp, u16 status_code),
+		     void *ctx)
+{
+	struct gas_query_pending *query;
+	int dialog_token;
+
+	if (!gas || wpabuf_len(req) < 3)
+		return -1;
+
+	dialog_token = gas_query_new_dialog_token(gas, dst);
+	if (dialog_token < 0)
+		return -1;
+
+	query = os_zalloc(sizeof(*query));
+	if (query == NULL)
+		return -1;
+
+	query->gas = gas;
+	os_memcpy(query->addr, dst, ETH_ALEN);
+	query->dialog_token = dialog_token;
+	query->freq = freq;
+	query->cb = cb;
+	query->ctx = ctx;
+	query->req = req;
+	dl_list_add(&gas->pending, &query->list);
+
+	*(wpabuf_mhead_u8(req) + 2) = dialog_token;
+
+	wpa_msg(gas->msg_ctx, MSG_INFO, GAS_QUERY_START "addr=" MACSTR
+		" dialog_token=%u freq=%d",
+		MAC2STR(query->addr), query->dialog_token, query->freq);
+
+	gas_query_tx_initial_req(gas, query);
+
+	return dialog_token;
+}
diff --git a/src/ap/gas_query_ap.h b/src/ap/gas_query_ap.h
new file mode 100644
index 0000000..70f1f05
--- /dev/null
+++ b/src/ap/gas_query_ap.h
@@ -0,0 +1,43 @@
+/*
+ * Generic advertisement service (GAS) query
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011-2017, Qualcomm Atheros
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef GAS_QUERY_AP_H
+#define GAS_QUERY_AP_H
+
+struct gas_query_ap;
+
+struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd,
+					void *msg_ctx);
+void gas_query_ap_deinit(struct gas_query_ap *gas);
+int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ,
+		    const u8 *data, size_t len, int freq);
+
+/**
+ * enum gas_query_ap_result - GAS query result
+ */
+enum gas_query_ap_result {
+	GAS_QUERY_AP_SUCCESS,
+	GAS_QUERY_AP_FAILURE,
+	GAS_QUERY_AP_TIMEOUT,
+	GAS_QUERY_AP_PEER_ERROR,
+	GAS_QUERY_AP_INTERNAL_ERROR,
+	GAS_QUERY_AP_DELETED_AT_DEINIT
+};
+
+int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq,
+		     struct wpabuf *req,
+		     void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+				enum gas_query_ap_result result,
+				const struct wpabuf *adv_proto,
+				const struct wpabuf *resp, u16 status_code),
+		     void *ctx);
+void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst,
+			    const u8 *data, size_t data_len, int ok);
+
+#endif /* GAS_QUERY_AP_H */
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
index 96cd703..fadb740 100644
--- a/src/ap/gas_serv.c
+++ b/src/ap/gas_serv.c
@@ -11,14 +11,31 @@
 #include "common.h"
 #include "common/ieee802_11_defs.h"
 #include "common/gas.h"
+#include "common/wpa_ctrl.h"
 #include "utils/eloop.h"
 #include "hostapd.h"
 #include "ap_config.h"
 #include "ap_drv_ops.h"
+#include "dpp_hostapd.h"
 #include "sta_info.h"
 #include "gas_serv.h"
 
 
+#ifdef CONFIG_DPP
+static void gas_serv_write_dpp_adv_proto(struct wpabuf *buf)
+{
+	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);
+}
+#endif /* CONFIG_DPP */
+
+
 static void convert_to_protected_dual(struct wpabuf *msg)
 {
 	u8 *categ = wpabuf_mhead_u8(msg);
@@ -828,6 +845,22 @@
 #endif /* CONFIG_HS20 */
 
 
+#ifdef CONFIG_MBO
+static void anqp_add_mbo_cell_data_conn_pref(struct hostapd_data *hapd,
+					     struct wpabuf *buf)
+{
+	if (hapd->conf->mbo_cell_data_conn_pref >= 0) {
+		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+		wpabuf_put_be24(buf, OUI_WFA);
+		wpabuf_put_u8(buf, MBO_ANQP_OUI_TYPE);
+		wpabuf_put_u8(buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF);
+		wpabuf_put_u8(buf, hapd->conf->mbo_cell_data_conn_pref);
+		gas_anqp_set_element_len(buf, len);
+	}
+}
+#endif /* CONFIG_MBO */
+
+
 static size_t anqp_get_required_len(struct hostapd_data *hapd,
 				    const u16 *infoid,
 				    unsigned int num_infoid)
@@ -933,6 +966,11 @@
 		anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
 #endif /* CONFIG_HS20 */
 
+#ifdef CONFIG_MBO
+	if (request & ANQP_REQ_MBO_CELL_DATA_CONN_PREF)
+		anqp_add_mbo_cell_data_conn_pref(hapd, buf);
+#endif /* CONFIG_MBO */
+
 	return buf;
 }
 
@@ -1152,49 +1190,12 @@
 }
 
 
-static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
-				    const u8 *pos, const u8 *end,
-				    struct anqp_query_info *qi)
+static void rx_anqp_vendor_specific_hs20(struct hostapd_data *hapd,
+					 const u8 *pos, const u8 *end,
+					 struct anqp_query_info *qi)
 {
-	u32 oui;
 	u8 subtype;
 
-	if (end - pos < 4) {
-		wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
-			   "Query element");
-		return;
-	}
-
-	oui = WPA_GET_BE24(pos);
-	pos += 3;
-	if (oui != OUI_WFA) {
-		wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
-			   oui);
-		return;
-	}
-
-#ifdef CONFIG_P2P
-	if (*pos == P2P_OUI_TYPE) {
-		/*
-		 * This is for P2P SD and will be taken care of by the P2P
-		 * implementation. This query needs to be ignored in the generic
-		 * GAS server to avoid duplicated response.
-		 */
-		wpa_printf(MSG_DEBUG,
-			   "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server",
-			   *pos);
-		qi->p2p_sd = 1;
-		return;
-	}
-#endif /* CONFIG_P2P */
-
-	if (*pos != HS20_ANQP_OUI_TYPE) {
-		wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
-			   *pos);
-		return;
-	}
-	pos++;
-
 	if (end - pos <= 1)
 		return;
 
@@ -1224,6 +1225,115 @@
 #endif /* CONFIG_HS20 */
 
 
+#ifdef CONFIG_P2P
+static void rx_anqp_vendor_specific_p2p(struct hostapd_data *hapd,
+					struct anqp_query_info *qi)
+{
+	/*
+	 * This is for P2P SD and will be taken care of by the P2P
+	 * implementation. This query needs to be ignored in the generic
+	 * GAS server to avoid duplicated response.
+	 */
+	wpa_printf(MSG_DEBUG,
+		   "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server",
+		   P2P_OUI_TYPE);
+	qi->p2p_sd = 1;
+	return;
+}
+#endif /* CONFIG_P2P */
+
+
+#ifdef CONFIG_MBO
+
+static void rx_anqp_mbo_query_list(struct hostapd_data *hapd, u8 subtype,
+				  struct anqp_query_info *qi)
+{
+	switch (subtype) {
+	case MBO_ANQP_SUBTYPE_CELL_CONN_PREF:
+		set_anqp_req(ANQP_REQ_MBO_CELL_DATA_CONN_PREF,
+			     "Cellular Data Connection Preference",
+			     hapd->conf->mbo_cell_data_conn_pref >= 0, qi);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO subtype %u",
+			   subtype);
+		break;
+	}
+}
+
+
+static void rx_anqp_vendor_specific_mbo(struct hostapd_data *hapd,
+					const u8 *pos, const u8 *end,
+					struct anqp_query_info *qi)
+{
+	u8 subtype;
+
+	if (end - pos < 1)
+		return;
+
+	subtype = *pos++;
+	switch (subtype) {
+	case MBO_ANQP_SUBTYPE_QUERY_LIST:
+		wpa_printf(MSG_DEBUG, "ANQP: MBO Query List");
+		while (pos < end) {
+			rx_anqp_mbo_query_list(hapd, *pos, qi);
+			pos++;
+		}
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO query subtype %u",
+			   subtype);
+		break;
+	}
+}
+
+#endif /* CONFIG_MBO */
+
+
+static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
+				    const u8 *pos, const u8 *end,
+				    struct anqp_query_info *qi)
+{
+	u32 oui;
+
+	if (end - pos < 4) {
+		wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
+			   "Query element");
+		return;
+	}
+
+	oui = WPA_GET_BE24(pos);
+	pos += 3;
+	if (oui != OUI_WFA) {
+		wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
+			   oui);
+		return;
+	}
+
+	switch (*pos) {
+#ifdef CONFIG_P2P
+	case P2P_OUI_TYPE:
+		rx_anqp_vendor_specific_p2p(hapd, qi);
+		break;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_HS20
+	case HS20_ANQP_OUI_TYPE:
+		rx_anqp_vendor_specific_hs20(hapd, pos + 1, end, qi);
+		break;
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+	case MBO_ANQP_OUI_TYPE:
+		rx_anqp_vendor_specific_mbo(hapd, pos + 1, end, qi);
+		break;
+#endif /* CONFIG_MBO */
+	default:
+		wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
+			   *pos);
+		break;
+	}
+}
+
+
 static void gas_serv_req_local_processing(struct hostapd_data *hapd,
 					  const u8 *sa, u8 dialog_token,
 					  struct anqp_query_info *qi, int prot,
@@ -1300,6 +1410,72 @@
 }
 
 
+#ifdef CONFIG_DPP
+static void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
+					const u8 *sa, u8 dialog_token,
+					int prot, struct wpabuf *buf)
+{
+	struct wpabuf *tx_buf;
+
+	if (wpabuf_len(buf) > hapd->conf->gas_frag_limit ||
+	    hapd->conf->gas_comeback_delay) {
+		struct gas_dialog_info *di;
+		u16 comeback_delay = 1;
+
+		if (hapd->conf->gas_comeback_delay) {
+			/* Testing - allow overriding of the delay value */
+			comeback_delay = hapd->conf->gas_comeback_delay;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Too long response to fit in initial response - use GAS comeback");
+		di = gas_dialog_create(hapd, sa, dialog_token);
+		if (!di) {
+			wpa_printf(MSG_INFO, "DPP: Could not create dialog for "
+				   MACSTR " (dialog token %u)",
+				   MAC2STR(sa), dialog_token);
+			wpabuf_free(buf);
+			tx_buf = gas_build_initial_resp(
+				dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
+				0, 10);
+			if (tx_buf)
+				gas_serv_write_dpp_adv_proto(tx_buf);
+		} else {
+			di->prot = prot;
+			di->sd_resp = buf;
+			di->sd_resp_pos = 0;
+			tx_buf = gas_build_initial_resp(
+				dialog_token, WLAN_STATUS_SUCCESS,
+				comeback_delay, 10);
+			if (tx_buf)
+				gas_serv_write_dpp_adv_proto(tx_buf);
+		}
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: GAS Initial response (no comeback)");
+		tx_buf = gas_build_initial_resp(
+			dialog_token, WLAN_STATUS_SUCCESS, 0,
+			10 + 2 + wpabuf_len(buf));
+		if (tx_buf) {
+			gas_serv_write_dpp_adv_proto(tx_buf);
+			wpabuf_put_le16(tx_buf, wpabuf_len(buf));
+			wpabuf_put_buf(tx_buf, buf);
+			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+		}
+		wpabuf_free(buf);
+	}
+	if (!tx_buf)
+		return;
+	if (prot)
+		convert_to_protected_dual(tx_buf);
+	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+				wpabuf_head(tx_buf),
+				wpabuf_len(tx_buf));
+	wpabuf_free(tx_buf);
+}
+#endif /* CONFIG_DPP */
+
+
 static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
 					const u8 *sa,
 					const u8 *data, size_t len, int prot,
@@ -1312,6 +1488,9 @@
 	u16 slen;
 	struct anqp_query_info qi;
 	const u8 *adv_proto;
+#ifdef CONFIG_DPP
+	int dpp = 0;
+#endif /* CONFIG_DPP */
 
 	if (len < 1 + 2)
 		return;
@@ -1339,6 +1518,15 @@
 	next = pos + slen;
 	pos++; /* skip QueryRespLenLimit and PAME-BI */
 
+#ifdef CONFIG_DPP
+	if (slen == 8 && *pos == WLAN_EID_VENDOR_SPECIFIC &&
+	    pos[1] == 5 && WPA_GET_BE24(&pos[2]) == OUI_WFA &&
+	    pos[5] == DPP_OUI_TYPE && pos[6] == 0x01) {
+		wpa_printf(MSG_DEBUG, "DPP: Configuration Request");
+		dpp = 1;
+	} else
+#endif /* CONFIG_DPP */
+
 	if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
 		struct wpabuf *buf;
 		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
@@ -1378,6 +1566,18 @@
 		return;
 	end = pos + slen;
 
+#ifdef CONFIG_DPP
+	if (dpp) {
+		struct wpabuf *msg;
+
+		msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen);
+		if (!msg)
+			return;
+		gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg);
+		return;
+	}
+#endif /* CONFIG_DPP */
+
 	/* ANQP Query Request */
 	while (pos < end) {
 		u16 info_id, elen;
@@ -1399,11 +1599,9 @@
 		case ANQP_QUERY_LIST:
 			rx_anqp_query_list(hapd, pos, pos + elen, &qi);
 			break;
-#ifdef CONFIG_HS20
 		case ANQP_VENDOR_SPECIFIC:
 			rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
 			break;
-#endif /* CONFIG_HS20 */
 		default:
 			wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
 				   "Request element %u", info_id);
@@ -1467,6 +1665,18 @@
 		gas_serv_dialog_clear(dialog);
 		return;
 	}
+#ifdef CONFIG_DPP
+	if (dialog->dpp) {
+		tx_buf = gas_build_comeback_resp(dialog_token,
+						 WLAN_STATUS_SUCCESS,
+						 dialog->sd_frag_id, more, 0,
+						 10 + frag_len);
+		if (tx_buf) {
+			gas_serv_write_dpp_adv_proto(tx_buf);
+			wpabuf_put_buf(tx_buf, buf);
+		}
+	} else
+#endif /* CONFIG_DPP */
 	tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
 						  WLAN_STATUS_SUCCESS,
 						  dialog->sd_frag_id,
@@ -1490,6 +1700,10 @@
 	} else {
 		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
 			"SD response sent");
+#ifdef CONFIG_DPP
+		if (dialog->dpp)
+			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+#endif /* CONFIG_DPP */
 		gas_serv_dialog_clear(dialog);
 		gas_serv_free_dialogs(hapd, sa);
 	}
diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h
index 9051e4f..3a30298 100644
--- a/src/ap/gas_serv.h
+++ b/src/ap/gas_serv.h
@@ -41,7 +41,7 @@
 #define ANQP_REQ_EMERGENCY_NAI \
 	(1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST))
 /*
- * First 16 Hotspot 2.0 vendor specific ANQP-elements can be included in the
+ * First 15 Hotspot 2.0 vendor specific ANQP-elements can be included in the
  * optimized bitmap.
  */
 #define ANQP_REQ_HS_CAPABILITY_LIST \
@@ -60,6 +60,9 @@
 	(0x10000 << HS20_STYPE_OSU_PROVIDERS_LIST)
 #define ANQP_REQ_ICON_REQUEST \
 	(0x10000 << HS20_STYPE_ICON_REQUEST)
+/* The first MBO ANQP-element can be included in the optimized bitmap. */
+#define ANQP_REQ_MBO_CELL_DATA_CONN_PREF \
+	(BIT(29) << MBO_ANQP_SUBTYPE_CELL_CONN_PREF)
 
 struct gas_dialog_info {
 	u8 valid;
@@ -68,6 +71,7 @@
 	size_t sd_resp_pos; /* Offset in sd_resp */
 	u8 sd_frag_id;
 	int prot; /* whether Protected Dual of Public Action frame is used */
+	int dpp; /* whether this is a DPP Config Response */
 };
 
 struct hostapd_data;
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index cf8a8cb..12911df 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -31,6 +31,8 @@
 #include "vlan_init.h"
 #include "wpa_auth.h"
 #include "wps_hostapd.h"
+#include "dpp_hostapd.h"
+#include "gas_query_ap.h"
 #include "hw_features.h"
 #include "wpa_auth_glue.h"
 #include "ap_drv_ops.h"
@@ -46,6 +48,7 @@
 #include "neighbor_db.h"
 #include "rrm.h"
 #include "fils_hlp.h"
+#include "acs.h"
 
 
 static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
@@ -301,6 +304,10 @@
 #endif /* CONFIG_NO_RADIUS */
 
 	hostapd_deinit_wps(hapd);
+#ifdef CONFIG_DPP
+	hostapd_dpp_deinit(hapd);
+	gas_query_ap_deinit(hapd->gas);
+#endif /* CONFIG_DPP */
 
 	authsrv_deinit(hapd);
 
@@ -394,8 +401,11 @@
 	hostapd_stop_setup_timers(iface);
 #endif /* NEED_AP_MLME */
 #endif /* CONFIG_IEEE80211N */
+	if (iface->current_mode)
+		acs_cleanup(iface);
 	hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
 	iface->hw_features = NULL;
+	iface->current_mode = NULL;
 	os_free(iface->current_rates);
 	iface->current_rates = NULL;
 	os_free(iface->basic_rates);
@@ -491,9 +501,12 @@
 			ret = -1;
 		}
 	}
-	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Deauthenticate all stations");
-	os_memset(addr, 0xff, ETH_ALEN);
-	hostapd_drv_sta_deauth(hapd, addr, reason);
+	if (hapd->conf && hapd->conf->broadcast_deauth) {
+		wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+			"Deauthenticate all stations");
+		os_memset(addr, 0xff, ETH_ALEN);
+		hostapd_drv_sta_deauth(hapd, addr, reason);
+	}
 	hostapd_free_stas(hapd);
 
 	return ret;
@@ -969,7 +982,7 @@
 #endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_MESH
-	if (hapd->iface->mconf == NULL)
+	if ((hapd->conf->mesh & MESH_ENABLED) && hapd->iface->mconf == NULL)
 		flush_old_stations = 0;
 #endif /* CONFIG_MESH */
 
@@ -1070,6 +1083,14 @@
 	if (hostapd_init_wps(hapd, conf))
 		return -1;
 
+#ifdef CONFIG_DPP
+	hapd->gas = gas_query_ap_init(hapd, hapd->msg_ctx);
+	if (!hapd->gas)
+		return -1;
+	if (hostapd_dpp_init(hapd))
+		return -1;
+#endif /* CONFIG_DPP */
+
 	if (authsrv_init(hapd) < 0)
 		return -1;
 
@@ -1157,7 +1178,7 @@
 	struct hostapd_tx_queue_params *p;
 
 #ifdef CONFIG_MESH
-	if (iface->mconf == NULL)
+	if ((hapd->conf->mesh & MESH_ENABLED) && iface->mconf == NULL)
 		return;
 #endif /* CONFIG_MESH */
 
@@ -1659,6 +1680,102 @@
 }
 
 
+#ifdef CONFIG_OWE
+
+static int hostapd_owe_iface_iter(struct hostapd_iface *iface, void *ctx)
+{
+	struct hostapd_data *hapd = ctx;
+	size_t i;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		struct hostapd_data *bss = iface->bss[i];
+
+		if (os_strcmp(hapd->conf->owe_transition_ifname,
+			      bss->conf->iface) != 0)
+			continue;
+
+		wpa_printf(MSG_DEBUG,
+			   "OWE: ifname=%s found transition mode ifname=%s BSSID "
+			   MACSTR " SSID %s",
+			   hapd->conf->iface, bss->conf->iface,
+			   MAC2STR(bss->own_addr),
+			   wpa_ssid_txt(bss->conf->ssid.ssid,
+					bss->conf->ssid.ssid_len));
+		if (!bss->conf->ssid.ssid_set || !bss->conf->ssid.ssid_len ||
+		    is_zero_ether_addr(bss->own_addr))
+			continue;
+
+		os_memcpy(hapd->conf->owe_transition_bssid, bss->own_addr,
+			  ETH_ALEN);
+		os_memcpy(hapd->conf->owe_transition_ssid,
+			  bss->conf->ssid.ssid, bss->conf->ssid.ssid_len);
+		hapd->conf->owe_transition_ssid_len = bss->conf->ssid.ssid_len;
+		wpa_printf(MSG_DEBUG,
+			   "OWE: Copied transition mode information");
+		return 1;
+	}
+
+	return 0;
+}
+
+
+int hostapd_owe_trans_get_info(struct hostapd_data *hapd)
+{
+	if (hapd->conf->owe_transition_ssid_len > 0 &&
+	    !is_zero_ether_addr(hapd->conf->owe_transition_bssid))
+		return 0;
+
+	/* Find transition mode SSID/BSSID information from a BSS operated by
+	 * this hostapd instance. */
+	if (!hapd->iface->interfaces ||
+	    !hapd->iface->interfaces->for_each_interface)
+		return hostapd_owe_iface_iter(hapd->iface, hapd);
+	else
+		return hapd->iface->interfaces->for_each_interface(
+			hapd->iface->interfaces, hostapd_owe_iface_iter, hapd);
+}
+
+
+static int hostapd_owe_iface_iter2(struct hostapd_iface *iface, void *ctx)
+{
+	size_t i;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		struct hostapd_data *bss = iface->bss[i];
+		int res;
+
+		if (!bss->conf->owe_transition_ifname[0])
+			continue;
+		res = hostapd_owe_trans_get_info(bss);
+		if (res == 0)
+			continue;
+		wpa_printf(MSG_DEBUG,
+			   "OWE: Matching transition mode interface enabled - update beacon data for %s",
+			   bss->conf->iface);
+		ieee802_11_set_beacon(bss);
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_OWE */
+
+
+static void hostapd_owe_update_trans(struct hostapd_iface *iface)
+{
+#ifdef CONFIG_OWE
+	/* Check whether the enabled BSS can complete OWE transition mode
+	 * configuration for any pending interface. */
+	if (!iface->interfaces ||
+	    !iface->interfaces->for_each_interface)
+		hostapd_owe_iface_iter2(iface, NULL);
+	else
+		iface->interfaces->for_each_interface(
+			iface->interfaces, hostapd_owe_iface_iter2, NULL);
+#endif /* CONFIG_OWE */
+}
+
+
 static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
 						 int err)
 {
@@ -1834,6 +1951,7 @@
 #endif /* CONFIG_FST */
 
 	hostapd_set_state(iface, HAPD_IFACE_ENABLED);
+	hostapd_owe_update_trans(iface);
 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED);
 	if (hapd->setup_complete_cb)
 		hapd->setup_complete_cb(hapd->setup_complete_cb_ctx);
@@ -2010,6 +2128,10 @@
 	dl_list_init(&hapd->ctrl_dst);
 	dl_list_init(&hapd->nr_db);
 	hapd->dhcp_sock = -1;
+#ifdef CONFIG_IEEE80211R_AP
+	dl_list_init(&hapd->l2_queue);
+	dl_list_init(&hapd->l2_oui_queue);
+#endif /* CONFIG_IEEE80211R_AP */
 
 	return hapd;
 }
@@ -2621,6 +2743,7 @@
 				return -1;
 			}
 		}
+		hostapd_owe_update_trans(hapd_iface);
 		return 0;
 	}
 
@@ -2838,6 +2961,9 @@
 	ieee802_1x_new_station(hapd, sta);
 	if (reassoc) {
 		if (sta->auth_alg != WLAN_AUTH_FT &&
+		    sta->auth_alg != WLAN_AUTH_FILS_SK &&
+		    sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
+		    sta->auth_alg != WLAN_AUTH_FILS_PK &&
 		    !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)))
 			wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH);
 	} else
@@ -2946,60 +3072,52 @@
 		goto free_ap_params;
 
 	ret = -1;
-	beacon->head = os_malloc(params.head_len);
+	beacon->head = os_memdup(params.head, params.head_len);
 	if (!beacon->head)
 		goto free_ap_extra_ies;
 
-	os_memcpy(beacon->head, params.head, params.head_len);
 	beacon->head_len = params.head_len;
 
-	beacon->tail = os_malloc(params.tail_len);
+	beacon->tail = os_memdup(params.tail, params.tail_len);
 	if (!beacon->tail)
 		goto free_beacon;
 
-	os_memcpy(beacon->tail, params.tail, params.tail_len);
 	beacon->tail_len = params.tail_len;
 
 	if (params.proberesp != NULL) {
-		beacon->probe_resp = os_malloc(params.proberesp_len);
+		beacon->probe_resp = os_memdup(params.proberesp,
+					       params.proberesp_len);
 		if (!beacon->probe_resp)
 			goto free_beacon;
 
-		os_memcpy(beacon->probe_resp, params.proberesp,
-			  params.proberesp_len);
 		beacon->probe_resp_len = params.proberesp_len;
 	}
 
 	/* copy the extra ies */
 	if (beacon_extra) {
-		beacon->beacon_ies = os_malloc(wpabuf_len(beacon_extra));
+		beacon->beacon_ies = os_memdup(beacon_extra->buf,
+					       wpabuf_len(beacon_extra));
 		if (!beacon->beacon_ies)
 			goto free_beacon;
 
-		os_memcpy(beacon->beacon_ies,
-			  beacon_extra->buf, wpabuf_len(beacon_extra));
 		beacon->beacon_ies_len = wpabuf_len(beacon_extra);
 	}
 
 	if (proberesp_extra) {
-		beacon->proberesp_ies =
-			os_malloc(wpabuf_len(proberesp_extra));
+		beacon->proberesp_ies = os_memdup(proberesp_extra->buf,
+						  wpabuf_len(proberesp_extra));
 		if (!beacon->proberesp_ies)
 			goto free_beacon;
 
-		os_memcpy(beacon->proberesp_ies, proberesp_extra->buf,
-			  wpabuf_len(proberesp_extra));
 		beacon->proberesp_ies_len = wpabuf_len(proberesp_extra);
 	}
 
 	if (assocresp_extra) {
-		beacon->assocresp_ies =
-			os_malloc(wpabuf_len(assocresp_extra));
+		beacon->assocresp_ies = os_memdup(assocresp_extra->buf,
+						  wpabuf_len(assocresp_extra));
 		if (!beacon->assocresp_ies)
 			goto free_beacon;
 
-		os_memcpy(beacon->assocresp_ies, assocresp_extra->buf,
-			  wpabuf_len(assocresp_extra));
 		beacon->assocresp_ies_len = wpabuf_len(assocresp_extra);
 	}
 
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 5ab623d..f77e6ec 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -53,6 +53,9 @@
 #ifndef CONFIG_NO_VLAN
 	struct dynamic_iface *vlan_priv;
 #endif /* CONFIG_NO_VLAN */
+#ifdef CONFIG_ETH_P_OUI
+	struct dl_list eth_p_oui; /* OUI Extended EtherType handlers */
+#endif /* CONFIG_ETH_P_OUI */
 	int eloop_initialized;
 };
 
@@ -185,6 +188,17 @@
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
 
 	struct l2_packet_data *l2;
+
+#ifdef CONFIG_IEEE80211R_AP
+	struct dl_list l2_queue;
+	struct dl_list l2_oui_queue;
+	struct eth_p_oui_ctx *oui_pull;
+	struct eth_p_oui_ctx *oui_resp;
+	struct eth_p_oui_ctx *oui_push;
+	struct eth_p_oui_ctx *oui_sreq;
+	struct eth_p_oui_ctx *oui_sresp;
+#endif /* CONFIG_IEEE80211R_AP */
+
 	struct wps_context *wps;
 
 	int beacon_set_done;
@@ -290,10 +304,27 @@
 	unsigned int ext_eapol_frame_io:1;
 
 	struct l2_packet_data *l2_test;
+
+	enum wpa_alg last_gtk_alg;
+	int last_gtk_key_idx;
+	u8 last_gtk[WPA_GTK_MAX_LEN];
+	size_t last_gtk_len;
+
+#ifdef CONFIG_IEEE80211W
+	enum wpa_alg last_igtk_alg;
+	int last_igtk_key_idx;
+	u8 last_igtk[WPA_IGTK_MAX_LEN];
+	size_t last_igtk_len;
+#endif /* CONFIG_IEEE80211W */
 #endif /* CONFIG_TESTING_OPTIONS */
 
 #ifdef CONFIG_MBO
 	unsigned int mbo_assoc_disallow;
+	/**
+	 * enable_oce - Enable OCE if it is enabled by user and device also
+	 *		supports OCE.
+	 */
+	u8 enable_oce;
 #endif /* CONFIG_MBO */
 
 	struct dl_list nr_db;
@@ -305,6 +336,29 @@
 	unsigned int range_req_active:1;
 
 	int dhcp_sock; /* UDP socket used with the DHCP server */
+
+#ifdef CONFIG_DPP
+	struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */
+	struct dl_list dpp_configurator; /* struct dpp_configurator */
+	int dpp_init_done;
+	struct dpp_authentication *dpp_auth;
+	u8 dpp_allowed_roles;
+	int dpp_qr_mutual;
+	int dpp_auth_ok_on_ack;
+	struct gas_query_ap *gas;
+	struct dpp_pkex *dpp_pkex;
+	struct dpp_bootstrap_info *dpp_pkex_bi;
+	char *dpp_pkex_code;
+	char *dpp_pkex_identifier;
+	char *dpp_pkex_auth_cmd;
+	char *dpp_configurator_params;
+#ifdef CONFIG_TESTING_OPTIONS
+	char *dpp_config_obj_override;
+	char *dpp_discovery_override;
+	char *dpp_groups_override;
+	unsigned int dpp_ignore_netaccesskey_mismatch:1;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_DPP */
 };
 
 
@@ -461,6 +515,8 @@
 
 	struct dl_list sta_seen; /* struct hostapd_sta_info */
 	unsigned int num_sta_seen;
+
+	u8 dfs_domain;
 };
 
 /* hostapd.c */
@@ -501,6 +557,7 @@
 				const struct hostapd_freq_params *freq_params);
 void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
 void hostapd_periodic_iface(struct hostapd_iface *iface);
+int hostapd_owe_trans_get_info(struct hostapd_data *hapd);
 
 /* utils.c */
 int hostapd_register_probereq_cb(struct hostapd_data *hapd,
@@ -512,6 +569,8 @@
 void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr);
 
 /* drv_callbacks.c (TODO: move to somewhere else?) */
+void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd,
+				      struct sta_info *sta);
 int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
 			const u8 *ie, size_t ielen, int reassoc);
 void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 2d6cef1..84e74ee 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -78,10 +78,12 @@
 	int i, j;
 	u16 num_modes, flags;
 	struct hostapd_hw_modes *modes;
+	u8 dfs_domain;
 
 	if (hostapd_drv_none(hapd))
 		return -1;
-	modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags);
+	modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags,
+					    &dfs_domain);
 	if (modes == NULL) {
 		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
@@ -91,6 +93,7 @@
 	}
 
 	iface->hw_flags = flags;
+	iface->dfs_domain = dfs_domain;
 
 	hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
 	iface->hw_features = modes;
@@ -331,6 +334,7 @@
 		iface->conf->secondary_channel = 0;
 		iface->conf->vht_oper_centr_freq_seg0_idx = 0;
 		iface->conf->vht_oper_centr_freq_seg1_idx = 0;
+		iface->conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT;
 		res = 1;
 		wpa_printf(MSG_INFO, "Fallback to 20 MHz");
 	}
@@ -722,14 +726,33 @@
 
 static int hostapd_is_usable_chans(struct hostapd_iface *iface)
 {
+	int secondary_chan;
+
 	if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1))
 		return 0;
 
 	if (!iface->conf->secondary_channel)
 		return 1;
 
-	return hostapd_is_usable_chan(iface, iface->conf->channel +
-				      iface->conf->secondary_channel * 4, 0);
+	if (!iface->conf->ht40_plus_minus_allowed)
+		return hostapd_is_usable_chan(
+			iface, iface->conf->channel +
+			iface->conf->secondary_channel * 4, 0);
+
+	/* Both HT40+ and HT40- are set, pick a valid secondary channel */
+	secondary_chan = iface->conf->channel + 4;
+	if (hostapd_is_usable_chan(iface, secondary_chan, 0)) {
+		iface->conf->secondary_channel = 1;
+		return 1;
+	}
+
+	secondary_chan = iface->conf->channel - 4;
+	if (hostapd_is_usable_chan(iface, secondary_chan, 0)) {
+		iface->conf->secondary_channel = -1;
+		return 1;
+	}
+
+	return 0;
 }
 
 
@@ -909,5 +932,19 @@
 
 int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
 {
-	return hw_get_chan(hapd->iface->current_mode, freq);
+	int i, channel;
+	struct hostapd_hw_modes *mode;
+
+	channel = hw_get_chan(hapd->iface->current_mode, freq);
+	if (channel)
+		return channel;
+	/* Check other available modes since the channel list for the current
+	 * mode did not include the specified frequency. */
+	for (i = 0; i < hapd->iface->num_hw_features; i++) {
+		mode = &hapd->iface->hw_features[i];
+		channel = hw_get_chan(mode, freq);
+		if (channel)
+			return channel;
+	}
+	return 0;
 }
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index c6234dc..65c4d88 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -14,6 +14,8 @@
 #include "utils/eloop.h"
 #include "crypto/crypto.h"
 #include "crypto/sha256.h"
+#include "crypto/sha384.h"
+#include "crypto/sha512.h"
 #include "crypto/random.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
@@ -46,8 +48,20 @@
 #include "rrm.h"
 #include "taxonomy.h"
 #include "fils_hlp.h"
+#include "dpp_hostapd.h"
+#include "gas_query_ap.h"
 
 
+#ifdef CONFIG_FILS
+static struct wpabuf *
+prepare_auth_resp_fils(struct hostapd_data *hapd,
+		       struct sta_info *sta, u16 *resp,
+		       struct rsn_pmksa_cache_entry *pmksa,
+		       struct wpabuf *erp_resp,
+		       const u8 *msk, size_t msk_len,
+		       int *is_pub);
+#endif /* CONFIG_FILS */
+
 u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
 {
 	u8 *pos = eid;
@@ -347,16 +361,19 @@
 					     struct sta_info *sta, int update)
 {
 	struct wpabuf *buf;
+	const char *password;
 
-	if (hapd->conf->ssid.wpa_passphrase == NULL) {
+	password = hapd->conf->sae_password;
+	if (!password)
+		password = hapd->conf->ssid.wpa_passphrase;
+	if (!password) {
 		wpa_printf(MSG_DEBUG, "SAE: No password available");
 		return NULL;
 	}
 
 	if (update &&
 	    sae_prepare_commit(hapd->own_addr, sta->addr,
-			       (u8 *) hapd->conf->ssid.wpa_passphrase,
-			       os_strlen(hapd->conf->ssid.wpa_passphrase),
+			       (u8 *) password, os_strlen(password),
 			       sta->sae) < 0) {
 		wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
 		return NULL;
@@ -774,6 +791,27 @@
 	int resp = WLAN_STATUS_SUCCESS;
 	struct wpabuf *data = NULL;
 
+#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;
+		send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+				auth_transaction, resp, pos, end - pos);
+		goto remove_sta;
+	}
+
+	if (hapd->conf->sae_commit_override && auth_transaction == 1) {
+		wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override");
+		send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+				auth_transaction, resp,
+				wpabuf_head(hapd->conf->sae_commit_override),
+				wpabuf_len(hapd->conf->sae_commit_override));
+		goto remove_sta;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 	if (!sta->sae) {
 		if (auth_transaction != 1 ||
 		    status_code != WLAN_STATUS_SUCCESS) {
@@ -999,6 +1037,8 @@
 #endif /* CONFIG_IEEE80211W */
 	if (res == WPA_INVALID_MDIE)
 		return WLAN_STATUS_INVALID_MDIE;
+	if (res == WPA_INVALID_PMKID)
+		return WLAN_STATUS_INVALID_PMKID;
 	if (res != WPA_IE_OK)
 		return WLAN_STATUS_INVALID_IE;
 	return WLAN_STATUS_SUCCESS;
@@ -1009,16 +1049,17 @@
 
 static void handle_auth_fils_finish(struct hostapd_data *hapd,
 				    struct sta_info *sta, u16 resp,
-				    struct rsn_pmksa_cache_entry *pmksa,
-				    struct wpabuf *erp_resp,
-				    const u8 *msk, size_t msk_len);
+				    struct wpabuf *data, int pub);
 
-static void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
-			    const struct ieee80211_mgmt *mgmt, size_t len,
-			    u16 auth_transaction, u16 status_code)
+void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
+		      const u8 *pos, size_t len, u16 auth_alg,
+		      u16 auth_transaction, u16 status_code,
+		      void (*cb)(struct hostapd_data *hapd,
+				 struct sta_info *sta, u16 resp,
+				 struct wpabuf *data, int pub))
 {
 	u16 resp = WLAN_STATUS_SUCCESS;
-	const u8 *pos, *end;
+	const u8 *end;
 	struct ieee802_11_elems elems;
 	int res;
 	struct wpa_ie_data rsn;
@@ -1027,14 +1068,83 @@
 	if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS)
 		return;
 
-	pos = mgmt->u.auth.variable;
-	end = ((const u8 *) mgmt) + len;
+	end = pos + len;
 
 	wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields",
 		    pos, end - pos);
 
-	/* TODO: Finite Cyclic Group when using PK or PFS */
-	/* TODO: Element when using PK or PFS */
+	/* TODO: FILS PK */
+#ifdef CONFIG_FILS_SK_PFS
+	if (auth_alg == WLAN_AUTH_FILS_SK_PFS) {
+		u16 group;
+		struct wpabuf *pub;
+		size_t elem_len;
+
+		/* Using FILS PFS */
+
+		/* Finite Cyclic Group */
+		if (end - pos < 2) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: No room for Finite Cyclic Group");
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		group = WPA_GET_LE16(pos);
+		pos += 2;
+		if (group != hapd->conf->fils_dh_group) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Unsupported Finite Cyclic Group: %u (expected %u)",
+				   group, hapd->conf->fils_dh_group);
+			resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+			goto fail;
+		}
+
+		crypto_ecdh_deinit(sta->fils_ecdh);
+		sta->fils_ecdh = crypto_ecdh_init(group);
+		if (!sta->fils_ecdh) {
+			wpa_printf(MSG_INFO,
+				   "FILS: Could not initialize ECDH with group %d",
+				   group);
+			resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+			goto fail;
+		}
+
+		pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1);
+		if (!pub) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Failed to derive ECDH public key");
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		elem_len = wpabuf_len(pub);
+		wpabuf_free(pub);
+
+		/* Element */
+		if ((size_t) (end - pos) < elem_len) {
+			wpa_printf(MSG_DEBUG, "FILS: No room for Element");
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+
+		wpabuf_free(sta->fils_g_sta);
+		sta->fils_g_sta = wpabuf_alloc_copy(pos, elem_len);
+		wpabuf_clear_free(sta->fils_dh_ss);
+		sta->fils_dh_ss = crypto_ecdh_set_peerkey(sta->fils_ecdh, 1,
+							  pos, elem_len);
+		if (!sta->fils_dh_ss) {
+			wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed");
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", sta->fils_dh_ss);
+		pos += elem_len;
+	} else {
+		crypto_ecdh_deinit(sta->fils_ecdh);
+		sta->fils_ecdh = NULL;
+		wpabuf_clear_free(sta->fils_dh_ss);
+		sta->fils_dh_ss = NULL;
+	}
+#endif /* CONFIG_FILS_SK_PFS */
 
 	wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos);
 	if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) {
@@ -1066,14 +1176,11 @@
 
 	res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
 				  elems.rsn_ie - 2, elems.rsn_ie_len + 2,
-				  elems.mdie, elems.mdie_len);
+				  elems.mdie, elems.mdie_len, NULL, 0);
 	resp = wpa_res_to_status_code(res);
 	if (resp != WLAN_STATUS_SUCCESS)
 		goto fail;
 
-	/* TODO: MDE when using FILS+FT */
-	/* TODO: FTE when using FILS+FT */
-
 	if (!elems.fils_nonce) {
 		wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
 		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -1099,6 +1206,11 @@
 						   pmkid);
 			if (pmksa)
 				break;
+			pmksa = wpa_auth_pmksa_get_fils_cache_id(hapd->wpa_auth,
+								 sta->addr,
+								 pmkid);
+			if (pmksa)
+				break;
 			pmkid += PMKID_LEN;
 			num--;
 		}
@@ -1134,13 +1246,22 @@
 					ieee802_1x_alloc_eapol_sm(hapd, sta);
 			}
 			wpa_printf(MSG_DEBUG,
-				   "FILS: Forward EAP-Identity/Re-auth Start to authentication server");
+				   "FILS: Forward EAP-Initiate/Re-auth to authentication server");
 			ieee802_1x_encapsulate_radius(
 				hapd, sta, elems.fils_wrapped_data,
 				elems.fils_wrapped_data_len);
+			sta->fils_pending_cb = cb;
 			wpa_printf(MSG_DEBUG,
 				   "FILS: Will send Authentication frame once the response from authentication server is available");
 			sta->flags |= WLAN_STA_PENDING_FILS_ERP;
+			/* Calculate pending PMKID here so that we do not need
+			 * to maintain a copy of the EAP-Initiate/Reauth
+			 * message. */
+			if (fils_pmkid_erp(wpa_auth_sta_key_mgmt(sta->wpa_sm),
+					   elems.fils_wrapped_data,
+					   elems.fils_wrapped_data_len,
+					   sta->fils_erp_pmkid) == 0)
+				sta->fils_erp_pmkid_set = 1;
 			return;
 #else /* CONFIG_NO_RADIUS */
 			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -1150,15 +1271,30 @@
 	}
 
 fail:
-	handle_auth_fils_finish(hapd, sta, resp, pmksa, NULL, NULL, 0);
+	if (cb) {
+		struct wpabuf *data;
+		int pub = 0;
+
+		data = prepare_auth_resp_fils(hapd, sta, &resp, pmksa, NULL,
+					      NULL, 0, &pub);
+		if (!data) {
+			wpa_printf(MSG_DEBUG,
+				   "%s: prepare_auth_resp_fils() returned failure",
+				   __func__);
+		}
+
+		cb(hapd, sta, resp, data, pub);
+	}
 }
 
 
-static void handle_auth_fils_finish(struct hostapd_data *hapd,
-				    struct sta_info *sta, u16 resp,
-				    struct rsn_pmksa_cache_entry *pmksa,
-				    struct wpabuf *erp_resp,
-				    const u8 *msk, size_t msk_len)
+static struct wpabuf *
+prepare_auth_resp_fils(struct hostapd_data *hapd,
+		       struct sta_info *sta, u16 *resp,
+		       struct rsn_pmksa_cache_entry *pmksa,
+		       struct wpabuf *erp_resp,
+		       const u8 *msk, size_t msk_len,
+		       int *is_pub)
 {
 	u8 fils_nonce[FILS_NONCE_LEN];
 	size_t ielen;
@@ -1168,51 +1304,86 @@
 	const u8 *pmk = NULL;
 	size_t pmk_len = 0;
 	u8 pmk_buf[PMK_LEN_MAX];
+	struct wpabuf *pub = NULL;
 
-	if (resp != WLAN_STATUS_SUCCESS)
+	if (*resp != WLAN_STATUS_SUCCESS)
 		goto fail;
 
 	ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen);
 	if (!ie) {
-		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 		goto fail;
 	}
+
 	if (pmksa) {
 		/* Add PMKID of the selected PMKSA into RSNE */
 		ie_buf = os_malloc(ielen + 2 + 2 + PMKID_LEN);
 		if (!ie_buf) {
-			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 			goto fail;
 		}
+
 		os_memcpy(ie_buf, ie, ielen);
 		if (wpa_insert_pmkid(ie_buf, &ielen, pmksa->pmkid) < 0) {
-			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 			goto fail;
 		}
 		ie = ie_buf;
 	}
 
 	if (random_get_bytes(fils_nonce, FILS_NONCE_LEN) < 0) {
-		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 		goto fail;
 	}
 	wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce",
 		    fils_nonce, FILS_NONCE_LEN);
 
-	data = wpabuf_alloc(1000 + ielen);
+#ifdef CONFIG_FILS_SK_PFS
+	if (sta->fils_dh_ss && sta->fils_ecdh) {
+		pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1);
+		if (!pub) {
+			*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+	}
+#endif /* CONFIG_FILS_SK_PFS */
+
+	data = wpabuf_alloc(1000 + ielen + (pub ? wpabuf_len(pub) : 0));
 	if (!data) {
-		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 		goto fail;
 	}
 
-	/* TODO: Finite Cyclic Group when using PK or PFS */
-	/* TODO: Element when using PK or PFS */
+	/* TODO: FILS PK */
+#ifdef CONFIG_FILS_SK_PFS
+	if (pub) {
+		/* Finite Cyclic Group */
+		wpabuf_put_le16(data, hapd->conf->fils_dh_group);
+
+		/* Element */
+		wpabuf_put_buf(data, pub);
+	}
+#endif /* CONFIG_FILS_SK_PFS */
 
 	/* RSNE */
 	wpabuf_put_data(data, ie, ielen);
 
-	/* TODO: MDE when using FILS+FT */
-	/* TODO: FTE when using FILS+FT */
+	/* MDE when using FILS+FT (already included in ie,ielen with RSNE) */
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm))) {
+		/* FTE[R1KH-ID,R0KH-ID] when using FILS+FT */
+		int res;
+
+		res = wpa_auth_write_fte(hapd->wpa_auth, wpabuf_put(data, 0),
+					 wpabuf_tailroom(data));
+		if (res < 0) {
+			*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		wpabuf_put(data, res);
+	}
+#endif /* CONFIG_IEEE80211R_AP */
 
 	/* FILS Nonce */
 	wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
@@ -1238,14 +1409,41 @@
 
 		if (fils_rmsk_to_pmk(wpa_auth_sta_key_mgmt(sta->wpa_sm),
 				     msk, msk_len, sta->fils_snonce, fils_nonce,
-				     NULL, 0, pmk_buf, &pmk_len)) {
+				     sta->fils_dh_ss ?
+				     wpabuf_head(sta->fils_dh_ss) : NULL,
+				     sta->fils_dh_ss ?
+				     wpabuf_len(sta->fils_dh_ss) : 0,
+				     pmk_buf, &pmk_len)) {
 			wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK");
-			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 			wpabuf_free(data);
 			data = NULL;
 			goto fail;
 		}
 		pmk = pmk_buf;
+
+		/* Don't use DHss in PTK derivation if PMKSA caching is not
+		 * used. */
+		wpabuf_clear_free(sta->fils_dh_ss);
+		sta->fils_dh_ss = NULL;
+
+		if (sta->fils_erp_pmkid_set) {
+			/* TODO: get PMKLifetime from WPA parameters */
+			unsigned int dot11RSNAConfigPMKLifetime = 43200;
+
+			sta->fils_erp_pmkid_set = 0;
+			if (wpa_auth_pmksa_add2(
+				    hapd->wpa_auth, sta->addr,
+				    pmk, pmk_len,
+				    sta->fils_erp_pmkid,
+				    sta->session_timeout_set ?
+				    sta->session_timeout :
+				    dot11RSNAConfigPMKLifetime,
+				    wpa_auth_sta_key_mgmt(sta->wpa_sm)) < 0) {
+				wpa_printf(MSG_ERROR,
+					   "FILS: Failed to add PMKSA cache entry based on ERP");
+			}
+		}
 	} else if (pmksa) {
 		pmk = pmksa->pmk;
 		pmk_len = pmksa->pmk_len;
@@ -1253,23 +1451,50 @@
 
 	if (!pmk) {
 		wpa_printf(MSG_DEBUG, "FILS: No PMK available");
-		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 		wpabuf_free(data);
 		data = NULL;
 		goto fail;
 	}
 
 	if (fils_auth_pmk_to_ptk(sta->wpa_sm, pmk, pmk_len,
-				 sta->fils_snonce, fils_nonce) < 0) {
-		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				 sta->fils_snonce, fils_nonce,
+				 sta->fils_dh_ss ?
+				 wpabuf_head(sta->fils_dh_ss) : NULL,
+				 sta->fils_dh_ss ?
+				 wpabuf_len(sta->fils_dh_ss) : 0,
+				 sta->fils_g_sta, pub) < 0) {
+		*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 		wpabuf_free(data);
 		data = NULL;
 		goto fail;
 	}
 
 fail:
-	send_auth_reply(hapd, sta->addr, hapd->own_addr, WLAN_AUTH_FILS_SK, 2,
-			resp,
+	if (is_pub)
+		*is_pub = pub != NULL;
+	os_free(ie_buf);
+	wpabuf_free(pub);
+	wpabuf_clear_free(sta->fils_dh_ss);
+	sta->fils_dh_ss = NULL;
+#ifdef CONFIG_FILS_SK_PFS
+	crypto_ecdh_deinit(sta->fils_ecdh);
+	sta->fils_ecdh = NULL;
+#endif /* CONFIG_FILS_SK_PFS */
+	return data;
+}
+
+
+static void handle_auth_fils_finish(struct hostapd_data *hapd,
+				    struct sta_info *sta, u16 resp,
+				    struct wpabuf *data, int pub)
+{
+	u16 auth_alg;
+
+	auth_alg = (pub ||
+		    resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) ?
+		WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
+	send_auth_reply(hapd, sta->addr, hapd->own_addr, auth_alg, 2, resp,
 			data ? wpabuf_head(data) : (u8 *) "",
 			data ? wpabuf_len(data) : 0);
 	wpabuf_free(data);
@@ -1280,11 +1505,9 @@
 			       "authentication OK (FILS)");
 		sta->flags |= WLAN_STA_AUTH;
 		wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
-		sta->auth_alg = WLAN_AUTH_FILS_SK;
+		sta->auth_alg = pub ? WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
 		mlme_authenticate_indication(hapd, sta);
 	}
-
-	os_free(ie_buf);
 }
 
 
@@ -1293,10 +1516,23 @@
 				 struct wpabuf *erp_resp,
 				 const u8 *msk, size_t msk_len)
 {
+	struct wpabuf *data;
+	int pub = 0;
+	u16 resp;
+
 	sta->flags &= ~WLAN_STA_PENDING_FILS_ERP;
-	handle_auth_fils_finish(hapd, sta, success ? WLAN_STATUS_SUCCESS :
-				WLAN_STATUS_UNSPECIFIED_FAILURE, NULL,
-				erp_resp, msk, msk_len);
+
+	if (!sta->fils_pending_cb)
+		return;
+	resp = success ? WLAN_STATUS_SUCCESS : WLAN_STATUS_UNSPECIFIED_FAILURE;
+	data = prepare_auth_resp_fils(hapd, sta, &resp, NULL, erp_resp,
+				      msk, msk_len, &pub);
+	if (!data) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: prepare_auth_resp_fils() returned failure",
+			   __func__);
+	}
+	sta->fils_pending_cb(hapd, sta, resp, data, pub);
 }
 
 #endif /* CONFIG_FILS */
@@ -1467,6 +1703,9 @@
 #ifdef CONFIG_FILS
 	      (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
 	       auth_alg == WLAN_AUTH_FILS_SK) ||
+	      (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
+	       hapd->conf->fils_dh_group &&
+	       auth_alg == WLAN_AUTH_FILS_SK_PFS) ||
 #endif /* CONFIG_FILS */
 	      ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
 	       auth_alg == WLAN_AUTH_SHARED_KEY))) {
@@ -1733,8 +1972,11 @@
 #endif /* CONFIG_SAE */
 #ifdef CONFIG_FILS
 	case WLAN_AUTH_FILS_SK:
-		handle_auth_fils(hapd, sta, mgmt, len, auth_transaction,
-				 status_code);
+	case WLAN_AUTH_FILS_SK_PFS:
+		handle_auth_fils(hapd, sta, mgmt->u.auth.variable,
+				 len - IEEE80211_HDRLEN - sizeof(mgmt->u.auth),
+				 auth_alg, auth_transaction, status_code,
+				 handle_auth_fils_finish);
 		return;
 #endif /* CONFIG_FILS */
 	}
@@ -1888,6 +2130,173 @@
 }
 
 
+#ifdef CONFIG_OWE
+
+static int owe_group_supported(struct hostapd_data *hapd, u16 group)
+{
+	int i;
+	int *groups = hapd->conf->owe_groups;
+
+	if (group != 19 && group != 20 && group != 21)
+		return 0;
+
+	if (!groups)
+		return 1;
+
+	for (i = 0; groups[i] > 0; i++) {
+		if (groups[i] == group)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static u16 owe_process_assoc_req(struct hostapd_data *hapd,
+				 struct sta_info *sta, const u8 *owe_dh,
+				 u8 owe_dh_len)
+{
+	struct wpabuf *secret, *pub, *hkey;
+	int res;
+	u8 prk[SHA512_MAC_LEN], pmkid[SHA512_MAC_LEN];
+	const char *info = "OWE Key Generation";
+	const u8 *addr[2];
+	size_t len[2];
+	u16 group;
+	size_t hash_len, prime_len;
+
+	if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
+		wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching");
+		return WLAN_STATUS_SUCCESS;
+	}
+
+	group = WPA_GET_LE16(owe_dh);
+	if (!owe_group_supported(hapd, group)) {
+		wpa_printf(MSG_DEBUG, "OWE: Unsupported DH group %u", group);
+		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+	}
+	if (group == 19)
+		prime_len = 32;
+	else if (group == 20)
+		prime_len = 48;
+	else if (group == 21)
+		prime_len = 66;
+	else
+		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+
+	crypto_ecdh_deinit(sta->owe_ecdh);
+	sta->owe_ecdh = crypto_ecdh_init(group);
+	if (!sta->owe_ecdh)
+		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+	sta->owe_group = group;
+
+	secret = crypto_ecdh_set_peerkey(sta->owe_ecdh, 0, owe_dh + 2,
+					 owe_dh_len - 2);
+	secret = wpabuf_zeropad(secret, prime_len);
+	if (!secret) {
+		wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret);
+
+	/* prk = HKDF-extract(C | A | group, z) */
+
+	pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
+	if (!pub) {
+		wpabuf_clear_free(secret);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	/* PMKID = Truncate-128(Hash(C | A)) */
+	addr[0] = owe_dh + 2;
+	len[0] = owe_dh_len - 2;
+	addr[1] = wpabuf_head(pub);
+	len[1] = wpabuf_len(pub);
+	if (group == 19) {
+		res = sha256_vector(2, addr, len, pmkid);
+		hash_len = SHA256_MAC_LEN;
+	} else if (group == 20) {
+		res = sha384_vector(2, addr, len, pmkid);
+		hash_len = SHA384_MAC_LEN;
+	} else if (group == 21) {
+		res = sha512_vector(2, addr, len, pmkid);
+		hash_len = SHA512_MAC_LEN;
+	} else {
+		wpabuf_free(pub);
+		wpabuf_clear_free(secret);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	pub = wpabuf_zeropad(pub, prime_len);
+	if (res < 0 || !pub) {
+		wpabuf_free(pub);
+		wpabuf_clear_free(secret);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	hkey = wpabuf_alloc(owe_dh_len - 2 + wpabuf_len(pub) + 2);
+	if (!hkey) {
+		wpabuf_free(pub);
+		wpabuf_clear_free(secret);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	wpabuf_put_data(hkey, owe_dh + 2, owe_dh_len - 2); /* C */
+	wpabuf_put_buf(hkey, pub); /* A */
+	wpabuf_free(pub);
+	wpabuf_put_le16(hkey, group); /* group */
+	if (group == 19)
+		res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey),
+				  wpabuf_head(secret), wpabuf_len(secret), prk);
+	else if (group == 20)
+		res = hmac_sha384(wpabuf_head(hkey), wpabuf_len(hkey),
+				  wpabuf_head(secret), wpabuf_len(secret), prk);
+	else if (group == 21)
+		res = hmac_sha512(wpabuf_head(hkey), wpabuf_len(hkey),
+				  wpabuf_head(secret), wpabuf_len(secret), prk);
+	wpabuf_clear_free(hkey);
+	wpabuf_clear_free(secret);
+	if (res < 0)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, hash_len);
+
+	/* PMK = HKDF-expand(prk, "OWE Key Generation", n) */
+
+	os_free(sta->owe_pmk);
+	sta->owe_pmk = os_malloc(hash_len);
+	if (!sta->owe_pmk) {
+		os_memset(prk, 0, SHA512_MAC_LEN);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (group == 19)
+		res = hmac_sha256_kdf(prk, hash_len, NULL, (const u8 *) info,
+				      os_strlen(info), sta->owe_pmk, hash_len);
+	else if (group == 20)
+		res = hmac_sha384_kdf(prk, hash_len, NULL, (const u8 *) info,
+				      os_strlen(info), sta->owe_pmk, hash_len);
+	else if (group == 21)
+		res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info,
+				      os_strlen(info), sta->owe_pmk, hash_len);
+	os_memset(prk, 0, SHA512_MAC_LEN);
+	if (res < 0) {
+		os_free(sta->owe_pmk);
+		sta->owe_pmk = NULL;
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	sta->owe_pmk_len = hash_len;
+
+	wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sta->owe_pmk, sta->owe_pmk_len);
+	wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN);
+	wpa_auth_pmksa_add2(hapd->wpa_auth, sta->addr, sta->owe_pmk,
+			    sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE);
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+#endif /* CONFIG_OWE */
+
+
 static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 			   const u8 *ies, size_t ies_len, int reassoc)
 {
@@ -2029,7 +2438,8 @@
 		}
 		res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
 					  wpa_ie, wpa_ie_len,
-					  elems.mdie, elems.mdie_len);
+					  elems.mdie, elems.mdie_len,
+					  elems.owe_dh, elems.owe_dh_len);
 		resp = wpa_res_to_status_code(res);
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
@@ -2099,6 +2509,17 @@
 		}
 #endif /* CONFIG_SAE */
 
+#ifdef CONFIG_OWE
+		if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+		    wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
+		    elems.owe_dh) {
+			resp = owe_process_assoc_req(hapd, sta, elems.owe_dh,
+						     elems.owe_dh_len);
+			if (resp != WLAN_STATUS_SUCCESS)
+				return resp;
+		}
+#endif /* CONFIG_OWE */
+
 #ifdef CONFIG_IEEE80211N
 		if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) &&
 		    wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) {
@@ -2224,7 +2645,8 @@
 	 */
 	if (!sta->added_unassoc &&
 	    (!(sta->flags & WLAN_STA_AUTHORIZED) ||
-	     !wpa_auth_sta_ft_tk_already_set(sta->wpa_sm))) {
+	     (!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) &&
+	      !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)))) {
 		hostapd_drv_sta_remove(hapd, sta->addr);
 		wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED);
 		set = 0;
@@ -2287,6 +2709,10 @@
 	if (sta && sta->fils_hlp_resp)
 		buflen += wpabuf_len(sta->fils_hlp_resp);
 #endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+	if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
+		buflen += 150;
+#endif /* CONFIG_OWE */
 	buf = os_zalloc(buflen);
 	if (!buf) {
 		res = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -2324,6 +2750,13 @@
 	}
 #endif /* CONFIG_IEEE80211R_AP */
 
+#ifdef CONFIG_OWE
+	if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
+		p = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, p,
+						  buf + buflen - p,
+						  ies, ies_len);
+#endif /* CONFIG_OWE */
+
 #ifdef CONFIG_IEEE80211W
 	if (sta && status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY)
 		p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
@@ -2463,6 +2896,30 @@
 	}
 #endif /* CONFIG_FILS */
 
+#ifdef CONFIG_OWE
+	if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+	    sta && sta->owe_ecdh &&
+	    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));
@@ -2499,14 +2956,11 @@
 	sta->hlp_dhcp_discover = NULL;
 
 	/*
-	 * Remove the station in case tranmission of a success response fails
-	 * (the STA was added associated to the driver) or if the station was
-	 * previously added unassociated.
+	 * Remove the station in case transmission of a success response fails.
+	 * At this point the station was already added associated to the driver.
 	 */
-	if (reply_res != WLAN_STATUS_SUCCESS || sta->added_unassoc) {
+	if (reply_res != WLAN_STATUS_SUCCESS)
 		hostapd_drv_sta_remove(hapd, sta->addr);
-		sta->added_unassoc = 0;
-	}
 }
 
 
@@ -2518,7 +2972,10 @@
 	wpa_printf(MSG_DEBUG,
 		   "FILS: HLP response timeout - continue with association response for "
 		   MACSTR, MAC2STR(sta->addr));
-	fils_hlp_finish_assoc(hapd, sta);
+	if (sta->fils_drv_assoc_finish)
+		hostapd_notify_assoc_fils_finish(hapd, sta);
+	else
+		fils_hlp_finish_assoc(hapd, sta);
 }
 
 #endif /* CONFIG_FILS */
@@ -2720,12 +3177,11 @@
 		/* The end of the payload is encrypted. Need to decrypt it
 		 * before parsing. */
 
-		tmp = os_malloc(left);
+		tmp = os_memdup(pos, left);
 		if (!tmp) {
 			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 			goto fail;
 		}
-		os_memcpy(tmp, pos, left);
 
 		left = fils_decrypt_assoc(sta->wpa_sm, sta->fils_session, mgmt,
 					  len, tmp, left);
@@ -2873,6 +3329,7 @@
 		sta->fils_pending_assoc_req = tmp;
 		sta->fils_pending_assoc_req_len = left;
 		sta->fils_pending_assoc_is_reassoc = reassoc;
+		sta->fils_drv_assoc_finish = 0;
 		wpa_printf(MSG_DEBUG,
 			   "FILS: Waiting for HLP processing before sending (Re)Association Response frame to "
 			   MACSTR, MAC2STR(sta->addr));
@@ -3124,11 +3581,11 @@
 	case WLAN_ACTION_SA_QUERY:
 		return hostapd_sa_query_action(hapd, mgmt, len);
 #endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
 	case WLAN_ACTION_WNM:
 		ieee802_11_rx_wnm_action_ap(hapd, mgmt, len);
 		return 1;
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
 #ifdef CONFIG_FST
 	case WLAN_ACTION_FST:
 		if (hapd->iface->fst)
@@ -3148,8 +3605,40 @@
 				   "HT20/40 coex mgmt frame received from STA "
 				   MACSTR, MAC2STR(mgmt->sa));
 			hostapd_2040_coex_action(hapd, mgmt, len);
+			return 1;
 		}
 #endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_DPP
+		if (len >= IEEE80211_HDRLEN + 6 &&
+		    mgmt->u.action.u.vs_public_action.action ==
+		    WLAN_PA_VENDOR_SPECIFIC &&
+		    WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
+		    OUI_WFA &&
+		    mgmt->u.action.u.vs_public_action.variable[0] ==
+		    DPP_OUI_TYPE) {
+			const u8 *pos, *end;
+
+			pos = mgmt->u.action.u.vs_public_action.oui;
+			end = ((const u8 *) mgmt) + len;
+			hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos,
+					      hapd->iface->freq);
+			return 1;
+		}
+		if (len >= IEEE80211_HDRLEN + 2 &&
+		    (mgmt->u.action.u.public_action.action ==
+		     WLAN_PA_GAS_INITIAL_RESP ||
+		     mgmt->u.action.u.public_action.action ==
+		     WLAN_PA_GAS_COMEBACK_RESP)) {
+			const u8 *pos, *end;
+
+			pos = &mgmt->u.action.u.public_action.action;
+			end = ((const u8 *) mgmt) + len;
+			gas_query_ap_rx(hapd->gas, mgmt->sa,
+					mgmt->u.action.category,
+					pos, end - pos, hapd->iface->freq);
+			return 1;
+		}
+#endif /* CONFIG_DPP */
 		if (hapd->public_action_cb) {
 			hapd->public_action_cb(hapd->public_action_cb_ctx,
 					       (u8 *) mgmt, len,
@@ -3193,10 +3682,9 @@
 		 */
 		wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action "
 			   "frame back to sender");
-		resp = os_malloc(len);
+		resp = os_memdup(mgmt, len);
 		if (resp == NULL)
 			return 0;
-		os_memcpy(resp, mgmt, len);
 		os_memcpy(resp->da, resp->sa, ETH_ALEN);
 		os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
 		os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
@@ -3266,7 +3754,9 @@
 		return 1;
 	}
 
-	if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
+	if ((!is_broadcast_ether_addr(mgmt->da) ||
+	     stype != WLAN_FC_STYPE_ACTION) &&
+	    os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
 		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
 			       "MGMT: DA=" MACSTR " not our address",
@@ -3605,6 +4095,36 @@
 
 	if (is_multicast_ether_addr(mgmt->da))
 		return;
+#ifdef CONFIG_DPP
+	if (len >= IEEE80211_HDRLEN + 6 &&
+	    mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
+	    mgmt->u.action.u.vs_public_action.action ==
+	    WLAN_PA_VENDOR_SPECIFIC &&
+	    WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
+	    OUI_WFA &&
+	    mgmt->u.action.u.vs_public_action.variable[0] ==
+	    DPP_OUI_TYPE) {
+		const u8 *pos, *end;
+
+		pos = &mgmt->u.action.u.vs_public_action.variable[1];
+		end = ((const u8 *) mgmt) + len;
+		hostapd_dpp_tx_status(hapd, mgmt->da, pos, end - pos, ok);
+		return;
+	}
+	if (len >= IEEE80211_HDRLEN + 2 &&
+	    mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
+	    (mgmt->u.action.u.public_action.action ==
+	     WLAN_PA_GAS_INITIAL_REQ ||
+	     mgmt->u.action.u.public_action.action ==
+	     WLAN_PA_GAS_COMEBACK_REQ)) {
+		const u8 *pos, *end;
+
+		pos = mgmt->u.action.u.public_action.variable;
+		end = ((const u8 *) mgmt) + len;
+		gas_query_ap_tx_status(hapd->gas, mgmt->da, pos, end - pos, ok);
+		return;
+	}
+#endif /* CONFIG_DPP */
 	sta = ap_get_sta(hapd, mgmt->da);
 	if (!sta) {
 		wpa_printf(MSG_DEBUG, "handle_action_cb: STA " MACSTR
@@ -3642,8 +4162,16 @@
 
 #ifdef CONFIG_TESTING_OPTIONS
 	if (hapd->ext_mgmt_frame_handling) {
-		wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-TX-STATUS stype=%u ok=%d",
-			stype, ok);
+		size_t hex_len = 2 * len + 1;
+		char *hex = os_malloc(hex_len);
+
+		if (hex) {
+			wpa_snprintf_hex(hex, hex_len, buf, len);
+			wpa_msg(hapd->msg_ctx, MSG_INFO,
+				"MGMT-TX-STATUS stype=%u ok=%d buf=%s",
+				stype, ok, hex);
+			os_free(hex);
+		}
 		return;
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index ce3abcb..3b381b4 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -55,8 +55,8 @@
 u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid);
-u8 * hostapd_eid_vendor_he_capab(struct hostapd_data *hapd, u8 *eid);
-u8 * hostapd_eid_vendor_he_operation(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid);
 
 int hostapd_ht_operation_update(struct hostapd_iface *iface);
 void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
@@ -144,5 +144,11 @@
 				 const u8 *msk, size_t msk_len);
 void fils_hlp_timeout(void *eloop_ctx, void *eloop_data);
 void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta);
+void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
+		      const u8 *pos, size_t len, u16 auth_alg,
+		      u16 auth_transaction, u16 status_code,
+		      void (*cb)(struct hostapd_data *hapd,
+				 struct sta_info *sta,
+				 u16 resp, struct wpabuf *data, int pub));
 
 #endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index 1e0358c..3308398 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -327,14 +327,13 @@
 			return HOSTAPD_ACL_REJECT;
 		}
 
-		query->auth_msg = os_malloc(len);
+		query->auth_msg = os_memdup(msg, len);
 		if (query->auth_msg == NULL) {
 			wpa_printf(MSG_ERROR, "Failed to allocate memory for "
 				   "auth frame.");
 			hostapd_acl_query_free(query);
 			return HOSTAPD_ACL_REJECT;
 		}
-		os_memcpy(query->auth_msg, msg, len);
 		query->auth_msg_len = len;
 		query->next = hapd->acl_queries;
 		hapd->acl_queries = query;
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
index 7d6a84f..1a8d469 100644
--- a/src/ap/ieee802_11_he.c
+++ b/src/ap/ieee802_11_he.c
@@ -10,14 +10,13 @@
 
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
-#include "common/qca-vendor.h"
 #include "hostapd.h"
 #include "ap_config.h"
 #include "beacon.h"
 #include "ieee802_11.h"
 #include "dfs.h"
 
-u8 * hostapd_eid_vendor_he_capab(struct hostapd_data *hapd, u8 *eid)
+u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid)
 {
 	struct ieee80211_he_capabilities *cap;
 	u8 *pos = eid;
@@ -25,17 +24,10 @@
 	if (!hapd->iface->current_mode)
 		return eid;
 
-	/* For now, use a vendor specific element since the P802.11ax draft is
-	 * still subject to changes and the contents of this element may change.
-	 * This can be replaced with the actual element once P802.11ax is
-	 * finalized. */
-	/* Vendor HE Capabilities element */
-	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
-	*pos++ = 4 /* The Vendor OUI, subtype */ +
-		sizeof(struct ieee80211_he_capabilities);
+	*pos++ = WLAN_EID_EXTENSION;
+	*pos++ = 1 + sizeof(struct ieee80211_he_capabilities);
+	*pos++ = WLAN_EID_EXT_HE_CAPABILITIES;
 
-	WPA_PUT_BE32(pos, (OUI_QCA << 8) | QCA_VENDOR_ELEM_HE_CAPAB);
-	pos += 4;
 	cap = (struct ieee80211_he_capabilities *) pos;
 	os_memset(cap, 0, sizeof(*cap));
 
@@ -57,7 +49,7 @@
 }
 
 
-u8 * hostapd_eid_vendor_he_operation(struct hostapd_data *hapd, u8 *eid)
+u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid)
 {
 	struct ieee80211_he_operation *oper;
 	u8 *pos = eid;
@@ -65,17 +57,10 @@
 	if (!hapd->iface->current_mode)
 		return eid;
 
-	/* For now, use a vendor specific element since the P802.11ax draft is
-	 * still subject to changes and the contents of this element may change.
-	 * This can be replaced with the actual element once P802.11ax is
-	 * finalized. */
-	/* Vendor HE Operation element */
-	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
-	*pos++ = 4 /* The Vendor OUI, subtype */ +
-		sizeof(struct ieee80211_he_operation);
+	*pos++ = WLAN_EID_EXTENSION;
+	*pos++ = 1 + sizeof(struct ieee80211_he_operation);
+	*pos++ = WLAN_EID_EXT_HE_OPERATION;
 
-	WPA_PUT_BE32(pos, (OUI_QCA << 8) | QCA_VENDOR_ELEM_HE_OPER);
-	pos += 4;
 	oper = (struct ieee80211_he_operation *) pos;
 	os_memset(oper, 0, sizeof(*oper));
 
@@ -95,6 +80,8 @@
 			(hapd->iface->conf->he_op.he_rts_threshold <<
 			 HE_OPERATION_RTS_THRESHOLD_OFFSET);
 
+	/* TODO: conditional MaxBSSID Indicator subfield */
+
 	pos += sizeof(*oper);
 
 	return pos;
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index daacf7e..902f64f 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -186,9 +186,9 @@
 			*pos |= 0x08; /* Bit 19 - BSS Transition */
 		break;
 	case 3: /* Bits 24-31 */
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
 		*pos |= 0x02; /* Bit 25 - SSID List */
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
 		if (hapd->conf->time_advertisement == 2)
 			*pos |= 0x08; /* Bit 27 - UTC TSF Offset */
 		if (hapd->conf->interworking)
@@ -254,10 +254,10 @@
 	if (len < 9 &&
 	    (hapd->conf->ftm_initiator || hapd->conf->ftm_responder))
 		len = 9;
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
 	if (len < 4)
 		len = 4;
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
 #ifdef CONFIG_HS20
 	if (hapd->conf->hs20 && len < 6)
 		len = 6;
@@ -516,7 +516,7 @@
 {
 	u8 *pos = eid;
 
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
 	if (hapd->conf->ap_max_inactivity > 0) {
 		unsigned int val;
 		*pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD;
@@ -534,7 +534,7 @@
 		pos += 2;
 		*pos++ = 0x00; /* TODO: Protected Keep-Alive Required */
 	}
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
 
 	return pos;
 }
@@ -544,23 +544,38 @@
 
 u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len)
 {
-	u8 mbo[6], *mbo_pos = mbo;
+	u8 mbo[9], *mbo_pos = mbo;
 	u8 *pos = eid;
 
-	if (!hapd->conf->mbo_enabled)
+	if (!hapd->conf->mbo_enabled && !hapd->enable_oce)
 		return eid;
 
-	*mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND;
-	*mbo_pos++ = 1;
-	/* Not Cellular aware */
-	*mbo_pos++ = 0;
+	if (hapd->conf->mbo_enabled) {
+		*mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND;
+		*mbo_pos++ = 1;
+		/* Not Cellular aware */
+		*mbo_pos++ = 0;
+	}
 
-	if (hapd->mbo_assoc_disallow) {
+	if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) {
 		*mbo_pos++ = MBO_ATTR_ID_ASSOC_DISALLOW;
 		*mbo_pos++ = 1;
 		*mbo_pos++ = hapd->mbo_assoc_disallow;
 	}
 
+	if (hapd->enable_oce & (OCE_AP | OCE_STA_CFON)) {
+		u8 ctrl;
+
+		ctrl = OCE_RELEASE;
+		if ((hapd->enable_oce & (OCE_AP | OCE_STA_CFON)) ==
+		    OCE_STA_CFON)
+			ctrl |= OCE_IS_STA_CFON;
+
+		*mbo_pos++ = OCE_ATTR_ID_CAPA_IND;
+		*mbo_pos++ = 1;
+		*mbo_pos++ = ctrl;
+	}
+
 	pos += mbo_add_ie(pos, len, mbo, mbo_pos - mbo);
 
 	return pos;
@@ -569,14 +584,24 @@
 
 u8 hostapd_mbo_ie_len(struct hostapd_data *hapd)
 {
-	if (!hapd->conf->mbo_enabled)
+	u8 len;
+
+	if (!hapd->conf->mbo_enabled && !hapd->enable_oce)
 		return 0;
 
 	/*
 	 * MBO IE header (6) + Capability Indication attribute (3) +
 	 * Association Disallowed attribute (3) = 12
 	 */
-	return 6 + 3 + (hapd->mbo_assoc_disallow ? 3 : 0);
+	len = 6;
+	if (hapd->conf->mbo_enabled)
+		len += 3 + (hapd->mbo_assoc_disallow ? 3 : 0);
+
+	/* OCE capability indication attribute (3) */
+	if (hapd->enable_oce & (OCE_AP | OCE_STA_CFON))
+		len += 3;
+
+	return len;
 }
 
 #endif /* CONFIG_MBO */
@@ -630,7 +655,10 @@
 		fils_info |= BIT(8); /* HESSID Included */
 	/* FILS Shared Key Authentication without PFS Supported */
 	fils_info |= BIT(9);
-	/* TODO: B10: FILS Shared Key Authentication with PFS Supported */
+	if (hapd->conf->fils_dh_group) {
+		/* FILS Shared Key Authentication with PFS Supported */
+		fils_info |= BIT(10);
+	}
 	/* TODO: B11: FILS Public Key Authentication Supported */
 	/* B12..B15: Reserved */
 	WPA_PUT_LE16(pos, fils_info);
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index fccdc72..793d381 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -973,7 +973,9 @@
 	}
 
 	key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
-	if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) {
+	if (key_mgmt != -1 &&
+	    (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE ||
+	     key_mgmt == WPA_KEY_MGMT_DPP)) {
 		wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - "
 			   "STA is using PSK");
 		return;
@@ -1116,7 +1118,9 @@
 	}
 
 	key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
-	if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) {
+	if (key_mgmt != -1 &&
+	    (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE ||
+	     key_mgmt == WPA_KEY_MGMT_DPP)) {
 		wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - using PSK");
 		/*
 		 * Clear any possible EAPOL authenticator state to support
@@ -1420,11 +1424,10 @@
 			}
 		} while (class_len < 1);
 
-		nclass[nclass_count].data = os_malloc(class_len);
+		nclass[nclass_count].data = os_memdup(attr_class, class_len);
 		if (nclass[nclass_count].data == NULL)
 			break;
 
-		os_memcpy(nclass[nclass_count].data, attr_class, class_len);
 		nclass[nclass_count].len = class_len;
 		nclass_count++;
 	}
@@ -2072,11 +2075,10 @@
 	}
 
 	if (eap_user->password) {
-		user->password = os_malloc(eap_user->password_len);
+		user->password = os_memdup(eap_user->password,
+					   eap_user->password_len);
 		if (user->password == NULL)
 			goto out;
-		os_memcpy(user->password, eap_user->password,
-			  eap_user->password_len);
 		user->password_len = eap_user->password_len;
 		user->password_hash = eap_user->password_hash;
 	}
@@ -2228,6 +2230,7 @@
 	conf.erp_domain = hapd->conf->erp_domain;
 	conf.erp = hapd->conf->eap_server_erp;
 	conf.tls_session_lifetime = hapd->conf->tls_session_lifetime;
+	conf.tls_flags = hapd->conf->tls_flags;
 	conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key;
 	conf.eap_fast_a_id = hapd->conf->eap_fast_a_id;
 	conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len;
diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c
index 3c086bf..4d6a92e 100644
--- a/src/ap/ndisc_snoop.c
+++ b/src/ap/ndisc_snoop.c
@@ -182,4 +182,5 @@
 void ndisc_snoop_deinit(struct hostapd_data *hapd)
 {
 	l2_packet_deinit(hapd->sock_ndisc);
+	hapd->sock_ndisc = NULL;
 }
diff --git a/src/ap/peerkey_auth.c b/src/ap/peerkey_auth.c
deleted file mode 100644
index 93e775b..0000000
--- a/src/ap/peerkey_auth.c
+++ /dev/null
@@ -1,355 +0,0 @@
-/*
- * hostapd - PeerKey for Direct Link Setup (DLS)
- * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "utils/includes.h"
-
-#include "utils/common.h"
-#include "utils/eloop.h"
-#include "crypto/sha1.h"
-#include "crypto/sha256.h"
-#include "crypto/random.h"
-#include "wpa_auth.h"
-#include "wpa_auth_i.h"
-#include "wpa_auth_ie.h"
-
-#ifdef CONFIG_PEERKEY
-
-struct wpa_stsl_search {
-	const u8 *addr;
-	struct wpa_state_machine *sm;
-};
-
-
-static int wpa_stsl_select_sta(struct wpa_state_machine *sm, void *ctx)
-{
-	struct wpa_stsl_search *search = ctx;
-	if (os_memcmp(search->addr, sm->addr, ETH_ALEN) == 0) {
-		search->sm = sm;
-		return 1;
-	}
-	return 0;
-}
-
-
-static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth,
-			       struct wpa_state_machine *sm, const u8 *peer,
-			       u16 mui, u16 error_type)
-{
-	u8 kde[2 + RSN_SELECTOR_LEN + ETH_ALEN +
-	       2 + RSN_SELECTOR_LEN + sizeof(struct rsn_error_kde)];
-	u8 *pos;
-	struct rsn_error_kde error;
-
-	wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
-			"Sending SMK Error");
-
-	pos = kde;
-
-	if (peer) {
-		pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN,
-				  NULL, 0);
-	}
-
-	error.mui = host_to_be16(mui);
-	error.error_type = host_to_be16(error_type);
-	pos = wpa_add_kde(pos, RSN_KEY_DATA_ERROR,
-			  (u8 *) &error, sizeof(error), NULL, 0);
-
-	__wpa_send_eapol(wpa_auth, sm,
-			 WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
-			 WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_ERROR,
-			 NULL, NULL, kde, pos - kde, 0, 0, 0);
-}
-
-
-void wpa_smk_m1(struct wpa_authenticator *wpa_auth,
-		struct wpa_state_machine *sm, struct wpa_eapol_key *key,
-		const u8 *key_data, size_t key_data_len)
-{
-	struct wpa_eapol_ie_parse kde;
-	struct wpa_stsl_search search;
-	u8 *buf, *pos;
-	size_t buf_len;
-
-	if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
-		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1");
-		return;
-	}
-
-	if (kde.rsn_ie == NULL || kde.mac_addr == NULL ||
-	    kde.mac_addr_len < ETH_ALEN) {
-		wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in "
-			   "SMK M1");
-		return;
-	}
-
-	/* Initiator = sm->addr; Peer = kde.mac_addr */
-
-	search.addr = kde.mac_addr;
-	search.sm = NULL;
-	if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) ==
-	    0 || search.sm == NULL) {
-		wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR
-			   " aborted - STA not associated anymore",
-			   MAC2STR(kde.mac_addr));
-		wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK,
-				   STK_ERR_STA_NR);
-		return;
-	}
-
-	buf_len = kde.rsn_ie_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN;
-	buf = os_malloc(buf_len);
-	if (buf == NULL)
-		return;
-	/* Initiator RSN IE */
-	os_memcpy(buf, kde.rsn_ie, kde.rsn_ie_len);
-	pos = buf + kde.rsn_ie_len;
-	/* Initiator MAC Address */
-	pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->addr, ETH_ALEN,
-			  NULL, 0);
-
-	/* SMK M2:
-	 * EAPOL-Key(S=1, M=1, A=1, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce,
-	 *           MIC=MIC, DataKDs=(RSNIE_I, MAC_I KDE)
-	 */
-
-	wpa_auth_logger(wpa_auth, search.sm->addr, LOGGER_DEBUG,
-			"Sending SMK M2");
-
-	__wpa_send_eapol(wpa_auth, search.sm,
-			 WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
-			 WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE,
-			 NULL, key->key_nonce, buf, pos - buf, 0, 0, 0);
-
-	os_free(buf);
-}
-
-
-static void wpa_send_smk_m4(struct wpa_authenticator *wpa_auth,
-			    struct wpa_state_machine *sm,
-			    struct wpa_eapol_key *key,
-			    struct wpa_eapol_ie_parse *kde,
-			    const u8 *smk)
-{
-	u8 *buf, *pos;
-	size_t buf_len;
-	u32 lifetime;
-
-	/* SMK M4:
-	 * EAPOL-Key(S=1, M=1, A=0, I=1, K=0, SM=1, KeyRSC=0, Nonce=PNonce,
-	 *           MIC=MIC, DataKDs=(MAC_I KDE, INonce KDE, SMK KDE,
-	 *           Lifetime KDE)
-	 */
-
-	buf_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN +
-		2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN +
-		2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN +
-		2 + RSN_SELECTOR_LEN + sizeof(lifetime);
-	pos = buf = os_malloc(buf_len);
-	if (buf == NULL)
-		return;
-
-	/* Initiator MAC Address */
-	pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, kde->mac_addr, ETH_ALEN,
-			  NULL, 0);
-
-	/* Initiator Nonce */
-	pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, kde->nonce, WPA_NONCE_LEN,
-			  NULL, 0);
-
-	/* SMK with PNonce */
-	pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN,
-			  key->key_nonce, WPA_NONCE_LEN);
-
-	/* Lifetime */
-	lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */
-	pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
-			  (u8 *) &lifetime, sizeof(lifetime), NULL, 0);
-
-	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
-			"Sending SMK M4");
-
-	__wpa_send_eapol(wpa_auth, sm,
-			 WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
-			 WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_SMK_MESSAGE,
-			 NULL, key->key_nonce, buf, pos - buf, 0, 1, 0);
-
-	os_free(buf);
-}
-
-
-static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth,
-			    struct wpa_state_machine *sm,
-			    struct wpa_eapol_key *key,
-			    struct wpa_eapol_ie_parse *kde,
-			    const u8 *smk, const u8 *peer)
-{
-	u8 *buf, *pos;
-	size_t buf_len;
-	u32 lifetime;
-
-	/* SMK M5:
-	 * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce,
-	 *           MIC=MIC, DataKDs=(RSNIE_P, MAC_P KDE, PNonce, SMK KDE,
-	 *                             Lifetime KDE))
-	 */
-
-	buf_len = kde->rsn_ie_len +
-		2 + RSN_SELECTOR_LEN + ETH_ALEN +
-		2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN +
-		2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN +
-		2 + RSN_SELECTOR_LEN + sizeof(lifetime);
-	pos = buf = os_malloc(buf_len);
-	if (buf == NULL)
-		return;
-
-	/* Peer RSN IE */
-	os_memcpy(pos, kde->rsn_ie, kde->rsn_ie_len);
-	pos += kde->rsn_ie_len;
-
-	/* Peer MAC Address */
-	pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0);
-
-	/* PNonce */
-	pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, key->key_nonce,
-			  WPA_NONCE_LEN, NULL, 0);
-
-	/* SMK and INonce */
-	pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN,
-			  kde->nonce, WPA_NONCE_LEN);
-
-	/* Lifetime */
-	lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */
-	pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
-			  (u8 *) &lifetime, sizeof(lifetime), NULL, 0);
-
-	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
-			"Sending SMK M5");
-
-	__wpa_send_eapol(wpa_auth, sm,
-			 WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
-			 WPA_KEY_INFO_SMK_MESSAGE,
-			 NULL, kde->nonce, buf, pos - buf, 0, 1, 0);
-
-	os_free(buf);
-}
-
-
-void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
-		struct wpa_state_machine *sm, struct wpa_eapol_key *key,
-		const u8 *key_data, size_t key_data_len)
-{
-	struct wpa_eapol_ie_parse kde;
-	struct wpa_stsl_search search;
-	u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos;
-
-	if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
-		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3");
-		return;
-	}
-
-	if (kde.rsn_ie == NULL ||
-	    kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN ||
-	    kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN) {
-		wpa_printf(MSG_INFO, "RSN: No RSN IE, MAC address KDE, or "
-			   "Nonce KDE in SMK M3");
-		return;
-	}
-
-	/* Peer = sm->addr; Initiator = kde.mac_addr;
-	 * Peer Nonce = key->key_nonce; Initiator Nonce = kde.nonce */
-
-	search.addr = kde.mac_addr;
-	search.sm = NULL;
-	if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) ==
-	    0 || search.sm == NULL) {
-		wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR
-			   " aborted - STA not associated anymore",
-			   MAC2STR(kde.mac_addr));
-		wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK,
-				   STK_ERR_STA_NR);
-		return;
-	}
-
-	if (random_get_bytes(smk, PMK_LEN)) {
-		wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK");
-		return;
-	}
-
-	/* SMK = PRF-256(Random number, "SMK Derivation",
-	 *               AA || Time || INonce || PNonce)
-	 */
-	os_memcpy(buf, wpa_auth->addr, ETH_ALEN);
-	pos = buf + ETH_ALEN;
-	wpa_get_ntp_timestamp(pos);
-	pos += 8;
-	os_memcpy(pos, kde.nonce, WPA_NONCE_LEN);
-	pos += WPA_NONCE_LEN;
-	os_memcpy(pos, key->key_nonce, WPA_NONCE_LEN);
-#ifdef CONFIG_IEEE80211W
-	sha256_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf),
-		   smk, PMK_LEN);
-#else /* CONFIG_IEEE80211W */
-	sha1_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf),
-		 smk, PMK_LEN);
-#endif /* CONFIG_IEEE80211W */
-
-	wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", smk, PMK_LEN);
-
-	wpa_send_smk_m4(wpa_auth, sm, key, &kde, smk);
-	wpa_send_smk_m5(wpa_auth, search.sm, key, &kde, smk, sm->addr);
-
-	/* Authenticator does not need SMK anymore and it is required to forget
-	 * it. */
-	os_memset(smk, 0, sizeof(*smk));
-}
-
-
-void wpa_smk_error(struct wpa_authenticator *wpa_auth,
-		   struct wpa_state_machine *sm,
-		   const u8 *key_data, size_t key_data_len)
-{
-	struct wpa_eapol_ie_parse kde;
-	struct wpa_stsl_search search;
-	struct rsn_error_kde error;
-	u16 mui, error_type;
-
-	if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
-		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error");
-		return;
-	}
-
-	if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN ||
-	    kde.error == NULL || kde.error_len < sizeof(error)) {
-		wpa_printf(MSG_INFO, "RSN: No MAC address or Error KDE in "
-			   "SMK Error");
-		return;
-	}
-
-	search.addr = kde.mac_addr;
-	search.sm = NULL;
-	if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) ==
-	    0 || search.sm == NULL) {
-		wpa_printf(MSG_DEBUG, "RSN: Peer STA " MACSTR " not "
-			   "associated for SMK Error message from " MACSTR,
-			   MAC2STR(kde.mac_addr), MAC2STR(sm->addr));
-		return;
-	}
-
-	os_memcpy(&error, kde.error, sizeof(error));
-	mui = be_to_host16(error.mui);
-	error_type = be_to_host16(error.error_type);
-	wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
-			 "STA reported SMK Error: Peer " MACSTR
-			 " MUI %d Error Type %d",
-			 MAC2STR(kde.mac_addr), mui, error_type);
-
-	wpa_smk_send_error(wpa_auth, search.sm, sm->addr, mui, error_type);
-}
-
-#endif /* CONFIG_PEERKEY */
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index bce5abf..15e2c49 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -338,8 +338,7 @@
 	else if (wpa_key_mgmt_suite_b(akmp))
 		rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
 	else
-		rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
-			  wpa_key_mgmt_sha256(akmp));
+		rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp);
 	os_get_reltime(&now);
 	entry->expiration = now.sec;
 	if (session_timeout > 0)
@@ -518,7 +517,7 @@
 		if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0)
 			continue;
 		rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid,
-			  wpa_key_mgmt_sha256(entry->akmp));
+			  entry->akmp);
 		if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0)
 			return entry;
 	}
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
index bd1b672..2ef2174 100644
--- a/src/ap/pmksa_cache_auth.h
+++ b/src/ap/pmksa_cache_auth.h
@@ -35,6 +35,7 @@
 };
 
 struct rsn_pmksa_cache;
+struct radius_das_attrs;
 
 struct rsn_pmksa_cache *
 pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index af8c754..b1fde3c 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / Station table
- * Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -17,6 +17,7 @@
 #include "radius/radius_client.h"
 #include "p2p/p2p.h"
 #include "fst/fst.h"
+#include "crypto/crypto.h"
 #include "hostapd.h"
 #include "accounting.h"
 #include "ieee802_1x.h"
@@ -344,8 +345,18 @@
 	wpabuf_free(sta->fils_hlp_resp);
 	wpabuf_free(sta->hlp_dhcp_discover);
 	eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+#ifdef CONFIG_FILS_SK_PFS
+	crypto_ecdh_deinit(sta->fils_ecdh);
+	wpabuf_clear_free(sta->fils_dh_ss);
+	wpabuf_free(sta->fils_g_sta);
+#endif /* CONFIG_FILS_SK_PFS */
 #endif /* CONFIG_FILS */
 
+#ifdef CONFIG_OWE
+	bin_clear_free(sta->owe_pmk, sta->owe_pmk_len);
+	crypto_ecdh_deinit(sta->owe_ecdh);
+#endif /* CONFIG_OWE */
+
 	os_free(sta);
 }
 
@@ -606,7 +617,7 @@
 
 static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx)
 {
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
 	struct hostapd_data *hapd = eloop_ctx;
 	struct sta_info *sta = timeout_ctx;
 
@@ -617,7 +628,7 @@
 
 	wnm_send_ess_disassoc_imminent(hapd, sta, sta->hs20_session_info_url,
 				       sta->hs20_disassoc_timer);
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
 }
 
 
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 6f55403..3fb60f6 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -1,6 +1,6 @@
 /*
  * hostapd / Station table
- * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -12,11 +12,11 @@
 #ifdef CONFIG_MESH
 /* needed for mesh_plink_state enum */
 #include "common/defs.h"
-#include "common/wpa_common.h"
 #endif /* CONFIG_MESH */
 
 #include "list.h"
 #include "vlan.h"
+#include "common/wpa_common.h"
 #include "common/ieee802_11_defs.h"
 
 /* STA flags */
@@ -48,6 +48,7 @@
  * Supported Rates IEs). */
 #define WLAN_SUPP_RATES_MAX 32
 
+struct hostapd_data;
 
 struct mbo_non_pref_chan_info {
 	struct mbo_non_pref_chan_info *next;
@@ -173,11 +174,11 @@
 	struct os_reltime sa_query_start;
 #endif /* CONFIG_IEEE80211W */
 
-#ifdef CONFIG_INTERWORKING
+#if defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP)
 #define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */
 	struct gas_dialog_info *gas_dialog;
 	u8 gas_dialog_next;
-#endif /* CONFIG_INTERWORKING */
+#endif /* CONFIG_INTERWORKING || CONFIG_DPP */
 
 	struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
 	struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */
@@ -225,13 +226,37 @@
 #ifdef CONFIG_FILS
 	u8 fils_snonce[FILS_NONCE_LEN];
 	u8 fils_session[FILS_SESSION_LEN];
+	u8 fils_erp_pmkid[PMKID_LEN];
 	u8 *fils_pending_assoc_req;
 	size_t fils_pending_assoc_req_len;
 	unsigned int fils_pending_assoc_is_reassoc:1;
 	unsigned int fils_dhcp_rapid_commit_proxy:1;
+	unsigned int fils_erp_pmkid_set:1;
+	unsigned int fils_drv_assoc_finish:1;
 	struct wpabuf *fils_hlp_resp;
 	struct wpabuf *hlp_dhcp_discover;
+	void (*fils_pending_cb)(struct hostapd_data *hapd, struct sta_info *sta,
+				u16 resp, struct wpabuf *data, int pub);
+#ifdef CONFIG_FILS_SK_PFS
+	struct crypto_ecdh *fils_ecdh;
+#endif /* CONFIG_FILS_SK_PFS */
+	struct wpabuf *fils_dh_ss;
+	struct wpabuf *fils_g_sta;
 #endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+	u8 *owe_pmk;
+	size_t owe_pmk_len;
+	struct crypto_ecdh *owe_ecdh;
+	u16 owe_group;
+#endif /* CONFIG_OWE */
+
+#ifdef CONFIG_TESTING_OPTIONS
+	enum wpa_alg last_tk_alg;
+	int last_tk_key_idx;
+	u8 last_tk[WPA_TK_MAX_LEN];
+	size_t last_tk_len;
+#endif /* CONFIG_TESTING_OPTIONS */
 };
 
 
@@ -251,8 +276,6 @@
 #define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5)
 
 
-struct hostapd_data;
-
 int ap_for_each_sta(struct hostapd_data *hapd,
 		    int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
 			      void *ctx),
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 85e85e0..8265fa1 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -35,11 +35,13 @@
 
 static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx);
 static int wpa_sm_step(struct wpa_state_machine *sm);
-static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data,
-			      size_t data_len);
+static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK,
+			      u8 *data, size_t data_len);
 #ifdef CONFIG_FILS
 static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
 			    u8 *buf, size_t buf_len, u16 *_key_data_len);
+static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
+					     const struct wpabuf *hlp);
 #endif /* CONFIG_FILS */
 static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx);
 static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
@@ -63,6 +65,7 @@
 static const u32 eapol_key_timeout_first = 100; /* ms */
 static const u32 eapol_key_timeout_subseq = 1000; /* ms */
 static const u32 eapol_key_timeout_first_group = 500; /* ms */
+static const u32 eapol_key_timeout_no_retrans = 4000; /* ms */
 
 /* TODO: make these configurable */
 static const int dot11RSNAConfigPMKLifetime = 43200;
@@ -108,12 +111,12 @@
 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)
+					  const u8 *prev_psk, size_t *psk_len)
 {
 	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);
+				     prev_psk, psk_len);
 }
 
 
@@ -224,13 +227,13 @@
 
 
 static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth,
-			       const u8 *addr)
+			       const u8 *addr, u16 reason)
 {
 	if (wpa_auth->cb->disconnect == NULL)
 		return;
-	wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR, MAC2STR(addr));
-	wpa_auth->cb->disconnect(wpa_auth->cb_ctx, addr,
-				 WLAN_REASON_PREV_AUTH_NOT_VALID);
+	wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR " (reason %u)",
+		   MAC2STR(addr), reason);
+	wpa_auth->cb->disconnect(wpa_auth->cb_ctx, addr, reason);
 }
 
 
@@ -518,6 +521,7 @@
 #ifdef CONFIG_IEEE80211R_AP
 	wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache);
 	wpa_auth->ft_pmk_cache = NULL;
+	wpa_ft_deinit(wpa_auth);
 #endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_P2P
@@ -703,6 +707,9 @@
 	sm->pending_1_of_4_timeout = 0;
 	eloop_cancel_timeout(wpa_sm_call_step, sm, NULL);
 	eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
+#ifdef CONFIG_IEEE80211R_AP
+	wpa_ft_sta_deinit(sm);
+#endif /* CONFIG_IEEE80211R_AP */
 	if (sm->in_step_loop) {
 		/* Must not free state machine while wpa_sm_step() is running.
 		 * Freeing will be completed in the end of wpa_sm_step(). */
@@ -842,16 +849,16 @@
 	struct wpa_ptk PTK;
 	int ok = 0;
 	const u8 *pmk = NULL;
-	unsigned int pmk_len;
+	size_t pmk_len;
 
 	os_memset(&PTK, 0, sizeof(PTK));
 	for (;;) {
-		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+		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);
+					       sm->p2p_dev_addr, pmk, &pmk_len);
 			if (pmk == NULL)
 				break;
-			pmk_len = PMK_LEN;
 		} else {
 			pmk = sm->PMK;
 			pmk_len = sm->pmk_len;
@@ -860,8 +867,8 @@
 		if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK) < 0)
 			break;
 
-		if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len)
-		    == 0) {
+		if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
+				       data, data_len) == 0) {
 			ok = 1;
 			break;
 		}
@@ -894,8 +901,7 @@
 	struct ieee802_1x_hdr *hdr;
 	struct wpa_eapol_key *key;
 	u16 key_info, key_data_length;
-	enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST,
-	       SMK_M1, SMK_M3, SMK_ERROR } msg;
+	enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST } msg;
 	char *msgtxt;
 	struct wpa_eapol_ie_parse kde;
 	const u8 *key_data;
@@ -906,7 +912,7 @@
 		return;
 	wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL data", data, data_len);
 
-	mic_len = wpa_mic_len(sm->wpa_key_mgmt);
+	mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
 	keyhdrlen = sizeof(*key) + mic_len + 2;
 
 	if (data_len < sizeof(*hdr) + keyhdrlen) {
@@ -969,19 +975,12 @@
 	/* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys
 	 * are set */
 
-	if ((key_info & (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) ==
-	    (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) {
-		if (key_info & WPA_KEY_INFO_ERROR) {
-			msg = SMK_ERROR;
-			msgtxt = "SMK Error";
-		} else {
-			msg = SMK_M1;
-			msgtxt = "SMK M1";
-		}
-	} else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
-		msg = SMK_M3;
-		msgtxt = "SMK M3";
-	} else if (key_info & WPA_KEY_INFO_REQUEST) {
+	if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
+		wpa_printf(MSG_DEBUG, "WPA: Ignore SMK message");
+		return;
+	}
+
+	if (key_info & WPA_KEY_INFO_REQUEST) {
 		msg = REQUEST;
 		msgtxt = "Request";
 	} else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) {
@@ -997,7 +996,6 @@
 		msgtxt = "2/4 Pairwise";
 	}
 
-	/* TODO: key_info type validation for PeerKey */
 	if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 ||
 	    msg == GROUP_2) {
 		u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK;
@@ -1017,7 +1015,10 @@
 			}
 
 			if (!wpa_use_aes_cmac(sm) &&
+			    !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
 			    !wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
+			    sm->wpa_key_mgmt != WPA_KEY_MGMT_OWE &&
+			    sm->wpa_key_mgmt != WPA_KEY_MGMT_DPP &&
 			    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
 				wpa_auth_logger(wpa_auth, sm->addr,
 						LOGGER_WARNING,
@@ -1028,7 +1029,9 @@
 		}
 
 		if ((wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
-		     wpa_key_mgmt_fils(sm->wpa_key_mgmt)) &&
+		     wpa_key_mgmt_fils(sm->wpa_key_mgmt) ||
+		     sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
+		     sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) &&
 		    ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
 			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING,
 					"did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
@@ -1152,7 +1155,8 @@
 				   "collect more entropy for random number "
 				   "generation");
 			random_mark_pool_ready();
-			wpa_sta_disconnect(wpa_auth, sm->addr);
+			wpa_sta_disconnect(wpa_auth, sm->addr,
+					   WLAN_REASON_PREV_AUTH_NOT_VALID);
 			return;
 		}
 		break;
@@ -1176,28 +1180,6 @@
 			return;
 		}
 		break;
-#ifdef CONFIG_PEERKEY
-	case SMK_M1:
-	case SMK_M3:
-	case SMK_ERROR:
-		if (!wpa_auth->conf.peerkey) {
-			wpa_printf(MSG_DEBUG, "RSN: SMK M1/M3/Error, but "
-				   "PeerKey use disabled - ignoring message");
-			return;
-		}
-		if (!sm->PTK_valid) {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
-					"received EAPOL-Key msg SMK in "
-					"invalid state - dropped");
-			return;
-		}
-		break;
-#else /* CONFIG_PEERKEY */
-	case SMK_M1:
-	case SMK_M3:
-	case SMK_ERROR:
-		return; /* STSL disabled - ignore SMK messages */
-#endif /* CONFIG_PEERKEY */
 	case REQUEST:
 		break;
 	}
@@ -1230,8 +1212,8 @@
 	sm->MICVerified = FALSE;
 	if (sm->PTK_valid && !sm->update_snonce) {
 		if (mic_len &&
-		    wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data,
-				       data_len) &&
+		    wpa_verify_key_mic(sm->wpa_key_mgmt, sm->pmk_len, &sm->PTK,
+				       data, data_len) &&
 		    (msg != PAIRWISE_4 || !sm->alt_snonce_valid ||
 		     wpa_try_alt_snonce(sm, data, data_len))) {
 			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
@@ -1269,12 +1251,7 @@
 		 * even though MAC address KDE is not normally encrypted,
 		 * supplicant is allowed to encrypt it.
 		 */
-		if (msg == SMK_ERROR) {
-#ifdef CONFIG_PEERKEY
-			wpa_smk_error(wpa_auth, sm, key_data, key_data_length);
-#endif /* CONFIG_PEERKEY */
-			return;
-		} else if (key_info & WPA_KEY_INFO_ERROR) {
+		if (key_info & WPA_KEY_INFO_ERROR) {
 			if (wpa_receive_error_report(
 				    wpa_auth, sm,
 				    !(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0)
@@ -1284,11 +1261,6 @@
 					"received EAPOL-Key Request for new "
 					"4-Way Handshake");
 			wpa_request_new_ptk(sm);
-#ifdef CONFIG_PEERKEY
-		} else if (msg == SMK_M1) {
-			wpa_smk_m1(wpa_auth, sm, key, key_data,
-				   key_data_length);
-#endif /* CONFIG_PEERKEY */
 		} else if (key_data_length > 0 &&
 			   wpa_parse_kde_ies(key_data, key_data_length,
 					     &kde) == 0 &&
@@ -1327,18 +1299,10 @@
 		wpa_replay_counter_mark_invalid(sm->key_replay, NULL);
 	}
 
-#ifdef CONFIG_PEERKEY
-	if (msg == SMK_M3) {
-		wpa_smk_m3(wpa_auth, sm, key, key_data, key_data_length);
-		return;
-	}
-#endif /* CONFIG_PEERKEY */
-
 	os_free(sm->last_rx_eapol_key);
-	sm->last_rx_eapol_key = os_malloc(data_len);
+	sm->last_rx_eapol_key = os_memdup(data, data_len);
 	if (sm->last_rx_eapol_key == NULL)
 		return;
-	os_memcpy(sm->last_rx_eapol_key, data, data_len);
 	sm->last_rx_eapol_key_len = data_len;
 
 	sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE);
@@ -1412,7 +1376,7 @@
 	int i;
 	u8 *key_mic, *key_data;
 
-	mic_len = wpa_mic_len(sm->wpa_key_mgmt);
+	mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
 	keyhdrlen = sizeof(*key) + mic_len + 2;
 
 	len = sizeof(struct ieee802_1x_hdr) + keyhdrlen;
@@ -1420,6 +1384,8 @@
 	if (force_version)
 		version = force_version;
 	else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
+		 sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE ||
+		 sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
 		 wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
 		 wpa_key_mgmt_fils(sm->wpa_key_mgmt))
 		version = WPA_KEY_INFO_TYPE_AKM_DEFINED;
@@ -1445,6 +1411,8 @@
 	key_data_len = kde_len;
 
 	if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+	     sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE ||
+	     sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
 	     sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
 	     wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
 	     version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) {
@@ -1478,13 +1446,11 @@
 	WPA_PUT_BE16(key->key_info, key_info);
 
 	alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group;
-	if ((key_info & WPA_KEY_INFO_SMK_MESSAGE) ||
-	    (sm->wpa == WPA_VERSION_WPA2 && !pairwise))
+	if (sm->wpa == WPA_VERSION_WPA2 && !pairwise)
 		WPA_PUT_BE16(key->key_length, 0);
 	else
 		WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg));
 
-	/* FIX: STSL: what to use as key_replay_counter? */
 	for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) {
 		sm->key_replay[i].valid = sm->key_replay[i - 1].valid;
 		os_memcpy(sm->key_replay[i].counter,
@@ -1547,9 +1513,14 @@
 		wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
 				buf, key_data_len);
 		if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+		    sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE ||
+		    sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
 		    sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
 		    wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
 		    version == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+			wpa_printf(MSG_DEBUG,
+				   "WPA: Encrypt Key Data using AES-WRAP (KEK length %u)",
+				   (unsigned int) sm->PTK.kek_len);
 			if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len,
 				     (key_data_len - 8) / 8, buf, key_data)) {
 				os_free(hdr);
@@ -1560,6 +1531,9 @@
 #ifndef CONFIG_NO_RC4
 		} else if (sm->PTK.kek_len == 16) {
 			u8 ek[32];
+
+			wpa_printf(MSG_DEBUG,
+				   "WPA: Encrypt Key Data using RC4");
 			os_memcpy(key->key_iv,
 				  sm->group->Counter + WPA_NONCE_LEN - 16, 16);
 			inc_byte_array(sm->group->Counter, WPA_NONCE_LEN);
@@ -1631,6 +1605,9 @@
 			eapol_key_timeout_first_group;
 	else
 		timeout_ms = eapol_key_timeout_subseq;
+	if (wpa_auth->conf.wpa_disable_eapol_key_retries &&
+	    (!pairwise || (key_info & WPA_KEY_INFO_MIC)))
+		timeout_ms = eapol_key_timeout_no_retrans;
 	if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC))
 		sm->pending_1_of_4_timeout = 1;
 	wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry "
@@ -1640,15 +1617,15 @@
 }
 
 
-static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data,
-			      size_t data_len)
+static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK,
+			      u8 *data, size_t data_len)
 {
 	struct ieee802_1x_hdr *hdr;
 	struct wpa_eapol_key *key;
 	u16 key_info;
 	int ret = 0;
 	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN], *mic_pos;
-	size_t mic_len = wpa_mic_len(akmp);
+	size_t mic_len = wpa_mic_len(akmp, pmk_len);
 
 	if (data_len < sizeof(*hdr) + sizeof(*key))
 		return -1;
@@ -1751,6 +1728,16 @@
 #else /* CONFIG_IEEE80211R_AP */
 		break;
 #endif /* CONFIG_IEEE80211R_AP */
+	case WPA_ASSOC_FILS:
+#ifdef CONFIG_FILS
+		wpa_printf(MSG_DEBUG,
+			   "FILS: TK configuration after association");
+		fils_set_tk(sm);
+		sm->fils_completed = 1;
+		return 0;
+#else /* CONFIG_FILS */
+		break;
+#endif /* CONFIG_FILS */
 	case WPA_DRV_STA_REMOVED:
 		sm->tk_already_set = FALSE;
 		return 0;
@@ -1813,7 +1800,9 @@
 	wpa_remove_ptk(sm);
 	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0);
 	sm->TimeoutCtr = 0;
-	if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+	if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+	    sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
+	    sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) {
 		wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
 				   WPA_EAPOL_authorized, 0);
 	}
@@ -1822,9 +1811,14 @@
 
 SM_STATE(WPA_PTK, DISCONNECT)
 {
+	u16 reason = sm->disconnect_reason;
+
 	SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk);
 	sm->Disconnect = FALSE;
-	wpa_sta_disconnect(sm->wpa_auth, sm->addr);
+	sm->disconnect_reason = 0;
+	if (!reason)
+		reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
+	wpa_sta_disconnect(sm->wpa_auth, sm->addr, reason);
 }
 
 
@@ -1940,6 +1934,14 @@
 		wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache");
 		os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
 		sm->pmk_len = sm->pmksa->pmk_len;
+#ifdef CONFIG_DPP
+	} else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No PMKSA cache entry for STA - reject connection");
+		sm->Disconnect = TRUE;
+		sm->disconnect_reason = WLAN_REASON_INVALID_PMKID;
+		return;
+#endif /* CONFIG_DPP */
 	} else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) {
 		unsigned int pmk_len;
 
@@ -1989,16 +1991,26 @@
 SM_STATE(WPA_PTK, INITPSK)
 {
 	const u8 *psk;
+	size_t psk_len;
+
 	SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk);
-	psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL);
+	psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL,
+			       &psk_len);
 	if (psk) {
-		os_memcpy(sm->PMK, psk, PMK_LEN);
-		sm->pmk_len = PMK_LEN;
+		os_memcpy(sm->PMK, psk, psk_len);
+		sm->pmk_len = psk_len;
 #ifdef CONFIG_IEEE80211R_AP
 		os_memcpy(sm->xxkey, psk, PMK_LEN);
 		sm->xxkey_len = PMK_LEN;
 #endif /* CONFIG_IEEE80211R_AP */
 	}
+#ifdef CONFIG_SAE
+	if (wpa_auth_uses_sae(sm) && sm->pmksa) {
+		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;
+	}
+#endif /* CONFIG_SAE */
 	sm->req_replay_counter_used = 0;
 }
 
@@ -2027,7 +2039,9 @@
 	 * one possible PSK for this STA.
 	 */
 	if (sm->wpa == WPA_VERSION_WPA2 &&
-	    wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) &&
+	    (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) ||
+	     (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && sm->pmksa) ||
+	     wpa_key_mgmt_sae(sm->wpa_key_mgmt)) &&
 	    sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) {
 		pmkid = buf;
 		pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
@@ -2047,7 +2061,7 @@
 			 */
 			rsn_pmkid(sm->PMK, sm->pmk_len, sm->wpa_auth->addr,
 				  sm->addr, &pmkid[2 + RSN_SELECTOR_LEN],
-				  wpa_key_mgmt_sha256(sm->wpa_key_mgmt));
+				  sm->wpa_key_mgmt);
 		}
 	}
 	wpa_send_eapol(sm->wpa_auth, sm,
@@ -2074,22 +2088,55 @@
 #ifdef CONFIG_FILS
 
 int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
-			 size_t pmk_len, const u8 *snonce, const u8 *anonce)
+			 size_t pmk_len, const u8 *snonce, const u8 *anonce,
+			 const u8 *dhss, size_t dhss_len,
+			 struct wpabuf *g_sta, struct wpabuf *g_ap)
 {
 	u8 ick[FILS_ICK_MAX_LEN];
 	size_t ick_len;
 	int res;
+	u8 fils_ft[FILS_FT_MAX_LEN];
+	size_t fils_ft_len = 0;
 
 	res = fils_pmk_to_ptk(pmk, pmk_len, sm->addr, sm->wpa_auth->addr,
-			      snonce, anonce, &sm->PTK, ick, &ick_len,
-			      sm->wpa_key_mgmt, sm->pairwise);
+			      snonce, anonce, dhss, dhss_len,
+			      &sm->PTK, ick, &ick_len,
+			      sm->wpa_key_mgmt, sm->pairwise,
+			      fils_ft, &fils_ft_len);
 	if (res < 0)
 		return res;
 	sm->PTK_valid = TRUE;
+	sm->tk_already_set = FALSE;
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (fils_ft_len) {
+		struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+		struct wpa_auth_config *conf = &wpa_auth->conf;
+		u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
+
+		if (wpa_derive_pmk_r0(fils_ft, fils_ft_len,
+				      conf->ssid, conf->ssid_len,
+				      conf->mobility_domain,
+				      conf->r0_key_holder,
+				      conf->r0_key_holder_len,
+				      sm->addr, pmk_r0, pmk_r0_name) < 0)
+			return -1;
+
+		wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0", pmk_r0, PMK_LEN);
+		wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name",
+			    pmk_r0_name, WPA_PMK_NAME_LEN);
+		wpa_ft_store_pmk_r0(wpa_auth, sm->addr, pmk_r0, pmk_r0_name,
+				    sm->pairwise);
+		os_memset(fils_ft, 0, sizeof(fils_ft));
+	}
+#endif /* CONFIG_IEEE80211R_AP */
 
 	res = fils_key_auth_sk(ick, ick_len, snonce, anonce,
 			       sm->addr, sm->wpa_auth->addr,
-			       NULL, 0, NULL, 0, /* TODO: SK+PFS */
+			       g_sta ? wpabuf_head(g_sta) : NULL,
+			       g_sta ? wpabuf_len(g_sta) : 0,
+			       g_ap ? wpabuf_head(g_ap) : NULL,
+			       g_ap ? wpabuf_len(g_ap) : 0,
 			       sm->wpa_key_mgmt, sm->fils_key_auth_sta,
 			       sm->fils_key_auth_ap,
 			       &sm->fils_key_auth_len);
@@ -2158,13 +2205,109 @@
 }
 
 
+const u8 * wpa_fils_validate_fils_session(struct wpa_state_machine *sm,
+					  const u8 *ies, size_t ies_len,
+					  const u8 *fils_session)
+{
+	const u8 *ie, *end;
+	const u8 *session = NULL;
+
+	if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Not a FILS AKM - reject association");
+		return NULL;
+	}
+
+	/* Verify Session element */
+	ie = ies;
+	end = ((const u8 *) ie) + ies_len;
+	while (ie + 1 < end) {
+		if (ie + 2 + ie[1] > end)
+			break;
+		if (ie[0] == WLAN_EID_EXTENSION &&
+		    ie[1] >= 1 + FILS_SESSION_LEN &&
+		    ie[2] == WLAN_EID_EXT_FILS_SESSION) {
+			session = ie;
+			break;
+		}
+		ie += 2 + ie[1];
+	}
+
+	if (!session) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: %s: Could not find FILS Session element in Assoc Req - reject",
+			   __func__);
+		return NULL;
+	}
+
+	if (!fils_session) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: %s: Could not find FILS Session element in STA entry - reject",
+			   __func__);
+		return NULL;
+	}
+
+	if (os_memcmp(fils_session, session + 3, FILS_SESSION_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FILS: Session mismatch");
+		wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
+			    fils_session, FILS_SESSION_LEN);
+		wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session",
+			    session + 3, FILS_SESSION_LEN);
+		return NULL;
+	}
+	return session;
+}
+
+
+int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies,
+				  size_t ies_len)
+{
+	struct ieee802_11_elems elems;
+
+	if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Failed to parse decrypted elements");
+		return -1;
+	}
+
+	if (!elems.fils_session) {
+		wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
+		return -1;
+	}
+
+	if (!elems.fils_key_confirm) {
+		wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element");
+		return -1;
+	}
+
+	if (elems.fils_key_confirm_len != sm->fils_key_auth_len) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Unexpected Key-Auth length %d (expected %d)",
+			   elems.fils_key_confirm_len,
+			   (int) sm->fils_key_auth_len);
+		return -1;
+	}
+
+	if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_sta,
+		      sm->fils_key_auth_len) != 0) {
+		wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch");
+		wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth",
+			    elems.fils_key_confirm, elems.fils_key_confirm_len);
+		wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth",
+			    sm->fils_key_auth_sta, sm->fils_key_auth_len);
+		return -1;
+	}
+
+	return 0;
+}
+
+
 int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
 		       const struct ieee80211_mgmt *mgmt, size_t frame_len,
 		       u8 *pos, size_t left)
 {
 	u16 fc, stype;
 	const u8 *end, *ie_start, *ie, *session, *crypt;
-	struct ieee802_11_elems elems;
 	const u8 *aad[5];
 	size_t aad_len[5];
 
@@ -2193,32 +2336,13 @@
 	 * Find FILS Session element which is the last unencrypted element in
 	 * the frame.
 	 */
-	session = NULL;
-	while (ie + 1 < end) {
-		if (ie + 2 + ie[1] > end)
-			break;
-		if (ie[0] == WLAN_EID_EXTENSION &&
-		    ie[1] >= 1 + FILS_SESSION_LEN &&
-		    ie[2] == WLAN_EID_EXT_FILS_SESSION) {
-			session = ie;
-			break;
-		}
-		ie += 2 + ie[1];
+	session = wpa_fils_validate_fils_session(sm, ie, end - ie,
+						 fils_session);
+	if (!session) {
+		wpa_printf(MSG_DEBUG, "FILS: Session validation failed");
+		return -1;
 	}
 
-	if (!session) {
-		wpa_printf(MSG_DEBUG,
-			   "FILS: Could not find FILS Session element in Association Request frame - reject");
-		return -1;
-	}
-	if (os_memcmp(fils_session, session + 3, FILS_SESSION_LEN) != 0) {
-		wpa_printf(MSG_DEBUG, "FILS: Session mismatch");
-		wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
-			    fils_session, FILS_SESSION_LEN);
-		wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session",
-			    session + 3, FILS_SESSION_LEN);
-		return -1;
-	}
 	crypt = session + 2 + session[1];
 
 	if (end - crypt < AES_BLOCK_SIZE) {
@@ -2257,31 +2381,8 @@
 	wpa_hexdump(MSG_DEBUG, "FILS: Decrypted Association Request elements",
 		    pos, left - AES_BLOCK_SIZE);
 
-	if (ieee802_11_parse_elems(pos, left - AES_BLOCK_SIZE, &elems, 1) ==
-	    ParseFailed) {
-		wpa_printf(MSG_DEBUG,
-			   "FILS: Failed to parse decrypted elements");
-		return -1;
-	}
-	if (!elems.fils_key_confirm) {
-		wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element");
-		return -1;
-	}
-	if (elems.fils_key_confirm_len != sm->fils_key_auth_len) {
-		wpa_printf(MSG_DEBUG,
-			   "FILS: Unexpected Key-Auth length %d (expected %d)",
-			   elems.fils_key_confirm_len,
-			   (int) sm->fils_key_auth_len);
-		return -1;
-	}
-	if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_sta,
-		      sm->fils_key_auth_len) != 0) {
-		wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch");
-		wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth",
-			    elems.fils_key_confirm,
-			    elems.fils_key_confirm_len);
-		wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth",
-			    sm->fils_key_auth_sta, sm->fils_key_auth_len);
+	if (wpa_fils_validate_key_confirm(sm, pos, left - AES_BLOCK_SIZE) < 0) {
+		wpa_printf(MSG_DEBUG, "FILS: Key Confirm validation failed");
 		return -1;
 	}
 
@@ -2297,11 +2398,6 @@
 	u8 *pos = buf + current_len;
 	struct ieee80211_mgmt *mgmt;
 	struct wpabuf *plain;
-	u8 *len, *tmp, *tmp2;
-	u8 hdr[2];
-	u8 *gtk, dummy_gtk[32];
-	size_t gtk_len;
-	struct wpa_group *gsm;
 	const u8 *aad[5];
 	size_t aad_len[5];
 
@@ -2337,10 +2433,54 @@
 	aad_len[4] = pos - aad[4];
 
 	/* The following elements will be encrypted with AES-SIV */
+	plain = fils_prepare_plainbuf(sm, hlp);
+	if (!plain) {
+		wpa_printf(MSG_DEBUG, "FILS: Plain buffer prep failed");
+		return -1;
+	}
+
+	if (pos + wpabuf_len(plain) + AES_BLOCK_SIZE > end) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Not enough room for FILS elements");
+		wpabuf_free(plain);
+		return -1;
+	}
+
+	wpa_hexdump_buf_key(MSG_DEBUG, "FILS: Association Response plaintext",
+			    plain);
+
+	if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len,
+			    wpabuf_head(plain), wpabuf_len(plain),
+			    5, aad, aad_len, pos) < 0) {
+		wpabuf_free(plain);
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG,
+		    "FILS: Encrypted Association Response elements",
+		    pos, AES_BLOCK_SIZE + wpabuf_len(plain));
+	current_len += wpabuf_len(plain) + AES_BLOCK_SIZE;
+	wpabuf_free(plain);
+
+	sm->fils_completed = 1;
+
+	return current_len;
+}
+
+
+static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
+					     const struct wpabuf *hlp)
+{
+	struct wpabuf *plain;
+	u8 *len, *tmp, *tmp2;
+	u8 hdr[2];
+	u8 *gtk, dummy_gtk[32];
+	size_t gtk_len;
+	struct wpa_group *gsm;
 
 	plain = wpabuf_alloc(1000);
 	if (!plain)
-		return -1;
+		return NULL;
 
 	/* TODO: FILS Public Key */
 
@@ -2374,7 +2514,7 @@
 		 */
 		if (random_get_bytes(dummy_gtk, gtk_len) < 0) {
 			wpabuf_free(plain);
-			return -1;
+			return NULL;
 		}
 		gtk = dummy_gtk;
 	}
@@ -2391,33 +2531,7 @@
 	wpabuf_put(plain, tmp2 - tmp);
 
 	*len = (u8 *) wpabuf_put(plain, 0) - len - 1;
-
-	if (pos + wpabuf_len(plain) + AES_BLOCK_SIZE > end) {
-		wpa_printf(MSG_DEBUG,
-			   "FILS: Not enough room for FILS elements");
-		wpabuf_free(plain);
-		return -1;
-	}
-
-	wpa_hexdump_buf_key(MSG_DEBUG, "FILS: Association Response plaintext",
-			    plain);
-
-	if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len,
-			    wpabuf_head(plain), wpabuf_len(plain),
-			    5, aad, aad_len, pos) < 0) {
-		wpabuf_free(plain);
-		return -1;
-	}
-
-	wpa_hexdump(MSG_DEBUG,
-		    "FILS: Encrypted Association Response elements",
-		    pos, AES_BLOCK_SIZE + wpabuf_len(plain));
-	current_len += wpabuf_len(plain) + AES_BLOCK_SIZE;
-	wpabuf_free(plain);
-
-	sm->fils_completed = 1;
-
-	return current_len;
+	return plain;
 }
 
 
@@ -2426,8 +2540,14 @@
 	enum wpa_alg alg;
 	int klen;
 
-	if (!sm || !sm->PTK_valid)
+	if (!sm || !sm->PTK_valid) {
+		wpa_printf(MSG_DEBUG, "FILS: No valid PTK available to set TK");
 		return -1;
+	}
+	if (sm->tk_already_set) {
+		wpa_printf(MSG_DEBUG, "FILS: TK already set to the driver");
+		return -1;
+	}
 
 	alg = wpa_cipher_to_alg(sm->pairwise);
 	klen = wpa_cipher_key_len(sm->pairwise);
@@ -2438,10 +2558,41 @@
 		wpa_printf(MSG_DEBUG, "FILS: Failed to set TK to the driver");
 		return -1;
 	}
+	sm->tk_already_set = TRUE;
 
 	return 0;
 }
 
+
+u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *buf,
+				    const u8 *fils_session, struct wpabuf *hlp)
+{
+	struct wpabuf *plain;
+	u8 *pos = buf;
+
+	/* FILS Session */
+	*pos++ = WLAN_EID_EXTENSION; /* Element ID */
+	*pos++ = 1 + FILS_SESSION_LEN; /* Length */
+	*pos++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */
+	os_memcpy(pos, fils_session, FILS_SESSION_LEN);
+	pos += FILS_SESSION_LEN;
+
+	plain = fils_prepare_plainbuf(sm, hlp);
+	if (!plain) {
+		wpa_printf(MSG_DEBUG, "FILS: Plain buffer prep failed");
+		return NULL;
+	}
+
+	os_memcpy(pos, wpabuf_head(plain), wpabuf_len(plain));
+	pos += wpabuf_len(plain);
+
+	wpa_printf(MSG_DEBUG, "%s: plain buf_len: %u", __func__,
+		   (unsigned int) wpabuf_len(plain));
+	wpabuf_free(plain);
+	sm->fils_completed = 1;
+	return pos;
+}
+
 #endif /* CONFIG_FILS */
 
 
@@ -2451,7 +2602,7 @@
 	struct wpa_ptk PTK;
 	int ok = 0, psk_found = 0;
 	const u8 *pmk = NULL;
-	unsigned int pmk_len;
+	size_t pmk_len;
 	int ft;
 	const u8 *eapol_key_ie, *key_data, *mic;
 	u16 key_data_length;
@@ -2465,19 +2616,19 @@
 	sm->update_snonce = FALSE;
 	os_memset(&PTK, 0, sizeof(PTK));
 
-	mic_len = wpa_mic_len(sm->wpa_key_mgmt);
+	mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
 
 	/* WPA with IEEE 802.1X: use the derived PMK from EAP
 	 * WPA-PSK: iterate through possible PSKs and select the one matching
 	 * the packet */
 	for (;;) {
-		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+		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);
+					       sm->p2p_dev_addr, pmk, &pmk_len);
 			if (pmk == NULL)
 				break;
 			psk_found = 1;
-			pmk_len = PMK_LEN;
 		} else {
 			pmk = sm->PMK;
 			pmk_len = sm->pmk_len;
@@ -2487,7 +2638,7 @@
 			break;
 
 		if (mic_len &&
-		    wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK,
+		    wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
 				       sm->last_rx_eapol_key,
 				       sm->last_rx_eapol_key_len) == 0) {
 			ok = 1;
@@ -2556,12 +2707,14 @@
 		wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4",
 			    eapol_key_ie, eapol_key_ie_len);
 		/* MLME-DEAUTHENTICATE.request */
-		wpa_sta_disconnect(wpa_auth, sm->addr);
+		wpa_sta_disconnect(wpa_auth, sm->addr,
+				   WLAN_REASON_PREV_AUTH_NOT_VALID);
 		return;
 	}
 #ifdef CONFIG_IEEE80211R_AP
 	if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
-		wpa_sta_disconnect(wpa_auth, sm->addr);
+		wpa_sta_disconnect(wpa_auth, sm->addr,
+				   WLAN_REASON_PREV_AUTH_NOT_VALID);
 		return;
 	}
 #endif /* CONFIG_IEEE80211R_AP */
@@ -2706,6 +2859,11 @@
 	sm->TimeoutEvt = FALSE;
 
 	sm->TimeoutCtr++;
+	if (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
+	    sm->TimeoutCtr > 1) {
+		/* Do not allow retransmission of EAPOL-Key msg 3/4 */
+		return;
+	}
 	if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) {
 		/* No point in sending the EAPOL-Key - we will disconnect
 		 * immediately following this. */
@@ -2871,7 +3029,8 @@
 
 	wpa_send_eapol(sm->wpa_auth, sm,
 		       (secure ? WPA_KEY_INFO_SECURE : 0) |
-		       (wpa_mic_len(sm->wpa_key_mgmt) ? WPA_KEY_INFO_MIC : 0) |
+		       (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
+			WPA_KEY_INFO_MIC : 0) |
 		       WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
 		       WPA_KEY_INFO_KEY_TYPE,
 		       _rsc, sm->ANonce, kde, pos - kde, keyidx, encr);
@@ -2888,7 +3047,8 @@
 		int klen = wpa_cipher_key_len(sm->pairwise);
 		if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
 				     sm->PTK.tk, klen)) {
-			wpa_sta_disconnect(sm->wpa_auth, sm->addr);
+			wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+					   WLAN_REASON_PREV_AUTH_NOT_VALID);
 			return;
 		}
 		/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
@@ -2901,7 +3061,9 @@
 					       sm->wpa_auth, sm);
 		}
 
-		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+		    sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
+		    sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) {
 			wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
 					   WPA_EAPOL_authorized, 1);
 		}
@@ -2973,15 +3135,22 @@
 		    wpa_auth_get_eapol(sm->wpa_auth, sm->addr,
 				       WPA_EAPOL_keyRun) > 0)
 			SM_ENTER(WPA_PTK, INITPMK);
-		else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)
+		else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+			 sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE
 			 /* FIX: && 802.1X::keyRun */)
 			SM_ENTER(WPA_PTK, INITPSK);
+		else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP)
+			SM_ENTER(WPA_PTK, INITPMK);
 		break;
 	case WPA_PTK_INITPMK:
 		if (wpa_auth_get_eapol(sm->wpa_auth, sm->addr,
-				       WPA_EAPOL_keyAvailable) > 0)
+				       WPA_EAPOL_keyAvailable) > 0) {
 			SM_ENTER(WPA_PTK, PTKSTART);
-		else {
+#ifdef CONFIG_DPP
+		} else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && sm->pmksa) {
+			SM_ENTER(WPA_PTK, PTKSTART);
+#endif /* CONFIG_DPP */
+		} else {
 			wpa_auth->dot11RSNA4WayHandshakeFailures++;
 			wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
 					"INITPMK - keyAvailable = false");
@@ -2990,9 +3159,13 @@
 		break;
 	case WPA_PTK_INITPSK:
 		if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr,
-				     NULL))
+				     NULL, NULL)) {
 			SM_ENTER(WPA_PTK, PTKSTART);
-		else {
+#ifdef CONFIG_SAE
+		} else if (wpa_auth_uses_sae(sm) && sm->pmksa) {
+			SM_ENTER(WPA_PTK, PTKSTART);
+#endif /* CONFIG_SAE */
+		} else {
 			wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
 					"no PSK configured for the STA");
 			wpa_auth->dot11RSNA4WayHandshakeFailures++;
@@ -3033,7 +3206,9 @@
 			 sm->EAPOLKeyPairwise && sm->MICVerified)
 			SM_ENTER(WPA_PTK, PTKINITDONE);
 		else if (sm->TimeoutCtr >
-			 sm->wpa_auth->conf.wpa_pairwise_update_count) {
+			 sm->wpa_auth->conf.wpa_pairwise_update_count ||
+			 (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
+			  sm->TimeoutCtr > 1)) {
 			wpa_auth->dot11RSNA4WayHandshakeFailures++;
 			wpa_auth_vlogger(
 				sm->wpa_auth, sm->addr, LOGGER_DEBUG,
@@ -3073,6 +3248,11 @@
 	SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group);
 
 	sm->GTimeoutCtr++;
+	if (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
+	    sm->GTimeoutCtr > 1) {
+		/* Do not allow retransmission of EAPOL-Key group msg 1/2 */
+		return;
+	}
 	if (sm->GTimeoutCtr > sm->wpa_auth->conf.wpa_group_update_count) {
 		/* No point in sending the EAPOL-Key - we will disconnect
 		 * immediately following this. */
@@ -3120,7 +3300,8 @@
 
 	wpa_send_eapol(sm->wpa_auth, sm,
 		       WPA_KEY_INFO_SECURE |
-		       (wpa_mic_len(sm->wpa_key_mgmt) ? WPA_KEY_INFO_MIC : 0) |
+		       (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
+			WPA_KEY_INFO_MIC : 0) |
 		       WPA_KEY_INFO_ACK |
 		       (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
 		       rsc, NULL, kde, kde_len, gsm->GN, 1);
@@ -3175,7 +3356,9 @@
 		    !sm->EAPOLKeyPairwise && sm->MICVerified)
 			SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED);
 		else if (sm->GTimeoutCtr >
-			 sm->wpa_auth->conf.wpa_group_update_count)
+			 sm->wpa_auth->conf.wpa_group_update_count ||
+			 (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
+			  sm->GTimeoutCtr > 1))
 			SM_ENTER(WPA_PTK_GROUP, KEYERROR);
 		else if (sm->TimeoutEvt)
 			SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING);
@@ -3278,7 +3461,7 @@
 }
 
 
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
 /* update GTK when exiting WNM-Sleep Mode */
 void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm)
 {
@@ -3357,7 +3540,7 @@
 	return pos - start;
 }
 #endif /* CONFIG_IEEE80211W */
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
 
 
 static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
@@ -3763,6 +3946,14 @@
 }
 
 
+int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm)
+{
+	if (!sm || !wpa_key_mgmt_fils(sm->wpa_key_mgmt))
+		return 0;
+	return sm->tk_already_set;
+}
+
+
 int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
 			     struct rsn_pmksa_cache_entry *entry)
 {
@@ -3856,6 +4047,22 @@
 }
 
 
+int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
+			const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+			int session_timeout, int akmp)
+{
+	if (wpa_auth->conf.disable_pmksa_caching)
+		return -1;
+
+	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
+				 NULL, 0, wpa_auth->addr, addr, session_timeout,
+				 NULL, akmp))
+		return 0;
+
+	return -1;
+}
+
+
 void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
 			   const u8 *sta_addr)
 {
@@ -4199,6 +4406,14 @@
 				       (timeout_ms % 1000) * 1000,
 				       wpa_send_eapol_timeout, wpa_auth, sm);
 	}
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (sm->eapol_status_cb) {
+		sm->eapol_status_cb(sm->eapol_status_cb_ctx1,
+				    sm->eapol_status_cb_ctx2);
+		sm->eapol_status_cb = NULL;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 }
 
 
@@ -4245,3 +4460,317 @@
 	for (group = wpa_auth->group; group; group = group->next)
 		wpa_group_config_group_keys(wpa_auth, group);
 }
+
+
+#ifdef CONFIG_FILS
+
+struct wpa_auth_fils_iter_data {
+	struct wpa_authenticator *auth;
+	const u8 *cache_id;
+	struct rsn_pmksa_cache_entry *pmksa;
+	const u8 *spa;
+	const u8 *pmkid;
+};
+
+
+static int wpa_auth_fils_iter(struct wpa_authenticator *a, void *ctx)
+{
+	struct wpa_auth_fils_iter_data *data = ctx;
+
+	if (a == data->auth || !a->conf.fils_cache_id_set ||
+	    os_memcmp(a->conf.fils_cache_id, data->cache_id,
+		      FILS_CACHE_ID_LEN) != 0)
+		return 0;
+	data->pmksa = pmksa_cache_auth_get(a->pmksa, data->spa, data->pmkid);
+	return data->pmksa != NULL;
+}
+
+
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth,
+				 const u8 *sta_addr, const u8 *pmkid)
+{
+	struct wpa_auth_fils_iter_data idata;
+
+	if (!wpa_auth->conf.fils_cache_id_set)
+		return NULL;
+	idata.auth = wpa_auth;
+	idata.cache_id = wpa_auth->conf.fils_cache_id;
+	idata.pmksa = NULL;
+	idata.spa = sta_addr;
+	idata.pmkid = pmkid;
+	wpa_auth_for_each_auth(wpa_auth, wpa_auth_fils_iter, &idata);
+	return idata.pmksa;
+}
+
+
+#ifdef CONFIG_IEEE80211R_AP
+int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, u8 *buf, size_t len)
+{
+	struct wpa_auth_config *conf = &wpa_auth->conf;
+
+	return wpa_write_ftie(conf, conf->r0_key_holder,
+			      conf->r0_key_holder_len,
+			      NULL, NULL, buf, len, NULL, 0);
+}
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
+				   u8 *fils_anonce, u8 *fils_snonce,
+				   u8 *fils_kek, size_t *fils_kek_len)
+{
+	os_memcpy(fils_anonce, sm->ANonce, WPA_NONCE_LEN);
+	os_memcpy(fils_snonce, sm->SNonce, WPA_NONCE_LEN);
+	os_memcpy(fils_kek, sm->PTK.kek, WPA_KEK_MAX_LEN);
+	*fils_kek_len = sm->PTK.kek_len;
+}
+
+#endif /* CONFIG_FILS */
+
+
+#if CONFIG_TESTING_OPTIONS
+
+int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
+		       void (*cb)(void *ctx1, void *ctx2),
+		       void *ctx1, void *ctx2)
+{
+	const u8 *anonce = sm->ANonce;
+	u8 anonce_buf[WPA_NONCE_LEN];
+
+	if (change_anonce) {
+		if (random_get_bytes(anonce_buf, WPA_NONCE_LEN))
+			return -1;
+		anonce = anonce_buf;
+	}
+
+	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+			"sending 1/4 msg of 4-Way Handshake (TESTING)");
+	wpa_send_eapol(sm->wpa_auth, sm,
+		       WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL,
+		       anonce, NULL, 0, 0, 0);
+	return 0;
+}
+
+
+int wpa_auth_resend_m3(struct wpa_state_machine *sm,
+		       void (*cb)(void *ctx1, void *ctx2),
+		       void *ctx1, void *ctx2)
+{
+	u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, *opos;
+	size_t gtk_len, kde_len;
+	struct wpa_group *gsm = sm->group;
+	u8 *wpa_ie;
+	int wpa_ie_len, secure, keyidx, encr = 0;
+
+	/* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
+	   GTK[GN], IGTK, [FTIE], [TIE * 2])
+	 */
+
+	/* Use 0 RSC */
+	os_memset(rsc, 0, WPA_KEY_RSC_LEN);
+	/* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */
+	wpa_ie = sm->wpa_auth->wpa_ie;
+	wpa_ie_len = sm->wpa_auth->wpa_ie_len;
+	if (sm->wpa == WPA_VERSION_WPA &&
+	    (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+	    wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) {
+		/* WPA-only STA, remove RSN IE and possible MDIE */
+		wpa_ie = wpa_ie + wpa_ie[1] + 2;
+		if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN)
+			wpa_ie = wpa_ie + wpa_ie[1] + 2;
+		wpa_ie_len = wpa_ie[1] + 2;
+	}
+	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+			"sending 3/4 msg of 4-Way Handshake (TESTING)");
+	if (sm->wpa == WPA_VERSION_WPA2) {
+		/* WPA2 send GTK in the 4-way handshake */
+		secure = 1;
+		gtk = gsm->GTK[gsm->GN - 1];
+		gtk_len = gsm->GTK_len;
+		keyidx = gsm->GN;
+		_rsc = rsc;
+		encr = 1;
+	} else {
+		/* WPA does not include GTK in msg 3/4 */
+		secure = 0;
+		gtk = NULL;
+		gtk_len = 0;
+		keyidx = 0;
+		_rsc = NULL;
+		if (sm->rx_eapol_key_secure) {
+			/*
+			 * It looks like Windows 7 supplicant tries to use
+			 * Secure bit in msg 2/4 after having reported Michael
+			 * MIC failure and it then rejects the 4-way handshake
+			 * if msg 3/4 does not set Secure bit. Work around this
+			 * by setting the Secure bit here even in the case of
+			 * WPA if the supplicant used it first.
+			 */
+			wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+					"STA used Secure bit in WPA msg 2/4 - "
+					"set Secure for 3/4 as workaround");
+			secure = 1;
+		}
+	}
+
+	kde_len = wpa_ie_len + ieee80211w_kde_len(sm);
+	if (gtk)
+		kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
+#ifdef CONFIG_IEEE80211R_AP
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */
+		kde_len += 300; /* FTIE + 2 * TIE */
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+	kde = os_malloc(kde_len);
+	if (kde == NULL)
+		return -1;
+
+	pos = kde;
+	os_memcpy(pos, wpa_ie, wpa_ie_len);
+	pos += wpa_ie_len;
+#ifdef CONFIG_IEEE80211R_AP
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		int res;
+		size_t elen;
+
+		elen = pos - kde;
+		res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name);
+		if (res < 0) {
+			wpa_printf(MSG_ERROR, "FT: Failed to insert "
+				   "PMKR1Name into RSN IE in EAPOL-Key data");
+			os_free(kde);
+			return -1;
+		}
+		pos -= wpa_ie_len;
+		pos += elen;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+	if (gtk) {
+		u8 hdr[2];
+		hdr[0] = keyidx & 0x03;
+		hdr[1] = 0;
+		pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+				  gtk, gtk_len);
+	}
+	opos = pos;
+	pos = ieee80211w_kde_add(sm, pos);
+	if (pos - opos >= WPA_IGTK_KDE_PREFIX_LEN) {
+		opos += 2; /* skip keyid */
+		os_memset(opos, 0, 6); /* clear PN */
+	}
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		int res;
+		struct wpa_auth_config *conf;
+
+		conf = &sm->wpa_auth->conf;
+		if (sm->assoc_resp_ftie &&
+		    kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) {
+			os_memcpy(pos, sm->assoc_resp_ftie,
+				  2 + sm->assoc_resp_ftie[1]);
+			res = 2 + sm->assoc_resp_ftie[1];
+		} else {
+			res = wpa_write_ftie(conf, conf->r0_key_holder,
+					     conf->r0_key_holder_len,
+					     NULL, NULL, pos,
+					     kde + kde_len - pos,
+					     NULL, 0);
+		}
+		if (res < 0) {
+			wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE "
+				   "into EAPOL-Key Key Data");
+			os_free(kde);
+			return -1;
+		}
+		pos += res;
+
+		/* TIE[ReassociationDeadline] (TU) */
+		*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+		*pos++ = 5;
+		*pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE;
+		WPA_PUT_LE32(pos, conf->reassociation_deadline);
+		pos += 4;
+
+		/* TIE[KeyLifetime] (seconds) */
+		*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+		*pos++ = 5;
+		*pos++ = WLAN_TIMEOUT_KEY_LIFETIME;
+		WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60);
+		pos += 4;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+
+	wpa_send_eapol(sm->wpa_auth, sm,
+		       (secure ? WPA_KEY_INFO_SECURE : 0) |
+		       (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
+			WPA_KEY_INFO_MIC : 0) |
+		       WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
+		       WPA_KEY_INFO_KEY_TYPE,
+		       _rsc, sm->ANonce, kde, pos - kde, keyidx, encr);
+	os_free(kde);
+	return 0;
+}
+
+
+int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
+			     void (*cb)(void *ctx1, void *ctx2),
+			     void *ctx1, void *ctx2)
+{
+	u8 rsc[WPA_KEY_RSC_LEN];
+	struct wpa_group *gsm = sm->group;
+	const u8 *kde;
+	u8 *kde_buf = NULL, *pos, *opos, hdr[2];
+	size_t kde_len;
+	u8 *gtk;
+
+	/* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */
+	os_memset(rsc, 0, WPA_KEY_RSC_LEN);
+	/* Use 0 RSC */
+	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+			"sending 1/2 msg of Group Key Handshake (TESTING)");
+
+	gtk = gsm->GTK[gsm->GN - 1];
+	if (sm->wpa == WPA_VERSION_WPA2) {
+		kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
+			ieee80211w_kde_len(sm);
+		kde_buf = os_malloc(kde_len);
+		if (kde_buf == NULL)
+			return -1;
+
+		kde = pos = kde_buf;
+		hdr[0] = gsm->GN & 0x03;
+		hdr[1] = 0;
+		pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+				  gtk, gsm->GTK_len);
+		opos = pos;
+		pos = ieee80211w_kde_add(sm, pos);
+		if (pos - opos >= WPA_IGTK_KDE_PREFIX_LEN) {
+			opos += 2; /* skip keyid */
+			os_memset(opos, 0, 6); /* clear PN */
+		}
+		kde_len = pos - kde;
+	} else {
+		kde = gtk;
+		kde_len = gsm->GTK_len;
+	}
+
+	sm->eapol_status_cb = cb;
+	sm->eapol_status_cb_ctx1 = ctx1;
+	sm->eapol_status_cb_ctx2 = ctx2;
+
+	wpa_send_eapol(sm->wpa_auth, sm,
+		       WPA_KEY_INFO_SECURE |
+		       (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
+			WPA_KEY_INFO_MIC : 0) |
+		       WPA_KEY_INFO_ACK |
+		       (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
+		       rsc, NULL, kde, kde_len, gsm->GN, 1);
+
+	os_free(kde_buf);
+	return 0;
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 387d146..22f33dd 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -1,6 +1,6 @@
 /*
  * hostapd - IEEE 802.11i-2004 / WPA Authenticator
- * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -37,73 +37,89 @@
 
 #define FT_PACKET_REQUEST 0
 #define FT_PACKET_RESPONSE 1
-/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r */
-#define FT_PACKET_R0KH_R1KH_PULL 200
-#define FT_PACKET_R0KH_R1KH_RESP 201
-#define FT_PACKET_R0KH_R1KH_PUSH 202
 
-#define FT_R0KH_R1KH_PULL_NONCE_LEN 16
-#define FT_R0KH_R1KH_PULL_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \
-				    WPA_PMK_NAME_LEN + FT_R1KH_ID_LEN + \
-				    ETH_ALEN)
-#define FT_R0KH_R1KH_PULL_PAD_LEN ((8 - FT_R0KH_R1KH_PULL_DATA_LEN % 8) % 8)
+/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r. These
+ * use OUI Extended EtherType as the encapsulating format. */
+#define FT_PACKET_R0KH_R1KH_PULL 0x01
+#define FT_PACKET_R0KH_R1KH_RESP 0x02
+#define FT_PACKET_R0KH_R1KH_PUSH 0x03
+#define FT_PACKET_R0KH_R1KH_SEQ_REQ 0x04
+#define FT_PACKET_R0KH_R1KH_SEQ_RESP 0x05
 
-struct ft_r0kh_r1kh_pull_frame {
-	u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
-	u8 packet_type; /* FT_PACKET_R0KH_R1KH_PULL */
-	le16 data_length; /* little endian length of data (44) */
-	u8 ap_address[ETH_ALEN];
+/* packet layout
+ *  IEEE 802 extended OUI ethertype frame header
+ *  u16 authlen (little endian)
+ *  multiple of struct ft_rrb_tlv (authenticated only, length = authlen)
+ *  multiple of struct ft_rrb_tlv (AES-SIV encrypted, AES-SIV needs an extra
+ *                                 blocksize length)
+ *
+ * AES-SIV AAD;
+ *  source MAC address (6)
+ *  authenticated-only TLVs (authlen)
+ *  subtype (1; FT_PACKET_*)
+ */
 
-	u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
-	u8 pmk_r0_name[WPA_PMK_NAME_LEN];
-	u8 r1kh_id[FT_R1KH_ID_LEN];
-	u8 s1kh_id[ETH_ALEN];
-	u8 pad[FT_R0KH_R1KH_PULL_PAD_LEN]; /* 8-octet boundary for AES block */
-	u8 key_wrap_extra[8];
+#define FT_RRB_NONCE_LEN 16
+
+#define FT_RRB_LAST_EMPTY     0 /* placeholder or padding */
+
+#define FT_RRB_SEQ            1 /* struct ft_rrb_seq */
+#define FT_RRB_NONCE          2 /* size FT_RRB_NONCE_LEN */
+#define FT_RRB_TIMESTAMP      3 /* le32 unix seconds */
+
+#define FT_RRB_R0KH_ID        4 /* FT_R0KH_ID_MAX_LEN */
+#define FT_RRB_R1KH_ID        5 /* FT_R1KH_ID_LEN */
+#define FT_RRB_S1KH_ID        6 /* ETH_ALEN */
+
+#define FT_RRB_PMK_R0_NAME    7 /* WPA_PMK_NAME_LEN */
+#define FT_RRB_PMK_R0         8 /* PMK_LEN */
+#define FT_RRB_PMK_R1_NAME    9 /* WPA_PMK_NAME_LEN */
+#define FT_RRB_PMK_R1        10 /* PMK_LEN */
+
+#define FT_RRB_PAIRWISE      11 /* le16 */
+
+struct ft_rrb_tlv {
+	le16 type;
+	le16 len;
+	/* followed by data of length len */
 } STRUCT_PACKED;
 
-#define FT_R0KH_R1KH_RESP_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \
-				    FT_R1KH_ID_LEN + ETH_ALEN + PMK_LEN + \
-				    WPA_PMK_NAME_LEN + 2)
-#define FT_R0KH_R1KH_RESP_PAD_LEN ((8 - FT_R0KH_R1KH_RESP_DATA_LEN % 8) % 8)
-struct ft_r0kh_r1kh_resp_frame {
-	u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
-	u8 packet_type; /* FT_PACKET_R0KH_R1KH_RESP */
-	le16 data_length; /* little endian length of data (78) */
-	u8 ap_address[ETH_ALEN];
-
-	u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; /* copied from pull */
-	u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */
-	u8 s1kh_id[ETH_ALEN]; /* copied from pull */
-	u8 pmk_r1[PMK_LEN];
-	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
-	le16 pairwise;
-	u8 pad[FT_R0KH_R1KH_RESP_PAD_LEN]; /* 8-octet boundary for AES block */
-	u8 key_wrap_extra[8];
+struct ft_rrb_seq {
+	le32 dom;
+	le32 seq;
+	le32 ts;
 } STRUCT_PACKED;
 
-#define FT_R0KH_R1KH_PUSH_DATA_LEN (4 + FT_R1KH_ID_LEN + ETH_ALEN + \
-				    WPA_PMK_NAME_LEN + PMK_LEN + \
-				    WPA_PMK_NAME_LEN + 2)
-#define FT_R0KH_R1KH_PUSH_PAD_LEN ((8 - FT_R0KH_R1KH_PUSH_DATA_LEN % 8) % 8)
-struct ft_r0kh_r1kh_push_frame {
-	u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
-	u8 packet_type; /* FT_PACKET_R0KH_R1KH_PUSH */
-	le16 data_length; /* little endian length of data (82) */
-	u8 ap_address[ETH_ALEN];
-
-	/* Encrypted with AES key-wrap */
-	u8 timestamp[4]; /* current time in seconds since unix epoch, little
-			  * endian */
-	u8 r1kh_id[FT_R1KH_ID_LEN];
-	u8 s1kh_id[ETH_ALEN];
-	u8 pmk_r0_name[WPA_PMK_NAME_LEN];
-	u8 pmk_r1[PMK_LEN];
-	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
-	le16 pairwise;
-	u8 pad[FT_R0KH_R1KH_PUSH_PAD_LEN]; /* 8-octet boundary for AES block */
-	u8 key_wrap_extra[8];
-} STRUCT_PACKED;
+/* session TLVs:
+ *   required: PMK_R1, PMK_R1_NAME, PAIRWISE
+ *
+ * pull frame TLVs:
+ *   auth:
+ *     required: SEQ, NONCE, R0KH_ID, R1KH_ID
+ *   encrypted:
+ *     required: PMK_R0_NAME, S1KH_ID
+ *
+ * response frame TLVs:
+ *   auth:
+ *     required: SEQ, NONCE, R0KH_ID, R1KH_ID
+ *   encrypted:
+ *     required: S1KH_ID
+ *     optional: session TLVs
+ *
+ * push frame TLVs:
+ *   auth:
+ *     required: SEQ, R0KH_ID, R1KH_ID
+ *   encrypted:
+ *     required: S1KH_ID, PMK_R0_NAME, session TLVs
+ *
+ * sequence number request frame TLVs:
+ *   auth:
+ *     required: R0KH_ID, R1KH_ID, NONCE
+ *
+ * sequence number response frame TLVs:
+ *   auth:
+ *     required: SEQ, NONCE, R0KH_ID, R1KH_ID
+ */
 
 #ifdef _MSC_VER
 #pragma pack(pop)
@@ -116,6 +132,7 @@
 struct wpa_state_machine;
 struct rsn_pmksa_cache_entry;
 struct eapol_state_machine;
+struct ft_remote_seq;
 
 
 struct ft_remote_r0kh {
@@ -123,7 +140,8 @@
 	u8 addr[ETH_ALEN];
 	u8 id[FT_R0KH_ID_MAX_LEN];
 	size_t id_len;
-	u8 key[16];
+	u8 key[32];
+	struct ft_remote_seq *seq;
 };
 
 
@@ -131,7 +149,8 @@
 	struct ft_remote_r1kh *next;
 	u8 addr[ETH_ALEN];
 	u8 id[FT_R1KH_ID_LEN];
-	u8 key[16];
+	u8 key[32];
+	struct ft_remote_seq *seq;
 };
 
 
@@ -146,10 +165,10 @@
 	int wpa_ptk_rekey;
 	u32 wpa_group_update_count;
 	u32 wpa_pairwise_update_count;
+	int wpa_disable_eapol_key_retries;
 	int rsn_pairwise;
 	int rsn_preauth;
 	int eapol_version;
-	int peerkey;
 	int wmm_enabled;
 	int wmm_uapsd;
 	int disable_pmksa_caching;
@@ -167,9 +186,13 @@
 	size_t r0_key_holder_len;
 	u8 r1_key_holder[FT_R1KH_ID_LEN];
 	u32 r0_key_lifetime;
+	int rkh_pos_timeout;
+	int rkh_neg_timeout;
+	int rkh_pull_timeout; /* ms */
+	int rkh_pull_retries;
 	u32 reassociation_deadline;
-	struct ft_remote_r0kh *r0kh_list;
-	struct ft_remote_r1kh *r1kh_list;
+	struct ft_remote_r0kh **r0kh_list;
+	struct ft_remote_r1kh **r1kh_list;
 	int pmk_r1_push;
 	int ft_over_ds;
 	int ft_psk_generate_local;
@@ -187,6 +210,10 @@
 	u8 ip_addr_start[4];
 	u8 ip_addr_end[4];
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_FILS
+	unsigned int fils_cache_id_set:1;
+	u8 fils_cache_id[FILS_CACHE_ID_LEN];
+#endif /* CONFIG_FILS */
 };
 
 typedef enum {
@@ -209,7 +236,7 @@
 			  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);
+			      const u8 *prev_psk, size_t *psk_len);
 	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);
@@ -222,6 +249,8 @@
 						  void *ctx), void *cb_ctx);
 	int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data,
 			  size_t data_len);
+	int (*send_oui)(void *ctx, const u8 *dst, u8 oui_suffix, const u8 *data,
+			size_t data_len);
 #ifdef CONFIG_IEEE80211R_AP
 	struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr);
 	int (*send_ft_action)(void *ctx, const u8 *dst,
@@ -247,13 +276,14 @@
 	WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE,
 	WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL,
 	WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER,
-	WPA_INVALID_MDIE, WPA_INVALID_PROTO
+	WPA_INVALID_MDIE, WPA_INVALID_PROTO, WPA_INVALID_PMKID
 };
 
 int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
 			struct wpa_state_machine *sm,
 			const u8 *wpa_ie, size_t wpa_ie_len,
-			const u8 *mdie, size_t mdie_len);
+			const u8 *mdie, size_t mdie_len,
+			const u8 *owe_dh, size_t owe_dh_len);
 int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
 		      struct wpa_state_machine *sm,
 		      const u8 *osen_ie, size_t osen_ie_len);
@@ -270,7 +300,7 @@
 		 u8 *data, size_t data_len);
 enum wpa_event {
 	WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH,
-	WPA_REAUTH_EAPOL, WPA_ASSOC_FT, WPA_DRV_STA_REMOVED
+	WPA_REAUTH_EAPOL, WPA_ASSOC_FT, WPA_ASSOC_FILS, WPA_DRV_STA_REMOVED
 };
 void wpa_remove_ptk(struct wpa_state_machine *sm);
 int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event);
@@ -284,6 +314,7 @@
 int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm);
 int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm);
 int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm);
+int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm);
 int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
 			     struct rsn_pmksa_cache_entry *entry);
 struct rsn_pmksa_cache_entry *
@@ -300,6 +331,9 @@
 			       struct eapol_state_machine *eapol);
 int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
 			   const u8 *pmk, const u8 *pmkid);
+int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
+			const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+			int session_timeout, int akmp);
 void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
 			   const u8 *sta_addr);
 int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
@@ -315,6 +349,9 @@
 struct rsn_pmksa_cache_entry *
 wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
 		   const u8 *pmkid);
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth,
+				 const u8 *sta_addr, const u8 *pmkid);
 void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
 			      struct wpa_state_machine *sm,
 			      struct wpa_authenticator *wpa_auth,
@@ -338,7 +375,12 @@
 int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len);
 int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
 		  const u8 *data, size_t data_len);
+void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
+		       const u8 *dst_addr, u8 oui_suffix, const u8 *data,
+		       size_t data_len);
 void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr);
+void wpa_ft_deinit(struct wpa_authenticator *wpa_auth);
+void wpa_ft_sta_deinit(struct wpa_state_machine *sm);
 #endif /* CONFIG_IEEE80211R_AP */
 
 void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm);
@@ -359,7 +401,9 @@
 int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id);
 int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id);
 int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
-			 size_t pmk_len, const u8 *snonce, const u8 *anonce);
+			 size_t pmk_len, const u8 *snonce, const u8 *anonce,
+			 const u8 *dhss, size_t dhss_len,
+			 struct wpabuf *g_sta, struct wpabuf *g_ap);
 int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
 		       const struct ieee80211_mgmt *mgmt, size_t frame_len,
 		       u8 *pos, size_t left);
@@ -367,5 +411,31 @@
 		       size_t current_len, size_t max_len,
 		       const struct wpabuf *hlp);
 int fils_set_tk(struct wpa_state_machine *sm);
+u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *eid,
+				    const u8 *fils_session,
+				    struct wpabuf *fils_hlp_resp);
+const u8 *  wpa_fils_validate_fils_session(struct wpa_state_machine *sm,
+					   const u8 *ies, size_t ies_len,
+					   const u8 *fils_session);
+int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies,
+				  size_t ies_len);
+
+int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, u8 *buf, size_t len);
+void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
+				   u8 *fils_anonce, u8 *fils_snonce,
+				   u8 *fils_kek, size_t *fils_kek_len);
+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);
+
+int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
+		       void (*cb)(void *ctx1, void *ctx2),
+		       void *ctx1, void *ctx2);
+int wpa_auth_resend_m3(struct wpa_state_machine *sm,
+		       void (*cb)(void *ctx1, void *ctx2),
+		       void *ctx1, void *ctx2);
+int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
+			     void (*cb)(void *ctx1, void *ctx2),
+			     void *ctx1, void *ctx2);
 
 #endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 210d300..153752d 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -13,6 +13,8 @@
 #include "utils/list.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/random.h"
 #include "ap_config.h"
@@ -24,11 +26,381 @@
 
 #ifdef CONFIG_IEEE80211R_AP
 
+const unsigned int ftRRBseqTimeout = 10;
+const unsigned int ftRRBmaxQueueLen = 100;
+
+
 static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
 				     const u8 *current_ap, const u8 *sta_addr,
 				     u16 status, const u8 *resp_ies,
 				     size_t resp_ies_len);
+static void ft_finish_pull(struct wpa_state_machine *sm);
+static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx);
+static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx);
 
+struct tlv_list {
+	u16 type;
+	size_t len;
+	const u8 *data;
+};
+
+
+/**
+ * wpa_ft_rrb_decrypt - Decrypt FT RRB message
+ * @key: AES-SIV key for AEAD
+ * @key_len: Length of key in octets
+ * @enc: Pointer to encrypted TLVs
+ * @enc_len: Length of encrypted TLVs in octets
+ * @auth: Pointer to authenticated TLVs
+ * @auth_len: Length of authenticated TLVs in octets
+ * @src_addr: MAC address of the frame sender
+ * @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*)
+ * @plain: Pointer to return the pointer to the allocated plaintext buffer;
+ *	needs to be freed by the caller if not NULL;
+ *	will only be returned on success
+ * @plain_len: Pointer to return the length of the allocated plaintext buffer
+ *	in octets
+ * 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 *auth, const size_t auth_len,
+			      const u8 *src_addr, u8 type,
+			      u8 **plain, size_t *plain_size)
+{
+	const u8 *ad[3] = { src_addr, auth, &type };
+	size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) };
+
+	wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypt using key", key, key_len);
+
+	if (!key) { /* skip decryption */
+		*plain = os_memdup(enc, enc_len);
+		if (enc_len > 0 && !*plain)
+			goto err;
+
+		*plain_size = enc_len;
+
+		return 0;
+	}
+
+	*plain = NULL;
+
+	/* SIV overhead */
+	if (enc_len < AES_BLOCK_SIZE)
+		goto err;
+
+	*plain = os_zalloc(enc_len - AES_BLOCK_SIZE);
+	if (!*plain)
+		goto err;
+
+	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",
+			*plain, *plain_size);
+	return 0;
+err:
+	os_free(*plain);
+	*plain = NULL;
+	*plain_size = 0;
+
+	wpa_printf(MSG_ERROR, "FT(RRB): Failed to decrypt");
+
+	return -1;
+}
+
+
+/* get first tlv record in packet matching type
+ * @data (decrypted) packet
+ * @return 0 on success else -1
+ */
+static int wpa_ft_rrb_get_tlv(const u8 *plain, size_t plain_len,
+			      u16 type, size_t *tlv_len, const u8 **tlv_data)
+{
+	const struct ft_rrb_tlv *f;
+	size_t left;
+	le16 type16;
+	size_t len;
+
+	left = plain_len;
+	type16 = host_to_le16(type);
+
+	while (left >= sizeof(*f)) {
+		f = (const struct ft_rrb_tlv *) plain;
+
+		left -= sizeof(*f);
+		plain += sizeof(*f);
+		len = le_to_host16(f->len);
+
+		if (left < len) {
+			wpa_printf(MSG_DEBUG, "FT: RRB message truncated");
+			break;
+		}
+
+		if (f->type == type16) {
+			*tlv_len = len;
+			*tlv_data = plain;
+			return 0;
+		}
+
+		left -= len;
+		plain += len;
+	}
+
+	return -1;
+}
+
+
+static void wpa_ft_rrb_dump(const u8 *plain, const size_t plain_len)
+{
+	const struct ft_rrb_tlv *f;
+	size_t left;
+	size_t len;
+
+	left = plain_len;
+
+	wpa_printf(MSG_DEBUG, "FT: RRB dump message");
+	while (left >= sizeof(*f)) {
+		f = (const struct ft_rrb_tlv *) plain;
+
+		left -= sizeof(*f);
+		plain += sizeof(*f);
+		len = le_to_host16(f->len);
+
+		wpa_printf(MSG_DEBUG, "FT: RRB TLV type = %d, len = %zu",
+			   le_to_host16(f->type), len);
+
+		if (left < len) {
+			wpa_printf(MSG_DEBUG,
+				   "FT: RRB message truncated: left %zu bytes, need %zu",
+				   left, len);
+			break;
+		}
+
+		wpa_hexdump(MSG_DEBUG, "FT: RRB TLV data", plain, len);
+
+		left -= len;
+		plain += len;
+	}
+
+	if (left > 0)
+		wpa_hexdump(MSG_DEBUG, "FT: RRB TLV padding", plain, left);
+
+	wpa_printf(MSG_DEBUG, "FT: RRB dump message end");
+}
+
+
+static size_t wpa_ft_tlv_len(const struct tlv_list *tlvs)
+{
+	size_t tlv_len = 0;
+	int i;
+
+	if (!tlvs)
+		return 0;
+
+	for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) {
+		tlv_len += sizeof(struct ft_rrb_tlv);
+		tlv_len += tlvs[i].len;
+	}
+
+	return tlv_len;
+}
+
+
+static size_t wpa_ft_tlv_lin(const struct tlv_list *tlvs, u8 *start,
+			     u8 *endpos)
+{
+	int i;
+	size_t tlv_len;
+	struct ft_rrb_tlv *hdr;
+	u8 *pos;
+
+	if (!tlvs)
+		return 0;
+
+	tlv_len = 0;
+	pos = start;
+	for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) {
+		if (tlv_len + sizeof(*hdr) > (size_t) (endpos - start))
+			return tlv_len;
+		tlv_len += sizeof(*hdr);
+		hdr = (struct ft_rrb_tlv *) pos;
+		hdr->type = host_to_le16(tlvs[i].type);
+		hdr->len = host_to_le16(tlvs[i].len);
+		pos = start + tlv_len;
+
+		if (tlv_len + tlvs[i].len > (size_t) (endpos - start))
+			return tlv_len;
+		tlv_len += tlvs[i].len;
+		os_memcpy(pos, tlvs[i].data, tlvs[i].len);
+		pos = start + tlv_len;
+	}
+
+	return tlv_len;
+}
+
+
+static int wpa_ft_rrb_lin(const struct tlv_list *tlvs1,
+			  const struct tlv_list *tlvs2,
+			  u8 **plain, size_t *plain_len)
+{
+	u8 *pos, *endpos;
+	size_t tlv_len;
+
+	tlv_len = wpa_ft_tlv_len(tlvs1);
+	tlv_len += wpa_ft_tlv_len(tlvs2);
+
+	*plain_len = tlv_len;
+	*plain = os_zalloc(tlv_len);
+	if (!*plain) {
+		wpa_printf(MSG_ERROR, "FT: Failed to allocate plaintext");
+		goto err;
+	}
+
+	pos = *plain;
+	endpos = *plain + tlv_len;
+	pos += wpa_ft_tlv_lin(tlvs1, pos, endpos);
+	pos += wpa_ft_tlv_lin(tlvs2, pos, endpos);
+
+	/* sanity check */
+	if (pos != endpos) {
+		wpa_printf(MSG_ERROR, "FT: Length error building RRB");
+		goto err;
+	}
+
+	return 0;
+
+err:
+	os_free(*plain);
+	*plain = NULL;
+	*plain_len = 0;
+	return -1;
+}
+
+
+static int wpa_ft_rrb_encrypt(const u8 *key, const size_t key_len,
+			      const u8 *plain, const size_t plain_len,
+			      const u8 *auth, const size_t auth_len,
+			      const u8 *src_addr, u8 type, u8 *enc)
+{
+	const u8 *ad[3] = { src_addr, auth, &type };
+	size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(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);
+
+	if (!key) {
+		/* encryption not needed, return plaintext as packet */
+		os_memcpy(enc, plain, plain_len);
+	} else if (aes_siv_encrypt(key, key_len, plain, plain_len,
+				   3, ad, ad_len, enc) < 0) {
+		wpa_printf(MSG_ERROR, "FT: Failed to encrypt RRB-OUI message");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * wpa_ft_rrb_build - Build and encrypt an FT RRB message
+ * @key: AES-SIV key for AEAD
+ * @key_len: Length of key in octets
+ * @tlvs_enc0: First set of to-be-encrypted TLVs
+ * @tlvs_enc1: Second set of to-be-encrypted TLVs
+ * @tlvs_auth: Set of to-be-authenticated TLVs
+ * @src_addr: MAC address of the frame sender
+ * @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*)
+ * @packet Pointer to return the pointer to the allocated packet buffer;
+ *         needs to be freed by the caller if not null;
+ *         will only be returned on success
+ * @packet_len: Pointer to return the length of the allocated buffer in octets
+ * Returns: 0 on success, -1 on error
+ */
+static int wpa_ft_rrb_build(const u8 *key, const size_t key_len,
+			    const struct tlv_list *tlvs_enc0,
+			    const struct tlv_list *tlvs_enc1,
+			    const struct tlv_list *tlvs_auth,
+			    const u8 *src_addr, u8 type,
+			    u8 **packet, size_t *packet_len)
+{
+	u8 *plain = NULL, *auth = NULL, *pos;
+	size_t plain_len = 0, auth_len = 0;
+	int ret = -1;
+
+	if (wpa_ft_rrb_lin(tlvs_enc0, tlvs_enc1, &plain, &plain_len) < 0)
+		goto out;
+
+	if (wpa_ft_rrb_lin(tlvs_auth, NULL, &auth, &auth_len) < 0)
+		goto out;
+
+	*packet_len = sizeof(u16) + auth_len + plain_len;
+	if (key)
+		*packet_len += AES_BLOCK_SIZE;
+	*packet = os_zalloc(*packet_len);
+	if (!*packet)
+		goto out;
+
+	pos = *packet;
+	WPA_PUT_LE16(pos, auth_len);
+	pos += 2;
+	os_memcpy(pos, auth, auth_len);
+	pos += auth_len;
+	if (wpa_ft_rrb_encrypt(key, key_len, plain, plain_len, auth,
+			       auth_len, src_addr, type, pos) < 0)
+		goto out;
+
+	ret = 0;
+
+out:
+	bin_clear_free(plain, plain_len);
+	os_free(auth);
+
+	if (ret) {
+		wpa_printf(MSG_ERROR, "FT: Failed to build RRB-OUI message");
+		os_free(*packet);
+		*packet = NULL;
+		*packet_len = 0;
+	}
+
+	return ret;
+}
+
+
+#define RRB_GET_SRC(srcfield, type, field, txt, checklength) do { \
+	if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \
+				&f_##field##_len, &f_##field) < 0 || \
+	    (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \
+		wpa_printf(MSG_INFO, "FT: Missing required " #field \
+			   " in %s from " MACSTR, txt, MAC2STR(src_addr)); \
+		wpa_ft_rrb_dump(srcfield, srcfield##_len); \
+		goto out; \
+	} \
+} while (0)
+
+#define RRB_GET(type, field, txt, checklength) \
+	RRB_GET_SRC(plain, type, field, txt, checklength)
+#define RRB_GET_AUTH(type, field, txt, checklength) \
+	RRB_GET_SRC(auth, type, field, txt, checklength)
+
+#define RRB_GET_OPTIONAL_SRC(srcfield, type, field, txt, checklength) do { \
+	if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \
+				&f_##field##_len, &f_##field) < 0 || \
+	    (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \
+		wpa_printf(MSG_DEBUG, "FT: Missing optional " #field \
+			   " in %s from " MACSTR, txt, MAC2STR(src_addr)); \
+		f_##field##_len = 0; \
+		f_##field = NULL; \
+	} \
+} while (0)
+
+#define RRB_GET_OPTIONAL(type, field, txt, checklength) \
+	RRB_GET_OPTIONAL_SRC(plain, type, field, txt, checklength)
+#define RRB_GET_OPTIONAL_AUTH(type, field, txt, checklength) \
+	RRB_GET_OPTIONAL_SRC(auth, type, field, txt, checklength)
 
 static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst,
 			   const u8 *data, size_t data_len)
@@ -41,6 +413,19 @@
 }
 
 
+static int wpa_ft_rrb_oui_send(struct wpa_authenticator *wpa_auth,
+			       const u8 *dst, u8 oui_suffix,
+			       const u8 *data, size_t data_len)
+{
+	if (!wpa_auth->cb->send_oui)
+		return -1;
+	wpa_printf(MSG_DEBUG, "FT: RRB-OUI type %u send to " MACSTR,
+		   oui_suffix, MAC2STR(dst));
+	return wpa_auth->cb->send_oui(wpa_auth->cb_ctx, dst, oui_suffix, data,
+				      data_len);
+}
+
+
 static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth,
 			      const u8 *dst, const u8 *data, size_t data_len)
 {
@@ -58,7 +443,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);
+				     prev_psk, NULL);
 }
 
 
@@ -153,6 +538,301 @@
 }
 
 
+/* A packet to be handled after seq response */
+struct ft_remote_item {
+	struct dl_list list;
+
+	u8 nonce[FT_RRB_NONCE_LEN];
+	struct os_reltime nonce_ts;
+
+	u8 src_addr[ETH_ALEN];
+	u8 *enc;
+	size_t enc_len;
+	u8 *auth;
+	size_t auth_len;
+	int (*cb)(struct wpa_authenticator *wpa_auth,
+		  const u8 *src_addr,
+		  const u8 *enc, size_t enc_len,
+		  const u8 *auth, size_t auth_len,
+		  int no_defer);
+};
+
+
+static void wpa_ft_rrb_seq_free(struct ft_remote_item *item)
+{
+	eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, ELOOP_ALL_CTX, item);
+	dl_list_del(&item->list);
+	bin_clear_free(item->enc, item->enc_len);
+	os_free(item->auth);
+	os_free(item);
+}
+
+
+static void wpa_ft_rrb_seq_flush(struct wpa_authenticator *wpa_auth,
+				 struct ft_remote_seq *rkh_seq, int cb)
+{
+	struct ft_remote_item *item, *n;
+
+	dl_list_for_each_safe(item, n, &rkh_seq->rx.queue,
+			      struct ft_remote_item, list) {
+		if (cb && item->cb)
+			item->cb(wpa_auth, item->src_addr, item->enc,
+				 item->enc_len, item->auth, item->auth_len, 1);
+		wpa_ft_rrb_seq_free(item);
+	}
+}
+
+
+static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct ft_remote_item *item = timeout_ctx;
+
+	wpa_ft_rrb_seq_free(item);
+}
+
+
+static int
+wpa_ft_rrb_seq_req(struct wpa_authenticator *wpa_auth,
+		   struct ft_remote_seq *rkh_seq, const u8 *src_addr,
+		   const u8 *f_r0kh_id, size_t f_r0kh_id_len,
+		   const u8 *f_r1kh_id, const u8 *key, size_t key_len,
+		   const u8 *enc, size_t enc_len,
+		   const u8 *auth, size_t auth_len,
+		   int (*cb)(struct wpa_authenticator *wpa_auth,
+			     const u8 *src_addr,
+			     const u8 *enc, size_t enc_len,
+			     const u8 *auth, size_t auth_len,
+			     int no_defer))
+{
+	struct ft_remote_item *item = NULL;
+	u8 *packet = NULL;
+	size_t packet_len;
+	struct tlv_list seq_req_auth[] = {
+		{ .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN,
+		  .data = NULL /* to be filled: item->nonce */ },
+		{ .type = FT_RRB_R0KH_ID, .len = f_r0kh_id_len,
+		  .data = f_r0kh_id },
+		{ .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
+		  .data = f_r1kh_id },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
+
+	if (dl_list_len(&rkh_seq->rx.queue) >= ftRRBmaxQueueLen) {
+		wpa_printf(MSG_DEBUG, "FT: Sequence number queue too long");
+		goto err;
+	}
+
+	item = os_zalloc(sizeof(*item));
+	if (!item)
+		goto err;
+
+	os_memcpy(item->src_addr, src_addr, ETH_ALEN);
+	item->cb = cb;
+
+	if (random_get_bytes(item->nonce, FT_RRB_NONCE_LEN) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Seq num nonce: out of random bytes");
+		goto err;
+	}
+
+	if (os_get_reltime(&item->nonce_ts) < 0)
+		goto err;
+
+	if (enc && enc_len > 0) {
+		item->enc = os_memdup(enc, enc_len);
+		item->enc_len = enc_len;
+		if (!item->enc)
+			goto err;
+	}
+
+	if (auth && auth_len > 0) {
+		item->auth = os_memdup(auth, auth_len);
+		item->auth_len = auth_len;
+		if (!item->auth)
+			goto err;
+	}
+
+	eloop_register_timeout(ftRRBseqTimeout, 0, wpa_ft_rrb_seq_timeout,
+			       wpa_auth, item);
+
+	seq_req_auth[0].data = item->nonce;
+
+	if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_req_auth,
+			     wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
+			     &packet, &packet_len) < 0) {
+		item = NULL; /* some other seq resp might still accept this */
+		goto err;
+	}
+
+	dl_list_add(&rkh_seq->rx.queue, &item->list);
+
+	wpa_ft_rrb_oui_send(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
+			    packet, packet_len);
+
+	os_free(packet);
+
+	return 0;
+err:
+	wpa_printf(MSG_DEBUG, "FT: Failed to send sequence number request");
+	if (item) {
+		os_free(item->auth);
+		bin_clear_free(item->enc, item->enc_len);
+		os_free(item);
+	}
+
+	return -1;
+}
+
+
+#define FT_RRB_SEQ_OK    0
+#define FT_RRB_SEQ_DROP  1
+#define FT_RRB_SEQ_DEFER 2
+
+static int
+wpa_ft_rrb_seq_chk(struct ft_remote_seq *rkh_seq, const u8 *src_addr,
+		   const u8 *enc, size_t enc_len,
+		   const u8 *auth, size_t auth_len,
+		   const char *msgtype, int no_defer)
+{
+	const u8 *f_seq;
+	size_t f_seq_len;
+	const struct ft_rrb_seq *msg_both;
+	u32 msg_seq, msg_off, rkh_off;
+	struct os_reltime now;
+	unsigned int i;
+
+	RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both));
+	wpa_hexdump(MSG_DEBUG, "FT: sequence number", f_seq, f_seq_len);
+	msg_both = (const struct ft_rrb_seq *) f_seq;
+
+	if (rkh_seq->rx.num_last == 0) {
+		/* first packet from remote */
+		goto defer;
+	}
+
+	if (le_to_host32(msg_both->dom) != rkh_seq->rx.dom) {
+		/* remote might have rebooted */
+		goto defer;
+	}
+
+	if (os_get_reltime(&now) == 0) {
+		u32 msg_ts_now_remote, msg_ts_off;
+		struct os_reltime now_remote;
+
+		os_reltime_sub(&now, &rkh_seq->rx.time_offset, &now_remote);
+		msg_ts_now_remote = now_remote.sec;
+		msg_ts_off = le_to_host32(msg_both->ts) -
+			(msg_ts_now_remote - ftRRBseqTimeout);
+		if (msg_ts_off > 2 * ftRRBseqTimeout)
+			goto defer;
+	}
+
+	msg_seq = le_to_host32(msg_both->seq);
+	rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx];
+	msg_off = msg_seq - rkh_off;
+	if (msg_off > 0xC0000000)
+		goto out; /* too old message, drop it */
+
+	if (msg_off <= 0x40000000) {
+		for (i = 0; i < rkh_seq->rx.num_last; i++) {
+			if (rkh_seq->rx.last[i] == msg_seq)
+				goto out; /* duplicate message, drop it */
+		}
+
+		return FT_RRB_SEQ_OK;
+	}
+
+defer:
+	if (no_defer)
+		goto out;
+
+	wpa_printf(MSG_DEBUG, "FT: Possibly invalid sequence number in %s from "
+		   MACSTR, msgtype, MAC2STR(src_addr));
+
+	return FT_RRB_SEQ_DEFER;
+out:
+	wpa_printf(MSG_DEBUG, "FT: Invalid sequence number in %s from " MACSTR,
+		   msgtype, MAC2STR(src_addr));
+
+	return FT_RRB_SEQ_DROP;
+}
+
+
+static void
+wpa_ft_rrb_seq_accept(struct wpa_authenticator *wpa_auth,
+		      struct ft_remote_seq *rkh_seq, const u8 *src_addr,
+		      const u8 *auth, size_t auth_len,
+		      const char *msgtype)
+{
+	const u8 *f_seq;
+	size_t f_seq_len;
+	const struct ft_rrb_seq *msg_both;
+	u32 msg_seq, msg_off, min_off, rkh_off;
+	int minidx = 0;
+	unsigned int i;
+
+	RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both));
+	msg_both = (const struct ft_rrb_seq *) f_seq;
+
+	msg_seq = le_to_host32(msg_both->seq);
+
+	if (rkh_seq->rx.num_last < FT_REMOTE_SEQ_BACKLOG) {
+		rkh_seq->rx.last[rkh_seq->rx.num_last] = msg_seq;
+		rkh_seq->rx.num_last++;
+		return;
+	}
+
+	rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx];
+	for (i = 0; i < rkh_seq->rx.num_last; i++) {
+		msg_off = rkh_seq->rx.last[i] - rkh_off;
+		min_off = rkh_seq->rx.last[minidx] - rkh_off;
+		if (msg_off < min_off && i != rkh_seq->rx.offsetidx)
+			minidx = i;
+	}
+	rkh_seq->rx.last[rkh_seq->rx.offsetidx] = msg_seq;
+	rkh_seq->rx.offsetidx = minidx;
+
+	return;
+out:
+	/* RRB_GET_AUTH should never fail here as
+	 * wpa_ft_rrb_seq_chk() verified FT_RRB_SEQ presence. */
+	wpa_printf(MSG_ERROR, "FT: %s() failed", __func__);
+}
+
+
+static int wpa_ft_new_seq(struct ft_remote_seq *rkh_seq,
+			  struct ft_rrb_seq *f_seq)
+{
+	struct os_reltime now;
+
+	if (os_get_reltime(&now) < 0)
+		return -1;
+
+	if (!rkh_seq->tx.dom) {
+		if (random_get_bytes((u8 *) &rkh_seq->tx.seq,
+				     sizeof(rkh_seq->tx.seq))) {
+			wpa_printf(MSG_ERROR,
+				   "FT: Failed to get random data for sequence number initialization");
+			rkh_seq->tx.seq = now.usec;
+		}
+		if (random_get_bytes((u8 *) &rkh_seq->tx.dom,
+				     sizeof(rkh_seq->tx.dom))) {
+			wpa_printf(MSG_ERROR,
+				   "FT: Failed to get random data for sequence number initialization");
+			rkh_seq->tx.dom = now.usec;
+		}
+		rkh_seq->tx.dom |= 1;
+	}
+
+	f_seq->dom = host_to_le32(rkh_seq->tx.dom);
+	f_seq->seq = host_to_le32(rkh_seq->tx.seq);
+	f_seq->ts = host_to_le32(now.sec);
+
+	rkh_seq->tx.seq++;
+
+	return 0;
+}
+
+
 struct wpa_ft_pmk_r0_sa {
 	struct wpa_ft_pmk_r0_sa *next;
 	u8 pmk_r0[PMK_LEN];
@@ -212,9 +892,9 @@
 }
 
 
-static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
-			       const u8 *spa, const u8 *pmk_r0,
-			       const u8 *pmk_r0_name, int pairwise)
+int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
+			const u8 *spa, const u8 *pmk_r0,
+			const u8 *pmk_r0_name, int pairwise)
 {
 	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
 	struct wpa_ft_pmk_r0_sa *r0;
@@ -239,7 +919,7 @@
 
 static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth,
 			       const u8 *spa, const u8 *pmk_r0_name,
-			       u8 *pmk_r0, int *pairwise)
+			       const struct wpa_ft_pmk_r0_sa **r0_out)
 {
 	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
 	struct wpa_ft_pmk_r0_sa *r0;
@@ -249,15 +929,14 @@
 		if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 &&
 		    os_memcmp_const(r0->pmk_r0_name, pmk_r0_name,
 				    WPA_PMK_NAME_LEN) == 0) {
-			os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN);
-			if (pairwise)
-				*pairwise = r0->pairwise;
+			*r0_out = r0;
 			return 0;
 		}
 
 		r0 = r0->next;
 	}
 
+	*r0_out = NULL;
 	return -1;
 }
 
@@ -312,61 +991,522 @@
 }
 
 
+static int wpa_ft_rrb_init_r0kh_seq(struct ft_remote_r0kh *r0kh)
+{
+	if (r0kh->seq)
+		return 0;
+
+	r0kh->seq = os_zalloc(sizeof(*r0kh->seq));
+	if (!r0kh->seq) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to allocate r0kh->seq");
+		return -1;
+	}
+
+	dl_list_init(&r0kh->seq->rx.queue);
+
+	return 0;
+}
+
+
+static void wpa_ft_rrb_lookup_r0kh(struct wpa_authenticator *wpa_auth,
+				   const u8 *f_r0kh_id, size_t f_r0kh_id_len,
+				   struct ft_remote_r0kh **r0kh_out,
+				   struct ft_remote_r0kh **r0kh_wildcard)
+{
+	struct ft_remote_r0kh *r0kh;
+
+	*r0kh_wildcard = NULL;
+	*r0kh_out = NULL;
+
+	if (wpa_auth->conf.r0kh_list)
+		r0kh = *wpa_auth->conf.r0kh_list;
+	else
+		r0kh = NULL;
+	for (; r0kh; r0kh = r0kh->next) {
+		if (r0kh->id_len == 1 && r0kh->id[0] == '*')
+			*r0kh_wildcard = r0kh;
+		if (f_r0kh_id && r0kh->id_len == f_r0kh_id_len &&
+		    os_memcmp_const(f_r0kh_id, r0kh->id, f_r0kh_id_len) == 0)
+			*r0kh_out = r0kh;
+	}
+
+	if (!*r0kh_out && !*r0kh_wildcard)
+		wpa_printf(MSG_DEBUG, "FT: No matching R0KH found");
+
+	if (*r0kh_out && wpa_ft_rrb_init_r0kh_seq(*r0kh_out) < 0)
+		*r0kh_out = NULL;
+}
+
+
+static int wpa_ft_rrb_init_r1kh_seq(struct ft_remote_r1kh *r1kh)
+{
+	if (r1kh->seq)
+		return 0;
+
+	r1kh->seq = os_zalloc(sizeof(*r1kh->seq));
+	if (!r1kh->seq) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to allocate r1kh->seq");
+		return -1;
+	}
+
+	dl_list_init(&r1kh->seq->rx.queue);
+
+	return 0;
+}
+
+
+static void wpa_ft_rrb_lookup_r1kh(struct wpa_authenticator *wpa_auth,
+				   const u8 *f_r1kh_id,
+				   struct ft_remote_r1kh **r1kh_out,
+				   struct ft_remote_r1kh **r1kh_wildcard)
+{
+	struct ft_remote_r1kh *r1kh;
+
+	*r1kh_wildcard = NULL;
+	*r1kh_out = NULL;
+
+	if (wpa_auth->conf.r1kh_list)
+		r1kh = *wpa_auth->conf.r1kh_list;
+	else
+		r1kh = NULL;
+	for (; r1kh; r1kh = r1kh->next) {
+		if (is_zero_ether_addr(r1kh->addr) &&
+		    is_zero_ether_addr(r1kh->id))
+			*r1kh_wildcard = r1kh;
+		if (f_r1kh_id &&
+		    os_memcmp_const(r1kh->id, f_r1kh_id, FT_R1KH_ID_LEN) == 0)
+			*r1kh_out = r1kh;
+	}
+
+	if (!*r1kh_out && !*r1kh_wildcard)
+		wpa_printf(MSG_DEBUG, "FT: No matching R1KH found");
+
+	if (*r1kh_out && wpa_ft_rrb_init_r1kh_seq(*r1kh_out) < 0)
+		*r1kh_out = NULL;
+}
+
+
+static int wpa_ft_rrb_check_r0kh(struct wpa_authenticator *wpa_auth,
+				 const u8 *f_r0kh_id, size_t f_r0kh_id_len)
+{
+	if (f_r0kh_id_len != wpa_auth->conf.r0_key_holder_len ||
+	    os_memcmp_const(f_r0kh_id, wpa_auth->conf.r0_key_holder,
+			    f_r0kh_id_len) != 0)
+		return -1;
+
+	return 0;
+}
+
+
+static int wpa_ft_rrb_check_r1kh(struct wpa_authenticator *wpa_auth,
+				 const u8 *f_r1kh_id)
+{
+	if (os_memcmp_const(f_r1kh_id, wpa_auth->conf.r1_key_holder,
+			    FT_R1KH_ID_LEN) != 0)
+		return -1;
+
+	return 0;
+}
+
+
+static void wpa_ft_rrb_del_r0kh(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_authenticator *wpa_auth = eloop_ctx;
+	struct ft_remote_r0kh *r0kh, *prev = NULL;
+
+	if (!wpa_auth->conf.r0kh_list)
+		return;
+
+	for (r0kh = *wpa_auth->conf.r0kh_list; r0kh; r0kh = r0kh->next) {
+		if (r0kh == timeout_ctx)
+			break;
+		prev = r0kh;
+	}
+	if (!r0kh)
+		return;
+	if (prev)
+		prev->next = r0kh->next;
+	else
+		*wpa_auth->conf.r0kh_list = r0kh->next;
+	if (r0kh->seq)
+		wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0);
+	os_free(r0kh->seq);
+	os_free(r0kh);
+}
+
+
+static void wpa_ft_rrb_r0kh_replenish(struct wpa_authenticator *wpa_auth,
+				      struct ft_remote_r0kh *r0kh, int timeout)
+{
+	if (timeout > 0)
+		eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
+					wpa_auth, r0kh);
+}
+
+
+static void wpa_ft_rrb_r0kh_timeout(struct wpa_authenticator *wpa_auth,
+				    struct ft_remote_r0kh *r0kh, int timeout)
+{
+	eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth, r0kh);
+
+	if (timeout > 0)
+		eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
+				       wpa_auth, r0kh);
+}
+
+
+static struct ft_remote_r0kh *
+wpa_ft_rrb_add_r0kh(struct wpa_authenticator *wpa_auth,
+		    struct ft_remote_r0kh *r0kh_wildcard,
+		    const u8 *src_addr, const u8 *r0kh_id, size_t id_len,
+		    int timeout)
+{
+	struct ft_remote_r0kh *r0kh;
+
+	if (!wpa_auth->conf.r0kh_list)
+		return NULL;
+
+	r0kh = os_zalloc(sizeof(*r0kh));
+	if (!r0kh)
+		return NULL;
+
+	if (src_addr)
+		os_memcpy(r0kh->addr, src_addr, sizeof(r0kh->addr));
+
+	if (id_len > FT_R0KH_ID_MAX_LEN)
+		id_len = FT_R0KH_ID_MAX_LEN;
+	os_memcpy(r0kh->id, r0kh_id, id_len);
+	r0kh->id_len = id_len;
+
+	os_memcpy(r0kh->key, r0kh_wildcard->key, sizeof(r0kh->key));
+
+	r0kh->next = *wpa_auth->conf.r0kh_list;
+	*wpa_auth->conf.r0kh_list = r0kh;
+
+	if (timeout > 0)
+		eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
+				       wpa_auth, r0kh);
+
+	if (wpa_ft_rrb_init_r0kh_seq(r0kh) < 0)
+		return NULL;
+
+	return r0kh;
+}
+
+
+static void wpa_ft_rrb_del_r1kh(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_authenticator *wpa_auth = eloop_ctx;
+	struct ft_remote_r1kh *r1kh, *prev = NULL;
+
+	if (!wpa_auth->conf.r1kh_list)
+		return;
+
+	for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) {
+		if (r1kh == timeout_ctx)
+			break;
+		prev = r1kh;
+	}
+	if (!r1kh)
+		return;
+	if (prev)
+		prev->next = r1kh->next;
+	else
+		*wpa_auth->conf.r1kh_list = r1kh->next;
+	if (r1kh->seq)
+		wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0);
+	os_free(r1kh->seq);
+	os_free(r1kh);
+}
+
+
+static void wpa_ft_rrb_r1kh_replenish(struct wpa_authenticator *wpa_auth,
+				      struct ft_remote_r1kh *r1kh, int timeout)
+{
+	if (timeout > 0)
+		eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r1kh,
+					wpa_auth, r1kh);
+}
+
+
+static struct ft_remote_r1kh *
+wpa_ft_rrb_add_r1kh(struct wpa_authenticator *wpa_auth,
+		    struct ft_remote_r1kh *r1kh_wildcard,
+		    const u8 *src_addr, const u8 *r1kh_id, int timeout)
+{
+	struct ft_remote_r1kh *r1kh;
+
+	if (!wpa_auth->conf.r1kh_list)
+		return NULL;
+
+	r1kh = os_zalloc(sizeof(*r1kh));
+	if (!r1kh)
+		return NULL;
+
+	os_memcpy(r1kh->addr, src_addr, sizeof(r1kh->addr));
+	os_memcpy(r1kh->id, r1kh_id, sizeof(r1kh->id));
+	os_memcpy(r1kh->key, r1kh_wildcard->key, sizeof(r1kh->key));
+	r1kh->next = *wpa_auth->conf.r1kh_list;
+	*wpa_auth->conf.r1kh_list = r1kh;
+
+	if (timeout > 0)
+		eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r1kh,
+				       wpa_auth, r1kh);
+
+	if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0)
+		return NULL;
+
+	return r1kh;
+}
+
+
+void wpa_ft_sta_deinit(struct wpa_state_machine *sm)
+{
+	eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL);
+}
+
+
+static void wpa_ft_deinit_seq(struct wpa_authenticator *wpa_auth)
+{
+	struct ft_remote_r0kh *r0kh;
+	struct ft_remote_r1kh *r1kh;
+
+	eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, wpa_auth, ELOOP_ALL_CTX);
+
+	if (wpa_auth->conf.r0kh_list)
+		r0kh = *wpa_auth->conf.r0kh_list;
+	else
+		r0kh = NULL;
+	for (; r0kh; r0kh = r0kh->next) {
+		if (!r0kh->seq)
+			continue;
+		wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0);
+		os_free(r0kh->seq);
+		r0kh->seq = NULL;
+	}
+
+	if (wpa_auth->conf.r1kh_list)
+		r1kh = *wpa_auth->conf.r1kh_list;
+	else
+		r1kh = NULL;
+	for (; r1kh; r1kh = r1kh->next) {
+		if (!r1kh->seq)
+			continue;
+		wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0);
+		os_free(r1kh->seq);
+		r1kh->seq = NULL;
+	}
+}
+
+
+static void wpa_ft_deinit_rkh_tmp(struct wpa_authenticator *wpa_auth)
+{
+	struct ft_remote_r0kh *r0kh, *r0kh_next, *r0kh_prev = NULL;
+	struct ft_remote_r1kh *r1kh, *r1kh_next, *r1kh_prev = NULL;
+
+	if (wpa_auth->conf.r0kh_list)
+		r0kh = *wpa_auth->conf.r0kh_list;
+	else
+		r0kh = NULL;
+	while (r0kh) {
+		r0kh_next = r0kh->next;
+		if (eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth,
+					 r0kh) > 0) {
+			if (r0kh_prev)
+				r0kh_prev->next = r0kh_next;
+			else
+				*wpa_auth->conf.r0kh_list = r0kh_next;
+			os_free(r0kh);
+		} else {
+			r0kh_prev = r0kh;
+		}
+		r0kh = r0kh_next;
+	}
+
+	if (wpa_auth->conf.r1kh_list)
+		r1kh = *wpa_auth->conf.r1kh_list;
+	else
+		r1kh = NULL;
+	while (r1kh) {
+		r1kh_next = r1kh->next;
+		if (eloop_cancel_timeout(wpa_ft_rrb_del_r1kh, wpa_auth,
+					 r1kh) > 0) {
+			if (r1kh_prev)
+				r1kh_prev->next = r1kh_next;
+			else
+				*wpa_auth->conf.r1kh_list = r1kh_next;
+			os_free(r1kh);
+		} else {
+			r1kh_prev = r1kh;
+		}
+		r1kh = r1kh_next;
+	}
+}
+
+
+void wpa_ft_deinit(struct wpa_authenticator *wpa_auth)
+{
+	wpa_ft_deinit_seq(wpa_auth);
+	wpa_ft_deinit_rkh_tmp(wpa_auth);
+}
+
+
+static void wpa_ft_block_r0kh(struct wpa_authenticator *wpa_auth,
+			      const u8 *f_r0kh_id, size_t f_r0kh_id_len)
+{
+	struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
+
+	if (!wpa_auth->conf.rkh_neg_timeout)
+		return;
+
+	wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len,
+			       &r0kh, &r0kh_wildcard);
+
+	if (!r0kh_wildcard) {
+		/* r0kh removed after neg_timeout and might need re-adding */
+		return;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "FT: Blacklist R0KH-ID",
+		    f_r0kh_id, f_r0kh_id_len);
+
+	if (r0kh) {
+		wpa_ft_rrb_r0kh_timeout(wpa_auth, r0kh,
+					wpa_auth->conf.rkh_neg_timeout);
+		os_memset(r0kh->addr, 0, ETH_ALEN);
+	} else
+		wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, NULL, f_r0kh_id,
+				    f_r0kh_id_len,
+				    wpa_auth->conf.rkh_neg_timeout);
+}
+
+
+static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_state_machine *sm = eloop_ctx;
+
+	wpa_printf(MSG_DEBUG, "FT: Timeout pending pull request for " MACSTR,
+		   MAC2STR(sm->addr));
+	if (sm->ft_pending_pull_left_retries <= 0)
+		wpa_ft_block_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len);
+
+	/* cancel multiple timeouts */
+	eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL);
+	ft_finish_pull(sm);
+}
+
+
 static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
 			      const u8 *ies, size_t ies_len,
 			      const u8 *pmk_r0_name)
 {
-	struct ft_remote_r0kh *r0kh;
-	struct ft_r0kh_r1kh_pull_frame frame, f;
+	struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
+	u8 *packet = NULL;
+	const u8 *key, *f_r1kh_id = sm->wpa_auth->conf.r1_key_holder;
+	size_t packet_len, key_len;
+	struct ft_rrb_seq f_seq;
+	int tsecs, tusecs, first;
+	struct wpabuf *ft_pending_req_ies;
+	int r0kh_timeout;
+	struct tlv_list req_enc[] = {
+		{ .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN,
+		  .data = pmk_r0_name },
+		{ .type = FT_RRB_S1KH_ID, .len = ETH_ALEN,
+		  .data = sm->addr },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
+	struct tlv_list req_auth[] = {
+		{ .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN,
+		  .data = sm->ft_pending_pull_nonce },
+		{ .type = FT_RRB_SEQ, .len = sizeof(f_seq),
+		  .data = (u8 *) &f_seq },
+		{ .type = FT_RRB_R0KH_ID, .len = sm->r0kh_id_len,
+		  .data = sm->r0kh_id },
+		{ .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
+		  .data = f_r1kh_id },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
 
-	r0kh = sm->wpa_auth->conf.r0kh_list;
-	while (r0kh) {
-		if (r0kh->id_len == sm->r0kh_id_len &&
-		    os_memcmp_const(r0kh->id, sm->r0kh_id, sm->r0kh_id_len) ==
-		    0)
-			break;
-		r0kh = r0kh->next;
+	if (sm->ft_pending_pull_left_retries <= 0)
+		return -1;
+	first = sm->ft_pending_pull_left_retries ==
+		sm->wpa_auth->conf.rkh_pull_retries;
+	sm->ft_pending_pull_left_retries--;
+
+	wpa_ft_rrb_lookup_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len,
+			       &r0kh, &r0kh_wildcard);
+
+	/* Keep r0kh sufficiently long in the list for seq num check */
+	r0kh_timeout = sm->wpa_auth->conf.rkh_pull_timeout / 1000 +
+		1 + ftRRBseqTimeout;
+	if (r0kh) {
+		wpa_ft_rrb_r0kh_replenish(sm->wpa_auth, r0kh, r0kh_timeout);
+	} else if (r0kh_wildcard) {
+		wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID");
+		/* r0kh->addr: updated by SEQ_RESP and wpa_ft_expire_pull */
+		r0kh = wpa_ft_rrb_add_r0kh(sm->wpa_auth, r0kh_wildcard,
+					   r0kh_wildcard->addr,
+					   sm->r0kh_id, sm->r0kh_id_len,
+					   r0kh_timeout);
 	}
 	if (r0kh == NULL) {
 		wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
 			    sm->r0kh_id, sm->r0kh_id_len);
 		return -1;
 	}
+	if (is_zero_ether_addr(r0kh->addr)) {
+		wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID is blacklisted",
+			    sm->r0kh_id, sm->r0kh_id_len);
+		return -1;
+	}
+
+	key = r0kh->key;
+	key_len = sizeof(r0kh->key);
 
 	wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH "
 		   "address " MACSTR, MAC2STR(r0kh->addr));
 
-	os_memset(&frame, 0, sizeof(frame));
-	frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
-	frame.packet_type = FT_PACKET_R0KH_R1KH_PULL;
-	frame.data_length = host_to_le16(FT_R0KH_R1KH_PULL_DATA_LEN);
-	os_memcpy(frame.ap_address, sm->wpa_auth->addr, ETH_ALEN);
+	if (r0kh->seq->rx.num_last == 0) {
+		/* A sequence request will be sent out anyway when pull
+		 * response is received. Send it out now to avoid one RTT. */
+		wpa_ft_rrb_seq_req(sm->wpa_auth, r0kh->seq, r0kh->addr,
+				   r0kh->id, r0kh->id_len, f_r1kh_id, key,
+				   key_len, NULL, 0, NULL, 0, NULL);
+	}
 
-	/* aes_wrap() does not support inplace encryption, so use a temporary
-	 * buffer for the data. */
-	if (random_get_bytes(f.nonce, FT_R0KH_R1KH_PULL_NONCE_LEN)) {
+	if (first &&
+	    random_get_bytes(sm->ft_pending_pull_nonce, FT_RRB_NONCE_LEN) < 0) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
 			   "nonce");
 		return -1;
 	}
-	os_memcpy(sm->ft_pending_pull_nonce, f.nonce,
-		  FT_R0KH_R1KH_PULL_NONCE_LEN);
-	os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
-	os_memcpy(f.r1kh_id, sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
-	os_memcpy(f.s1kh_id, sm->addr, ETH_ALEN);
-	os_memset(f.pad, 0, sizeof(f.pad));
 
-	if (aes_wrap(r0kh->key, sizeof(r0kh->key),
-		     (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
-		     f.nonce, frame.nonce) < 0)
+	if (wpa_ft_new_seq(r0kh->seq, &f_seq) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+		return -1;
+	}
+
+	if (wpa_ft_rrb_build(key, key_len, req_enc, NULL, req_auth,
+			     sm->wpa_auth->addr, FT_PACKET_R0KH_R1KH_PULL,
+			     &packet, &packet_len) < 0)
 		return -1;
 
+	ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len);
 	wpabuf_free(sm->ft_pending_req_ies);
-	sm->ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len);
-	if (sm->ft_pending_req_ies == NULL)
+	sm->ft_pending_req_ies = ft_pending_req_ies;
+	if (!sm->ft_pending_req_ies) {
+		os_free(packet);
 		return -1;
+	}
 
-	wpa_ft_rrb_send(sm->wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame));
+	tsecs = sm->wpa_auth->conf.rkh_pull_timeout / 1000;
+	tusecs = (sm->wpa_auth->conf.rkh_pull_timeout % 1000) * 1000;
+	eloop_register_timeout(tsecs, tusecs, wpa_ft_expire_pull, sm, NULL);
+
+	wpa_ft_rrb_oui_send(sm->wpa_auth, r0kh->addr, FT_PACKET_R0KH_R1KH_PULL,
+			    packet, packet_len);
+
+	os_free(packet);
 
 	return 0;
 }
@@ -891,6 +2031,12 @@
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
 	else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
+#ifdef CONFIG_FILS
+	else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
+	else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384;
+#endif /* CONFIG_FILS */
 	ciphers = parse->pairwise_cipher & sm->wpa_auth->conf.rsn_pairwise;
 	if (!ciphers) {
 		wpa_printf(MSG_DEBUG, "FT: Invalid pairwise cipher (0x%x) from "
@@ -984,8 +2130,8 @@
 	} else if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name,
 				       pmk_r1, &pairwise) < 0) {
 		if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
-			wpa_printf(MSG_DEBUG, "FT: Did not have matching "
-				   "PMK-R1 and unknown R0KH-ID");
+			wpa_printf(MSG_DEBUG,
+				   "FT: Did not have matching PMK-R1 and either unknown or blocked R0KH-ID or NAK from R0KH");
 			return WLAN_STATUS_INVALID_PMKID;
 		}
 
@@ -1077,6 +2223,7 @@
 	sm->ft_pending_cb = cb;
 	sm->ft_pending_cb_ctx = ctx;
 	sm->ft_pending_auth_transaction = auth_transaction;
+	sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries;
 	res = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies,
 				      &resp_ies_len);
 	if (res < 0) {
@@ -1358,6 +2505,7 @@
 	sm->ft_pending_cb = wpa_ft_rrb_rx_request_cb;
 	sm->ft_pending_cb_ctx = sm;
 	os_memcpy(sm->ft_pending_current_ap, current_ap, ETH_ALEN);
+	sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries;
 	res = wpa_ft_process_auth_req(sm, body, len, &resp_ies,
 				      &resp_ies_len);
 	if (res < 0) {
@@ -1423,115 +2571,339 @@
 }
 
 
+static int wpa_ft_rrb_build_r0(const u8 *key, const size_t key_len,
+			       const struct tlv_list *tlvs,
+			       const struct wpa_ft_pmk_r0_sa *pmk_r0,
+			       const u8 *r1kh_id, const u8 *s1kh_id,
+			       const struct tlv_list *tlv_auth,
+			       const u8 *src_addr, u8 type,
+			       u8 **packet, size_t *packet_len)
+{
+	u8 pmk_r1[PMK_LEN];
+	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+	u8 f_pairwise[sizeof(le16)];
+	int ret;
+	struct tlv_list sess_tlv[] = {
+		{ .type = FT_RRB_PMK_R1, .len = sizeof(pmk_r1),
+		  .data = pmk_r1 },
+		{ .type = FT_RRB_PMK_R1_NAME, .len = sizeof(pmk_r1_name),
+		  .data = pmk_r1_name },
+		{ .type = FT_RRB_PAIRWISE, .len = sizeof(f_pairwise),
+		  .data = f_pairwise },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
+
+	if (!pmk_r0)
+		return wpa_ft_rrb_build(key, key_len, tlvs, NULL, tlv_auth,
+					src_addr, type, packet, packet_len);
+
+	if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh_id,
+			      s1kh_id, pmk_r1, pmk_r1_name) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
+	WPA_PUT_LE16(f_pairwise, pmk_r0->pairwise);
+
+	ret = wpa_ft_rrb_build(key, key_len, tlvs, sess_tlv, tlv_auth,
+			       src_addr, type, packet, packet_len);
+
+	os_memset(pmk_r1, 0, sizeof(pmk_r1));
+
+	return ret;
+}
+
+
 static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
 			      const u8 *src_addr,
-			      const u8 *data, size_t data_len)
+			      const u8 *enc, size_t enc_len,
+			      const u8 *auth, size_t auth_len,
+			      int no_defer)
 {
-	struct ft_r0kh_r1kh_pull_frame f;
-	const u8 *crypt;
-	u8 *plain;
-	struct ft_remote_r1kh *r1kh;
-	struct ft_r0kh_r1kh_resp_frame resp, r;
-	u8 pmk_r0[PMK_LEN];
-	int pairwise;
+	const char *msgtype = "pull request";
+	u8 *plain = NULL, *packet = NULL;
+	size_t plain_len = 0, packet_len = 0;
+	struct ft_remote_r1kh *r1kh, *r1kh_wildcard;
+	const u8 *key;
+	size_t key_len;
+	int seq_ret;
+	const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id, *f_s1kh_id, *f_pmk_r0_name;
+	size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len, f_s1kh_id_len;
+	size_t f_pmk_r0_name_len;
+	const struct wpa_ft_pmk_r0_sa *r0;
+	int ret;
+	struct tlv_list resp[2];
+	struct tlv_list resp_auth[5];
+	struct ft_rrb_seq f_seq;
 
 	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull");
 
-	if (data_len < sizeof(f))
-		return -1;
+	RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1);
+	wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len);
 
-	r1kh = wpa_auth->conf.r1kh_list;
-	while (r1kh) {
-		if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0)
-			break;
-		r1kh = r1kh->next;
-	}
-	if (r1kh == NULL) {
-		wpa_printf(MSG_DEBUG, "FT: No matching R1KH address found for "
-			   "PMK-R1 pull source address " MACSTR,
-			   MAC2STR(src_addr));
-		return -1;
+	if (wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len)) {
+		wpa_printf(MSG_DEBUG, "FT: R0KH-ID mismatch");
+		goto out;
 	}
 
-	crypt = data + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce);
-	os_memset(&f, 0, sizeof(f));
-	plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce);
-	/* aes_unwrap() does not support inplace decryption, so use a temporary
-	 * buffer for the data. */
-	if (aes_unwrap(r1kh->key, sizeof(r1kh->key),
-		       (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
-		       crypt, plain) < 0) {
-		wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
-			   "request from " MACSTR, MAC2STR(src_addr));
-		return -1;
+	RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN);
+	wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id));
+
+	wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh, &r1kh_wildcard);
+	if (r1kh) {
+		key = r1kh->key;
+		key_len = sizeof(r1kh->key);
+	} else if (r1kh_wildcard) {
+		wpa_printf(MSG_DEBUG, "FT: Using wildcard R1KH-ID");
+		key = r1kh_wildcard->key;
+		key_len = sizeof(r1kh_wildcard->key);
+	} else {
+		goto out;
 	}
 
-	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce",
-		    f.nonce, sizeof(f.nonce));
-	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name",
-		    f.pmk_r0_name, WPA_PMK_NAME_LEN);
-	wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID="
-		   MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id));
+	RRB_GET_AUTH(FT_RRB_NONCE, nonce, "pull request", FT_RRB_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len);
 
-	os_memset(&resp, 0, sizeof(resp));
-	resp.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
-	resp.packet_type = FT_PACKET_R0KH_R1KH_RESP;
-	resp.data_length = host_to_le16(FT_R0KH_R1KH_RESP_DATA_LEN);
-	os_memcpy(resp.ap_address, wpa_auth->addr, ETH_ALEN);
-
-	/* aes_wrap() does not support inplace encryption, so use a temporary
-	 * buffer for the data. */
-	os_memcpy(r.nonce, f.nonce, sizeof(f.nonce));
-	os_memcpy(r.r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN);
-	os_memcpy(r.s1kh_id, f.s1kh_id, ETH_ALEN);
-	if (wpa_ft_fetch_pmk_r0(wpa_auth, f.s1kh_id, f.pmk_r0_name, pmk_r0,
-				&pairwise) < 0) {
-		wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name found for "
-			   "PMK-R1 pull");
-		return -1;
+	seq_ret = FT_RRB_SEQ_DROP;
+	if (r1kh)
+		seq_ret = wpa_ft_rrb_seq_chk(r1kh->seq, src_addr, enc, enc_len,
+					     auth, auth_len, msgtype, no_defer);
+	if (!no_defer && r1kh_wildcard &&
+	    (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) {
+		/* wildcard: r1kh-id unknown or changed addr -> do a seq req */
+		seq_ret = FT_RRB_SEQ_DEFER;
 	}
 
-	if (wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id,
-			      r.pmk_r1, r.pmk_r1_name) < 0) {
-		os_memset(pmk_r0, 0, PMK_LEN);
-		return -1;
-	}
-	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", r.pmk_r1, PMK_LEN);
-	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name,
-		    WPA_PMK_NAME_LEN);
-	r.pairwise = host_to_le16(pairwise);
-	os_memset(r.pad, 0, sizeof(r.pad));
+	if (seq_ret == FT_RRB_SEQ_DROP)
+		goto out;
 
-	if (aes_wrap(r1kh->key, sizeof(r1kh->key),
-		     (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
-		     r.nonce, resp.nonce) < 0) {
-		os_memset(pmk_r0, 0, PMK_LEN);
-		return -1;
+	if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len,
+			       src_addr, FT_PACKET_R0KH_R1KH_PULL,
+			       &plain, &plain_len) < 0)
+		goto out;
+
+	if (!r1kh)
+		r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard, src_addr,
+					   f_r1kh_id,
+					   wpa_auth->conf.rkh_pos_timeout);
+	if (!r1kh)
+		goto out;
+
+	if (seq_ret == FT_RRB_SEQ_DEFER) {
+		wpa_ft_rrb_seq_req(wpa_auth, r1kh->seq, src_addr, f_r0kh_id,
+				   f_r0kh_id_len, f_r1kh_id, key, key_len,
+				   enc, enc_len, auth, auth_len,
+				   &wpa_ft_rrb_rx_pull);
+		goto out;
 	}
 
-	os_memset(pmk_r0, 0, PMK_LEN);
+	wpa_ft_rrb_seq_accept(wpa_auth, r1kh->seq, src_addr, auth, auth_len,
+			      msgtype);
+	wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh,
+				  wpa_auth->conf.rkh_pos_timeout);
 
-	wpa_ft_rrb_send(wpa_auth, src_addr, (u8 *) &resp, sizeof(resp));
+	RRB_GET(FT_RRB_PMK_R0_NAME, pmk_r0_name, msgtype, WPA_PMK_NAME_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", f_pmk_r0_name,
+		    f_pmk_r0_name_len);
+
+	RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id));
+
+	if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+		goto out;
+	}
+
+	resp[0].type = FT_RRB_S1KH_ID;
+	resp[0].len = f_s1kh_id_len;
+	resp[0].data = f_s1kh_id;
+	resp[1].type = FT_RRB_LAST_EMPTY;
+	resp[1].len = 0;
+	resp[1].data = NULL;
+
+	resp_auth[0].type = FT_RRB_NONCE;
+	resp_auth[0].len = f_nonce_len;
+	resp_auth[0].data = f_nonce;
+	resp_auth[1].type = FT_RRB_SEQ;
+	resp_auth[1].len = sizeof(f_seq);
+	resp_auth[1].data = (u8 *) &f_seq;
+	resp_auth[2].type = FT_RRB_R0KH_ID;
+	resp_auth[2].len = f_r0kh_id_len;
+	resp_auth[2].data = f_r0kh_id;
+	resp_auth[3].type = FT_RRB_R1KH_ID;
+	resp_auth[3].len = f_r1kh_id_len;
+	resp_auth[3].data = f_r1kh_id;
+	resp_auth[4].type = FT_RRB_LAST_EMPTY;
+	resp_auth[4].len = 0;
+	resp_auth[4].data = NULL;
+
+	if (wpa_ft_fetch_pmk_r0(wpa_auth, f_s1kh_id, f_pmk_r0_name, &r0) < 0)
+		wpa_printf(MSG_DEBUG, "FT: No matching PMK-R0-Name found");
+
+	ret = wpa_ft_rrb_build_r0(key, key_len, resp, r0, f_r1kh_id, f_s1kh_id,
+				  resp_auth, wpa_auth->addr,
+				  FT_PACKET_R0KH_R1KH_RESP,
+				  &packet, &packet_len);
+
+	if (!ret)
+		wpa_ft_rrb_oui_send(wpa_auth, src_addr,
+				    FT_PACKET_R0KH_R1KH_RESP, packet,
+				    packet_len);
+
+out:
+	os_free(plain);
+	os_free(packet);
 
 	return 0;
 }
 
 
-static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx)
+/* @returns  0 on success
+ *          -1 on error
+ *          -2 if FR_RRB_PAIRWISE is missing
+ */
+static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth,
+			    const u8 *src_addr, u8 type,
+			    const u8 *enc, size_t enc_len,
+			    const u8 *auth, size_t auth_len,
+			    const char *msgtype, u8 *s1kh_id_out,
+			    int (*cb)(struct wpa_authenticator *wpa_auth,
+				      const u8 *src_addr,
+				      const u8 *enc, size_t enc_len,
+				      const u8 *auth, size_t auth_len,
+				      int no_defer))
 {
-	struct wpa_state_machine *sm = eloop_ctx;
+	u8 *plain = NULL;
+	size_t plain_len = 0;
+	struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
+	const u8 *key;
+	size_t key_len;
+	int seq_ret;
+	const u8 *f_r1kh_id, *f_s1kh_id, *f_r0kh_id;
+	const u8 *f_pmk_r1_name, *f_pairwise, *f_pmk_r1;
+	size_t f_r1kh_id_len, f_s1kh_id_len, f_r0kh_id_len;
+	size_t f_pmk_r1_name_len, f_pairwise_len, f_pmk_r1_len;
+	int pairwise;
+	int ret = -1;
+
+	RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1);
+	wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len);
+
+	RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN);
+	wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id));
+
+	if (wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id)) {
+		wpa_printf(MSG_DEBUG, "FT: R1KH-ID mismatch");
+		goto out;
+	}
+
+	wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, &r0kh,
+			       &r0kh_wildcard);
+	if (r0kh) {
+		key = r0kh->key;
+		key_len = sizeof(r0kh->key);
+	} else if (r0kh_wildcard) {
+		wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID");
+		key = r0kh_wildcard->key;
+		key_len = sizeof(r0kh_wildcard->key);
+	} else {
+		goto out;
+	}
+
+	seq_ret = FT_RRB_SEQ_DROP;
+	if (r0kh) {
+		seq_ret = wpa_ft_rrb_seq_chk(r0kh->seq, src_addr, enc, enc_len,
+					     auth, auth_len, msgtype,
+					     cb ? 0 : 1);
+	}
+	if (cb && r0kh_wildcard &&
+	    (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) {
+		/* wildcard: r0kh-id unknown or changed addr -> do a seq req */
+		seq_ret = FT_RRB_SEQ_DEFER;
+	}
+
+	if (seq_ret == FT_RRB_SEQ_DROP)
+		goto out;
+
+	if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len,
+			       src_addr, type, &plain, &plain_len) < 0)
+		goto out;
+
+	if (!r0kh)
+		r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, src_addr,
+					   f_r0kh_id, f_r0kh_id_len,
+					   wpa_auth->conf.rkh_pos_timeout);
+	if (!r0kh)
+		goto out;
+
+	if (seq_ret == FT_RRB_SEQ_DEFER) {
+		wpa_ft_rrb_seq_req(wpa_auth, r0kh->seq, src_addr, f_r0kh_id,
+				   f_r0kh_id_len, f_r1kh_id, key, key_len,
+				   enc, enc_len, auth, auth_len, cb);
+		goto out;
+	}
+
+	wpa_ft_rrb_seq_accept(wpa_auth, r0kh->seq, src_addr, auth, auth_len,
+			      msgtype);
+	wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh,
+				  wpa_auth->conf.rkh_pos_timeout);
+
+	RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id));
+
+	if (s1kh_id_out)
+		os_memcpy(s1kh_id_out, f_s1kh_id, ETH_ALEN);
+
+	ret = -2;
+	RRB_GET(FT_RRB_PAIRWISE, pairwise, msgtype, sizeof(le16));
+	wpa_hexdump(MSG_DEBUG, "FT: pairwise", f_pairwise, f_pairwise_len);
+
+	ret = -1;
+	RRB_GET(FT_RRB_PMK_R1_NAME, pmk_r1_name, msgtype, WPA_PMK_NAME_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name",
+		    f_pmk_r1_name, WPA_PMK_NAME_LEN);
+
+	RRB_GET(FT_RRB_PMK_R1, pmk_r1, msgtype, PMK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f_pmk_r1, PMK_LEN);
+
+	pairwise = WPA_GET_LE16(f_pairwise);
+
+	if (wpa_ft_store_pmk_r1(wpa_auth, f_s1kh_id, f_pmk_r1, f_pmk_r1_name,
+				pairwise) < 0)
+		goto out;
+
+	ret = 0;
+out:
+	if (plain) {
+		os_memset(plain, 0, plain_len);
+		os_free(plain);
+	}
+
+	return ret;
+
+}
+
+
+static void ft_finish_pull(struct wpa_state_machine *sm)
+{
 	int res;
 	u8 *resp_ies;
 	size_t resp_ies_len;
 	u16 status;
 
+	if (!sm->ft_pending_cb || !sm->ft_pending_req_ies)
+		return;
+
 	res = wpa_ft_process_auth_req(sm, wpabuf_head(sm->ft_pending_req_ies),
 				      wpabuf_len(sm->ft_pending_req_ies),
 				      &resp_ies, &resp_ies_len);
+	if (res < 0) {
+		/* this loop is broken by ft_pending_pull_left_retries */
+		wpa_printf(MSG_DEBUG,
+			   "FT: Callback postponed until response is available");
+		return;
+	}
 	wpabuf_free(sm->ft_pending_req_ies);
 	sm->ft_pending_req_ies = NULL;
-	if (res < 0)
-		res = WLAN_STATUS_UNSPECIFIED_FAILURE;
 	status = res;
 	wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR
 		   " - status %u", MAC2STR(sm->addr), status);
@@ -1543,172 +2915,386 @@
 }
 
 
-static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx)
-{
-	struct ft_r0kh_r1kh_resp_frame *frame = ctx;
+struct ft_get_sta_ctx {
+	const u8 *nonce;
+	const u8 *s1kh_id;
+	struct wpa_state_machine *sm;
+};
 
-	if (os_memcmp(frame->s1kh_id, sm->addr, ETH_ALEN) != 0 ||
-	    os_memcmp(frame->nonce, sm->ft_pending_pull_nonce,
-		      FT_R0KH_R1KH_PULL_NONCE_LEN) != 0 ||
+
+static int ft_get_sta_cb(struct wpa_state_machine *sm, void *ctx)
+{
+	struct ft_get_sta_ctx *info = ctx;
+
+	if ((info->s1kh_id &&
+	     os_memcmp(info->s1kh_id, sm->addr, ETH_ALEN) != 0) ||
+	    os_memcmp(info->nonce, sm->ft_pending_pull_nonce,
+		      FT_RRB_NONCE_LEN) != 0 ||
 	    sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
 		return 0;
 
-	wpa_printf(MSG_DEBUG, "FT: Response to a pending pull request for "
-		   MACSTR " - process from timeout", MAC2STR(sm->addr));
-	eloop_register_timeout(0, 0, ft_pull_resp_cb_finish, sm, NULL);
+	info->sm = sm;
+
 	return 1;
 }
 
 
 static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
 			      const u8 *src_addr,
-			      const u8 *data, size_t data_len)
+			      const u8 *enc, size_t enc_len,
+			      const u8 *auth, size_t auth_len,
+			      int no_defer)
 {
-	struct ft_r0kh_r1kh_resp_frame f;
-	const u8 *crypt;
-	u8 *plain;
-	struct ft_remote_r0kh *r0kh;
-	int pairwise, res;
+	const char *msgtype = "pull response";
+	int nak, ret = -1;
+	struct ft_get_sta_ctx ctx;
+	u8 s1kh_id[ETH_ALEN];
+	const u8 *f_nonce;
+	size_t f_nonce_len;
 
 	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response");
 
-	if (data_len < sizeof(f))
-		return -1;
+	RRB_GET_AUTH(FT_RRB_NONCE, nonce, msgtype, FT_RRB_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len);
 
-	r0kh = wpa_auth->conf.r0kh_list;
-	while (r0kh) {
-		if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0)
-			break;
-		r0kh = r0kh->next;
-	}
-	if (r0kh == NULL) {
-		wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for "
-			   "PMK-R0 pull response source address " MACSTR,
-			   MAC2STR(src_addr));
+	os_memset(&ctx, 0, sizeof(ctx));
+	ctx.nonce = f_nonce;
+	if (!wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) {
+		/* nonce not found */
+		wpa_printf(MSG_DEBUG, "FT: Invalid nonce");
 		return -1;
 	}
 
-	crypt = data + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce);
-	os_memset(&f, 0, sizeof(f));
-	plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce);
-	/* aes_unwrap() does not support inplace decryption, so use a temporary
-	 * buffer for the data. */
-	if (aes_unwrap(r0kh->key, sizeof(r0kh->key),
-		       (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
-		       crypt, plain) < 0) {
-		wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
-			   "response from " MACSTR, MAC2STR(src_addr));
+	ret = wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_RESP,
+			       enc, enc_len, auth, auth_len, msgtype, s1kh_id,
+			       no_defer ? NULL : &wpa_ft_rrb_rx_resp);
+	if (ret == -2) {
+		ret = 0;
+		nak = 1;
+	} else {
+		nak = 0;
+	}
+	if (ret < 0)
 		return -1;
+
+	ctx.s1kh_id = s1kh_id;
+	if (wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: Response to a pending pull request for " MACSTR,
+			   MAC2STR(ctx.sm->addr));
+		eloop_cancel_timeout(wpa_ft_expire_pull, ctx.sm, NULL);
+		if (nak)
+			ctx.sm->ft_pending_pull_left_retries = 0;
+		ft_finish_pull(ctx.sm);
 	}
 
-	if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder,
-			    FT_R1KH_ID_LEN) != 0) {
-		wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a "
-			   "matching R1KH-ID");
-		return -1;
-	}
-
-	pairwise = le_to_host16(f.pairwise);
-	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce",
-		    f.nonce, sizeof(f.nonce));
-	wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID="
-		   MACSTR " pairwise=0x%x",
-		   MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise);
-	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1",
-			f.pmk_r1, PMK_LEN);
-	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name",
-			f.pmk_r1_name, WPA_PMK_NAME_LEN);
-
-	res = wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
-				  pairwise);
-	wpa_printf(MSG_DEBUG, "FT: Look for pending pull request");
-	wpa_auth_for_each_sta(wpa_auth, ft_pull_resp_cb, &f);
-	os_memset(f.pmk_r1, 0, PMK_LEN);
-
-	return res ? 0 : -1;
+out:
+	return ret;
 }
 
 
 static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
 			      const u8 *src_addr,
-			      const u8 *data, size_t data_len)
+			      const u8 *enc, size_t enc_len,
+			      const u8 *auth, size_t auth_len, int no_defer)
 {
-	struct ft_r0kh_r1kh_push_frame f;
-	const u8 *crypt;
-	u8 *plain;
-	struct ft_remote_r0kh *r0kh;
-	struct os_time now;
-	os_time_t tsend;
-	int pairwise;
+	const char *msgtype = "push";
 
 	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push");
 
-	if (data_len < sizeof(f))
+	if (wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_PUSH,
+			     enc, enc_len, auth, auth_len, msgtype, NULL,
+			     no_defer ? NULL : wpa_ft_rrb_rx_push) < 0)
 		return -1;
 
-	r0kh = wpa_auth->conf.r0kh_list;
-	while (r0kh) {
-		if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0)
-			break;
-		r0kh = r0kh->next;
-	}
-	if (r0kh == NULL) {
-		wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for "
-			   "PMK-R0 push source address " MACSTR,
-			   MAC2STR(src_addr));
-		return -1;
-	}
-
-	crypt = data + offsetof(struct ft_r0kh_r1kh_push_frame, timestamp);
-	os_memset(&f, 0, sizeof(f));
-	plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame,
-				       timestamp);
-	/* aes_unwrap() does not support inplace decryption, so use a temporary
-	 * buffer for the data. */
-	if (aes_unwrap(r0kh->key, sizeof(r0kh->key),
-		       (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
-		       crypt, plain) < 0) {
-		wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from "
-			   MACSTR, MAC2STR(src_addr));
-		return -1;
-	}
-
-	os_get_time(&now);
-	tsend = WPA_GET_LE32(f.timestamp);
-	if ((now.sec > tsend && now.sec - tsend > 60) ||
-	    (now.sec < tsend && tsend - now.sec > 60)) {
-		wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not have a valid "
-			   "timestamp: sender time %d own time %d\n",
-			   (int) tsend, (int) now.sec);
-		return -1;
-	}
-
-	if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder,
-			    FT_R1KH_ID_LEN) != 0) {
-		wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching "
-			   "R1KH-ID (received " MACSTR " own " MACSTR ")",
-			   MAC2STR(f.r1kh_id),
-			   MAC2STR(wpa_auth->conf.r1_key_holder));
-		return -1;
-	}
-
-	pairwise = le_to_host16(f.pairwise);
-	wpa_printf(MSG_DEBUG, "FT: PMK-R1 push - R1KH-ID=" MACSTR " S1KH-ID="
-		   MACSTR " pairwise=0x%x",
-		   MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise);
-	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 push - PMK-R1",
-			f.pmk_r1, PMK_LEN);
-	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 push - PMKR1Name",
-			f.pmk_r1_name, WPA_PMK_NAME_LEN);
-
-	wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
-			    pairwise);
-	os_memset(f.pmk_r1, 0, PMK_LEN);
-
 	return 0;
 }
 
 
+static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth,
+			     const u8 *src_addr, int type,
+			     const u8 *enc, size_t enc_len,
+			     const u8 *auth, size_t auth_len,
+			     struct ft_remote_seq **rkh_seq,
+			     u8 **key, size_t *key_len,
+			     struct ft_remote_r0kh **r0kh_out,
+			     struct ft_remote_r1kh **r1kh_out,
+			     struct ft_remote_r0kh **r0kh_wildcard_out,
+			     struct ft_remote_r1kh **r1kh_wildcard_out)
+{
+	struct ft_remote_r0kh *r0kh = NULL;
+	struct ft_remote_r1kh *r1kh = NULL;
+	const u8 *f_r0kh_id, *f_r1kh_id;
+	size_t f_r0kh_id_len, f_r1kh_id_len;
+	int to_r0kh, to_r1kh;
+	u8 *plain = NULL;
+	size_t plain_len = 0;
+	struct ft_remote_r0kh *r0kh_wildcard;
+	struct ft_remote_r1kh *r1kh_wildcard;
+
+	RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1);
+	RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN);
+
+	to_r0kh = !wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len);
+	to_r1kh = !wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id);
+
+	if (to_r0kh && to_r1kh) {
+		wpa_printf(MSG_DEBUG, "FT: seq - local R0KH-ID and R1KH-ID");
+		goto out;
+	}
+
+	if (!to_r0kh && !to_r1kh) {
+		wpa_printf(MSG_DEBUG, "FT: seq - remote R0KH-ID and R1KH-ID");
+		goto out;
+	}
+
+	if (!to_r0kh) {
+		wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len,
+				       &r0kh, &r0kh_wildcard);
+		if (!r0kh_wildcard &&
+		    (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) {
+			wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
+				    f_r0kh_id, f_r0kh_id_len);
+			goto out;
+		}
+		if (r0kh) {
+			*key = r0kh->key;
+			*key_len = sizeof(r0kh->key);
+		} else {
+			*key = r0kh_wildcard->key;
+			*key_len = sizeof(r0kh_wildcard->key);
+		}
+	}
+
+	if (!to_r1kh) {
+		wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh,
+				       &r1kh_wildcard);
+		if (!r1kh_wildcard &&
+		    (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) {
+			wpa_hexdump(MSG_DEBUG, "FT: Did not find R1KH-ID",
+				    f_r1kh_id, FT_R1KH_ID_LEN);
+			goto out;
+		}
+		if (r1kh) {
+			*key = r1kh->key;
+			*key_len = sizeof(r1kh->key);
+		} else {
+			*key = r1kh_wildcard->key;
+			*key_len = sizeof(r1kh_wildcard->key);
+		}
+	}
+
+	if (wpa_ft_rrb_decrypt(*key, *key_len, enc, enc_len, auth, auth_len,
+			       src_addr, type, &plain, &plain_len) < 0)
+		goto out;
+
+	os_free(plain);
+
+	if (!to_r0kh) {
+		if (!r0kh)
+			r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard,
+						   src_addr, f_r0kh_id,
+						   f_r0kh_id_len,
+						   ftRRBseqTimeout);
+		if (!r0kh)
+			goto out;
+
+		wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, ftRRBseqTimeout);
+		*rkh_seq = r0kh->seq;
+		if (r0kh_out)
+			*r0kh_out = r0kh;
+		if (r0kh_wildcard_out)
+			*r0kh_wildcard_out = r0kh_wildcard;
+	}
+
+	if (!to_r1kh) {
+		if (!r1kh)
+			r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard,
+						   src_addr, f_r1kh_id,
+						   ftRRBseqTimeout);
+		if (!r1kh)
+			goto out;
+
+		wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, ftRRBseqTimeout);
+		*rkh_seq = r1kh->seq;
+		if (r1kh_out)
+			*r1kh_out = r1kh;
+		if (r1kh_wildcard_out)
+			*r1kh_wildcard_out = r1kh_wildcard;
+	}
+
+	return 0;
+out:
+	return -1;
+}
+
+
+static int wpa_ft_rrb_rx_seq_req(struct wpa_authenticator *wpa_auth,
+				 const u8 *src_addr,
+				 const u8 *enc, size_t enc_len,
+				 const u8 *auth, size_t auth_len,
+				 int no_defer)
+{
+	int ret = -1;
+	struct ft_rrb_seq f_seq;
+	const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id;
+	size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len;
+	struct ft_remote_seq *rkh_seq = NULL;
+	u8 *packet = NULL, *key = NULL;
+	size_t packet_len = 0, key_len = 0;
+	struct tlv_list seq_resp_auth[5];
+
+	wpa_printf(MSG_DEBUG, "FT: Received sequence number request");
+
+	if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
+			      enc, enc_len, auth, auth_len, &rkh_seq, &key,
+			      &key_len, NULL, NULL, NULL, NULL) < 0)
+		goto out;
+
+	RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq request", FT_RRB_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: seq request - nonce", f_nonce, f_nonce_len);
+
+	RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1);
+	RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN);
+
+	if (wpa_ft_new_seq(rkh_seq, &f_seq) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+		goto out;
+	}
+
+	seq_resp_auth[0].type = FT_RRB_NONCE;
+	seq_resp_auth[0].len = f_nonce_len;
+	seq_resp_auth[0].data = f_nonce;
+	seq_resp_auth[1].type = FT_RRB_SEQ;
+	seq_resp_auth[1].len = sizeof(f_seq);
+	seq_resp_auth[1].data = (u8 *) &f_seq;
+	seq_resp_auth[2].type = FT_RRB_R0KH_ID;
+	seq_resp_auth[2].len = f_r0kh_id_len;
+	seq_resp_auth[2].data = f_r0kh_id;
+	seq_resp_auth[3].type = FT_RRB_R1KH_ID;
+	seq_resp_auth[3].len = FT_R1KH_ID_LEN;
+	seq_resp_auth[3].data = f_r1kh_id;
+	seq_resp_auth[4].type = FT_RRB_LAST_EMPTY;
+	seq_resp_auth[4].len = 0;
+	seq_resp_auth[4].data = NULL;
+
+	if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_resp_auth,
+			     wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_RESP,
+			     &packet, &packet_len) < 0)
+		goto out;
+
+	wpa_ft_rrb_oui_send(wpa_auth, src_addr,
+			    FT_PACKET_R0KH_R1KH_SEQ_RESP, packet,
+			    packet_len);
+
+out:
+	os_free(packet);
+
+	return ret;
+}
+
+
+static int wpa_ft_rrb_rx_seq_resp(struct wpa_authenticator *wpa_auth,
+				  const u8 *src_addr,
+				  const u8 *enc, size_t enc_len,
+				  const u8 *auth, size_t auth_len,
+				  int no_defer)
+{
+	u8 *key = NULL;
+	size_t key_len = 0;
+	struct ft_remote_r0kh *r0kh = NULL, *r0kh_wildcard = NULL;
+	struct ft_remote_r1kh *r1kh = NULL, *r1kh_wildcard = NULL;
+	const u8 *f_nonce, *f_seq;
+	size_t f_nonce_len, f_seq_len;
+	struct ft_remote_seq *rkh_seq = NULL;
+	struct ft_remote_item *item;
+	struct os_reltime now, now_remote;
+	int seq_ret, found;
+	const struct ft_rrb_seq *msg_both;
+	u32 msg_dom, msg_seq;
+
+	wpa_printf(MSG_DEBUG, "FT: Received sequence number response");
+
+	if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_RESP,
+			      enc, enc_len, auth, auth_len, &rkh_seq, &key,
+			      &key_len, &r0kh, &r1kh, &r0kh_wildcard,
+			      &r1kh_wildcard) < 0)
+		goto out;
+
+	RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq response", FT_RRB_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: seq response - nonce", f_nonce,
+		    f_nonce_len);
+
+	found = 0;
+	dl_list_for_each(item, &rkh_seq->rx.queue, struct ft_remote_item,
+			 list) {
+		if (os_memcmp_const(f_nonce, item->nonce,
+				    FT_RRB_NONCE_LEN) != 0 ||
+		    os_get_reltime(&now) < 0 ||
+		    os_reltime_expired(&now, &item->nonce_ts, ftRRBseqTimeout))
+			continue;
+
+		found = 1;
+		break;
+	}
+	if (!found) {
+		wpa_printf(MSG_DEBUG, "FT: seq response - bad nonce");
+		goto out;
+	}
+
+	if (r0kh) {
+		wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh,
+					  wpa_auth->conf.rkh_pos_timeout);
+		if (r0kh_wildcard)
+			os_memcpy(r0kh->addr, src_addr, ETH_ALEN);
+	}
+
+	if (r1kh) {
+		wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh,
+					  wpa_auth->conf.rkh_pos_timeout);
+		if (r1kh_wildcard)
+			os_memcpy(r1kh->addr, src_addr, ETH_ALEN);
+	}
+
+	seq_ret = wpa_ft_rrb_seq_chk(rkh_seq, src_addr, enc, enc_len, auth,
+				     auth_len, "seq response", 1);
+	if (seq_ret == FT_RRB_SEQ_OK) {
+		wpa_printf(MSG_DEBUG, "FT: seq response - valid seq number");
+		wpa_ft_rrb_seq_accept(wpa_auth, rkh_seq, src_addr, auth,
+				      auth_len, "seq response");
+	} else {
+		wpa_printf(MSG_DEBUG, "FT: seq response - reset seq number");
+
+		RRB_GET_AUTH(FT_RRB_SEQ, seq, "seq response",
+			     sizeof(*msg_both));
+		msg_both = (const struct ft_rrb_seq *) f_seq;
+
+		msg_dom = le_to_host32(msg_both->dom);
+		msg_seq = le_to_host32(msg_both->seq);
+		now_remote.sec = le_to_host32(msg_both->ts);
+		now_remote.usec = 0;
+
+		rkh_seq->rx.num_last = 2;
+		rkh_seq->rx.dom = msg_dom;
+		rkh_seq->rx.offsetidx = 0;
+		/* Accept some older, possibly cached packets as well */
+		rkh_seq->rx.last[0] = msg_seq - FT_REMOTE_SEQ_BACKLOG -
+			dl_list_len(&rkh_seq->rx.queue);
+		rkh_seq->rx.last[1] = msg_seq;
+
+		/* local time - offset = remote time
+		 * <=> local time - remote time = offset */
+		os_reltime_sub(&now, &now_remote, &rkh_seq->rx.time_offset);
+	}
+
+	wpa_ft_rrb_seq_flush(wpa_auth, rkh_seq, 1);
+
+	return 0;
+out:
+	return -1;
+}
+
+
 int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
 		  const u8 *data, size_t data_len)
 {
@@ -1750,13 +3336,6 @@
 		return -1;
 	}
 
-	if (frame->packet_type == FT_PACKET_R0KH_R1KH_PULL)
-		return wpa_ft_rrb_rx_pull(wpa_auth, src_addr, data, data_len);
-	if (frame->packet_type == FT_PACKET_R0KH_R1KH_RESP)
-		return wpa_ft_rrb_rx_resp(wpa_auth, src_addr, data, data_len);
-	if (frame->packet_type == FT_PACKET_R0KH_R1KH_PUSH)
-		return wpa_ft_rrb_rx_push(wpa_auth, src_addr, data, data_len);
-
 	wpa_hexdump(MSG_MSGDUMP, "FT: RRB - FT Action frame", pos, alen);
 
 	if (alen < 1 + 1 + 2 * ETH_ALEN) {
@@ -1834,48 +3413,114 @@
 }
 
 
+void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
+		       const u8 *dst_addr, u8 oui_suffix, const u8 *data,
+		       size_t data_len)
+{
+	const u8 *auth, *enc;
+	size_t alen, elen;
+	int no_defer = 0;
+
+	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);
+
+	if (is_multicast_ether_addr(src_addr)) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: RRB-OUI received frame from multicast address "
+			   MACSTR, MAC2STR(src_addr));
+		return;
+	}
+
+	if (is_multicast_ether_addr(dst_addr)) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: RRB-OUI received frame from remote AP " MACSTR
+			   " to multicast address " MACSTR,
+			   MAC2STR(src_addr), MAC2STR(dst_addr));
+		no_defer = 1;
+	}
+
+	if (data_len < sizeof(u16)) {
+		wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short");
+		return;
+	}
+
+	alen = WPA_GET_LE16(data);
+	if (data_len < sizeof(u16) + alen) {
+		wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short");
+		return;
+	}
+
+	auth = data + sizeof(u16);
+	enc = data + sizeof(u16) + alen;
+	elen = data_len - sizeof(u16) - alen;
+
+	switch (oui_suffix) {
+	case FT_PACKET_R0KH_R1KH_PULL:
+		wpa_ft_rrb_rx_pull(wpa_auth, src_addr, enc, elen, auth, alen,
+				   no_defer);
+		break;
+	case FT_PACKET_R0KH_R1KH_RESP:
+		wpa_ft_rrb_rx_resp(wpa_auth, src_addr, enc, elen, auth, alen,
+				   no_defer);
+		break;
+	case FT_PACKET_R0KH_R1KH_PUSH:
+		wpa_ft_rrb_rx_push(wpa_auth, src_addr, enc, elen, auth, alen,
+				   no_defer);
+		break;
+	case FT_PACKET_R0KH_R1KH_SEQ_REQ:
+		wpa_ft_rrb_rx_seq_req(wpa_auth, src_addr, enc, elen, auth, alen,
+				      no_defer);
+		break;
+	case FT_PACKET_R0KH_R1KH_SEQ_RESP:
+		wpa_ft_rrb_rx_seq_resp(wpa_auth, src_addr, enc, elen, auth,
+				       alen, no_defer);
+		break;
+	}
+}
+
+
 static int wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
 				  struct wpa_ft_pmk_r0_sa *pmk_r0,
 				  struct ft_remote_r1kh *r1kh,
-				  const u8 *s1kh_id, int pairwise)
+				  const u8 *s1kh_id)
 {
-	struct ft_r0kh_r1kh_push_frame frame, f;
-	struct os_time now;
-	const u8 *plain;
-	u8 *crypt;
+	u8 *packet;
+	size_t packet_len;
+	struct ft_rrb_seq f_seq;
+	struct tlv_list push[] = {
+		{ .type = FT_RRB_S1KH_ID, .len = ETH_ALEN,
+		  .data = s1kh_id },
+		{ .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN,
+		  .data = pmk_r0->pmk_r0_name },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
+	struct tlv_list push_auth[] = {
+		{ .type = FT_RRB_SEQ, .len = sizeof(f_seq),
+		  .data = (u8 *) &f_seq },
+		{ .type = FT_RRB_R0KH_ID,
+		  .len = wpa_auth->conf.r0_key_holder_len,
+		  .data = wpa_auth->conf.r0_key_holder },
+		{ .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
+		  .data = r1kh->id },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
 
-	os_memset(&frame, 0, sizeof(frame));
-	frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
-	frame.packet_type = FT_PACKET_R0KH_R1KH_PUSH;
-	frame.data_length = host_to_le16(FT_R0KH_R1KH_PUSH_DATA_LEN);
-	os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN);
-
-	/* aes_wrap() does not support inplace encryption, so use a temporary
-	 * buffer for the data. */
-	os_memcpy(f.r1kh_id, r1kh->id, FT_R1KH_ID_LEN);
-	os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN);
-	os_memcpy(f.pmk_r0_name, pmk_r0->pmk_r0_name, WPA_PMK_NAME_LEN);
-	if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id,
-			      s1kh_id, f.pmk_r1, f.pmk_r1_name) < 0)
+	if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
 		return -1;
-	wpa_printf(MSG_DEBUG, "FT: R1KH-ID " MACSTR, MAC2STR(r1kh->id));
-	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f.pmk_r1, PMK_LEN);
-	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", f.pmk_r1_name,
-		    WPA_PMK_NAME_LEN);
-	os_get_time(&now);
-	WPA_PUT_LE32(f.timestamp, now.sec);
-	f.pairwise = host_to_le16(pairwise);
-	os_memset(f.pad, 0, sizeof(f.pad));
-	plain = ((const u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame,
-					     timestamp);
-	crypt = ((u8 *) &frame) + offsetof(struct ft_r0kh_r1kh_push_frame,
-					   timestamp);
-	if (aes_wrap(r1kh->key, sizeof(r1kh->key),
-		     (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
-		     plain, crypt) < 0)
+	}
+
+	if (wpa_ft_rrb_build_r0(r1kh->key, sizeof(r1kh->key), push, pmk_r0,
+				r1kh->id, s1kh_id, push_auth, wpa_auth->addr,
+				FT_PACKET_R0KH_R1KH_PUSH,
+				&packet, &packet_len) < 0)
 		return -1;
 
-	wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame));
+	wpa_ft_rrb_oui_send(wpa_auth, r1kh->addr, FT_PACKET_R0KH_R1KH_PUSH,
+			    packet, packet_len);
+
+	os_free(packet);
 	return 0;
 }
 
@@ -1887,6 +3532,8 @@
 
 	if (!wpa_auth->conf.pmk_r1_push)
 		return;
+	if (!wpa_auth->conf.r1kh_list)
+		return;
 
 	r0 = wpa_auth->ft_pmk_cache->pmk_r0;
 	while (r0) {
@@ -1902,10 +3549,13 @@
 	wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs "
 		   "for STA " MACSTR, MAC2STR(addr));
 
-	r1kh = wpa_auth->conf.r1kh_list;
-	while (r1kh) {
-		wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr, r0->pairwise);
-		r1kh = r1kh->next;
+	for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) {
+		if (is_zero_ether_addr(r1kh->addr) ||
+		    is_zero_ether_addr(r1kh->id))
+			continue;
+		if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0)
+			continue;
+		wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr);
 	}
 }
 
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 394f77a..98133a0 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -9,6 +9,8 @@
 #include "utils/includes.h"
 
 #include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/list.h"
 #include "common/ieee802_11_defs.h"
 #include "common/sae.h"
 #include "common/wpa_ctrl.h"
@@ -17,6 +19,7 @@
 #include "eapol_auth/eapol_auth_sm_i.h"
 #include "eap_server/eap.h"
 #include "l2_packet/l2_packet.h"
+#include "eth_p_oui.h"
 #include "hostapd.h"
 #include "ieee802_1x.h"
 #include "preauth_auth.h"
@@ -24,6 +27,7 @@
 #include "tkip_countermeasures.h"
 #include "ap_drv_ops.h"
 #include "ap_config.h"
+#include "pmksa_cache_auth.h"
 #include "wpa_auth.h"
 #include "wpa_auth_glue.h"
 
@@ -42,11 +46,12 @@
 	wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey;
 	wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey;
 	wconf->wpa_group_update_count = conf->wpa_group_update_count;
+	wconf->wpa_disable_eapol_key_retries =
+		conf->wpa_disable_eapol_key_retries;
 	wconf->wpa_pairwise_update_count = conf->wpa_pairwise_update_count;
 	wconf->rsn_pairwise = conf->rsn_pairwise;
 	wconf->rsn_preauth = conf->rsn_preauth;
 	wconf->eapol_version = conf->eapol_version;
-	wconf->peerkey = conf->peerkey;
 	wconf->wmm_enabled = conf->wmm_enabled;
 	wconf->wmm_uapsd = conf->wmm_uapsd;
 	wconf->disable_pmksa_caching = conf->disable_pmksa_caching;
@@ -71,8 +76,12 @@
 	os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN);
 	wconf->r0_key_lifetime = conf->r0_key_lifetime;
 	wconf->reassociation_deadline = conf->reassociation_deadline;
-	wconf->r0kh_list = conf->r0kh_list;
-	wconf->r1kh_list = conf->r1kh_list;
+	wconf->rkh_pos_timeout = conf->rkh_pos_timeout;
+	wconf->rkh_neg_timeout = conf->rkh_neg_timeout;
+	wconf->rkh_pull_timeout = conf->rkh_pull_timeout;
+	wconf->rkh_pull_retries = conf->rkh_pull_retries;
+	wconf->r0kh_list = &conf->r0kh_list;
+	wconf->r1kh_list = &conf->r1kh_list;
 	wconf->pmk_r1_push = conf->pmk_r1_push;
 	wconf->ft_over_ds = conf->ft_over_ds;
 	wconf->ft_psk_generate_local = conf->ft_psk_generate_local;
@@ -110,6 +119,11 @@
 	os_memcpy(wconf->ip_addr_start, conf->ip_addr_start, 4);
 	os_memcpy(wconf->ip_addr_end, conf->ip_addr_end, 4);
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_FILS
+	wconf->fils_cache_id_set = conf->fils_cache_id_set;
+	os_memcpy(wconf->fils_cache_id, conf->fils_cache_id,
+		  FILS_CACHE_ID_LEN);
+#endif /* CONFIG_FILS */
 }
 
 
@@ -226,20 +240,47 @@
 
 static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
 					   const u8 *p2p_dev_addr,
-					   const u8 *prev_psk)
+					   const u8 *prev_psk, size_t *psk_len)
 {
 	struct hostapd_data *hapd = ctx;
 	struct sta_info *sta = ap_get_sta(hapd, addr);
 	const u8 *psk;
 
+	if (psk_len)
+		*psk_len = PMK_LEN;
+
 #ifdef CONFIG_SAE
 	if (sta && sta->auth_alg == WLAN_AUTH_SAE) {
 		if (!sta->sae || prev_psk)
 			return NULL;
 		return sta->sae->pmk;
 	}
+	if (sta && wpa_auth_uses_sae(sta->wpa_sm)) {
+		wpa_printf(MSG_DEBUG,
+			   "No PSK for STA trying to use SAE with PMKSA caching");
+		return NULL;
+	}
 #endif /* CONFIG_SAE */
 
+#ifdef CONFIG_OWE
+	if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+	    sta && sta->owe_pmk) {
+		if (psk_len)
+			*psk_len = sta->owe_pmk_len;
+		return sta->owe_pmk;
+	}
+	if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && sta) {
+		struct rsn_pmksa_cache_entry *sa;
+
+		sa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
+		if (sa && sa->akmp == WPA_KEY_MGMT_OWE) {
+			if (psk_len)
+				*psk_len = sa->pmk_len;
+			return sa->pmk;
+		}
+	}
+#endif /* CONFIG_OWE */
+
 	psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk);
 	/*
 	 * This is about to iterate over all psks, prev_psk gives the last
@@ -310,6 +351,37 @@
 			return -1;
 	}
 
+#ifdef CONFIG_TESTING_OPTIONS
+	if (addr && !is_broadcast_ether_addr(addr)) {
+		struct sta_info *sta;
+
+		sta = ap_get_sta(hapd, addr);
+		if (sta) {
+			sta->last_tk_alg = alg;
+			sta->last_tk_key_idx = idx;
+			if (key)
+				os_memcpy(sta->last_tk, key, key_len);
+			sta->last_tk_len = key_len;
+		}
+#ifdef CONFIG_IEEE80211W
+	} else if (alg == WPA_CIPHER_AES_128_CMAC ||
+		   alg == WPA_CIPHER_BIP_GMAC_128 ||
+		   alg == WPA_CIPHER_BIP_GMAC_256 ||
+		   alg == WPA_CIPHER_BIP_CMAC_256) {
+		hapd->last_igtk_alg = alg;
+		hapd->last_igtk_key_idx = idx;
+		if (key)
+			os_memcpy(hapd->last_igtk, key, key_len);
+		hapd->last_igtk_len = key_len;
+#endif /* CONFIG_IEEE80211W */
+	} else {
+		hapd->last_gtk_alg = alg;
+		hapd->last_gtk_key_idx = idx;
+		if (key)
+			os_memcpy(hapd->last_gtk, key, key_len);
+		hapd->last_gtk_len = key_len;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 	return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0,
 				   key, key_len);
 }
@@ -406,6 +478,31 @@
 
 #ifdef CONFIG_IEEE80211R_AP
 
+struct wpa_ft_rrb_rx_later_data {
+	struct dl_list list;
+	u8 addr[ETH_ALEN];
+	size_t data_len;
+	/* followed by data_len octets of data */
+};
+
+static void hostapd_wpa_ft_rrb_rx_later(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct wpa_ft_rrb_rx_later_data *data, *n;
+
+	dl_list_for_each_safe(data, n, &hapd->l2_queue,
+			      struct wpa_ft_rrb_rx_later_data, list) {
+		if (hapd->wpa_auth) {
+			wpa_ft_rrb_rx(hapd->wpa_auth, data->addr,
+				      (const u8 *) (data + 1),
+				      data->data_len);
+		}
+		dl_list_del(&data->list);
+		os_free(data);
+	}
+}
+
+
 struct wpa_auth_ft_iface_iter_data {
 	struct hostapd_data *src_hapd;
 	const u8 *dst;
@@ -417,27 +514,48 @@
 static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx)
 {
 	struct wpa_auth_ft_iface_iter_data *idata = ctx;
+	struct wpa_ft_rrb_rx_later_data *data;
 	struct hostapd_data *hapd;
 	size_t j;
 
 	for (j = 0; j < iface->num_bss; j++) {
 		hapd = iface->bss[j];
-		if (hapd == idata->src_hapd)
+		if (hapd == idata->src_hapd ||
+		    !hapd->wpa_auth ||
+		    os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) != 0)
 			continue;
-		if (!hapd->wpa_auth)
-			continue;
-		if (os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) == 0) {
-			wpa_printf(MSG_DEBUG, "FT: Send RRB data directly to "
-				   "locally managed BSS " MACSTR "@%s -> "
-				   MACSTR "@%s",
-				   MAC2STR(idata->src_hapd->own_addr),
-				   idata->src_hapd->conf->iface,
-				   MAC2STR(hapd->own_addr), hapd->conf->iface);
-			wpa_ft_rrb_rx(hapd->wpa_auth,
-				      idata->src_hapd->own_addr,
-				      idata->data, idata->data_len);
+
+		wpa_printf(MSG_DEBUG,
+			   "FT: Send RRB data directly to locally managed BSS "
+			   MACSTR "@%s -> " MACSTR "@%s",
+			   MAC2STR(idata->src_hapd->own_addr),
+			   idata->src_hapd->conf->iface,
+			   MAC2STR(hapd->own_addr), hapd->conf->iface);
+
+		/* Defer wpa_ft_rrb_rx() until next eloop step as this is
+		 * when it would be triggered when reading from a socket.
+		 * This avoids
+		 * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv,
+		 * that is calling hapd0:recv handler from within
+		 * hapd0:send directly.
+		 */
+		data = os_zalloc(sizeof(*data) + idata->data_len);
+		if (!data)
 			return 1;
-		}
+
+		os_memcpy(data->addr, idata->src_hapd->own_addr, ETH_ALEN);
+		os_memcpy(data + 1, idata->data, idata->data_len);
+		data->data_len = idata->data_len;
+
+		dl_list_add(&hapd->l2_queue, &data->list);
+
+		if (!eloop_is_timeout_registered(hostapd_wpa_ft_rrb_rx_later,
+						 hapd, NULL))
+			eloop_register_timeout(0, 0,
+					       hostapd_wpa_ft_rrb_rx_later,
+					       hapd, NULL);
+
+		return 1;
 	}
 
 	return 0;
@@ -506,6 +624,156 @@
 }
 
 
+#ifdef CONFIG_ETH_P_OUI
+static struct eth_p_oui_ctx * hostapd_wpa_get_oui(struct hostapd_data *hapd,
+						  u8 oui_suffix)
+{
+	switch (oui_suffix) {
+#ifdef CONFIG_IEEE80211R_AP
+	case FT_PACKET_R0KH_R1KH_PULL:
+		return hapd->oui_pull;
+	case FT_PACKET_R0KH_R1KH_RESP:
+		return hapd->oui_resp;
+	case FT_PACKET_R0KH_R1KH_PUSH:
+		return hapd->oui_push;
+	case FT_PACKET_R0KH_R1KH_SEQ_REQ:
+		return hapd->oui_sreq;
+	case FT_PACKET_R0KH_R1KH_SEQ_RESP:
+		return hapd->oui_sresp;
+#endif /* CONFIG_IEEE80211R_AP */
+	default:
+		return NULL;
+	}
+}
+#endif /* CONFIG_ETH_P_OUI */
+
+
+#ifdef CONFIG_IEEE80211R_AP
+
+struct oui_deliver_later_data {
+	struct dl_list list;
+	u8 src_addr[ETH_ALEN];
+	u8 dst_addr[ETH_ALEN];
+	size_t data_len;
+	u8 oui_suffix;
+	/* followed by data_len octets of data */
+};
+
+static void hostapd_oui_deliver_later(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct oui_deliver_later_data *data, *n;
+	struct eth_p_oui_ctx *oui_ctx;
+
+	dl_list_for_each_safe(data, n, &hapd->l2_oui_queue,
+			      struct oui_deliver_later_data, list) {
+		oui_ctx = hostapd_wpa_get_oui(hapd, data->oui_suffix);
+		if (hapd->wpa_auth && oui_ctx) {
+			eth_p_oui_deliver(oui_ctx, data->src_addr,
+					  data->dst_addr,
+					  (const u8 *) (data + 1),
+					  data->data_len);
+		}
+		dl_list_del(&data->list);
+		os_free(data);
+	}
+}
+
+
+struct wpa_auth_oui_iface_iter_data {
+	struct hostapd_data *src_hapd;
+	const u8 *dst_addr;
+	const u8 *data;
+	size_t data_len;
+	u8 oui_suffix;
+};
+
+static int hostapd_wpa_auth_oui_iter(struct hostapd_iface *iface, void *ctx)
+{
+	struct wpa_auth_oui_iface_iter_data *idata = ctx;
+	struct oui_deliver_later_data *data;
+	struct hostapd_data *hapd;
+	size_t j;
+
+	for (j = 0; j < iface->num_bss; j++) {
+		hapd = iface->bss[j];
+		if (hapd == idata->src_hapd)
+			continue;
+		if (!is_multicast_ether_addr(idata->dst_addr) &&
+		    os_memcmp(hapd->own_addr, idata->dst_addr, ETH_ALEN) != 0)
+			continue;
+
+		/* defer eth_p_oui_deliver until next eloop step as this is
+		 * when it would be triggerd from reading from sock
+		 * This avoids
+		 * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv,
+		 * that is calling hapd0:recv handler from within
+		 * hapd0:send directly.
+		 */
+		data = os_zalloc(sizeof(*data) + idata->data_len);
+		if (!data)
+			return 1;
+
+		os_memcpy(data->src_addr, idata->src_hapd->own_addr, ETH_ALEN);
+		os_memcpy(data->dst_addr, idata->dst_addr, ETH_ALEN);
+		os_memcpy(data + 1, idata->data, idata->data_len);
+		data->data_len = idata->data_len;
+		data->oui_suffix = idata->oui_suffix;
+
+		dl_list_add(&hapd->l2_oui_queue, &data->list);
+
+		if (!eloop_is_timeout_registered(hostapd_oui_deliver_later,
+						 hapd, NULL))
+			eloop_register_timeout(0, 0,
+					       hostapd_oui_deliver_later,
+					       hapd, NULL);
+
+		return 1;
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+static int hostapd_wpa_auth_send_oui(void *ctx, const u8 *dst, u8 oui_suffix,
+				     const u8 *data, size_t data_len)
+{
+#ifdef CONFIG_ETH_P_OUI
+	struct hostapd_data *hapd = ctx;
+	struct eth_p_oui_ctx *oui_ctx;
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (hapd->iface->interfaces &&
+	    hapd->iface->interfaces->for_each_interface) {
+		struct wpa_auth_oui_iface_iter_data idata;
+		int res;
+
+		idata.src_hapd = hapd;
+		idata.dst_addr = dst;
+		idata.data = data;
+		idata.data_len = data_len;
+		idata.oui_suffix = oui_suffix;
+		res = hapd->iface->interfaces->for_each_interface(
+			hapd->iface->interfaces, hostapd_wpa_auth_oui_iter,
+			&idata);
+		if (res == 1)
+			return data_len;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+
+	oui_ctx = hostapd_wpa_get_oui(hapd, oui_suffix);
+	if (!oui_ctx)
+		return -1;
+
+	return eth_p_oui_send(oui_ctx, hapd->own_addr, dst, data, data_len);
+#else /* CONFIG_ETH_P_OUI */
+	return -1;
+#endif /* CONFIG_ETH_P_OUI */
+}
+
+
 #ifdef CONFIG_IEEE80211R_AP
 
 static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst,
@@ -584,6 +852,22 @@
 }
 
 
+static void hostapd_rrb_oui_receive(void *ctx, const u8 *src_addr,
+				    const u8 *dst_addr, u8 oui_suffix,
+				    const u8 *buf, size_t len)
+{
+	struct hostapd_data *hapd = ctx;
+
+	wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> "
+		   MACSTR, MAC2STR(src_addr), MAC2STR(dst_addr));
+	if (!is_multicast_ether_addr(dst_addr) &&
+	    os_memcmp(hapd->own_addr, dst_addr, ETH_ALEN) != 0)
+		return;
+	wpa_ft_rrb_oui_rx(hapd->wpa_auth, src_addr, dst_addr, oui_suffix, buf,
+			  len);
+}
+
+
 static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr,
 				      u8 *tspec_ie, size_t tspec_ielen)
 {
@@ -591,6 +875,58 @@
 	return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen);
 }
 
+
+
+static int hostapd_wpa_register_ft_oui(struct hostapd_data *hapd,
+				       const char *ft_iface)
+{
+	hapd->oui_pull = eth_p_oui_register(hapd, ft_iface,
+					    FT_PACKET_R0KH_R1KH_PULL,
+					    hostapd_rrb_oui_receive, hapd);
+	if (!hapd->oui_pull)
+		return -1;
+
+	hapd->oui_resp = eth_p_oui_register(hapd, ft_iface,
+					    FT_PACKET_R0KH_R1KH_RESP,
+					    hostapd_rrb_oui_receive, hapd);
+	if (!hapd->oui_resp)
+		return -1;
+
+	hapd->oui_push = eth_p_oui_register(hapd, ft_iface,
+					    FT_PACKET_R0KH_R1KH_PUSH,
+					    hostapd_rrb_oui_receive, hapd);
+	if (!hapd->oui_push)
+		return -1;
+
+	hapd->oui_sreq = eth_p_oui_register(hapd, ft_iface,
+					    FT_PACKET_R0KH_R1KH_SEQ_REQ,
+					    hostapd_rrb_oui_receive, hapd);
+	if (!hapd->oui_sreq)
+		return -1;
+
+	hapd->oui_sresp = eth_p_oui_register(hapd, ft_iface,
+					     FT_PACKET_R0KH_R1KH_SEQ_RESP,
+					     hostapd_rrb_oui_receive, hapd);
+	if (!hapd->oui_sresp)
+		return -1;
+
+	return 0;
+}
+
+
+static void hostapd_wpa_unregister_ft_oui(struct hostapd_data *hapd)
+{
+	eth_p_oui_unregister(hapd->oui_pull);
+	hapd->oui_pull = NULL;
+	eth_p_oui_unregister(hapd->oui_resp);
+	hapd->oui_resp = NULL;
+	eth_p_oui_unregister(hapd->oui_push);
+	hapd->oui_push = NULL;
+	eth_p_oui_unregister(hapd->oui_sreq);
+	hapd->oui_sreq = NULL;
+	eth_p_oui_unregister(hapd->oui_sresp);
+	hapd->oui_sresp = NULL;
+}
 #endif /* CONFIG_IEEE80211R_AP */
 
 
@@ -612,6 +948,7 @@
 		.for_each_sta = hostapd_wpa_auth_for_each_sta,
 		.for_each_auth = hostapd_wpa_auth_for_each_auth,
 		.send_ether = hostapd_wpa_auth_send_ether,
+		.send_oui = hostapd_wpa_auth_send_oui,
 #ifdef CONFIG_IEEE80211R_AP
 		.send_ft_action = hostapd_wpa_auth_send_ft_action,
 		.add_sta = hostapd_wpa_auth_add_sta,
@@ -654,9 +991,11 @@
 #ifdef CONFIG_IEEE80211R_AP
 	if (!hostapd_drv_none(hapd) &&
 	    wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) {
-		hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ?
-					  hapd->conf->bridge :
-					  hapd->conf->iface, NULL, ETH_P_RRB,
+		const char *ft_iface;
+
+		ft_iface = hapd->conf->bridge[0] ? hapd->conf->bridge :
+			   hapd->conf->iface;
+		hapd->l2 = l2_packet_init(ft_iface, NULL, ETH_P_RRB,
 					  hostapd_rrb_receive, hapd, 1);
 		if (hapd->l2 == NULL &&
 		    (hapd->driver == NULL ||
@@ -665,6 +1004,12 @@
 				   "interface");
 			return -1;
 		}
+
+		if (hostapd_wpa_register_ft_oui(hapd, ft_iface)) {
+			wpa_printf(MSG_ERROR,
+				   "Failed to open ETH_P_OUI interface");
+			return -1;
+		}
 	}
 #endif /* CONFIG_IEEE80211R_AP */
 
@@ -705,7 +1050,12 @@
 	ieee802_1x_deinit(hapd);
 
 #ifdef CONFIG_IEEE80211R_AP
+	eloop_cancel_timeout(hostapd_wpa_ft_rrb_rx_later, hapd, ELOOP_ALL_CTX);
+	hostapd_wpa_ft_rrb_rx_later(hapd, NULL); /* flush without delivering */
+	eloop_cancel_timeout(hostapd_oui_deliver_later, hapd, ELOOP_ALL_CTX);
+	hostapd_oui_deliver_later(hapd, NULL); /* flush without delivering */
 	l2_packet_deinit(hapd->l2);
 	hapd->l2 = NULL;
+	hostapd_wpa_unregister_ft_oui(hapd);
 #endif /* CONFIG_IEEE80211R_AP */
 }
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 9564f24..befa800 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -9,6 +9,8 @@
 #ifndef WPA_AUTH_I_H
 #define WPA_AUTH_I_H
 
+#include "utils/list.h"
+
 /* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */
 #define RSNA_MAX_EAPOL_RETRIES 4
 
@@ -41,6 +43,7 @@
 	Boolean AuthenticationRequest;
 	Boolean ReAuthenticationRequest;
 	Boolean Disconnect;
+	u16 disconnect_reason; /* specific reason code to use with Disconnect */
 	u32 TimeoutCtr;
 	u32 GTimeoutCtr;
 	Boolean TimeoutEvt;
@@ -122,9 +125,10 @@
 			      const u8 *ies, size_t ies_len);
 	void *ft_pending_cb_ctx;
 	struct wpabuf *ft_pending_req_ies;
-	u8 ft_pending_pull_nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
+	u8 ft_pending_pull_nonce[FT_RRB_NONCE_LEN];
 	u8 ft_pending_auth_transaction;
 	u8 ft_pending_current_ap[ETH_ALEN];
+	int ft_pending_pull_left_retries;
 #endif /* CONFIG_IEEE80211R_AP */
 
 	int pending_1_of_4_timeout;
@@ -139,6 +143,12 @@
 	size_t fils_key_auth_len;
 	unsigned int fils_completed:1;
 #endif /* CONFIG_FILS */
+
+#ifdef CONFIG_TESTING_OPTIONS
+	void (*eapol_status_cb)(void *ctx1, void *ctx2);
+	void *eapol_status_cb_ctx1;
+	void *eapol_status_cb_ctx2;
+#endif /* CONFIG_TESTING_OPTIONS */
 };
 
 
@@ -212,6 +222,38 @@
 };
 
 
+#ifdef CONFIG_IEEE80211R_AP
+
+#define FT_REMOTE_SEQ_BACKLOG 16
+struct ft_remote_seq_rx {
+	u32 dom;
+	struct os_reltime time_offset; /* local time - offset = remote time */
+
+	/* accepted sequence numbers: (offset ... offset + 0x40000000]
+	 *   (except those in last)
+	 * dropped sequence numbers: (offset - 0x40000000 ... offset]
+	 * all others trigger SEQ_REQ message (except first message)
+	 */
+	u32 last[FT_REMOTE_SEQ_BACKLOG];
+	unsigned int num_last;
+	u32 offsetidx;
+
+	struct dl_list queue; /* send nonces + rrb msgs awaiting seq resp */
+};
+
+struct ft_remote_seq_tx {
+	u32 dom; /* non zero if initialized */
+	u32 seq;
+};
+
+struct ft_remote_seq {
+	struct ft_remote_seq_rx rx;
+	struct ft_remote_seq_tx tx;
+};
+
+#endif /* CONFIG_IEEE80211R_AP */
+
+
 int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
 		     const u8 *pmkid);
 void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
@@ -230,18 +272,6 @@
 			   int (*cb)(struct wpa_authenticator *a, void *ctx),
 			   void *cb_ctx);
 
-#ifdef CONFIG_PEERKEY
-void wpa_smk_error(struct wpa_authenticator *wpa_auth,
-		   struct wpa_state_machine *sm,
-		   const u8 *key_data, size_t key_data_len);
-void wpa_smk_m1(struct wpa_authenticator *wpa_auth,
-		struct wpa_state_machine *sm, struct wpa_eapol_key *key,
-		const u8 *key_data, size_t key_data_len);
-void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
-		struct wpa_state_machine *sm, struct wpa_eapol_key *key,
-		const u8 *key_data, size_t key_data_len);
-#endif /* CONFIG_PEERKEY */
-
 #ifdef CONFIG_IEEE80211R_AP
 int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len);
 int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
@@ -254,6 +284,9 @@
 struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void);
 void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache);
 void wpa_ft_install_ptk(struct wpa_state_machine *sm);
+int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
+			const u8 *spa, const u8 *pmk_r0,
+			const u8 *pmk_r0_name, int pairwise);
 #endif /* CONFIG_IEEE80211R_AP */
 
 #endif /* WPA_AUTH_I_H */
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index c770d62..0196d00 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -234,6 +234,20 @@
 	}
 #endif /* CONFIG_IEEE80211R_AP */
 #endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OWE);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_DPP */
 
 #ifdef CONFIG_RSN_TESTING
 	if (rsn_testing) {
@@ -254,8 +268,6 @@
 	capab = 0;
 	if (conf->rsn_preauth)
 		capab |= WPA_CAPABILITY_PREAUTH;
-	if (conf->peerkey)
-		capab |= WPA_CAPABILITY_PEERKEY_ENABLED;
 	if (conf->wmm_enabled) {
 		/* 4 PTKSA replay counters when using WMM */
 		capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
@@ -498,7 +510,8 @@
 int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
 			struct wpa_state_machine *sm,
 			const u8 *wpa_ie, size_t wpa_ie_len,
-			const u8 *mdie, size_t mdie_len)
+			const u8 *mdie, size_t mdie_len,
+			const u8 *owe_dh, size_t owe_dh_len)
 {
 	struct wpa_ie_data data;
 	int ciphers, key_mgmt, res, version;
@@ -567,6 +580,14 @@
 			selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
 		else if (data.key_mgmt & WPA_KEY_MGMT_PSK)
 			selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+#ifdef CONFIG_OWE
+		else if (data.key_mgmt & WPA_KEY_MGMT_OWE)
+			selector = RSN_AUTH_KEY_MGMT_OWE;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+		else if (data.key_mgmt & WPA_KEY_MGMT_DPP)
+			selector = RSN_AUTH_KEY_MGMT_DPP;
+#endif /* CONFIG_DPP */
 		wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
 
 		selector = wpa_cipher_to_suite(WPA_PROTO_RSN,
@@ -659,6 +680,14 @@
 #endif /* CONFIG_SAE */
 	else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+#ifdef CONFIG_OWE
+	else if (key_mgmt & WPA_KEY_MGMT_OWE)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_OWE;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+	else if (key_mgmt & WPA_KEY_MGMT_DPP)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_DPP;
+#endif /* CONFIG_DPP */
 	else
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
 
@@ -723,6 +752,19 @@
 	}
 #endif /* CONFIG_IEEE80211R_AP */
 
+#ifdef CONFIG_OWE
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && !owe_dh) {
+		wpa_printf(MSG_DEBUG,
+			   "OWE: No Diffie-Hellman Parameter element");
+		return WPA_INVALID_AKMP;
+	}
+	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");
+		return WPA_INVALID_AKMP;
+	}
+#endif /* CONFIG_OWE */
+
 	sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
 	if (sm->pairwise < 0)
 		return WPA_INVALID_PAIRWISE;
@@ -775,6 +817,14 @@
 		os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN);
 	}
 
+#ifdef CONFIG_DPP
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && !sm->pmksa) {
+		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+				 "No PMKSA cache entry found for DPP");
+		return WPA_INVALID_PMKID;
+	}
+#endif /* CONFIG_DPP */
+
 	if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) {
 		os_free(sm->wpa_ie);
 		sm->wpa_ie = os_malloc(wpa_ie_len);
@@ -867,36 +917,6 @@
 		return 0;
 	}
 
-#ifdef CONFIG_PEERKEY
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) {
-		ie->smk = pos + 2 + RSN_SELECTOR_LEN;
-		ie->smk_len = pos[1] - RSN_SELECTOR_LEN;
-		return 0;
-	}
-
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) {
-		ie->nonce = pos + 2 + RSN_SELECTOR_LEN;
-		ie->nonce_len = pos[1] - RSN_SELECTOR_LEN;
-		return 0;
-	}
-
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) {
-		ie->lifetime = pos + 2 + RSN_SELECTOR_LEN;
-		ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN;
-		return 0;
-	}
-
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) {
-		ie->error = pos + 2 + RSN_SELECTOR_LEN;
-		ie->error_len = pos[1] - RSN_SELECTOR_LEN;
-		return 0;
-	}
-#endif /* CONFIG_PEERKEY */
-
 #ifdef CONFIG_IEEE80211W
 	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
 	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
@@ -990,3 +1010,19 @@
 {
 	return sm ? sm->mgmt_frame_prot : 0;
 }
+
+
+#ifdef CONFIG_OWE
+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)
+{
+	int res;
+
+	res = wpa_write_rsn_ie(&sm->wpa_auth->conf, pos, max_len,
+			       sm->pmksa ? sm->pmksa->pmkid : NULL);
+	if (res < 0)
+		return pos;
+	return pos + res;
+}
+#endif /* CONFIG_OWE */
diff --git a/src/ap/wpa_auth_ie.h b/src/ap/wpa_auth_ie.h
index 5c3bd18..73e4333 100644
--- a/src/ap/wpa_auth_ie.h
+++ b/src/ap/wpa_auth_ie.h
@@ -19,16 +19,6 @@
 	size_t gtk_len;
 	const u8 *mac_addr;
 	size_t mac_addr_len;
-#ifdef CONFIG_PEERKEY
-	const u8 *smk;
-	size_t smk_len;
-	const u8 *nonce;
-	size_t nonce_len;
-	const u8 *lifetime;
-	size_t lifetime_len;
-	const u8 *error;
-	size_t error_len;
-#endif /* CONFIG_PEERKEY */
 #ifdef CONFIG_IEEE80211W
 	const u8 *igtk;
 	size_t igtk_len;
diff --git a/src/common/defs.h b/src/common/defs.h
index eaccced..1de099f 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -55,6 +55,8 @@
 #define WPA_KEY_MGMT_FILS_SHA384 BIT(19)
 #define WPA_KEY_MGMT_FT_FILS_SHA256 BIT(20)
 #define WPA_KEY_MGMT_FT_FILS_SHA384 BIT(21)
+#define WPA_KEY_MGMT_OWE BIT(22)
+#define WPA_KEY_MGMT_DPP BIT(23)
 
 static inline int wpa_key_mgmt_wpa_ieee8021x(int akm)
 {
@@ -136,7 +138,9 @@
 	return wpa_key_mgmt_wpa_ieee8021x(akm) ||
 		wpa_key_mgmt_wpa_psk(akm) ||
 		wpa_key_mgmt_fils(akm) ||
-		wpa_key_mgmt_sae(akm);
+		wpa_key_mgmt_sae(akm) ||
+		akm == WPA_KEY_MGMT_OWE ||
+		akm == WPA_KEY_MGMT_DPP;
 }
 
 static inline int wpa_key_mgmt_wpa_any(int akm)
@@ -161,7 +165,12 @@
 #define WPA_AUTH_ALG_FT BIT(3)
 #define WPA_AUTH_ALG_SAE BIT(4)
 #define WPA_AUTH_ALG_FILS BIT(5)
+#define WPA_AUTH_ALG_FILS_SK_PFS BIT(6)
 
+static inline int wpa_auth_alg_fils(int alg)
+{
+	return !!(alg & (WPA_AUTH_ALG_FILS | WPA_AUTH_ALG_FILS_SK_PFS));
+}
 
 enum wpa_alg {
 	WPA_ALG_NONE,
@@ -380,4 +389,8 @@
 	SIM_STATE_ERROR,
 };
 
+#define OCE_STA BIT(0)
+#define OCE_STA_CFON BIT(1)
+#define OCE_AP BIT(2)
+
 #endif /* DEFS_H */
diff --git a/src/common/dhcp.h b/src/common/dhcp.h
index f2ef61e..e38512c 100644
--- a/src/common/dhcp.h
+++ b/src/common/dhcp.h
@@ -10,7 +10,13 @@
 #define DHCP_H
 
 #include <netinet/ip.h>
+#if __FAVOR_BSD
 #include <netinet/udp.h>
+#else
+#define __FAVOR_BSD 1
+#include <netinet/udp.h>
+#undef __FAVOR_BSD
+#endif
 
 #define DHCP_SERVER_PORT 67
 #define DHCP_CLIENT_PORT 68
diff --git a/src/common/dpp.c b/src/common/dpp.c
new file mode 100644
index 0000000..5107950
--- /dev/null
+++ b/src/common/dpp.c
@@ -0,0 +1,5998 @@
+/*
+ * DPP functionality shared between hostapd and wpa_supplicant
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <openssl/opensslv.h>
+#include <openssl/err.h>
+
+#include "utils/common.h"
+#include "utils/base64.h"
+#include "utils/json.h"
+#include "common/ieee802_11_common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "crypto/sha384.h"
+#include "crypto/sha512.h"
+#include "dpp.h"
+
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+/* Compatibility wrappers for older versions. */
+
+static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
+{
+	sig->r = r;
+	sig->s = s;
+	return 1;
+}
+
+
+static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr,
+			   const BIGNUM **ps)
+{
+	if (pr)
+		*pr = sig->r;
+	if (ps)
+		*ps = sig->s;
+}
+
+#endif
+
+
+static const struct dpp_curve_params dpp_curves[] = {
+	/* The mandatory to support and the default NIST P-256 curve needs to
+	 * be the first entry on this list. */
+	{ "prime256v1", 32, 32, 16, 32, "P-256", 19, "ES256" },
+	{ "secp384r1", 48, 48, 24, 48, "P-384", 20, "ES384" },
+	{ "secp521r1", 64, 64, 32, 66, "P-521", 21, "ES512" },
+	{ "brainpoolP256r1", 32, 32, 16, 32, "BP-256", 28, "BS256" },
+	{ "brainpoolP384r1", 48, 48, 24, 48, "BP-384", 29, "BS384" },
+	{ "brainpoolP512r1", 64, 64, 32, 64, "BP-512", 30, "BS512" },
+	{ NULL, 0, 0, 0, 0, NULL, 0, NULL }
+};
+
+
+/* Role-specific elements for PKEX */
+
+/* NIST P-256 */
+static const u8 pkex_init_x_p256[32] = {
+	0x56, 0x26, 0x12, 0xcf, 0x36, 0x48, 0xfe, 0x0b,
+	0x07, 0x04, 0xbb, 0x12, 0x22, 0x50, 0xb2, 0x54,
+	0xb1, 0x94, 0x64, 0x7e, 0x54, 0xce, 0x08, 0x07,
+	0x2e, 0xec, 0xca, 0x74, 0x5b, 0x61, 0x2d, 0x25
+ };
+static const u8 pkex_init_y_p256[32] = {
+	0x3e, 0x44, 0xc7, 0xc9, 0x8c, 0x1c, 0xa1, 0x0b,
+	0x20, 0x09, 0x93, 0xb2, 0xfd, 0xe5, 0x69, 0xdc,
+	0x75, 0xbc, 0xad, 0x33, 0xc1, 0xe7, 0xc6, 0x45,
+	0x4d, 0x10, 0x1e, 0x6a, 0x3d, 0x84, 0x3c, 0xa4
+ };
+static const u8 pkex_resp_x_p256[32] = {
+	0x1e, 0xa4, 0x8a, 0xb1, 0xa4, 0xe8, 0x42, 0x39,
+	0xad, 0x73, 0x07, 0xf2, 0x34, 0xdf, 0x57, 0x4f,
+	0xc0, 0x9d, 0x54, 0xbe, 0x36, 0x1b, 0x31, 0x0f,
+	0x59, 0x91, 0x52, 0x33, 0xac, 0x19, 0x9d, 0x76
+};
+static const u8 pkex_resp_y_p256[32] = {
+	0x26, 0x04, 0x09, 0x45, 0x0a, 0x05, 0x20, 0xe7,
+	0xa7, 0x27, 0xc1, 0x36, 0x76, 0x85, 0xca, 0x3e,
+	0x42, 0x16, 0xf4, 0x89, 0x85, 0x34, 0x6e, 0xd5,
+	0x17, 0xde, 0xc0, 0xb8, 0xad, 0xfd, 0xb2, 0x98
+};
+
+/* NIST P-384 */
+static const u8 pkex_init_x_p384[48] = {
+	0x95, 0x3f, 0x42, 0x9e, 0x50, 0x7f, 0xf9, 0xaa,
+	0xac, 0x1a, 0xf2, 0x85, 0x2e, 0x64, 0x91, 0x68,
+	0x64, 0xc4, 0x3c, 0xb7, 0x5c, 0xf8, 0xc9, 0x53,
+	0x6e, 0x58, 0x4c, 0x7f, 0xc4, 0x64, 0x61, 0xac,
+	0x51, 0x8a, 0x6f, 0xfe, 0xab, 0x74, 0xe6, 0x12,
+	0x81, 0xac, 0x38, 0x5d, 0x41, 0xe6, 0xb9, 0xa3
+};
+static const u8 pkex_init_y_p384[48] = {
+	0x89, 0xd0, 0x97, 0x7b, 0x59, 0x4f, 0xa6, 0xd6,
+	0x7c, 0x5d, 0x93, 0x5b, 0x93, 0xc4, 0x07, 0xa9,
+	0x89, 0xee, 0xd5, 0xcd, 0x6f, 0x42, 0xf8, 0x38,
+	0xc8, 0xc6, 0x62, 0x24, 0x69, 0x0c, 0xd4, 0x48,
+	0xd8, 0x44, 0xd6, 0xc2, 0xe8, 0xcc, 0x62, 0x6b,
+	0x3c, 0x25, 0x53, 0xba, 0x4f, 0x71, 0xf8, 0xe7
+};
+static const u8 pkex_resp_x_p384[48] = {
+	0xad, 0xbe, 0xd7, 0x1d, 0x3a, 0x71, 0x64, 0x98,
+	0x5f, 0xb4, 0xd6, 0x4b, 0x50, 0xd0, 0x84, 0x97,
+	0x4b, 0x7e, 0x57, 0x70, 0xd2, 0xd9, 0xf4, 0x92,
+	0x2a, 0x3f, 0xce, 0x99, 0xc5, 0x77, 0x33, 0x44,
+	0x14, 0x56, 0x92, 0xcb, 0xae, 0x46, 0x64, 0xdf,
+	0xe0, 0xbb, 0xd7, 0xb1, 0x29, 0x20, 0x72, 0xdf
+};
+static const u8 pkex_resp_y_p384[48] = {
+	0x54, 0x58, 0x20, 0xad, 0x55, 0x1d, 0xca, 0xf3,
+	0x1c, 0x8a, 0xcd, 0x19, 0x40, 0xf9, 0x37, 0x83,
+	0xc7, 0xd6, 0xb3, 0x13, 0x7d, 0x53, 0x28, 0x5c,
+	0xf6, 0x2d, 0xf1, 0xdd, 0xa5, 0x8b, 0xad, 0x5d,
+	0x81, 0xab, 0xb1, 0x00, 0x39, 0xd6, 0xcc, 0x9c,
+	0xea, 0x1e, 0x84, 0x1d, 0xbf, 0xe3, 0x35, 0xf9
+};
+
+/* NIST P-521 */
+static const u8 pkex_init_x_p521[66] = {
+	0x00, 0x16, 0x20, 0x45, 0x19, 0x50, 0x95, 0x23,
+	0x0d, 0x24, 0xbe, 0x00, 0x87, 0xdc, 0xfa, 0xf0,
+	0x58, 0x9a, 0x01, 0x60, 0x07, 0x7a, 0xca, 0x76,
+	0x01, 0xab, 0x2d, 0x5a, 0x46, 0xcd, 0x2c, 0xb5,
+	0x11, 0x9a, 0xff, 0xaa, 0x48, 0x04, 0x91, 0x38,
+	0xcf, 0x86, 0xfc, 0xa4, 0xa5, 0x0f, 0x47, 0x01,
+	0x80, 0x1b, 0x30, 0xa3, 0xae, 0xe8, 0x1c, 0x2e,
+	0xea, 0xcc, 0xf0, 0x03, 0x9f, 0x77, 0x4c, 0x8d,
+	0x97, 0x76
+};
+static const u8 pkex_init_y_p521[66] = {
+	0x01, 0x4c, 0x71, 0xfd, 0x1b, 0xd5, 0x9c, 0xa6,
+	0xed, 0x39, 0xef, 0x45, 0xc5, 0x06, 0xfd, 0x66,
+	0xc0, 0xeb, 0x0f, 0xbf, 0x21, 0xa3, 0x36, 0x74,
+	0xfd, 0xaa, 0x05, 0x6e, 0x4e, 0x33, 0x95, 0x42,
+	0x1a, 0x9d, 0x3f, 0x3a, 0x1c, 0x5e, 0xa8, 0x60,
+	0xf7, 0xe5, 0x59, 0x1d, 0x07, 0xaa, 0x6f, 0x40,
+	0x0a, 0x59, 0x3c, 0x27, 0xad, 0xe0, 0x48, 0xfd,
+	0xd1, 0x83, 0x37, 0x4c, 0xdf, 0xe1, 0x86, 0x72,
+	0xfc, 0x57
+};
+static const u8 pkex_resp_x_p521[66] = {
+	0x00, 0x79, 0xe4, 0x4d, 0x6b, 0x5e, 0x12, 0x0a,
+	0x18, 0x2c, 0xb3, 0x05, 0x77, 0x0f, 0xc3, 0x44,
+	0x1a, 0xcd, 0x78, 0x46, 0x14, 0xee, 0x46, 0x3f,
+	0xab, 0xc9, 0x59, 0x7c, 0x85, 0xa0, 0xc2, 0xfb,
+	0x02, 0x32, 0x99, 0xde, 0x5d, 0xe1, 0x0d, 0x48,
+	0x2d, 0x71, 0x7d, 0x8d, 0x3f, 0x61, 0x67, 0x9e,
+	0x2b, 0x8b, 0x12, 0xde, 0x10, 0x21, 0x55, 0x0a,
+	0x5b, 0x2d, 0xe8, 0x05, 0x09, 0xf6, 0x20, 0x97,
+	0x84, 0xb4
+};
+static const u8 pkex_resp_y_p521[66] = {
+	0x01, 0xb9, 0x9c, 0xc6, 0x41, 0x32, 0x5b, 0xd2,
+	0x35, 0xd8, 0x8b, 0x2b, 0xe4, 0x6e, 0xcc, 0xdf,
+	0x7c, 0x38, 0xc4, 0x5b, 0xf6, 0x74, 0x71, 0x5c,
+	0x77, 0x16, 0x8a, 0x80, 0xa9, 0x84, 0xc7, 0x7b,
+	0x9d, 0xfd, 0x83, 0x6f, 0xae, 0xf8, 0x24, 0x16,
+	0x2f, 0x21, 0x25, 0x65, 0xa2, 0x1a, 0x6b, 0x2d,
+	0x30, 0x62, 0xb3, 0xcc, 0x6e, 0x59, 0x3c, 0x7f,
+	0x58, 0x91, 0x81, 0x72, 0x07, 0x8c, 0x91, 0xac,
+	0x31, 0x1e
+};
+
+/* Brainpool P-256r1 */
+static const u8 pkex_init_x_bp_p256r1[32] = {
+	0x46, 0x98, 0x18, 0x6c, 0x27, 0xcd, 0x4b, 0x10,
+	0x7d, 0x55, 0xa3, 0xdd, 0x89, 0x1f, 0x9f, 0xca,
+	0xc7, 0x42, 0x5b, 0x8a, 0x23, 0xed, 0xf8, 0x75,
+	0xac, 0xc7, 0xe9, 0x8d, 0xc2, 0x6f, 0xec, 0xd8
+};
+static const u8 pkex_init_y_bp_p256r1[32] = {
+	0x16, 0x30, 0x68, 0x32, 0x3b, 0xb0, 0x21, 0xee,
+	0xeb, 0xf7, 0xb6, 0x7c, 0xae, 0x52, 0x26, 0x42,
+	0x59, 0x28, 0x58, 0xb6, 0x14, 0x90, 0xed, 0x69,
+	0xd0, 0x67, 0xea, 0x25, 0x60, 0x0f, 0xa9, 0x6c
+};
+static const u8 pkex_resp_x_bp_p256r1[32] = {
+	0x90, 0x18, 0x84, 0xc9, 0xdc, 0xcc, 0xb5, 0x2f,
+	0x4a, 0x3f, 0x4f, 0x18, 0x0a, 0x22, 0x56, 0x6a,
+	0xa9, 0xef, 0xd4, 0xe6, 0xc3, 0x53, 0xc2, 0x1a,
+	0x23, 0x54, 0xdd, 0x08, 0x7e, 0x10, 0xd8, 0xe3
+};
+static const u8 pkex_resp_y_bp_p256r1[32] = {
+	0x2a, 0xfa, 0x98, 0x9b, 0xe3, 0xda, 0x30, 0xfd,
+	0x32, 0x28, 0xcb, 0x66, 0xfb, 0x40, 0x7f, 0xf2,
+	0xb2, 0x25, 0x80, 0x82, 0x44, 0x85, 0x13, 0x7e,
+	0x4b, 0xb5, 0x06, 0xc0, 0x03, 0x69, 0x23, 0x64
+};
+
+/* Brainpool P-384r1 */
+static const u8 pkex_init_x_bp_p384r1[48] = {
+	0x0a, 0x2c, 0xeb, 0x49, 0x5e, 0xb7, 0x23, 0xbd,
+	0x20, 0x5b, 0xe0, 0x49, 0xdf, 0xcf, 0xcf, 0x19,
+	0x37, 0x36, 0xe1, 0x2f, 0x59, 0xdb, 0x07, 0x06,
+	0xb5, 0xeb, 0x2d, 0xae, 0xc2, 0xb2, 0x38, 0x62,
+	0xa6, 0x73, 0x09, 0xa0, 0x6c, 0x0a, 0xa2, 0x30,
+	0x99, 0xeb, 0xf7, 0x1e, 0x47, 0xb9, 0x5e, 0xbe
+};
+static const u8 pkex_init_y_bp_p384r1[48] = {
+	0x54, 0x76, 0x61, 0x65, 0x75, 0x5a, 0x2f, 0x99,
+	0x39, 0x73, 0xca, 0x6c, 0xf9, 0xf7, 0x12, 0x86,
+	0x54, 0xd5, 0xd4, 0xad, 0x45, 0x7b, 0xbf, 0x32,
+	0xee, 0x62, 0x8b, 0x9f, 0x52, 0xe8, 0xa0, 0xc9,
+	0xb7, 0x9d, 0xd1, 0x09, 0xb4, 0x79, 0x1c, 0x3e,
+	0x1a, 0xbf, 0x21, 0x45, 0x66, 0x6b, 0x02, 0x52
+};
+static const u8 pkex_resp_x_bp_p384r1[48] = {
+	0x03, 0xa2, 0x57, 0xef, 0xe8, 0x51, 0x21, 0xa0,
+	0xc8, 0x9e, 0x21, 0x02, 0xb5, 0x9a, 0x36, 0x25,
+	0x74, 0x22, 0xd1, 0xf2, 0x1b, 0xa8, 0x9a, 0x9b,
+	0x97, 0xbc, 0x5a, 0xeb, 0x26, 0x15, 0x09, 0x71,
+	0x77, 0x59, 0xec, 0x8b, 0xb7, 0xe1, 0xe8, 0xce,
+	0x65, 0xb8, 0xaf, 0xf8, 0x80, 0xae, 0x74, 0x6c
+};
+static const u8 pkex_resp_y_bp_p384r1[48] = {
+	0x2f, 0xd9, 0x6a, 0xc7, 0x3e, 0xec, 0x76, 0x65,
+	0x2d, 0x38, 0x7f, 0xec, 0x63, 0x26, 0x3f, 0x04,
+	0xd8, 0x4e, 0xff, 0xe1, 0x0a, 0x51, 0x74, 0x70,
+	0xe5, 0x46, 0x63, 0x7f, 0x5c, 0xc0, 0xd1, 0x7c,
+	0xfb, 0x2f, 0xea, 0xe2, 0xd8, 0x0f, 0x84, 0xcb,
+	0xe9, 0x39, 0x5c, 0x64, 0xfe, 0xcb, 0x2f, 0xf1
+};
+
+/* Brainpool P-512r1 */
+static const u8 pkex_init_x_bp_p512r1[64] = {
+	0x4c, 0xe9, 0xb6, 0x1c, 0xe2, 0x00, 0x3c, 0x9c,
+	0xa9, 0xc8, 0x56, 0x52, 0xaf, 0x87, 0x3e, 0x51,
+	0x9c, 0xbb, 0x15, 0x31, 0x1e, 0xc1, 0x05, 0xfc,
+	0x7c, 0x77, 0xd7, 0x37, 0x61, 0x27, 0xd0, 0x95,
+	0x98, 0xee, 0x5d, 0xa4, 0x3d, 0x09, 0xdb, 0x3d,
+	0xfa, 0x89, 0x9e, 0x7f, 0xa6, 0xa6, 0x9c, 0xff,
+	0x83, 0x5c, 0x21, 0x6c, 0x3e, 0xf2, 0xfe, 0xdc,
+	0x63, 0xe4, 0xd1, 0x0e, 0x75, 0x45, 0x69, 0x0f
+};
+static const u8 pkex_init_y_bp_p512r1[64] = {
+	0x5a, 0x28, 0x01, 0xbe, 0x96, 0x82, 0x4e, 0xf6,
+	0xfa, 0xed, 0x7d, 0xfd, 0x48, 0x8b, 0x48, 0x4e,
+	0xd1, 0x97, 0x87, 0xc4, 0x05, 0x5d, 0x15, 0x2a,
+	0xf4, 0x91, 0x4b, 0x75, 0x90, 0xd9, 0x34, 0x2c,
+	0x3c, 0x12, 0xf2, 0xf5, 0x25, 0x94, 0x24, 0x34,
+	0xa7, 0x6d, 0x66, 0xbc, 0x27, 0xa4, 0xa0, 0x8d,
+	0xd5, 0xe1, 0x54, 0xa3, 0x55, 0x26, 0xd4, 0x14,
+	0x17, 0x0f, 0xc1, 0xc7, 0x3d, 0x68, 0x7f, 0x5a
+};
+static const u8 pkex_resp_x_bp_p512r1[64] = {
+	0x2a, 0x60, 0x32, 0x27, 0xa1, 0xe6, 0x94, 0x72,
+	0x1c, 0x48, 0xbe, 0xc5, 0x77, 0x14, 0x30, 0x76,
+	0xe4, 0xbf, 0xf7, 0x7b, 0xc5, 0xfd, 0xdf, 0x19,
+	0x1e, 0x0f, 0xdf, 0x1c, 0x40, 0xfa, 0x34, 0x9e,
+	0x1f, 0x42, 0x24, 0xa3, 0x2c, 0xd5, 0xc7, 0xc9,
+	0x7b, 0x47, 0x78, 0x96, 0xf1, 0x37, 0x0e, 0x88,
+	0xcb, 0xa6, 0x52, 0x29, 0xd7, 0xa8, 0x38, 0x29,
+	0x8e, 0x6e, 0x23, 0x47, 0xd4, 0x4b, 0x70, 0x3e
+};
+static const u8 pkex_resp_y_bp_p512r1[64] = {
+	0x2a, 0xbe, 0x59, 0xe6, 0xc4, 0xb3, 0xd8, 0x09,
+	0x66, 0x89, 0x0a, 0x2d, 0x19, 0xf0, 0x9c, 0x9f,
+	0xb4, 0xab, 0x8f, 0x50, 0x68, 0x3c, 0x74, 0x64,
+	0x4e, 0x19, 0x55, 0x81, 0x9b, 0x48, 0x5c, 0xf4,
+	0x12, 0x8d, 0xb9, 0xd8, 0x02, 0x5b, 0xe1, 0x26,
+	0x7e, 0x19, 0x5c, 0xfd, 0x70, 0xf7, 0x4b, 0xdc,
+	0xb5, 0x5d, 0xc1, 0x7a, 0xe9, 0xd1, 0x05, 0x2e,
+	0xd1, 0xfd, 0x2f, 0xce, 0x63, 0x77, 0x48, 0x2c
+};
+
+
+static int dpp_hash_vector(const struct dpp_curve_params *curve,
+			   size_t num_elem, const u8 *addr[], const size_t *len,
+			   u8 *mac)
+{
+	if (curve->hash_len == 32)
+		return sha256_vector(num_elem, addr, len, mac);
+	if (curve->hash_len == 48)
+		return sha384_vector(num_elem, addr, len, mac);
+	if (curve->hash_len == 64)
+		return sha512_vector(num_elem, addr, len, mac);
+	return -1;
+}
+
+
+static int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len,
+			   const char *label, u8 *out, size_t outlen)
+{
+	if (hash_len == 32)
+		return hmac_sha256_kdf(secret, secret_len, NULL,
+				       (const u8 *) label, os_strlen(label),
+				       out, outlen);
+	if (hash_len == 48)
+		return hmac_sha384_kdf(secret, secret_len, NULL,
+				       (const u8 *) label, os_strlen(label),
+				       out, outlen);
+	if (hash_len == 64)
+		return hmac_sha512_kdf(secret, secret_len, NULL,
+				       (const u8 *) label, os_strlen(label),
+				       out, outlen);
+	return -1;
+}
+
+
+static int dpp_hmac_vector(size_t hash_len, const u8 *key, size_t key_len,
+			   size_t num_elem, const u8 *addr[],
+			   const size_t *len, u8 *mac)
+{
+	if (hash_len == 32)
+		return hmac_sha256_vector(key, key_len, num_elem, addr, len,
+					  mac);
+	if (hash_len == 48)
+		return hmac_sha384_vector(key, key_len, num_elem, addr, len,
+					  mac);
+	if (hash_len == 64)
+		return hmac_sha512_vector(key, key_len, num_elem, addr, len,
+					  mac);
+	return -1;
+}
+
+
+static int dpp_hmac(size_t hash_len, const u8 *key, size_t key_len,
+		    const u8 *data, size_t data_len, u8 *mac)
+{
+	if (hash_len == 32)
+		return hmac_sha256(key, key_len, data, data_len, mac);
+	if (hash_len == 48)
+		return hmac_sha384(key, key_len, data, data_len, mac);
+	if (hash_len == 64)
+		return hmac_sha512(key, key_len, data, data_len, mac);
+	return -1;
+}
+
+
+static struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix)
+{
+	int len, res;
+	EC_KEY *eckey;
+	struct wpabuf *buf;
+	unsigned char *pos;
+
+	eckey = EVP_PKEY_get1_EC_KEY(pkey);
+	if (!eckey)
+		return NULL;
+	EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED);
+	len = i2o_ECPublicKey(eckey, NULL);
+	if (len <= 0) {
+		wpa_printf(MSG_ERROR,
+			   "DDP: Failed to determine public key encoding length");
+		EC_KEY_free(eckey);
+		return NULL;
+	}
+
+	buf = wpabuf_alloc(len);
+	if (!buf) {
+		EC_KEY_free(eckey);
+		return NULL;
+	}
+
+	pos = wpabuf_put(buf, len);
+	res = i2o_ECPublicKey(eckey, &pos);
+	EC_KEY_free(eckey);
+	if (res != len) {
+		wpa_printf(MSG_ERROR,
+			   "DDP: Failed to encode public key (res=%d/%d)",
+			   res, len);
+		wpabuf_free(buf);
+		return NULL;
+	}
+
+	if (!prefix) {
+		/* Remove 0x04 prefix to match DPP definition */
+		pos = wpabuf_mhead(buf);
+		os_memmove(pos, pos + 1, len - 1);
+		buf->used--;
+	}
+
+	return buf;
+}
+
+
+static EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group,
+					     const u8 *buf_x, const u8 *buf_y,
+					     size_t len)
+{
+	EC_KEY *eckey = NULL;
+	BN_CTX *ctx;
+	EC_POINT *point = NULL;
+	BIGNUM *x = NULL, *y = NULL;
+	EVP_PKEY *pkey = NULL;
+
+	ctx = BN_CTX_new();
+	if (!ctx) {
+		wpa_printf(MSG_ERROR, "DPP: Out of memory");
+		return NULL;
+	}
+
+	point = EC_POINT_new(group);
+	x = BN_bin2bn(buf_x, len, NULL);
+	y = BN_bin2bn(buf_y, len, NULL);
+	if (!point || !x || !y) {
+		wpa_printf(MSG_ERROR, "DPP: Out of memory");
+		goto fail;
+	}
+
+	if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	if (!EC_POINT_is_on_curve(group, point, ctx) ||
+	    EC_POINT_is_at_infinity(group, point)) {
+		wpa_printf(MSG_ERROR, "DPP: Invalid point");
+		goto fail;
+	}
+
+	eckey = EC_KEY_new();
+	if (!eckey ||
+	    EC_KEY_set_group(eckey, group) != 1 ||
+	    EC_KEY_set_public_key(eckey, point) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to set EC_KEY: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
+
+	pkey = EVP_PKEY_new();
+	if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) {
+		wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY");
+		goto fail;
+	}
+
+out:
+	BN_free(x);
+	BN_free(y);
+	EC_KEY_free(eckey);
+	EC_POINT_free(point);
+	BN_CTX_free(ctx);
+	return pkey;
+fail:
+	EVP_PKEY_free(pkey);
+	pkey = NULL;
+	goto out;
+}
+
+
+static EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key,
+				       const u8 *buf, size_t len)
+{
+	EC_KEY *eckey;
+	const EC_GROUP *group;
+	EVP_PKEY *pkey = NULL;
+
+	if (len & 1)
+		return NULL;
+
+	eckey = EVP_PKEY_get1_EC_KEY(group_key);
+	if (!eckey) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Could not get EC_KEY from group_key");
+		return NULL;
+	}
+
+	group = EC_KEY_get0_group(eckey);
+	if (group)
+		pkey = dpp_set_pubkey_point_group(group, buf, buf + len / 2,
+						  len / 2);
+	else
+		wpa_printf(MSG_ERROR, "DPP: Could not get EC group");
+
+	EC_KEY_free(eckey);
+	return pkey;
+}
+
+
+struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type,
+			      size_t len)
+{
+	struct wpabuf *msg;
+
+	msg = wpabuf_alloc(8 + len);
+	if (!msg)
+		return NULL;
+	wpabuf_put_u8(msg, WLAN_ACTION_PUBLIC);
+	wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC);
+	wpabuf_put_be24(msg, OUI_WFA);
+	wpabuf_put_u8(msg, DPP_OUI_TYPE);
+	wpabuf_put_u8(msg, 1); /* Crypto Suite */
+	wpabuf_put_u8(msg, type);
+	return msg;
+}
+
+
+const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len)
+{
+	u16 id, alen;
+	const u8 *pos = buf, *end = buf + len;
+
+	while (end - pos >= 4) {
+		id = WPA_GET_LE16(pos);
+		pos += 2;
+		alen = WPA_GET_LE16(pos);
+		pos += 2;
+		if (alen > end - pos)
+			return NULL;
+		if (id == req_id) {
+			*ret_len = alen;
+			return pos;
+		}
+		pos += alen;
+	}
+
+	return NULL;
+}
+
+
+int dpp_check_attrs(const u8 *buf, size_t len)
+{
+	const u8 *pos, *end;
+
+	pos = buf;
+	end = buf + len;
+	while (end - pos >= 4) {
+		u16 id, alen;
+
+		id = WPA_GET_LE16(pos);
+		pos += 2;
+		alen = WPA_GET_LE16(pos);
+		pos += 2;
+		wpa_printf(MSG_MSGDUMP, "DPP: Attribute ID %04x len %u",
+			   id, alen);
+		if (alen > end - pos) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Truncated message - not enough room for the attribute - dropped");
+			return -1;
+		}
+		pos += alen;
+	}
+
+	if (end != pos) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected octets (%d) after the last attribute",
+			   (int) (end - pos));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info)
+{
+	if (!info)
+		return;
+	os_free(info->uri);
+	os_free(info->info);
+	EVP_PKEY_free(info->pubkey);
+	os_free(info);
+}
+
+
+const char * dpp_bootstrap_type_txt(enum dpp_bootstrap_type type)
+{
+	switch (type) {
+	case DPP_BOOTSTRAP_QR_CODE:
+		return "QRCODE";
+	case DPP_BOOTSTRAP_PKEX:
+		return "PKEX";
+	}
+	return "??";
+}
+
+
+static int dpp_uri_valid_info(const char *info)
+{
+	while (*info) {
+		unsigned char val = *info++;
+
+		if (val < 0x20 || val > 0x7e || val == 0x3b)
+			return 0;
+	}
+
+	return 1;
+}
+
+
+static int dpp_clone_uri(struct dpp_bootstrap_info *bi, const char *uri)
+{
+	bi->uri = os_strdup(uri);
+	return bi->uri ? 0 : -1;
+}
+
+
+int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi,
+			    const char *chan_list)
+{
+	const char *pos = chan_list;
+	int opclass, channel, freq;
+
+	while (pos && *pos && *pos != ';') {
+		opclass = atoi(pos);
+		if (opclass <= 0)
+			goto fail;
+		pos = os_strchr(pos, '/');
+		if (!pos)
+			goto fail;
+		pos++;
+		channel = atoi(pos);
+		if (channel <= 0)
+			goto fail;
+		while (*pos >= '0' && *pos <= '9')
+			pos++;
+		freq = ieee80211_chan_to_freq(NULL, opclass, channel);
+		wpa_printf(MSG_DEBUG,
+			   "DPP: URI channel-list: opclass=%d channel=%d ==> freq=%d",
+			   opclass, channel, freq);
+		if (freq < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Ignore unknown URI channel-list channel (opclass=%d channel=%d)",
+				   opclass, channel);
+		} else if (bi->num_freq == DPP_BOOTSTRAP_MAX_FREQ) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Too many channels in URI channel-list - ignore list");
+			bi->num_freq = 0;
+			break;
+		} else {
+			bi->freq[bi->num_freq++] = freq;
+		}
+
+		if (*pos == ';' || *pos == '\0')
+			break;
+		if (*pos != ',')
+			goto fail;
+		pos++;
+	}
+
+	return 0;
+fail:
+	wpa_printf(MSG_DEBUG, "DPP: Invalid URI channel-list");
+	return -1;
+}
+
+
+int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac)
+{
+	if (!mac)
+		return 0;
+
+	if (hwaddr_aton2(mac, bi->mac_addr) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Invalid URI mac");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: URI mac: " MACSTR, MAC2STR(bi->mac_addr));
+
+	return 0;
+}
+
+
+int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info)
+{
+	const char *end;
+
+	if (!info)
+		return 0;
+
+	end = os_strchr(info, ';');
+	if (!end)
+		end = info + os_strlen(info);
+	bi->info = os_malloc(end - info + 1);
+	if (!bi->info)
+		return -1;
+	os_memcpy(bi->info, info, end - info);
+	bi->info[end - info] = '\0';
+	wpa_printf(MSG_DEBUG, "DPP: URI(information): %s", bi->info);
+	if (!dpp_uri_valid_info(bi->info)) {
+		wpa_printf(MSG_DEBUG, "DPP: Invalid URI information payload");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static const struct dpp_curve_params *
+dpp_get_curve_oid(const ASN1_OBJECT *poid)
+{
+	ASN1_OBJECT *oid;
+	int i;
+
+	for (i = 0; dpp_curves[i].name; i++) {
+		oid = OBJ_txt2obj(dpp_curves[i].name, 0);
+		if (oid && OBJ_cmp(poid, oid) == 0)
+			return &dpp_curves[i];
+	}
+	return NULL;
+}
+
+
+static const struct dpp_curve_params * dpp_get_curve_nid(int nid)
+{
+	int i, tmp;
+
+	if (!nid)
+		return NULL;
+	for (i = 0; dpp_curves[i].name; i++) {
+		tmp = OBJ_txt2nid(dpp_curves[i].name);
+		if (tmp == nid)
+			return &dpp_curves[i];
+	}
+	return NULL;
+}
+
+
+static int dpp_parse_uri_pk(struct dpp_bootstrap_info *bi, const char *info)
+{
+	const char *end;
+	u8 *data;
+	size_t data_len;
+	EVP_PKEY *pkey;
+	const unsigned char *p;
+	int res;
+	X509_PUBKEY *pub = NULL;
+	ASN1_OBJECT *ppkalg;
+	const unsigned char *pk;
+	int ppklen;
+	X509_ALGOR *pa;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+	ASN1_OBJECT *pa_oid;
+#else
+	const ASN1_OBJECT *pa_oid;
+#endif
+	const void *pval;
+	int ptype;
+	const ASN1_OBJECT *poid;
+	char buf[100];
+
+	end = os_strchr(info, ';');
+	if (!end)
+		return -1;
+
+	data = base64_decode((const unsigned char *) info, end - info,
+			     &data_len);
+	if (!data) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid base64 encoding on URI public-key");
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Base64 decoded URI public-key",
+		    data, data_len);
+
+	if (sha256_vector(1, (const u8 **) &data, &data_len,
+			  bi->pubkey_hash) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Public key hash",
+		    bi->pubkey_hash, SHA256_MAC_LEN);
+
+	/* DER encoded ASN.1 SubjectPublicKeyInfo
+	 *
+	 * SubjectPublicKeyInfo  ::=  SEQUENCE  {
+	 *      algorithm            AlgorithmIdentifier,
+	 *      subjectPublicKey     BIT STRING  }
+	 *
+	 * AlgorithmIdentifier  ::=  SEQUENCE  {
+	 *      algorithm               OBJECT IDENTIFIER,
+	 *      parameters              ANY DEFINED BY algorithm OPTIONAL  }
+	 *
+	 * subjectPublicKey = compressed format public key per ANSI X9.63
+	 * algorithm = ecPublicKey (1.2.840.10045.2.1)
+	 * parameters = shall be present and shall be OBJECT IDENTIFIER; e.g.,
+	 *       prime256v1 (1.2.840.10045.3.1.7)
+	 */
+
+	p = data;
+	pkey = d2i_PUBKEY(NULL, &p, data_len);
+	os_free(data);
+
+	if (!pkey) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not parse URI public-key SubjectPublicKeyInfo");
+		return -1;
+	}
+
+	if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: SubjectPublicKeyInfo does not describe an EC key");
+		EVP_PKEY_free(pkey);
+		return -1;
+	}
+
+	res = X509_PUBKEY_set(&pub, pkey);
+	if (res != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: Could not set pubkey");
+		goto fail;
+	}
+
+	res = X509_PUBKEY_get0_param(&ppkalg, &pk, &ppklen, &pa, pub);
+	if (res != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not extract SubjectPublicKeyInfo parameters");
+		goto fail;
+	}
+	res = OBJ_obj2txt(buf, sizeof(buf), ppkalg, 0);
+	if (res < 0 || (size_t) res >= sizeof(buf)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not extract SubjectPublicKeyInfo algorithm");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey algorithm: %s", buf);
+	if (os_strcmp(buf, "id-ecPublicKey") != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unsupported SubjectPublicKeyInfo algorithm");
+		goto fail;
+	}
+
+	X509_ALGOR_get0(&pa_oid, &ptype, (void *) &pval, pa);
+	if (ptype != V_ASN1_OBJECT) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: SubjectPublicKeyInfo parameters did not contain an OID");
+		goto fail;
+	}
+	poid = pval;
+	res = OBJ_obj2txt(buf, sizeof(buf), poid, 0);
+	if (res < 0 || (size_t) res >= sizeof(buf)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not extract SubjectPublicKeyInfo parameters OID");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey parameters: %s", buf);
+	bi->curve = dpp_get_curve_oid(poid);
+	if (!bi->curve) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unsupported SubjectPublicKeyInfo curve: %s",
+			   buf);
+		goto fail;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "DPP: URI subjectPublicKey", pk, ppklen);
+
+	X509_PUBKEY_free(pub);
+	bi->pubkey = pkey;
+	return 0;
+fail:
+	X509_PUBKEY_free(pub);
+	EVP_PKEY_free(pkey);
+	return -1;
+}
+
+
+static struct dpp_bootstrap_info * dpp_parse_uri(const char *uri)
+{
+	const char *pos = uri;
+	const char *end;
+	const char *chan_list = NULL, *mac = NULL, *info = NULL, *pk = NULL;
+	struct dpp_bootstrap_info *bi;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "DPP: URI", uri, os_strlen(uri));
+
+	if (os_strncmp(pos, "DPP:", 4) != 0) {
+		wpa_printf(MSG_INFO, "DPP: Not a DPP URI");
+		return NULL;
+	}
+	pos += 4;
+
+	for (;;) {
+		end = os_strchr(pos, ';');
+		if (!end)
+			break;
+
+		if (end == pos) {
+			/* Handle terminating ";;" and ignore unexpected ";"
+			 * for parsing robustness. */
+			pos++;
+			continue;
+		}
+
+		if (pos[0] == 'C' && pos[1] == ':' && !chan_list)
+			chan_list = pos + 2;
+		else if (pos[0] == 'M' && pos[1] == ':' && !mac)
+			mac = pos + 2;
+		else if (pos[0] == 'I' && pos[1] == ':' && !info)
+			info = pos + 2;
+		else if (pos[0] == 'K' && pos[1] == ':' && !pk)
+			pk = pos + 2;
+		else
+			wpa_hexdump_ascii(MSG_DEBUG,
+					  "DPP: Ignore unrecognized URI parameter",
+					  pos, end - pos);
+		pos = end + 1;
+	}
+
+	if (!pk) {
+		wpa_printf(MSG_INFO, "DPP: URI missing public-key");
+		return NULL;
+	}
+
+	bi = os_zalloc(sizeof(*bi));
+	if (!bi)
+		return NULL;
+
+	if (dpp_clone_uri(bi, uri) < 0 ||
+	    dpp_parse_uri_chan_list(bi, chan_list) < 0 ||
+	    dpp_parse_uri_mac(bi, mac) < 0 ||
+	    dpp_parse_uri_info(bi, info) < 0 ||
+	    dpp_parse_uri_pk(bi, pk) < 0) {
+		dpp_bootstrap_info_free(bi);
+		bi = NULL;
+	}
+
+	return bi;
+}
+
+
+struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri)
+{
+	struct dpp_bootstrap_info *bi;
+
+	bi = dpp_parse_uri(uri);
+	if (bi)
+		bi->type = DPP_BOOTSTRAP_QR_CODE;
+	return bi;
+}
+
+
+static void dpp_debug_print_key(const char *title, EVP_PKEY *key)
+{
+	EC_KEY *eckey;
+	BIO *out;
+	size_t rlen;
+	char *txt;
+	int res;
+	unsigned char *der = NULL;
+	int der_len;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return;
+
+	EVP_PKEY_print_private(out, key, 0, NULL);
+	rlen = BIO_ctrl_pending(out);
+	txt = os_malloc(rlen + 1);
+	if (txt) {
+		res = BIO_read(out, txt, rlen);
+		if (res > 0) {
+			txt[res] = '\0';
+			wpa_printf(MSG_DEBUG, "%s: %s", title, txt);
+		}
+		os_free(txt);
+	}
+	BIO_free(out);
+
+	eckey = EVP_PKEY_get1_EC_KEY(key);
+	if (!eckey)
+		return;
+
+	der_len = i2d_ECPrivateKey(eckey, &der);
+	if (der_len > 0)
+		wpa_hexdump_key(MSG_DEBUG, "DPP: ECPrivateKey", der, der_len);
+	OPENSSL_free(der);
+	if (der_len <= 0) {
+		der = NULL;
+		der_len = i2d_EC_PUBKEY(eckey, &der);
+		if (der_len > 0)
+			wpa_hexdump(MSG_DEBUG, "DPP: EC_PUBKEY", der, der_len);
+		OPENSSL_free(der);
+	}
+
+	EC_KEY_free(eckey);
+}
+
+
+static EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve)
+{
+#ifdef OPENSSL_IS_BORINGSSL
+	EVP_PKEY_CTX *kctx = NULL;
+	const EC_GROUP *group;
+	EC_KEY *ec_params;
+#else
+	EVP_PKEY_CTX *pctx, *kctx = NULL;
+#endif
+	EVP_PKEY *params = NULL, *key = NULL;
+	int nid;
+
+	wpa_printf(MSG_DEBUG, "DPP: Generating a keypair");
+
+	nid = OBJ_txt2nid(curve->name);
+	if (nid == NID_undef) {
+		wpa_printf(MSG_INFO, "DPP: Unsupported curve %s", curve->name);
+		return NULL;
+	}
+#ifdef OPENSSL_IS_BORINGSSL
+	group = EC_GROUP_new_by_curve_name(nid);
+	ec_params = EC_KEY_new();
+	if (!ec_params || EC_KEY_set_group(ec_params, group) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to generate EC_KEY parameters");
+		goto fail;
+	}
+	EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE);
+	params = EVP_PKEY_new();
+	if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to generate EVP_PKEY parameters");
+		goto fail;
+	}
+#else
+	pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
+	if (!pctx ||
+	    EVP_PKEY_paramgen_init(pctx) != 1 ||
+	    EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, nid) != 1 ||
+	    EVP_PKEY_CTX_set_ec_param_enc(pctx, OPENSSL_EC_NAMED_CURVE) != 1 ||
+	    EVP_PKEY_paramgen(pctx, &params) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to generate EVP_PKEY parameters");
+		EVP_PKEY_CTX_free(pctx);
+		goto fail;
+	}
+	EVP_PKEY_CTX_free(pctx);
+#endif
+
+	kctx = EVP_PKEY_CTX_new(params, NULL);
+	if (!kctx ||
+	    EVP_PKEY_keygen_init(kctx) != 1 ||
+	    EVP_PKEY_keygen(kctx, &key) != 1) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to generate EC key");
+		goto fail;
+	}
+
+	if (wpa_debug_show_keys)
+		dpp_debug_print_key("Own generated key", key);
+
+	EVP_PKEY_free(params);
+	EVP_PKEY_CTX_free(kctx);
+	return key;
+fail:
+	EVP_PKEY_CTX_free(kctx);
+	EVP_PKEY_free(params);
+	return NULL;
+}
+
+
+static const struct dpp_curve_params *
+dpp_get_curve_name(const char *name)
+{
+	int i;
+
+	for (i = 0; dpp_curves[i].name; i++) {
+		if (os_strcmp(name, dpp_curves[i].name) == 0 ||
+		    (dpp_curves[i].jwk_crv &&
+		     os_strcmp(name, dpp_curves[i].jwk_crv) == 0))
+			return &dpp_curves[i];
+	}
+	return NULL;
+}
+
+
+static const struct dpp_curve_params *
+dpp_get_curve_jwk_crv(const char *name)
+{
+	int i;
+
+	for (i = 0; dpp_curves[i].name; i++) {
+		if (dpp_curves[i].jwk_crv &&
+		    os_strcmp(name, dpp_curves[i].jwk_crv) == 0)
+			return &dpp_curves[i];
+	}
+	return NULL;
+}
+
+
+static EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve,
+				  const u8 *privkey, size_t privkey_len)
+{
+	EVP_PKEY *pkey;
+	EC_KEY *eckey;
+	const EC_GROUP *group;
+	int nid;
+
+	pkey = EVP_PKEY_new();
+	if (!pkey)
+		return NULL;
+	eckey = d2i_ECPrivateKey(NULL, &privkey, privkey_len);
+	if (!eckey) {
+		wpa_printf(MSG_INFO,
+			   "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		EVP_PKEY_free(pkey);
+		return NULL;
+	}
+	group = EC_KEY_get0_group(eckey);
+	if (!group) {
+		EC_KEY_free(eckey);
+		EVP_PKEY_free(pkey);
+		return NULL;
+	}
+	nid = EC_GROUP_get_curve_name(group);
+	*curve = dpp_get_curve_nid(nid);
+	if (!*curve) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Unsupported curve (nid=%d) in pre-assigned key",
+			   nid);
+		EC_KEY_free(eckey);
+		EVP_PKEY_free(pkey);
+		return NULL;
+	}
+
+	if (EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) {
+		EC_KEY_free(eckey);
+		EVP_PKEY_free(pkey);
+		return NULL;
+	}
+	return pkey;
+}
+
+
+int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi)
+{
+	unsigned char *der = NULL;
+	int der_len;
+	EC_KEY *eckey;
+	int res;
+	size_t len;
+
+	/* Need to get the compressed form of the public key through EC_KEY, so
+	 * cannot use the simpler i2d_PUBKEY() here. */
+	eckey = EVP_PKEY_get1_EC_KEY(bi->pubkey);
+	if (!eckey)
+		return -1;
+	EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED);
+	der_len = i2d_EC_PUBKEY(eckey, &der);
+	EC_KEY_free(eckey);
+	if (der_len <= 0) {
+		wpa_printf(MSG_ERROR,
+			   "DDP: Failed to build DER encoded public key");
+		OPENSSL_free(der);
+		return -1;
+	}
+
+	len = der_len;
+	res = sha256_vector(1, (const u8 **) &der, &len, bi->pubkey_hash);
+	OPENSSL_free(der);
+	if (res < 0)
+		wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+	return res;
+}
+
+
+char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
+		  const u8 *privkey, size_t privkey_len)
+{
+	unsigned char *base64 = NULL;
+	char *pos, *end;
+	size_t len;
+	unsigned char *der = NULL;
+	int der_len;
+	EC_KEY *eckey;
+
+	if (!curve) {
+		bi->curve = &dpp_curves[0];
+	} else {
+		bi->curve = dpp_get_curve_name(curve);
+		if (!bi->curve) {
+			wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
+				   curve);
+			return NULL;
+		}
+	}
+	if (privkey)
+		bi->pubkey = dpp_set_keypair(&bi->curve, privkey, privkey_len);
+	else
+		bi->pubkey = dpp_gen_keypair(bi->curve);
+	if (!bi->pubkey)
+		goto fail;
+	bi->own = 1;
+
+	/* Need to get the compressed form of the public key through EC_KEY, so
+	 * cannot use the simpler i2d_PUBKEY() here. */
+	eckey = EVP_PKEY_get1_EC_KEY(bi->pubkey);
+	if (!eckey)
+		goto fail;
+	EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED);
+	der_len = i2d_EC_PUBKEY(eckey, &der);
+	EC_KEY_free(eckey);
+	if (der_len <= 0) {
+		wpa_printf(MSG_ERROR,
+			   "DDP: Failed to build DER encoded public key");
+		goto fail;
+	}
+
+	len = der_len;
+	if (sha256_vector(1, (const u8 **) &der, &len, bi->pubkey_hash) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+		goto fail;
+	}
+
+	base64 = base64_encode(der, der_len, &len);
+	OPENSSL_free(der);
+	der = NULL;
+	if (!base64)
+		goto fail;
+	pos = (char *) base64;
+	end = pos + len;
+	for (;;) {
+		pos = os_strchr(pos, '\n');
+		if (!pos)
+			break;
+		os_memmove(pos, pos + 1, end - pos);
+	}
+	return (char *) base64;
+fail:
+	os_free(base64);
+	OPENSSL_free(der);
+	return NULL;
+}
+
+
+static int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1,
+			 unsigned int hash_len)
+{
+	u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+	const char *info = "first intermediate key";
+	int res;
+
+	/* k1 = HKDF(<>, "first intermediate key", M.x) */
+
+	/* HKDF-Extract(<>, M.x) */
+	os_memset(salt, 0, hash_len);
+	if (dpp_hmac(hash_len, salt, hash_len, Mx, Mx_len, prk) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=M.x)",
+			prk, hash_len);
+
+	/* HKDF-Expand(PRK, info, L) */
+	res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k1, hash_len);
+	os_memset(prk, 0, hash_len);
+	if (res < 0)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: k1 = HKDF-Expand(PRK, info, L)",
+			k1, hash_len);
+	return 0;
+}
+
+
+static int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2,
+			 unsigned int hash_len)
+{
+	u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+	const char *info = "second intermediate key";
+	int res;
+
+	/* k2 = HKDF(<>, "second intermediate key", N.x) */
+
+	/* HKDF-Extract(<>, N.x) */
+	os_memset(salt, 0, hash_len);
+	res = dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk);
+	if (res < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
+			prk, hash_len);
+
+	/* HKDF-Expand(PRK, info, L) */
+	res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k2, hash_len);
+	os_memset(prk, 0, hash_len);
+	if (res < 0)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: k2 = HKDF-Expand(PRK, info, L)",
+			k2, hash_len);
+	return 0;
+}
+
+
+static int dpp_derive_ke(struct dpp_authentication *auth, u8 *ke,
+			 unsigned int hash_len)
+{
+	size_t nonce_len;
+	u8 nonces[2 * DPP_MAX_NONCE_LEN];
+	const char *info_ke = "DPP Key";
+	u8 prk[DPP_MAX_HASH_LEN];
+	int res;
+	const u8 *addr[3];
+	size_t len[3];
+	size_t num_elem = 0;
+
+	/* ke = HKDF(I-nonce | R-nonce, "DPP Key", M.x | N.x [| L.x]) */
+
+	/* HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x]) */
+	nonce_len = auth->curve->nonce_len;
+	os_memcpy(nonces, auth->i_nonce, nonce_len);
+	os_memcpy(&nonces[nonce_len], auth->r_nonce, nonce_len);
+	addr[num_elem] = auth->Mx;
+	len[num_elem] = auth->secret_len;
+	num_elem++;
+	addr[num_elem] = auth->Nx;
+	len[num_elem] = auth->secret_len;
+	num_elem++;
+	if (auth->peer_bi && auth->own_bi) {
+		addr[num_elem] = auth->Lx;
+		len[num_elem] = auth->secret_len;
+		num_elem++;
+	}
+	res = dpp_hmac_vector(hash_len, nonces, 2 * nonce_len,
+			      num_elem, addr, len, prk);
+	if (res < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
+			prk, hash_len);
+
+	/* HKDF-Expand(PRK, info, L) */
+	res = dpp_hkdf_expand(hash_len, prk, hash_len, info_ke, ke, hash_len);
+	os_memset(prk, 0, hash_len);
+	if (res < 0)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ke = HKDF-Expand(PRK, info, L)",
+			ke, hash_len);
+	return 0;
+}
+
+
+struct dpp_authentication * dpp_auth_init(void *msg_ctx,
+					  struct dpp_bootstrap_info *peer_bi,
+					  struct dpp_bootstrap_info *own_bi,
+					  int configurator)
+{
+	struct dpp_authentication *auth;
+	size_t nonce_len;
+	EVP_PKEY_CTX *ctx = NULL;
+	size_t secret_len;
+	struct wpabuf *msg, *pi = NULL;
+	u8 clear[4 + DPP_MAX_NONCE_LEN + 4 + 1];
+	u8 wrapped_data[4 + DPP_MAX_NONCE_LEN + 4 + 1 + AES_BLOCK_SIZE];
+	u8 *pos;
+	const u8 *addr[2];
+	size_t len[2], siv_len, attr_len;
+	u8 *attr_start, *attr_end;
+
+	auth = os_zalloc(sizeof(*auth));
+	if (!auth)
+		return NULL;
+	auth->msg_ctx = msg_ctx;
+	auth->initiator = 1;
+	auth->configurator = configurator;
+	auth->peer_bi = peer_bi;
+	auth->own_bi = own_bi;
+	auth->curve = peer_bi->curve;
+
+	nonce_len = auth->curve->nonce_len;
+	if (random_get_bytes(auth->i_nonce, nonce_len)) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", auth->i_nonce, nonce_len);
+
+	auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+	if (!auth->own_protocol_key)
+		goto fail;
+
+	pi = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+	if (!pi)
+		goto fail;
+
+	/* ECDH: M = pI * BR */
+	ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, auth->peer_bi->pubkey) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
+	    secret_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	auth->secret_len = secret_len;
+	EVP_PKEY_CTX_free(ctx);
+	ctx = NULL;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
+			auth->Mx, auth->secret_len);
+
+	if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
+			  auth->curve->hash_len) < 0)
+		goto fail;
+
+	/* Build DPP Authentication Request frame attributes */
+	attr_len = 2 * (4 + SHA256_MAC_LEN) + 4 + wpabuf_len(pi) +
+		4 + sizeof(wrapped_data);
+	msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ, attr_len);
+	if (!msg)
+		goto fail;
+	auth->req_msg = msg;
+
+	attr_start = wpabuf_put(msg, 0);
+
+	/* Responder Bootstrapping Key Hash */
+	wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
+	wpabuf_put_le16(msg, SHA256_MAC_LEN);
+	wpabuf_put_data(msg, auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+
+	/* Initiator Bootstrapping Key Hash */
+	wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
+	wpabuf_put_le16(msg, SHA256_MAC_LEN);
+	if (auth->own_bi)
+		wpabuf_put_data(msg, auth->own_bi->pubkey_hash, SHA256_MAC_LEN);
+	else
+		os_memset(wpabuf_put(msg, SHA256_MAC_LEN), 0, SHA256_MAC_LEN);
+
+	/* Initiator Protocol Key */
+	wpabuf_put_le16(msg, DPP_ATTR_I_PROTOCOL_KEY);
+	wpabuf_put_le16(msg, wpabuf_len(pi));
+	wpabuf_put_buf(msg, pi);
+	wpabuf_free(pi);
+	pi = NULL;
+
+	/* Wrapped data ({I-nonce, I-capabilities}k1) */
+	pos = clear;
+	/* I-nonce */
+	WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+	pos += 2;
+	WPA_PUT_LE16(pos, nonce_len);
+	pos += 2;
+	os_memcpy(pos, auth->i_nonce, nonce_len);
+	pos += nonce_len;
+	/* I-capabilities */
+	WPA_PUT_LE16(pos, DPP_ATTR_I_CAPABILITIES);
+	pos += 2;
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	auth->i_capab = configurator ? DPP_CAPAB_CONFIGURATOR :
+		DPP_CAPAB_ENROLLEE;
+	*pos++ = auth->i_capab;
+
+	attr_end = wpabuf_put(msg, 0);
+
+	/* OUI, OUI type, Crypto Suite, DPP frame type */
+	addr[0] = wpabuf_head_u8(msg) + 2;
+	len[0] = 3 + 1 + 1 + 1;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+	/* Attributes before Wrapped Data */
+	addr[1] = attr_start;
+	len[1] = attr_end - attr_start;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+	siv_len = pos - clear;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
+	if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len,
+			    2, addr, len, wrapped_data) < 0)
+		goto fail;
+	siv_len += AES_BLOCK_SIZE;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, siv_len);
+
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, siv_len);
+	wpabuf_put_data(msg, wrapped_data, siv_len);
+
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Authentication Request frame attributes", msg);
+
+	return auth;
+fail:
+	wpabuf_free(pi);
+	EVP_PKEY_CTX_free(ctx);
+	dpp_auth_deinit(auth);
+	return NULL;
+}
+
+
+struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
+				   const char *json)
+{
+	size_t nonce_len;
+	size_t json_len, clear_len;
+	struct wpabuf *clear = NULL, *msg = NULL;
+	u8 *wrapped;
+
+	wpa_printf(MSG_DEBUG, "DPP: Build configuration request");
+
+	nonce_len = auth->curve->nonce_len;
+	if (random_get_bytes(auth->e_nonce, nonce_len)) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to generate E-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", auth->e_nonce, nonce_len);
+	json_len = os_strlen(json);
+	wpa_hexdump_ascii(MSG_DEBUG, "DPP: configAttr JSON", json, json_len);
+
+	/* { E-nonce, configAttrib }ke */
+	clear_len = 4 + nonce_len + 4 + json_len;
+	clear = wpabuf_alloc(clear_len);
+	msg = wpabuf_alloc(4 + clear_len + AES_BLOCK_SIZE);
+	if (!clear || !msg)
+		goto fail;
+
+	/* 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);
+
+	/* configAttrib */
+	wpabuf_put_le16(clear, DPP_ATTR_CONFIG_ATTR_OBJ);
+	wpabuf_put_le16(clear, json_len);
+	wpabuf_put_data(clear, json, json_len);
+
+	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);
+
+	/* No AES-SIV AD */
+	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),
+			    0, NULL, NULL, wrapped) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Configuration Request frame attributes", msg);
+	wpabuf_free(clear);
+	return msg;
+
+fail:
+	wpabuf_free(clear);
+	wpabuf_free(msg);
+	return NULL;
+}
+
+
+static void dpp_auth_success(struct dpp_authentication *auth)
+{
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Authentication success - clear temporary keys");
+	os_memset(auth->Mx, 0, sizeof(auth->Mx));
+	os_memset(auth->Nx, 0, sizeof(auth->Nx));
+	os_memset(auth->Lx, 0, sizeof(auth->Lx));
+	os_memset(auth->k1, 0, sizeof(auth->k1));
+	os_memset(auth->k2, 0, sizeof(auth->k2));
+
+	auth->auth_success = 1;
+}
+
+
+static int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth)
+{
+	struct wpabuf *pix, *prx, *bix, *brx;
+	const u8 *addr[7];
+	size_t len[7];
+	size_t i, num_elem = 0;
+	size_t nonce_len;
+	u8 zero = 0;
+	int res = -1;
+
+	/* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+	nonce_len = auth->curve->nonce_len;
+
+	if (auth->initiator) {
+		pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+		prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+		if (auth->own_bi)
+			bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+		else
+			bix = NULL;
+		brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+	} else {
+		pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+		prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+		if (auth->peer_bi)
+			bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+		else
+			bix = NULL;
+		brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+	}
+	if (!pix || !prx || !brx)
+		goto fail;
+
+	addr[num_elem] = auth->i_nonce;
+	len[num_elem] = nonce_len;
+	num_elem++;
+
+	addr[num_elem] = auth->r_nonce;
+	len[num_elem] = nonce_len;
+	num_elem++;
+
+	addr[num_elem] = wpabuf_head(pix);
+	len[num_elem] = wpabuf_len(pix) / 2;
+	num_elem++;
+
+	addr[num_elem] = wpabuf_head(prx);
+	len[num_elem] = wpabuf_len(prx) / 2;
+	num_elem++;
+
+	if (bix) {
+		addr[num_elem] = wpabuf_head(bix);
+		len[num_elem] = wpabuf_len(bix) / 2;
+		num_elem++;
+	}
+
+	addr[num_elem] = wpabuf_head(brx);
+	len[num_elem] = wpabuf_len(brx) / 2;
+	num_elem++;
+
+	addr[num_elem] = &zero;
+	len[num_elem] = 1;
+	num_elem++;
+
+	wpa_printf(MSG_DEBUG, "DPP: R-auth hash components");
+	for (i = 0; i < num_elem; i++)
+		wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
+	res = dpp_hash_vector(auth->curve, num_elem, addr, len, r_auth);
+	if (res == 0)
+		wpa_hexdump(MSG_DEBUG, "DPP: R-auth", r_auth,
+			    auth->curve->hash_len);
+fail:
+	wpabuf_free(pix);
+	wpabuf_free(prx);
+	wpabuf_free(bix);
+	wpabuf_free(brx);
+	return res;
+}
+
+
+static int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth)
+{
+	struct wpabuf *pix = NULL, *prx = NULL, *bix = NULL, *brx = NULL;
+	const u8 *addr[7];
+	size_t len[7];
+	size_t i, num_elem = 0;
+	size_t nonce_len;
+	u8 one = 1;
+	int res = -1;
+
+	/* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
+	nonce_len = auth->curve->nonce_len;
+
+	if (auth->initiator) {
+		pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+		prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+		if (auth->own_bi)
+			bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+		else
+			bix = NULL;
+		if (!auth->peer_bi)
+			goto fail;
+		brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+	} else {
+		pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+		prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+		if (auth->peer_bi)
+			bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+		else
+			bix = NULL;
+		if (!auth->own_bi)
+			goto fail;
+		brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+	}
+	if (!pix || !prx || !brx)
+		goto fail;
+
+	addr[num_elem] = auth->r_nonce;
+	len[num_elem] = nonce_len;
+	num_elem++;
+
+	addr[num_elem] = auth->i_nonce;
+	len[num_elem] = nonce_len;
+	num_elem++;
+
+	addr[num_elem] = wpabuf_head(prx);
+	len[num_elem] = wpabuf_len(prx) / 2;
+	num_elem++;
+
+	addr[num_elem] = wpabuf_head(pix);
+	len[num_elem] = wpabuf_len(pix) / 2;
+	num_elem++;
+
+	addr[num_elem] = wpabuf_head(brx);
+	len[num_elem] = wpabuf_len(brx) / 2;
+	num_elem++;
+
+	if (bix) {
+		addr[num_elem] = wpabuf_head(bix);
+		len[num_elem] = wpabuf_len(bix) / 2;
+		num_elem++;
+	}
+
+	addr[num_elem] = &one;
+	len[num_elem] = 1;
+	num_elem++;
+
+	wpa_printf(MSG_DEBUG, "DPP: I-auth hash components");
+	for (i = 0; i < num_elem; i++)
+		wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
+	res = dpp_hash_vector(auth->curve, num_elem, addr, len, i_auth);
+	if (res == 0)
+		wpa_hexdump(MSG_DEBUG, "DPP: I-auth", i_auth,
+			    auth->curve->hash_len);
+fail:
+	wpabuf_free(pix);
+	wpabuf_free(prx);
+	wpabuf_free(bix);
+	wpabuf_free(brx);
+	return res;
+}
+
+
+static int dpp_auth_derive_l_responder(struct dpp_authentication *auth)
+{
+	const EC_GROUP *group;
+	EC_POINT *l = NULL;
+	EC_KEY *BI = NULL, *bR = NULL, *pR = NULL;
+	const EC_POINT *BI_point;
+	BN_CTX *bnctx;
+	BIGNUM *lx, *sum, *q;
+	const BIGNUM *bR_bn, *pR_bn;
+	int ret = -1;
+	int num_bytes, offset;
+
+	/* L = ((bR + pR) modulo q) * BI */
+
+	bnctx = BN_CTX_new();
+	sum = BN_new();
+	q = BN_new();
+	lx = BN_new();
+	if (!bnctx || !sum || !q || !lx)
+		goto fail;
+	BI = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey);
+	if (!BI)
+		goto fail;
+	BI_point = EC_KEY_get0_public_key(BI);
+	group = EC_KEY_get0_group(BI);
+	if (!group)
+		goto fail;
+
+	bR = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey);
+	pR = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key);
+	if (!bR || !pR)
+		goto fail;
+	bR_bn = EC_KEY_get0_private_key(bR);
+	pR_bn = EC_KEY_get0_private_key(pR);
+	if (!bR_bn || !pR_bn)
+		goto fail;
+	if (EC_GROUP_get_order(group, q, bnctx) != 1 ||
+	    BN_mod_add(sum, bR_bn, pR_bn, q, bnctx) != 1)
+		goto fail;
+	l = EC_POINT_new(group);
+	if (!l ||
+	    EC_POINT_mul(group, l, NULL, BI_point, sum, bnctx) != 1 ||
+	    EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
+						bnctx) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	num_bytes = BN_num_bytes(lx);
+	if ((size_t) num_bytes > auth->secret_len)
+		goto fail;
+	if (auth->secret_len > (size_t) num_bytes)
+		offset = auth->secret_len - num_bytes;
+	else
+		offset = 0;
+
+	os_memset(auth->Lx, 0, offset);
+	BN_bn2bin(lx, auth->Lx + offset);
+	wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
+	ret = 0;
+fail:
+	EC_POINT_clear_free(l);
+	EC_KEY_free(BI);
+	EC_KEY_free(bR);
+	EC_KEY_free(pR);
+	BN_clear_free(lx);
+	BN_clear_free(sum);
+	BN_free(q);
+	BN_CTX_free(bnctx);
+	return ret;
+}
+
+
+static int dpp_auth_derive_l_initiator(struct dpp_authentication *auth)
+{
+	const EC_GROUP *group;
+	EC_POINT *l = NULL, *sum = NULL;
+	EC_KEY *bI = NULL, *BR = NULL, *PR = NULL;
+	const EC_POINT *BR_point, *PR_point;
+	BN_CTX *bnctx;
+	BIGNUM *lx;
+	const BIGNUM *bI_bn;
+	int ret = -1;
+	int num_bytes, offset;
+
+	/* L = bI * (BR + PR) */
+
+	bnctx = BN_CTX_new();
+	lx = BN_new();
+	if (!bnctx || !lx)
+		goto fail;
+	BR = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey);
+	PR = EVP_PKEY_get1_EC_KEY(auth->peer_protocol_key);
+	if (!BR || !PR)
+		goto fail;
+	BR_point = EC_KEY_get0_public_key(BR);
+	PR_point = EC_KEY_get0_public_key(PR);
+
+	bI = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey);
+	if (!bI)
+		goto fail;
+	group = EC_KEY_get0_group(bI);
+	bI_bn = EC_KEY_get0_private_key(bI);
+	if (!group || !bI_bn)
+		goto fail;
+	sum = EC_POINT_new(group);
+	l = EC_POINT_new(group);
+	if (!sum || !l ||
+	    EC_POINT_add(group, sum, BR_point, PR_point, bnctx) != 1 ||
+	    EC_POINT_mul(group, l, NULL, sum, bI_bn, bnctx) != 1 ||
+	    EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
+						bnctx) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	num_bytes = BN_num_bytes(lx);
+	if ((size_t) num_bytes > auth->secret_len)
+		goto fail;
+	if (auth->secret_len > (size_t) num_bytes)
+		offset = auth->secret_len - num_bytes;
+	else
+		offset = 0;
+
+	os_memset(auth->Lx, 0, offset);
+	BN_bn2bin(lx, auth->Lx + offset);
+	wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
+	ret = 0;
+fail:
+	EC_POINT_clear_free(l);
+	EC_KEY_free(bI);
+	EC_KEY_free(BR);
+	EC_KEY_free(PR);
+	BN_clear_free(lx);
+	BN_CTX_free(bnctx);
+	return ret;
+}
+
+
+static int dpp_auth_build_resp(struct dpp_authentication *auth)
+{
+	size_t nonce_len;
+	EVP_PKEY_CTX *ctx = NULL;
+	size_t secret_len;
+	struct wpabuf *msg, *pr = NULL;
+	u8 r_auth[4 + DPP_MAX_HASH_LEN];
+	u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE];
+#define DPP_AUTH_RESP_CLEAR_LEN 2 * (4 + DPP_MAX_NONCE_LEN) + 4 + 1 + \
+		4 + sizeof(wrapped_r_auth)
+	size_t wrapped_r_auth_len;
+	u8 clear[DPP_AUTH_RESP_CLEAR_LEN];
+	u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN + AES_BLOCK_SIZE];
+	u8 *pos;
+	const u8 *addr[2];
+	size_t len[2], siv_len, attr_len;
+	u8 *attr_start, *attr_end;
+
+	wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
+
+	nonce_len = auth->curve->nonce_len;
+	if (random_get_bytes(auth->r_nonce, nonce_len)) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", auth->r_nonce, nonce_len);
+
+	auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+	if (!auth->own_protocol_key)
+		goto fail;
+
+	pr = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+	if (!pr)
+		goto fail;
+
+	/* ECDH: N = pR * PI */
+	ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, auth->peer_protocol_key) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
+	    secret_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	EVP_PKEY_CTX_free(ctx);
+	ctx = NULL;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
+			auth->Nx, auth->secret_len);
+
+	if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
+			  auth->curve->hash_len) < 0)
+		goto fail;
+
+	if (auth->own_bi && auth->peer_bi) {
+		/* Mutual authentication */
+		if (dpp_auth_derive_l_responder(auth) < 0)
+			goto fail;
+	}
+
+	if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0)
+		goto fail;
+
+	/* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+	WPA_PUT_LE16(r_auth, DPP_ATTR_R_AUTH_TAG);
+	WPA_PUT_LE16(&r_auth[2], auth->curve->hash_len);
+	if (dpp_gen_r_auth(auth, r_auth + 4) < 0 ||
+	    aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+			    r_auth, 4 + auth->curve->hash_len,
+			    0, NULL, NULL, wrapped_r_auth) < 0)
+		goto fail;
+	wrapped_r_auth_len = 4 + auth->curve->hash_len + AES_BLOCK_SIZE;
+	wpa_hexdump(MSG_DEBUG, "DPP: {R-auth}ke",
+		    wrapped_r_auth, wrapped_r_auth_len);
+
+	/* Build DPP Authentication Response frame attributes */
+	attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
+		4 + wpabuf_len(pr) + 4 + sizeof(wrapped_data);
+	msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len);
+	if (!msg)
+		goto fail;
+	wpabuf_free(auth->resp_msg);
+	auth->resp_msg = msg;
+
+	attr_start = wpabuf_put(msg, 0);
+
+	/* DPP Status */
+	wpabuf_put_le16(msg, DPP_ATTR_STATUS);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, DPP_STATUS_OK);
+
+	/* Responder Bootstrapping Key Hash */
+	wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
+	wpabuf_put_le16(msg, SHA256_MAC_LEN);
+	wpabuf_put_data(msg, auth->own_bi->pubkey_hash, SHA256_MAC_LEN);
+
+	if (auth->peer_bi) {
+		/* Mutual authentication */
+		/* Initiator Bootstrapping Key Hash */
+		wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
+		wpabuf_put_le16(msg, SHA256_MAC_LEN);
+		wpabuf_put_data(msg, auth->peer_bi->pubkey_hash,
+				SHA256_MAC_LEN);
+	}
+
+	/* Responder Protocol Key */
+	wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
+	wpabuf_put_le16(msg, wpabuf_len(pr));
+	wpabuf_put_buf(msg, pr);
+	wpabuf_free(pr);
+	pr = NULL;
+
+	attr_end = wpabuf_put(msg, 0);
+
+	/* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */
+	pos = clear;
+	/* R-nonce */
+	WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE);
+	pos += 2;
+	WPA_PUT_LE16(pos, nonce_len);
+	pos += 2;
+	os_memcpy(pos, auth->r_nonce, nonce_len);
+	pos += nonce_len;
+	/* I-nonce */
+	WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+	pos += 2;
+	WPA_PUT_LE16(pos, nonce_len);
+	pos += 2;
+	os_memcpy(pos, auth->i_nonce, nonce_len);
+	pos += nonce_len;
+	/* R-capabilities */
+	WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES);
+	pos += 2;
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
+		DPP_CAPAB_ENROLLEE;
+	*pos++ = auth->r_capab;
+	/* {R-auth}ke */
+	WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA);
+	pos += 2;
+	WPA_PUT_LE16(pos, wrapped_r_auth_len);
+	pos += 2;
+	os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len);
+	pos += wrapped_r_auth_len;
+
+	/* OUI, OUI type, Crypto Suite, DPP frame type */
+	addr[0] = wpabuf_head_u8(msg) + 2;
+	len[0] = 3 + 1 + 1 + 1;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+	/* Attributes before Wrapped Data */
+	addr[1] = attr_start;
+	len[1] = attr_end - attr_start;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+	siv_len = pos - clear;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
+	if (aes_siv_encrypt(auth->k2, auth->curve->hash_len, clear, siv_len,
+			    2, addr, len, wrapped_data) < 0)
+		goto fail;
+	siv_len += AES_BLOCK_SIZE;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, siv_len);
+
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, siv_len);
+	wpabuf_put_data(msg, wrapped_data, siv_len);
+
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Authentication Response frame attributes", msg);
+
+	return 0;
+
+fail:
+	wpabuf_free(pr);
+	return -1;
+}
+
+
+static int dpp_auth_build_resp_status(struct dpp_authentication *auth,
+				      enum dpp_status_error status)
+{
+	size_t nonce_len;
+	struct wpabuf *msg;
+#define DPP_AUTH_RESP_CLEAR_LEN2 4 + DPP_MAX_NONCE_LEN + 4 + 1
+	u8 clear[DPP_AUTH_RESP_CLEAR_LEN2];
+	u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN2 + AES_BLOCK_SIZE];
+	u8 *pos;
+	const u8 *addr[2];
+	size_t len[2], siv_len, attr_len;
+	u8 *attr_start, *attr_end;
+
+	wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
+
+	/* Build DPP Authentication Response frame attributes */
+	attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) + 4 + sizeof(wrapped_data);
+	msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len);
+	if (!msg)
+		goto fail;
+	wpabuf_free(auth->resp_msg);
+	auth->resp_msg = msg;
+
+	attr_start = wpabuf_put(msg, 0);
+
+	/* DPP Status */
+	wpabuf_put_le16(msg, DPP_ATTR_STATUS);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, status);
+
+	/* Responder Bootstrapping Key Hash */
+	wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
+	wpabuf_put_le16(msg, SHA256_MAC_LEN);
+	wpabuf_put_data(msg, auth->own_bi->pubkey_hash, SHA256_MAC_LEN);
+
+	if (auth->peer_bi) {
+		/* Mutual authentication */
+		/* Initiator Bootstrapping Key Hash */
+		wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
+		wpabuf_put_le16(msg, SHA256_MAC_LEN);
+		wpabuf_put_data(msg, auth->peer_bi->pubkey_hash,
+				SHA256_MAC_LEN);
+	}
+
+	attr_end = wpabuf_put(msg, 0);
+
+	/* Wrapped data ({I-nonce, R-capabilities}k1) */
+	pos = clear;
+	/* I-nonce */
+	nonce_len = auth->curve->nonce_len;
+	WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+	pos += 2;
+	WPA_PUT_LE16(pos, nonce_len);
+	pos += 2;
+	os_memcpy(pos, auth->i_nonce, nonce_len);
+	pos += nonce_len;
+	/* R-capabilities */
+	WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES);
+	pos += 2;
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
+		DPP_CAPAB_ENROLLEE;
+	*pos++ = auth->r_capab;
+
+	/* OUI, OUI type, Crypto Suite, DPP frame type */
+	addr[0] = wpabuf_head_u8(msg) + 2;
+	len[0] = 3 + 1 + 1 + 1;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+	/* Attributes before Wrapped Data */
+	addr[1] = attr_start;
+	len[1] = attr_end - attr_start;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+	siv_len = pos - clear;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
+	if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len,
+			    2, addr, len, wrapped_data) < 0)
+		goto fail;
+	siv_len += AES_BLOCK_SIZE;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, siv_len);
+
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, siv_len);
+	wpabuf_put_data(msg, wrapped_data, siv_len);
+
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Authentication Response frame attributes", msg);
+
+	return 0;
+
+fail:
+	return -1;
+}
+
+
+struct dpp_authentication *
+dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
+		struct dpp_bootstrap_info *peer_bi,
+		struct dpp_bootstrap_info *own_bi,
+		unsigned int freq, const u8 *hdr, const u8 *attr_start,
+		const u8 *wrapped_data,	u16 wrapped_data_len)
+{
+	EVP_PKEY *pi = NULL;
+	EVP_PKEY_CTX *ctx = NULL;
+	size_t secret_len;
+	const u8 *addr[2];
+	size_t len[2];
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	const u8 *i_proto, *i_nonce, *i_capab, *i_bootstrap;
+	u16 i_proto_len, i_nonce_len, i_capab_len, i_bootstrap_len;
+	struct dpp_authentication *auth = NULL;
+	size_t attr_len;
+
+	if (wrapped_data_len < AES_BLOCK_SIZE)
+		return NULL;
+
+	attr_len = wrapped_data - 4 - attr_start;
+
+	auth = os_zalloc(sizeof(*auth));
+	if (!auth)
+		goto fail;
+	auth->msg_ctx = msg_ctx;
+	auth->peer_bi = peer_bi;
+	auth->own_bi = own_bi;
+	auth->curve = own_bi->curve;
+	auth->curr_freq = freq;
+
+	i_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_PROTOCOL_KEY,
+			       &i_proto_len);
+	if (!i_proto) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing required Initiator Protocol Key attribute");
+		goto fail;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key",
+		    i_proto, i_proto_len);
+
+	/* M = bR * PI */
+	pi = dpp_set_pubkey_point(own_bi->pubkey, i_proto, i_proto_len);
+	if (!pi) {
+		wpa_printf(MSG_DEBUG, "DPP: Invalid Initiator Protocol Key");
+		goto fail;
+	}
+	dpp_debug_print_key("Peer (Initiator) Protocol Key", pi);
+
+	ctx = EVP_PKEY_CTX_new(own_bi->pubkey, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, pi) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
+	    secret_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	auth->secret_len = secret_len;
+	EVP_PKEY_CTX_free(ctx);
+	ctx = NULL;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
+			auth->Mx, auth->secret_len);
+
+	if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
+			  auth->curve->hash_len) < 0)
+		goto fail;
+
+	addr[0] = hdr;
+	len[0] = DPP_HDR_LEN;
+	addr[1] = attr_start;
+	len[1] = attr_len;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		goto fail;
+	if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    2, addr, len, unwrapped) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: 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) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+			       &i_nonce_len);
+	if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing or invalid I-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+	os_memcpy(auth->i_nonce, i_nonce, i_nonce_len);
+
+	i_capab = dpp_get_attr(unwrapped, unwrapped_len,
+			       DPP_ATTR_I_CAPABILITIES,
+			       &i_capab_len);
+	if (!i_capab || i_capab_len < 1) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing or invalid I-capabilities");
+		goto fail;
+	}
+	auth->i_capab = i_capab[0];
+	wpa_printf(MSG_DEBUG, "DPP: I-capabilities: 0x%02x", auth->i_capab);
+
+	bin_clear_free(unwrapped, unwrapped_len);
+	unwrapped = NULL;
+
+	switch (auth->i_capab & DPP_CAPAB_ROLE_MASK) {
+	case DPP_CAPAB_ENROLLEE:
+		if (!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Local policy does not allow Configurator role");
+			goto not_compatible;
+		}
+		wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
+		auth->configurator = 1;
+		break;
+	case DPP_CAPAB_CONFIGURATOR:
+		if (!(dpp_allowed_roles & DPP_CAPAB_ENROLLEE)) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Local policy does not allow Enrollee role");
+			goto not_compatible;
+		}
+		wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
+		auth->configurator = 0;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "DPP: Unexpected role in I-capabilities");
+		goto not_compatible;
+	}
+
+	auth->peer_protocol_key = pi;
+	pi = NULL;
+	if (qr_mutual && !peer_bi && own_bi->type == DPP_BOOTSTRAP_QR_CODE) {
+		char hex[SHA256_MAC_LEN * 2 + 1];
+
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Mutual authentication required with QR Codes, but peer info is not yet available - request more time");
+		if (dpp_auth_build_resp_status(auth,
+					       DPP_STATUS_RESPONSE_PENDING) < 0)
+			goto fail;
+		i_bootstrap = dpp_get_attr(attr_start, attr_len,
+					   DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+					   &i_bootstrap_len);
+		if (i_bootstrap && i_bootstrap_len == SHA256_MAC_LEN) {
+			auth->response_pending = 1;
+			os_memcpy(auth->waiting_pubkey_hash,
+				  i_bootstrap, i_bootstrap_len);
+			wpa_snprintf_hex(hex, sizeof(hex), i_bootstrap,
+					 i_bootstrap_len);
+		} else {
+			hex[0] = '\0';
+		}
+
+		wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_SCAN_PEER_QR_CODE
+			"%s", hex);
+		return auth;
+	}
+	if (dpp_auth_build_resp(auth) < 0)
+		goto fail;
+
+	return auth;
+
+not_compatible:
+	wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
+		"i-capab=0x%02x", auth->i_capab);
+	if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)
+		auth->configurator = 1;
+	else
+		auth->configurator = 0;
+	auth->peer_protocol_key = pi;
+	pi = NULL;
+	if (dpp_auth_build_resp_status(auth, DPP_STATUS_NOT_COMPATIBLE) < 0)
+		goto fail;
+
+	auth->remove_on_tx_status = 1;
+	return auth;
+fail:
+	bin_clear_free(unwrapped, unwrapped_len);
+	EVP_PKEY_free(pi);
+	EVP_PKEY_CTX_free(ctx);
+	dpp_auth_deinit(auth);
+	return NULL;
+}
+
+
+int dpp_notify_new_qr_code(struct dpp_authentication *auth,
+			   struct dpp_bootstrap_info *peer_bi)
+{
+	if (!auth || !auth->response_pending ||
+	    os_memcmp(auth->waiting_pubkey_hash, peer_bi->pubkey_hash,
+		      SHA256_MAC_LEN) != 0)
+		return 0;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: New scanned QR Code has matching public key that was needed to continue DPP Authentication exchange with "
+		   MACSTR, MAC2STR(auth->peer_mac_addr));
+	auth->peer_bi = peer_bi;
+
+	if (dpp_auth_build_resp(auth) < 0)
+		return -1;
+
+	return 1;
+}
+
+
+static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth)
+{
+	struct wpabuf *msg;
+	u8 i_auth[4 + DPP_MAX_HASH_LEN];
+	size_t i_auth_len;
+	const u8 *addr[2];
+	size_t len[2], attr_len;
+	u8 *wrapped_i_auth;
+	u8 *attr_start, *attr_end;
+
+	wpa_printf(MSG_DEBUG, "DPP: Build Authentication Confirmation");
+
+	i_auth_len = 4 + auth->curve->hash_len;
+	/* Build DPP Authentication Confirmation frame attributes */
+	attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
+		4 + i_auth_len + AES_BLOCK_SIZE;
+	msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, attr_len);
+	if (!msg)
+		goto fail;
+
+	attr_start = wpabuf_put(msg, 0);
+
+	/* DPP Status */
+	wpabuf_put_le16(msg, DPP_ATTR_STATUS);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, DPP_STATUS_OK);
+
+	/* Responder Bootstrapping Key Hash */
+	wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
+	wpabuf_put_le16(msg, SHA256_MAC_LEN);
+	wpabuf_put_data(msg, auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+
+	if (auth->own_bi) {
+		/* Mutual authentication */
+		/* Initiator Bootstrapping Key Hash */
+		wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
+		wpabuf_put_le16(msg, SHA256_MAC_LEN);
+		wpabuf_put_data(msg, auth->own_bi->pubkey_hash, SHA256_MAC_LEN);
+	}
+
+	attr_end = wpabuf_put(msg, 0);
+
+	/* OUI, OUI type, Crypto Suite, DPP frame type */
+	addr[0] = wpabuf_head_u8(msg) + 2;
+	len[0] = 3 + 1 + 1 + 1;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+	/* Attributes before Wrapped Data */
+	addr[1] = attr_start;
+	len[1] = attr_end - attr_start;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, i_auth_len + AES_BLOCK_SIZE);
+	wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE);
+	/* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
+	WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG);
+	WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len);
+	if (dpp_gen_i_auth(auth, i_auth + 4) < 0 ||
+	    aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+			    i_auth, i_auth_len,
+			    2, addr, len, wrapped_i_auth) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke",
+		    wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE);
+
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Authentication Confirmation frame attributes",
+			msg);
+	dpp_auth_success(auth);
+
+	return msg;
+
+fail:
+	return NULL;
+}
+
+
+static void
+dpp_auth_resp_rx_status(struct dpp_authentication *auth, const u8 *hdr,
+			const u8 *attr_start, size_t attr_len,
+			const u8 *wrapped_data, u16 wrapped_data_len,
+			enum dpp_status_error status)
+{
+	const u8 *addr[2];
+	size_t len[2];
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	const u8 *i_nonce, *r_capab;
+	u16 i_nonce_len, r_capab_len;
+
+	if (status == DPP_STATUS_NOT_COMPATIBLE) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Responder reported incompatible roles");
+	} else if (status == DPP_STATUS_RESPONSE_PENDING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Responder reported more time needed");
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Responder reported failure (status %d)",
+			   status);
+		return;
+	}
+
+	addr[0] = hdr;
+	len[0] = DPP_HDR_LEN;
+	addr[1] = attr_start;
+	len[1] = attr_len;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		goto fail;
+	if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    2, addr, len, unwrapped) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: 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) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+			       &i_nonce_len);
+	if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing or invalid I-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+	if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: I-nonce mismatch");
+		goto fail;
+	}
+
+	r_capab = dpp_get_attr(unwrapped, unwrapped_len,
+			       DPP_ATTR_R_CAPABILITIES,
+			       &r_capab_len);
+	if (!r_capab || r_capab_len < 1) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing or invalid R-capabilities");
+		goto fail;
+	}
+	auth->r_capab = r_capab[0];
+	wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
+	if (status == DPP_STATUS_NOT_COMPATIBLE) {
+		wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
+			"r-capab=0x%02x", auth->r_capab);
+	} else if (status == DPP_STATUS_RESPONSE_PENDING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Continue waiting for full DPP Authentication Response");
+		wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_RESPONSE_PENDING);
+	}
+fail:
+	bin_clear_free(unwrapped, unwrapped_len);
+}
+
+
+struct wpabuf *
+dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
+		 const u8 *attr_start, size_t attr_len)
+{
+	EVP_PKEY *pr;
+	EVP_PKEY_CTX *ctx = NULL;
+	size_t secret_len;
+	const u8 *addr[2];
+	size_t len[2];
+	u8 *unwrapped = NULL, *unwrapped2 = NULL;
+	size_t unwrapped_len = 0, unwrapped2_len = 0;
+	const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *r_proto,
+		*r_nonce, *i_nonce, *r_capab, *wrapped2, *r_auth;
+	u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
+		r_proto_len, r_nonce_len, i_nonce_len, r_capab_len,
+		wrapped2_len, r_auth_len;
+	u8 r_auth2[DPP_MAX_HASH_LEN];
+
+	wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing required Wrapped data attribute");
+		return NULL;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
+		    wrapped_data, wrapped_data_len);
+
+	if (wrapped_data_len < AES_BLOCK_SIZE)
+		return NULL;
+
+	attr_len = wrapped_data - 4 - attr_start;
+
+	r_bootstrap = dpp_get_attr(attr_start, attr_len,
+				   DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+				   &r_bootstrap_len);
+	if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
+		return NULL;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
+		    r_bootstrap, r_bootstrap_len);
+	if (os_memcmp(r_bootstrap, auth->peer_bi->pubkey_hash,
+		      SHA256_MAC_LEN) != 0) {
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Expected Responder Bootstrapping Key Hash",
+			    auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+		return NULL;
+	}
+
+	i_bootstrap = dpp_get_attr(attr_start, attr_len,
+				   DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+				   &i_bootstrap_len);
+	if (i_bootstrap) {
+		if (i_bootstrap_len != SHA256_MAC_LEN) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Invalid Initiator Bootstrapping Key Hash attribute");
+			return NULL;
+		}
+		wpa_hexdump(MSG_MSGDUMP,
+			    "DPP: Initiator Bootstrapping Key Hash",
+			    i_bootstrap, i_bootstrap_len);
+		if (!auth->own_bi ||
+		    os_memcmp(i_bootstrap, auth->own_bi->pubkey_hash,
+			      SHA256_MAC_LEN) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Initiator Bootstrapping Key Hash attribute did not match");
+			return NULL;
+		}
+	}
+
+	status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
+			      &status_len);
+	if (!status || status_len < 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required DPP Status attribute");
+		return NULL;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+	auth->auth_resp_status = status[0];
+	if (status[0] != DPP_STATUS_OK) {
+		dpp_auth_resp_rx_status(auth, hdr, attr_start,
+					attr_len, wrapped_data,
+					wrapped_data_len, status[0]);
+		return NULL;
+	}
+
+	r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY,
+			       &r_proto_len);
+	if (!r_proto) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing required Responder Protocol Key attribute");
+		return NULL;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key",
+		    r_proto, r_proto_len);
+
+	/* N = pI * PR */
+	pr = dpp_set_pubkey_point(auth->own_protocol_key, r_proto, r_proto_len);
+	if (!pr) {
+		wpa_printf(MSG_DEBUG, "DPP: Invalid Responder Protocol Key");
+		return NULL;
+	}
+	dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
+
+	ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, pr) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
+	    secret_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	EVP_PKEY_CTX_free(ctx);
+	ctx = NULL;
+	auth->peer_protocol_key = pr;
+	pr = NULL;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
+			auth->Nx, auth->secret_len);
+
+	if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
+			  auth->curve->hash_len) < 0)
+		goto fail;
+
+	addr[0] = hdr;
+	len[0] = DPP_HDR_LEN;
+	addr[1] = attr_start;
+	len[1] = attr_len;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		goto fail;
+	if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    2, addr, len, unwrapped) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: 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) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
+			       &r_nonce_len);
+	if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing or invalid R-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
+	os_memcpy(auth->r_nonce, r_nonce, r_nonce_len);
+
+	i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+			       &i_nonce_len);
+	if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing or invalid I-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+	if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: I-nonce mismatch");
+		goto fail;
+	}
+
+	if (auth->own_bi && auth->peer_bi) {
+		/* Mutual authentication */
+		if (dpp_auth_derive_l_initiator(auth) < 0)
+			goto fail;
+	}
+
+	if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0)
+		goto fail;
+
+	r_capab = dpp_get_attr(unwrapped, unwrapped_len,
+			       DPP_ATTR_R_CAPABILITIES,
+			       &r_capab_len);
+	if (!r_capab || r_capab_len < 1) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing or invalid R-capabilities");
+		goto fail;
+	}
+	auth->r_capab = r_capab[0];
+	wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
+	if ((auth->configurator && (auth->r_capab & DPP_CAPAB_CONFIGURATOR)) ||
+	    (!auth->configurator && (auth->r_capab & DPP_CAPAB_ENROLLEE))) {
+		wpa_printf(MSG_DEBUG, "DPP: Incompatible role selection");
+		goto fail;
+	}
+
+	wrapped2 = dpp_get_attr(unwrapped, unwrapped_len,
+				DPP_ATTR_WRAPPED_DATA, &wrapped2_len);
+	if (!wrapped2 || wrapped2_len < AES_BLOCK_SIZE) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid Secondary Wrapped Data");
+		goto fail;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped2, wrapped2_len);
+	unwrapped2_len = wrapped2_len - AES_BLOCK_SIZE;
+	unwrapped2 = os_malloc(unwrapped2_len);
+	if (!unwrapped2)
+		goto fail;
+	if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+			    wrapped2, wrapped2_len,
+			    0, NULL, NULL, unwrapped2) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped2, unwrapped2_len);
+
+	if (dpp_check_attrs(unwrapped2, unwrapped2_len) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in secondary unwrapped data");
+		goto fail;
+	}
+
+	r_auth = dpp_get_attr(unwrapped2, unwrapped2_len, DPP_ATTR_R_AUTH_TAG,
+			       &r_auth_len);
+	if (!r_auth || r_auth_len != auth->curve->hash_len) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid Responder Authenticating Tag");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Received Responder Authenticating Tag",
+		    r_auth, r_auth_len);
+	/* R-auth' = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+	if (dpp_gen_r_auth(auth, r_auth2) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: Calculated Responder Authenticating Tag",
+		    r_auth2, r_auth_len);
+	if (os_memcmp(r_auth, r_auth2, r_auth_len) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Mismatching Responder Authenticating Tag");
+		goto fail;
+	}
+
+	bin_clear_free(unwrapped, unwrapped_len);
+	bin_clear_free(unwrapped2, unwrapped2_len);
+
+	return dpp_auth_build_conf(auth);
+
+fail:
+	bin_clear_free(unwrapped, unwrapped_len);
+	bin_clear_free(unwrapped2, unwrapped2_len);
+	EVP_PKEY_free(pr);
+	EVP_PKEY_CTX_free(ctx);
+	return NULL;
+}
+
+
+int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
+		     const u8 *attr_start, size_t attr_len)
+{
+	const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *i_auth;
+	u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
+		i_auth_len;
+	const u8 *addr[2];
+	size_t len[2];
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	u8 i_auth2[DPP_MAX_HASH_LEN];
+
+	wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing required Wrapped data attribute");
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
+		    wrapped_data, wrapped_data_len);
+
+	if (wrapped_data_len < AES_BLOCK_SIZE)
+		return -1;
+
+	attr_len = wrapped_data - 4 - attr_start;
+
+	r_bootstrap = dpp_get_attr(attr_start, attr_len,
+				   DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+				   &r_bootstrap_len);
+	if (!r_bootstrap || r_bootstrap > wrapped_data ||
+	    r_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
+		    r_bootstrap, r_bootstrap_len);
+	if (os_memcmp(r_bootstrap, auth->own_bi->pubkey_hash,
+		      SHA256_MAC_LEN) != 0) {
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Expected Responder Bootstrapping Key Hash",
+			    auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+		return -1;
+	}
+
+	i_bootstrap = dpp_get_attr(attr_start, attr_len,
+				   DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+				   &i_bootstrap_len);
+	if (i_bootstrap) {
+		if (i_bootstrap > wrapped_data ||
+		    i_bootstrap_len != SHA256_MAC_LEN) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Invalid Initiator Bootstrapping Key Hash attribute");
+			return -1;
+		}
+		wpa_hexdump(MSG_MSGDUMP,
+			    "DPP: Initiator Bootstrapping Key Hash",
+			    i_bootstrap, i_bootstrap_len);
+		if (!auth->peer_bi ||
+		    os_memcmp(i_bootstrap, auth->peer_bi->pubkey_hash,
+			      SHA256_MAC_LEN) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Initiator Bootstrapping Key Hash attribute did not match");
+			return -1;
+		}
+	}
+
+	status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
+			      &status_len);
+	if (!status || status_len < 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required DPP Status attribute");
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+	if (status[0] != DPP_STATUS_OK) {
+		wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
+		return -1;
+	}
+
+	addr[0] = hdr;
+	len[0] = DPP_HDR_LEN;
+	addr[1] = attr_start;
+	len[1] = attr_len;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		return -1;
+	if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    2, addr, len, unwrapped) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: 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) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	i_auth = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
+			      &i_auth_len);
+	if (!i_auth || i_auth_len != auth->curve->hash_len) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid Initiator Authenticating Tag");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Received Initiator Authenticating Tag",
+		    i_auth, i_auth_len);
+	/* I-auth' = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
+	if (dpp_gen_i_auth(auth, i_auth2) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: Calculated Initiator Authenticating Tag",
+		    i_auth2, i_auth_len);
+	if (os_memcmp(i_auth, i_auth2, i_auth_len) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Mismatching Initiator Authenticating Tag");
+		goto fail;
+	}
+
+	bin_clear_free(unwrapped, unwrapped_len);
+	dpp_auth_success(auth);
+	return 0;
+fail:
+	bin_clear_free(unwrapped, unwrapped_len);
+	return -1;
+}
+
+
+void dpp_configuration_free(struct dpp_configuration *conf)
+{
+	if (!conf)
+		return;
+	str_clear_free(conf->passphrase);
+	bin_clear_free(conf, sizeof(*conf));
+}
+
+
+void dpp_auth_deinit(struct dpp_authentication *auth)
+{
+	if (!auth)
+		return;
+	dpp_configuration_free(auth->conf_ap);
+	dpp_configuration_free(auth->conf_sta);
+	EVP_PKEY_free(auth->own_protocol_key);
+	EVP_PKEY_free(auth->peer_protocol_key);
+	wpabuf_free(auth->req_msg);
+	wpabuf_free(auth->resp_msg);
+	wpabuf_free(auth->conf_req);
+	os_free(auth->connector);
+	wpabuf_free(auth->net_access_key);
+	wpabuf_free(auth->c_sign_key);
+#ifdef CONFIG_TESTING_OPTIONS
+	os_free(auth->config_obj_override);
+	os_free(auth->discovery_override);
+	os_free(auth->groups_override);
+#endif /* CONFIG_TESTING_OPTIONS */
+	bin_clear_free(auth, sizeof(*auth));
+}
+
+
+static struct wpabuf *
+dpp_build_conf_start(struct dpp_authentication *auth,
+		     struct dpp_configuration *conf, size_t tailroom)
+{
+	struct wpabuf *buf;
+	char ssid[6 * sizeof(conf->ssid) + 1];
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (auth->discovery_override)
+		tailroom += os_strlen(auth->discovery_override);
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	buf = wpabuf_alloc(200 + tailroom);
+	if (!buf)
+		return NULL;
+	wpabuf_put_str(buf, "{\"wi-fi_tech\":\"infra\",\"discovery\":");
+#ifdef CONFIG_TESTING_OPTIONS
+	if (auth->discovery_override) {
+		wpa_printf(MSG_DEBUG, "DPP: TESTING - discovery override: '%s'",
+			   auth->discovery_override);
+		wpabuf_put_str(buf, auth->discovery_override);
+		wpabuf_put_u8(buf, ',');
+		return buf;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	wpabuf_put_str(buf, "{\"ssid\":\"");
+	json_escape_string(ssid, sizeof(ssid),
+			   (const char *) conf->ssid, conf->ssid_len);
+	wpabuf_put_str(buf, ssid);
+	wpabuf_put_str(buf, "\"");
+	/* TODO: optional channel information */
+	wpabuf_put_str(buf, "},");
+
+	return buf;
+}
+
+
+static int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len)
+{
+	int num_bytes, offset;
+
+	num_bytes = BN_num_bytes(bn);
+	if ((size_t) num_bytes > len)
+		return -1;
+	offset = len - num_bytes;
+	os_memset(pos, 0, offset);
+	BN_bn2bin(bn, pos + offset);
+	return 0;
+}
+
+
+static int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key,
+			 const char *kid, const struct dpp_curve_params *curve)
+{
+	struct wpabuf *pub;
+	const u8 *pos;
+	char *x = NULL, *y = NULL;
+	int ret = -1;
+
+	pub = dpp_get_pubkey_point(key, 0);
+	if (!pub)
+		goto fail;
+	pos = wpabuf_head(pub);
+	x = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0);
+	pos += curve->prime_len;
+	y = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0);
+	if (!x || !y)
+		goto fail;
+
+	wpabuf_put_str(buf, "\"");
+	wpabuf_put_str(buf, name);
+	wpabuf_put_str(buf, "\":{\"kty\":\"EC\",\"crv\":\"");
+	wpabuf_put_str(buf, curve->jwk_crv);
+	wpabuf_put_str(buf, "\",\"x\":\"");
+	wpabuf_put_str(buf, x);
+	wpabuf_put_str(buf, "\",\"y\":\"");
+	wpabuf_put_str(buf, y);
+	if (kid) {
+		wpabuf_put_str(buf, "\",\"kid\":\"");
+		wpabuf_put_str(buf, kid);
+	}
+	wpabuf_put_str(buf, "\"}");
+	ret = 0;
+fail:
+	wpabuf_free(pub);
+	os_free(x);
+	os_free(y);
+	return ret;
+}
+
+
+static struct wpabuf *
+dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap,
+		       struct dpp_configuration *conf)
+{
+	struct wpabuf *buf = NULL;
+	char *signed1 = NULL, *signed2 = NULL, *signed3 = NULL;
+	size_t tailroom;
+	const struct dpp_curve_params *curve;
+	char jws_prot_hdr[100];
+	size_t signed1_len, signed2_len, signed3_len;
+	struct wpabuf *dppcon = NULL;
+	unsigned char *signature = NULL;
+	const unsigned char *p;
+	size_t signature_len;
+	EVP_MD_CTX *md_ctx = NULL;
+	ECDSA_SIG *sig = NULL;
+	char *dot = ".";
+	const EVP_MD *sign_md;
+	const BIGNUM *r, *s;
+	size_t extra_len = 1000;
+
+	if (!auth->conf) {
+		wpa_printf(MSG_INFO,
+			   "DPP: No configurator specified - cannot generate DPP config object");
+		goto fail;
+	}
+	curve = auth->conf->curve;
+	if (curve->hash_len == SHA256_MAC_LEN) {
+		sign_md = EVP_sha256();
+	} else if (curve->hash_len == SHA384_MAC_LEN) {
+		sign_md = EVP_sha384();
+	} else if (curve->hash_len == SHA512_MAC_LEN) {
+		sign_md = EVP_sha512();
+	} else {
+		wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm");
+		goto fail;
+	}
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (auth->groups_override)
+		extra_len += os_strlen(auth->groups_override);
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* Connector (JSON dppCon object) */
+	dppcon = wpabuf_alloc(extra_len + 2 * auth->curve->prime_len * 4 / 3);
+	if (!dppcon)
+		goto fail;
+#ifdef CONFIG_TESTING_OPTIONS
+	if (auth->groups_override) {
+		wpabuf_put_u8(dppcon, '{');
+		if (auth->groups_override) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: TESTING - groups override: '%s'",
+				   auth->groups_override);
+			wpabuf_put_str(dppcon, "\"groups\":");
+			wpabuf_put_str(dppcon, auth->groups_override);
+			wpabuf_put_u8(dppcon, ',');
+		}
+		goto skip_groups;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	wpabuf_put_str(dppcon, "{\"groups\":[{\"groupId\":\"*\",");
+	wpabuf_printf(dppcon, "\"netRole\":\"%s\"}],", ap ? "ap" : "sta");
+#ifdef CONFIG_TESTING_OPTIONS
+skip_groups:
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL,
+			  auth->curve) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK");
+		goto fail;
+	}
+	if (conf->netaccesskey_expiry) {
+		struct os_tm tm;
+
+		if (os_gmtime(conf->netaccesskey_expiry, &tm) < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Failed to generate expiry string");
+			goto fail;
+		}
+		wpabuf_printf(dppcon,
+			      ",\"expiry\":\"%04u-%02u-%02uT%02u:%02u:%02uZ\"",
+			      tm.year, tm.month, tm.day,
+			      tm.hour, tm.min, tm.sec);
+	}
+	wpabuf_put_u8(dppcon, '}');
+	wpa_printf(MSG_DEBUG, "DPP: dppCon: %s",
+		   (const char *) wpabuf_head(dppcon));
+
+	os_snprintf(jws_prot_hdr, sizeof(jws_prot_hdr),
+		    "{\"typ\":\"dppCon\",\"kid\":\"%s\",\"alg\":\"%s\"}",
+		    auth->conf->kid, curve->jws_alg);
+	signed1 = (char *) base64_url_encode((unsigned char *) jws_prot_hdr,
+					     os_strlen(jws_prot_hdr),
+					     &signed1_len, 0);
+	signed2 = (char *) base64_url_encode(wpabuf_head(dppcon),
+					     wpabuf_len(dppcon),
+					     &signed2_len, 0);
+	if (!signed1 || !signed2)
+		goto fail;
+
+	md_ctx = EVP_MD_CTX_create();
+	if (!md_ctx)
+		goto fail;
+
+	ERR_clear_error();
+	if (EVP_DigestSignInit(md_ctx, NULL, sign_md, NULL,
+			       auth->conf->csign) != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignInit failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	if (EVP_DigestSignUpdate(md_ctx, signed1, signed1_len) != 1 ||
+	    EVP_DigestSignUpdate(md_ctx, dot, 1) != 1 ||
+	    EVP_DigestSignUpdate(md_ctx, signed2, signed2_len) != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignUpdate failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	if (EVP_DigestSignFinal(md_ctx, NULL, &signature_len) != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	signature = os_malloc(signature_len);
+	if (!signature)
+		goto fail;
+	if (EVP_DigestSignFinal(md_ctx, signature, &signature_len) != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (DER)",
+		    signature, signature_len);
+	/* Convert to raw coordinates r,s */
+	p = signature;
+	sig = d2i_ECDSA_SIG(NULL, &p, signature_len);
+	if (!sig)
+		goto fail;
+	ECDSA_SIG_get0(sig, &r, &s);
+	if (dpp_bn2bin_pad(r, signature, curve->prime_len) < 0 ||
+	    dpp_bn2bin_pad(s, signature + curve->prime_len,
+			   curve->prime_len) < 0)
+		goto fail;
+	signature_len = 2 * curve->prime_len;
+	wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (raw r,s)",
+		    signature, signature_len);
+	signed3 = (char *) base64_url_encode(signature, signature_len,
+					     &signed3_len, 0);
+	if (!signed3)
+		goto fail;
+
+	tailroom = 1000;
+	tailroom += 2 * curve->prime_len * 4 / 3 + os_strlen(auth->conf->kid);
+	tailroom += signed1_len + signed2_len + signed3_len;
+	buf = dpp_build_conf_start(auth, conf, tailroom);
+	if (!buf)
+		return NULL;
+
+	wpabuf_put_str(buf, "\"cred\":{\"akm\":\"dpp\",\"signedConnector\":\"");
+	wpabuf_put_str(buf, signed1);
+	wpabuf_put_u8(buf, '.');
+	wpabuf_put_str(buf, signed2);
+	wpabuf_put_u8(buf, '.');
+	wpabuf_put_str(buf, signed3);
+	wpabuf_put_str(buf, "\",");
+	if (dpp_build_jwk(buf, "csign", auth->conf->csign, auth->conf->kid,
+			  curve) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to build csign JWK");
+		goto fail;
+	}
+
+	wpabuf_put_str(buf, "}}");
+
+	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object",
+			      wpabuf_head(buf), wpabuf_len(buf));
+
+out:
+	EVP_MD_CTX_destroy(md_ctx);
+	ECDSA_SIG_free(sig);
+	os_free(signed1);
+	os_free(signed2);
+	os_free(signed3);
+	os_free(signature);
+	wpabuf_free(dppcon);
+	return buf;
+fail:
+	wpa_printf(MSG_DEBUG, "DPP: Failed to build configuration object");
+	wpabuf_free(buf);
+	buf = NULL;
+	goto out;
+}
+
+
+static struct wpabuf *
+dpp_build_conf_obj_legacy(struct dpp_authentication *auth, int ap,
+			  struct dpp_configuration *conf)
+{
+	struct wpabuf *buf;
+
+	buf = dpp_build_conf_start(auth, conf, 1000);
+	if (!buf)
+		return NULL;
+
+	wpabuf_put_str(buf, "\"cred\":{\"akm\":\"psk\",");
+	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, "\"");
+	}
+	wpabuf_put_str(buf, "}}");
+
+	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)",
+			      wpabuf_head(buf), wpabuf_len(buf));
+
+	return buf;
+}
+
+
+static struct wpabuf *
+dpp_build_conf_obj(struct dpp_authentication *auth, int ap)
+{
+	struct dpp_configuration *conf;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (auth->config_obj_override) {
+		wpa_printf(MSG_DEBUG, "DPP: Testing - Config Object override");
+		return wpabuf_alloc_copy(auth->config_obj_override,
+					 os_strlen(auth->config_obj_override));
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	conf = ap ? auth->conf_ap : auth->conf_sta;
+	if (!conf) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No configuration available for Enrollee(%s) - reject configuration request",
+			   ap ? "ap" : "sta");
+		return NULL;
+	}
+
+	if (conf->dpp)
+		return dpp_build_conf_obj_dpp(auth, ap, conf);
+	return dpp_build_conf_obj_legacy(auth, ap, conf);
+}
+
+
+static struct wpabuf *
+dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
+		    u16 e_nonce_len, int ap)
+{
+	struct wpabuf *conf;
+	size_t clear_len;
+	struct wpabuf *clear = NULL, *msg = NULL;
+	u8 *wrapped;
+	const u8 *addr[1];
+	size_t len[1];
+	enum dpp_status_error status;
+
+	conf = dpp_build_conf_obj(auth, ap);
+	if (conf) {
+		wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON",
+				  wpabuf_head(conf), wpabuf_len(conf));
+	}
+	status = conf ? DPP_STATUS_OK : DPP_STATUS_CONFIGURE_FAILURE;
+
+	/* { E-nonce, configurationObject}ke */
+	clear_len = 4 + e_nonce_len;
+	if (conf)
+		clear_len += 4 + wpabuf_len(conf);
+	clear = wpabuf_alloc(clear_len);
+	msg = wpabuf_alloc(4 + 1 + 4 + clear_len + AES_BLOCK_SIZE);
+	if (!clear || !msg)
+		goto fail;
+
+	/* E-nonce */
+	wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
+	wpabuf_put_le16(clear, e_nonce_len);
+	wpabuf_put_data(clear, e_nonce, e_nonce_len);
+
+	if (conf) {
+		wpabuf_put_le16(clear, DPP_ATTR_CONFIG_OBJ);
+		wpabuf_put_le16(clear, wpabuf_len(conf));
+		wpabuf_put_buf(clear, conf);
+		wpabuf_free(conf);
+		conf = NULL;
+	}
+
+	/* DPP Status */
+	wpabuf_put_le16(msg, DPP_ATTR_STATUS);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, status);
+
+	addr[0] = wpabuf_head(msg);
+	len[0] = wpabuf_len(msg);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
+
+	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),
+			    1, addr, len, wrapped) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+	wpabuf_free(clear);
+	clear = NULL;
+
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Configuration Response attributes", msg);
+	return msg;
+fail:
+	wpabuf_free(conf);
+	wpabuf_free(clear);
+	wpabuf_free(msg);
+	return NULL;
+}
+
+
+struct wpabuf *
+dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
+		size_t attr_len)
+{
+	const u8 *wrapped_data, *e_nonce, *config_attr;
+	u16 wrapped_data_len, e_nonce_len, config_attr_len;
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	struct wpabuf *resp = NULL;
+	struct json_token *root = NULL, *token;
+	int ap;
+
+	if (dpp_check_attrs(attr_start, attr_len) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in config request");
+		return NULL;
+	}
+
+	wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Wrapped data attribute");
+		return NULL;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		return NULL;
+	if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    0, NULL, NULL, unwrapped) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: 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) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: 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) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid Enrollee Nonce attribute");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
+
+	config_attr = dpp_get_attr(unwrapped, unwrapped_len,
+				   DPP_ATTR_CONFIG_ATTR_OBJ,
+				   &config_attr_len);
+	if (!config_attr) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid Config Attributes attribute");
+		goto fail;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "DPP: Config Attributes",
+			  config_attr, config_attr_len);
+
+	root = json_parse((const char *) config_attr, config_attr_len);
+	if (!root) {
+		wpa_printf(MSG_DEBUG, "DPP: Could not parse Config Attributes");
+		goto fail;
+	}
+
+	token = json_get_member(root, "name");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG, "DPP: No Config Attributes - name");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Enrollee name = '%s'", token->string);
+
+	token = json_get_member(root, "wi-fi_tech");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG, "DPP: No Config Attributes - wi-fi_tech");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: wi-fi_tech = '%s'", token->string);
+	if (os_strcmp(token->string, "infra") != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech '%s'",
+			   token->string);
+		goto fail;
+	}
+
+	token = json_get_member(root, "netRole");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG, "DPP: No Config Attributes - netRole");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: netRole = '%s'", token->string);
+	if (os_strcmp(token->string, "sta") == 0) {
+		ap = 0;
+	} else if (os_strcmp(token->string, "ap") == 0) {
+		ap = 1;
+	} else {
+		wpa_printf(MSG_DEBUG, "DPP: Unsupported netRole '%s'",
+			   token->string);
+		goto fail;
+	}
+
+	resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, ap);
+
+fail:
+	json_free(root);
+	os_free(unwrapped);
+	return resp;
+}
+
+
+static struct wpabuf *
+dpp_parse_jws_prot_hdr(const struct dpp_curve_params *curve,
+		       const u8 *prot_hdr, u16 prot_hdr_len,
+		       const EVP_MD **ret_md)
+{
+	struct json_token *root, *token;
+	struct wpabuf *kid = NULL;
+
+	root = json_parse((const char *) prot_hdr, prot_hdr_len);
+	if (!root) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: JSON parsing failed for JWS Protected Header");
+		goto fail;
+	}
+
+	if (root->type != JSON_OBJECT) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: JWS Protected Header root is not an object");
+		goto fail;
+	}
+
+	token = json_get_member(root, "typ");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG, "DPP: No typ string value found");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header typ=%s",
+		   token->string);
+	if (os_strcmp(token->string, "dppCon") != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unsupported JWS Protected Header typ=%s",
+			   token->string);
+		goto fail;
+	}
+
+	token = json_get_member(root, "alg");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG, "DPP: No alg string value found");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header alg=%s",
+		   token->string);
+	if (os_strcmp(token->string, curve->jws_alg) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected JWS Protected Header alg=%s (expected %s based on C-sign-key)",
+			   token->string, curve->jws_alg);
+		goto fail;
+	}
+	if (os_strcmp(token->string, "ES256") == 0 ||
+	    os_strcmp(token->string, "BS256") == 0)
+		*ret_md = EVP_sha256();
+	else if (os_strcmp(token->string, "ES384") == 0 ||
+		 os_strcmp(token->string, "BS384") == 0)
+		*ret_md = EVP_sha384();
+	else if (os_strcmp(token->string, "ES512") == 0 ||
+		 os_strcmp(token->string, "BS512") == 0)
+		*ret_md = EVP_sha512();
+	else
+		*ret_md = NULL;
+	if (!*ret_md) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unsupported JWS Protected Header alg=%s",
+			   token->string);
+		goto fail;
+	}
+
+	kid = json_get_member_base64url(root, "kid");
+	if (!kid) {
+		wpa_printf(MSG_DEBUG, "DPP: No kid string value found");
+		goto fail;
+	}
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: JWS Protected Header kid (decoded)",
+			kid);
+
+fail:
+	json_free(root);
+	return kid;
+}
+
+
+static int dpp_parse_cred_legacy(struct dpp_authentication *auth,
+				 struct json_token *cred)
+{
+	struct json_token *pass, *psk_hex;
+
+	wpa_printf(MSG_DEBUG, "DPP: Legacy akm=psk credential");
+
+	pass = json_get_member(cred, "pass");
+	psk_hex = json_get_member(cred, "psk_hex");
+
+	if (pass && pass->type == JSON_STRING) {
+		size_t len = os_strlen(pass->string);
+
+		wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Legacy passphrase",
+				      pass->string, len);
+		if (len < 8 || len > 63)
+			return -1;
+		os_strlcpy(auth->passphrase, pass->string,
+			   sizeof(auth->passphrase));
+	} else if (psk_hex && psk_hex->type == JSON_STRING) {
+		if (os_strlen(psk_hex->string) != PMK_LEN * 2 ||
+		    hexstr2bin(psk_hex->string, auth->psk, PMK_LEN) < 0) {
+			wpa_printf(MSG_DEBUG, "DPP: Invalid psk_hex encoding");
+			return -1;
+		}
+		wpa_hexdump_key(MSG_DEBUG, "DPP: Legacy PSK",
+				auth->psk, PMK_LEN);
+		auth->psk_set = 1;
+	} else {
+		wpa_printf(MSG_DEBUG, "DPP: No pass or psk_hex strings found");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static EVP_PKEY * dpp_parse_jwk(struct json_token *jwk,
+				const struct dpp_curve_params **key_curve)
+{
+	struct json_token *token;
+	const struct dpp_curve_params *curve;
+	struct wpabuf *x = NULL, *y = NULL;
+	EC_GROUP *group;
+	EVP_PKEY *pkey = NULL;
+
+	token = json_get_member(jwk, "kty");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG, "DPP: No kty in JWK");
+		goto fail;
+	}
+	if (os_strcmp(token->string, "EC") != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Unexpected JWK kty '%s",
+			   token->string);
+		goto fail;
+	}
+
+	token = json_get_member(jwk, "crv");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG, "DPP: No crv in JWK");
+		goto fail;
+	}
+	curve = dpp_get_curve_jwk_crv(token->string);
+	if (!curve) {
+		wpa_printf(MSG_DEBUG, "DPP: Unsupported JWK crv '%s'",
+			   token->string);
+		goto fail;
+	}
+
+	x = json_get_member_base64url(jwk, "x");
+	if (!x) {
+		wpa_printf(MSG_DEBUG, "DPP: No x in JWK");
+		goto fail;
+	}
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK x", x);
+	if (wpabuf_len(x) != curve->prime_len) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected JWK x length %u (expected %u for curve %s)",
+			   (unsigned int) wpabuf_len(x),
+			   (unsigned int) curve->prime_len, curve->name);
+		goto fail;
+	}
+
+	y = json_get_member_base64url(jwk, "y");
+	if (!y) {
+		wpa_printf(MSG_DEBUG, "DPP: No y in JWK");
+		goto fail;
+	}
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK y", y);
+	if (wpabuf_len(y) != curve->prime_len) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected JWK y length %u (expected %u for curve %s)",
+			   (unsigned int) wpabuf_len(y),
+			   (unsigned int) curve->prime_len, curve->name);
+		goto fail;
+	}
+
+	group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
+	if (!group) {
+		wpa_printf(MSG_DEBUG, "DPP: Could not prepare group for JWK");
+		goto fail;
+	}
+
+	pkey = dpp_set_pubkey_point_group(group, wpabuf_head(x), wpabuf_head(y),
+					  wpabuf_len(x));
+	*key_curve = curve;
+
+fail:
+	wpabuf_free(x);
+	wpabuf_free(y);
+
+	return pkey;
+}
+
+
+int dpp_key_expired(const char *timestamp, os_time_t *expiry)
+{
+	struct os_time now;
+	unsigned int year, month, day, hour, min, sec;
+	os_time_t utime;
+	const char *pos;
+
+	/* ISO 8601 date and time:
+	 * <date>T<time>
+	 * YYYY-MM-DDTHH:MM:SSZ
+	 * YYYY-MM-DDTHH:MM:SS+03:00
+	 */
+	if (os_strlen(timestamp) < 19) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Too short timestamp - assume expired key");
+		return 1;
+	}
+	if (sscanf(timestamp, "%04u-%02u-%02uT%02u:%02u:%02u",
+		   &year, &month, &day, &hour, &min, &sec) != 6) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to parse expiration day - assume expired key");
+		return 1;
+	}
+
+	if (os_mktime(year, month, day, hour, min, sec, &utime) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid date/time information - assume expired key");
+		return 1;
+	}
+
+	pos = timestamp + 19;
+	if (*pos == 'Z' || *pos == '\0') {
+		/* In UTC - no need to adjust */
+	} else if (*pos == '-' || *pos == '+') {
+		int items;
+
+		/* Adjust local time to UTC */
+		items = sscanf(pos + 1, "%02u:%02u", &hour, &min);
+		if (items < 1) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Invalid time zone designator (%s) - assume expired key",
+				   pos);
+			return 1;
+		}
+		if (*pos == '-')
+			utime += 3600 * hour;
+		if (*pos == '+')
+			utime -= 3600 * hour;
+		if (items > 1) {
+			if (*pos == '-')
+				utime += 60 * min;
+			if (*pos == '+')
+				utime -= 60 * min;
+		}
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid time zone designator (%s) - assume expired key",
+			   pos);
+		return 1;
+	}
+	if (expiry)
+		*expiry = utime;
+
+	if (os_get_time(&now) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Cannot get current time - assume expired key");
+		return 1;
+	}
+
+	if (now.sec > utime) {
+		wpa_printf(MSG_DEBUG, "DPP: Key has expired (%lu < %lu)",
+			   utime, now.sec);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static int dpp_parse_connector(struct dpp_authentication *auth,
+			       const unsigned char *payload,
+			       u16 payload_len)
+{
+	struct json_token *root, *groups, *netkey, *token;
+	int ret = -1;
+	EVP_PKEY *key = NULL;
+	const struct dpp_curve_params *curve;
+	unsigned int rules = 0;
+
+	root = json_parse((const char *) payload, payload_len);
+	if (!root) {
+		wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed");
+		goto fail;
+	}
+
+	groups = json_get_member(root, "groups");
+	if (!groups || groups->type != JSON_ARRAY) {
+		wpa_printf(MSG_DEBUG, "DPP: No groups array found");
+		goto skip_groups;
+	}
+	for (token = groups->child; token; token = token->sibling) {
+		struct json_token *id, *role;
+
+		id = json_get_member(token, "groupId");
+		if (!id || id->type != JSON_STRING) {
+			wpa_printf(MSG_DEBUG, "DPP: Missing groupId string");
+			goto fail;
+		}
+
+		role = json_get_member(token, "netRole");
+		if (!role || role->type != JSON_STRING) {
+			wpa_printf(MSG_DEBUG, "DPP: Missing netRole string");
+			goto fail;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "DPP: connector group: groupId='%s' netRole='%s'",
+			   id->string, role->string);
+		rules++;
+	}
+skip_groups:
+
+	if (!rules) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Connector includes no groups");
+		goto fail;
+	}
+
+	token = json_get_member(root, "expiry");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No expiry string found - connector does not expire");
+	} else {
+		wpa_printf(MSG_DEBUG, "DPP: expiry = %s", token->string);
+		if (dpp_key_expired(token->string,
+				    &auth->net_access_key_expiry)) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Connector (netAccessKey) has expired");
+			goto fail;
+		}
+	}
+
+	netkey = json_get_member(root, "netAccessKey");
+	if (!netkey || netkey->type != JSON_OBJECT) {
+		wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found");
+		goto fail;
+	}
+
+	key = dpp_parse_jwk(netkey, &curve);
+	if (!key)
+		goto fail;
+	dpp_debug_print_key("DPP: Received netAccessKey", key);
+
+	if (EVP_PKEY_cmp(key, auth->own_protocol_key) != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: netAccessKey in connector does not match own protocol key");
+#ifdef CONFIG_TESTING_OPTIONS
+		if (auth->ignore_netaccesskey_mismatch) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: TESTING - skip netAccessKey mismatch");
+		} else {
+			goto fail;
+		}
+#else /* CONFIG_TESTING_OPTIONS */
+		goto fail;
+#endif /* CONFIG_TESTING_OPTIONS */
+	}
+
+	ret = 0;
+fail:
+	EVP_PKEY_free(key);
+	json_free(root);
+	return ret;
+}
+
+
+static int dpp_check_pubkey_match(EVP_PKEY *pub, struct wpabuf *r_hash)
+{
+	struct wpabuf *uncomp;
+	int res;
+	u8 hash[SHA256_MAC_LEN];
+	const u8 *addr[1];
+	size_t len[1];
+
+	if (wpabuf_len(r_hash) != SHA256_MAC_LEN)
+		return -1;
+	uncomp = dpp_get_pubkey_point(pub, 1);
+	if (!uncomp)
+		return -1;
+	addr[0] = wpabuf_head(uncomp);
+	len[0] = wpabuf_len(uncomp);
+	wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed public key",
+		    addr[0], len[0]);
+	res = sha256_vector(1, addr, len, hash);
+	wpabuf_free(uncomp);
+	if (res < 0)
+		return -1;
+	if (os_memcmp(hash, wpabuf_head(r_hash), SHA256_MAC_LEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Received hash value does not match calculated public key hash value");
+		wpa_hexdump(MSG_DEBUG, "DPP: Calculated hash",
+			    hash, SHA256_MAC_LEN);
+		return -1;
+	}
+	return 0;
+}
+
+
+static void dpp_copy_csign(struct dpp_authentication *auth, EVP_PKEY *csign)
+{
+	unsigned char *der = NULL;
+	int der_len;
+
+	der_len = i2d_PUBKEY(csign, &der);
+	if (der_len <= 0)
+		return;
+	wpabuf_free(auth->c_sign_key);
+	auth->c_sign_key = wpabuf_alloc_copy(der, der_len);
+	OPENSSL_free(der);
+}
+
+
+static void dpp_copy_netaccesskey(struct dpp_authentication *auth)
+{
+	unsigned char *der = NULL;
+	int der_len;
+	EC_KEY *eckey;
+
+	eckey = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key);
+	if (!eckey)
+		return;
+
+	der_len = i2d_ECPrivateKey(eckey, &der);
+	if (der_len <= 0) {
+		EC_KEY_free(eckey);
+		return;
+	}
+	wpabuf_free(auth->net_access_key);
+	auth->net_access_key = wpabuf_alloc_copy(der, der_len);
+	OPENSSL_free(der);
+	EC_KEY_free(eckey);
+}
+
+
+struct dpp_signed_connector_info {
+	unsigned char *payload;
+	size_t payload_len;
+};
+
+static int
+dpp_process_signed_connector(struct dpp_signed_connector_info *info,
+			     EVP_PKEY *csign_pub, const char *connector)
+{
+	int ret = -1;
+	const char *pos, *end, *signed_start, *signed_end;
+	struct wpabuf *kid = NULL;
+	unsigned char *prot_hdr = NULL, *signature = NULL;
+	size_t prot_hdr_len = 0, signature_len = 0;
+	const EVP_MD *sign_md = NULL;
+	unsigned char *der = NULL;
+	int der_len;
+	int res;
+	EVP_MD_CTX *md_ctx = NULL;
+	ECDSA_SIG *sig = NULL;
+	BIGNUM *r = NULL, *s = NULL;
+	const struct dpp_curve_params *curve;
+	EC_KEY *eckey;
+	const EC_GROUP *group;
+	int nid;
+
+	eckey = EVP_PKEY_get1_EC_KEY(csign_pub);
+	if (!eckey)
+		goto fail;
+	group = EC_KEY_get0_group(eckey);
+	if (!group)
+		goto fail;
+	nid = EC_GROUP_get_curve_name(group);
+	curve = dpp_get_curve_nid(nid);
+	if (!curve)
+		goto fail;
+	wpa_printf(MSG_DEBUG, "DPP: C-sign-key group: %s", curve->jwk_crv);
+	os_memset(info, 0, sizeof(*info));
+
+	signed_start = pos = connector;
+	end = os_strchr(pos, '.');
+	if (!end) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing dot(1) in signedConnector");
+		goto fail;
+	}
+	prot_hdr = base64_url_decode((const unsigned char *) pos,
+				     end - pos, &prot_hdr_len);
+	if (!prot_hdr) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to base64url decode signedConnector JWS Protected Header");
+		goto fail;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG,
+			  "DPP: signedConnector - JWS Protected Header",
+			  prot_hdr, prot_hdr_len);
+	kid = dpp_parse_jws_prot_hdr(curve, prot_hdr, prot_hdr_len, &sign_md);
+	if (!kid)
+		goto fail;
+	if (wpabuf_len(kid) != SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected signedConnector JWS Protected Header kid length: %u (expected %u)",
+			   (unsigned int) wpabuf_len(kid), SHA256_MAC_LEN);
+		goto fail;
+	}
+
+	pos = end + 1;
+	end = os_strchr(pos, '.');
+	if (!end) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing dot(2) in signedConnector");
+		goto fail;
+	}
+	signed_end = end - 1;
+	info->payload = base64_url_decode((const unsigned char *) pos,
+					  end - pos, &info->payload_len);
+	if (!info->payload) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to base64url decode signedConnector JWS Payload");
+		goto fail;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG,
+			  "DPP: signedConnector - JWS Payload",
+			  info->payload, info->payload_len);
+	pos = end + 1;
+	signature = base64_url_decode((const unsigned char *) pos,
+				      os_strlen(pos), &signature_len);
+	if (!signature) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to base64url decode signedConnector signature");
+		goto fail;
+		}
+	wpa_hexdump(MSG_DEBUG, "DPP: signedConnector - signature",
+		    signature, signature_len);
+
+	if (dpp_check_pubkey_match(csign_pub, kid) < 0)
+		goto fail;
+
+	if (signature_len & 0x01) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected signedConnector signature length (%d)",
+			   (int) signature_len);
+		goto fail;
+	}
+
+	/* JWS Signature encodes the signature (r,s) as two octet strings. Need
+	 * to convert that to DER encoded ECDSA_SIG for OpenSSL EVP routines. */
+	r = BN_bin2bn(signature, signature_len / 2, NULL);
+	s = BN_bin2bn(signature + signature_len / 2, signature_len / 2, NULL);
+	sig = ECDSA_SIG_new();
+	if (!r || !s || !sig || ECDSA_SIG_set0(sig, r, s) != 1)
+		goto fail;
+	r = NULL;
+	s = NULL;
+
+	der_len = i2d_ECDSA_SIG(sig, &der);
+	if (der_len <= 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Could not DER encode signature");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: DER encoded signature", der, der_len);
+	md_ctx = EVP_MD_CTX_create();
+	if (!md_ctx)
+		goto fail;
+
+	ERR_clear_error();
+	if (EVP_DigestVerifyInit(md_ctx, NULL, sign_md, NULL, csign_pub) != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyInit failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	if (EVP_DigestVerifyUpdate(md_ctx, signed_start,
+				   signed_end - signed_start + 1) != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyUpdate failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	res = EVP_DigestVerifyFinal(md_ctx, der, der_len);
+	if (res != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: EVP_DigestVerifyFinal failed (res=%d): %s",
+			   res, ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	ret = 0;
+fail:
+	EC_KEY_free(eckey);
+	EVP_MD_CTX_destroy(md_ctx);
+	os_free(prot_hdr);
+	wpabuf_free(kid);
+	os_free(signature);
+	ECDSA_SIG_free(sig);
+	BN_free(r);
+	BN_free(s);
+	OPENSSL_free(der);
+	return ret;
+}
+
+
+static int dpp_parse_cred_dpp(struct dpp_authentication *auth,
+			      struct json_token *cred)
+{
+	struct dpp_signed_connector_info info;
+	struct json_token *token, *csign;
+	int ret = -1;
+	EVP_PKEY *csign_pub = NULL;
+	const struct dpp_curve_params *key_curve = NULL;
+	const char *signed_connector;
+
+	os_memset(&info, 0, sizeof(info));
+
+	wpa_printf(MSG_DEBUG, "DPP: Connector credential");
+
+	csign = json_get_member(cred, "csign");
+	if (!csign || csign->type != JSON_OBJECT) {
+		wpa_printf(MSG_DEBUG, "DPP: No csign JWK in JSON");
+		goto fail;
+	}
+
+	csign_pub = dpp_parse_jwk(csign, &key_curve);
+	if (!csign_pub) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to parse csign JWK");
+		goto fail;
+	}
+	dpp_debug_print_key("DPP: Received C-sign-key", csign_pub);
+
+	token = json_get_member(cred, "signedConnector");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG, "DPP: No signedConnector string found");
+		goto fail;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "DPP: signedConnector",
+			  token->string, os_strlen(token->string));
+	signed_connector = token->string;
+
+	if (os_strchr(signed_connector, '"') ||
+	    os_strchr(signed_connector, '\n')) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected character in signedConnector");
+		goto fail;
+	}
+
+	if (dpp_process_signed_connector(&info, csign_pub,
+					 signed_connector) < 0)
+		goto fail;
+
+	if (dpp_parse_connector(auth, info.payload, info.payload_len) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to parse connector");
+		goto fail;
+	}
+
+	os_free(auth->connector);
+	auth->connector = os_strdup(signed_connector);
+
+	dpp_copy_csign(auth, csign_pub);
+	dpp_copy_netaccesskey(auth);
+
+	ret = 0;
+fail:
+	EVP_PKEY_free(csign_pub);
+	os_free(info.payload);
+	return ret;
+}
+
+
+static int dpp_parse_conf_obj(struct dpp_authentication *auth,
+			      const u8 *conf_obj, u16 conf_obj_len)
+{
+	int ret = -1;
+	struct json_token *root, *token, *discovery, *cred;
+
+	root = json_parse((const char *) conf_obj, conf_obj_len);
+	if (!root)
+		return -1;
+	if (root->type != JSON_OBJECT) {
+		wpa_printf(MSG_DEBUG, "DPP: JSON root is not an object");
+		goto fail;
+	}
+
+	token = json_get_member(root, "wi-fi_tech");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG, "DPP: No wi-fi_tech string value found");
+		goto fail;
+	}
+	if (os_strcmp(token->string, "infra") != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech value: '%s'",
+			   token->string);
+		goto fail;
+	}
+
+	discovery = json_get_member(root, "discovery");
+	if (!discovery || discovery->type != JSON_OBJECT) {
+		wpa_printf(MSG_DEBUG, "DPP: No discovery object in JSON");
+		goto fail;
+	}
+
+	token = json_get_member(discovery, "ssid");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No discovery::ssid string value found");
+		goto fail;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "DPP: discovery::ssid",
+			  token->string, os_strlen(token->string));
+	if (os_strlen(token->string) > SSID_MAX_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Too long discovery::ssid string value");
+		goto fail;
+	}
+	auth->ssid_len = os_strlen(token->string);
+	os_memcpy(auth->ssid, token->string, auth->ssid_len);
+
+	cred = json_get_member(root, "cred");
+	if (!cred || cred->type != JSON_OBJECT) {
+		wpa_printf(MSG_DEBUG, "DPP: No cred object in JSON");
+		goto fail;
+	}
+
+	token = json_get_member(cred, "akm");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No cred::akm string value found");
+		goto fail;
+	}
+	if (os_strcmp(token->string, "psk") == 0) {
+		if (dpp_parse_cred_legacy(auth, cred) < 0)
+			goto fail;
+	} else if (os_strcmp(token->string, "dpp") == 0) {
+		if (dpp_parse_cred_dpp(auth, cred) < 0)
+			goto fail;
+	} else {
+		wpa_printf(MSG_DEBUG, "DPP: Unsupported akm: %s",
+			   token->string);
+		goto fail;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: JSON parsing completed successfully");
+	ret = 0;
+fail:
+	json_free(root);
+	return ret;
+}
+
+
+int dpp_conf_resp_rx(struct dpp_authentication *auth,
+		     const struct wpabuf *resp)
+{
+	const u8 *wrapped_data, *e_nonce, *status, *conf_obj;
+	u16 wrapped_data_len, e_nonce_len, status_len, conf_obj_len;
+	const u8 *addr[1];
+	size_t len[1];
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	int ret = -1;
+
+	if (dpp_check_attrs(wpabuf_head(resp), wpabuf_len(resp)) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in config response");
+		return -1;
+	}
+
+	wrapped_data = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp),
+				    DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Wrapped data attribute");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		return -1;
+
+	addr[0] = wpabuf_head(resp);
+	len[0] = wrapped_data - 4 - (const u8 *) wpabuf_head(resp);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
+
+	if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    1, addr, len, unwrapped) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: 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) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: 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) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: 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) {
+		wpa_printf(MSG_DEBUG, "Enrollee Nonce mismatch");
+		goto fail;
+	}
+
+	status = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp),
+			      DPP_ATTR_STATUS, &status_len);
+	if (!status || status_len < 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required DPP Status attribute");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+	if (status[0] != DPP_STATUS_OK) {
+		wpa_printf(MSG_DEBUG, "DPP: Configuration failed");
+		goto fail;
+	}
+
+	conf_obj = dpp_get_attr(unwrapped, unwrapped_len,
+				DPP_ATTR_CONFIG_OBJ, &conf_obj_len);
+	if (!conf_obj) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing required Configuration Object attribute");
+		goto fail;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON",
+			  conf_obj, conf_obj_len);
+	if (dpp_parse_conf_obj(auth, conf_obj, conf_obj_len) < 0)
+		goto fail;
+
+	ret = 0;
+
+fail:
+	os_free(unwrapped);
+	return ret;
+}
+
+
+void dpp_configurator_free(struct dpp_configurator *conf)
+{
+	if (!conf)
+		return;
+	EVP_PKEY_free(conf->csign);
+	os_free(conf->kid);
+	os_free(conf);
+}
+
+
+struct dpp_configurator *
+dpp_keygen_configurator(const char *curve, const u8 *privkey,
+			size_t privkey_len)
+{
+	struct dpp_configurator *conf;
+	struct wpabuf *csign_pub = NULL;
+	u8 kid_hash[SHA256_MAC_LEN];
+	const u8 *addr[1];
+	size_t len[1];
+
+	conf = os_zalloc(sizeof(*conf));
+	if (!conf)
+		return NULL;
+
+	if (!curve) {
+		conf->curve = &dpp_curves[0];
+	} else {
+		conf->curve = dpp_get_curve_name(curve);
+		if (!conf->curve) {
+			wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
+				   curve);
+			return NULL;
+		}
+	}
+	if (privkey)
+		conf->csign = dpp_set_keypair(&conf->curve, privkey,
+					      privkey_len);
+	else
+		conf->csign = dpp_gen_keypair(conf->curve);
+	if (!conf->csign)
+		goto fail;
+	conf->own = 1;
+
+	csign_pub = dpp_get_pubkey_point(conf->csign, 1);
+	if (!csign_pub) {
+		wpa_printf(MSG_INFO, "DPP: Failed to extract C-sign-key");
+		goto fail;
+	}
+
+	/* kid = SHA256(ANSI X9.63 uncompressed C-sign-key) */
+	addr[0] = wpabuf_head(csign_pub);
+	len[0] = wpabuf_len(csign_pub);
+	if (sha256_vector(1, addr, len, kid_hash) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to derive kid for C-sign-key");
+		goto fail;
+	}
+
+	conf->kid = (char *) base64_url_encode(kid_hash, sizeof(kid_hash),
+					       NULL, 0);
+	if (!conf->kid)
+		goto fail;
+out:
+	wpabuf_free(csign_pub);
+	return conf;
+fail:
+	dpp_configurator_free(conf);
+	conf = NULL;
+	goto out;
+}
+
+
+int dpp_configurator_own_config(struct dpp_authentication *auth,
+				const char *curve)
+{
+	struct wpabuf *conf_obj;
+	int ret = -1;
+
+	if (!auth->conf) {
+		wpa_printf(MSG_DEBUG, "DPP: No configurator specified");
+		return -1;
+	}
+
+	if (!curve) {
+		auth->curve = &dpp_curves[0];
+	} else {
+		auth->curve = dpp_get_curve_name(curve);
+		if (!auth->curve) {
+			wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
+				   curve);
+			return -1;
+		}
+	}
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Building own configuration/connector with curve %s",
+		   auth->curve->name);
+
+	auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+	if (!auth->own_protocol_key)
+		return -1;
+	dpp_copy_netaccesskey(auth);
+	auth->peer_protocol_key = auth->own_protocol_key;
+	dpp_copy_csign(auth, auth->conf->csign);
+
+	conf_obj = dpp_build_conf_obj(auth, 0);
+	if (!conf_obj)
+		goto fail;
+	ret = dpp_parse_conf_obj(auth, wpabuf_head(conf_obj),
+				 wpabuf_len(conf_obj));
+fail:
+	wpabuf_free(conf_obj);
+	auth->peer_protocol_key = NULL;
+	return ret;
+}
+
+
+static int dpp_compatible_netrole(const char *role1, const char *role2)
+{
+	return (os_strcmp(role1, "sta") == 0 && os_strcmp(role2, "ap") == 0) ||
+		(os_strcmp(role1, "ap") == 0 && os_strcmp(role2, "sta") == 0);
+}
+
+
+static int dpp_connector_compatible_group(struct json_token *root,
+					  const char *group_id,
+					  const char *net_role)
+{
+	struct json_token *groups, *token;
+
+	groups = json_get_member(root, "groups");
+	if (!groups || groups->type != JSON_ARRAY)
+		return 0;
+
+	for (token = groups->child; token; token = token->sibling) {
+		struct json_token *id, *role;
+
+		id = json_get_member(token, "groupId");
+		if (!id || id->type != JSON_STRING)
+			continue;
+
+		role = json_get_member(token, "netRole");
+		if (!role || role->type != JSON_STRING)
+			continue;
+
+		if (os_strcmp(id->string, "*") != 0 &&
+		    os_strcmp(group_id, "*") != 0 &&
+		    os_strcmp(id->string, group_id) != 0)
+			continue;
+
+		if (dpp_compatible_netrole(role->string, net_role))
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int dpp_connector_match_groups(struct json_token *own_root,
+				      struct json_token *peer_root)
+{
+	struct json_token *groups, *token;
+
+	groups = json_get_member(peer_root, "groups");
+	if (!groups || groups->type != JSON_ARRAY) {
+		wpa_printf(MSG_DEBUG, "DPP: No peer groups array found");
+		return 0;
+	}
+
+	for (token = groups->child; token; token = token->sibling) {
+		struct json_token *id, *role;
+
+		id = json_get_member(token, "groupId");
+		if (!id || id->type != JSON_STRING) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Missing peer groupId string");
+			continue;
+		}
+
+		role = json_get_member(token, "netRole");
+		if (!role || role->type != JSON_STRING) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Missing peer groups::netRole string");
+			continue;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "DPP: peer connector group: groupId='%s' netRole='%s'",
+			   id->string, role->string);
+		if (dpp_connector_compatible_group(own_root, id->string,
+						   role->string)) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Compatible group/netRole in own connector");
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+
+static int dpp_derive_pmk(const u8 *Nx, size_t Nx_len, u8 *pmk,
+			  unsigned int hash_len)
+{
+	u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+	const char *info = "DPP PMK";
+	int res;
+
+	/* PMK = HKDF(<>, "DPP PMK", N.x) */
+
+	/* HKDF-Extract(<>, N.x) */
+	os_memset(salt, 0, hash_len);
+	if (dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
+			prk, hash_len);
+
+	/* HKDF-Expand(PRK, info, L) */
+	res = dpp_hkdf_expand(hash_len, prk, hash_len, info, pmk, hash_len);
+	os_memset(prk, 0, hash_len);
+	if (res < 0)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PMK = HKDF-Expand(PRK, info, L)",
+			pmk, hash_len);
+	return 0;
+}
+
+
+static int dpp_derive_pmkid(const struct dpp_curve_params *curve,
+			    EVP_PKEY *own_key, EVP_PKEY *peer_key, u8 *pmkid)
+{
+	struct wpabuf *nkx, *pkx;
+	int ret = -1, res;
+	const u8 *addr[2];
+	size_t len[2];
+	u8 hash[SHA256_MAC_LEN];
+
+	/* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */
+	nkx = dpp_get_pubkey_point(own_key, 0);
+	pkx = dpp_get_pubkey_point(peer_key, 0);
+	if (!nkx || !pkx)
+		goto fail;
+	addr[0] = wpabuf_head(nkx);
+	len[0] = wpabuf_len(nkx) / 2;
+	addr[1] = wpabuf_head(pkx);
+	len[1] = wpabuf_len(pkx) / 2;
+	if (len[0] != len[1])
+		goto fail;
+	if (os_memcmp(addr[0], addr[1], len[0]) > 0) {
+		addr[0] = wpabuf_head(pkx);
+		addr[1] = wpabuf_head(nkx);
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 1", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 2", addr[1], len[1]);
+	res = sha256_vector(2, addr, len, hash);
+	if (res < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash output", hash, SHA256_MAC_LEN);
+	os_memcpy(pmkid, hash, PMKID_LEN);
+	wpa_hexdump(MSG_DEBUG, "DPP: PMKID", pmkid, PMKID_LEN);
+	ret = 0;
+fail:
+	wpabuf_free(nkx);
+	wpabuf_free(pkx);
+	return ret;
+}
+
+
+int dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
+		   const u8 *net_access_key, size_t net_access_key_len,
+		   const u8 *csign_key, size_t csign_key_len,
+		   const u8 *peer_connector, size_t peer_connector_len,
+		   os_time_t *expiry)
+{
+	struct json_token *root = NULL, *netkey, *token;
+	struct json_token *own_root = NULL;
+	int ret = -1;
+	EVP_PKEY *own_key = NULL, *peer_key = NULL;
+	struct wpabuf *own_key_pub = NULL;
+	const struct dpp_curve_params *curve, *own_curve;
+	struct dpp_signed_connector_info info;
+	const unsigned char *p;
+	EVP_PKEY *csign = NULL;
+	char *signed_connector = NULL;
+	const char *pos, *end;
+	unsigned char *own_conn = NULL;
+	size_t own_conn_len;
+	EVP_PKEY_CTX *ctx = NULL;
+	size_t Nx_len;
+	u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
+
+	os_memset(intro, 0, sizeof(*intro));
+	os_memset(&info, 0, sizeof(info));
+	if (expiry)
+		*expiry = 0;
+
+	p = csign_key;
+	csign = d2i_PUBKEY(NULL, &p, csign_key_len);
+	if (!csign) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to parse local C-sign-key information");
+		goto fail;
+	}
+
+	own_key = dpp_set_keypair(&own_curve, net_access_key,
+				  net_access_key_len);
+	if (!own_key) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
+		goto fail;
+	}
+
+	pos = os_strchr(own_connector, '.');
+	if (!pos) {
+		wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the first dot (.)");
+		goto fail;
+	}
+	pos++;
+	end = os_strchr(pos, '.');
+	if (!end) {
+		wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the second dot (.)");
+		goto fail;
+	}
+	own_conn = base64_url_decode((const unsigned char *) pos,
+				     end - pos, &own_conn_len);
+	if (!own_conn) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to base64url decode own signedConnector JWS Payload");
+		goto fail;
+	}
+
+	own_root = json_parse((const char *) own_conn, own_conn_len);
+	if (!own_root) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to parse local connector");
+		goto fail;
+	}
+
+	wpa_hexdump_ascii(MSG_DEBUG, "DPP: Peer signedConnector",
+			  peer_connector, peer_connector_len);
+	signed_connector = os_malloc(peer_connector_len + 1);
+	if (!signed_connector)
+		goto fail;
+	os_memcpy(signed_connector, peer_connector, peer_connector_len);
+	signed_connector[peer_connector_len] = '\0';
+
+	if (dpp_process_signed_connector(&info, csign, signed_connector) < 0)
+		goto fail;
+
+	root = json_parse((const char *) info.payload, info.payload_len);
+	if (!root) {
+		wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed");
+		goto fail;
+	}
+
+	if (!dpp_connector_match_groups(own_root, root)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer connector does not include compatible group netrole with own connector");
+		goto fail;
+	}
+
+	token = json_get_member(root, "expiry");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No expiry string found - connector does not expire");
+	} else {
+		wpa_printf(MSG_DEBUG, "DPP: expiry = %s", token->string);
+		if (dpp_key_expired(token->string, expiry)) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Connector (netAccessKey) has expired");
+			goto fail;
+		}
+	}
+
+	netkey = json_get_member(root, "netAccessKey");
+	if (!netkey || netkey->type != JSON_OBJECT) {
+		wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found");
+		goto fail;
+	}
+
+	peer_key = dpp_parse_jwk(netkey, &curve);
+	if (!peer_key)
+		goto fail;
+	dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
+
+	if (own_curve != curve) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Mismatching netAccessKey curves (%s != %s)",
+			   own_curve->name, curve->name);
+		goto fail;
+	}
+
+	/* ECDH: N = nk * PK */
+	ctx = EVP_PKEY_CTX_new(own_key, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, peer_key) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &Nx_len) != 1 ||
+	    Nx_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, Nx, &Nx_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
+			Nx, Nx_len);
+
+	/* PMK = HKDF(<>, "DPP PMK", N.x) */
+	if (dpp_derive_pmk(Nx, Nx_len, intro->pmk, curve->hash_len) < 0) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to derive PMK");
+		goto fail;
+	}
+	intro->pmk_len = curve->hash_len;
+
+	/* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */
+	if (dpp_derive_pmkid(curve, own_key, peer_key, intro->pmkid) < 0) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to derive PMKID");
+		goto fail;
+	}
+
+	ret = 0;
+fail:
+	if (ret < 0)
+		os_memset(intro, 0, sizeof(*intro));
+	os_memset(Nx, 0, sizeof(Nx));
+	EVP_PKEY_CTX_free(ctx);
+	os_free(own_conn);
+	os_free(signed_connector);
+	os_free(info.payload);
+	EVP_PKEY_free(own_key);
+	wpabuf_free(own_key_pub);
+	EVP_PKEY_free(peer_key);
+	EVP_PKEY_free(csign);
+	json_free(root);
+	json_free(own_root);
+	return ret;
+}
+
+
+static EVP_PKEY * dpp_pkex_get_role_elem(const struct dpp_curve_params *curve,
+					 int init)
+{
+	EC_GROUP *group;
+	size_t len = curve->prime_len;
+	const u8 *x, *y;
+
+	switch (curve->ike_group) {
+	case 19:
+		x = init ? pkex_init_x_p256 : pkex_resp_x_p256;
+		y = init ? pkex_init_y_p256 : pkex_resp_y_p256;
+		break;
+	case 20:
+		x = init ? pkex_init_x_p384 : pkex_resp_x_p384;
+		y = init ? pkex_init_y_p384 : pkex_resp_y_p384;
+		break;
+	case 21:
+		x = init ? pkex_init_x_p521 : pkex_resp_x_p521;
+		y = init ? pkex_init_y_p521 : pkex_resp_y_p521;
+		break;
+	case 28:
+		x = init ? pkex_init_x_bp_p256r1 : pkex_resp_x_bp_p256r1;
+		y = init ? pkex_init_y_bp_p256r1 : pkex_resp_y_bp_p256r1;
+		break;
+	case 29:
+		x = init ? pkex_init_x_bp_p384r1 : pkex_resp_x_bp_p384r1;
+		y = init ? pkex_init_y_bp_p384r1 : pkex_resp_y_bp_p384r1;
+		break;
+	case 30:
+		x = init ? pkex_init_x_bp_p512r1 : pkex_resp_x_bp_p512r1;
+		y = init ? pkex_init_y_bp_p512r1 : pkex_resp_y_bp_p512r1;
+		break;
+	default:
+		return NULL;
+	}
+
+	group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
+	if (!group)
+		return NULL;
+	return dpp_set_pubkey_point_group(group, x, y, len);
+}
+
+
+static EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve,
+				     const u8 *mac_init, const char *code,
+				     const char *identifier, BN_CTX *bnctx,
+				     const EC_GROUP **ret_group)
+{
+	u8 hash[DPP_MAX_HASH_LEN];
+	const u8 *addr[3];
+	size_t len[3];
+	unsigned int num_elem = 0;
+	EC_POINT *Qi = NULL;
+	EVP_PKEY *Pi = NULL;
+	EC_KEY *Pi_ec = NULL;
+	const EC_POINT *Pi_point;
+	BIGNUM *hash_bn = NULL;
+	const EC_GROUP *group = NULL;
+	EC_GROUP *group2 = NULL;
+
+	/* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
+
+	wpa_printf(MSG_DEBUG, "DPP: MAC-Initiator: " MACSTR, MAC2STR(mac_init));
+	addr[num_elem] = mac_init;
+	len[num_elem] = ETH_ALEN;
+	num_elem++;
+	if (identifier) {
+		wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
+			   identifier);
+		addr[num_elem] = (const u8 *) identifier;
+		len[num_elem] = os_strlen(identifier);
+		num_elem++;
+	}
+	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
+	addr[num_elem] = (const u8 *) code;
+	len[num_elem] = os_strlen(code);
+	num_elem++;
+	if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG,
+			"DPP: H(MAC-Initiator | [identifier |] code)",
+			hash, curve->hash_len);
+	Pi = dpp_pkex_get_role_elem(curve, 1);
+	if (!Pi)
+		goto fail;
+	dpp_debug_print_key("DPP: Pi", Pi);
+	Pi_ec = EVP_PKEY_get1_EC_KEY(Pi);
+	if (!Pi_ec)
+		goto fail;
+	Pi_point = EC_KEY_get0_public_key(Pi_ec);
+
+	group = EC_KEY_get0_group(Pi_ec);
+	if (!group)
+		goto fail;
+	group2 = EC_GROUP_dup(group);
+	if (!group2)
+		goto fail;
+	Qi = EC_POINT_new(group2);
+	if (!Qi) {
+		EC_GROUP_free(group2);
+		goto fail;
+	}
+	hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
+	if (!hash_bn ||
+	    EC_POINT_mul(group2, Qi, NULL, Pi_point, hash_bn, bnctx) != 1)
+		goto fail;
+	if (EC_POINT_is_at_infinity(group, Qi)) {
+		wpa_printf(MSG_INFO, "PDP: Qi is the point-at-infinity");
+		goto fail;
+	}
+out:
+	EC_KEY_free(Pi_ec);
+	EVP_PKEY_free(Pi);
+	BN_clear_free(hash_bn);
+	if (ret_group)
+		*ret_group = group2;
+	return Qi;
+fail:
+	EC_POINT_free(Qi);
+	Qi = NULL;
+	goto out;
+}
+
+
+static EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve,
+				     const u8 *mac_resp, const char *code,
+				     const char *identifier, BN_CTX *bnctx,
+				     const EC_GROUP **ret_group)
+{
+	u8 hash[DPP_MAX_HASH_LEN];
+	const u8 *addr[3];
+	size_t len[3];
+	unsigned int num_elem = 0;
+	EC_POINT *Qr = NULL;
+	EVP_PKEY *Pr = NULL;
+	EC_KEY *Pr_ec = NULL;
+	const EC_POINT *Pr_point;
+	BIGNUM *hash_bn = NULL;
+	const EC_GROUP *group = NULL;
+	EC_GROUP *group2 = NULL;
+
+	/* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
+
+	wpa_printf(MSG_DEBUG, "DPP: MAC-Responder: " MACSTR, MAC2STR(mac_resp));
+	addr[num_elem] = mac_resp;
+	len[num_elem] = ETH_ALEN;
+	num_elem++;
+	if (identifier) {
+		wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
+			   identifier);
+		addr[num_elem] = (const u8 *) identifier;
+		len[num_elem] = os_strlen(identifier);
+		num_elem++;
+	}
+	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
+	addr[num_elem] = (const u8 *) code;
+	len[num_elem] = os_strlen(code);
+	num_elem++;
+	if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG,
+			"DPP: H(MAC-Responder | [identifier |] code)",
+			hash, curve->hash_len);
+	Pr = dpp_pkex_get_role_elem(curve, 0);
+	if (!Pr)
+		goto fail;
+	dpp_debug_print_key("DPP: Pr", Pr);
+	Pr_ec = EVP_PKEY_get1_EC_KEY(Pr);
+	if (!Pr_ec)
+		goto fail;
+	Pr_point = EC_KEY_get0_public_key(Pr_ec);
+
+	group = EC_KEY_get0_group(Pr_ec);
+	if (!group)
+		goto fail;
+	group2 = EC_GROUP_dup(group);
+	if (!group2)
+		goto fail;
+	Qr = EC_POINT_new(group2);
+	if (!Qr) {
+		EC_GROUP_free(group2);
+		goto fail;
+	}
+	hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
+	if (!hash_bn ||
+	    EC_POINT_mul(group2, Qr, NULL, Pr_point, hash_bn, bnctx) != 1)
+		goto fail;
+out:
+	EC_KEY_free(Pr_ec);
+	EVP_PKEY_free(Pr);
+	BN_clear_free(hash_bn);
+	if (ret_group)
+		*ret_group = group2;
+	return Qr;
+fail:
+	EC_POINT_free(Qr);
+	Qr = NULL;
+	goto out;
+}
+
+
+static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex)
+{
+	EC_KEY *X_ec = NULL;
+	const EC_POINT *X_point;
+	BN_CTX *bnctx = NULL;
+	const EC_GROUP *group;
+	EC_POINT *Qi = NULL, *M = NULL;
+	struct wpabuf *M_buf = NULL;
+	BIGNUM *Mx = NULL, *My = NULL;
+	struct wpabuf *msg = NULL;
+	size_t attr_len;
+	const struct dpp_curve_params *curve = pkex->own_bi->curve;
+	int num_bytes, offset;
+
+	wpa_printf(MSG_DEBUG, "DPP: Build PKEX Exchange Request");
+
+	/* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
+	bnctx = BN_CTX_new();
+	if (!bnctx)
+		goto fail;
+	Qi = dpp_pkex_derive_Qi(curve, pkex->own_mac, pkex->code,
+				pkex->identifier, bnctx, &group);
+	if (!Qi)
+		goto fail;
+
+	/* Generate a random ephemeral keypair x/X */
+	pkex->x = dpp_gen_keypair(curve);
+	if (!pkex->x)
+		goto fail;
+
+	/* M = X + Qi */
+	X_ec = EVP_PKEY_get1_EC_KEY(pkex->x);
+	if (!X_ec)
+		goto fail;
+	X_point = EC_KEY_get0_public_key(X_ec);
+	if (!X_point)
+		goto fail;
+	M = EC_POINT_new(group);
+	Mx = BN_new();
+	My = BN_new();
+	if (!M || !Mx || !My ||
+	    EC_POINT_add(group, M, X_point, Qi, bnctx) != 1 ||
+	    EC_POINT_get_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1)
+		goto fail;
+
+	/* Initiator -> Responder: group, [identifier,] M */
+	attr_len = 4 + 2;
+	if (pkex->identifier)
+		attr_len += 4 + os_strlen(pkex->identifier);
+	attr_len += 4 + 2 * curve->prime_len;
+	msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_REQ, attr_len);
+	if (!msg)
+		goto fail;
+
+	/* Finite Cyclic Group attribute */
+	wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
+	wpabuf_put_le16(msg, 2);
+	wpabuf_put_le16(msg, curve->ike_group);
+
+	/* Code Identifier attribute */
+	if (pkex->identifier) {
+		wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
+		wpabuf_put_le16(msg, os_strlen(pkex->identifier));
+		wpabuf_put_str(msg, pkex->identifier);
+	}
+
+	/* M in Encrypted Key attribute */
+	wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
+	wpabuf_put_le16(msg, 2 * curve->prime_len);
+
+	num_bytes = BN_num_bytes(Mx);
+	if ((size_t) num_bytes > curve->prime_len)
+		goto fail;
+	if (curve->prime_len > (size_t) num_bytes)
+		offset = curve->prime_len - num_bytes;
+	else
+		offset = 0;
+	os_memset(wpabuf_put(msg, offset), 0, offset);
+	BN_bn2bin(Mx, wpabuf_put(msg, num_bytes));
+	os_memset(pkex->Mx, 0, offset);
+	BN_bn2bin(Mx, pkex->Mx + offset);
+
+	num_bytes = BN_num_bytes(My);
+	if ((size_t) num_bytes > curve->prime_len)
+		goto fail;
+	if (curve->prime_len > (size_t) num_bytes)
+		offset = curve->prime_len - num_bytes;
+	else
+		offset = 0;
+	os_memset(wpabuf_put(msg, offset), 0, offset);
+	BN_bn2bin(My, wpabuf_put(msg, num_bytes));
+
+out:
+	wpabuf_free(M_buf);
+	EC_KEY_free(X_ec);
+	EC_POINT_free(M);
+	EC_POINT_free(Qi);
+	BN_clear_free(Mx);
+	BN_clear_free(My);
+	BN_CTX_free(bnctx);
+	return msg;
+fail:
+	wpa_printf(MSG_INFO, "DPP: Failed to build PKEX Exchange Request");
+	wpabuf_free(msg);
+	msg = NULL;
+	goto out;
+}
+
+
+struct dpp_pkex * dpp_pkex_init(struct dpp_bootstrap_info *bi,
+				const u8 *own_mac,
+				const char *identifier,
+				const char *code)
+{
+	struct dpp_pkex *pkex;
+
+	pkex = os_zalloc(sizeof(*pkex));
+	if (!pkex)
+		return NULL;
+	pkex->initiator = 1;
+	pkex->own_bi = bi;
+	os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
+	if (identifier) {
+		pkex->identifier = os_strdup(identifier);
+		if (!pkex->identifier)
+			goto fail;
+	}
+	pkex->code = os_strdup(code);
+	if (!pkex->code)
+		goto fail;
+	pkex->exchange_req = dpp_pkex_build_exchange_req(pkex);
+	if (!pkex->exchange_req)
+		goto fail;
+	return pkex;
+fail:
+	dpp_pkex_free(pkex);
+	return NULL;
+}
+
+
+struct dpp_pkex * dpp_pkex_rx_exchange_req(struct dpp_bootstrap_info *bi,
+					   const u8 *own_mac,
+					   const u8 *peer_mac,
+					   const char *identifier,
+					   const char *code,
+					   const u8 *buf, size_t len)
+{
+	const u8 *attr_group, *attr_id, *attr_key;
+	u16 attr_group_len, attr_id_len, attr_key_len;
+	const struct dpp_curve_params *curve = bi->curve;
+	u16 ike_group;
+	struct dpp_pkex *pkex = NULL;
+	EC_POINT *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL, *N = NULL;
+	BN_CTX *bnctx = NULL;
+	const EC_GROUP *group;
+	BIGNUM *Mx = NULL, *My = NULL;
+	EC_KEY *Y_ec = NULL, *X_ec = NULL;;
+	const EC_POINT *Y_point;
+	BIGNUM *Nx = NULL, *Ny = NULL;
+	struct wpabuf *msg = NULL;
+	size_t attr_len;
+	int num_bytes, offset;
+
+	attr_id = dpp_get_attr(buf, len, DPP_ATTR_CODE_IDENTIFIER,
+			       &attr_id_len);
+	if (!attr_id && identifier) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No PKEX code identifier received, but expected one");
+		return NULL;
+	}
+	if (attr_id && identifier &&
+	    (os_strlen(identifier) != attr_id_len ||
+	     os_memcmp(identifier, attr_id, attr_id_len) != 0)) {
+		wpa_printf(MSG_DEBUG, "DPP: PKEX code identifier mismatch");
+		return NULL;
+	}
+
+	attr_group = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
+				  &attr_group_len);
+	if (!attr_group || attr_group_len != 2) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid Finite Cyclic Group attribute");
+		return NULL;
+	}
+	ike_group = WPA_GET_LE16(attr_group);
+	if (ike_group != curve->ike_group) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Mismatching PKEX curve: peer=%u own=%u",
+			   ike_group, curve->ike_group);
+		/* TODO: error response with suggested curve:
+		 * DPP Status, group */
+		return NULL;
+	}
+
+	/* M in Encrypted Key attribute */
+	attr_key = dpp_get_attr(buf, len, DPP_ATTR_ENCRYPTED_KEY,
+				&attr_key_len);
+	if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2 ||
+	    attr_key_len / 2 > DPP_MAX_SHARED_SECRET_LEN) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing Encrypted Key attribute");
+		return NULL;
+	}
+
+	/* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
+	bnctx = BN_CTX_new();
+	if (!bnctx)
+		goto fail;
+	Qi = dpp_pkex_derive_Qi(curve, peer_mac, code, identifier, bnctx,
+				&group);
+	if (!Qi)
+		goto fail;
+
+	/* X' = M - Qi */
+	X = EC_POINT_new(group);
+	M = EC_POINT_new(group);
+	Mx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
+	My = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
+	if (!X || !M || !Mx || !My ||
+	    EC_POINT_set_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1 ||
+	    EC_POINT_is_at_infinity(group, M) ||
+	    !EC_POINT_is_on_curve(group, M, bnctx) ||
+	    EC_POINT_invert(group, Qi, bnctx) != 1 ||
+	    EC_POINT_add(group, X, M, Qi, bnctx) != 1 ||
+	    EC_POINT_is_at_infinity(group, X) ||
+	    !EC_POINT_is_on_curve(group, X, bnctx))
+		goto fail;
+
+	pkex = os_zalloc(sizeof(*pkex));
+	if (!pkex)
+		goto fail;
+	pkex->own_bi = bi;
+	os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
+	os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
+	if (identifier) {
+		pkex->identifier = os_strdup(identifier);
+		if (!pkex->identifier)
+			goto fail;
+	}
+	pkex->code = os_strdup(code);
+	if (!pkex->code)
+		goto fail;
+
+	os_memcpy(pkex->Mx, attr_key, attr_key_len / 2);
+
+	X_ec = EC_KEY_new();
+	if (!X_ec ||
+	    EC_KEY_set_group(X_ec, group) != 1 ||
+	    EC_KEY_set_public_key(X_ec, X) != 1)
+		goto fail;
+	pkex->x = EVP_PKEY_new();
+	if (!pkex->x ||
+	    EVP_PKEY_set1_EC_KEY(pkex->x, X_ec) != 1)
+		goto fail;
+
+	/* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
+	Qr = dpp_pkex_derive_Qr(curve, own_mac, code, identifier, bnctx, NULL);
+	if (!Qr)
+		goto fail;
+
+	/* Generate a random ephemeral keypair y/Y */
+	pkex->y = dpp_gen_keypair(curve);
+	if (!pkex->y)
+		goto fail;
+
+	/* N = Y + Qr */
+	Y_ec = EVP_PKEY_get1_EC_KEY(pkex->y);
+	if (!Y_ec)
+		goto fail;
+	Y_point = EC_KEY_get0_public_key(Y_ec);
+	if (!Y_point)
+		goto fail;
+	N = EC_POINT_new(group);
+	Nx = BN_new();
+	Ny = BN_new();
+	if (!N || !Nx || !Ny ||
+	    EC_POINT_add(group, N, Y_point, Qr, bnctx) != 1 ||
+	    EC_POINT_get_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1)
+		goto fail;
+
+	/* Initiator -> Responder: DPP Status, [identifier,] N */
+	attr_len = 4 + 1;
+	if (identifier)
+		attr_len += 4 + os_strlen(identifier);
+	attr_len += 4 + 2 * curve->prime_len;
+	msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_RESP, attr_len);
+	if (!msg)
+		goto fail;
+
+	/* DPP Status */
+	wpabuf_put_le16(msg, DPP_ATTR_STATUS);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, DPP_STATUS_OK);
+
+	/* Code Identifier attribute */
+	if (pkex->identifier) {
+		wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
+		wpabuf_put_le16(msg, os_strlen(pkex->identifier));
+		wpabuf_put_str(msg, pkex->identifier);
+	}
+
+	/* N in Encrypted Key attribute */
+	wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
+	wpabuf_put_le16(msg, 2 * curve->prime_len);
+
+	num_bytes = BN_num_bytes(Nx);
+	if ((size_t) num_bytes > curve->prime_len)
+		goto fail;
+	if (curve->prime_len > (size_t) num_bytes)
+		offset = curve->prime_len - num_bytes;
+	else
+		offset = 0;
+	os_memset(wpabuf_put(msg, offset), 0, offset);
+	BN_bn2bin(Nx, wpabuf_put(msg, num_bytes));
+	os_memset(pkex->Nx, 0, offset);
+	BN_bn2bin(Nx, pkex->Nx + offset);
+
+	num_bytes = BN_num_bytes(Ny);
+	if ((size_t) num_bytes > curve->prime_len)
+		goto fail;
+	if (curve->prime_len > (size_t) num_bytes)
+		offset = curve->prime_len - num_bytes;
+	else
+		offset = 0;
+	os_memset(wpabuf_put(msg, offset), 0, offset);
+	BN_bn2bin(Ny, wpabuf_put(msg, num_bytes));
+
+	pkex->exchange_resp = msg;
+	msg = NULL;
+	pkex->exchange_done = 1;
+
+out:
+	wpabuf_free(msg);
+	BN_CTX_free(bnctx);
+	EC_POINT_free(Qi);
+	EC_POINT_free(Qr);
+	BN_free(Mx);
+	BN_free(My);
+	BN_free(Nx);
+	BN_free(Ny);
+	EC_POINT_free(M);
+	EC_POINT_free(N);
+	EC_POINT_free(X);
+	EC_KEY_free(X_ec);
+	EC_KEY_free(Y_ec);
+	return pkex;
+fail:
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request processing faileed");
+	dpp_pkex_free(pkex);
+	pkex = NULL;
+	goto out;
+}
+
+
+static int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp,
+			     const u8 *Mx, size_t Mx_len,
+			     const u8 *Nx, size_t Nx_len,
+			     const char *code,
+			     const u8 *Kx, size_t Kx_len,
+			     u8 *z, unsigned int hash_len)
+{
+	u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+	int res;
+	u8 *info, *pos;
+	size_t info_len;
+
+	/* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
+	 */
+
+	/* HKDF-Extract(<>, IKM=K.x) */
+	os_memset(salt, 0, hash_len);
+	if (dpp_hmac(hash_len, salt, hash_len, Kx, Kx_len, prk) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
+			prk, hash_len);
+	info_len = 2 * ETH_ALEN + Mx_len + Nx_len + os_strlen(code);
+	info = os_malloc(info_len);
+	if (!info)
+		return -1;
+	pos = info;
+	os_memcpy(pos, mac_init, ETH_ALEN);
+	pos += ETH_ALEN;
+	os_memcpy(pos, mac_resp, ETH_ALEN);
+	pos += ETH_ALEN;
+	os_memcpy(pos, Mx, Mx_len);
+	pos += Mx_len;
+	os_memcpy(pos, Nx, Nx_len);
+	pos += Nx_len;
+	os_memcpy(pos, code, os_strlen(code));
+
+	/* HKDF-Expand(PRK, info, L) */
+	if (hash_len == 32)
+		res = hmac_sha256_kdf(prk, hash_len, NULL, info, info_len,
+				      z, hash_len);
+	else if (hash_len == 48)
+		res = hmac_sha384_kdf(prk, hash_len, NULL, info, info_len,
+				      z, hash_len);
+	else if (hash_len == 64)
+		res = hmac_sha512_kdf(prk, hash_len, NULL, info, info_len,
+				      z, hash_len);
+	else
+		res = -1;
+	os_free(info);
+	os_memset(prk, 0, hash_len);
+	if (res < 0)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: z = HKDF-Expand(PRK, info, L)",
+			z, hash_len);
+	return 0;
+}
+
+
+struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
+					  const u8 *buf, size_t buflen)
+{
+	const u8 *attr_status, *attr_id, *attr_key;
+	u16 attr_status_len, attr_id_len, attr_key_len;
+	const EC_GROUP *group;
+	BN_CTX *bnctx = NULL;
+	size_t clear_len;
+	struct wpabuf *clear = NULL;
+	u8 *wrapped;
+	struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
+	const struct dpp_curve_params *curve = pkex->own_bi->curve;
+	EC_POINT *Qr = NULL, *Y = NULL, *N = NULL;
+	BIGNUM *Nx = NULL, *Ny = NULL;
+	EVP_PKEY_CTX *ctx = NULL;
+	EC_KEY *Y_ec = NULL;
+	size_t Jx_len, Kx_len;
+	u8 Jx[DPP_MAX_SHARED_SECRET_LEN], Kx[DPP_MAX_SHARED_SECRET_LEN];
+	const u8 *addr[4];
+	size_t len[4];
+	u8 u[DPP_MAX_HASH_LEN];
+	u8 octet;
+	int res;
+
+	attr_status = dpp_get_attr(buf, buflen, DPP_ATTR_STATUS,
+				   &attr_status_len);
+	if (!attr_status || attr_status_len != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: No DPP Status attribute");
+		return NULL;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Status %u", attr_status[0]);
+	if (attr_status[0] != DPP_STATUS_OK) {
+		wpa_printf(MSG_DEBUG, "DPP: PKEX failed");
+		return NULL;
+	}
+
+	attr_id = dpp_get_attr(buf, buflen, DPP_ATTR_CODE_IDENTIFIER,
+			       &attr_id_len);
+	if (!attr_id && pkex->identifier) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No PKEX code identifier received, but expected one");
+		return NULL;
+	}
+	if (attr_id && pkex->identifier &&
+	    (os_strlen(pkex->identifier) != attr_id_len ||
+	     os_memcmp(pkex->identifier, attr_id, attr_id_len) != 0)) {
+		wpa_printf(MSG_DEBUG, "DPP: PKEX code identifier mismatch");
+		return NULL;
+	}
+
+	/* N in Encrypted Key attribute */
+	attr_key = dpp_get_attr(buf, buflen, DPP_ATTR_ENCRYPTED_KEY,
+				&attr_key_len);
+	if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing Encrypted Key attribute");
+		return NULL;
+	}
+
+	/* Qr = H(MAC-Responder | [identifier |] code) * Pr */
+	bnctx = BN_CTX_new();
+	if (!bnctx)
+		goto fail;
+	Qr = dpp_pkex_derive_Qr(curve, pkex->peer_mac, pkex->code,
+				pkex->identifier, bnctx, &group);
+	if (!Qr)
+		goto fail;
+
+	/* Y' = N - Qr */
+	Y = EC_POINT_new(group);
+	N = EC_POINT_new(group);
+	Nx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
+	Ny = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
+	if (!Y || !N || !Nx || !Ny ||
+	    EC_POINT_set_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1 ||
+	    EC_POINT_is_at_infinity(group, N) ||
+	    !EC_POINT_is_on_curve(group, N, bnctx) ||
+	    EC_POINT_invert(group, Qr, bnctx) != 1 ||
+	    EC_POINT_add(group, Y, N, Qr, bnctx) != 1 ||
+	    EC_POINT_is_at_infinity(group, Y) ||
+	    !EC_POINT_is_on_curve(group, Y, bnctx))
+		goto fail;
+
+	pkex->exchange_done = 1;
+
+	/* ECDH: J = a * Y’ */
+	Y_ec = EC_KEY_new();
+	if (!Y_ec ||
+	    EC_KEY_set_group(Y_ec, group) != 1 ||
+	    EC_KEY_set_public_key(Y_ec, Y) != 1)
+		goto fail;
+	pkex->y = EVP_PKEY_new();
+	if (!pkex->y ||
+	    EVP_PKEY_set1_EC_KEY(pkex->y, Y_ec) != 1)
+		goto fail;
+	ctx = EVP_PKEY_CTX_new(pkex->own_bi->pubkey, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, pkex->y) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &Jx_len) != 1 ||
+	    Jx_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, Jx, &Jx_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
+			Jx, Jx_len);
+
+	/* u = HMAC(J.x,  MAC-Initiator | A.x | Y’.x | X.x ) */
+	A_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
+	Y_pub = dpp_get_pubkey_point(pkex->y, 0);
+	X_pub = dpp_get_pubkey_point(pkex->x, 0);
+	if (!A_pub || !Y_pub || !X_pub)
+		goto fail;
+	addr[0] = pkex->own_mac;
+	len[0] = ETH_ALEN;
+	addr[1] = wpabuf_head(A_pub);
+	len[1] = wpabuf_len(A_pub) / 2;
+	addr[2] = wpabuf_head(Y_pub);
+	len[2] = wpabuf_len(Y_pub) / 2;
+	addr[3] = wpabuf_head(X_pub);
+	len[3] = wpabuf_len(X_pub) / 2;
+	if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: u", u, curve->hash_len);
+
+	/* K = x * Y’ */
+	EVP_PKEY_CTX_free(ctx);
+	ctx = EVP_PKEY_CTX_new(pkex->x, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, pkex->y) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &Kx_len) != 1 ||
+	    Kx_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, Kx, &Kx_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
+			Kx, Kx_len);
+
+	/* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
+	 */
+	res = dpp_pkex_derive_z(pkex->own_mac, pkex->peer_mac,
+				pkex->Mx, curve->prime_len,
+				attr_key /* N.x */, attr_key_len / 2,
+				pkex->code, Kx, Kx_len,
+				pkex->z, curve->hash_len);
+	os_memset(Kx, 0, Kx_len);
+	if (res < 0)
+		goto fail;
+
+	/* {A, u, [bootstrapping info]}z */
+	clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
+	clear = wpabuf_alloc(clear_len);
+	msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_REQ,
+			    4 + clear_len + AES_BLOCK_SIZE);
+	if (!clear || !msg)
+		goto fail;
+
+	/* A in Bootstrap Key attribute */
+	wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+	wpabuf_put_le16(clear, wpabuf_len(A_pub));
+	wpabuf_put_buf(clear, A_pub);
+
+	/* u in I-Auth tag attribute */
+	wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
+	wpabuf_put_le16(clear, curve->hash_len);
+	wpabuf_put_data(clear, u, curve->hash_len);
+
+	addr[0] = wpabuf_head_u8(msg) + 2;
+	len[0] = DPP_HDR_LEN;
+	octet = 0;
+	addr[1] = &octet;
+	len[1] = sizeof(octet);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+	wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+	if (aes_siv_encrypt(pkex->z, curve->hash_len,
+			    wpabuf_head(clear), wpabuf_len(clear),
+			    2, addr, len, wrapped) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+out:
+	wpabuf_free(clear);
+	wpabuf_free(A_pub);
+	wpabuf_free(X_pub);
+	wpabuf_free(Y_pub);
+	EC_POINT_free(Qr);
+	EC_POINT_free(Y);
+	EC_POINT_free(N);
+	BN_free(Nx);
+	BN_free(Ny);
+	EC_KEY_free(Y_ec);
+	EVP_PKEY_CTX_free(ctx);
+	BN_CTX_free(bnctx);
+	return msg;
+fail:
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response processing faileed");
+	wpabuf_free(msg);
+	msg = NULL;
+	goto out;
+}
+
+
+struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
+					      const u8 *hdr,
+					      const u8 *buf, size_t buflen)
+{
+	const struct dpp_curve_params *curve = pkex->own_bi->curve;
+	EVP_PKEY_CTX *ctx;
+	size_t Jx_len, Kx_len, Lx_len;
+	u8 Jx[DPP_MAX_SHARED_SECRET_LEN], Kx[DPP_MAX_SHARED_SECRET_LEN];
+	u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
+	const u8 *wrapped_data, *b_key, *peer_u;
+	u16 wrapped_data_len, b_key_len, peer_u_len = 0;
+	const u8 *addr[4];
+	size_t len[4];
+	u8 octet;
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
+	struct wpabuf *B_pub = NULL;
+	u8 u[DPP_MAX_HASH_LEN], v[DPP_MAX_HASH_LEN];
+	size_t clear_len;
+	struct wpabuf *clear = NULL;
+	u8 *wrapped;
+	int res;
+
+	/* K = y * X' */
+	ctx = EVP_PKEY_CTX_new(pkex->y, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, pkex->x) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &Kx_len) != 1 ||
+	    Kx_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, Kx, &Kx_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
+			Kx, Kx_len);
+
+	/* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
+	 */
+	res = dpp_pkex_derive_z(pkex->peer_mac, pkex->own_mac,
+				pkex->Mx, curve->prime_len,
+				pkex->Nx, curve->prime_len, pkex->code,
+				Kx, Kx_len, pkex->z, curve->hash_len);
+	os_memset(Kx, 0, Kx_len);
+	if (res < 0)
+		goto fail;
+
+	wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Wrapped data attribute");
+		goto fail;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		goto fail;
+
+	addr[0] = hdr;
+	len[0] = DPP_HDR_LEN;
+	octet = 0;
+	addr[1] = &octet;
+	len[1] = sizeof(octet);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+	if (aes_siv_decrypt(pkex->z, curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    2, addr, len, unwrapped) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: 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) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
+			     &b_key_len);
+	if (!b_key || b_key_len != 2 * curve->prime_len) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No valid peer bootstrapping key found");
+		goto fail;
+	}
+	pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
+							b_key_len);
+	if (!pkex->peer_bootstrap_key)
+		goto fail;
+	dpp_debug_print_key("DPP: Peer bootstrap public key",
+			    pkex->peer_bootstrap_key);
+
+	/* ECDH: J' = y * A' */
+	EVP_PKEY_CTX_free(ctx);
+	ctx = EVP_PKEY_CTX_new(pkex->y, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, pkex->peer_bootstrap_key) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &Jx_len) != 1 ||
+	    Jx_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, Jx, &Jx_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
+			Jx, Jx_len);
+
+	/* u' = HMAC(J'.x, MAC-Initiator | A'.x | Y.x | X'.x) */
+	A_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
+	Y_pub = dpp_get_pubkey_point(pkex->y, 0);
+	X_pub = dpp_get_pubkey_point(pkex->x, 0);
+	if (!A_pub || !Y_pub || !X_pub)
+		goto fail;
+	addr[0] = pkex->peer_mac;
+	len[0] = ETH_ALEN;
+	addr[1] = wpabuf_head(A_pub);
+	len[1] = wpabuf_len(A_pub) / 2;
+	addr[2] = wpabuf_head(Y_pub);
+	len[2] = wpabuf_len(Y_pub) / 2;
+	addr[3] = wpabuf_head(X_pub);
+	len[3] = wpabuf_len(X_pub) / 2;
+	if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
+		goto fail;
+
+	peer_u = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
+			      &peer_u_len);
+	if (!peer_u || peer_u_len != curve->hash_len ||
+	    os_memcmp(peer_u, u, curve->hash_len) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: No valid u (I-Auth tag) found");
+		wpa_hexdump(MSG_DEBUG, "DPP: Calculated u'",
+			    u, curve->hash_len);
+		wpa_hexdump(MSG_DEBUG, "DPP: Received u", peer_u, peer_u_len);
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Valid u (I-Auth tag) received");
+
+	/* ECDH: L = b * X' */
+	EVP_PKEY_CTX_free(ctx);
+	ctx = EVP_PKEY_CTX_new(pkex->own_bi->pubkey, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, pkex->x) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &Lx_len) != 1 ||
+	    Lx_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, Lx, &Lx_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
+			Lx, Lx_len);
+
+	/* v = HMAC(L.x, MAC-Responder | B.x | X'.x | Y.x) */
+	B_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
+	if (!B_pub)
+		goto fail;
+	addr[0] = pkex->own_mac;
+	len[0] = ETH_ALEN;
+	addr[1] = wpabuf_head(B_pub);
+	len[1] = wpabuf_len(B_pub) / 2;
+	addr[2] = wpabuf_head(X_pub);
+	len[2] = wpabuf_len(X_pub) / 2;
+	addr[3] = wpabuf_head(Y_pub);
+	len[3] = wpabuf_len(Y_pub) / 2;
+	if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: v", v, curve->hash_len);
+
+	/* {B, v [bootstrapping info]}z */
+	clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
+	clear = wpabuf_alloc(clear_len);
+	msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_RESP,
+			    4 + clear_len + AES_BLOCK_SIZE);
+	if (!clear || !msg)
+		goto fail;
+
+	/* A in Bootstrap Key attribute */
+	wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+	wpabuf_put_le16(clear, wpabuf_len(B_pub));
+	wpabuf_put_buf(clear, B_pub);
+
+	/* v in R-Auth tag attribute */
+	wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
+	wpabuf_put_le16(clear, curve->hash_len);
+	wpabuf_put_data(clear, v, curve->hash_len);
+
+	addr[0] = wpabuf_head_u8(msg) + 2;
+	len[0] = DPP_HDR_LEN;
+	octet = 1;
+	addr[1] = &octet;
+	len[1] = sizeof(octet);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+	wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+	if (aes_siv_encrypt(pkex->z, curve->hash_len,
+			    wpabuf_head(clear), wpabuf_len(clear),
+			    2, addr, len, wrapped) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+out:
+	EVP_PKEY_CTX_free(ctx);
+	os_free(unwrapped);
+	wpabuf_free(A_pub);
+	wpabuf_free(B_pub);
+	wpabuf_free(X_pub);
+	wpabuf_free(Y_pub);
+	wpabuf_free(clear);
+	return msg;
+fail:
+	wpabuf_free(msg);
+	msg = NULL;
+	goto out;
+}
+
+
+int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr,
+				   const u8 *buf, size_t buflen)
+{
+	const struct dpp_curve_params *curve = pkex->own_bi->curve;
+	const u8 *wrapped_data, *b_key, *peer_v;
+	u16 wrapped_data_len, b_key_len, peer_v_len = 0;
+	const u8 *addr[4];
+	size_t len[4];
+	u8 octet;
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	int ret = -1;
+	u8 v[DPP_MAX_HASH_LEN];
+	size_t Lx_len;
+	u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
+	EVP_PKEY_CTX *ctx = NULL;
+	struct wpabuf *B_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
+
+	wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Wrapped data attribute");
+		goto fail;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		goto fail;
+
+	addr[0] = hdr;
+	len[0] = DPP_HDR_LEN;
+	octet = 1;
+	addr[1] = &octet;
+	len[1] = sizeof(octet);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+	if (aes_siv_decrypt(pkex->z, curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    2, addr, len, unwrapped) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: 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) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
+			     &b_key_len);
+	if (!b_key || b_key_len != 2 * curve->prime_len) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No valid peer bootstrapping key found");
+		goto fail;
+	}
+	pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
+							b_key_len);
+	if (!pkex->peer_bootstrap_key)
+		goto fail;
+	dpp_debug_print_key("DPP: Peer bootstrap public key",
+			    pkex->peer_bootstrap_key);
+
+	/* ECDH: L' = x * B' */
+	ctx = EVP_PKEY_CTX_new(pkex->x, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, pkex->peer_bootstrap_key) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &Lx_len) != 1 ||
+	    Lx_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, Lx, &Lx_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
+			Lx, Lx_len);
+
+	/* v' = HMAC(L.x, MAC-Responder | B'.x | X.x | Y'.x) */
+	B_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
+	X_pub = dpp_get_pubkey_point(pkex->x, 0);
+	Y_pub = dpp_get_pubkey_point(pkex->y, 0);
+	if (!B_pub || !X_pub || !Y_pub)
+		goto fail;
+	addr[0] = pkex->peer_mac;
+	len[0] = ETH_ALEN;
+	addr[1] = wpabuf_head(B_pub);
+	len[1] = wpabuf_len(B_pub) / 2;
+	addr[2] = wpabuf_head(X_pub);
+	len[2] = wpabuf_len(X_pub) / 2;
+	addr[3] = wpabuf_head(Y_pub);
+	len[3] = wpabuf_len(Y_pub) / 2;
+	if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
+		goto fail;
+
+	peer_v = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_AUTH_TAG,
+			      &peer_v_len);
+	if (!peer_v || peer_v_len != curve->hash_len ||
+	    os_memcmp(peer_v, v, curve->hash_len) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: No valid v (R-Auth tag) found");
+		wpa_hexdump(MSG_DEBUG, "DPP: Calculated v'",
+			    v, curve->hash_len);
+		wpa_hexdump(MSG_DEBUG, "DPP: Received v", peer_v, peer_v_len);
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Valid v (R-Auth tag) received");
+
+	ret = 0;
+out:
+	wpabuf_free(B_pub);
+	wpabuf_free(X_pub);
+	wpabuf_free(Y_pub);
+	EVP_PKEY_CTX_free(ctx);
+	os_free(unwrapped);
+	return ret;
+fail:
+	goto out;
+}
+
+
+void dpp_pkex_free(struct dpp_pkex *pkex)
+{
+	if (!pkex)
+		return;
+
+	os_free(pkex->identifier);
+	os_free(pkex->code);
+	EVP_PKEY_free(pkex->x);
+	EVP_PKEY_free(pkex->y);
+	EVP_PKEY_free(pkex->peer_bootstrap_key);
+	wpabuf_free(pkex->exchange_req);
+	wpabuf_free(pkex->exchange_resp);
+	os_free(pkex);
+}
diff --git a/src/common/dpp.h b/src/common/dpp.h
new file mode 100644
index 0000000..dfee499
--- /dev/null
+++ b/src/common/dpp.h
@@ -0,0 +1,277 @@
+/*
+ * DPP functionality shared between hostapd and wpa_supplicant
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DPP_H
+#define DPP_H
+
+#include <openssl/x509.h>
+
+#include "utils/list.h"
+#include "common/wpa_common.h"
+#include "crypto/sha256.h"
+
+#define DPP_HDR_LEN (4 + 2) /* OUI, OUI Type, Crypto Suite, DPP frame type */
+
+enum dpp_public_action_frame_type {
+	DPP_PA_AUTHENTICATION_REQ = 0,
+	DPP_PA_AUTHENTICATION_RESP = 1,
+	DPP_PA_AUTHENTICATION_CONF = 2,
+	DPP_PA_PEER_DISCOVERY_REQ = 5,
+	DPP_PA_PEER_DISCOVERY_RESP = 6,
+	DPP_PA_PKEX_EXCHANGE_REQ = 7,
+	DPP_PA_PKEX_EXCHANGE_RESP = 8,
+	DPP_PA_PKEX_COMMIT_REVEAL_REQ = 9,
+	DPP_PA_PKEX_COMMIT_REVEAL_RESP = 10,
+};
+
+enum dpp_attribute_id {
+	DPP_ATTR_STATUS = 0x1000,
+	DPP_ATTR_I_BOOTSTRAP_KEY_HASH = 0x1001,
+	DPP_ATTR_R_BOOTSTRAP_KEY_HASH = 0x1002,
+	DPP_ATTR_I_PROTOCOL_KEY = 0x1003,
+	DPP_ATTR_WRAPPED_DATA = 0x1004,
+	DPP_ATTR_I_NONCE = 0x1005,
+	DPP_ATTR_I_CAPABILITIES = 0x1006,
+	DPP_ATTR_R_NONCE = 0x1007,
+	DPP_ATTR_R_CAPABILITIES = 0x1008,
+	DPP_ATTR_R_PROTOCOL_KEY = 0x1009,
+	DPP_ATTR_I_AUTH_TAG = 0x100A,
+	DPP_ATTR_R_AUTH_TAG = 0x100B,
+	DPP_ATTR_CONFIG_OBJ = 0x100C,
+	DPP_ATTR_CONNECTOR = 0x100D,
+	DPP_ATTR_CONFIG_ATTR_OBJ = 0x100E,
+	DPP_ATTR_BOOTSTRAP_KEY = 0x100F,
+	DPP_ATTR_OWN_NET_NK_HASH = 0x1011,
+	DPP_ATTR_FINITE_CYCLIC_GROUP = 0x1012,
+	DPP_ATTR_ENCRYPTED_KEY = 0x1013,
+	DPP_ATTR_ENROLLEE_NONCE = 0x1014,
+	DPP_ATTR_CODE_IDENTIFIER = 0x1015,
+	DPP_ATTR_TRANSACTION_ID = 0x1016,
+};
+
+enum dpp_status_error {
+	DPP_STATUS_OK = 0,
+	DPP_STATUS_NOT_COMPATIBLE = 1,
+	DPP_STATUS_AUTH_FAILURE = 2,
+	DPP_STATUS_UNWRAP_FAILURE = 3,
+	DPP_STATUS_BAD_GROUP = 4,
+	DPP_STATUS_CONFIGURE_FAILURE = 5,
+	DPP_STATUS_RESPONSE_PENDING = 6,
+};
+
+#define DPP_CAPAB_ENROLLEE BIT(0)
+#define DPP_CAPAB_CONFIGURATOR BIT(1)
+#define DPP_CAPAB_ROLE_MASK (BIT(0) | BIT(1))
+
+#define DPP_BOOTSTRAP_MAX_FREQ 30
+#define DPP_MAX_NONCE_LEN 32
+#define DPP_MAX_HASH_LEN 64
+#define DPP_MAX_SHARED_SECRET_LEN 66
+
+struct dpp_curve_params {
+	const char *name;
+	size_t hash_len;
+	size_t aes_siv_key_len;
+	size_t nonce_len;
+	size_t prime_len;
+	const char *jwk_crv;
+	u16 ike_group;
+	const char *jws_alg;
+};
+
+enum dpp_bootstrap_type {
+	DPP_BOOTSTRAP_QR_CODE,
+	DPP_BOOTSTRAP_PKEX,
+};
+
+struct dpp_bootstrap_info {
+	struct dl_list list;
+	unsigned int id;
+	enum dpp_bootstrap_type type;
+	char *uri;
+	u8 mac_addr[ETH_ALEN];
+	char *info;
+	unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ];
+	unsigned int num_freq;
+	int own;
+	EVP_PKEY *pubkey;
+	u8 pubkey_hash[SHA256_MAC_LEN];
+	const struct dpp_curve_params *curve;
+};
+
+struct dpp_pkex {
+	unsigned int initiator:1;
+	unsigned int exchange_done:1;
+	struct dpp_bootstrap_info *own_bi;
+	u8 own_mac[ETH_ALEN];
+	u8 peer_mac[ETH_ALEN];
+	char *identifier;
+	char *code;
+	EVP_PKEY *x;
+	EVP_PKEY *y;
+	u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
+	u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
+	u8 z[DPP_MAX_HASH_LEN];
+	EVP_PKEY *peer_bootstrap_key;
+	struct wpabuf *exchange_req;
+	struct wpabuf *exchange_resp;
+};
+
+struct dpp_configuration {
+	u8 ssid[32];
+	size_t ssid_len;
+	int dpp; /* whether to use DPP or legacy configuration */
+
+	/* For DPP configuration (connector) */
+	os_time_t netaccesskey_expiry;
+
+	/* TODO: groups */
+
+	/* For legacy configuration */
+	char *passphrase;
+	u8 psk[32];
+};
+
+struct dpp_authentication {
+	void *msg_ctx;
+	const struct dpp_curve_params *curve;
+	struct dpp_bootstrap_info *peer_bi;
+	struct dpp_bootstrap_info *own_bi;
+	u8 waiting_pubkey_hash[SHA256_MAC_LEN];
+	int response_pending;
+	enum dpp_status_error auth_resp_status;
+	u8 peer_mac_addr[ETH_ALEN];
+	u8 i_nonce[DPP_MAX_NONCE_LEN];
+	u8 r_nonce[DPP_MAX_NONCE_LEN];
+	u8 e_nonce[DPP_MAX_NONCE_LEN];
+	u8 i_capab;
+	u8 r_capab;
+	EVP_PKEY *own_protocol_key;
+	EVP_PKEY *peer_protocol_key;
+	struct wpabuf *req_msg;
+	struct wpabuf *resp_msg;
+	unsigned int curr_freq;
+	size_t secret_len;
+	u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
+	u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
+	u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
+	u8 k1[DPP_MAX_HASH_LEN];
+	u8 k2[DPP_MAX_HASH_LEN];
+	u8 ke[DPP_MAX_HASH_LEN];
+	int initiator;
+	int configurator;
+	int remove_on_tx_status;
+	int auth_success;
+	struct wpabuf *conf_req;
+	struct dpp_configuration *conf_ap;
+	struct dpp_configuration *conf_sta;
+	struct dpp_configurator *conf;
+	char *connector; /* received signedConnector */
+	u8 ssid[SSID_MAX_LEN];
+	u8 ssid_len;
+	char passphrase[64];
+	u8 psk[PMK_LEN];
+	int psk_set;
+	struct wpabuf *net_access_key;
+	os_time_t net_access_key_expiry;
+	struct wpabuf *c_sign_key;
+#ifdef CONFIG_TESTING_OPTIONS
+	char *config_obj_override;
+	char *discovery_override;
+	char *groups_override;
+	unsigned int ignore_netaccesskey_mismatch:1;
+#endif /* CONFIG_TESTING_OPTIONS */
+};
+
+struct dpp_configurator {
+	struct dl_list list;
+	unsigned int id;
+	int own;
+	EVP_PKEY *csign;
+	char *kid;
+	const struct dpp_curve_params *curve;
+};
+
+struct dpp_introduction {
+	u8 pmkid[PMKID_LEN];
+	u8 pmk[PMK_LEN_MAX];
+	size_t pmk_len;
+};
+
+void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info);
+const char * dpp_bootstrap_type_txt(enum dpp_bootstrap_type type);
+int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi);
+int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi,
+			    const char *chan_list);
+int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac);
+int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info);
+struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri);
+char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
+		  const u8 *privkey, size_t privkey_len);
+struct dpp_authentication * dpp_auth_init(void *msg_ctx,
+					  struct dpp_bootstrap_info *peer_bi,
+					  struct dpp_bootstrap_info *own_bi,
+					  int configurator);
+struct dpp_authentication *
+dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
+		struct dpp_bootstrap_info *peer_bi,
+		struct dpp_bootstrap_info *own_bi,
+		unsigned int freq, const u8 *hdr, const u8 *attr_start,
+		const u8 *wrapped_data, u16 wrapped_data_len);
+struct wpabuf *
+dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
+		 const u8 *attr_start, size_t attr_len);
+struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
+				   const char *json);
+int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
+		     const u8 *attr_start, size_t attr_len);
+int dpp_notify_new_qr_code(struct dpp_authentication *auth,
+			   struct dpp_bootstrap_info *peer_bi);
+void dpp_configuration_free(struct dpp_configuration *conf);
+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);
+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);
+int dpp_check_attrs(const u8 *buf, size_t len);
+int dpp_key_expired(const char *timestamp, os_time_t *expiry);
+void dpp_configurator_free(struct dpp_configurator *conf);
+struct dpp_configurator *
+dpp_keygen_configurator(const char *curve, const u8 *privkey,
+			size_t privkey_len);
+int dpp_configurator_own_config(struct dpp_authentication *auth,
+				const char *curve);
+int dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
+		   const u8 *net_access_key, size_t net_access_key_len,
+		   const u8 *csign_key, size_t csign_key_len,
+		   const u8 *peer_connector, size_t peer_connector_len,
+		   os_time_t *expiry);
+struct dpp_pkex * dpp_pkex_init(struct dpp_bootstrap_info *bi,
+				const u8 *own_mac,
+				const char *identifier,
+				const char *code);
+struct dpp_pkex * dpp_pkex_rx_exchange_req(struct dpp_bootstrap_info *bi,
+					   const u8 *own_mac,
+					   const u8 *peer_mac,
+					   const char *identifier,
+					   const char *code,
+					   const u8 *buf, size_t len);
+struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
+					  const u8 *buf, size_t len);
+struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
+					      const u8 *hdr,
+					      const u8 *buf, size_t len);
+int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr,
+				   const u8 *buf, size_t len);
+void dpp_pkex_free(struct dpp_pkex *pkex);
+
+#endif /* DPP_H */
diff --git a/src/common/gas.c b/src/common/gas.c
index cff9254..ba21b22 100644
--- a/src/common/gas.c
+++ b/src/common/gas.c
@@ -75,7 +75,7 @@
 }
 
 
-static struct wpabuf *
+struct wpabuf *
 gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more,
 			u16 comeback_delay, size_t size)
 {
diff --git a/src/common/gas.h b/src/common/gas.h
index 306adc5..4c93e31 100644
--- a/src/common/gas.h
+++ b/src/common/gas.h
@@ -14,6 +14,9 @@
 struct wpabuf * gas_build_comeback_req(u8 dialog_token);
 struct wpabuf * gas_build_initial_resp(u8 dialog_token, u16 status_code,
 				       u16 comeback_delay, size_t size);
+struct wpabuf *
+gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more,
+			u16 comeback_delay, size_t size);
 struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size);
 struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code,
 					    u16 comeback_delay, size_t size);
diff --git a/src/common/gas_server.c b/src/common/gas_server.c
new file mode 100644
index 0000000..b258675
--- /dev/null
+++ b/src/common/gas_server.c
@@ -0,0 +1,483 @@
+/*
+ * Generic advertisement service (GAS) server
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "utils/common.h"
+#include "utils/list.h"
+#include "utils/eloop.h"
+#include "ieee802_11_defs.h"
+#include "gas.h"
+#include "gas_server.h"
+
+
+#define MAX_ADV_PROTO_ID_LEN 10
+#define GAS_QUERY_TIMEOUT 10
+
+struct gas_server_handler {
+	struct dl_list list;
+	u8 adv_proto_id[MAX_ADV_PROTO_ID_LEN];
+	u8 adv_proto_id_len;
+	struct wpabuf * (*req_cb)(void *ctx, const u8 *sa,
+				  const u8 *query, size_t query_len);
+	void (*status_cb)(void *ctx, struct wpabuf *resp, int ok);
+	void *ctx;
+	struct gas_server *gas;
+};
+
+struct gas_server_response {
+	struct dl_list list;
+	size_t offset;
+	u8 frag_id;
+	struct wpabuf *resp;
+	int freq;
+	u8 dst[ETH_ALEN];
+	u8 dialog_token;
+	struct gas_server_handler *handler;
+};
+
+struct gas_server {
+	struct dl_list handlers; /* struct gas_server_handler::list */
+	struct dl_list responses; /* struct gas_server_response::list */
+	void (*tx)(void *ctx, int freq, const u8 *da, struct wpabuf *resp,
+		   unsigned int wait_time);
+	void *ctx;
+};
+
+static void gas_server_free_response(struct gas_server_response *response);
+
+
+static void gas_server_response_timeout(void *eloop_ctx, void *user_ctx)
+{
+	struct gas_server_response *response = eloop_ctx;
+
+	wpa_printf(MSG_DEBUG, "GAS: Response @%p timeout for " MACSTR
+		   " (dialog_token=%u freq=%d frag_id=%u sent=%lu/%lu) - drop pending data",
+		   response, MAC2STR(response->dst), response->dialog_token,
+		   response->freq, response->frag_id,
+		   (unsigned long) response->offset,
+		   (unsigned long) wpabuf_len(response->resp));
+	response->handler->status_cb(response->handler->ctx,
+				     response->resp, 0);
+	response->resp = NULL;
+	dl_list_del(&response->list);
+	gas_server_free_response(response);
+}
+
+
+static void gas_server_free_response(struct gas_server_response *response)
+{
+	if (!response)
+		return;
+	wpa_printf(MSG_DEBUG, "DPP: Free GAS response @%p", response);
+	eloop_cancel_timeout(gas_server_response_timeout, response, NULL);
+	wpabuf_free(response->resp);
+	os_free(response);
+}
+
+
+static void
+gas_server_send_resp(struct gas_server *gas, struct gas_server_handler *handler,
+		     const u8 *da, int freq, u8 dialog_token,
+		     struct wpabuf *query_resp)
+{
+	size_t max_len = (freq > 56160) ? 928 : 1400;
+	size_t hdr_len = 24 + 2 + 5 + 3 + handler->adv_proto_id_len + 2;
+	size_t resp_frag_len;
+	struct wpabuf *resp;
+	u16 comeback_delay;
+	struct gas_server_response *response;
+
+	if (!query_resp)
+		return;
+
+	response = os_zalloc(sizeof(*response));
+	if (!response)
+		return;
+	wpa_printf(MSG_DEBUG, "DPP: Allocated GAS response @%p", response);
+	response->freq = freq;
+	response->handler = handler;
+	os_memcpy(response->dst, da, ETH_ALEN);
+	response->dialog_token = dialog_token;
+	if (hdr_len + wpabuf_len(query_resp) > max_len) {
+		/* Need to use comeback to initiate fragmentation */
+		comeback_delay = 1;
+		resp_frag_len = 0;
+	} else {
+		/* Full response fits into the initial response */
+		comeback_delay = 0;
+		resp_frag_len = wpabuf_len(query_resp);
+	}
+
+	resp = gas_build_initial_resp(dialog_token, WLAN_STATUS_SUCCESS,
+				      comeback_delay,
+				      handler->adv_proto_id_len +
+				      resp_frag_len);
+	if (!resp) {
+		gas_server_free_response(response);
+		return;
+	}
+
+	/* Advertisement Protocol element */
+	wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO);
+	wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */
+	wpabuf_put_u8(resp, 0x7f);
+	/* Advertisement Protocol ID */
+	wpabuf_put_data(resp, handler->adv_proto_id, handler->adv_proto_id_len);
+
+	/* Query Response Length */
+	wpabuf_put_le16(resp, resp_frag_len);
+	if (!comeback_delay)
+		wpabuf_put_buf(resp, query_resp);
+
+	if (comeback_delay) {
+		wpa_printf(MSG_DEBUG,
+			   "GAS: Need to fragment query response");
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "GAS: Full query response fits in the GAS Initial Response frame");
+	}
+	response->offset = resp_frag_len;
+	response->resp = query_resp;
+	dl_list_add(&gas->responses, &response->list);
+	gas->tx(gas->ctx, freq, da, resp, comeback_delay ? 2000 : 0);
+	wpabuf_free(resp);
+	eloop_register_timeout(GAS_QUERY_TIMEOUT, 0,
+			       gas_server_response_timeout, response, NULL);
+}
+
+
+static int
+gas_server_rx_initial_req(struct gas_server *gas, const u8 *da, const u8 *sa,
+			  const u8 *bssid, int freq, u8 dialog_token,
+			  const u8 *data, size_t len)
+{
+	const u8 *pos, *end, *adv_proto, *query_req;
+	u8 adv_proto_len;
+	u16 query_req_len;
+	struct gas_server_handler *handler;
+	struct wpabuf *resp;
+
+	wpa_hexdump(MSG_MSGDUMP, "GAS: Received GAS Initial Request frame",
+		    data, len);
+	pos = data;
+	end = data + len;
+
+	if (end - pos < 2 || pos[0] != WLAN_EID_ADV_PROTO) {
+		wpa_printf(MSG_DEBUG,
+			   "GAS: No Advertisement Protocol element found");
+		return -1;
+	}
+	pos++;
+	adv_proto_len = *pos++;
+	if (end - pos < adv_proto_len || adv_proto_len < 2) {
+		wpa_printf(MSG_DEBUG,
+			   "GAS: Truncated Advertisement Protocol element");
+		return -1;
+	}
+
+	adv_proto = pos;
+	pos += adv_proto_len;
+	wpa_hexdump(MSG_MSGDUMP, "GAS: Advertisement Protocol element",
+		    adv_proto, adv_proto_len);
+
+	if (end - pos < 2) {
+		wpa_printf(MSG_DEBUG, "GAS: No Query Request Length field");
+		return -1;
+	}
+	query_req_len = WPA_GET_LE16(pos);
+	pos += 2;
+	if (end - pos < query_req_len) {
+		wpa_printf(MSG_DEBUG, "GAS: Truncated Query Request field");
+		return -1;
+	}
+	query_req = pos;
+	pos += query_req_len;
+	wpa_hexdump(MSG_MSGDUMP, "GAS: Query Request",
+		    query_req, query_req_len);
+
+	if (pos < end) {
+		wpa_hexdump(MSG_MSGDUMP,
+			    "GAS: Ignored extra data after Query Request field",
+			    pos, end - pos);
+	}
+
+	dl_list_for_each(handler, &gas->handlers, struct gas_server_handler,
+			 list) {
+		if (adv_proto_len < 1 + handler->adv_proto_id_len ||
+		    os_memcmp(adv_proto + 1, handler->adv_proto_id,
+			      handler->adv_proto_id_len) != 0)
+			continue;
+
+		wpa_printf(MSG_DEBUG,
+			   "GAS: Calling handler for the requested Advertisement Protocol ID");
+		resp = handler->req_cb(handler->ctx, sa, query_req,
+				       query_req_len);
+		wpa_hexdump_buf(MSG_MSGDUMP, "GAS: Response from the handler",
+				resp);
+		gas_server_send_resp(gas, handler, sa, freq, dialog_token,
+				     resp);
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "GAS: No registered handler for the requested Advertisement Protocol ID");
+	return -1;
+}
+
+
+static void
+gas_server_handle_rx_comeback_req(struct gas_server_response *response)
+{
+	struct gas_server_handler *handler = response->handler;
+	struct gas_server *gas = handler->gas;
+	size_t max_len = (response->freq > 56160) ? 928 : 1400;
+	size_t hdr_len = 24 + 2 + 6 + 3 + handler->adv_proto_id_len + 2;
+	size_t remaining, resp_frag_len;
+	struct wpabuf *resp;
+
+	remaining = wpabuf_len(response->resp) - response->offset;
+	if (hdr_len + remaining > max_len)
+		resp_frag_len = max_len - hdr_len;
+	else
+		resp_frag_len = remaining;
+	wpa_printf(MSG_DEBUG,
+		   "GAS: Sending out %u/%u remaining Query Response octets",
+		   (unsigned int) resp_frag_len, (unsigned int) remaining);
+
+	resp = gas_build_comeback_resp(response->dialog_token,
+				       WLAN_STATUS_SUCCESS,
+				       response->frag_id++,
+				       resp_frag_len < remaining, 0,
+				       handler->adv_proto_id_len +
+				       resp_frag_len);
+	if (!resp) {
+		gas_server_free_response(response);
+		return;
+	}
+
+	/* Advertisement Protocol element */
+	wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO);
+	wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */
+	wpabuf_put_u8(resp, 0x7f);
+	/* Advertisement Protocol ID */
+	wpabuf_put_data(resp, handler->adv_proto_id, handler->adv_proto_id_len);
+
+	/* Query Response Length */
+	wpabuf_put_le16(resp, resp_frag_len);
+	wpabuf_put_data(resp, wpabuf_head_u8(response->resp) + response->offset,
+			resp_frag_len);
+
+	response->offset += resp_frag_len;
+
+	gas->tx(gas->ctx, response->freq, response->dst, resp,
+		remaining > resp_frag_len ? 2000 : 0);
+	wpabuf_free(resp);
+}
+
+
+static int
+gas_server_rx_comeback_req(struct gas_server *gas, const u8 *da, const u8 *sa,
+			   const u8 *bssid, int freq, u8 dialog_token)
+{
+	struct gas_server_response *response;
+
+	dl_list_for_each(response, &gas->responses, struct gas_server_response,
+			 list) {
+		if (response->dialog_token != dialog_token ||
+		    os_memcmp(sa, response->dst, ETH_ALEN) != 0)
+			continue;
+		gas_server_handle_rx_comeback_req(response);
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "GAS: No pending GAS response for " MACSTR
+		   " (dialog token %u)", MAC2STR(sa), dialog_token);
+	return -1;
+}
+
+
+/**
+ * gas_query_rx - Indicate reception of a Public Action or Protected Dual frame
+ * @gas: GAS query data from gas_server_init()
+ * @da: Destination MAC address of the Action frame
+ * @sa: Source MAC address of the Action frame
+ * @bssid: BSSID of the Action frame
+ * @categ: Category of the Action frame
+ * @data: Payload of the Action frame
+ * @len: Length of @data
+ * @freq: Frequency (in MHz) on which the frame was received
+ * Returns: 0 if the Public Action frame was a GAS request frame or -1 if not
+ */
+int gas_server_rx(struct gas_server *gas, const u8 *da, const u8 *sa,
+		  const u8 *bssid, u8 categ, const u8 *data, size_t len,
+		  int freq)
+{
+	u8 action, dialog_token;
+	const u8 *pos, *end;
+
+	if (!gas || len < 2)
+		return -1;
+
+	if (categ == WLAN_ACTION_PROTECTED_DUAL)
+		return -1; /* Not supported for now */
+
+	pos = data;
+	end = data + len;
+	action = *pos++;
+	dialog_token = *pos++;
+
+	if (action != WLAN_PA_GAS_INITIAL_REQ &&
+	    action != WLAN_PA_GAS_COMEBACK_REQ)
+		return -1; /* Not a GAS request */
+
+	wpa_printf(MSG_DEBUG, "GAS: Received GAS %s Request frame DA=" MACSTR
+		   " SA=" MACSTR " BSSID=" MACSTR
+		   " freq=%d dialog_token=%u len=%u",
+		   action == WLAN_PA_GAS_INITIAL_REQ ? "Initial" : "Comeback",
+		   MAC2STR(da), MAC2STR(sa), MAC2STR(bssid), freq, dialog_token,
+		   (unsigned int) len);
+
+	if (action == WLAN_PA_GAS_INITIAL_REQ)
+		return gas_server_rx_initial_req(gas, da, sa, bssid,
+						 freq, dialog_token,
+						 pos, end - pos);
+	return gas_server_rx_comeback_req(gas, da, sa, bssid,
+					  freq, dialog_token);
+}
+
+
+static void gas_server_handle_tx_status(struct gas_server_response *response,
+					int ack)
+{
+	if (ack && response->offset < wpabuf_len(response->resp)) {
+		wpa_printf(MSG_DEBUG,
+			   "GAS: More fragments remaining - keep pending entry");
+		return;
+	}
+
+	if (!ack)
+		wpa_printf(MSG_DEBUG,
+			   "GAS: No ACK received - drop pending entry");
+	else
+		wpa_printf(MSG_DEBUG,
+			   "GAS: Last fragment of the response sent out - drop pending entry");
+
+	response->handler->status_cb(response->handler->ctx,
+				     response->resp, ack);
+	response->resp = NULL;
+	dl_list_del(&response->list);
+	gas_server_free_response(response);
+}
+
+
+void gas_server_tx_status(struct gas_server *gas, const u8 *dst, const u8 *data,
+			  size_t data_len, int ack)
+{
+	const u8 *pos;
+	u8 action, code, dialog_token;
+	struct gas_server_response *response;
+
+	if (data_len < 24 + 3)
+		return;
+	pos = data + 24;
+	action = *pos++;
+	code = *pos++;
+	dialog_token = *pos++;
+	if (action != WLAN_ACTION_PUBLIC ||
+	    (code != WLAN_PA_GAS_INITIAL_RESP &&
+	     code != WLAN_PA_GAS_COMEBACK_RESP))
+		return;
+	wpa_printf(MSG_DEBUG, "GAS: TX status dst=" MACSTR
+		   " ack=%d %s dialog_token=%u",
+		   MAC2STR(dst), ack,
+		   code == WLAN_PA_GAS_INITIAL_RESP ? "initial" : "comeback",
+		   dialog_token);
+	dl_list_for_each(response, &gas->responses, struct gas_server_response,
+			 list) {
+		if (response->dialog_token != dialog_token ||
+		    os_memcmp(dst, response->dst, ETH_ALEN) != 0)
+			continue;
+		gas_server_handle_tx_status(response, ack);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "GAS: No pending response matches TX status");
+}
+
+
+struct gas_server * gas_server_init(void *ctx,
+				    void (*tx)(void *ctx, int freq,
+					       const u8 *da,
+					       struct wpabuf *buf,
+					       unsigned int wait_time))
+{
+	struct gas_server *gas;
+
+	gas = os_zalloc(sizeof(*gas));
+	if (!gas)
+		return NULL;
+	gas->ctx = ctx;
+	gas->tx = tx;
+	dl_list_init(&gas->handlers);
+	dl_list_init(&gas->responses);
+	return gas;
+}
+
+
+void gas_server_deinit(struct gas_server *gas)
+{
+	struct gas_server_handler *handler, *tmp;
+	struct gas_server_response *response, *tmp_r;
+
+	if (!gas)
+		return;
+
+	dl_list_for_each_safe(handler, tmp, &gas->handlers,
+			      struct gas_server_handler, list) {
+		dl_list_del(&handler->list);
+		os_free(handler);
+	}
+
+	dl_list_for_each_safe(response, tmp_r, &gas->responses,
+			      struct gas_server_response, list) {
+		dl_list_del(&response->list);
+		gas_server_free_response(response);
+	}
+
+	os_free(gas);
+}
+
+
+int gas_server_register(struct gas_server *gas,
+			const u8 *adv_proto_id, u8 adv_proto_id_len,
+			struct wpabuf *
+			(*req_cb)(void *ctx, const u8 *sa,
+				  const u8 *query, size_t query_len),
+			void (*status_cb)(void *ctx, struct wpabuf *resp,
+					  int ok),
+			void *ctx)
+{
+	struct gas_server_handler *handler;
+
+	if (!gas || adv_proto_id_len > MAX_ADV_PROTO_ID_LEN)
+		return -1;
+	handler = os_zalloc(sizeof(*handler));
+	if (!handler)
+		return -1;
+
+	os_memcpy(handler->adv_proto_id, adv_proto_id, adv_proto_id_len);
+	handler->adv_proto_id_len = adv_proto_id_len;
+	handler->req_cb = req_cb;
+	handler->status_cb = status_cb;
+	handler->ctx = ctx;
+	handler->gas = gas;
+	dl_list_add(&gas->handlers, &handler->list);
+
+	return 0;
+}
diff --git a/src/common/gas_server.h b/src/common/gas_server.h
new file mode 100644
index 0000000..299f529
--- /dev/null
+++ b/src/common/gas_server.h
@@ -0,0 +1,44 @@
+/*
+ * Generic advertisement service (GAS) server
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef GAS_SERVER_H
+#define GAS_SERVER_H
+
+#ifdef CONFIG_GAS_SERVER
+
+struct gas_server;
+
+struct gas_server * gas_server_init(void *ctx,
+				    void (*tx)(void *ctx, int freq,
+					       const u8 *da,
+					       struct wpabuf *buf,
+					       unsigned int wait_time));
+void gas_server_deinit(struct gas_server *gas);
+int gas_server_register(struct gas_server *gas,
+			const u8 *adv_proto_id, u8 adv_proto_id_len,
+			struct wpabuf *
+			(*req_cb)(void *ctx, const u8 *sa,
+				  const u8 *query, size_t query_len),
+			void (*status_cb)(void *ctx, struct wpabuf *resp,
+					  int ok),
+			void *ctx);
+int gas_server_rx(struct gas_server *gas, const u8 *da, const u8 *sa,
+		  const u8 *bssid, u8 categ, const u8 *data, size_t len,
+		  int freq);
+void gas_server_tx_status(struct gas_server *gas, const u8 *dst, const u8 *data,
+			  size_t data_len, int ack);
+
+#else /* CONFIG_GAS_SERVER */
+
+static inline void gas_server_deinit(struct gas_server *gas)
+{
+}
+
+#endif /* CONFIG_GAS_SERVER */
+
+#endif /* GAS_SERVER_H */
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index 7a96f96..fdd5e73 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -526,8 +526,7 @@
 	} while (0)
 
 	VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK);
-	VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ);
-	VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ);
+	VHT_CAP_CHECK_MAX(VHT_CAP_SUPP_CHAN_WIDTH_MASK);
 	VHT_CAP_CHECK(VHT_CAP_RXLDPC);
 	VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80);
 	VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160);
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index a8d68e5..120d4e8 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -250,6 +250,12 @@
 			break;
 		elems->fils_nonce = pos;
 		break;
+	case WLAN_EID_EXT_OWE_DH_PARAM:
+		if (elen < 2)
+			break;
+		elems->owe_dh = pos;
+		elems->owe_dh_len = elen;
+		break;
 	default:
 		if (show_errors) {
 			wpa_printf(MSG_MSGDUMP,
@@ -1113,7 +1119,7 @@
 			return -1;
 		return 5000 + 5 * chan;
 	case 129: /* center freqs 50, 114; 160 MHz */
-		if (chan < 50 || chan > 114)
+		if (chan < 36 || chan > 128)
 			return -1;
 		return 5000 + 5 * chan;
 	case 180: /* 60 GHz band, channels 1..4 */
@@ -1427,6 +1433,40 @@
 }
 
 
+/**
+ * get_ie_ext - Fetch a specified extended information element from IEs buffer
+ * @ies: Information elements buffer
+ * @len: Information elements buffer length
+ * @ext: Information element extension identifier (WLAN_EID_EXT_*)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the first matching information element in the IEs
+ * buffer or %NULL in case the element is not found.
+ */
+const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext)
+{
+	const u8 *end;
+
+	if (!ies)
+		return NULL;
+
+	end = ies + len;
+
+	while (end - ies > 1) {
+		if (2 + ies[1] > end - ies)
+			break;
+
+		if (ies[0] == WLAN_EID_EXTENSION && ies[1] >= 1 &&
+		    ies[2] == ext)
+			return ies;
+
+		ies += 2 + ies[1];
+	}
+
+	return NULL;
+}
+
+
 size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len)
 {
 	/*
@@ -1594,3 +1634,105 @@
 
 	return op;
 }
+
+
+int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
+				    size_t nei_rep_len)
+{
+	u8 *nei_pos = nei_rep;
+	const char *end;
+
+	/*
+	 * BSS Transition Candidate List Entries - Neighbor Report elements
+	 * neighbor=<BSSID>,<BSSID Information>,<Operating Class>,
+	 * <Channel Number>,<PHY Type>[,<hexdump of Optional Subelements>]
+	 */
+	while (pos) {
+		u8 *nei_start;
+		long int val;
+		char *endptr, *tmp;
+
+		pos = os_strstr(pos, " neighbor=");
+		if (!pos)
+			break;
+		if (nei_pos + 15 > nei_rep + nei_rep_len) {
+			wpa_printf(MSG_DEBUG,
+				   "Not enough room for additional neighbor");
+			return -1;
+		}
+		pos += 10;
+
+		nei_start = nei_pos;
+		*nei_pos++ = WLAN_EID_NEIGHBOR_REPORT;
+		nei_pos++; /* length to be filled in */
+
+		if (hwaddr_aton(pos, nei_pos)) {
+			wpa_printf(MSG_DEBUG, "Invalid BSSID");
+			return -1;
+		}
+		nei_pos += ETH_ALEN;
+		pos += 17;
+		if (*pos != ',') {
+			wpa_printf(MSG_DEBUG, "Missing BSSID Information");
+			return -1;
+		}
+		pos++;
+
+		val = strtol(pos, &endptr, 0);
+		WPA_PUT_LE32(nei_pos, val);
+		nei_pos += 4;
+		if (*endptr != ',') {
+			wpa_printf(MSG_DEBUG, "Missing Operating Class");
+			return -1;
+		}
+		pos = endptr + 1;
+
+		*nei_pos++ = atoi(pos); /* Operating Class */
+		pos = os_strchr(pos, ',');
+		if (pos == NULL) {
+			wpa_printf(MSG_DEBUG, "Missing Channel Number");
+			return -1;
+		}
+		pos++;
+
+		*nei_pos++ = atoi(pos); /* Channel Number */
+		pos = os_strchr(pos, ',');
+		if (pos == NULL) {
+			wpa_printf(MSG_DEBUG, "Missing PHY Type");
+			return -1;
+		}
+		pos++;
+
+		*nei_pos++ = atoi(pos); /* PHY Type */
+		end = os_strchr(pos, ' ');
+		tmp = os_strchr(pos, ',');
+		if (tmp && (!end || tmp < end)) {
+			/* Optional Subelements (hexdump) */
+			size_t len;
+
+			pos = tmp + 1;
+			end = os_strchr(pos, ' ');
+			if (end)
+				len = end - pos;
+			else
+				len = os_strlen(pos);
+			if (nei_pos + len / 2 > nei_rep + nei_rep_len) {
+				wpa_printf(MSG_DEBUG,
+					   "Not enough room for neighbor subelements");
+				return -1;
+			}
+			if (len & 0x01 ||
+			    hexstr2bin(pos, nei_pos, len / 2) < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "Invalid neighbor subelement info");
+				return -1;
+			}
+			nei_pos += len / 2;
+			pos = end;
+		}
+
+		nei_start[1] = nei_pos - nei_start - 2;
+	}
+
+	return nei_pos - nei_rep;
+}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 966eeac..9276158 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -78,6 +78,7 @@
 	const u8 *fils_wrapped_data;
 	const u8 *fils_pk;
 	const u8 *fils_nonce;
+	const u8 *owe_dh;
 
 	u8 ssid_len;
 	u8 supp_rates_len;
@@ -120,6 +121,7 @@
 	u8 key_delivery_len;
 	u8 fils_wrapped_data_len;
 	u8 fils_pk_len;
+	u8 owe_dh_len;
 
 	struct mb_ies_info mb_ies;
 };
@@ -174,6 +176,7 @@
 extern size_t global_op_class_size;
 
 const u8 * get_ie(const u8 *ies, size_t len, u8 eid);
+const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext);
 
 size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len);
 
@@ -186,4 +189,7 @@
 
 const struct oper_class_map * get_oper_class(const char *country, u8 op_class);
 
+int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
+				    size_t nei_rep_len);
+
 #endif /* IEEE802_11_COMMON_H */
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 15f6d42..b7fa563 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -462,6 +462,9 @@
 #define WLAN_EID_EXT_FILS_PUBLIC_KEY 12
 #define WLAN_EID_EXT_FILS_NONCE 13
 #define WLAN_EID_EXT_FUTURE_CHANNEL_GUIDANCE 14
+#define WLAN_EID_EXT_OWE_DH_PARAM 32
+#define WLAN_EID_EXT_HE_CAPABILITIES 35
+#define WLAN_EID_EXT_HE_OPERATION 36
 
 
 /* Action frame categories (IEEE Std 802.11-2016, 9.4.1.11, Table 9-76) */
@@ -1126,6 +1129,7 @@
 #define VHT_CAP_SUPP_CHAN_WIDTH_160MHZ              ((u32) BIT(2))
 #define VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ     ((u32) BIT(3))
 #define VHT_CAP_SUPP_CHAN_WIDTH_MASK                ((u32) BIT(2) | BIT(3))
+#define VHT_CAP_SUPP_CHAN_WIDTH_MASK_SHIFT          2
 #define VHT_CAP_RXLDPC                              ((u32) BIT(4))
 #define VHT_CAP_SHORT_GI_80                         ((u32) BIT(5))
 #define VHT_CAP_SHORT_GI_160                        ((u32) BIT(6))
@@ -1192,6 +1196,8 @@
 #define OSEN_IE_VENDOR_TYPE 0x506f9a12
 #define MBO_IE_VENDOR_TYPE 0x506f9a16
 #define MBO_OUI_TYPE 22
+#define OWE_IE_VENDOR_TYPE 0x506f9a1c
+#define OWE_OUI_TYPE 28
 
 #define WMM_OUI_TYPE 2
 #define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0
@@ -1336,6 +1342,7 @@
 
 /* MBO v0.0_r19, 4.2: MBO Attributes */
 /* Table 4-5: MBO Attributes */
+/* OCE v0.0.10, Table 4-3: OCE Attributes */
 enum mbo_attr_id {
 	MBO_ATTR_ID_AP_CAPA_IND = 1,
 	MBO_ATTR_ID_NON_PREF_CHAN_REPORT = 2,
@@ -1345,6 +1352,10 @@
 	MBO_ATTR_ID_TRANSITION_REASON = 6,
 	MBO_ATTR_ID_TRANSITION_REJECT_REASON = 7,
 	MBO_ATTR_ID_ASSOC_RETRY_DELAY = 8,
+	OCE_ATTR_ID_CAPA_IND = 101,
+	OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT = 102,
+	OCE_ATTR_ID_REDUCED_WAN_METRICS = 103,
+	OCE_ATTR_ID_RNR_COMPLETENESS = 104,
 };
 
 /* MBO v0.0_r19, 4.2.1: MBO AP Capability Indication Attribute */
@@ -1419,9 +1430,17 @@
 	WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA = 3,
 };
 
-/* MBO v0.0_r25, 4.3: MBO ANQP-elements */
+/* MBO v0.0_r27, 4.3: MBO ANQP-elements */
 #define MBO_ANQP_OUI_TYPE 0x12
-#define MBO_ANQP_SUBTYPE_CELL_CONN_PREF 1
+#define MBO_ANQP_SUBTYPE_QUERY_LIST 1
+#define MBO_ANQP_SUBTYPE_CELL_CONN_PREF 2
+#define MAX_MBO_ANQP_SUBTYPE MBO_ANQP_SUBTYPE_CELL_CONN_PREF
+
+/* OCE v0.0.10, 4.2.1: OCE Capability Indication Attribute */
+#define OCE_RELEASE 1
+#define OCE_RELEASE_MASK (BIT(0) | BIT(1) | BIT(2))
+#define OCE_IS_STA_CFON BIT(3)
+#define OCE_IS_NON_OCE_AP_PRESENT BIT(4)
 
 /* Wi-Fi Direct (P2P) */
 
@@ -1570,7 +1589,9 @@
 	WFD_SUBELEM_COUPLED_SINK = 6,
 	WFD_SUBELEM_EXT_CAPAB = 7,
 	WFD_SUBELEM_LOCAL_IP_ADDRESS = 8,
-	WFD_SUBELEM_SESSION_INFO = 9
+	WFD_SUBELEM_SESSION_INFO = 9,
+	WFD_SUBELEM_MAC_INFO = 10,
+	WFD_SUBELEM_R2_DEVICE_INFO = 11,
 };
 
 /* 802.11s */
@@ -1963,18 +1984,17 @@
 struct ieee80211_he_capabilities {
 	u8 he_mac_capab_info[5];
 	u8 he_phy_capab_info[9];
-	u16 he_txrx_mcs_support;
-	/* possibly followed by Tx Rx MCS NSS descriptor */
-	u8 variable[];
+	u8 he_txrx_mcs_support[12]; /* TODO: 4, 8, or 12 octets */
 	/* PPE Thresholds (optional) */
 } STRUCT_PACKED;
 
 struct ieee80211_he_operation {
 	u32 he_oper_params;
-	u8 he_mcs_nss_set[3];
+	u8 he_mcs_nss_set[2];
 	u8 vht_op_info_chwidth;
 	u8 vht_op_info_chan_center_freq_seg0_idx;
 	u8 vht_op_info_chan_center_freq_seg1_idx;
+	/* Followed by conditional MaxBSSID Indicator subfield (u8) */
 } STRUCT_PACKED;
 
 /* HE Capabilities Information defines */
@@ -2009,4 +2029,7 @@
 #define HE_OPERATION_BSS_COLOR_DISABLED		((u32) BIT(30))
 #define HE_OPERATION_BSS_DUAL_BEACON		((u32) BIT(31))
 
+/* DPP Public Action frame identifiers - OUI_WFA */
+#define DPP_OUI_TYPE 0x1A
+
 #endif /* IEEE802_11_DEFS_H */
diff --git a/src/common/privsep_commands.h b/src/common/privsep_commands.h
index 0f47518..b85c6c3 100644
--- a/src/common/privsep_commands.h
+++ b/src/common/privsep_commands.h
@@ -92,7 +92,6 @@
 	PRIVSEP_EVENT_MICHAEL_MIC_FAILURE,
 	PRIVSEP_EVENT_INTERFACE_STATUS,
 	PRIVSEP_EVENT_PMKID_CANDIDATE,
-	PRIVSEP_EVENT_STKSTART,
 	PRIVSEP_EVENT_FT_RESPONSE,
 	PRIVSEP_EVENT_RX_EAPOL,
 	PRIVSEP_EVENT_SCAN_STARTED,
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index 934f09d..b4f7d12 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -90,6 +90,27 @@
  *	which supports DFS offloading, to indicate a radar pattern has been
  *	detected. The channel is now unusable.
  *
+ * @QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG: Command used to set configuration
+ *	for IEEE 802.11 communicating outside the context of a basic service
+ *	set, called OCB command. Uses the attributes defines in
+ *	enum qca_wlan_vendor_attr_ocb_set_config.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME: Command used to set OCB
+ *	UTC time. Use the attributes defines in
+ *	enum qca_wlan_vendor_attr_ocb_set_utc_time.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_OCB_START_TIMING_ADVERT: Command used to start
+ *	sending OCB timing advert frames. Uses the attributes defines in
+ *	enum qca_wlan_vendor_attr_ocb_start_timing_advert.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_OCB_STOP_TIMING_ADVERT: Command used to stop
+ *	OCB timing advert. Uses the attributes defines in
+ *	enum qca_wlan_vendor_attr_ocb_stop_timing_advert.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_OCB_GET_TSF_TIMER: Command used to get TSF
+ *	timer value. Uses the attributes defines in
+ *	enum qca_wlan_vendor_attr_ocb_get_tsf_resp.
+ *
  * @QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START: Command used to
  *	start the P2P Listen offload function in device and pass the listen
  *	channel, period, interval, count, device types, and vendor specific
@@ -288,6 +309,35 @@
  * @QCA_NL80211_VENDOR_SUBCMD_SET_TRACE_LEVEL: Set the trace level for a
  *	specific QCA module. The trace levels are represented by
  *	enum qca_attr_trace_level attributes.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT: Set the Beam Refinement
+ *	Protocol antenna limit in different modes. See enum
+ *	qca_wlan_vendor_attr_brp_ant_limit_mode.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START: Start spectral scan. The scan
+ *	parameters are specified by enum qca_wlan_vendor_attr_spectral_scan.
+ *	This returns a cookie (%QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE)
+ *	identifying the operation in success case.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP: Stop spectral scan. This uses
+ *	a cookie (%QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE) from
+ *	@QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START to identify the scan to
+ *	be stopped.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS: Set the active Type Of Service on the
+ *	specific interface. This can be used to modify some of the low level
+ *	scan parameters (off channel dwell time, home channel time) in the
+ *	driver/firmware. These parameters are maintained within the host driver.
+ *	This command is valid only when the interface is in the connected state.
+ *	These scan parameters shall be reset by the driver/firmware once
+ *	disconnected. The attributes used with this command are defined in
+ *	enum qca_wlan_vendor_attr_active_tos.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_HANG: Event indicating to the user space that the
+ *	driver has detected an internal failure. This event carries the
+ *	information indicating the reason that triggered this detection. The
+ *	attributes for this command are defined in
+ *	enum qca_wlan_vendor_attr_hang.
  */
 enum qca_nl80211_vendor_subcmds {
 	QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -415,6 +465,11 @@
 	QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET = 150,
 	QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS = 151,
 	QCA_NL80211_VENDOR_SUBCMD_SET_TRACE_LEVEL = 152,
+	QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT = 153,
+	QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START = 154,
+	QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP = 155,
+	QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS = 156,
+	QCA_NL80211_VENDOR_SUBCMD_HANG = 157,
 };
 
 
@@ -573,6 +628,22 @@
 	 * driver.
 	 */
 	QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO = 37,
+	/* Used in QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT command
+	 * See enum qca_wlan_vendor_attr_brp_ant_limit_mode.
+	 */
+	QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE = 38,
+	/* Used in QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT command
+	 * to define the number of antennas to use for BRP.
+	 * different purpose in each ANT_LIMIT_MODE:
+	 * DISABLE - ignored
+	 * EFFECTIVE - upper limit to number of antennas to be used
+	 * FORCE - exact number of antennas to be used
+	 * unsigned 8 bit value
+	 */
+	QCA_WLAN_VENDOR_ATTR_BRP_ANT_NUM_LIMIT = 39,
+	/* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command
+	 * to report the corresponding antenna index to the chain RSSI value */
+	QCA_WLAN_VENDOR_ATTR_ANTENNA_INFO = 40,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
@@ -595,6 +666,38 @@
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK,
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK,
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS,
+	/* Indicates the status of re-association requested by user space for
+	 * the BSSID specified by QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID.
+	 * Type u16.
+	 * Represents the status code from AP. Use
+	 * %WLAN_STATUS_UNSPECIFIED_FAILURE if the device cannot give you the
+	 * real status code for failures.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_STATUS,
+	/* This attribute indicates that the old association was maintained when
+	 * a re-association is requested by user space and that re-association
+	 * attempt fails (i.e., cannot connect to the requested BSS, but can
+	 * remain associated with the BSS with which the association was in
+	 * place when being requested to roam). Used along with
+	 * WLAN_VENDOR_ATTR_ROAM_AUTH_STATUS to indicate the current
+	 * re-association status. Type flag.
+	 * This attribute is applicable only for re-association failure cases.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RETAIN_CONNECTION,
+	/* This attribute specifies the PMK if one was newly generated during
+	 * FILS roaming. This is added to the PMKSA cache and is used in
+	 * subsequent connections with PMKSA caching.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK = 11,
+	/* This attribute specifies the PMKID used/generated for the current
+	 * FILS roam. This is used in subsequent connections with PMKSA caching.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID = 12,
+	/* A 16-bit unsigned value specifying the next sequence number to use
+	 * in ERP message in the currently associated realm. This is used in
+	 * doing subsequent ERP based connections in the same realm.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM = 13,
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX =
@@ -682,6 +785,15 @@
  *	Devices whilst in Listen state, rather than having the user space
  *	wpa_supplicant do it. Information from received P2P requests are
  *	forwarded from firmware to host whenever the host processor wakes up.
+ * @QCA_WLAN_VENDOR_FEATURE_OCE_STA: Device supports all OCE non-AP STA
+ *	specific features.
+ * @QCA_WLAN_VENDOR_FEATURE_OCE_AP: Device supports all OCE AP specific
+ *	features.
+ * @QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON: Device supports OCE STA-CFON
+ *	specific features only. If a Device sets this bit but not the
+ *	%QCA_WLAN_VENDOR_FEATURE_OCE_AP, the userspace shall assume that
+ *	this Device may not support all OCE AP functionalities but can support
+ *	only OCE STA-CFON functionalities.
  * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
  */
 enum qca_wlan_vendor_features {
@@ -689,6 +801,9 @@
 	QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY     = 1,
 	QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS = 2,
 	QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD	= 3,
+	QCA_WLAN_VENDOR_FEATURE_OCE_STA                 = 4,
+	QCA_WLAN_VENDOR_FEATURE_OCE_AP                  = 5,
+	QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON            = 6,
 	NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
 };
 
@@ -714,6 +829,112 @@
 	QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_AFTER_LAST - 1
 };
 
+/**
+ * enum qca_wlan_vendor_attr_ocb_set_config - Vendor subcmd attributes to set
+ *	OCB config
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT: Number of channels in the
+ *	configuration
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE: Size of the schedule
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY: Array of channels
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY: Array of channels to be
+ *	scheduled
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY: Array of NDL channel
+ *	information
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY: Array of NDL
+ *	active state configuration
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS: Configuration flags such as
+ *	OCB_CONFIG_FLAG_80211_FRAME_MODE
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_DEF_TX_PARAM: Default TX parameters to
+ *	use in the case that a packet is sent without a TX control header
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_TA_MAX_DURATION: Max duration after the
+ *	last TA received that the local time set by TA is synchronous to other
+ *	communicating OCB STAs.
+ */
+enum qca_wlan_vendor_attr_ocb_set_config {
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT = 1,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE = 2,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY = 3,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY = 4,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY = 5,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY = 6,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS = 7,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_DEF_TX_PARAM = 8,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_TA_MAX_DURATION = 9,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_MAX =
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ocb_set_utc_time - Vendor subcmd attributes to set
+ *	UTC time
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE: The UTC time as an array of
+ *	10 bytes
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR: The time error as an array of
+ *	5 bytes
+ */
+enum qca_wlan_vendor_attr_ocb_set_utc_time {
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE = 1,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR = 2,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_MAX =
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ocb_start_timing_advert - Vendor subcmd attributes
+ *	to start sending timing advert frames
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ: Cannel frequency
+ *	on which to send the frames
+ * @QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE: Number of times
+ *	the frame is sent in 5 seconds
+ */
+enum qca_wlan_vendor_attr_ocb_start_timing_advert {
+	QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ = 1,
+	QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE = 2,
+	QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_MAX =
+	QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ocb_stop_timing_advert - Vendor subcmd attributes
+ *	to stop timing advert
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ: The channel
+ *	frequency on which to stop the timing advert
+ */
+enum qca_wlan_vendor_attr_ocb_stop_timing_advert {
+	QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ = 1,
+	QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_MAX =
+	QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ocb_get_tsf_response - Vendor subcmd attributes to
+ *	get TSF timer value
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH: Higher 32 bits of the
+ *	timer
+ * @QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW: Lower 32 bits of the timer
+ */
+enum qca_wlan_vendor_attr_ocb_get_tsf_resp {
+	QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH = 1,
+	QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW = 2,
+	QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_MAX =
+	QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_AFTER_LAST - 1
+};
+
 enum qca_vendor_attr_get_preferred_freq_list {
 	QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_INVALID,
 	/* A 32-unsigned value; the interface type/mode for which the preferred
@@ -1133,6 +1354,97 @@
 	QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_24 = 37,
 	/* 8-bit unsigned value to set the beacon miss threshold in 5 GHz */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_5 = 38,
+	/* 32-bit unsigned value to configure 5 or 10 MHz channel width for
+	 * station device while in disconnect state. The attribute use the
+	 * value of enum nl80211_chan_width: NL80211_CHAN_WIDTH_5 means 5 MHz,
+	 * NL80211_CHAN_WIDTH_10 means 10 MHz. If set, the device work in 5 or
+	 * 10 MHz channel width, the station will not connect to a BSS using 20
+	 * MHz or higher bandwidth. Set to NL80211_CHAN_WIDTH_20_NOHT to
+	 * clear this constraint. */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_SUB20_CHAN_WIDTH = 39,
+	/* 32-bit unsigned value to configure the propagation absolute delay
+	 * for 2G/5G band (units in us) */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_ABS_DELAY = 40,
+	/* 32-bit unsigned value to set probe period */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_PERIOD = 41,
+	/* 32-bit unsigned value to set stay period */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_STAY_PERIOD = 42,
+	/* 32-bit unsigned value to set snr diff */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SNR_DIFF = 43,
+	/* 32-bit unsigned value to set probe dwell time */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_DWELL_TIME = 44,
+	/* 32-bit unsigned value to set mgmt snr weight */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_MGMT_SNR_WEIGHT = 45,
+	/* 32-bit unsigned value to set data snr weight */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_DATA_SNR_WEIGHT = 46,
+	/* 32-bit unsigned value to set ack snr weight */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ACK_SNR_WEIGHT = 47,
+	/* 32-bit unsigned value to configure the listen interval.
+	 * This is in units of beacon intervals. This configuration alters
+	 * the negotiated listen interval with the AP during the connection.
+	 * It is highly recommended to configure a value less than or equal to
+	 * the one negotiated during the association. Configuring any greater
+	 * value can have adverse effects (frame loss, AP disassociating STA,
+	 * etc.).
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_LISTEN_INTERVAL = 48,
+	/*
+	 * 8 bit unsigned value that is set on an AP/GO virtual interface to
+	 * disable operations that would cause the AP/GO to leave its operating
+	 * channel.
+	 *
+	 * This will restrict the scans to the AP/GO operating channel and the
+	 * channels of the other band, if DBS is supported.A STA/CLI interface
+	 * brought up after this setting is enabled, will be restricted to
+	 * connecting to devices only on the AP/GO interface's operating channel
+	 * or on the other band in DBS case. P2P supported channel list is
+	 * modified, to only include AP interface's operating-channel and the
+	 * channels of the other band if DBS is supported.
+	 *
+	 * These restrictions are only applicable as long as the AP/GO interface
+	 * is alive. If the AP/GO interface is brought down then this
+	 * setting/restriction is forgotten.
+	 *
+	 * If this variable is set on an AP/GO interface while a multi-channel
+	 * concurrent session is active, it has no effect on the operation of
+	 * the current interfaces, other than restricting the scan to the AP/GO
+	 * operating channel and the other band channels if DBS is supported.
+	 * However, if the STA is brought down and restarted then the new STA
+	 * connection will either be formed on the AP/GO channel or on the
+	 * other band in a DBS case. This is because of the scan being
+	 * restricted on these channels as mentioned above.
+	 *
+	 * 1-Restrict / 0-Don't restrict offchannel operations.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_RESTRICT_OFFCHANNEL = 49,
+	/*
+	 * 8 bit unsigned value to enable/disable LRO (Large Receive Offload)
+	 * on an interface.
+	 * 1 - Enable, 0 - Disable.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_LRO = 50,
+
+	/*
+	 * 8 bit unsigned value to globally enable/disable scan
+	 * 1 - Enable, 0 - Disable.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_ENABLE = 51,
+
+	/* 8-bit unsigned value to set the total beacon miss count
+	 * This paramater will set the total beacon miss count.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TOTAL_BEACON_MISS_COUNT = 52,
+
+	/* Unsigned 32-bit value to configure the number of continuous
+	 * Beacon Miss which shall be used by the firmware to penalize
+	 * the RSSI for BTC.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_PENALIZE_AFTER_NCONS_BEACON_MISS_BTC = 53,
+
+	/* 8-bit unsigned value to configure the driver and below layers to
+	 * enable/disable all FILS features.
+	 * 0-enable, 1-disable */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_DISABLE_FILS = 54,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST,
@@ -1835,6 +2147,24 @@
 };
 
 /**
+ * BRP antenna limit mode
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_DISABLE: Disable BRP force
+ *	antenna limit, BRP will be performed as usual.
+ * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_EFFECTIVE: Define maximal
+ *	antennas limit. the hardware may use less antennas than the
+ *	maximum limit.
+ * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_FORCE: The hardware will
+ *	use exactly the specified number of antennas for BRP.
+ */
+enum qca_wlan_vendor_attr_brp_ant_limit_mode {
+	QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_DISABLE,
+	QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_EFFECTIVE,
+	QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_FORCE,
+	QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_MAX
+};
+
+/**
  * enum qca_wlan_vendor_attr_dmg_rf_sector_cfg - Attributes for
  * DMG RF sector configuration for a single RF module.
  * The values are defined in a compact way which closely matches
@@ -2389,7 +2719,7 @@
 /**
  * enum qca_vendor_attr_sar_limits - Attributes for SAR power limits
  *
- * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT: Optional (u32) value to
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE: Optional (u32) value to
  *	select which SAR power limit table should be used. Valid
  *	values are enumerated in enum
  *	%qca_vendor_attr_sar_limits_selections. The existing SAR
@@ -3000,6 +3330,8 @@
 	QCA_WLAN_VENDOR_ATTR_EPNO_SAME_NETWORK_BONUS = 20,
 	QCA_WLAN_VENDOR_ATTR_EPNO_SECURE_BONUS = 21,
 	QCA_WLAN_VENDOR_ATTR_EPNO_BAND5GHZ_BONUS = 22,
+	/* Unsigned 32-bit value, representing the PNO Request ID */
+	QCA_WLAN_VENDOR_ATTR_PNO_CONFIG_REQUEST_ID = 23,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_PNO_AFTER_LAST,
@@ -3023,6 +3355,24 @@
 };
 
 /**
+ * qca_wlan_vendor_attr_external_acs_policy: Attribute values for
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_POLICY to the vendor subcmd
+ * QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS. This represents the
+ * external ACS policies to select the channels w.r.t. the PCL weights.
+ * (QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL represents the channels and
+ * their PCL weights.)
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_MANDATORY: Mandatory to
+ * select a channel with non-zero PCL weight.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_PREFERRED: Prefer a
+ * channel with non-zero PCL weight.
+ *
+ */
+enum qca_wlan_vendor_attr_external_acs_policy {
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_PREFERRED,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_MANDATORY,
+};
+
+/**
  * qca_wlan_vendor_channel_prop_flags: This represent the flags for a channel.
  * This is used by QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS.
  */
@@ -3077,6 +3427,30 @@
 	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT160        = 1 << 26,
 	/* VHT 80+80 channel */
 	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT80_80      = 1 << 27,
+	/* HE 20 channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE20          = 1 << 28,
+	/* HE 40 with extension channel above */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40PLUS      = 1 << 29,
+	/* HE 40 with extension channel below */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40MINUS     = 1 << 30,
+	/* HE 40 intolerant */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40INTOL     = 1 << 31,
+};
+
+/**
+ * qca_wlan_vendor_channel_prop_flags_2: This represents the flags for a
+ * channel, and is a continuation of qca_wlan_vendor_channel_prop_flags. This is
+ * used by QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS_2.
+ */
+enum qca_wlan_vendor_channel_prop_flags_2 {
+	/* HE 40 intolerant mark bit for ACS use */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40INTOLMARK = 1 << 0,
+	/* HE 80 channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE80          = 1 << 1,
+	/* HE 160 channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE160         = 1 << 2,
+	/* HE 80+80 channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE80_80       = 1 << 3,
 };
 
 /**
@@ -3140,6 +3514,10 @@
 	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_0 = 9,
 	/* VHT segment 1 (u8) */
 	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_1 = 10,
+	/* A bitmask (u32) with flags specified in
+	 * enum qca_wlan_vendor_channel_prop_flags_2.
+	 */
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS_2 = 11,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_LAST,
@@ -3208,6 +3586,11 @@
 	 * in enum qca_wlan_vendor_external_acs_event_chan_info_attr.
 	 */
 	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO = 12,
+	/* External ACS policy such as PCL mandatory, PCL preferred, etc.
+	 * It uses values defined in enum
+	 * qca_wlan_vendor_attr_external_acs_policy.
+	 */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_POLICY = 13,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_LAST,
@@ -3331,11 +3714,11 @@
 	/* Flag indicating if the station's link to the AP is active.
 	 * Active Link - If included, Inactive link - If not included
 	 */
-	QCA_ATTR_NUD_STATS_AP_LINK_ACTIVE= 9,
+	QCA_ATTR_NUD_STATS_AP_LINK_ACTIVE = 9,
 	/* Flag indicating if there is any duplicate address detected (DAD).
 	 * Yes - If detected, No - If not detected.
 	 */
-	QCA_ATTR_NUD_STATS_IS_DAD = 9,
+	QCA_ATTR_NUD_STATS_IS_DAD = 10,
 
 	/* keep last */
 	QCA_ATTR_NUD_STATS_GET_LAST,
@@ -3422,4 +3805,168 @@
 	QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_AFTER_LAST - 1,
 };
 
+/**
+ * enum qca_wlan_vendor_attr_spectral_scan - Spectral scan config parameters
+ */
+enum qca_wlan_vendor_attr_spectral_scan {
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_INVALID = 0,
+	/* Number of times the chip enters spectral scan mode before
+	 * deactivating spectral scans. When set to 0, chip will enter spectral
+	 * scan mode continuously. u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_SCAN_COUNT = 1,
+	/* Spectral scan period. Period increment resolution is 256*Tclk,
+	 * where Tclk = 1/44 MHz (Gmode), 1/40 MHz (Amode). u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_SCAN_PERIOD = 2,
+	/* Spectral scan priority. u32 attribute. */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_PRIORITY = 3,
+	/* Number of FFT data points to compute. u32 attribute. */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FFT_SIZE = 4,
+	/* Enable targeted gain change before starting the spectral scan FFT.
+	 * u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_GC_ENA = 5,
+	/* Restart a queued spectral scan. u32 attribute. */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RESTART_ENA = 6,
+	/* Noise floor reference number for the calculation of bin power.
+	 * u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_NOISE_FLOOR_REF = 7,
+	/* Disallow spectral scan triggers after TX/RX packets by setting
+	 * this delay value to roughly SIFS time period or greater.
+	 * u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_INIT_DELAY = 8,
+	/* Number of strong bins (inclusive) per sub-channel, below
+	 * which a signal is declared a narrow band tone. u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_NB_TONE_THR = 9,
+	/* Specify the threshold over which a bin is declared strong (for
+	 * scan bandwidth analysis). u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_STR_BIN_THR = 10,
+	/* Spectral scan report mode. u32 attribute. */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_WB_RPT_MODE = 11,
+	/* RSSI report mode, if the ADC RSSI is below
+	 * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RSSI_THR,
+	 * then FFTs will not trigger, but timestamps and summaries get
+	 * reported. u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RSSI_RPT_MODE = 12,
+	/* ADC RSSI must be greater than or equal to this threshold (signed dB)
+	 * to ensure spectral scan reporting with normal error code.
+	 * u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RSSI_THR = 13,
+	/* Format of frequency bin magnitude for spectral scan triggered FFTs:
+	 * 0: linear magnitude, 1: log magnitude (20*log10(lin_mag)).
+	 * u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_PWR_FORMAT = 14,
+	/* Format of FFT report to software for spectral scan triggered FFTs.
+	 * 0: No FFT report (only spectral scan summary report)
+	 * 1: 2-dword summary of metrics for each completed FFT + spectral scan
+	 * report
+	 * 2: 2-dword summary of metrics for each completed FFT + 1x-oversampled
+	 * bins (in-band) per FFT + spectral scan summary report
+	 * 3: 2-dword summary of metrics for each completed FFT + 2x-oversampled
+	 * bins (all) per FFT + spectral scan summary report
+	 * u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RPT_MODE = 15,
+	/* Number of LSBs to shift out in order to scale the FFT bins.
+	 * u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_BIN_SCALE = 16,
+	/* Set to 1 (with spectral_scan_pwr_format=1), to report bin magnitudes
+	 * in dBm power. u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_DBM_ADJ = 17,
+	/* Per chain enable mask to select input ADC for search FFT.
+	 * u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_CHN_MASK = 18,
+	/* An unsigned 64-bit integer provided by host driver to identify the
+	 * spectral scan request. This attribute is included in the scan
+	 * response message for @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START
+	 * and used as an attribute in
+	 * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP to identify the
+	 * specific scan to be stopped.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE = 19,
+
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_MAX =
+		QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_tos {
+	QCA_WLAN_VENDOR_TOS_BK = 0,
+	QCA_WLAN_VENDOR_TOS_BE = 1,
+	QCA_WLAN_VENDOR_TOS_VI = 2,
+	QCA_WLAN_VENDOR_TOS_VO = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_active_tos - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS.
+ */
+enum qca_wlan_vendor_attr_active_tos {
+	QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_INVALID = 0,
+	/* Type Of Service - Represented by qca_wlan_vendor_tos */
+	QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS = 1,
+	/* Flag attribute representing the start (attribute included) or stop
+	 * (attribute not included) of the respective TOS.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_START = 2,
+};
+
+enum qca_wlan_vendor_hang_reason {
+	/* Unspecified reason */
+	QCA_WLAN_HANG_REASON_UNSPECIFIED = 0,
+	/* No Map for the MAC entry for the received frame */
+	QCA_WLAN_HANG_RX_HASH_NO_ENTRY_FOUND = 1,
+	/* Peer deletion timeout happened */
+	QCA_WLAN_HANG_PEER_DELETION_TIMEDOUT = 2,
+	/* Peer unmap timeout */
+	QCA_WLAN_HANG_PEER_UNMAP_TIMEDOUT = 3,
+	/* Scan request timed out */
+	QCA_WLAN_HANG_SCAN_REQ_EXPIRED = 4,
+	/* Consecutive Scan attempt failures */
+	QCA_WLAN_HANG_SCAN_ATTEMPT_FAILURES = 5,
+	/* Unable to get the message buffer */
+	QCA_WLAN_HANG_GET_MSG_BUFF_FAILURE = 6,
+	/* Current command processing is timedout */
+	QCA_WLAN_HANG_ACTIVE_LIST_TIMEOUT = 7,
+	/* Timeout for an ACK from FW for suspend request */
+	QCA_WLAN_HANG_SUSPEND_TIMEOUT = 8,
+	/* Timeout for an ACK from FW for resume request */
+	QCA_WLAN_HANG_RESUME_TIMEOUT = 9,
+	/* Transmission timeout for consecutive data frames */
+	QCA_WLAN_HANG_TRANSMISSIONS_TIMEOUT = 10,
+	/* Timeout for the TX completion status of data frame */
+	QCA_WLAN_HANG_TX_COMPLETE_TIMEOUT = 11,
+	/* DXE failure for TX/RX, DXE resource unavailability */
+	QCA_WLAN_HANG_DXE_FAILURE = 12,
+	/* WMI pending commands exceed the maximum count */
+	QCA_WLAN_HANG_WMI_EXCEED_MAX_PENDING_CMDS = 13,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_hang - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_HANG.
+ */
+enum qca_wlan_vendor_attr_hang {
+	QCA_WLAN_VENDOR_ATTR_HANG_INVALID = 0,
+	/* Reason for the hang - u32 attribute with a value from enum
+	 * qca_wlan_vendor_hang_reason.
+	 */
+	QCA_WLAN_VENDOR_ATTR_HANG_REASON = 1,
+
+	QCA_WLAN_VENDOR_ATTR_HANG_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_HANG_MAX =
+		QCA_WLAN_VENDOR_ATTR_HANG_AFTER_LAST - 1,
+};
+
 #endif /* QCA_VENDOR_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index fd167d6..4bab6b9 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -13,6 +13,7 @@
 #include "crypto/sha1.h"
 #include "crypto/sha256.h"
 #include "crypto/sha384.h"
+#include "crypto/sha512.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/crypto.h"
 #include "ieee802_11_defs.h"
@@ -20,7 +21,7 @@
 #include "wpa_common.h"
 
 
-static unsigned int wpa_kck_len(int akmp)
+static unsigned int wpa_kck_len(int akmp, size_t pmk_len)
 {
 	switch (akmp) {
 	case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
@@ -30,13 +31,17 @@
 	case WPA_KEY_MGMT_FILS_SHA384:
 	case WPA_KEY_MGMT_FT_FILS_SHA384:
 		return 0;
+	case WPA_KEY_MGMT_DPP:
+		return pmk_len / 2;
+	case WPA_KEY_MGMT_OWE:
+		return pmk_len / 2;
 	default:
 		return 16;
 	}
 }
 
 
-static unsigned int wpa_kek_len(int akmp)
+static unsigned int wpa_kek_len(int akmp, size_t pmk_len)
 {
 	switch (akmp) {
 	case WPA_KEY_MGMT_FILS_SHA384:
@@ -46,13 +51,17 @@
 	case WPA_KEY_MGMT_FILS_SHA256:
 	case WPA_KEY_MGMT_FT_FILS_SHA256:
 		return 32;
+	case WPA_KEY_MGMT_DPP:
+		return pmk_len <= 32 ? 16 : 32;
+	case WPA_KEY_MGMT_OWE:
+		return pmk_len <= 32 ? 16 : 32;
 	default:
 		return 16;
 	}
 }
 
 
-unsigned int wpa_mic_len(int akmp)
+unsigned int wpa_mic_len(int akmp, size_t pmk_len)
 {
 	switch (akmp) {
 	case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
@@ -62,6 +71,10 @@
 	case WPA_KEY_MGMT_FT_FILS_SHA256:
 	case WPA_KEY_MGMT_FT_FILS_SHA384:
 		return 0;
+	case WPA_KEY_MGMT_DPP:
+		return pmk_len / 2;
+	case WPA_KEY_MGMT_OWE:
+		return pmk_len / 2;
 	default:
 		return 16;
 	}
@@ -91,30 +104,43 @@
 int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
 		      const u8 *buf, size_t len, u8 *mic)
 {
-	u8 hash[SHA384_MAC_LEN];
+	u8 hash[SHA512_MAC_LEN];
+
+	if (key_len == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "WPA: KCK not set - cannot calculate MIC");
+		return -1;
+	}
 
 	switch (ver) {
 #ifndef CONFIG_FIPS
 	case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4:
+		wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using HMAC-MD5");
 		return hmac_md5(key, key_len, buf, len, mic);
 #endif /* CONFIG_FIPS */
 	case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES:
+		wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using HMAC-SHA1");
 		if (hmac_sha1(key, key_len, buf, len, hash))
 			return -1;
 		os_memcpy(mic, hash, MD5_MAC_LEN);
 		break;
 #if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
 	case WPA_KEY_INFO_TYPE_AES_128_CMAC:
+		wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using AES-CMAC");
 		return omac1_aes_128(key, buf, len, mic);
 #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
 	case WPA_KEY_INFO_TYPE_AKM_DEFINED:
 		switch (akmp) {
 #ifdef CONFIG_HS20
 		case WPA_KEY_MGMT_OSEN:
+			wpa_printf(MSG_DEBUG,
+				   "WPA: EAPOL-Key MIC using AES-CMAC (AKM-defined - OSEN)");
 			return omac1_aes_128(key, buf, len, mic);
 #endif /* CONFIG_HS20 */
 #ifdef CONFIG_SUITEB
 		case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
+			wpa_printf(MSG_DEBUG,
+				   "WPA: EAPOL-Key MIC using HMAC-SHA256 (AKM-defined - Suite B)");
 			if (hmac_sha256(key, key_len, buf, len, hash))
 				return -1;
 			os_memcpy(mic, hash, MD5_MAC_LEN);
@@ -122,16 +148,70 @@
 #endif /* CONFIG_SUITEB */
 #ifdef CONFIG_SUITEB192
 		case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+			wpa_printf(MSG_DEBUG,
+				   "WPA: EAPOL-Key MIC using HMAC-SHA384 (AKM-defined - Suite B 192-bit)");
 			if (hmac_sha384(key, key_len, buf, len, hash))
 				return -1;
 			os_memcpy(mic, hash, 24);
 			break;
 #endif /* CONFIG_SUITEB192 */
+#ifdef CONFIG_OWE
+		case WPA_KEY_MGMT_OWE:
+			wpa_printf(MSG_DEBUG,
+				   "WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - OWE)",
+				   (unsigned int) key_len * 8 * 2);
+			if (key_len == 128 / 8) {
+				if (hmac_sha256(key, key_len, buf, len, hash))
+					return -1;
+			} else if (key_len == 192 / 8) {
+				if (hmac_sha384(key, key_len, buf, len, hash))
+					return -1;
+			} else if (key_len == 256 / 8) {
+				if (hmac_sha512(key, key_len, buf, len, hash))
+					return -1;
+			} else {
+				wpa_printf(MSG_INFO,
+					   "OWE: Unsupported KCK length: %u",
+					   (unsigned int) key_len);
+				return -1;
+			}
+			os_memcpy(mic, hash, key_len);
+			break;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+		case WPA_KEY_MGMT_DPP:
+			wpa_printf(MSG_DEBUG,
+				   "WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - DPP)",
+				   (unsigned int) key_len * 8 * 2);
+			if (key_len == 128 / 8) {
+				if (hmac_sha256(key, key_len, buf, len, hash))
+					return -1;
+			} else if (key_len == 192 / 8) {
+				if (hmac_sha384(key, key_len, buf, len, hash))
+					return -1;
+			} else if (key_len == 256 / 8) {
+				if (hmac_sha512(key, key_len, buf, len, hash))
+					return -1;
+			} else {
+				wpa_printf(MSG_INFO,
+					   "DPP: Unsupported KCK length: %u",
+					   (unsigned int) key_len);
+				return -1;
+			}
+			os_memcpy(mic, hash, key_len);
+			break;
+#endif /* CONFIG_DPP */
 		default:
+			wpa_printf(MSG_DEBUG,
+				   "WPA: EAPOL-Key MIC algorithm not known (AKM-defined - akmp=0x%x)",
+				   akmp);
 			return -1;
 		}
 		break;
 	default:
+		wpa_printf(MSG_DEBUG,
+			   "WPA: EAPOL-Key MIC algorithm not known (ver=%d)",
+			   ver);
 		return -1;
 	}
 
@@ -157,10 +237,6 @@
  * PTK = PRF-X(PMK, "Pairwise key expansion",
  *             Min(AA, SA) || Max(AA, SA) ||
  *             Min(ANonce, SNonce) || Max(ANonce, SNonce))
- *
- * STK = PRF-X(SMK, "Peer key expansion",
- *             Min(MAC_I, MAC_P) || Max(MAC_I, MAC_P) ||
- *             Min(INonce, PNonce) || Max(INonce, PNonce))
  */
 int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
 		   const u8 *addr1, const u8 *addr2,
@@ -171,6 +247,11 @@
 	u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
 	size_t ptk_len;
 
+	if (pmk_len == 0) {
+		wpa_printf(MSG_ERROR, "WPA: No PMK set for PT derivation");
+		return -1;
+	}
+
 	if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) {
 		os_memcpy(data, addr1, ETH_ALEN);
 		os_memcpy(data + ETH_ALEN, addr2, ETH_ALEN);
@@ -189,24 +270,56 @@
 			  WPA_NONCE_LEN);
 	}
 
-	ptk->kck_len = wpa_kck_len(akmp);
-	ptk->kek_len = wpa_kek_len(akmp);
+	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);
 	ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
 
+	if (wpa_key_mgmt_sha384(akmp)) {
 #if defined(CONFIG_SUITEB192) || defined(CONFIG_FILS)
-	if (wpa_key_mgmt_sha384(akmp))
-		sha384_prf(pmk, pmk_len, label, data, sizeof(data),
-			   tmp, ptk_len);
-	else
+		wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)");
+		if (sha384_prf(pmk, pmk_len, label, data, sizeof(data),
+			       tmp, ptk_len) < 0)
+			return -1;
+#else /* CONFIG_SUITEB192 || CONFIG_FILS */
+		return -1;
 #endif /* CONFIG_SUITEB192 || CONFIG_FILS */
+	} else if (wpa_key_mgmt_sha256(akmp) || akmp == WPA_KEY_MGMT_OWE) {
 #ifdef CONFIG_IEEE80211W
-	if (wpa_key_mgmt_sha256(akmp))
-		sha256_prf(pmk, pmk_len, label, data, sizeof(data),
-			   tmp, ptk_len);
-	else
+		wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)");
+		if (sha256_prf(pmk, pmk_len, label, data, sizeof(data),
+			       tmp, ptk_len) < 0)
+			return -1;
+#else /* CONFIG_IEEE80211W */
+		return -1;
 #endif /* CONFIG_IEEE80211W */
-		sha1_prf(pmk, pmk_len, label, data, sizeof(data), tmp, ptk_len);
+#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),
+			       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),
+			       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),
+			       tmp, ptk_len) < 0)
+			return -1;
+	} else if (akmp == WPA_KEY_MGMT_DPP) {
+		wpa_printf(MSG_INFO, "DPP: Unknown PMK length %u",
+			   (unsigned int) pmk_len);
+		return -1;
+#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,
+			     ptk_len) < 0)
+			return -1;
+	}
 
 	wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR,
 		   MAC2STR(addr1), MAC2STR(addr2));
@@ -303,17 +416,22 @@
 
 
 int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
-		    const u8 *snonce, const u8 *anonce, struct wpa_ptk *ptk,
-		    u8 *ick, size_t *ick_len, int akmp, int cipher)
+		    const u8 *snonce, const u8 *anonce, const u8 *dhss,
+		    size_t dhss_len, struct wpa_ptk *ptk,
+		    u8 *ick, size_t *ick_len, int akmp, int cipher,
+		    u8 *fils_ft, size_t *fils_ft_len)
 {
-	u8 data[2 * ETH_ALEN + 2 * FILS_NONCE_LEN];
-	u8 tmp[FILS_ICK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
+	u8 *data, *pos;
+	size_t data_len;
+	u8 tmp[FILS_ICK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN +
+	       FILS_FT_MAX_LEN];
 	size_t key_data_len;
 	const char *label = "FILS PTK Derivation";
+	int ret = -1;
 
 	/*
 	 * FILS-Key-Data = PRF-X(PMK, "FILS PTK Derivation",
-	 *                       SPA || AA || SNonce || ANonce)
+	 *                       SPA || AA || SNonce || ANonce [ || DHss ])
 	 * ICK = L(FILS-Key-Data, 0, ICK_bits)
 	 * KEK = L(FILS-Key-Data, ICK_bits, KEK_bits)
 	 * TK = L(FILS-Key-Data, ICK_bits + KEK_bits, TK_bits)
@@ -321,33 +439,63 @@
 	 * FILS-FT = L(FILS-Key-Data, ICK_bits + KEK_bits + TK_bits,
 	 *             FILS-FT_bits)
 	 */
-	os_memcpy(data, spa, ETH_ALEN);
-	os_memcpy(data + ETH_ALEN, aa, ETH_ALEN);
-	os_memcpy(data + 2 * ETH_ALEN, snonce, FILS_NONCE_LEN);
-	os_memcpy(data + 2 * ETH_ALEN + FILS_NONCE_LEN, anonce, FILS_NONCE_LEN);
+	data_len = 2 * ETH_ALEN + 2 * FILS_NONCE_LEN + dhss_len;
+	data = os_malloc(data_len);
+	if (!data)
+		goto err;
+	pos = data;
+	os_memcpy(pos, spa, ETH_ALEN);
+	pos += ETH_ALEN;
+	os_memcpy(pos, aa, ETH_ALEN);
+	pos += ETH_ALEN;
+	os_memcpy(pos, snonce, FILS_NONCE_LEN);
+	pos += FILS_NONCE_LEN;
+	os_memcpy(pos, anonce, FILS_NONCE_LEN);
+	pos += FILS_NONCE_LEN;
+	if (dhss)
+		os_memcpy(pos, dhss, dhss_len);
 
 	ptk->kck_len = 0;
-	ptk->kek_len = wpa_kek_len(akmp);
+	ptk->kek_len = wpa_kek_len(akmp, pmk_len);
 	ptk->tk_len = wpa_cipher_key_len(cipher);
 	if (wpa_key_mgmt_sha384(akmp))
 		*ick_len = 48;
 	else if (wpa_key_mgmt_sha256(akmp))
 		*ick_len = 32;
 	else
-		return -1;
+		goto err;
 	key_data_len = *ick_len + ptk->kek_len + ptk->tk_len;
 
-	if (wpa_key_mgmt_sha384(akmp))
-		sha384_prf(pmk, pmk_len, label, data, sizeof(data),
-			   tmp, key_data_len);
-	else if (sha256_prf(pmk, pmk_len, label, data, sizeof(data),
-			    tmp, key_data_len) < 0)
-		return -1;
+	if (fils_ft && fils_ft_len) {
+		if (akmp == WPA_KEY_MGMT_FT_FILS_SHA256) {
+			*fils_ft_len = 32;
+		} else if (akmp == WPA_KEY_MGMT_FT_FILS_SHA384) {
+			*fils_ft_len = 48;
+		} else {
+			*fils_ft_len = 0;
+			fils_ft = NULL;
+		}
+		key_data_len += *fils_ft_len;
+	}
+
+	if (wpa_key_mgmt_sha384(akmp)) {
+		wpa_printf(MSG_DEBUG, "FILS: PTK derivation using PRF(SHA384)");
+		if (sha384_prf(pmk, pmk_len, label, data, data_len,
+			       tmp, key_data_len) < 0)
+			goto err;
+	} else {
+		wpa_printf(MSG_DEBUG, "FILS: PTK derivation using PRF(SHA256)");
+		if (sha256_prf(pmk, pmk_len, label, data, data_len,
+			       tmp, key_data_len) < 0)
+			goto err;
+	}
 
 	wpa_printf(MSG_DEBUG, "FILS: PTK derivation - SPA=" MACSTR
 		   " AA=" MACSTR, MAC2STR(spa), MAC2STR(aa));
 	wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN);
 	wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN);
+	if (dhss)
+		wpa_hexdump_key(MSG_DEBUG, "FILS: DHss", dhss, dhss_len);
 	wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, pmk_len);
 	wpa_hexdump_key(MSG_DEBUG, "FILS: FILS-Key-Data", tmp, key_data_len);
 
@@ -360,10 +508,18 @@
 	os_memcpy(ptk->tk, tmp + *ick_len + ptk->kek_len, ptk->tk_len);
 	wpa_hexdump_key(MSG_DEBUG, "FILS: TK", ptk->tk, ptk->tk_len);
 
-	/* TODO: FILS-FT */
+	if (fils_ft && fils_ft_len) {
+		os_memcpy(fils_ft, tmp + *ick_len + ptk->kek_len + ptk->tk_len,
+			  *fils_ft_len);
+		wpa_hexdump_key(MSG_DEBUG, "FILS: FILS-FT",
+				fils_ft, *fils_ft_len);
+	}
 
 	os_memset(tmp, 0, sizeof(tmp));
-	return 0;
+	ret = 0;
+err:
+	bin_clear_free(data, data_len);
+	return ret;
 }
 
 
@@ -379,6 +535,14 @@
 	size_t num_elem = 4;
 	int res;
 
+	wpa_printf(MSG_DEBUG, "FILS: Key-Auth derivation: STA-MAC=" MACSTR
+		   " AP-BSSID=" MACSTR, MAC2STR(sta_addr), MAC2STR(bssid));
+	wpa_hexdump_key(MSG_DEBUG, "FILS: ICK", ick, ick_len);
+	wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FILS: gSTA", g_sta, g_sta_len);
+	wpa_hexdump(MSG_DEBUG, "FILS: gAP", g_ap, g_ap_len);
+
 	/*
 	 * For (Re)Association Request frame (STA->AP):
 	 * Key-Auth = HMAC-Hash(ICK, SNonce || ANonce || STA-MAC || AP-BSSID
@@ -765,6 +929,14 @@
 		return WPA_KEY_MGMT_FT_FILS_SHA256;
 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_FILS_SHA384)
 		return WPA_KEY_MGMT_FT_FILS_SHA384;
+#ifdef CONFIG_OWE
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OWE)
+		return WPA_KEY_MGMT_OWE;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_DPP)
+		return WPA_KEY_MGMT_DPP;
+#endif /* CONFIG_DPP */
 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OSEN)
 		return WPA_KEY_MGMT_OSEN;
 	return 0;
@@ -1248,8 +1420,8 @@
 	os_memcpy(pos, sta_addr, ETH_ALEN);
 	pos += ETH_ALEN;
 
-	ptk->kck_len = wpa_kck_len(akmp);
-	ptk->kek_len = wpa_kek_len(akmp);
+	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);
 	ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
 
@@ -1302,29 +1474,48 @@
  * @aa: Authenticator address
  * @spa: Supplicant address
  * @pmkid: Buffer for PMKID
- * @use_sha256: Whether to use SHA256-based KDF
+ * @akmp: Negotiated key management protocol
  *
- * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
- * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA)
+ * IEEE Std 802.11-2016 - 12.7.1.3 Pairwise key hierarchy
+ * AKM: 00-0F-AC:5, 00-0F-AC:6, 00-0F-AC:14, 00-0F-AC:16
+ * PMKID = Truncate-128(HMAC-SHA-256(PMK, "PMK Name" || AA || SPA))
+ * AKM: 00-0F-AC:11
+ * See rsn_pmkid_suite_b()
+ * AKM: 00-0F-AC:12
+ * See rsn_pmkid_suite_b_192()
+ * AKM: 00-0F-AC:15, 00-0F-AC:17
+ * PMKID = Truncate-128(HMAC-SHA-384(PMK, "PMK Name" || AA || SPA))
+ * Otherwise:
+ * PMKID = Truncate-128(HMAC-SHA-1(PMK, "PMK Name" || AA || SPA))
  */
 void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
-	       u8 *pmkid, int use_sha256)
+	       u8 *pmkid, int akmp)
 {
 	char *title = "PMK Name";
 	const u8 *addr[3];
 	const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
-	unsigned char hash[SHA256_MAC_LEN];
+	unsigned char hash[SHA384_MAC_LEN];
 
 	addr[0] = (u8 *) title;
 	addr[1] = aa;
 	addr[2] = spa;
 
-#ifdef CONFIG_IEEE80211W
-	if (use_sha256)
+	if (0) {
+#ifdef CONFIG_FILS
+	} else if (wpa_key_mgmt_sha384(akmp)) {
+		wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-384");
+		hmac_sha384_vector(pmk, pmk_len, 3, addr, len, hash);
+#endif /* CONFIG_FILS */
+#if defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS)
+	} else if (wpa_key_mgmt_sha256(akmp)) {
+		wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-256");
 		hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash);
-	else
-#endif /* CONFIG_IEEE80211W */
+#endif /* CONFIG_IEEE80211W || CONFIG_FILS */
+	} else {
+		wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-1");
 		hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash);
+	}
+	wpa_hexdump(MSG_DEBUG, "RSN: Derived PMKID", hash, PMKID_LEN);
 	os_memcpy(pmkid, hash, PMKID_LEN);
 }
 
@@ -1421,6 +1612,14 @@
 		return "GCMP-256";
 	case WPA_CIPHER_CCMP_256:
 		return "CCMP-256";
+	case WPA_CIPHER_AES_128_CMAC:
+		return "BIP";
+	case WPA_CIPHER_BIP_GMAC_128:
+		return "BIP-GMAC-128";
+	case WPA_CIPHER_BIP_GMAC_256:
+		return "BIP-GMAC-256";
+	case WPA_CIPHER_BIP_CMAC_256:
+		return "BIP-CMAC-256";
 	case WPA_CIPHER_GTK_NOT_USED:
 		return "GTK_NOT_USED";
 	default:
@@ -1486,6 +1685,10 @@
 		return "FT-FILS-SHA256";
 	case WPA_KEY_MGMT_FT_FILS_SHA384:
 		return "FT-FILS-SHA384";
+	case WPA_KEY_MGMT_OWE:
+		return "OWE";
+	case WPA_KEY_MGMT_DPP:
+		return "DPP";
 	default:
 		return "UNKNOWN";
 	}
@@ -1896,6 +2099,14 @@
 			val |= WPA_CIPHER_NONE;
 		else if (os_strcmp(start, "GTK_NOT_USED") == 0)
 			val |= WPA_CIPHER_GTK_NOT_USED;
+		else if (os_strcmp(start, "AES-128-CMAC") == 0)
+			val |= WPA_CIPHER_AES_128_CMAC;
+		else if (os_strcmp(start, "BIP-GMAC-128") == 0)
+			val |= WPA_CIPHER_BIP_GMAC_128;
+		else if (os_strcmp(start, "BIP-GMAC-256") == 0)
+			val |= WPA_CIPHER_BIP_GMAC_256;
+		else if (os_strcmp(start, "BIP-CMAC-256") == 0)
+			val |= WPA_CIPHER_BIP_CMAC_256;
 		else {
 			os_free(buf);
 			return -1;
@@ -1951,6 +2162,34 @@
 			return -1;
 		pos += ret;
 	}
+	if (ciphers & WPA_CIPHER_AES_128_CMAC) {
+		ret = os_snprintf(pos, end - pos, "%sAES-128-CMAC",
+				  pos == start ? "" : delim);
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+	if (ciphers & WPA_CIPHER_BIP_GMAC_128) {
+		ret = os_snprintf(pos, end - pos, "%sBIP-GMAC-128",
+				  pos == start ? "" : delim);
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+	if (ciphers & WPA_CIPHER_BIP_GMAC_256) {
+		ret = os_snprintf(pos, end - pos, "%sBIP-GMAC-256",
+				  pos == start ? "" : delim);
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+	if (ciphers & WPA_CIPHER_BIP_CMAC_256) {
+		ret = os_snprintf(pos, end - pos, "%sBIP-CMAC-256",
+				  pos == start ? "" : delim);
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
 	if (ciphers & WPA_CIPHER_NONE) {
 		ret = os_snprintf(pos, end - pos, "%sNONE",
 				  pos == start ? "" : delim);
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 5733c75..3b8c1fb 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -1,6 +1,6 @@
 /*
  * WPA definitions shared between hostapd and wpa_supplicant
- * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -13,13 +13,15 @@
 #define PMKID_LEN 16
 #define PMK_LEN 32
 #define PMK_LEN_SUITE_B_192 48
-#define PMK_LEN_MAX 48
+#define PMK_LEN_MAX 64
 #define WPA_REPLAY_COUNTER_LEN 8
 #define WPA_NONCE_LEN 32
 #define WPA_KEY_RSC_LEN 8
 #define WPA_GMK_LEN 32
 #define WPA_GTK_MAX_LEN 32
 
+#define OWE_DH_GROUP 19
+
 #define WPA_ALLOWED_PAIRWISE_CIPHERS \
 (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE | \
 WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256)
@@ -27,6 +29,9 @@
 (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | \
 WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \
 WPA_CIPHER_GTK_NOT_USED)
+#define WPA_ALLOWED_GROUP_MGMT_CIPHERS \
+(WPA_CIPHER_AES_128_CMAC | WPA_CIPHER_BIP_GMAC_128 | WPA_CIPHER_BIP_GMAC_256 | \
+WPA_CIPHER_BIP_CMAC_256)
 
 #define WPA_SELECTOR_LEN 4
 #define WPA_VERSION 1
@@ -63,8 +68,10 @@
 #define RSN_AUTH_KEY_MGMT_FILS_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 15)
 #define RSN_AUTH_KEY_MGMT_FT_FILS_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 16)
 #define RSN_AUTH_KEY_MGMT_FT_FILS_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 17)
+#define RSN_AUTH_KEY_MGMT_OWE RSN_SELECTOR(0x00, 0x0f, 0xac, 18)
 #define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00)
 #define RSN_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01)
+#define RSN_AUTH_KEY_MGMT_DPP RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x02)
 
 #define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0)
 #define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
@@ -98,12 +105,6 @@
 #endif
 #define RSN_KEY_DATA_MAC_ADDR RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
 #define RSN_KEY_DATA_PMKID RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
-#ifdef CONFIG_PEERKEY
-#define RSN_KEY_DATA_SMK RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
-#define RSN_KEY_DATA_NONCE RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
-#define RSN_KEY_DATA_LIFETIME RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
-#define RSN_KEY_DATA_ERROR RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
-#endif /* CONFIG_PEERKEY */
 #ifdef CONFIG_IEEE80211W
 #define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
 #endif /* CONFIG_IEEE80211W */
@@ -194,11 +195,12 @@
 	/* followed by Key Data Length bytes of Key Data */
 } STRUCT_PACKED;
 
-#define WPA_EAPOL_KEY_MIC_MAX_LEN 24
-#define WPA_KCK_MAX_LEN 24
+#define WPA_EAPOL_KEY_MIC_MAX_LEN 32
+#define WPA_KCK_MAX_LEN 32
 #define WPA_KEK_MAX_LEN 64
 #define WPA_TK_MAX_LEN 32
 #define FILS_ICK_MAX_LEN 48
+#define FILS_FT_MAX_LEN 48
 
 /**
  * struct wpa_ptk - WPA Pairwise Transient Key
@@ -279,22 +281,6 @@
 } STRUCT_PACKED;
 
 
-#ifdef CONFIG_PEERKEY
-enum {
-	STK_MUI_4WAY_STA_AP = 1,
-	STK_MUI_4WAY_STAT_STA = 2,
-	STK_MUI_GTK = 3,
-	STK_MUI_SMK = 4
-};
-
-enum {
-	STK_ERR_STA_NR = 1,
-	STK_ERR_STA_NRSN = 2,
-	STK_ERR_CPHR_NS = 3,
-	STK_ERR_NO_STSL = 4
-};
-#endif /* CONFIG_PEERKEY */
-
 struct rsn_error_kde {
 	be16 mui;
 	be16 error_type;
@@ -354,8 +340,10 @@
 int fils_pmkid_erp(int akmp, const u8 *reauth, size_t reauth_len,
 		   u8 *pmkid);
 int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
-		    const u8 *snonce, const u8 *anonce, struct wpa_ptk *ptk,
-		    u8 *ick, size_t *ick_len, int akmp, int cipher);
+		    const u8 *snonce, const u8 *anonce, const u8 *dhss,
+		    size_t dhss_len, struct wpa_ptk *ptk,
+		    u8 *ick, size_t *ick_len, int akmp, int cipher,
+		    u8 *fils_ft, size_t *fils_ft_len);
 int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce,
 		     const u8 *anonce, const u8 *sta_addr, const u8 *bssid,
 		     const u8 *g_sta, size_t g_sta_len,
@@ -403,7 +391,7 @@
 			 struct wpa_ie_data *data);
 
 void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
-	       u8 *pmkid, int use_sha256);
+	       u8 *pmkid, int akmp);
 #ifdef CONFIG_SUITEB
 int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa,
 		       const u8 *spa, u8 *pmkid);
@@ -472,7 +460,7 @@
 int wpa_parse_cipher(const char *value);
 int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim);
 int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise);
-unsigned int wpa_mic_len(int akmp);
+unsigned int wpa_mic_len(int akmp, size_t pmk_len);
 int fils_domain_name_hash(const char *domain, u8 *hash);
 
 #endif /* WPA_COMMON_H */
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 4649eab..c59bc60 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -143,6 +143,23 @@
 #define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS "
 #define WPS_EVENT_ER_SET_SEL_REG "WPS-ER-AP-SET-SEL-REG "
 
+/* DPP events */
+#define DPP_EVENT_AUTH_SUCCESS "DPP-AUTH-SUCCESS "
+#define DPP_EVENT_NOT_COMPATIBLE "DPP-NOT-COMPATIBLE "
+#define DPP_EVENT_RESPONSE_PENDING "DPP-RESPONSE-PENDING "
+#define DPP_EVENT_SCAN_PEER_QR_CODE "DPP-SCAN-PEER-QR-CODE "
+#define DPP_EVENT_CONF_RECEIVED "DPP-CONF-RECEIVED "
+#define DPP_EVENT_CONF_SENT "DPP-CONF-SENT "
+#define DPP_EVENT_CONF_FAILED "DPP-CONF-FAILED "
+#define DPP_EVENT_CONFOBJ_SSID "DPP-CONFOBJ-SSID "
+#define DPP_EVENT_CONFOBJ_PASS "DPP-CONFOBJ-PASS "
+#define DPP_EVENT_CONFOBJ_PSK "DPP-CONFOBJ-PSK "
+#define DPP_EVENT_CONNECTOR "DPP-CONNECTOR "
+#define DPP_EVENT_C_SIGN_KEY "DPP-C-SIGN-KEY "
+#define DPP_EVENT_NET_ACCESS_KEY "DPP-NET-ACCESS-KEY "
+#define DPP_EVENT_MISSING_CONNECTOR "DPP-MISSING-CONNECTOR "
+#define DPP_EVENT_NETWORK_ID "DPP-NETWORK-ID "
+
 /* MESH events */
 #define MESH_GROUP_STARTED "MESH-GROUP-STARTED "
 #define MESH_GROUP_REMOVED "MESH-GROUP-REMOVED "
@@ -234,6 +251,7 @@
 #define RX_HS20_ANQP "RX-HS20-ANQP "
 #define RX_HS20_ANQP_ICON "RX-HS20-ANQP-ICON "
 #define RX_HS20_ICON "RX-HS20-ICON "
+#define RX_MBO_ANQP "RX-MBO-ANQP "
 
 #define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION "
 #define HS20_DEAUTH_IMMINENT_NOTICE "HS20-DEAUTH-IMMINENT-NOTICE "
@@ -275,6 +293,7 @@
 #define DFS_EVENT_CAC_START "DFS-CAC-START "
 #define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED "
 #define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED "
+#define DFS_EVENT_PRE_CAC_EXPIRED "DFS-PRE-CAC-EXPIRED "
 
 #define AP_CSA_FINISHED "AP-CSA-FINISHED "
 
diff --git a/src/common/wpa_helpers.c b/src/common/wpa_helpers.c
index f159421..8e1c09e 100644
--- a/src/common/wpa_helpers.c
+++ b/src/common/wpa_helpers.c
@@ -222,7 +222,8 @@
 		if (get_wpa_status(ifname, "ip_address", ip, sizeof(ip)) == 0
 		    && strlen(ip) > 0) {
 			printf("IP address found: '%s'\n", ip);
-			return 0;
+			if (strncmp(ip, "169.254.", 8) != 0)
+				return 0;
 		}
 		ctrl = wpa_open_ctrl(ifname);
 		if (ctrl == NULL)
diff --git a/src/crypto/Makefile b/src/crypto/Makefile
index d181e72..ee93e41 100644
--- a/src/crypto/Makefile
+++ b/src/crypto/Makefile
@@ -14,6 +14,9 @@
 CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
 #CFLAGS += -DALL_DH_GROUPS
 CFLAGS += -DCONFIG_SHA256
+CFLAGS += -DCONFIG_SHA384
+CFLAGS += -DCONFIG_HMAC_SHA384_KDF
+CFLAGS += -DCONFIG_INTERNAL_SHA384
 
 LIB_OBJS= \
 	aes-cbc.o \
@@ -48,6 +51,8 @@
 	sha256-prf.o \
 	sha256-tlsprf.o \
 	sha256-internal.o \
+	sha384.o \
+	sha384-prf.o \
 	sha384-internal.o \
 	sha512-internal.o
 
diff --git a/src/crypto/aes-internal-dec.c b/src/crypto/aes-internal-dec.c
index 720c703..7482295 100644
--- a/src/crypto/aes-internal-dec.c
+++ b/src/crypto/aes-internal-dec.c
@@ -147,10 +147,12 @@
 	PUTU32(pt + 12, s3);
 }
 
-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
 {
 	u32 *rk = ctx;
 	rijndaelDecrypt(ctx, rk[AES_PRIV_NR_POS], crypt, plain);
+	return 0;
 }
 
 
diff --git a/src/crypto/aes-internal-enc.c b/src/crypto/aes-internal-enc.c
index f3c61b8..9fdb4f3 100644
--- a/src/crypto/aes-internal-enc.c
+++ b/src/crypto/aes-internal-enc.c
@@ -112,10 +112,11 @@
 }
 
 
-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
 {
 	u32 *rk = ctx;
 	rijndaelEncrypt(ctx, rk[AES_PRIV_NR_POS], plain, crypt);
+	return 0;
 }
 
 
diff --git a/src/crypto/aes-siv.c b/src/crypto/aes-siv.c
index 2bb79b5..b682f3a 100644
--- a/src/crypto/aes-siv.c
+++ b/src/crypto/aes-siv.c
@@ -95,11 +95,10 @@
 		xor(tmp, tmp2);
 	}
 	if (len[i] >= AES_BLOCK_SIZE) {
-		buf = os_malloc(len[i]);
+		buf = os_memdup(addr[i], len[i]);
 		if (!buf)
 			return -ENOMEM;
 
-		os_memcpy(buf, addr[i], len[i]);
 		xorend(buf, len[i], tmp, AES_BLOCK_SIZE);
 		data[0] = buf;
 		ret = omac1_aes_vector(key, key_len, 1, data, &len[i], mac);
diff --git a/src/crypto/aes.h b/src/crypto/aes.h
index 2de59e0..8ab3de2 100644
--- a/src/crypto/aes.h
+++ b/src/crypto/aes.h
@@ -12,10 +12,10 @@
 #define AES_BLOCK_SIZE 16
 
 void * aes_encrypt_init(const u8 *key, size_t len);
-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
 void aes_encrypt_deinit(void *ctx);
 void * aes_decrypt_init(const u8 *key, size_t len);
-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
 void aes_decrypt_deinit(void *ctx);
 
 #endif /* AES_H */
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index bdc3ba6..a723201 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -1,6 +1,6 @@
 /*
  * Wrapper functions for crypto libraries
- * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -106,8 +106,9 @@
  * @clear: 8 octets (in)
  * @key: 7 octets (in) (no parity bits included)
  * @cypher: 8 octets (out)
+ * Returns: 0 on success, -1 on failure
  */
-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher);
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher);
 
 /**
  * aes_encrypt_init - Initialize AES for encryption
@@ -122,8 +123,9 @@
  * @ctx: Context pointer from aes_encrypt_init()
  * @plain: Plaintext data to be encrypted (16 bytes)
  * @crypt: Buffer for the encrypted data (16 bytes)
+ * Returns: 0 on success, -1 on failure
  */
-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
 
 /**
  * aes_encrypt_deinit - Deinitialize AES encryption
@@ -144,8 +146,9 @@
  * @ctx: Context pointer from aes_encrypt_init()
  * @crypt: Encrypted data (16 bytes)
  * @plain: Buffer for the decrypted data (16 bytes)
+ * Returns: 0 on success, -1 on failure
  */
-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
 
 /**
  * aes_decrypt_deinit - Deinitialize AES decryption
@@ -829,4 +832,12 @@
 			const struct crypto_ec_point *a,
 			const struct crypto_ec_point *b);
 
+struct crypto_ecdh;
+
+struct crypto_ecdh * crypto_ecdh_init(int group);
+struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y);
+struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
+					const u8 *key, size_t len);
+void crypto_ecdh_deinit(struct crypto_ecdh *ecdh);
+
 #endif /* CRYPTO_H */
diff --git a/src/crypto/crypto_gnutls.c b/src/crypto/crypto_gnutls.c
index 0dfd54d..31a580e 100644
--- a/src/crypto/crypto_gnutls.c
+++ b/src/crypto/crypto_gnutls.c
@@ -30,7 +30,7 @@
 }
 
 
-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
 {
 	gcry_cipher_hd_t hd;
 	u8 pkey[8], next, tmp;
@@ -49,6 +49,7 @@
 	gcry_err_code(gcry_cipher_setkey(hd, pkey, 8));
 	gcry_cipher_encrypt(hd, cypher, 8, clear, 8);
 	gcry_cipher_close(hd);
+	return 0;
 }
 
 
@@ -107,10 +108,11 @@
 }
 
 
-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
 {
 	gcry_cipher_hd_t hd = ctx;
 	gcry_cipher_encrypt(hd, crypt, 16, plain, 16);
+	return 0;
 }
 
 
@@ -137,10 +139,11 @@
 }
 
 
-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
 {
 	gcry_cipher_hd_t hd = ctx;
 	gcry_cipher_decrypt(hd, plain, 16, crypt, 16);
+	return 0;
 }
 
 
diff --git a/src/crypto/crypto_libtomcrypt.c b/src/crypto/crypto_libtomcrypt.c
index a55edd1..b80ad57 100644
--- a/src/crypto/crypto_libtomcrypt.c
+++ b/src/crypto/crypto_libtomcrypt.c
@@ -35,7 +35,7 @@
 }
 
 
-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
 {
 	u8 pkey[8], next, tmp;
 	int i;
@@ -53,6 +53,7 @@
 	des_setup(pkey, 8, 0, &skey);
 	des_ecb_encrypt(clear, cypher, &skey);
 	des_done(&skey);
+	return 0;
 }
 
 
@@ -96,10 +97,10 @@
 }
 
 
-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
 {
 	symmetric_key *skey = ctx;
-	aes_ecb_encrypt(plain, crypt, skey);
+	return aes_ecb_encrypt(plain, crypt, skey) == CRYPT_OK ? 0 : -1;
 }
 
 
@@ -125,10 +126,10 @@
 }
 
 
-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
 {
 	symmetric_key *skey = ctx;
-	aes_ecb_encrypt(plain, (u8 *) crypt, skey);
+	return aes_ecb_encrypt(plain, (u8 *) crypt, skey) == CRYPT_OK ? 0 : -1;
 }
 
 
@@ -297,7 +298,7 @@
 struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
 					  const u8 *iv, const u8 *key,
 					  size_t key_len)
-{	
+{
 	struct crypto_cipher *ctx;
 	int idx, res, rc4 = 0;
 
diff --git a/src/crypto/crypto_linux.c b/src/crypto/crypto_linux.c
new file mode 100644
index 0000000..8099193
--- /dev/null
+++ b/src/crypto/crypto_linux.c
@@ -0,0 +1,1006 @@
+/*
+ * Crypto wrapper for Linux kernel AF_ALG
+ * Copyright (c) 2017, 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 <linux/if_alg.h>
+
+#include "common.h"
+#include "crypto.h"
+#include "md5.h"
+#include "sha1.h"
+#include "sha256.h"
+#include "sha384.h"
+#include "aes.h"
+
+
+#ifndef SOL_ALG
+#define SOL_ALG 279
+#endif /* SOL_ALG */
+
+
+static int linux_af_alg_socket(const char *type, const char *name)
+{
+	struct sockaddr_alg sa;
+	int s;
+
+	if (TEST_FAIL())
+		return -1;
+
+	s = socket(AF_ALG, SOCK_SEQPACKET, 0);
+	if (s < 0) {
+		wpa_printf(MSG_ERROR, "%s: Failed to open AF_ALG socket: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+
+	os_memset(&sa, 0, sizeof(sa));
+	sa.salg_family = AF_ALG;
+	os_strlcpy((char *) sa.salg_type, type, sizeof(sa.salg_type));
+	os_strlcpy((char *) sa.salg_name, name, sizeof(sa.salg_type));
+	if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "%s: Failed to bind AF_ALG socket(%s,%s): %s",
+			   __func__, type, name, strerror(errno));
+		close(s);
+		return -1;
+	}
+
+	return s;
+}
+
+
+static int linux_af_alg_hash_vector(const char *alg, const u8 *key,
+				    size_t key_len, size_t num_elem,
+				    const u8 *addr[], const size_t *len,
+				    u8 *mac, size_t mac_len)
+{
+	int s, t;
+	size_t i;
+	ssize_t res;
+	int ret = -1;
+
+	s = linux_af_alg_socket("hash", alg);
+	if (s < 0)
+		return -1;
+
+	if (key && setsockopt(s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) {
+		wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s",
+			   __func__, strerror(errno));
+		close(s);
+		return -1;
+	}
+
+	t = accept(s, NULL, NULL);
+	if (t < 0) {
+		wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s",
+			   __func__, strerror(errno));
+		close(s);
+		return -1;
+	}
+
+	for (i = 0; i < num_elem; i++) {
+		res = send(t, addr[i], len[i], i + 1 < num_elem ? MSG_MORE : 0);
+		if (res < 0) {
+			wpa_printf(MSG_ERROR,
+				   "%s: send on AF_ALG socket failed: %s",
+				   __func__, strerror(errno));
+			goto fail;
+		}
+		if ((size_t) res < len[i]) {
+			wpa_printf(MSG_ERROR,
+				   "%s: send on AF_ALG socket did not accept full buffer (%d/%d)",
+				   __func__, (int) res, (int) len[i]);
+			goto fail;
+		}
+	}
+
+	res = recv(t, mac, mac_len, 0);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR,
+			   "%s: recv on AF_ALG socket failed: %s",
+			   __func__, strerror(errno));
+		goto fail;
+	}
+	if ((size_t) res < mac_len) {
+		wpa_printf(MSG_ERROR,
+			   "%s: recv on AF_ALG socket did not return full buffer (%d/%d)",
+			   __func__, (int) res, (int) mac_len);
+		goto fail;
+	}
+
+	ret = 0;
+fail:
+	close(t);
+	close(s);
+
+	return ret;
+}
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return linux_af_alg_hash_vector("md4", NULL, 0, num_elem, addr, len,
+					mac, 16);
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return linux_af_alg_hash_vector("md5", NULL, 0, num_elem, addr, len,
+					mac, MD5_MAC_LEN);
+}
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		u8 *mac)
+{
+	return linux_af_alg_hash_vector("sha1", NULL, 0, num_elem, addr, len,
+					mac, SHA1_MAC_LEN);
+}
+
+
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	return linux_af_alg_hash_vector("sha256", NULL, 0, num_elem, addr, len,
+					mac, SHA256_MAC_LEN);
+}
+
+
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	return linux_af_alg_hash_vector("sha384", NULL, 0, num_elem, addr, len,
+					mac, SHA384_MAC_LEN);
+}
+
+
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	return linux_af_alg_hash_vector("sha512", NULL, 0, num_elem, addr, len,
+					mac, 64);
+}
+
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+		    const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return linux_af_alg_hash_vector("hmac(md5)", key, key_len, num_elem,
+					addr, len, mac, 16);
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	     u8 *mac)
+{
+	return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return linux_af_alg_hash_vector("hmac(sha1)", key, key_len, num_elem,
+					addr, len, mac, SHA1_MAC_LEN);
+}
+
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	      u8 *mac)
+{
+	return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return linux_af_alg_hash_vector("hmac(sha256)", key, key_len, num_elem,
+					addr, len, mac, SHA256_MAC_LEN);
+}
+
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return linux_af_alg_hash_vector("hmac(sha384)", key, key_len, num_elem,
+					addr, len, mac, SHA384_MAC_LEN);
+}
+
+
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+struct crypto_hash {
+	int s;
+	int t;
+	size_t mac_len;
+	int failed;
+};
+
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+				      size_t key_len)
+{
+	struct crypto_hash *ctx;
+	const char *name;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (!ctx)
+		return NULL;
+
+	switch (alg) {
+	case CRYPTO_HASH_ALG_MD5:
+		name = "md5";
+		ctx->mac_len = MD5_MAC_LEN;
+		break;
+	case CRYPTO_HASH_ALG_SHA1:
+		name = "sha1";
+		ctx->mac_len = SHA1_MAC_LEN;
+		break;
+	case CRYPTO_HASH_ALG_HMAC_MD5:
+		name = "hmac(md5)";
+		ctx->mac_len = MD5_MAC_LEN;
+		break;
+	case CRYPTO_HASH_ALG_HMAC_SHA1:
+		name = "hmac(sha1)";
+		ctx->mac_len = SHA1_MAC_LEN;
+		break;
+	case CRYPTO_HASH_ALG_SHA256:
+		name = "sha256";
+		ctx->mac_len = SHA256_MAC_LEN;
+		break;
+	case CRYPTO_HASH_ALG_HMAC_SHA256:
+		name = "hmac(sha256)";
+		ctx->mac_len = SHA256_MAC_LEN;
+		break;
+	case CRYPTO_HASH_ALG_SHA384:
+		name = "sha384";
+		ctx->mac_len = SHA384_MAC_LEN;
+		break;
+	case CRYPTO_HASH_ALG_SHA512:
+		name = "sha512";
+		ctx->mac_len = 64;
+		break;
+	default:
+		os_free(ctx);
+		return NULL;
+	}
+
+	ctx->s = linux_af_alg_socket("hash", name);
+	if (ctx->s < 0) {
+		os_free(ctx);
+		return NULL;
+	}
+
+	if (key && key_len &&
+	    setsockopt(ctx->s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) {
+		wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s",
+			   __func__, strerror(errno));
+		close(ctx->s);
+		os_free(ctx);
+		return NULL;
+	}
+
+	ctx->t = accept(ctx->s, NULL, NULL);
+	if (ctx->t < 0) {
+		wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s",
+			   __func__, strerror(errno));
+		close(ctx->s);
+		os_free(ctx);
+		return NULL;
+	}
+
+	return ctx;
+}
+
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+	ssize_t res;
+
+	if (!ctx)
+		return;
+
+	res = send(ctx->t, data, len, MSG_MORE);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR,
+			   "%s: send on AF_ALG socket failed: %s",
+			   __func__, strerror(errno));
+		ctx->failed = 1;
+		return;
+	}
+	if ((size_t) res < len) {
+		wpa_printf(MSG_ERROR,
+			   "%s: send on AF_ALG socket did not accept full buffer (%d/%d)",
+			   __func__, (int) res, (int) len);
+		ctx->failed = 1;
+		return;
+	}
+}
+
+
+static void crypto_hash_deinit(struct crypto_hash *ctx)
+{
+	close(ctx->s);
+	close(ctx->t);
+	os_free(ctx);
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+	ssize_t res;
+
+	if (!ctx)
+		return -2;
+
+	if (!mac || !len) {
+		crypto_hash_deinit(ctx);
+		return 0;
+	}
+
+	if (ctx->failed) {
+		crypto_hash_deinit(ctx);
+		return -2;
+	}
+
+	if (*len < ctx->mac_len) {
+		crypto_hash_deinit(ctx);
+		*len = ctx->mac_len;
+		return -1;
+	}
+	*len = ctx->mac_len;
+
+	res = recv(ctx->t, mac, ctx->mac_len, 0);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR,
+			   "%s: recv on AF_ALG socket failed: %s",
+			   __func__, strerror(errno));
+		crypto_hash_deinit(ctx);
+		return -2;
+	}
+	if ((size_t) res < ctx->mac_len) {
+		wpa_printf(MSG_ERROR,
+			   "%s: recv on AF_ALG socket did not return full buffer (%d/%d)",
+			   __func__, (int) res, (int) ctx->mac_len);
+		crypto_hash_deinit(ctx);
+		return -2;
+	}
+
+	crypto_hash_deinit(ctx);
+	return 0;
+}
+
+
+struct linux_af_alg_skcipher {
+	int s;
+	int t;
+};
+
+
+static void linux_af_alg_skcipher_deinit(struct linux_af_alg_skcipher *skcipher)
+{
+	if (!skcipher)
+		return;
+	if (skcipher->s >= 0)
+		close(skcipher->s);
+	if (skcipher->t >= 0)
+		close(skcipher->t);
+	os_free(skcipher);
+}
+
+
+static struct linux_af_alg_skcipher *
+linux_af_alg_skcipher(const char *alg, const u8 *key, size_t key_len)
+{
+	struct linux_af_alg_skcipher *skcipher;
+
+	skcipher = os_zalloc(sizeof(*skcipher));
+	if (!skcipher)
+		goto fail;
+	skcipher->t = -1;
+
+	skcipher->s = linux_af_alg_socket("skcipher", alg);
+	if (skcipher->s < 0)
+		goto fail;
+
+	if (setsockopt(skcipher->s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) {
+		wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s",
+			   __func__, strerror(errno));
+		goto fail;
+	}
+
+	skcipher->t = accept(skcipher->s, NULL, NULL);
+	if (skcipher->t < 0) {
+		wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s",
+			   __func__, strerror(errno));
+		goto fail;
+	}
+
+	return skcipher;
+fail:
+	linux_af_alg_skcipher_deinit(skcipher);
+	return NULL;
+}
+
+
+static int linux_af_alg_skcipher_oper(struct linux_af_alg_skcipher *skcipher,
+				      int enc, const u8 *in, u8 *out)
+{
+	char buf[CMSG_SPACE(sizeof(u32))];
+	struct iovec io[1];
+	struct msghdr msg;
+	struct cmsghdr *hdr;
+	ssize_t ret;
+	u32 *op;
+
+	io[0].iov_base = (void *) in;
+	io[0].iov_len = AES_BLOCK_SIZE;
+	os_memset(&msg, 0, sizeof(msg));
+	os_memset(buf, 0, sizeof(buf));
+	msg.msg_control = buf;
+	msg.msg_controllen = CMSG_SPACE(sizeof(u32));
+	msg.msg_iov = io;
+	msg.msg_iovlen = 1;
+	hdr = CMSG_FIRSTHDR(&msg);
+	hdr->cmsg_level = SOL_ALG;
+	hdr->cmsg_type = ALG_SET_OP;
+	hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+	op = (u32 *) CMSG_DATA(hdr);
+	*op = enc ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT;
+
+	ret = sendmsg(skcipher->t, &msg, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+
+	ret = read(skcipher->t, out, AES_BLOCK_SIZE);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: read failed: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+	if (ret < AES_BLOCK_SIZE) {
+		wpa_printf(MSG_ERROR,
+			   "%s: read did not return full data (%d/%d)",
+			   __func__, (int) ret, AES_BLOCK_SIZE);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+	return linux_af_alg_skcipher("ecb(aes)", key, len);
+}
+
+
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+	struct linux_af_alg_skcipher *skcipher = ctx;
+
+	return linux_af_alg_skcipher_oper(skcipher, 1, plain, crypt);
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+	linux_af_alg_skcipher_deinit(ctx);
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+	return linux_af_alg_skcipher("ecb(aes)", key, len);
+}
+
+
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+	struct linux_af_alg_skcipher *skcipher = ctx;
+
+	return linux_af_alg_skcipher_oper(skcipher, 0, crypt, plain);
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+	linux_af_alg_skcipher_deinit(ctx);
+}
+
+
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+	     u8 *data, size_t data_len)
+{
+	struct linux_af_alg_skcipher *skcipher;
+	u8 *skip_buf;
+	char buf[CMSG_SPACE(sizeof(u32))];
+	struct iovec io[2];
+	struct msghdr msg;
+	struct cmsghdr *hdr;
+	ssize_t ret;
+	u32 *op;
+
+	skip_buf = os_zalloc(skip + 1);
+	if (!skip_buf)
+		return -1;
+	skcipher = linux_af_alg_skcipher("ecb(arc4)", key, keylen);
+	if (!skcipher) {
+		os_free(skip_buf);
+		return -1;
+	}
+
+	io[0].iov_base = skip_buf;
+	io[0].iov_len = skip;
+	io[1].iov_base = data;
+	io[1].iov_len = data_len;
+	os_memset(&msg, 0, sizeof(msg));
+	os_memset(buf, 0, sizeof(buf));
+	msg.msg_control = buf;
+	msg.msg_controllen = CMSG_SPACE(sizeof(u32));
+	msg.msg_iov = io;
+	msg.msg_iovlen = 2;
+	hdr = CMSG_FIRSTHDR(&msg);
+	hdr->cmsg_level = SOL_ALG;
+	hdr->cmsg_type = ALG_SET_OP;
+	hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+	op = (u32 *) CMSG_DATA(hdr);
+	*op = ALG_OP_ENCRYPT;
+
+	ret = sendmsg(skcipher->t, &msg, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+			   __func__, strerror(errno));
+		os_free(skip_buf);
+		linux_af_alg_skcipher_deinit(skcipher);
+		return -1;
+	}
+	os_free(skip_buf);
+
+	msg.msg_control = NULL;
+	msg.msg_controllen = 0;
+	ret = recvmsg(skcipher->t, &msg, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: recvmsg failed: %s",
+			   __func__, strerror(errno));
+		linux_af_alg_skcipher_deinit(skcipher);
+		return -1;
+	}
+	linux_af_alg_skcipher_deinit(skcipher);
+
+	if ((size_t) ret < skip + data_len) {
+		wpa_printf(MSG_ERROR,
+			   "%s: recvmsg did not return full data (%d/%d)",
+			   __func__, (int) ret, (int) (skip + data_len));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+	u8 pkey[8], next, tmp;
+	int i;
+	struct linux_af_alg_skcipher *skcipher;
+	char buf[CMSG_SPACE(sizeof(u32))];
+	struct iovec io[1];
+	struct msghdr msg;
+	struct cmsghdr *hdr;
+	ssize_t ret;
+	u32 *op;
+	int res = -1;
+
+	/* Add parity bits to the key */
+	next = 0;
+	for (i = 0; i < 7; i++) {
+		tmp = key[i];
+		pkey[i] = (tmp >> i) | next | 1;
+		next = tmp << (7 - i);
+	}
+	pkey[i] = next | 1;
+
+	skcipher = linux_af_alg_skcipher("ecb(des)", pkey, sizeof(pkey));
+	if (!skcipher)
+		goto fail;
+
+	io[0].iov_base = (void *) clear;
+	io[0].iov_len = 8;
+	os_memset(&msg, 0, sizeof(msg));
+	os_memset(buf, 0, sizeof(buf));
+	msg.msg_control = buf;
+	msg.msg_controllen = CMSG_SPACE(sizeof(u32));
+	msg.msg_iov = io;
+	msg.msg_iovlen = 1;
+	hdr = CMSG_FIRSTHDR(&msg);
+	hdr->cmsg_level = SOL_ALG;
+	hdr->cmsg_type = ALG_SET_OP;
+	hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+	op = (u32 *) CMSG_DATA(hdr);
+	*op = ALG_OP_ENCRYPT;
+
+	ret = sendmsg(skcipher->t, &msg, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+			   __func__, strerror(errno));
+		goto fail;
+	}
+
+	ret = read(skcipher->t, cypher, 8);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: read failed: %s",
+			   __func__, strerror(errno));
+		goto fail;
+	}
+	if (ret < 8) {
+		wpa_printf(MSG_ERROR,
+			   "%s: read did not return full data (%d/8)",
+			   __func__, (int) ret);
+		goto fail;
+	}
+
+	res = 0;
+fail:
+	linux_af_alg_skcipher_deinit(skcipher);
+	return res;
+}
+
+
+static int aes_128_cbc_oper(const u8 *key, int enc, const u8 *iv,
+			    u8 *data, size_t data_len)
+{
+	struct linux_af_alg_skcipher *skcipher;
+	char buf[100];
+	struct iovec io[1];
+	struct msghdr msg;
+	struct cmsghdr *hdr;
+	ssize_t ret;
+	u32 *op;
+	struct af_alg_iv *alg_iv;
+	size_t iv_len = AES_BLOCK_SIZE;
+
+	skcipher = linux_af_alg_skcipher("cbc(aes)", key, 16);
+	if (!skcipher)
+		return -1;
+
+	io[0].iov_base = (void *) data;
+	io[0].iov_len = data_len;
+	os_memset(&msg, 0, sizeof(msg));
+	os_memset(buf, 0, sizeof(buf));
+	msg.msg_control = buf;
+	msg.msg_controllen = CMSG_SPACE(sizeof(u32)) +
+		CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+	msg.msg_iov = io;
+	msg.msg_iovlen = 1;
+
+	hdr = CMSG_FIRSTHDR(&msg);
+	hdr->cmsg_level = SOL_ALG;
+	hdr->cmsg_type = ALG_SET_OP;
+	hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+	op = (u32 *) CMSG_DATA(hdr);
+	*op = enc ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT;
+
+	hdr = CMSG_NXTHDR(&msg, hdr);
+	hdr->cmsg_level = SOL_ALG;
+	hdr->cmsg_type = ALG_SET_IV;
+	hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+	alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr);
+	alg_iv->ivlen = iv_len;
+	os_memcpy(alg_iv->iv, iv, iv_len);
+
+	ret = sendmsg(skcipher->t, &msg, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+			   __func__, strerror(errno));
+		linux_af_alg_skcipher_deinit(skcipher);
+		return -1;
+	}
+
+	ret = recvmsg(skcipher->t, &msg, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: recvmsg failed: %s",
+			   __func__, strerror(errno));
+		linux_af_alg_skcipher_deinit(skcipher);
+		return -1;
+	}
+	if ((size_t) ret < data_len) {
+		wpa_printf(MSG_ERROR,
+			   "%s: recvmsg not return full data (%d/%d)",
+			   __func__, (int) ret, (int) data_len);
+		linux_af_alg_skcipher_deinit(skcipher);
+		return -1;
+	}
+
+	linux_af_alg_skcipher_deinit(skcipher);
+	return 0;
+}
+
+
+int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+	return aes_128_cbc_oper(key, 1, iv, data, data_len);
+}
+
+
+int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+	return aes_128_cbc_oper(key, 0, iv, data, data_len);
+}
+
+
+int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return linux_af_alg_hash_vector("cmac(aes)", key, key_len, num_elem,
+					addr, len, mac, AES_BLOCK_SIZE);
+}
+
+
+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
+			 const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
+}
+
+
+int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+	return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
+}
+
+
+int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+	return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
+}
+
+
+int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
+	       u8 *plain)
+{
+	struct linux_af_alg_skcipher *skcipher;
+	char buf[100];
+	struct iovec io[1];
+	struct msghdr msg;
+	struct cmsghdr *hdr;
+	ssize_t ret;
+	u32 *op;
+	struct af_alg_iv *alg_iv;
+	size_t iv_len = 8;
+
+	skcipher = linux_af_alg_skcipher("kw(aes)", kek, kek_len);
+	if (!skcipher)
+		return -1;
+
+	io[0].iov_base = (void *) (cipher + iv_len);
+	io[0].iov_len = n * 8;
+	os_memset(&msg, 0, sizeof(msg));
+	os_memset(buf, 0, sizeof(buf));
+	msg.msg_control = buf;
+	msg.msg_controllen = CMSG_SPACE(sizeof(u32)) +
+		CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+	msg.msg_iov = io;
+	msg.msg_iovlen = 1;
+
+	hdr = CMSG_FIRSTHDR(&msg);
+	hdr->cmsg_level = SOL_ALG;
+	hdr->cmsg_type = ALG_SET_OP;
+	hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+	op = (u32 *) CMSG_DATA(hdr);
+	*op = ALG_OP_DECRYPT;
+
+	hdr = CMSG_NXTHDR(&msg, hdr);
+	hdr->cmsg_level = SOL_ALG;
+	hdr->cmsg_type = ALG_SET_IV;
+	hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+	alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr);
+	alg_iv->ivlen = iv_len;
+	os_memcpy(alg_iv->iv, cipher, iv_len);
+
+	ret = sendmsg(skcipher->t, &msg, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+
+	ret = read(skcipher->t, plain, n * 8);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: read failed: %s",
+			   __func__, strerror(errno));
+		linux_af_alg_skcipher_deinit(skcipher);
+		return -1;
+	}
+	if (ret < n * 8) {
+		wpa_printf(MSG_ERROR,
+			   "%s: read not return full data (%d/%d)",
+			   __func__, (int) ret, n * 8);
+		linux_af_alg_skcipher_deinit(skcipher);
+		return -1;
+	}
+
+	linux_af_alg_skcipher_deinit(skcipher);
+	return 0;
+}
+
+
+struct crypto_cipher {
+	struct linux_af_alg_skcipher *skcipher;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+					  const u8 *iv, const u8 *key,
+					  size_t key_len)
+{
+	struct crypto_cipher *ctx;
+	const char *name;
+	struct af_alg_iv *alg_iv;
+	size_t iv_len = 0;
+	char buf[100];
+	struct msghdr msg;
+	struct cmsghdr *hdr;
+	ssize_t ret;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (!ctx)
+		return NULL;
+
+	switch (alg) {
+	case CRYPTO_CIPHER_ALG_RC4:
+		name = "ecb(arc4)";
+		break;
+	case CRYPTO_CIPHER_ALG_AES:
+		name = "cbc(aes)";
+		iv_len = AES_BLOCK_SIZE;
+		break;
+	case CRYPTO_CIPHER_ALG_3DES:
+		name = "cbc(des3_ede)";
+		iv_len = 8;
+		break;
+	case CRYPTO_CIPHER_ALG_DES:
+		name = "cbc(des)";
+		iv_len = 8;
+		break;
+	default:
+		os_free(ctx);
+		return NULL;
+	}
+
+	ctx->skcipher = linux_af_alg_skcipher(name, key, key_len);
+	if (!ctx->skcipher) {
+		os_free(ctx);
+		return NULL;
+	}
+
+	if (iv && iv_len) {
+		os_memset(&msg, 0, sizeof(msg));
+		os_memset(buf, 0, sizeof(buf));
+		msg.msg_control = buf;
+		msg.msg_controllen = CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+		hdr = CMSG_FIRSTHDR(&msg);
+		hdr->cmsg_level = SOL_ALG;
+		hdr->cmsg_type = ALG_SET_IV;
+		hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+		alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr);
+		alg_iv->ivlen = iv_len;
+		os_memcpy(alg_iv->iv, iv, iv_len);
+
+		ret = sendmsg(ctx->skcipher->t, &msg, 0);
+		if (ret < 0) {
+			wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+				   __func__, strerror(errno));
+			linux_af_alg_skcipher_deinit(ctx->skcipher);
+			os_free(ctx);
+			return NULL;
+		}
+	}
+
+	return ctx;
+}
+
+
+static int crypto_cipher_oper(struct crypto_cipher *ctx, u32 type, const u8 *in,
+			      u8 *out, size_t len)
+{
+	char buf[CMSG_SPACE(sizeof(u32))];
+	struct iovec io[1];
+	struct msghdr msg;
+	struct cmsghdr *hdr;
+	ssize_t ret;
+	u32 *op;
+
+	io[0].iov_base = (void *) in;
+	io[0].iov_len = len;
+	os_memset(&msg, 0, sizeof(msg));
+	os_memset(buf, 0, sizeof(buf));
+	msg.msg_control = buf;
+	msg.msg_controllen = CMSG_SPACE(sizeof(u32));
+	msg.msg_iov = io;
+	msg.msg_iovlen = 1;
+	hdr = CMSG_FIRSTHDR(&msg);
+	hdr->cmsg_level = SOL_ALG;
+	hdr->cmsg_type = ALG_SET_OP;
+	hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+	op = (u32 *) CMSG_DATA(hdr);
+	*op = type;
+
+	ret = sendmsg(ctx->skcipher->t, &msg, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+
+	ret = read(ctx->skcipher->t, out, len);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: read failed: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+	if (ret < (ssize_t) len) {
+		wpa_printf(MSG_ERROR,
+			   "%s: read did not return full data (%d/%d)",
+			   __func__, (int) ret, (int) len);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+			  u8 *crypt, size_t len)
+{
+	return crypto_cipher_oper(ctx, ALG_OP_ENCRYPT, plain, crypt, len);
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+			  u8 *plain, size_t len)
+{
+	return crypto_cipher_oper(ctx, ALG_OP_DECRYPT, crypt, plain, len);
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+	if (ctx) {
+		linux_af_alg_skcipher_deinit(ctx->skcipher);
+		os_free(ctx);
+	}
+}
+
+
+int crypto_global_init(void)
+{
+	return 0;
+}
+
+
+void crypto_global_deinit(void)
+{
+}
diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c
index 0fa06d9..1cc73d8 100644
--- a/src/crypto/crypto_module_tests.c
+++ b/src/crypto/crypto_module_tests.c
@@ -1912,6 +1912,135 @@
 }
 
 
+static int test_extract_expand_hkdf(void)
+{
+	u8 prk[SHA256_MAC_LEN];
+	u8 okm[82];
+
+	/* RFC 5869, A.1 */
+	u8 ikm1[22] = {
+		0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+		0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+		0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b
+	};
+	u8 salt1[13] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b, 0x0c
+	};
+	u8 info1[10] = {
+		0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+		0xf8, 0xf9
+	};
+	u8 prk1[32] = {
+		0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf,
+		0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63,
+		0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31,
+		0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5
+	};
+	u8 okm1[42] = {
+		0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a,
+		0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a,
+		0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c,
+		0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf,
+		0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18,
+		0x58, 0x65
+	};
+
+	/* RFC 5869, A.2 */
+	u8 ikm2[80] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+		0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+		0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+		0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+		0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+		0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+		0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+		0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f
+	};
+	u8 salt2[80] = {
+		0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+		0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+		0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+		0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+		0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+		0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+		0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+		0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+		0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+		0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf
+	};
+	u8 info2[80] = {
+		0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+		0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+		0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+		0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+		0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+		0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+		0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+		0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+		0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+		0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+	};
+	u8 prk2[32] = {
+		0x06, 0xa6, 0xb8, 0x8c, 0x58, 0x53, 0x36, 0x1a,
+		0x06, 0x10, 0x4c, 0x9c, 0xeb, 0x35, 0xb4, 0x5c,
+		0xef, 0x76, 0x00, 0x14, 0x90, 0x46, 0x71, 0x01,
+		0x4a, 0x19, 0x3f, 0x40, 0xc1, 0x5f, 0xc2, 0x44
+	};
+	u8 okm2[82] = {
+		0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1,
+		0xc8, 0xe7, 0xf7, 0x8c, 0x59, 0x6a, 0x49, 0x34,
+		0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e, 0xfa, 0xd8,
+		0xa0, 0x50, 0xcc, 0x4c, 0x19, 0xaf, 0xa9, 0x7c,
+		0x59, 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72,
+		0x71, 0xcb, 0x41, 0xc6, 0x5e, 0x59, 0x0e, 0x09,
+		0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8,
+		0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71,
+		0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec, 0x3e, 0x87,
+		0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f,
+		0x1d, 0x87
+	};
+
+	wpa_printf(MSG_INFO, "Testing Extract-and-Expand HKDF (RFC 5869)");
+
+	wpa_printf(MSG_INFO, "RFC 5869 - Test Case 1");
+	if (hmac_sha256(salt1, sizeof(salt1), ikm1, sizeof(ikm1), prk) < 0)
+		return -1;
+	if (os_memcmp(prk, prk1, SHA256_MAC_LEN) != 0) {
+		wpa_printf(MSG_INFO, "HKDF-Extract mismatch in PRK");
+		return -1;
+	}
+	if (hmac_sha256_kdf(prk1, sizeof(prk1), NULL, info1, sizeof(info1),
+			    okm, sizeof(okm1)) < 0)
+		return -1;
+	if (os_memcmp(okm, okm1, sizeof(okm1)) != 0) {
+		wpa_printf(MSG_INFO, "HKDF-Expand mismatch in OKM");
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "RFC 5869 - Test Case 2");
+	if (hmac_sha256(salt2, sizeof(salt2), ikm2, sizeof(ikm2), prk) < 0)
+		return -1;
+	if (os_memcmp(prk, prk2, SHA256_MAC_LEN) != 0) {
+		wpa_printf(MSG_INFO, "HKDF-Extract mismatch in PRK");
+		return -1;
+	}
+	if (hmac_sha256_kdf(prk2, sizeof(prk2), NULL, info2, sizeof(info2),
+			    okm, sizeof(okm2)) < 0)
+		return -1;
+	if (os_memcmp(okm, okm2, sizeof(okm2)) != 0) {
+		wpa_printf(MSG_INFO, "HKDF-Expand mismatch in OKM");
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "Extract-and-Expand HKDF test cases passed");
+
+	return 0;
+}
+
+
 static int test_ms_funcs(void)
 {
 #ifndef CONFIG_FIPS
@@ -2030,6 +2159,7 @@
 	    test_sha256() ||
 	    test_sha384() ||
 	    test_fips186_2_prf() ||
+	    test_extract_expand_hkdf() ||
 	    test_ms_funcs())
 		ret = -1;
 
diff --git a/src/crypto/crypto_none.c b/src/crypto/crypto_none.c
index 011f3f3..5479194 100644
--- a/src/crypto/crypto_none.c
+++ b/src/crypto/crypto_none.c
@@ -18,6 +18,7 @@
 }
 
 
-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
 {
+	return 0;
 }
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 5f7896c..6bff202 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -1,6 +1,6 @@
 /*
  * Wrapper functions for OpenSSL libcrypto
- * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -161,7 +161,7 @@
 #endif /* CONFIG_FIPS */
 
 
-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
 {
 	u8 pkey[8], next, tmp;
 	int i;
@@ -179,6 +179,7 @@
 	DES_set_key((DES_cblock *) &pkey, &ks);
 	DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks,
 			DES_ENCRYPT);
+	return 0;
 }
 
 
@@ -245,6 +246,7 @@
 }
 #endif /* NO_SHA256_WRAPPER */
 
+
 #ifndef NO_SHA384_WRAPPER
 int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
 		  u8 *mac)
@@ -254,6 +256,15 @@
 #endif /* NO_SHA384_WRAPPER */
 
 
+#ifndef NO_SHA512_WRAPPER
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	return openssl_digest_vector(EVP_sha512(), num_elem, addr, len, mac);
+}
+#endif /* NO_SHA512_WRAPPER */
+
+
 static const EVP_CIPHER * aes_get_evp_cipher(size_t keylen)
 {
 	switch (keylen) {
@@ -295,14 +306,16 @@
 }
 
 
-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
 {
 	EVP_CIPHER_CTX *c = ctx;
 	int clen = 16;
 	if (EVP_EncryptUpdate(c, crypt, &clen, plain, 16) != 1) {
 		wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptUpdate failed: %s",
 			   ERR_error_string(ERR_get_error(), NULL));
+		return -1;
 	}
+	return 0;
 }
 
 
@@ -347,14 +360,16 @@
 }
 
 
-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
 {
 	EVP_CIPHER_CTX *c = ctx;
 	int plen = 16;
 	if (EVP_DecryptUpdate(c, plain, &plen, crypt, 16) != 1) {
 		wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptUpdate failed: %s",
 			   ERR_error_string(ERR_get_error(), NULL));
+		return -1;
 	}
+	return 0;
 }
 
 
@@ -1044,6 +1059,25 @@
 #endif /* CONFIG_SHA384 */
 
 
+#ifdef CONFIG_SHA512
+
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_hmac_vector(EVP_sha512(), key, key_len, num_elem, addr,
+				   len, mac, 64);
+}
+
+
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA512 */
+
+
 int crypto_get_random(void *buf, size_t len)
 {
 	if (RAND_bytes(buf, len) != 1)
@@ -1358,6 +1392,7 @@
 
 struct crypto_ec {
 	EC_GROUP *group;
+	int nid;
 	BN_CTX *bnctx;
 	BIGNUM *prime;
 	BIGNUM *order;
@@ -1415,6 +1450,7 @@
 	if (e == NULL)
 		return NULL;
 
+	e->nid = nid;
 	e->bnctx = BN_CTX_new();
 	e->group = EC_GROUP_new_by_curve_name(nid);
 	e->prime = BN_new();
@@ -1655,4 +1691,239 @@
 			    (const EC_POINT *) b, e->bnctx);
 }
 
+
+struct crypto_ecdh {
+	struct crypto_ec *ec;
+	EVP_PKEY *pkey;
+};
+
+struct crypto_ecdh * crypto_ecdh_init(int group)
+{
+	struct crypto_ecdh *ecdh;
+	EVP_PKEY *params = NULL;
+	EVP_PKEY_CTX *pctx = NULL;
+	EVP_PKEY_CTX *kctx = NULL;
+
+	ecdh = os_zalloc(sizeof(*ecdh));
+	if (!ecdh)
+		goto fail;
+
+	ecdh->ec = crypto_ec_init(group);
+	if (!ecdh->ec)
+		goto fail;
+
+	pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
+	if (!pctx)
+		goto fail;
+
+	if (EVP_PKEY_paramgen_init(pctx) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: EVP_PKEY_paramgen_init failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, ecdh->ec->nid) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: EVP_PKEY_CTX_set_ec_paramgen_curve_nid failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	if (EVP_PKEY_paramgen(pctx, &params) != 1) {
+		wpa_printf(MSG_ERROR, "OpenSSL: EVP_PKEY_paramgen failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	kctx = EVP_PKEY_CTX_new(params, NULL);
+	if (!kctx)
+		goto fail;
+
+	if (EVP_PKEY_keygen_init(kctx) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: EVP_PKEY_keygen_init failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	if (EVP_PKEY_keygen(kctx, &ecdh->pkey) != 1) {
+		wpa_printf(MSG_ERROR, "OpenSSL: EVP_PKEY_keygen failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+done:
+	EVP_PKEY_free(params);
+	EVP_PKEY_CTX_free(pctx);
+	EVP_PKEY_CTX_free(kctx);
+
+	return ecdh;
+fail:
+	crypto_ecdh_deinit(ecdh);
+	ecdh = NULL;
+	goto done;
+}
+
+
+struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y)
+{
+	struct wpabuf *buf = NULL;
+	EC_KEY *eckey;
+	const EC_POINT *pubkey;
+	BIGNUM *x, *y = NULL;
+	int len = BN_num_bytes(ecdh->ec->prime);
+	int res;
+
+	eckey = EVP_PKEY_get1_EC_KEY(ecdh->pkey);
+	if (!eckey)
+		return NULL;
+
+	pubkey = EC_KEY_get0_public_key(eckey);
+	if (!pubkey)
+		return NULL;
+
+	x = BN_new();
+	if (inc_y) {
+		y = BN_new();
+		if (!y)
+			goto fail;
+	}
+	buf = wpabuf_alloc(inc_y ? 2 * len : len);
+	if (!x || !buf)
+		goto fail;
+
+	if (EC_POINT_get_affine_coordinates_GFp(ecdh->ec->group, pubkey,
+						x, y, ecdh->ec->bnctx) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: EC_POINT_get_affine_coordinates_GFp failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	res = crypto_bignum_to_bin((struct crypto_bignum *) x,
+				   wpabuf_put(buf, len), len, len);
+	if (res < 0)
+		goto fail;
+
+	if (inc_y) {
+		res = crypto_bignum_to_bin((struct crypto_bignum *) y,
+					   wpabuf_put(buf, len), len, len);
+		if (res < 0)
+			goto fail;
+	}
+
+done:
+	BN_clear_free(x);
+	BN_clear_free(y);
+	EC_KEY_free(eckey);
+
+	return buf;
+fail:
+	wpabuf_free(buf);
+	buf = NULL;
+	goto done;
+}
+
+
+struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
+					const u8 *key, size_t len)
+{
+	BIGNUM *x, *y = NULL;
+	EVP_PKEY_CTX *ctx = NULL;
+	EVP_PKEY *peerkey = NULL;
+	struct wpabuf *secret = NULL;
+	size_t secret_len;
+	EC_POINT *pub;
+	EC_KEY *eckey = NULL;
+
+	x = BN_bin2bn(key, inc_y ? len / 2 : len, NULL);
+	pub = EC_POINT_new(ecdh->ec->group);
+	if (!x || !pub)
+		goto fail;
+
+	if (inc_y) {
+		y = BN_bin2bn(key + len / 2, len / 2, NULL);
+		if (!y)
+			goto fail;
+		if (!EC_POINT_set_affine_coordinates_GFp(ecdh->ec->group, pub,
+							 x, y,
+							 ecdh->ec->bnctx)) {
+			wpa_printf(MSG_ERROR,
+				   "OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s",
+				   ERR_error_string(ERR_get_error(), NULL));
+			goto fail;
+		}
+	} else if (!EC_POINT_set_compressed_coordinates_GFp(ecdh->ec->group,
+							    pub, x, 0,
+							    ecdh->ec->bnctx)) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: EC_POINT_set_compressed_coordinates_GFp failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	if (!EC_POINT_is_on_curve(ecdh->ec->group, pub, ecdh->ec->bnctx)) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: ECDH peer public key is not on curve");
+		goto fail;
+	}
+
+	eckey = EC_KEY_new_by_curve_name(ecdh->ec->nid);
+	if (!eckey || EC_KEY_set_public_key(eckey, pub) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: EC_KEY_set_public_key failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	peerkey = EVP_PKEY_new();
+	if (!peerkey || EVP_PKEY_set1_EC_KEY(peerkey, eckey) != 1)
+		goto fail;
+
+	ctx = EVP_PKEY_CTX_new(ecdh->pkey, NULL);
+	if (!ctx || EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, peerkey) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &secret_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: EVP_PKEY_derive(1) failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	secret = wpabuf_alloc(secret_len);
+	if (!secret)
+		goto fail;
+	if (EVP_PKEY_derive(ctx, wpabuf_put(secret, secret_len),
+			    &secret_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: EVP_PKEY_derive(2) failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+done:
+	BN_free(x);
+	BN_free(y);
+	EC_KEY_free(eckey);
+	EC_POINT_free(pub);
+	EVP_PKEY_CTX_free(ctx);
+	EVP_PKEY_free(peerkey);
+	return secret;
+fail:
+	wpabuf_free(secret);
+	secret = NULL;
+	goto done;
+}
+
+
+void crypto_ecdh_deinit(struct crypto_ecdh *ecdh)
+{
+	if (ecdh) {
+		crypto_ec_deinit(ecdh->ec);
+		EVP_PKEY_free(ecdh->pkey);
+		os_free(ecdh);
+	}
+}
+
 #endif /* CONFIG_ECC */
diff --git a/src/crypto/des-internal.c b/src/crypto/des-internal.c
index ebd9952..4ed6957 100644
--- a/src/crypto/des-internal.c
+++ b/src/crypto/des-internal.c
@@ -396,7 +396,7 @@
 
 /* wpa_supplicant/hostapd specific wrapper */
 
-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
 {
 	u8 pkey[8], next, tmp;
 	int i;
@@ -421,6 +421,7 @@
 
 	os_memset(pkey, 0, sizeof(pkey));
 	os_memset(ek, 0, sizeof(ek));
+	return 0;
 }
 
 
diff --git a/src/crypto/ms_funcs.c b/src/crypto/ms_funcs.c
index d0d6a96..aff7d33 100644
--- a/src/crypto/ms_funcs.c
+++ b/src/crypto/ms_funcs.c
@@ -140,17 +140,20 @@
  * @challenge: 8-octet Challenge (IN)
  * @password_hash: 16-octet PasswordHash (IN)
  * @response: 24-octet Response (OUT)
+ * Returns: 0 on success, -1 on failure
  */
-void challenge_response(const u8 *challenge, const u8 *password_hash,
-			u8 *response)
+int challenge_response(const u8 *challenge, const u8 *password_hash,
+		       u8 *response)
 {
 	u8 zpwd[7];
-	des_encrypt(challenge, password_hash, response);
-	des_encrypt(challenge, password_hash + 7, response + 8);
+
+	if (des_encrypt(challenge, password_hash, response) < 0 ||
+	    des_encrypt(challenge, password_hash + 7, response + 8) < 0)
+		return -1;
 	zpwd[0] = password_hash[14];
 	zpwd[1] = password_hash[15];
 	os_memset(zpwd + 2, 0, 5);
-	des_encrypt(challenge, zpwd, response + 16);
+	return des_encrypt(challenge, zpwd, response + 16);
 }
 
 
@@ -175,9 +178,9 @@
 
 	if (challenge_hash(peer_challenge, auth_challenge, username,
 			   username_len, challenge) ||
-	    nt_password_hash(password, password_len, password_hash))
+	    nt_password_hash(password, password_len, password_hash) ||
+	    challenge_response(challenge, password_hash, response))
 		return -1;
-	challenge_response(challenge, password_hash, response);
 	return 0;
 }
 
@@ -202,9 +205,9 @@
 
 	if (challenge_hash(peer_challenge, auth_challenge,
 			   username, username_len,
-			   challenge))
+			   challenge) ||
+	    challenge_response(challenge, password_hash, response))
 		return -1;
-	challenge_response(challenge, password_hash, response);
 	return 0;
 }
 
@@ -304,9 +307,10 @@
 			  size_t password_len, u8 *response)
 {
 	u8 password_hash[16];
-	if (nt_password_hash(password, password_len, password_hash))
+
+	if (nt_password_hash(password, password_len, password_hash) ||
+	    challenge_response(challenge, password_hash, response))
 		return -1;
-	challenge_response(challenge, password_hash, response);
 	return 0;
 }
 
@@ -487,12 +491,15 @@
  * @password_hash: 16-octer PasswordHash (IN)
  * @block: 16-octet Block (IN)
  * @cypher: 16-octer Cypher (OUT)
+ * Returns: 0 on success, -1 on failure
  */
-void nt_password_hash_encrypted_with_block(const u8 *password_hash,
-					   const u8 *block, u8 *cypher)
+int nt_password_hash_encrypted_with_block(const u8 *password_hash,
+					  const u8 *block, u8 *cypher)
 {
-	des_encrypt(password_hash, block, cypher);
-	des_encrypt(password_hash + 8, block + 7, cypher + 8);
+	if (des_encrypt(password_hash, block, cypher) < 0 ||
+	    des_encrypt(password_hash + 8, block + 7, cypher + 8) < 0)
+		return -1;
+	return 0;
 }
 
 
@@ -515,10 +522,10 @@
 	if (nt_password_hash(old_password, old_password_len,
 			     old_password_hash) ||
 	    nt_password_hash(new_password, new_password_len,
-			     new_password_hash))
+			     new_password_hash) ||
+	    nt_password_hash_encrypted_with_block(old_password_hash,
+						  new_password_hash,
+						  encrypted_password_hash))
 		return -1;
-	nt_password_hash_encrypted_with_block(old_password_hash,
-					      new_password_hash,
-					      encrypted_password_hash);
 	return 0;
 }
diff --git a/src/crypto/ms_funcs.h b/src/crypto/ms_funcs.h
index b5b5918..b8d55f0 100644
--- a/src/crypto/ms_funcs.h
+++ b/src/crypto/ms_funcs.h
@@ -31,8 +31,8 @@
 int nt_challenge_response(const u8 *challenge, const u8 *password,
 			  size_t password_len, u8 *response);
 
-void challenge_response(const u8 *challenge, const u8 *password_hash,
-			u8 *response);
+int challenge_response(const u8 *challenge, const u8 *password_hash,
+		       u8 *response);
 int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
 		   const u8 *username, size_t username_len, u8 *challenge);
 int nt_password_hash(const u8 *password, size_t password_len,
@@ -50,8 +50,8 @@
 	const u8 *new_password, size_t new_password_len,
 	const u8 *old_password, size_t old_password_len,
 	u8 *encrypted_pw_block);
-void nt_password_hash_encrypted_with_block(const u8 *password_hash,
-					   const u8 *block, u8 *cypher);
+int nt_password_hash_encrypted_with_block(const u8 *password_hash,
+					  const u8 *block, u8 *cypher);
 int old_nt_password_hash_encrypted_with_new_nt_password_hash(
 	const u8 *new_password, size_t new_password_len,
 	const u8 *old_password, size_t old_password_len,
diff --git a/src/crypto/random.c b/src/crypto/random.c
index 3a86a93..fb92417 100644
--- a/src/crypto/random.c
+++ b/src/crypto/random.c
@@ -66,6 +66,9 @@
 
 static u32 __ROL32(u32 x, u32 y)
 {
+	if (y == 0)
+		return x;
+
 	return (x << (y & 31)) | (x >> (32 - (y & 31)));
 }
 
diff --git a/src/crypto/sha256-kdf.c b/src/crypto/sha256-kdf.c
index e7509ce..af7d954 100644
--- a/src/crypto/sha256-kdf.c
+++ b/src/crypto/sha256-kdf.c
@@ -1,6 +1,6 @@
 /*
- * HMAC-SHA256 KDF (RFC 5295)
- * Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+ * HMAC-SHA256 KDF (RFC 5295) and HKDF-Expand(SHA256) (RFC 5869)
+ * Copyright (c) 2014-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -16,7 +16,8 @@
  * hmac_sha256_kdf - HMAC-SHA256 based KDF (RFC 5295)
  * @secret: Key for KDF
  * @secret_len: Length of the key in bytes
- * @label: A unique label for each purpose of the KDF
+ * @label: A unique label for each purpose of the KDF or %NULL to select
+ *	RFC 5869 HKDF-Expand() with arbitrary seed (= info)
  * @seed: Seed value to bind into the key
  * @seed_len: Length of the seed
  * @out: Buffer for the generated pseudo-random key
@@ -24,7 +25,9 @@
  * Returns: 0 on success, -1 on failure.
  *
  * This function is used to derive new, cryptographically separate keys from a
- * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2.
+ * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used
+ * with label = NULL and seed = info, this matches HKDF-Expand() defined in
+ * RFC 5869, Chapter 2.3.
  */
 int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
 		    const char *label, const u8 *seed, size_t seed_len,
@@ -38,8 +41,13 @@
 
 	addr[0] = T;
 	len[0] = SHA256_MAC_LEN;
-	addr[1] = (const unsigned char *) label;
-	len[1] = os_strlen(label) + 1;
+	if (label) {
+		addr[1] = (const unsigned char *) label;
+		len[1] = os_strlen(label) + 1;
+	} else {
+		addr[1] = (const u8 *) "";
+		len[1] = 0;
+	}
 	addr[2] = seed;
 	len[2] = seed_len;
 	addr[3] = &iter;
diff --git a/src/crypto/sha384-kdf.c b/src/crypto/sha384-kdf.c
new file mode 100644
index 0000000..1d19627
--- /dev/null
+++ b/src/crypto/sha384-kdf.c
@@ -0,0 +1,87 @@
+/*
+ * HMAC-SHA384 KDF (RFC 5295) and HKDF-Expand(SHA384) (RFC 5869)
+ * Copyright (c) 2014-2017, 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 "common.h"
+#include "sha384.h"
+
+
+/**
+ * hmac_sha384_kdf - HMAC-SHA384 based KDF (RFC 5295)
+ * @secret: Key for KDF
+ * @secret_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the KDF or %NULL to select
+ *	RFC 5869 HKDF-Expand() with arbitrary seed (= info)
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used
+ * with label = NULL and seed = info, this matches HKDF-Expand() defined in
+ * RFC 5869, Chapter 2.3.
+ */
+int hmac_sha384_kdf(const u8 *secret, size_t secret_len,
+		    const char *label, const u8 *seed, size_t seed_len,
+		    u8 *out, size_t outlen)
+{
+	u8 T[SHA384_MAC_LEN];
+	u8 iter = 1;
+	const unsigned char *addr[4];
+	size_t len[4];
+	size_t pos, clen;
+
+	addr[0] = T;
+	len[0] = SHA384_MAC_LEN;
+	if (label) {
+		addr[1] = (const unsigned char *) label;
+		len[1] = os_strlen(label) + 1;
+	} else {
+		addr[1] = (const u8 *) "";
+		len[1] = 0;
+	}
+	addr[2] = seed;
+	len[2] = seed_len;
+	addr[3] = &iter;
+	len[3] = 1;
+
+	if (hmac_sha384_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0)
+		return -1;
+
+	pos = 0;
+	for (;;) {
+		clen = outlen - pos;
+		if (clen > SHA384_MAC_LEN)
+			clen = SHA384_MAC_LEN;
+		os_memcpy(out + pos, T, clen);
+		pos += clen;
+
+		if (pos == outlen)
+			break;
+
+		if (iter == 255) {
+			os_memset(out, 0, outlen);
+			os_memset(T, 0, SHA384_MAC_LEN);
+			return -1;
+		}
+		iter++;
+
+		if (hmac_sha384_vector(secret, secret_len, 4, addr, len, T) < 0)
+		{
+			os_memset(out, 0, outlen);
+			os_memset(T, 0, SHA384_MAC_LEN);
+			return -1;
+		}
+	}
+
+	os_memset(T, 0, SHA384_MAC_LEN);
+	return 0;
+}
diff --git a/src/crypto/sha384-prf.c b/src/crypto/sha384-prf.c
index 653920b..03e3cb3 100644
--- a/src/crypto/sha384-prf.c
+++ b/src/crypto/sha384-prf.c
@@ -1,6 +1,6 @@
 /*
  * SHA384-based KDF (IEEE 802.11ac)
- * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -22,14 +22,16 @@
  * @data_len: Length of the data
  * @buf: Buffer for the generated pseudo-random key
  * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure
  *
  * This function is used to derive new, cryptographically separate keys from a
  * given key.
  */
-void sha384_prf(const u8 *key, size_t key_len, const char *label,
-		const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+int sha384_prf(const u8 *key, size_t key_len, const char *label,
+	       const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
 {
-	sha384_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8);
+	return sha384_prf_bits(key, key_len, label, data, data_len, buf,
+			       buf_len * 8);
 }
 
 
@@ -42,15 +44,16 @@
  * @data_len: Length of the data
  * @buf: Buffer for the generated pseudo-random key
  * @buf_len: Number of bits of key to generate
+ * Returns: 0 on success, -1 on failure
  *
  * This function is used to derive new, cryptographically separate keys from a
  * given key. If the requested buf_len is not divisible by eight, the least
  * significant 1-7 bits of the last octet in the output are not part of the
  * requested output.
  */
-void sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
-		     const u8 *data, size_t data_len, u8 *buf,
-		     size_t buf_len_bits)
+int sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
+		    const u8 *data, size_t data_len, u8 *buf,
+		    size_t buf_len_bits)
 {
 	u16 counter = 1;
 	size_t pos, plen;
@@ -75,11 +78,14 @@
 		plen = buf_len - pos;
 		WPA_PUT_LE16(counter_le, counter);
 		if (plen >= SHA384_MAC_LEN) {
-			hmac_sha384_vector(key, key_len, 4, addr, len,
-					   &buf[pos]);
+			if (hmac_sha384_vector(key, key_len, 4, addr, len,
+					       &buf[pos]) < 0)
+				return -1;
 			pos += SHA384_MAC_LEN;
 		} else {
-			hmac_sha384_vector(key, key_len, 4, addr, len, hash);
+			if (hmac_sha384_vector(key, key_len, 4, addr, len,
+					       hash) < 0)
+				return -1;
 			os_memcpy(&buf[pos], hash, plen);
 			pos += plen;
 			break;
@@ -97,4 +103,6 @@
 	}
 
 	os_memset(hash, 0, sizeof(hash));
+
+	return 0;
 }
diff --git a/src/crypto/sha384.h b/src/crypto/sha384.h
index 3deafa5..2241425 100644
--- a/src/crypto/sha384.h
+++ b/src/crypto/sha384.h
@@ -1,6 +1,6 @@
 /*
  * SHA384 hash implementation and interface functions
- * Copyright (c) 2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2015-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -15,10 +15,13 @@
 		       const u8 *addr[], const size_t *len, u8 *mac);
 int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
 		size_t data_len, u8 *mac);
-void sha384_prf(const u8 *key, size_t key_len, const char *label,
-		const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
-void sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
-		     const u8 *data, size_t data_len, u8 *buf,
-		     size_t buf_len_bits);
+int sha384_prf(const u8 *key, size_t key_len, const char *label,
+	       const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+int sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
+		    const u8 *data, size_t data_len, u8 *buf,
+		    size_t buf_len_bits);
+int hmac_sha384_kdf(const u8 *secret, size_t secret_len,
+		    const char *label, const u8 *seed, size_t seed_len,
+		    u8 *out, size_t outlen);
 
 #endif /* SHA384_H */
diff --git a/src/crypto/sha512-kdf.c b/src/crypto/sha512-kdf.c
new file mode 100644
index 0000000..8b71f9b
--- /dev/null
+++ b/src/crypto/sha512-kdf.c
@@ -0,0 +1,87 @@
+/*
+ * HMAC-SHA512 KDF (RFC 5295) and HKDF-Expand(SHA512) (RFC 5869)
+ * Copyright (c) 2014-2017, 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 "common.h"
+#include "sha512.h"
+
+
+/**
+ * hmac_sha512_kdf - HMAC-SHA512 based KDF (RFC 5295)
+ * @secret: Key for KDF
+ * @secret_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the KDF or %NULL to select
+ *	RFC 5869 HKDF-Expand() with arbitrary seed (= info)
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used
+ * with label = NULL and seed = info, this matches HKDF-Expand() defined in
+ * RFC 5869, Chapter 2.3.
+ */
+int hmac_sha512_kdf(const u8 *secret, size_t secret_len,
+		    const char *label, const u8 *seed, size_t seed_len,
+		    u8 *out, size_t outlen)
+{
+	u8 T[SHA512_MAC_LEN];
+	u8 iter = 1;
+	const unsigned char *addr[4];
+	size_t len[4];
+	size_t pos, clen;
+
+	addr[0] = T;
+	len[0] = SHA512_MAC_LEN;
+	if (label) {
+		addr[1] = (const unsigned char *) label;
+		len[1] = os_strlen(label) + 1;
+	} else {
+		addr[1] = (const u8 *) "";
+		len[1] = 0;
+	}
+	addr[2] = seed;
+	len[2] = seed_len;
+	addr[3] = &iter;
+	len[3] = 1;
+
+	if (hmac_sha512_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0)
+		return -1;
+
+	pos = 0;
+	for (;;) {
+		clen = outlen - pos;
+		if (clen > SHA512_MAC_LEN)
+			clen = SHA512_MAC_LEN;
+		os_memcpy(out + pos, T, clen);
+		pos += clen;
+
+		if (pos == outlen)
+			break;
+
+		if (iter == 255) {
+			os_memset(out, 0, outlen);
+			os_memset(T, 0, SHA512_MAC_LEN);
+			return -1;
+		}
+		iter++;
+
+		if (hmac_sha512_vector(secret, secret_len, 4, addr, len, T) < 0)
+		{
+			os_memset(out, 0, outlen);
+			os_memset(T, 0, SHA512_MAC_LEN);
+			return -1;
+		}
+	}
+
+	os_memset(T, 0, SHA512_MAC_LEN);
+	return 0;
+}
diff --git a/src/crypto/sha512-prf.c b/src/crypto/sha512-prf.c
new file mode 100644
index 0000000..3b2ad88
--- /dev/null
+++ b/src/crypto/sha512-prf.c
@@ -0,0 +1,108 @@
+/*
+ * SHA512-based KDF (IEEE 802.11ac)
+ * Copyright (c) 2003-2017, 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 "common.h"
+#include "sha512.h"
+#include "crypto.h"
+
+
+/**
+ * sha512_prf - SHA512-based Key derivation function (IEEE 802.11ac, 11.6.1.7.2)
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key.
+ */
+int sha512_prf(const u8 *key, size_t key_len, const char *label,
+	       const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+	return sha512_prf_bits(key, key_len, label, data, data_len, buf,
+			       buf_len * 8);
+}
+
+
+/**
+ * sha512_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bits of key to generate
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key. If the requested buf_len is not divisible by eight, the least
+ * significant 1-7 bits of the last octet in the output are not part of the
+ * requested output.
+ */
+int sha512_prf_bits(const u8 *key, size_t key_len, const char *label,
+		    const u8 *data, size_t data_len, u8 *buf,
+		    size_t buf_len_bits)
+{
+	u16 counter = 1;
+	size_t pos, plen;
+	u8 hash[SHA512_MAC_LEN];
+	const u8 *addr[4];
+	size_t len[4];
+	u8 counter_le[2], length_le[2];
+	size_t buf_len = (buf_len_bits + 7) / 8;
+
+	addr[0] = counter_le;
+	len[0] = 2;
+	addr[1] = (u8 *) label;
+	len[1] = os_strlen(label);
+	addr[2] = data;
+	len[2] = data_len;
+	addr[3] = length_le;
+	len[3] = sizeof(length_le);
+
+	WPA_PUT_LE16(length_le, buf_len_bits);
+	pos = 0;
+	while (pos < buf_len) {
+		plen = buf_len - pos;
+		WPA_PUT_LE16(counter_le, counter);
+		if (plen >= SHA512_MAC_LEN) {
+			if (hmac_sha512_vector(key, key_len, 4, addr, len,
+					       &buf[pos]) < 0)
+				return -1;
+			pos += SHA512_MAC_LEN;
+		} else {
+			if (hmac_sha512_vector(key, key_len, 4, addr, len,
+					       hash) < 0)
+				return -1;
+			os_memcpy(&buf[pos], hash, plen);
+			pos += plen;
+			break;
+		}
+		counter++;
+	}
+
+	/*
+	 * Mask out unused bits in the last octet if it does not use all the
+	 * bits.
+	 */
+	if (buf_len_bits % 8) {
+		u8 mask = 0xff << (8 - buf_len_bits % 8);
+		buf[pos - 1] &= mask;
+	}
+
+	os_memset(hash, 0, sizeof(hash));
+
+	return 0;
+}
diff --git a/src/crypto/sha512.h b/src/crypto/sha512.h
new file mode 100644
index 0000000..8e64c8b
--- /dev/null
+++ b/src/crypto/sha512.h
@@ -0,0 +1,27 @@
+/*
+ * SHA512 hash implementation and interface functions
+ * Copyright (c) 2015-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA512_H
+#define SHA512_H
+
+#define SHA512_MAC_LEN 64
+
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac);
+int sha512_prf(const u8 *key, size_t key_len, const char *label,
+	       const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+int sha512_prf_bits(const u8 *key, size_t key_len, const char *label,
+		    const u8 *data, size_t data_len, u8 *buf,
+		    size_t buf_len_bits);
+int hmac_sha512_kdf(const u8 *secret, size_t secret_len,
+		    const char *label, const u8 *seed, size_t seed_len,
+		    u8 *out, size_t outlen);
+
+#endif /* SHA512_H */
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index 11d504a..dc4117c 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -41,6 +41,7 @@
 	TLS_FAIL_SERVER_CHAIN_PROBE = 8,
 	TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9,
 	TLS_FAIL_DOMAIN_MISMATCH = 10,
+	TLS_FAIL_INSUFFICIENT_KEY_LEN = 11,
 };
 
 
@@ -80,6 +81,7 @@
 	int cert_in_cb;
 	const char *openssl_ciphers;
 	unsigned int tls_session_lifetime;
+	unsigned int tls_flags;
 
 	void (*event_cb)(void *ctx, enum tls_event ev,
 			 union tls_event_data *data);
@@ -97,6 +99,8 @@
 #define TLS_CONN_DISABLE_TLSv1_0 BIT(8)
 #define TLS_CONN_EXT_CERT_CHECK BIT(9)
 #define TLS_CONN_REQUIRE_OCSP_ALL BIT(10)
+#define TLS_CONN_SUITEB BIT(11)
+#define TLS_CONN_SUITEB_NO_ECDH BIT(12)
 
 /**
  * struct tls_connection_params - Parameters for TLS connection
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index 4521891..988c9d2 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -103,6 +103,15 @@
 
 #endif
 
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#ifdef CONFIG_SUITEB
+static int RSA_bits(const RSA *r)
+{
+	return BN_num_bits(r->n);
+}
+#endif /* CONFIG_SUITEB */
+#endif
+
 #ifdef ANDROID
 #include <openssl/pem.h>
 #include <keystore/keystore_get.h>
@@ -233,6 +242,9 @@
 
 	unsigned char client_random[SSL3_RANDOM_SIZE];
 	unsigned char server_random[SSL3_RANDOM_SIZE];
+
+	u16 cipher_suite;
+	int server_dh_prime_len;
 };
 
 
@@ -1025,7 +1037,7 @@
 	if (conf && conf->openssl_ciphers)
 		ciphers = conf->openssl_ciphers;
 	else
-		ciphers = "DEFAULT:!EXP:!LOW";
+		ciphers = TLS_DEFAULT_CIPHERS;
 	if (SSL_CTX_set_cipher_list(ssl, ciphers) != 1) {
 		wpa_printf(MSG_ERROR,
 			   "OpenSSL: Failed to set cipher string '%s'",
@@ -1304,6 +1316,95 @@
 }
 
 
+#ifdef CONFIG_SUITEB
+
+static void check_server_hello(struct tls_connection *conn,
+			       const u8 *pos, const u8 *end)
+{
+	size_t payload_len, id_len;
+
+	/*
+	 * Parse ServerHello to get the selected cipher suite since OpenSSL does
+	 * not make it cleanly available during handshake and we need to know
+	 * whether DHE was selected.
+	 */
+
+	if (end - pos < 3)
+		return;
+	payload_len = WPA_GET_BE24(pos);
+	pos += 3;
+
+	if ((size_t) (end - pos) < payload_len)
+		return;
+	end = pos + payload_len;
+
+	/* Skip Version and Random */
+	if (end - pos < 2 + SSL3_RANDOM_SIZE)
+		return;
+	pos += 2 + SSL3_RANDOM_SIZE;
+
+	/* Skip Session ID */
+	if (end - pos < 1)
+		return;
+	id_len = *pos++;
+	if ((size_t) (end - pos) < id_len)
+		return;
+	pos += id_len;
+
+	if (end - pos < 2)
+		return;
+	conn->cipher_suite = WPA_GET_BE16(pos);
+	wpa_printf(MSG_DEBUG, "OpenSSL: Server selected cipher suite 0x%x",
+		   conn->cipher_suite);
+}
+
+
+static void check_server_key_exchange(SSL *ssl, struct tls_connection *conn,
+				      const u8 *pos, const u8 *end)
+{
+	size_t payload_len;
+	u16 dh_len;
+	BIGNUM *p;
+	int bits;
+
+	if (!(conn->flags & TLS_CONN_SUITEB))
+		return;
+
+	/* DHE is enabled only with DHE-RSA-AES256-GCM-SHA384 */
+	if (conn->cipher_suite != 0x9f)
+		return;
+
+	if (end - pos < 3)
+		return;
+	payload_len = WPA_GET_BE24(pos);
+	pos += 3;
+
+	if ((size_t) (end - pos) < payload_len)
+		return;
+	end = pos + payload_len;
+
+	if (end - pos < 2)
+		return;
+	dh_len = WPA_GET_BE16(pos);
+	pos += 2;
+
+	if ((size_t) (end - pos) < dh_len)
+		return;
+	p = BN_bin2bn(pos, dh_len, NULL);
+	if (!p)
+		return;
+
+	bits = BN_num_bits(p);
+	BN_free(p);
+
+	conn->server_dh_prime_len = bits;
+	wpa_printf(MSG_DEBUG, "OpenSSL: Server DH prime length: %d bits",
+		   conn->server_dh_prime_len);
+}
+
+#endif /* CONFIG_SUITEB */
+
+
 static void tls_msg_cb(int write_p, int version, int content_type,
 		       const void *buf, size_t len, SSL *ssl, void *arg)
 {
@@ -1330,6 +1431,18 @@
 			conn->invalid_hb_used = 1;
 		}
 	}
+
+#ifdef CONFIG_SUITEB
+	/*
+	 * Need to parse these handshake messages to be able to check DH prime
+	 * length since OpenSSL does not expose the new cipher suite and DH
+	 * parameters during handshake (e.g., for cert_cb() callback).
+	 */
+	if (content_type == 22 && pos && len > 0 && pos[0] == 2)
+		check_server_hello(conn, pos + 1, pos + len);
+	if (content_type == 22 && pos && len > 0 && pos[0] == 12)
+		check_server_key_exchange(ssl, conn, pos + 1, pos + len);
+#endif /* CONFIG_SUITEB */
 }
 
 
@@ -1924,6 +2037,37 @@
 				       TLS_FAIL_SERVER_CHAIN_PROBE);
 	}
 
+#ifdef CONFIG_SUITEB
+	if (conn->flags & TLS_CONN_SUITEB) {
+		EVP_PKEY *pk;
+		RSA *rsa;
+		int len = -1;
+
+		pk = X509_get_pubkey(err_cert);
+		if (pk) {
+			rsa = EVP_PKEY_get1_RSA(pk);
+			if (rsa) {
+				len = RSA_bits(rsa);
+				RSA_free(rsa);
+			}
+			EVP_PKEY_free(pk);
+		}
+
+		if (len >= 0) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: RSA modulus size: %d bits", len);
+			if (len < 3072) {
+				preverify_ok = 0;
+				openssl_tls_fail_event(
+					conn, err_cert, err,
+					depth, buf,
+					"Insufficient RSA modulus size",
+					TLS_FAIL_INSUFFICIENT_KEY_LEN);
+			}
+		}
+	}
+#endif /* CONFIG_SUITEB */
+
 #ifdef OPENSSL_IS_BORINGSSL
 	if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) &&
 	    preverify_ok) {
@@ -2257,8 +2401,42 @@
 }
 
 
-static void tls_set_conn_flags(SSL *ssl, unsigned int flags)
+#ifdef CONFIG_SUITEB
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+static int suiteb_cert_cb(SSL *ssl, void *arg)
 {
+	struct tls_connection *conn = arg;
+
+	/*
+	 * This cert_cb() is not really the best location for doing a
+	 * constraint check for the ServerKeyExchange message, but this seems to
+	 * be the only place where the current OpenSSL sequence can be
+	 * terminated cleanly with an TLS alert going out to the server.
+	 */
+
+	if (!(conn->flags & TLS_CONN_SUITEB))
+		return 1;
+
+	/* DHE is enabled only with DHE-RSA-AES256-GCM-SHA384 */
+	if (conn->cipher_suite != 0x9f)
+		return 1;
+
+	if (conn->server_dh_prime_len >= 3072)
+		return 1;
+
+	wpa_printf(MSG_DEBUG,
+		   "OpenSSL: Server DH prime length (%d bits) not sufficient for Suite B RSA - reject handshake",
+		   conn->server_dh_prime_len);
+	return 0;
+}
+#endif /* OPENSSL_VERSION_NUMBER */
+#endif /* CONFIG_SUITEB */
+
+
+static int tls_set_conn_flags(struct tls_connection *conn, unsigned int flags)
+{
+	SSL *ssl = conn->ssl;
+
 #ifdef SSL_OP_NO_TICKET
 	if (flags & TLS_CONN_DISABLE_SESSION_TICKET)
 		SSL_set_options(ssl, SSL_OP_NO_TICKET);
@@ -2284,6 +2462,64 @@
 	else
 		SSL_clear_options(ssl, SSL_OP_NO_TLSv1_2);
 #endif /* SSL_OP_NO_TLSv1_2 */
+#ifdef CONFIG_SUITEB
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+	if (flags & TLS_CONN_SUITEB_NO_ECDH) {
+		const char *ciphers = "DHE-RSA-AES256-GCM-SHA384";
+
+		if (SSL_set_cipher_list(ssl, ciphers) != 1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set Suite B ciphers");
+			return -1;
+		}
+	} else if (flags & TLS_CONN_SUITEB) {
+		EC_KEY *ecdh;
+		const char *ciphers =
+			"ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384";
+
+		if (SSL_set_cipher_list(ssl, ciphers) != 1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set Suite B ciphers");
+			return -1;
+		}
+
+		if (SSL_set1_curves_list(ssl, "P-384") != 1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set Suite B curves");
+			return -1;
+		}
+
+		ecdh = EC_KEY_new_by_curve_name(NID_secp384r1);
+		if (!ecdh || SSL_set_tmp_ecdh(ssl, ecdh) != 1) {
+			EC_KEY_free(ecdh);
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set ECDH parameter");
+			return -1;
+		}
+		EC_KEY_free(ecdh);
+	}
+	if (flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) {
+		/* ECDSA+SHA384 if need to add EC support here */
+		if (SSL_set1_sigalgs_list(ssl, "RSA+SHA384") != 1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set Suite B sigalgs");
+			return -1;
+		}
+
+		SSL_set_options(ssl, SSL_OP_NO_TLSv1);
+		SSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
+		SSL_set_cert_cb(ssl, suiteb_cert_cb, conn);
+	}
+#else /* OPENSSL_VERSION_NUMBER < 0x10002000L */
+	if (flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: Suite B RSA case not supported with this OpenSSL version");
+		return -1;
+	}
+#endif /* OPENSSL_VERSION_NUMBER */
+#endif /* CONFIG_SUITEB */
+
+	return 0;
 }
 
 
@@ -2307,7 +2543,8 @@
 		SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
 	}
 
-	tls_set_conn_flags(conn->ssl, flags);
+	if (tls_set_conn_flags(conn, flags) < 0)
+		return -1;
 	conn->flags = flags;
 
 	SSL_set_accept_state(conn->ssl);
@@ -2369,9 +2606,9 @@
 		BIO *bio = BIO_from_keystore(&client_cert[11]);
 		X509 *x509 = NULL;
 		int ret = -1;
-		if (bio)
+		if (bio) {
 			x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
-
+		}
 		if (x509) {
 			if (SSL_use_certificate(conn->ssl, x509) == 1)
 				ret = 0;
@@ -2775,6 +3012,19 @@
 }
 
 
+static void tls_clear_default_passwd_cb(SSL_CTX *ssl_ctx, SSL *ssl)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL)
+	if (ssl) {
+		SSL_set_default_passwd_cb(ssl, NULL);
+		SSL_set_default_passwd_cb_userdata(ssl, NULL);
+	}
+#endif /* >= 1.1.0f && !LibreSSL && !BoringSSL */
+	SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
+	SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, NULL);
+}
+
+
 static int tls_connection_private_key(struct tls_data *data,
 				      struct tls_connection *conn,
 				      const char *private_key,
@@ -2796,6 +3046,15 @@
 	} else
 		passwd = NULL;
 
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL)
+	/*
+	 * In OpenSSL >= 1.1.0f SSL_use_PrivateKey_file() uses the callback
+	 * from the SSL object. See OpenSSL commit d61461a75253.
+	 */
+	SSL_set_default_passwd_cb(conn->ssl, tls_passwd_cb);
+	SSL_set_default_passwd_cb_userdata(conn->ssl, passwd);
+#endif /* >= 1.1.0f && !LibreSSL && !BoringSSL */
+	/* Keep these for OpenSSL < 1.1.0f */
 	SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb);
 	SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd);
 
@@ -2882,11 +3141,12 @@
 	if (!ok) {
 		tls_show_errors(MSG_INFO, __func__,
 				"Failed to load private key");
+		tls_clear_default_passwd_cb(ssl_ctx, conn->ssl);
 		os_free(passwd);
 		return -1;
 	}
 	ERR_clear_error();
-	SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
+	tls_clear_default_passwd_cb(ssl_ctx, conn->ssl);
 	os_free(passwd);
 
 	if (!SSL_check_private_key(conn->ssl)) {
@@ -2929,13 +3189,14 @@
 	    tls_read_pkcs12(data, NULL, private_key, passwd)) {
 		tls_show_errors(MSG_INFO, __func__,
 				"Failed to load private key");
+		tls_clear_default_passwd_cb(ssl_ctx, NULL);
 		os_free(passwd);
 		ERR_clear_error();
 		return -1;
 	}
+	tls_clear_default_passwd_cb(ssl_ctx, NULL);
 	os_free(passwd);
 	ERR_clear_error();
-	SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
 
 	if (!SSL_CTX_check_private_key(ssl_ctx)) {
 		tls_show_errors(MSG_INFO, __func__,
@@ -3306,6 +3567,41 @@
 		}
 	}
 
+#ifdef CONFIG_SUITEB
+	if ((conn->flags & TLS_CONN_SUITEB) && !server &&
+	    os_strncmp(SSL_get_cipher(conn->ssl), "DHE-", 4) == 0 &&
+	    conn->server_dh_prime_len < 3072) {
+		struct tls_context *context = conn->context;
+
+		/*
+		 * This should not be reached since earlier cert_cb should have
+		 * terminated the handshake. Keep this check here for extra
+		 * protection if anything goes wrong with the more low-level
+		 * checks based on having to parse the TLS handshake messages.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Server DH prime length: %d bits",
+			   conn->server_dh_prime_len);
+
+		if (context->event_cb) {
+			union tls_event_data ev;
+
+			os_memset(&ev, 0, sizeof(ev));
+			ev.alert.is_local = 1;
+			ev.alert.type = "fatal";
+			ev.alert.description = "insufficient security";
+			context->event_cb(context->cb_ctx, TLS_ALERT, &ev);
+		}
+		/*
+		 * Could send a TLS Alert to the server, but for now, simply
+		 * terminate handshake.
+		 */
+		conn->failed++;
+		conn->write_alerts++;
+		return NULL;
+	}
+#endif /* CONFIG_SUITEB */
+
 	/* Get the TLS handshake data to be sent to the server */
 	res = BIO_ctrl_pending(conn->ssl_out);
 	wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res);
@@ -3764,7 +4060,7 @@
 {
 	struct tls_connection *conn = arg;
 	const unsigned char *p;
-	int len, status, reason;
+	int len, status, reason, res;
 	OCSP_RESPONSE *rsp;
 	OCSP_BASICRESP *basic;
 	OCSP_CERTID *id;
@@ -3859,16 +4155,33 @@
 		return 0;
 	}
 
-	id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer);
+	id = OCSP_cert_to_id(EVP_sha256(), conn->peer_cert, conn->peer_issuer);
 	if (!id) {
-		wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier");
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Could not create OCSP certificate identifier (SHA256)");
 		OCSP_BASICRESP_free(basic);
 		OCSP_RESPONSE_free(rsp);
 		return 0;
 	}
 
-	if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at,
-				   &this_update, &next_update)) {
+	res = OCSP_resp_find_status(basic, id, &status, &reason, &produced_at,
+				    &this_update, &next_update);
+	if (!res) {
+		id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer);
+		if (!id) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: Could not create OCSP certificate identifier (SHA1)");
+			OCSP_BASICRESP_free(basic);
+			OCSP_RESPONSE_free(rsp);
+			return 0;
+		}
+
+		res = OCSP_resp_find_status(basic, id, &status, &reason,
+					    &produced_at, &this_update,
+					    &next_update);
+	}
+
+	if (!res) {
 		wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s",
 			   (conn->flags & TLS_CONN_REQUIRE_OCSP) ? "" :
 			   " (OCSP not required)");
@@ -4070,7 +4383,8 @@
 		return -1;
 	}
 
-	tls_set_conn_flags(conn->ssl, params->flags);
+	if (tls_set_conn_flags(conn, params->flags) < 0)
+		return -1;
 
 #ifdef OPENSSL_IS_BORINGSSL
 	if (params->flags & TLS_CONN_REQUEST_OCSP) {
@@ -4227,11 +4541,10 @@
 	wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket "
 		    "extension", data, len);
 
-	conn->session_ticket = os_malloc(len);
+	conn->session_ticket = os_memdup(data, len);
 	if (conn->session_ticket == NULL)
 		return 0;
 
-	os_memcpy(conn->session_ticket, data, len);
 	conn->session_ticket_len = len;
 
 	return 1;
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 7b3a6bd..a6307e3 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -21,6 +21,7 @@
 
 #include "common/defs.h"
 #include "common/ieee802_11_defs.h"
+#include "common/wpa_common.h"
 #ifdef CONFIG_MACSEC
 #include "pae/ieee802_1x_kay.h"
 #endif /* CONFIG_MACSEC */
@@ -64,6 +65,10 @@
 /* Filter unicast IP packets encrypted using the GTK */
 #define WPA_DATA_FRAME_FILTER_FLAG_GTK BIT(2)
 
+#define HOSTAPD_DFS_REGION_FCC	1
+#define HOSTAPD_DFS_REGION_ETSI	2
+#define HOSTAPD_DFS_REGION_JP	3
+
 /**
  * enum reg_change_initiator - Regulatory change initiator
  */
@@ -483,6 +488,15 @@
 	 unsigned int sched_scan_plans_num;
 
 	/**
+	 * sched_scan_start_delay - Delay to use before starting the first scan
+	 *
+	 * Delay (in seconds) before scheduling first scan plan cycle. The
+	 * driver may ignore this parameter and start immediately (or at any
+	 * other time), if this feature is not supported.
+	 */
+	 u32 sched_scan_start_delay;
+
+	/**
 	 * bssid - Specific BSSID to scan for
 	 *
 	 * This optional parameter can be used to replace the default wildcard
@@ -676,6 +690,68 @@
 };
 
 /**
+ * struct wpa_driver_sta_auth_params - Authentication parameters
+ * Data for struct wpa_driver_ops::sta_auth().
+ */
+struct wpa_driver_sta_auth_params {
+
+	/**
+	 * own_addr - Source address and BSSID for authentication frame
+	 */
+	const u8 *own_addr;
+
+	/**
+	 * addr - MAC address of the station to associate
+	 */
+	const u8 *addr;
+
+	/**
+	 * seq - authentication sequence number
+	 */
+	u16 seq;
+
+	/**
+	 * status - authentication response status code
+	 */
+	u16 status;
+
+	/**
+	 * ie - authentication frame ie buffer
+	 */
+	const u8 *ie;
+
+	/**
+	 * len - ie buffer length
+	 */
+	size_t len;
+
+	/**
+	 * fils_auth - Indicates whether FILS authentication is being performed
+	 */
+	int fils_auth;
+
+	/**
+	 * fils_anonce - ANonce (required for FILS)
+	 */
+	u8 fils_anonce[WPA_NONCE_LEN];
+
+	/**
+	 * fils_snonce - SNonce (required for FILS)
+	*/
+	u8 fils_snonce[WPA_NONCE_LEN];
+
+	/**
+	 * fils_kek - key for encryption (required for FILS)
+	 */
+	u8 fils_kek[WPA_KEK_MAX_LEN];
+
+	/**
+	 * fils_kek_len - Length of the fils_kek in octets (required for FILS)
+	 */
+	size_t fils_kek_len;
+};
+
+/**
  * struct wpa_driver_associate_params - Association parameters
  * Data for struct wpa_driver_ops::associate().
  */
@@ -738,7 +814,7 @@
 	 * WPA information element to be included in (Re)Association
 	 * Request (including information element id and length). Use
 	 * of this WPA IE is optional. If the driver generates the WPA
-	 * IE, it can use pairwise_suite, group_suite, and
+	 * IE, it can use pairwise_suite, group_suite, group_mgmt_suite, and
 	 * key_mgmt_suite to select proper algorithms. In this case,
 	 * the driver has to notify wpa_supplicant about the used WPA
 	 * IE by generating an event that the interface code will
@@ -778,6 +854,13 @@
 	unsigned int group_suite;
 
 	/**
+	 * mgmt_group_suite - Selected group management cipher suite (WPA_CIPHER_*)
+	 *
+	 * This is usually ignored if @wpa_ie is used.
+	 */
+	unsigned int mgmt_group_suite;
+
+	/**
 	 * key_mgmt_suite - Selected key management suite (WPA_KEY_MGMT_*)
 	 *
 	 * This is usually ignored if @wpa_ie is used.
@@ -816,43 +899,6 @@
 	enum mfp_options mgmt_frame_protection;
 
 	/**
-	 * ft_ies - IEEE 802.11r / FT information elements
-	 * If the supplicant is using IEEE 802.11r (FT) and has the needed keys
-	 * for fast transition, this parameter is set to include the IEs that
-	 * are to be sent in the next FT Authentication Request message.
-	 * update_ft_ies() handler is called to update the IEs for further
-	 * FT messages in the sequence.
-	 *
-	 * The driver should use these IEs only if the target AP is advertising
-	 * the same mobility domain as the one included in the MDIE here.
-	 *
-	 * In ap_scan=2 mode, the driver can use these IEs when moving to a new
-	 * AP after the initial association. These IEs can only be used if the
-	 * target AP is advertising support for FT and is using the same MDIE
-	 * and SSID as the current AP.
-	 *
-	 * The driver is responsible for reporting the FT IEs received from the
-	 * AP's response using wpa_supplicant_event() with EVENT_FT_RESPONSE
-	 * type. update_ft_ies() handler will then be called with the FT IEs to
-	 * include in the next frame in the authentication sequence.
-	 */
-	const u8 *ft_ies;
-
-	/**
-	 * ft_ies_len - Length of ft_ies in bytes
-	 */
-	size_t ft_ies_len;
-
-	/**
-	 * ft_md - FT Mobility domain (6 octets) (also included inside ft_ies)
-	 *
-	 * This value is provided to allow the driver interface easier access
-	 * to the current mobility domain. This value is set to %NULL if no
-	 * mobility domain is currently active.
-	 */
-	const u8 *ft_md;
-
-	/**
 	 * passphrase - RSN passphrase for PSK
 	 *
 	 * This value is made available only for WPA/WPA2-Personal (PSK) and
@@ -1002,6 +1048,43 @@
 	 * fils_nonces_len: Length of fils_nonce in bytes
 	 */
 	size_t fils_nonces_len;
+
+	/**
+	 * fils_erp_username - Username part of keyName-NAI
+	 */
+	const u8 *fils_erp_username;
+
+	/**
+	 * fils_erp_username_len - Length of fils_erp_username in bytes
+	 */
+	size_t fils_erp_username_len;
+
+	/**
+	 * fils_erp_realm - Realm/domain name to use in FILS ERP
+	 */
+	const u8 *fils_erp_realm;
+
+	/**
+	 * fils_erp_realm_len - Length of fils_erp_realm in bytes
+	 */
+	size_t fils_erp_realm_len;
+
+	/**
+	 * fils_erp_next_seq_num - The next sequence number to use in FILS ERP
+	 * messages
+	 */
+	u16 fils_erp_next_seq_num;
+
+	/**
+	 * fils_erp_rrk - Re-authentication root key (rRK) for the keyName-NAI
+	 * specified by fils_erp_username@fils_erp_realm.
+	 */
+	const u8 *fils_erp_rrk;
+
+	/**
+	 * fils_erp_rrk_len - Length of fils_erp_rrk in bytes
+	 */
+	size_t fils_erp_rrk_len;
 };
 
 enum hide_ssid {
@@ -1279,6 +1362,7 @@
 #define WPA_DRIVER_MESH_CONF_FLAG_PEER_LINK_TIMEOUT	0x00000002
 #define WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS	0x00000004
 #define WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE		0x00000008
+#define WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD	0x00000010
 	/*
 	 * TODO: Other mesh configuration parameters would go here.
 	 * See NL80211_MESHCONF_* for all the mesh config parameters.
@@ -1287,6 +1371,7 @@
 	int auto_plinks;
 	int peer_link_timeout;
 	int max_peer_links;
+	int rssi_threshold;
 	u16 ht_opmode;
 };
 
@@ -1321,6 +1406,12 @@
 #define WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK	0x00000080
 #define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B	0x00000100
 #define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192	0x00000200
+#define WPA_DRIVER_CAPA_KEY_MGMT_OWE		0x00000400
+#define WPA_DRIVER_CAPA_KEY_MGMT_DPP		0x00000800
+#define WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256    0x00001000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384    0x00002000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 0x00004000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 0x00008000
 	/** Bitfield of supported key management suites */
 	unsigned int key_mgmt;
 
@@ -1459,6 +1550,19 @@
 #define WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI	0x0001000000000000ULL
 /** Driver supports HE capabilities */
 #define WPA_DRIVER_FLAGS_HE_CAPABILITIES	0x0002000000000000ULL
+/** Driver supports FILS shared key offload */
+#define WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD	0x0004000000000000ULL
+/** Driver supports all OCE STA specific mandatory features */
+#define WPA_DRIVER_FLAGS_OCE_STA		0x0008000000000000ULL
+/** Driver supports all OCE AP specific mandatory features */
+#define WPA_DRIVER_FLAGS_OCE_AP			0x0010000000000000ULL
+/**
+ * Driver supports all OCE STA-CFON specific mandatory features only.
+ * If a driver sets this bit but not the %WPA_DRIVER_FLAGS_OCE_AP, the
+ * userspace shall assume that this driver may not support all OCE AP
+ * functionality but can support only OCE STA-CFON functionality.
+ */
+#define WPA_DRIVER_FLAGS_OCE_STA_CFON		0x0020000000000000ULL
 	u64 flags;
 
 #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -1580,18 +1684,35 @@
 
 struct hostapd_data;
 
+#define STA_DRV_DATA_TX_MCS BIT(0)
+#define STA_DRV_DATA_RX_MCS BIT(1)
+#define STA_DRV_DATA_TX_VHT_MCS BIT(2)
+#define STA_DRV_DATA_RX_VHT_MCS BIT(3)
+#define STA_DRV_DATA_TX_VHT_NSS BIT(4)
+#define STA_DRV_DATA_RX_VHT_NSS BIT(5)
+#define STA_DRV_DATA_TX_SHORT_GI BIT(6)
+#define STA_DRV_DATA_RX_SHORT_GI BIT(7)
+
 struct hostap_sta_driver_data {
 	unsigned long rx_packets, tx_packets;
 	unsigned long long rx_bytes, tx_bytes;
 	int bytes_64bit; /* whether 64-bit byte counters are supported */
 	unsigned long current_tx_rate;
+	unsigned long current_rx_rate;
 	unsigned long inactive_msec;
-	unsigned long flags;
+	unsigned long flags; /* bitfield of STA_DRV_DATA_* */
 	unsigned long num_ps_buf_frames;
 	unsigned long tx_retry_failed;
 	unsigned long tx_retry_count;
 	int last_rssi;
 	int last_ack_rssi;
+	s8 signal;
+	u8 rx_vhtmcs;
+	u8 tx_vhtmcs;
+	u8 rx_mcs;
+	u8 tx_mcs;
+	u8 rx_vht_nss;
+	u8 tx_vht_nss;
 };
 
 struct hostapd_sta_add_params {
@@ -1898,6 +2019,37 @@
 	const int *freq_list;
 };
 
+struct wpa_bss_trans_info {
+	u8 mbo_transition_reason;
+	u8 n_candidates;
+	u8 *bssid;
+};
+
+struct wpa_bss_candidate_info {
+	u8 num;
+	struct candidate_list {
+		u8 bssid[ETH_ALEN];
+		u8 is_accept;
+		u32 reject_reason;
+	} *candidates;
+};
+
+struct wpa_pmkid_params {
+	const u8 *bssid;
+	const u8 *ssid;
+	size_t ssid_len;
+	const u8 *fils_cache_id;
+	const u8 *pmkid;
+	const u8 *pmk;
+	size_t pmk_len;
+};
+
+/* Mask used to specify which connection parameters have to be updated */
+enum wpa_drv_update_connect_params_mask {
+	WPA_DRV_UPDATE_ASSOC_IES	= BIT(0),
+	WPA_DRV_UPDATE_FILS_ERP_INFO	= BIT(1),
+	WPA_DRV_UPDATE_AUTH_TYPE	= BIT(2),
+};
 
 /**
  * struct wpa_driver_ops - Driver interface API definition
@@ -2080,13 +2232,14 @@
 	/**
 	 * add_pmkid - Add PMKSA cache entry to the driver
 	 * @priv: private driver interface data
-	 * @bssid: BSSID for the PMKSA cache entry
-	 * @pmkid: PMKID for the PMKSA cache entry
+	 * @params: PMKSA parameters
 	 *
 	 * Returns: 0 on success, -1 on failure
 	 *
 	 * This function is called when a new PMK is received, as a result of
-	 * either normal authentication or RSN pre-authentication.
+	 * either normal authentication or RSN pre-authentication. The PMKSA
+	 * parameters are either a set of bssid, pmkid, and pmk; or a set of
+	 * ssid, fils_cache_id, pmkid, and pmk.
 	 *
 	 * If the driver generates RSN IE, i.e., it does not use wpa_ie in
 	 * associate(), add_pmkid() can be used to add new PMKSA cache entries
@@ -2094,18 +2247,18 @@
 	 * driver_ops function does not need to be implemented. Likewise, if
 	 * the driver does not support WPA, this function is not needed.
 	 */
-	int (*add_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid);
+	int (*add_pmkid)(void *priv, struct wpa_pmkid_params *params);
 
 	/**
 	 * remove_pmkid - Remove PMKSA cache entry to the driver
 	 * @priv: private driver interface data
-	 * @bssid: BSSID for the PMKSA cache entry
-	 * @pmkid: PMKID for the PMKSA cache entry
+	 * @params: PMKSA parameters
 	 *
 	 * Returns: 0 on success, -1 on failure
 	 *
 	 * This function is called when the supplicant drops a PMKSA cache
-	 * entry for any reason.
+	 * entry for any reason. The PMKSA parameters are either a set of
+	 * bssid and pmkid; or a set of ssid, fils_cache_id, and pmkid.
 	 *
 	 * If the driver generates RSN IE, i.e., it does not use wpa_ie in
 	 * associate(), remove_pmkid() can be used to synchronize PMKSA caches
@@ -2114,7 +2267,7 @@
 	 * implemented. Likewise, if the driver does not support WPA, this
 	 * function is not needed.
 	 */
-	int (*remove_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid);
+	int (*remove_pmkid)(void *priv, struct wpa_pmkid_params *params);
 
 	/**
 	 * flush_pmkid - Flush PMKSA cache
@@ -2229,12 +2382,13 @@
 	 * @priv: Private driver interface data
 	 * @num_modes: Variable for returning the number of returned modes
 	 * flags: Variable for returning hardware feature flags
+	 * @dfs: Variable for returning DFS region (HOSTAPD_DFS_REGION_*)
 	 * Returns: Pointer to allocated hardware data on success or %NULL on
 	 * failure. Caller is responsible for freeing this.
 	 */
 	struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv,
 							 u16 *num_modes,
-							 u16 *flags);
+							 u16 *flags, u8 *dfs);
 
 	/**
 	 * send_mlme - Send management frame from MLME
@@ -3239,19 +3393,13 @@
 
 	/**
 	 * sta_auth - Station authentication indication
-	 * @priv: Private driver interface data
-	 * @own_addr: Source address and BSSID for authentication frame
-	 * @addr: MAC address of the station to associate
-	 * @seq: authentication sequence number
-	 * @status: authentication response status code
-	 * @ie: authentication frame ie buffer
-	 * @len: ie buffer length
+	 * @priv: private driver interface data
+	 * @params: Station authentication parameters
 	 *
-	 * This function indicates the driver to send Authentication frame
-	 * to the station.
+	 * Returns: 0 on success, -1 on failure
 	 */
-	 int (*sta_auth)(void *priv, const u8 *own_addr, const u8 *addr,
-			 u16 seq, u16 status, const u8 *ie, size_t len);
+	 int (*sta_auth)(void *priv,
+			 struct wpa_driver_sta_auth_params *params);
 
 	/**
 	 * add_tspec - Add traffic stream
@@ -3808,8 +3956,52 @@
 	 * trigger control mode to the host driver.
 	 */
 	int (*set_tdls_mode)(void *priv, int tdls_external_control);
-};
 
+	/**
+	 * get_bss_transition_status - Get candidate BSS's transition status
+	 * @priv: Private driver interface data
+	 * @params: Candidate BSS list
+	 *
+	 * Get the accept or reject reason code for a list of BSS transition
+	 * candidates.
+	 */
+	struct wpa_bss_candidate_info *
+	(*get_bss_transition_status)(void *priv,
+				     struct wpa_bss_trans_info *params);
+	/**
+	 * ignore_assoc_disallow - Configure driver to ignore assoc_disallow
+	 * @priv: Private driver interface data
+	 * @ignore_disallow: 0 to not ignore, 1 to ignore
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*ignore_assoc_disallow)(void *priv, int ignore_disallow);
+
+	/**
+	 * set_bssid_blacklist - Set blacklist of BSSIDs to the driver
+	 * @priv: Private driver interface data
+	 * @num_bssid: Number of blacklist BSSIDs
+	 * @bssids: List of blacklisted BSSIDs
+	 */
+	int (*set_bssid_blacklist)(void *priv, unsigned int num_bssid,
+				   const u8 *bssid);
+
+	/**
+	 * update_connect_params - Update the connection parameters
+	 * @priv: Private driver interface data
+	 * @params: Association parameters
+	 * @mask: Bit mask indicating which parameters in @params have to be
+	 *	updated
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * Update the connection parameters when in connected state so that the
+	 * driver uses the updated parameters for subsequent roaming. This is
+	 * used only with drivers that implement internal BSS selection and
+	 * roaming.
+	 */
+	int (*update_connect_params)(
+		void *priv, struct wpa_driver_associate_params *params,
+		enum wpa_drv_update_connect_params_mask mask);
+};
 
 /**
  * enum wpa_event_type - Event type for wpa_supplicant_event() calls
@@ -3919,17 +4111,6 @@
 	EVENT_PMKID_CANDIDATE,
 
 	/**
-	 * EVENT_STKSTART - Request STK handshake (MLME-STKSTART.request)
-	 *
-	 * This event can be used to inform wpa_supplicant about desire to set
-	 * up secure direct link connection between two stations as defined in
-	 * IEEE 802.11e with a new PeerKey mechanism that replaced the original
-	 * STAKey negotiation. The caller will need to set peer address for the
-	 * event.
-	 */
-	EVENT_STKSTART,
-
-	/**
 	 * EVENT_TDLS - Request TDLS operation
 	 *
 	 * This event can be used to request a TDLS operation to be performed.
@@ -4306,6 +4487,15 @@
 	 * range.
 	 */
 	EVENT_BEACON_LOSS,
+
+	/**
+	 * EVENT_DFS_PRE_CAC_EXPIRED - Notify that channel availability check
+	 * done previously (Pre-CAC) on the channel has expired. This would
+	 * normally be on a non-ETSI DFS regulatory domain. DFS state of the
+	 * channel will be moved from available to usable. A new CAC has to be
+	 * performed before start operating on this channel.
+	 */
+	EVENT_DFS_PRE_CAC_EXPIRED,
 };
 
 
@@ -4484,6 +4674,8 @@
 
 		/**
 		 * ptk_kek - The derived PTK KEK
+		 * This is used in key management offload and also in FILS SK
+		 * offload.
 		 */
 		const u8 *ptk_kek;
 
@@ -4497,6 +4689,36 @@
 		 * 0 = unknown, 1 = unchanged, 2 = changed
 		 */
 		u8 subnet_status;
+
+		/**
+		 * The following information is used in FILS SK offload
+		 * @fils_erp_next_seq_num
+		 * @fils_pmk
+		 * @fils_pmk_len
+		 * @fils_pmkid
+		 */
+
+		/**
+		 * fils_erp_next_seq_num - The next sequence number to use in
+		 * FILS ERP messages
+		 */
+		u16 fils_erp_next_seq_num;
+
+		/**
+		 * fils_pmk - A new PMK if generated in case of FILS
+		 * authentication
+		 */
+		const u8 *fils_pmk;
+
+		/**
+		 * fils_pmk_len - Length of fils_pmk
+		 */
+		size_t fils_pmk_len;
+
+		/**
+		 * fils_pmkid - PMKID used or generated in FILS authentication
+		 */
+		const u8 *fils_pmkid;
 	} assoc_info;
 
 	/**
@@ -4593,13 +4815,6 @@
 	} pmkid_candidate;
 
 	/**
-	 * struct stkstart - Data for EVENT_STKSTART
-	 */
-	struct stkstart {
-		u8 peer[ETH_ALEN];
-	} stkstart;
-
-	/**
 	 * struct tdls - Data for EVENT_TDLS
 	 */
 	struct tdls {
@@ -4712,6 +4927,12 @@
 		 * timeout_reason - Reason for the timeout
 		 */
 		const char *timeout_reason;
+
+		/**
+		 * fils_erp_next_seq_num - The next sequence number to use in
+		 * FILS ERP messages
+		 */
+		u16 fils_erp_next_seq_num;
 	} assoc_reject;
 
 	struct timeout_event {
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index a88345f..d2b355c 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -36,6 +36,10 @@
 
 #include "ieee80211_external.h"
 
+/* Avoid conflicting definition from the driver header files with
+ * common/wpa_common.h */
+#undef WPA_OUI_TYPE
+
 
 #ifdef CONFIG_WPS
 #include <netpacket/packet.h>
@@ -55,7 +59,7 @@
 #include "netlink.h"
 #include "linux_ioctl.h"
 
-#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20) || defined(CONFIG_WNM) || defined(CONFIG_WPS)
+#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20) || defined(CONFIG_WNM) || defined(CONFIG_WPS) || defined(CONFIG_FILS)
 #define ATHEROS_USE_RAW_RECEIVE
 #endif
 
@@ -70,6 +74,7 @@
 	int	ioctl_sock;			/* socket for ioctl() use */
 	struct netlink_data *netlink;
 	int	we_version;
+	int fils_en;			/* FILS enable/disable in driver */
 	u8	acct_mac[ETH_ALEN];
 	struct hostap_sta_driver_data acct_data;
 
@@ -178,6 +183,25 @@
 }
 
 
+#ifdef CONFIG_FILS
+static int
+get80211param(struct atheros_driver_data *drv, int op, int *data)
+{
+	struct iwreq iwr;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+	iwr.u.mode = op;
+
+	if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_GETPARAM, &iwr) < 0)
+		return -1;
+
+	*data = iwr.u.mode;
+	return 0;
+}
+#endif /* CONFIG_FILS */
+
+
 static int
 set80211priv(struct atheros_driver_data *drv, int op, void *data, int len)
 {
@@ -692,10 +716,14 @@
 	wpa_hexdump(MSG_DEBUG, "atheros: set_generic_elem", ie, ie_len);
 
 	wpabuf_free(drv->wpa_ie);
-	drv->wpa_ie = wpabuf_alloc_copy(ie, ie_len);
+	if (ie)
+		drv->wpa_ie = wpabuf_alloc_copy(ie, ie_len);
+	else
+		drv->wpa_ie = NULL;
 
 	app_ie = (struct ieee80211req_getset_appiebuf *) buf;
-	os_memcpy(&(app_ie->app_buf[0]), ie, ie_len);
+	if (ie)
+		os_memcpy(&(app_ie->app_buf[0]), ie, ie_len);
 	app_ie->app_buflen = ie_len;
 
 	app_ie->app_frmtype = IEEE80211_APPIE_FRAME_BEACON;
@@ -931,11 +959,11 @@
 #ifdef CONFIG_WPS
 	filt.app_filterype |= IEEE80211_FILTER_TYPE_PROBE_REQ;
 #endif /* CONFIG_WPS */
-#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R)
+#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS)
 	filt.app_filterype |= (IEEE80211_FILTER_TYPE_ASSOC_REQ |
 			       IEEE80211_FILTER_TYPE_AUTH |
 			       IEEE80211_FILTER_TYPE_ACTION);
-#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W || CONFIG_FILS */
 #ifdef CONFIG_WNM
 	filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION;
 #endif /* CONFIG_WNM */
@@ -949,12 +977,12 @@
 			return ret;
 	}
 
-#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R)
+#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS)
 	drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW,
 				       atheros_raw_receive, drv, 1);
 	if (drv->sock_raw == NULL)
 		return -1;
-#endif /* CONFIG_WPS || CONFIG_IEEE80211R */
+#endif /* CONFIG_WPS || CONFIG_IEEE80211R || CONFIG_FILS */
 	return ret;
 }
 
@@ -981,7 +1009,8 @@
 	beac_ie = (struct ieee80211req_getset_appiebuf *) buf;
 	beac_ie->app_frmtype = frametype;
 	beac_ie->app_buflen = len;
-	os_memcpy(&(beac_ie->app_buf[0]), ie, len);
+	if (ie)
+		os_memcpy(&(beac_ie->app_buf[0]), ie, len);
 
 	/* append the WPA/RSN IE if it is set already */
 	if (((frametype == IEEE80211_APPIE_FRAME_BEACON) ||
@@ -1034,32 +1063,56 @@
 #define atheros_set_ap_wps_ie NULL
 #endif /* CONFIG_WPS */
 
-#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS)
 static int
-atheros_sta_auth(void *priv, const u8 *own_addr, const u8 *addr, u16 seq,
-		 u16 status_code, const u8 *ie, size_t len)
+atheros_sta_auth(void *priv, struct wpa_driver_sta_auth_params *params)
 {
 	struct atheros_driver_data *drv = priv;
 	struct ieee80211req_mlme mlme;
 	int ret;
 
 	wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d",
-		   __func__, ether_sprintf(addr), status_code);
+		   __func__, ether_sprintf(params->addr), params->status);
 
+#ifdef CONFIG_FILS
+	/* Copy FILS AAD parameters if the driver supports FILS */
+	if (params->fils_auth && drv->fils_en) {
+		wpa_printf(MSG_DEBUG, "%s: im_op IEEE80211_MLME_AUTH_FILS",
+			   __func__);
+		os_memcpy(mlme.fils_aad.ANonce, params->fils_anonce,
+			  IEEE80211_FILS_NONCE_LEN);
+		os_memcpy(mlme.fils_aad.SNonce, params->fils_snonce,
+			  IEEE80211_FILS_NONCE_LEN);
+		os_memcpy(mlme.fils_aad.kek, params->fils_kek,
+			  IEEE80211_MAX_WPA_KEK_LEN);
+		mlme.fils_aad.kek_len = params->fils_kek_len;
+		mlme.im_op = IEEE80211_MLME_AUTH_FILS;
+		wpa_hexdump(MSG_DEBUG, "FILS: ANonce",
+			    mlme.fils_aad.ANonce, FILS_NONCE_LEN);
+		wpa_hexdump(MSG_DEBUG, "FILS: SNonce",
+			    mlme.fils_aad.SNonce, FILS_NONCE_LEN);
+		wpa_hexdump_key(MSG_DEBUG, "FILS: KEK",
+				mlme.fils_aad.kek, mlme.fils_aad.kek_len);
+	} else {
+		mlme.im_op = IEEE80211_MLME_AUTH;
+	}
+#else /* CONFIG_FILS */
 	mlme.im_op = IEEE80211_MLME_AUTH;
-	mlme.im_reason = status_code;
-	mlme.im_seq = seq;
-	os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
-	mlme.im_optie_len = len;
-	if (len) {
-		if (len < IEEE80211_MAX_OPT_IE) {
-			os_memcpy(mlme.im_optie, ie, len);
+#endif /* CONFIG_FILS */
+
+	mlme.im_reason = params->status;
+	mlme.im_seq = params->seq;
+	os_memcpy(mlme.im_macaddr, params->addr, IEEE80211_ADDR_LEN);
+	mlme.im_optie_len = params->len;
+	if (params->len) {
+		if (params->len < IEEE80211_MAX_OPT_IE) {
+			os_memcpy(mlme.im_optie, params->ie, params->len);
 		} else {
 			wpa_printf(MSG_DEBUG, "%s: Not enough space to copy "
 				   "opt_ie STA (addr " MACSTR " reason %d, "
 				   "ie_len %d)",
-				   __func__, MAC2STR(addr), status_code,
-				   (int) len);
+				   __func__, MAC2STR(params->addr),
+				   params->status, (int) params->len);
 			return -1;
 		}
 	}
@@ -1067,7 +1120,7 @@
 	if (ret < 0) {
 		wpa_printf(MSG_DEBUG, "%s: Failed to auth STA (addr " MACSTR
 			   " reason %d)",
-			   __func__, MAC2STR(addr), status_code);
+			   __func__, MAC2STR(params->addr), params->status);
 	}
 	return ret;
 }
@@ -1110,7 +1163,7 @@
 	}
 	return ret;
 }
-#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W || CONFIG_FILS */
 
 static void
 atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
@@ -1257,7 +1310,7 @@
 		atheros_raw_receive(drv, NULL,
 				    (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
 #endif /* CONFIG_WPS */
-#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS)
 	} else if (os_strncmp(custom, "Manage.assoc_req ", 17) == 0) {
 		/* Format: "Manage.assoc_req <frame len>" | zero padding |
 		 * frame */
@@ -1281,7 +1334,7 @@
 		}
 		atheros_raw_receive(drv, NULL,
 				    (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
-#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R || CONFIG_FILS */
 #ifdef ATHEROS_USE_RAW_RECEIVE
 		} else if (os_strncmp(custom, "Manage.action ", 14) == 0) {
 		/* Format: "Manage.assoc_req <frame len>" | zero padding | frame
@@ -1603,6 +1656,27 @@
 			   len - sizeof(struct l2_ethhdr));
 }
 
+
+static void atheros_read_fils_cap(struct atheros_driver_data *drv)
+{
+	int fils = 0;
+
+#ifdef CONFIG_FILS
+	/* TODO: Would be better to have #ifdef on the IEEE80211_PARAM_* value
+	 * to automatically check this against the driver header files. */
+	if (get80211param(drv, IEEE80211_PARAM_ENABLE_FILS, &fils) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: Failed to get FILS capability from driver",
+			   __func__);
+		/* Assume driver does not support FILS */
+		fils = 0;
+	}
+#endif /* CONFIG_FILS */
+	drv->fils_en = fils;
+	wpa_printf(MSG_DEBUG, "atheros: fils_en=%d", drv->fils_en);
+}
+
+
 static void *
 atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params)
 {
@@ -1683,6 +1757,9 @@
 	if (atheros_wireless_event_init(drv))
 		goto bad;
 
+	/* Read FILS capability from the driver */
+	atheros_read_fils_cap(drv);
+
 	return drv;
 bad:
 	atheros_reset_appfilter(drv);
@@ -2158,7 +2235,7 @@
 	.set_ap_wps_ie		= atheros_set_ap_wps_ie,
 	.set_authmode		= atheros_set_authmode,
 	.set_ap			= atheros_set_ap,
-#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS)
 	.sta_assoc              = atheros_sta_assoc,
 	.sta_auth               = atheros_sta_auth,
 	.send_mlme       	= atheros_send_mgmt,
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index b6bcbca..0464304 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -35,7 +35,6 @@
 	E2S(ASSOCINFO);
 	E2S(INTERFACE_STATUS);
 	E2S(PMKID_CANDIDATE);
-	E2S(STKSTART);
 	E2S(TDLS);
 	E2S(FT_RESPONSE);
 	E2S(IBSS_RSN_START);
@@ -82,6 +81,7 @@
 	E2S(DFS_CAC_STARTED);
 	E2S(P2P_LO_STOP);
 	E2S(BEACON_LOSS);
+	E2S(DFS_PRE_CAC_EXPIRED);
 	}
 
 	return "UNKNOWN";
diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c
index 517a3bb..597da33 100644
--- a/src/drivers/driver_hostap.c
+++ b/src/drivers/driver_hostap.c
@@ -741,10 +741,9 @@
 	drv->generic_ie = NULL;
 	drv->generic_ie_len = 0;
 	if (elem) {
-		drv->generic_ie = os_malloc(elem_len);
+		drv->generic_ie = os_memdup(elem, elem_len);
 		if (drv->generic_ie == NULL)
 			return -1;
-		os_memcpy(drv->generic_ie, elem, elem_len);
 		drv->generic_ie_len = elem_len;
 	}
 
@@ -768,11 +767,10 @@
 	drv->wps_ie = NULL;
 	drv->wps_ie_len = 0;
 	if (proberesp) {
-		drv->wps_ie = os_malloc(wpabuf_len(proberesp));
+		drv->wps_ie = os_memdup(wpabuf_head(proberesp),
+					wpabuf_len(proberesp));
 		if (drv->wps_ie == NULL)
 			return -1;
-		os_memcpy(drv->wps_ie, wpabuf_head(proberesp),
-			  wpabuf_len(proberesp));
 		drv->wps_ie_len = wpabuf_len(proberesp);
 	}
 
@@ -1090,7 +1088,7 @@
 
 static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv,
 							    u16 *num_modes,
-							    u16 *flags)
+							    u16 *flags, u8 *dfs)
 {
 	struct hostapd_hw_modes *mode;
 	int i, clen, rlen;
@@ -1105,6 +1103,7 @@
 
 	*num_modes = 1;
 	*flags = 0;
+	*dfs = 0;
 
 	mode->mode = HOSTAPD_MODE_IEEE80211B;
 	mode->num_channels = 14;
diff --git a/src/drivers/driver_macsec_linux.c b/src/drivers/driver_macsec_linux.c
index 5dab77a..e89b3ba 100644
--- a/src/drivers/driver_macsec_linux.c
+++ b/src/drivers/driver_macsec_linux.c
@@ -168,6 +168,9 @@
 {
 	int err;
 
+	if (!drv->sk)
+		return 0;
+
 	if (!drv->link)
 		return 0;
 
@@ -231,10 +234,44 @@
 }
 
 
+static int macsec_check_macsec(void)
+{
+	struct nl_sock *sk;
+	int err = -1;
+
+	sk = nl_socket_alloc();
+	if (!sk) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc genl socket");
+		return -1;
+	}
+
+	if (genl_connect(sk) < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "connection to genl socket failed");
+		goto out_free;
+	}
+
+	if (genl_ctrl_resolve(sk, "macsec") < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "genl resolve failed - macsec kernel module not present?");
+		goto out_free;
+	}
+
+	err = 0;
+
+out_free:
+	nl_socket_free(sk);
+	return err;
+}
+
+
 static void * macsec_drv_wpa_init(void *ctx, const char *ifname)
 {
 	struct macsec_drv_data *drv;
 
+	if (macsec_check_macsec() < 0)
+		return NULL;
+
 	drv = os_zalloc(sizeof(*drv));
 	if (!drv)
 		return NULL;
@@ -982,6 +1019,11 @@
 
 	wpa_printf(MSG_DEBUG, "%s", __func__);
 
+	if (!drv->sk) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "NULL rtnl socket");
+		return -1;
+	}
+
 	link = rtnl_link_macsec_alloc();
 	if (!link) {
 		wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't allocate link");
@@ -1048,6 +1090,9 @@
 
 	wpa_printf(MSG_DEBUG, "%s", __func__);
 
+	if (!drv->sk)
+		return 0;
+
 	if (!drv->created_link) {
 		rtnl_link_put(drv->link);
 		drv->link = NULL;
diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c
index 9440f01..614c452 100644
--- a/src/drivers/driver_ndis.c
+++ b/src/drivers/driver_ndis.c
@@ -1220,12 +1220,16 @@
 }
 
 
-static int wpa_driver_ndis_add_pmkid(void *priv, const u8 *bssid,
-				     const u8 *pmkid)
+static int wpa_driver_ndis_add_pmkid(void *priv,
+				     struct wpa_pmkid_params *params)
 {
 	struct wpa_driver_ndis_data *drv = priv;
 	struct ndis_pmkid_entry *entry, *prev;
+	const u8 *bssid = params->bssid;
+	const u8 *pmkid = params->pmkid;
 
+	if (!bssid || !pmkid)
+		return -1;
 	if (drv->no_of_pmkid == 0)
 		return 0;
 
@@ -1261,12 +1265,16 @@
 }
 
 
-static int wpa_driver_ndis_remove_pmkid(void *priv, const u8 *bssid,
-		 			const u8 *pmkid)
+static int wpa_driver_ndis_remove_pmkid(void *priv,
+					struct wpa_pmkid_params *params)
 {
 	struct wpa_driver_ndis_data *drv = priv;
 	struct ndis_pmkid_entry *entry, *prev;
+	const u8 *bssid = params->bssid;
+	const u8 *pmkid = params->pmkid;
 
+	if (!bssid || !pmkid)
+		return -1;
 	if (drv->no_of_pmkid == 0)
 		return 0;
 
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index e9107b3..2ce03ed 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -911,7 +911,8 @@
 		nl80211_check_global(drv->global);
 		wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed "
 			   "interface");
-		wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL);
+		if (wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL) < 0)
+			return -1;
 		return 1;
 	}
 
@@ -920,19 +921,55 @@
 
 
 static struct wpa_driver_nl80211_data *
-nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len)
+nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len,
+		 int *init_failed)
 {
 	struct wpa_driver_nl80211_data *drv;
+	int res;
+
+	if (init_failed)
+		*init_failed = 0;
 	dl_list_for_each(drv, &global->interfaces,
 			 struct wpa_driver_nl80211_data, list) {
-		if (wpa_driver_nl80211_own_ifindex(drv, idx, buf, len) ||
-		    have_ifidx(drv, idx, IFIDX_ANY))
+		res = wpa_driver_nl80211_own_ifindex(drv, idx, buf, len);
+		if (res < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Found matching own interface, but failed to complete reinitialization");
+			if (init_failed)
+				*init_failed = 1;
+			return drv;
+		}
+		if (res > 0 || have_ifidx(drv, idx, IFIDX_ANY))
 			return drv;
 	}
 	return NULL;
 }
 
 
+static void nl80211_refresh_mac(struct wpa_driver_nl80211_data *drv,
+				int ifindex)
+{
+	struct i802_bss *bss;
+	u8 addr[ETH_ALEN];
+
+	bss = get_bss_ifindex(drv, ifindex);
+	if (bss &&
+	    linux_get_ifhwaddr(drv->global->ioctl_sock,
+			       bss->ifname, addr) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: %s: failed to re-read MAC address",
+			   bss->ifname);
+	} else if (bss && os_memcmp(addr, bss->addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Own MAC address on ifindex %d (%s) changed from "
+			   MACSTR " to " MACSTR,
+			   ifindex, bss->ifname,
+			   MAC2STR(bss->addr), MAC2STR(addr));
+		os_memcpy(bss->addr, addr, ETH_ALEN);
+	}
+}
+
+
 static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
 						 struct ifinfomsg *ifi,
 						 u8 *buf, size_t len)
@@ -945,6 +982,7 @@
 	char namebuf[IFNAMSIZ];
 	char ifname[IFNAMSIZ + 1];
 	char extra[100], *pos, *end;
+	int init_failed;
 
 	extra[0] = '\0';
 	pos = extra;
@@ -989,14 +1027,18 @@
 		   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
 		   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
 
-	drv = nl80211_find_drv(global, ifi->ifi_index, buf, len);
+	drv = nl80211_find_drv(global, ifi->ifi_index, buf, len, &init_failed);
 	if (!drv)
 		goto event_newlink;
+	if (init_failed)
+		return; /* do not update interface state */
 
 	if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
 		namebuf[0] = '\0';
 		if (if_indextoname(ifi->ifi_index, namebuf) &&
 		    linux_iface_up(drv->global->ioctl_sock, namebuf) > 0) {
+			/* Re-read MAC address as it may have changed */
+			nl80211_refresh_mac(drv, ifi->ifi_index);
 			wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
 				   "event since interface %s is up", namebuf);
 			drv->ignore_if_down_event = 0;
@@ -1023,7 +1065,7 @@
 			 * dynamic interfaces
 			 */
 			drv = nl80211_find_drv(global, ifi->ifi_index,
-					       buf, len);
+					       buf, len, NULL);
 			if (!drv)
 				return;
 		}
@@ -1044,27 +1086,8 @@
 				   "event since interface %s is marked "
 				   "removed", drv->first_bss->ifname);
 		} else {
-			struct i802_bss *bss;
-			u8 addr[ETH_ALEN];
-
 			/* Re-read MAC address as it may have changed */
-			bss = get_bss_ifindex(drv, ifi->ifi_index);
-			if (bss &&
-			    linux_get_ifhwaddr(drv->global->ioctl_sock,
-					       bss->ifname, addr) < 0) {
-				wpa_printf(MSG_DEBUG,
-					   "nl80211: %s: failed to re-read MAC address",
-					   bss->ifname);
-			} else if (bss &&
-				   os_memcmp(addr, bss->addr, ETH_ALEN) != 0) {
-				wpa_printf(MSG_DEBUG,
-					   "nl80211: Own MAC address on ifindex %d (%s) changed from "
-					   MACSTR " to " MACSTR,
-					   ifi->ifi_index, bss->ifname,
-					   MAC2STR(bss->addr),
-					   MAC2STR(addr));
-				os_memcpy(bss->addr, addr, ETH_ALEN);
-			}
+			nl80211_refresh_mac(drv, ifi->ifi_index);
 
 			wpa_printf(MSG_DEBUG, "nl80211: Interface up");
 			drv->if_disabled = 0;
@@ -1168,7 +1191,7 @@
 		   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
 		   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
 
-	drv = nl80211_find_drv(global, ifi->ifi_index, buf, len);
+	drv = nl80211_find_drv(global, ifi->ifi_index, buf, len, NULL);
 
 	if (ifi->ifi_family == AF_BRIDGE && brid && drv) {
 		/* device has been removed from bridge */
@@ -2009,7 +2032,7 @@
 	if (nl80211_register_action_frame(bss, (u8 *) "\x01\x04", 2) < 0)
 		ret = -1;
 #endif /* CONFIG_INTERWORKING */
-#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING)
+#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP)
 	/* GAS Initial Request */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0a", 2) < 0)
 		ret = -1;
@@ -2034,7 +2057,7 @@
 	/* Protected GAS Comeback Response */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0d", 2) < 0)
 		ret = -1;
-#endif /* CONFIG_P2P || CONFIG_INTERWORKING */
+#endif /* CONFIG_P2P || CONFIG_INTERWORKING || CONFIG_DPP */
 #ifdef CONFIG_P2P
 	/* P2P Public Action */
 	if (nl80211_register_action_frame(bss,
@@ -2047,6 +2070,13 @@
 					  5) < 0)
 		ret = -1;
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_DPP
+	/* DPP Public Action */
+	if (nl80211_register_action_frame(bss,
+					  (u8 *) "\x04\x09\x50\x6f\x9a\x1a",
+					  6) < 0)
+		ret = -1;
+#endif /* CONFIG_DPP */
 #ifdef CONFIG_IEEE80211W
 	/* SA Query Response */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0)
@@ -3125,6 +3155,27 @@
 }
 
 
+static enum nl80211_auth_type get_nl_auth_type(int wpa_auth_alg)
+{
+	if (wpa_auth_alg & WPA_AUTH_ALG_OPEN)
+		return NL80211_AUTHTYPE_OPEN_SYSTEM;
+	if (wpa_auth_alg & WPA_AUTH_ALG_SHARED)
+		return NL80211_AUTHTYPE_SHARED_KEY;
+	if (wpa_auth_alg & WPA_AUTH_ALG_LEAP)
+		return NL80211_AUTHTYPE_NETWORK_EAP;
+	if (wpa_auth_alg & WPA_AUTH_ALG_FT)
+		return NL80211_AUTHTYPE_FT;
+	if (wpa_auth_alg & WPA_AUTH_ALG_SAE)
+		return NL80211_AUTHTYPE_SAE;
+	if (wpa_auth_alg & WPA_AUTH_ALG_FILS)
+		return NL80211_AUTHTYPE_FILS_SK;
+	if (wpa_auth_alg & WPA_AUTH_ALG_FILS_SK_PFS)
+		return NL80211_AUTHTYPE_FILS_SK_PFS;
+
+	return NL80211_AUTHTYPE_MAX;
+}
+
+
 static int wpa_driver_nl80211_authenticate(
 	struct i802_bss *bss, struct wpa_driver_auth_params *params)
 {
@@ -3207,22 +3258,10 @@
 			    params->auth_data))
 			goto fail;
 	}
-	if (params->auth_alg & WPA_AUTH_ALG_OPEN)
-		type = NL80211_AUTHTYPE_OPEN_SYSTEM;
-	else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
-		type = NL80211_AUTHTYPE_SHARED_KEY;
-	else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
-		type = NL80211_AUTHTYPE_NETWORK_EAP;
-	else if (params->auth_alg & WPA_AUTH_ALG_FT)
-		type = NL80211_AUTHTYPE_FT;
-	else if (params->auth_alg & WPA_AUTH_ALG_SAE)
-		type = NL80211_AUTHTYPE_SAE;
-	else if (params->auth_alg & WPA_AUTH_ALG_FILS)
-		type = NL80211_AUTHTYPE_FILS_SK;
-	else
-		goto fail;
+	type = get_nl_auth_type(params->auth_alg);
 	wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
-	if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
+	if (type == NL80211_AUTHTYPE_MAX ||
+	    nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
 		goto fail;
 	if (params->local_state_change) {
 		wpa_printf(MSG_DEBUG, "  * Local state change only");
@@ -5065,6 +5104,47 @@
 }
 
 
+static int nl80211_put_fils_connect_params(struct wpa_driver_nl80211_data *drv,
+					   struct wpa_driver_associate_params *params,
+					   struct nl_msg *msg)
+{
+	if (params->fils_erp_username_len) {
+		wpa_hexdump_ascii(MSG_DEBUG, "  * FILS ERP EMSKname/username",
+				  params->fils_erp_username,
+				  params->fils_erp_username_len);
+		if (nla_put(msg, NL80211_ATTR_FILS_ERP_USERNAME,
+			    params->fils_erp_username_len,
+			    params->fils_erp_username))
+			return -1;
+	}
+
+	if (params->fils_erp_realm_len) {
+		wpa_hexdump_ascii(MSG_DEBUG, "  * FILS ERP Realm",
+				  params->fils_erp_realm,
+				  params->fils_erp_realm_len);
+		if (nla_put(msg, NL80211_ATTR_FILS_ERP_REALM,
+			    params->fils_erp_realm_len, params->fils_erp_realm))
+			return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "  * FILS ERP next seq %u",
+		   params->fils_erp_next_seq_num);
+	if (nla_put_u16(msg, NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM,
+			params->fils_erp_next_seq_num))
+		return -1;
+
+	if (params->fils_erp_rrk_len) {
+		wpa_printf(MSG_DEBUG, "  * FILS ERP rRK (len=%lu)",
+			   (unsigned long) params->fils_erp_rrk_len);
+		if (nla_put(msg, NL80211_ATTR_FILS_ERP_RRK,
+			    params->fils_erp_rrk_len, params->fils_erp_rrk))
+			return -1;
+	}
+
+	return 0;
+}
+
+
 static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
 				  struct wpa_driver_associate_params *params,
 				  struct nl_msg *msg)
@@ -5172,7 +5252,11 @@
 	    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_IEEE8021X_SUITE_B ||
-	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 ||
+	    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 ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA384) {
 		int mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
 
 		switch (params->key_mgmt_suite) {
@@ -5203,6 +5287,18 @@
 		case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
 			mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
 			break;
+		case WPA_KEY_MGMT_FILS_SHA256:
+			mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA256;
+			break;
+		case WPA_KEY_MGMT_FILS_SHA384:
+			mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA384;
+			break;
+		case WPA_KEY_MGMT_FT_FILS_SHA256:
+			mgmt = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256;
+			break;
+		case WPA_KEY_MGMT_FT_FILS_SHA384:
+			mgmt = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
+			break;
 		case WPA_KEY_MGMT_PSK:
 		default:
 			mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
@@ -5260,6 +5356,10 @@
 		drv->connect_reassoc = 1;
 	}
 
+	if ((params->auth_alg & WPA_AUTH_ALG_FILS) &&
+	    nl80211_put_fils_connect_params(drv, params, msg) != 0)
+		return -1;
+
 	return 0;
 }
 
@@ -5301,25 +5401,18 @@
 		algs++;
 	if (params->auth_alg & WPA_AUTH_ALG_LEAP)
 		algs++;
+	if (params->auth_alg & WPA_AUTH_ALG_FILS)
+		algs++;
 	if (algs > 1) {
 		wpa_printf(MSG_DEBUG, "  * Leave out Auth Type for automatic "
 			   "selection");
 		goto skip_auth_type;
 	}
 
-	if (params->auth_alg & WPA_AUTH_ALG_OPEN)
-		type = NL80211_AUTHTYPE_OPEN_SYSTEM;
-	else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
-		type = NL80211_AUTHTYPE_SHARED_KEY;
-	else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
-		type = NL80211_AUTHTYPE_NETWORK_EAP;
-	else if (params->auth_alg & WPA_AUTH_ALG_FT)
-		type = NL80211_AUTHTYPE_FT;
-	else
-		goto fail;
-
+	type = get_nl_auth_type(params->auth_alg);
 	wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
-	if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
+	if (type == NL80211_AUTHTYPE_MAX ||
+	    nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
 		goto fail;
 
 skip_auth_type:
@@ -5844,6 +5937,16 @@
 		[NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
 		[NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 },
 		[NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 },
+		[NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
+	};
+	struct nlattr *rate[NL80211_RATE_INFO_MAX + 1];
+	static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
+		[NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
+		[NL80211_RATE_INFO_BITRATE32] = { .type = NLA_U32 },
+		[NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
+		[NL80211_RATE_INFO_VHT_MCS] = { .type = NLA_U8 },
+		[NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
+		[NL80211_RATE_INFO_VHT_NSS] = { .type = NLA_U8 },
 	};
 
 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
@@ -5895,6 +5998,67 @@
 	if (stats[NL80211_STA_INFO_TX_FAILED])
 		data->tx_retry_failed =
 			nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]);
+	if (stats[NL80211_STA_INFO_SIGNAL])
+		data->signal = nla_get_u8(stats[NL80211_STA_INFO_SIGNAL]);
+
+	if (stats[NL80211_STA_INFO_TX_BITRATE] &&
+	    nla_parse_nested(rate, NL80211_RATE_INFO_MAX,
+			     stats[NL80211_STA_INFO_TX_BITRATE],
+			     rate_policy) == 0) {
+		if (rate[NL80211_RATE_INFO_BITRATE32])
+			data->current_tx_rate =
+				nla_get_u32(rate[NL80211_RATE_INFO_BITRATE32]);
+		else if (rate[NL80211_RATE_INFO_BITRATE])
+			data->current_tx_rate =
+				nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]);
+
+		if (rate[NL80211_RATE_INFO_MCS]) {
+			data->tx_mcs = nla_get_u8(rate[NL80211_RATE_INFO_MCS]);
+			data->flags |= STA_DRV_DATA_TX_MCS;
+		}
+		if (rate[NL80211_RATE_INFO_VHT_MCS]) {
+			data->tx_vhtmcs =
+				nla_get_u8(rate[NL80211_RATE_INFO_VHT_MCS]);
+			data->flags |= STA_DRV_DATA_TX_VHT_MCS;
+		}
+		if (rate[NL80211_RATE_INFO_SHORT_GI])
+			data->flags |= STA_DRV_DATA_TX_SHORT_GI;
+		if (rate[NL80211_RATE_INFO_VHT_NSS]) {
+			data->tx_vht_nss =
+				nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]);
+			data->flags |= STA_DRV_DATA_TX_VHT_NSS;
+		}
+	}
+
+	if (stats[NL80211_STA_INFO_RX_BITRATE] &&
+	    nla_parse_nested(rate, NL80211_RATE_INFO_MAX,
+			     stats[NL80211_STA_INFO_RX_BITRATE],
+			     rate_policy) == 0) {
+		if (rate[NL80211_RATE_INFO_BITRATE32])
+			data->current_rx_rate =
+				nla_get_u32(rate[NL80211_RATE_INFO_BITRATE32]);
+		else if (rate[NL80211_RATE_INFO_BITRATE])
+			data->current_rx_rate =
+				nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]);
+
+		if (rate[NL80211_RATE_INFO_MCS]) {
+			data->rx_mcs =
+				nla_get_u8(rate[NL80211_RATE_INFO_MCS]);
+			data->flags |= STA_DRV_DATA_RX_MCS;
+		}
+		if (rate[NL80211_RATE_INFO_VHT_MCS]) {
+			data->rx_vhtmcs =
+				nla_get_u8(rate[NL80211_RATE_INFO_VHT_MCS]);
+			data->flags |= STA_DRV_DATA_RX_VHT_MCS;
+		}
+		if (rate[NL80211_RATE_INFO_SHORT_GI])
+			data->flags |= STA_DRV_DATA_RX_SHORT_GI;
+		if (rate[NL80211_RATE_INFO_VHT_NSS]) {
+			data->rx_vht_nss =
+				nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]);
+			data->flags |= STA_DRV_DATA_RX_VHT_NSS;
+		}
+	}
 
 	return NL_SKIP;
 }
@@ -6319,7 +6483,7 @@
 			wpa_printf(MSG_ERROR, "nl80211: Failed to "
 				   "remove interface %s from bridge "
 				   "%s: %s",
-				   ifname, brname, strerror(errno));
+				   ifname, in_br, strerror(errno));
 			return -1;
 		}
 	}
@@ -7395,14 +7559,23 @@
 }
 
 
-static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid,
-			 const u8 *pmkid)
+static int nl80211_pmkid(struct i802_bss *bss, int cmd,
+			 struct wpa_pmkid_params *params)
 {
 	struct nl_msg *msg;
 
 	if (!(msg = nl80211_bss_msg(bss, 0, cmd)) ||
-	    (pmkid && nla_put(msg, NL80211_ATTR_PMKID, 16, pmkid)) ||
-	    (bssid && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid))) {
+	    (params->pmkid &&
+	     nla_put(msg, NL80211_ATTR_PMKID, 16, params->pmkid)) ||
+	    (params->bssid &&
+	     nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid)) ||
+	    (params->ssid_len &&
+	     nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) ||
+	    (params->fils_cache_id &&
+	     nla_put(msg, NL80211_ATTR_FILS_CACHE_ID, 2,
+		     params->fils_cache_id)) ||
+	    (params->pmk_len &&
+	     nla_put(msg, NL80211_ATTR_PMK, params->pmk_len, params->pmk))) {
 		nlmsg_free(msg);
 		return -ENOBUFS;
 	}
@@ -7411,28 +7584,52 @@
 }
 
 
-static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
+static int nl80211_add_pmkid(void *priv, struct wpa_pmkid_params *params)
 {
 	struct i802_bss *bss = priv;
-	wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR, MAC2STR(bssid));
-	return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, bssid, pmkid);
+
+	if (params->bssid)
+		wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR,
+			   MAC2STR(params->bssid));
+	else if (params->fils_cache_id && params->ssid_len) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Add PMKSA for cache id %02x%02x SSID %s",
+			   params->fils_cache_id[0], params->fils_cache_id[1],
+			   wpa_ssid_txt(params->ssid, params->ssid_len));
+	}
+
+	return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params);
 }
 
 
-static int nl80211_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
+static int nl80211_remove_pmkid(void *priv, struct wpa_pmkid_params *params)
 {
 	struct i802_bss *bss = priv;
-	wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR,
-		   MAC2STR(bssid));
-	return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, bssid, pmkid);
+
+	if (params->bssid)
+		wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR,
+			   MAC2STR(params->bssid));
+	else if (params->fils_cache_id && params->ssid_len) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Delete PMKSA for cache id %02x%02x SSID %s",
+			   params->fils_cache_id[0], params->fils_cache_id[1],
+			   wpa_ssid_txt(params->ssid, params->ssid_len));
+	}
+
+	return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, params);
 }
 
 
 static int nl80211_flush_pmkid(void *priv)
 {
 	struct i802_bss *bss = priv;
+	struct nl_msg *msg;
+
 	wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs");
-	return nl80211_pmkid(bss, NL80211_CMD_FLUSH_PMKSA, NULL, NULL);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_FLUSH_PMKSA);
+	if (!msg)
+		return -ENOBUFS;
+	return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
 }
 
 
@@ -7627,7 +7824,7 @@
 	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_REKEY_OFFLOAD)) ||
 	    !(replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA)) ||
 	    nla_put(msg, NL80211_REKEY_DATA_KEK, kek_len, kek) ||
-	    nla_put(msg, NL80211_REKEY_DATA_KCK, kck_len, kck) ||
+	    (kck_len && nla_put(msg, NL80211_REKEY_DATA_KCK, kck_len, kck)) ||
 	    nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN,
 		    replay_ctr)) {
 		nl80211_nlmsg_clear(msg);
@@ -8686,6 +8883,67 @@
 
 	return send_and_recv_msgs(drv, msg, NULL, NULL);
 }
+
+
+/* Reserved QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID value for wpa_supplicant */
+#define WPA_SUPPLICANT_CLIENT_ID 1
+
+static int nl80211_set_bssid_blacklist(void *priv, unsigned int num_bssid,
+				       const u8 *bssid)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *params, *nlbssids, *attr;
+	unsigned int i;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Set blacklist BSSID (num=%u)",
+		   num_bssid);
+
+	if (!drv->roam_vendor_cmd_avail)
+		return -1;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_ROAM) ||
+	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD,
+			QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BLACKLIST_BSSID) ||
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID,
+			WPA_SUPPLICANT_CLIENT_ID) ||
+	    nla_put_u32(msg,
+			QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID,
+			num_bssid))
+		goto fail;
+
+	nlbssids = nla_nest_start(
+		msg, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS);
+	if (!nlbssids)
+		goto fail;
+
+	for (i = 0; i < num_bssid; i++) {
+		attr = nla_nest_start(msg, i);
+		if (!attr)
+			goto fail;
+		if (nla_put(msg,
+			    QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID,
+			    ETH_ALEN, &bssid[i * ETH_ALEN]))
+			goto fail;
+		wpa_printf(MSG_DEBUG, "nl80211:   BSSID[%u]: " MACSTR, i,
+			   MAC2STR(&bssid[i * ETH_ALEN]));
+		nla_nest_end(msg, attr);
+	}
+	nla_nest_end(msg, nlbssids);
+	nla_nest_end(msg, params);
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL);
+
+fail:
+	nlmsg_free(msg);
+	return -1;
+}
+
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 
 
@@ -8772,7 +9030,10 @@
 			 params->auto_plinks)) ||
 	    ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS) &&
 	     nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
-			 params->max_peer_links)))
+			 params->max_peer_links)) ||
+	    ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD) &&
+	     nla_put_u32(msg, NL80211_MESHCONF_RSSI_THRESHOLD,
+			 params->rssi_threshold)))
 		return -1;
 
 	/*
@@ -9655,6 +9916,254 @@
 	return -1;
 }
 
+
+#ifdef CONFIG_MBO
+
+static enum mbo_transition_reject_reason
+nl80211_mbo_reject_reason_mapping(enum qca_wlan_btm_candidate_status status)
+{
+	switch (status) {
+	case QCA_STATUS_REJECT_EXCESSIVE_FRAME_LOSS_EXPECTED:
+		return MBO_TRANSITION_REJECT_REASON_FRAME_LOSS;
+	case QCA_STATUS_REJECT_EXCESSIVE_DELAY_EXPECTED:
+		return MBO_TRANSITION_REJECT_REASON_DELAY;
+	case QCA_STATUS_REJECT_INSUFFICIENT_QOS_CAPACITY:
+		return MBO_TRANSITION_REJECT_REASON_QOS_CAPACITY;
+	case QCA_STATUS_REJECT_LOW_RSSI:
+		return MBO_TRANSITION_REJECT_REASON_RSSI;
+	case QCA_STATUS_REJECT_HIGH_INTERFERENCE:
+		return MBO_TRANSITION_REJECT_REASON_INTERFERENCE;
+	case QCA_STATUS_REJECT_UNKNOWN:
+	default:
+		return MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
+	}
+}
+
+
+static void nl80211_parse_btm_candidate_info(struct candidate_list *candidate,
+					     struct nlattr *tb[], int num)
+{
+	enum qca_wlan_btm_candidate_status status;
+	char buf[50];
+
+	os_memcpy(candidate->bssid,
+		  nla_data(tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID]),
+		  ETH_ALEN);
+
+	status = nla_get_u32(
+		tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS]);
+	candidate->is_accept = status == QCA_STATUS_ACCEPT;
+	candidate->reject_reason = nl80211_mbo_reject_reason_mapping(status);
+
+	if (candidate->is_accept)
+		os_snprintf(buf, sizeof(buf), "Accepted");
+	else
+		os_snprintf(buf, sizeof(buf),
+			    "Rejected, Reject_reason: %d",
+			    candidate->reject_reason);
+	wpa_printf(MSG_DEBUG, "nl80211:   BSSID[%d]: " MACSTR " %s",
+		   num, MAC2STR(candidate->bssid), buf);
+}
+
+
+static int
+nl80211_get_bss_transition_status_handler(struct nl_msg *msg, void *arg)
+{
+	struct wpa_bss_candidate_info *info = arg;
+	struct candidate_list *candidate = info->candidates;
+	struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+	struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1];
+	static struct nla_policy policy[
+		QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1] = {
+		[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID] = {
+			.minlen = ETH_ALEN
+		},
+		[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS] = {
+			.type = NLA_U32,
+		},
+	};
+	struct nlattr *attr;
+	int rem;
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	u8 num;
+
+	num = info->num; /* number of candidates sent to driver */
+	info->num = 0;
+	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (!tb_msg[NL80211_ATTR_VENDOR_DATA] ||
+	    nla_parse_nested(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
+			     tb_msg[NL80211_ATTR_VENDOR_DATA], NULL) ||
+	    !tb_vendor[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO])
+		return NL_SKIP;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: WNM Candidate list received from driver");
+	nla_for_each_nested(attr,
+			    tb_vendor[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO],
+			    rem) {
+		if (info->num >= num ||
+		    nla_parse_nested(
+			    tb, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX,
+			    attr, policy) ||
+		    !tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID] ||
+		    !tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS])
+			break;
+
+		nl80211_parse_btm_candidate_info(candidate, tb, info->num);
+
+		candidate++;
+		info->num++;
+	}
+
+	return NL_SKIP;
+}
+
+
+static struct wpa_bss_candidate_info *
+nl80211_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *attr, *attr1, *attr2;
+	struct wpa_bss_candidate_info *info;
+	u8 i;
+	int ret;
+	u8 *pos;
+
+	if (!drv->fetch_bss_trans_status)
+		return NULL;
+
+	info = os_zalloc(sizeof(*info));
+	if (!info)
+		return NULL;
+	/* Allocate memory for number of candidates sent to driver */
+	info->candidates = os_calloc(params->n_candidates,
+				     sizeof(*info->candidates));
+	if (!info->candidates) {
+		os_free(info);
+		return NULL;
+	}
+
+	/* Copy the number of candidates being sent to driver. This is used in
+	 * nl80211_get_bss_transition_status_handler() to limit the number of
+	 * candidates that can be populated in info->candidates and will be
+	 * later overwritten with the actual number of candidates received from
+	 * the driver.
+	 */
+	info->num = params->n_candidates;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS))
+		goto fail;
+
+	attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+	if (!attr)
+		goto fail;
+
+	if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON,
+		       params->mbo_transition_reason))
+		goto fail;
+
+	attr1 = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO);
+	if (!attr1)
+		goto fail;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: WNM Candidate list info sending to driver: mbo_transition_reason: %d n_candidates: %d",
+		   params->mbo_transition_reason, params->n_candidates);
+	pos = params->bssid;
+	for (i = 0; i < params->n_candidates; i++) {
+		wpa_printf(MSG_DEBUG, "nl80211:   BSSID[%d]: " MACSTR, i,
+			   MAC2STR(pos));
+		attr2 = nla_nest_start(msg, i);
+		if (!attr2 ||
+		    nla_put(msg, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID,
+			    ETH_ALEN, pos))
+			goto fail;
+		pos += ETH_ALEN;
+		nla_nest_end(msg, attr2);
+	}
+
+	nla_nest_end(msg, attr1);
+	nla_nest_end(msg, attr);
+
+	ret = send_and_recv_msgs(drv, msg,
+				 nl80211_get_bss_transition_status_handler,
+				 info);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: WNM Get BSS transition status failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+		goto fail;
+	}
+	return info;
+
+fail:
+	nlmsg_free(msg);
+	os_free(info->candidates);
+	os_free(info);
+	return NULL;
+}
+
+
+/**
+ * nl80211_ignore_assoc_disallow - Configure driver to ignore assoc_disallow
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @ignore_assoc_disallow: 0 to not ignore, 1 to ignore
+ * Returns: 0 on success, -1 on failure
+ */
+static int nl80211_ignore_assoc_disallow(void *priv, int ignore_disallow)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *attr;
+	int ret = -1;
+
+	if (!drv->set_wifi_conf_vendor_cmd_avail)
+		return -1;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION))
+		goto fail;
+
+	attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+	if (!attr)
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Set ignore_assoc_disallow %d",
+		   ignore_disallow);
+	if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED,
+		       ignore_disallow))
+		goto fail;
+
+	nla_nest_end(msg, attr);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Set ignore_assoc_disallow failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+		goto fail;
+	}
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+#endif /* CONFIG_MBO */
+
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 
 
@@ -9782,6 +10291,56 @@
 }
 
 
+static int nl80211_update_connection_params(
+	void *priv, struct wpa_driver_associate_params *params,
+	enum wpa_drv_update_connect_params_mask mask)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret = -1;
+	enum nl80211_auth_type type;
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_CONNECT_PARAMS);
+	if (!msg)
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Update connection params (ifindex=%d)",
+		   drv->ifindex);
+
+	if ((mask & WPA_DRV_UPDATE_ASSOC_IES) && params->wpa_ie) {
+		if (nla_put(msg, NL80211_ATTR_IE, params->wpa_ie_len,
+			    params->wpa_ie))
+			goto fail;
+		wpa_hexdump(MSG_DEBUG, "  * IEs", params->wpa_ie,
+			    params->wpa_ie_len);
+	}
+
+	if (mask & WPA_DRV_UPDATE_AUTH_TYPE) {
+		type = get_nl_auth_type(params->auth_alg);
+		if (type == NL80211_AUTHTYPE_MAX ||
+		    nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
+			goto fail;
+		wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
+	}
+
+	if ((mask & WPA_DRV_UPDATE_FILS_ERP_INFO) &&
+	    nl80211_put_fils_connect_params(drv, params, msg))
+		goto fail;
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (ret)
+		wpa_dbg(drv->ctx, MSG_DEBUG,
+			"nl80211: Update connect params command failed: ret=%d (%s)",
+			ret, strerror(-ret));
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 	.name = "nl80211",
 	.desc = "Linux nl80211/cfg80211",
@@ -9899,7 +10458,13 @@
 	.p2p_lo_stop = nl80211_p2p_lo_stop,
 	.set_default_scan_ies = nl80211_set_default_scan_ies,
 	.set_tdls_mode = nl80211_set_tdls_mode,
+#ifdef CONFIG_MBO
+	.get_bss_transition_status = nl80211_get_bss_transition_status,
+	.ignore_assoc_disallow = nl80211_ignore_assoc_disallow,
+#endif /* CONFIG_MBO */
+	.set_bssid_blacklist = nl80211_set_bssid_blacklist,
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 	.configure_data_frame_filters = nl80211_configure_data_frame_filters,
 	.get_ext_capab = nl80211_get_ext_capab,
+	.update_connect_params = nl80211_update_connection_params,
 };
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index bdc79c5..23cf9db 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -162,6 +162,8 @@
 	unsigned int connect_reassoc:1;
 	unsigned int set_wifi_conf_vendor_cmd_avail:1;
 	unsigned int he_capab_vendor_cmd_avail:1;
+	unsigned int fetch_bss_trans_status:1;
+	unsigned int roam_vendor_cmd_avail:1;
 
 	u64 vendor_scan_cookie;
 	u64 remain_on_chan_cookie;
@@ -259,7 +261,8 @@
 
 int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv);
 struct hostapd_hw_modes *
-nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags);
+nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags,
+			    u8 *dfs_domain);
 
 int process_global_event(struct nl_msg *msg, void *arg);
 int process_bss_event(struct nl_msg *msg, void *arg);
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 7064ce1..f11a1d7 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -398,6 +398,9 @@
 	if (ext_feature_isset(ext_features, len,
 			      NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI))
 		capa->flags |= WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI;
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_FILS_SK_OFFLOAD))
+		capa->flags |= WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD;
 }
 
 
@@ -546,23 +549,22 @@
 			   nl80211_iftype_str(capa->iftype));
 
 		len = nla_len(tb1[NL80211_ATTR_EXT_CAPA]);
-		capa->ext_capa = os_malloc(len);
+		capa->ext_capa = os_memdup(nla_data(tb1[NL80211_ATTR_EXT_CAPA]),
+					   len);
 		if (!capa->ext_capa)
 			goto err;
 
-		os_memcpy(capa->ext_capa, nla_data(tb1[NL80211_ATTR_EXT_CAPA]),
-			  len);
 		capa->ext_capa_len = len;
 		wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities",
 			    capa->ext_capa, capa->ext_capa_len);
 
 		len = nla_len(tb1[NL80211_ATTR_EXT_CAPA_MASK]);
-		capa->ext_capa_mask = os_malloc(len);
+		capa->ext_capa_mask =
+			os_memdup(nla_data(tb1[NL80211_ATTR_EXT_CAPA_MASK]),
+				  len);
 		if (!capa->ext_capa_mask)
 			goto err;
 
-		os_memcpy(capa->ext_capa_mask,
-			  nla_data(tb1[NL80211_ATTR_EXT_CAPA_MASK]), len);
 		wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities mask",
 			    capa->ext_capa_mask, capa->ext_capa_len);
 
@@ -747,6 +749,12 @@
 				case QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES:
 					drv->he_capab_vendor_cmd_avail = 1;
 					break;
+				case QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS:
+					drv->fetch_bss_trans_status = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_ROAM:
+					drv->roam_vendor_cmd_avail = 1;
+					break;
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 				}
 			}
@@ -1106,6 +1114,12 @@
 		drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS;
 	if (check_feature(QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD, &info))
 		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD;
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_STA, &info))
+		drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_STA;
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_AP, &info))
+		drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_AP;
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON, &info))
+		drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_STA_CFON;
 	os_free(info.flags);
 }
 
@@ -1127,7 +1141,19 @@
 		WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
 		WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK |
 		WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B |
-		WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
+		WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192 |
+		WPA_DRIVER_CAPA_KEY_MGMT_OWE |
+		WPA_DRIVER_CAPA_KEY_MGMT_DPP;
+
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME)
+		drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
+			WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 |
+			WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 |
+			WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384;
+	else if (drv->capa.flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD)
+		drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
+			WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
+
 	drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
 		WPA_DRIVER_AUTH_SHARED |
 		WPA_DRIVER_AUTH_LEAP;
@@ -1203,6 +1229,7 @@
 	struct hostapd_hw_modes *modes;
 	int last_mode, last_chan_idx;
 	int failed;
+	u8 dfs_domain;
 };
 
 static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa,
@@ -1522,14 +1549,13 @@
 
 	mode11g = &modes[mode11g_idx];
 	mode->num_channels = mode11g->num_channels;
-	mode->channels = os_malloc(mode11g->num_channels *
+	mode->channels = os_memdup(mode11g->channels,
+				   mode11g->num_channels *
 				   sizeof(struct hostapd_channel_data));
 	if (mode->channels == NULL) {
 		(*num_modes)--;
 		return modes; /* Could not add 802.11b mode */
 	}
-	os_memcpy(mode->channels, mode11g->channels,
-		  mode11g->num_channels * sizeof(struct hostapd_channel_data));
 
 	mode->num_rates = 0;
 	mode->rates = os_malloc(4 * sizeof(int));
@@ -1732,6 +1758,20 @@
 }
 
 
+static void nl80211_set_dfs_domain(enum nl80211_dfs_regions region,
+				   u8 *dfs_domain)
+{
+	if (region == NL80211_DFS_FCC)
+		*dfs_domain = HOSTAPD_DFS_REGION_FCC;
+	else if (region == NL80211_DFS_ETSI)
+		*dfs_domain = HOSTAPD_DFS_REGION_ETSI;
+	else if (region == NL80211_DFS_JP)
+		*dfs_domain = HOSTAPD_DFS_REGION_JP;
+	else
+		*dfs_domain = 0;
+}
+
+
 static const char * dfs_domain_name(enum nl80211_dfs_regions region)
 {
 	switch (region) {
@@ -1778,6 +1818,7 @@
 	if (tb_msg[NL80211_ATTR_DFS_REGION]) {
 		enum nl80211_dfs_regions dfs_domain;
 		dfs_domain = nla_get_u8(tb_msg[NL80211_ATTR_DFS_REGION]);
+		nl80211_set_dfs_domain(dfs_domain, &results->dfs_domain);
 		wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s (%s)",
 			   (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]),
 			   dfs_domain_name(dfs_domain));
@@ -1854,7 +1895,8 @@
 
 
 struct hostapd_hw_modes *
-nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
+nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags,
+			    u8 *dfs_domain)
 {
 	u32 feat;
 	struct i802_bss *bss = priv;
@@ -1866,10 +1908,12 @@
 		.modes = NULL,
 		.last_mode = -1,
 		.failed = 0,
+		.dfs_domain = 0,
 	};
 
 	*num_modes = 0;
 	*flags = 0;
+	*dfs_domain = 0;
 
 	feat = get_nl80211_protocol_features(drv);
 	if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
@@ -1893,6 +1937,9 @@
 			*num_modes = 0;
 			return NULL;
 		}
+
+		*dfs_domain = result.dfs_domain;
+
 		return wpa_driver_nl80211_postprocess_modes(result.modes,
 							    num_modes);
 	}
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index ed2cbe4..e6bc254 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -284,7 +284,10 @@
 			       struct nlattr *key_replay_ctr,
 			       struct nlattr *ptk_kck,
 			       struct nlattr *ptk_kek,
-			       struct nlattr *subnet_status)
+			       struct nlattr *subnet_status,
+			       struct nlattr *fils_erp_next_seq_num,
+			       struct nlattr *fils_pmk,
+			       struct nlattr *fils_pmkid)
 {
 	union wpa_event_data event;
 	const u8 *ssid = NULL;
@@ -357,6 +360,9 @@
 				break;
 			}
 		}
+		if (fils_erp_next_seq_num)
+			event.assoc_reject.fils_erp_next_seq_num =
+				nla_get_u16(fils_erp_next_seq_num);
 		wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
 		return;
 	}
@@ -430,6 +436,18 @@
 		event.assoc_info.subnet_status = nla_get_u8(subnet_status);
 	}
 
+	if (fils_erp_next_seq_num)
+		event.assoc_info.fils_erp_next_seq_num =
+			nla_get_u16(fils_erp_next_seq_num);
+
+	if (fils_pmk) {
+		event.assoc_info.fils_pmk = nla_data(fils_pmk);
+		event.assoc_info.fils_pmk_len = nla_len(fils_pmk);
+	}
+
+	if (fils_pmkid)
+		event.assoc_info.fils_pmkid = nla_data(fils_pmkid);
+
 	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
 }
 
@@ -1568,6 +1586,10 @@
 	case NL80211_RADAR_NOP_FINISHED:
 		wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data);
 		break;
+	case NL80211_RADAR_PRE_CAC_EXPIRED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_PRE_CAC_EXPIRED,
+				     &data);
+		break;
 	default:
 		wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d "
 			   "received", event_type);
@@ -1750,7 +1772,10 @@
 			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR],
 			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK],
 			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK],
-			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS]);
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID]);
 }
 
 
@@ -2266,7 +2291,12 @@
 				   tb[NL80211_ATTR_RESP_IE],
 				   tb[NL80211_ATTR_TIMED_OUT],
 				   tb[NL80211_ATTR_TIMEOUT_REASON],
-				   NULL, NULL, NULL, NULL, NULL);
+				   NULL, NULL, NULL,
+				   tb[NL80211_ATTR_FILS_KEK],
+				   NULL,
+				   tb[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM],
+				   tb[NL80211_ATTR_PMK],
+				   tb[NL80211_ATTR_PMKID]);
 		break;
 	case NL80211_CMD_CH_SWITCH_NOTIFY:
 		mlme_event_ch_switch(drv,
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index 4417721..984485b 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -10,6 +10,7 @@
  */
 
 #include "includes.h"
+#include <time.h>
 #include <netlink/genl/genl.h>
 
 #include "utils/common.h"
@@ -595,6 +596,11 @@
 		}
 	}
 
+	if (params->sched_scan_start_delay &&
+	    nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_DELAY,
+			params->sched_scan_start_delay))
+		goto fail;
+
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 
 	/* TODO: if we get an error here, we should fall back to normal scan */
@@ -691,6 +697,7 @@
 		[NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC },
 		[NL80211_BSS_PARENT_TSF] = { .type = NLA_U64 },
 		[NL80211_BSS_PARENT_BSSID] = { .type = NLA_UNSPEC },
+		[NL80211_BSS_LAST_SEEN_BOOTTIME] = { .type = NLA_U64 },
 	};
 	struct wpa_scan_res *r;
 	const u8 *ie, *beacon_ie;
@@ -754,6 +761,23 @@
 	}
 	if (bss[NL80211_BSS_SEEN_MS_AGO])
 		r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
+	if (bss[NL80211_BSS_LAST_SEEN_BOOTTIME]) {
+		u64 boottime;
+		struct timespec ts;
+
+#ifndef CLOCK_BOOTTIME
+#define CLOCK_BOOTTIME 7
+#endif
+		if (clock_gettime(CLOCK_BOOTTIME, &ts) == 0) {
+			/* Use more accurate boottime information to update the
+			 * scan result age since the driver reports this and
+			 * CLOCK_BOOTTIME is available. */
+			boottime = nla_get_u64(
+				bss[NL80211_BSS_LAST_SEEN_BOOTTIME]);
+			r->age = ((u64) ts.tv_sec * 1000000000 +
+				  ts.tv_nsec - boottime) / 1000000;
+		}
+	}
 	r->ie_len = ie_len;
 	pos = (u8 *) (r + 1);
 	if (ie) {
diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c
index 655128a..a3f0837 100644
--- a/src/drivers/driver_privsep.c
+++ b/src/drivers/driver_privsep.c
@@ -184,10 +184,9 @@
 		if (len < 0 || len > 10000 || len > end - pos)
 			break;
 
-		r = os_malloc(len);
+		r = os_memdup(pos, len);
 		if (r == NULL)
 			break;
-		os_memcpy(r, pos, len);
 		pos += len;
 		if (sizeof(*r) + r->ie_len + r->beacon_ie_len > (size_t) len) {
 			wpa_printf(MSG_ERROR,
@@ -484,19 +483,6 @@
 }
 
 
-static void wpa_driver_privsep_event_stkstart(void *ctx, u8 *buf, size_t len)
-{
-	union wpa_event_data data;
-
-	if (len != ETH_ALEN)
-		return;
-
-	os_memset(&data, 0, sizeof(data));
-	os_memcpy(data.stkstart.peer, buf, ETH_ALEN);
-	wpa_supplicant_event(ctx, EVENT_STKSTART, &data);
-}
-
-
 static void wpa_driver_privsep_event_ft_response(void *ctx, u8 *buf,
 						 size_t len)
 {
@@ -590,10 +576,6 @@
 		wpa_driver_privsep_event_pmkid_candidate(drv->ctx, event_buf,
 							 event_len);
 		break;
-	case PRIVSEP_EVENT_STKSTART:
-		wpa_driver_privsep_event_stkstart(drv->ctx, event_buf,
-						  event_len);
-		break;
 	case PRIVSEP_EVENT_FT_RESPONSE:
 		wpa_driver_privsep_event_ft_response(drv->ctx, event_buf,
 						     event_len);
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index 458d458..659eda2 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -290,15 +290,6 @@
 	done:
 		os_free(resp_ies);
 		os_free(req_ies);
-#ifdef CONFIG_PEERKEY
-	} else if (os_strncmp(custom, "STKSTART.request=", 17) == 0) {
-		if (hwaddr_aton(custom + 17, data.stkstart.peer)) {
-			wpa_printf(MSG_DEBUG, "WEXT: unrecognized "
-				   "STKSTART.request '%s'", custom + 17);
-			return;
-		}
-		wpa_supplicant_event(ctx, EVENT_STKSTART, &data);
-#endif /* CONFIG_PEERKEY */
 	}
 }
 
@@ -362,12 +353,11 @@
 	wpa_hexdump(MSG_DEBUG, "AssocReq IE wireless event", (const u8 *) ev,
 		    len);
 	os_free(drv->assoc_req_ies);
-	drv->assoc_req_ies = os_malloc(len);
+	drv->assoc_req_ies = os_memdup(ev, len);
 	if (drv->assoc_req_ies == NULL) {
 		drv->assoc_req_ies_len = 0;
 		return -1;
 	}
-	os_memcpy(drv->assoc_req_ies, ev, len);
 	drv->assoc_req_ies_len = len;
 
 	return 0;
@@ -383,12 +373,11 @@
 	wpa_hexdump(MSG_DEBUG, "AssocResp IE wireless event", (const u8 *) ev,
 		    len);
 	os_free(drv->assoc_resp_ies);
-	drv->assoc_resp_ies = os_malloc(len);
+	drv->assoc_resp_ies = os_memdup(ev, len);
 	if (drv->assoc_resp_ies == NULL) {
 		drv->assoc_resp_ies_len = 0;
 		return -1;
 	}
-	os_memcpy(drv->assoc_resp_ies, ev, len);
 	drv->assoc_resp_ies_len = len;
 
 	return 0;
@@ -472,7 +461,7 @@
 				drv->assoc_resp_ies = NULL;
 				wpa_supplicant_event(drv->ctx, EVENT_DISASSOC,
 						     NULL);
-			
+
 			} else {
 				wpa_driver_wext_event_assoc_ies(drv);
 				wpa_supplicant_event(drv->ctx, EVENT_ASSOC,
@@ -2353,19 +2342,21 @@
 }
 
 
-static int wpa_driver_wext_add_pmkid(void *priv, const u8 *bssid,
-				     const u8 *pmkid)
+static int wpa_driver_wext_add_pmkid(void *priv,
+				     struct wpa_pmkid_params *params)
 {
 	struct wpa_driver_wext_data *drv = priv;
-	return wpa_driver_wext_pmksa(drv, IW_PMKSA_ADD, bssid, pmkid);
+	return wpa_driver_wext_pmksa(drv, IW_PMKSA_ADD, params->bssid,
+				     params->pmkid);
 }
 
 
-static int wpa_driver_wext_remove_pmkid(void *priv, const u8 *bssid,
-		 			const u8 *pmkid)
+static int wpa_driver_wext_remove_pmkid(void *priv,
+					struct wpa_pmkid_params *params)
 {
 	struct wpa_driver_wext_data *drv = priv;
-	return wpa_driver_wext_pmksa(drv, IW_PMKSA_REMOVE, bssid, pmkid);
+	return wpa_driver_wext_pmksa(drv, IW_PMKSA_REMOVE, params->bssid,
+				     params->pmkid);
 }
 
 
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index d6c62ee..6095a6c 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -10,7 +10,7 @@
  * Copyright 2008, 2009 Luis R. Rodriguez <lrodriguez@atheros.com>
  * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
  * Copyright 2008 Colin McCabe <colin@cozybit.com>
- * Copyright 2015	Intel Deutschland GmbH
+ * Copyright 2015-2017	Intel Deutschland GmbH
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -173,6 +173,42 @@
  */
 
 /**
+ * DOC: FILS shared key authentication offload
+ *
+ * FILS shared key authentication offload can be advertized by drivers by
+ * setting @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD flag. The drivers that support
+ * FILS shared key authentication offload should be able to construct the
+ * authentication and association frames for FILS shared key authentication and
+ * eventually do a key derivation as per IEEE 802.11ai. The below additional
+ * parameters should be given to driver in %NL80211_CMD_CONNECT.
+ *	%NL80211_ATTR_FILS_ERP_USERNAME - used to construct keyname_nai
+ *	%NL80211_ATTR_FILS_ERP_REALM - used to construct keyname_nai
+ *	%NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used to construct erp message
+ *	%NL80211_ATTR_FILS_ERP_RRK - used to generate the rIK and rMSK
+ * rIK should be used to generate an authentication tag on the ERP message and
+ * rMSK should be used to derive a PMKSA.
+ * rIK, rMSK should be generated and keyname_nai, sequence number should be used
+ * as specified in IETF RFC 6696.
+ *
+ * When FILS shared key authentication is completed, driver needs to provide the
+ * below additional parameters to userspace.
+ *	%NL80211_ATTR_FILS_KEK - used for key renewal
+ *	%NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used in further EAP-RP exchanges
+ *	%NL80211_ATTR_PMKID - used to identify the PMKSA used/generated
+ *	%Nl80211_ATTR_PMK - used to update PMKSA cache in userspace
+ * The PMKSA can be maintained in userspace persistently so that it can be used
+ * later after reboots or wifi turn off/on also.
+ *
+ * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertized by a FILS
+ * capable AP supporting PMK caching. It specifies the scope within which the
+ * PMKSAs are cached in an ESS. %NL80211_CMD_SET_PMKSA and
+ * %NL80211_CMD_DEL_PMKSA are enhanced to allow support for PMKSA caching based
+ * on FILS cache identifier. Additionally %NL80211_ATTR_PMK is used with
+ * %NL80211_SET_PMKSA to specify the PMK corresponding to a PMKSA for driver to
+ * use in a FILS shared key connection with PMKSA caching.
+ */
+
+/**
  * enum nl80211_commands - supported nl80211 commands
  *
  * @NL80211_CMD_UNSPEC: unspecified command to catch errors
@@ -370,10 +406,18 @@
  * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to
  *	NL80211_CMD_GET_SURVEY and on the "scan" multicast group)
  *
- * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry, using %NL80211_ATTR_MAC
- *	(for the BSSID) and %NL80211_ATTR_PMKID.
+ * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry using %NL80211_ATTR_MAC
+ *	(for the BSSID), %NL80211_ATTR_PMKID, and optionally %NL80211_ATTR_PMK
+ *	(PMK is used for PTKSA derivation in case of FILS shared key offload) or
+ *	using %NL80211_ATTR_SSID, %NL80211_ATTR_FILS_CACHE_ID,
+ *	%NL80211_ATTR_PMKID, and %NL80211_ATTR_PMK in case of FILS
+ *	authentication where %NL80211_ATTR_FILS_CACHE_ID is the identifier
+ *	advertized by a FILS capable AP identifying the scope of PMKSA in an
+ *	ESS.
  * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC
- *	(for the BSSID) and %NL80211_ATTR_PMKID.
+ *	(for the BSSID) and %NL80211_ATTR_PMKID or using %NL80211_ATTR_SSID,
+ *	%NL80211_ATTR_FILS_CACHE_ID, and %NL80211_ATTR_PMKID in case of FILS
+ *	authentication.
  * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries.
  *
  * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain
@@ -854,12 +898,15 @@
  *	cfg80211_scan_done().
  *
  * @NL80211_CMD_START_NAN: Start NAN operation, identified by its
- *	%NL80211_ATTR_WDEV interface. This interface must have been previously
- *	created with %NL80211_CMD_NEW_INTERFACE. After it has been started, the
- *	NAN interface will create or join a cluster. This command must have a
- *	valid %NL80211_ATTR_NAN_MASTER_PREF attribute and optional
- *	%NL80211_ATTR_NAN_DUAL attributes.
- *	After this command NAN functions can be added.
+ *	%NL80211_ATTR_WDEV interface. This interface must have been
+ *	previously created with %NL80211_CMD_NEW_INTERFACE. After it
+ *	has been started, the NAN interface will create or join a
+ *	cluster. This command must have a valid
+ *	%NL80211_ATTR_NAN_MASTER_PREF attribute and optional
+ *	%NL80211_ATTR_BANDS attributes.  If %NL80211_ATTR_BANDS is
+ *	omitted or set to 0, it means don't-care and the device will
+ *	decide what to use.  After this command NAN functions can be
+ *	added.
  * @NL80211_CMD_STOP_NAN: Stop the NAN operation, identified by
  *	its %NL80211_ATTR_WDEV interface.
  * @NL80211_CMD_ADD_NAN_FUNCTION: Add a NAN function. The function is defined
@@ -880,10 +927,14 @@
  *	This command is also used as a notification sent when a NAN function is
  *	terminated. This will contain a %NL80211_ATTR_NAN_FUNC_INST_ID
  *	and %NL80211_ATTR_COOKIE attributes.
- * @NL80211_CMD_CHANGE_NAN_CONFIG: Change current NAN configuration. NAN
- *	must be operational (%NL80211_CMD_START_NAN was executed).
- *	It must contain at least one of the following attributes:
- *	%NL80211_ATTR_NAN_MASTER_PREF, %NL80211_ATTR_NAN_DUAL.
+ * @NL80211_CMD_CHANGE_NAN_CONFIG: Change current NAN
+ *	configuration. NAN must be operational (%NL80211_CMD_START_NAN
+ *	was executed).  It must contain at least one of the following
+ *	attributes: %NL80211_ATTR_NAN_MASTER_PREF,
+ *	%NL80211_ATTR_BANDS.  If %NL80211_ATTR_BANDS is omitted, the
+ *	current configuration is not changed.  If it is present but
+ *	set to zero, the configuration is changed to don't-care
+ *	(i.e. the device can decide what to do).
  * @NL80211_CMD_NAN_FUNC_MATCH: Notification sent when a match is reported.
  *	This will contain a %NL80211_ATTR_NAN_MATCH nested attribute and
  *	%NL80211_ATTR_COOKIE.
@@ -1963,10 +2014,13 @@
  *	%NL80211_CMD_CHANGE_NAN_CONFIG. Its type is u8 and it can't be 0.
  *	Also, values 1 and 255 are reserved for certification purposes and
  *	should not be used during a normal device operation.
- * @NL80211_ATTR_NAN_DUAL: NAN dual band operation config (see
- *	&enum nl80211_nan_dual_band_conf). This attribute is used with
- *	%NL80211_CMD_START_NAN and optionally with
- *	%NL80211_CMD_CHANGE_NAN_CONFIG.
+ * @NL80211_ATTR_BANDS: operating bands configuration.  This is a u32
+ *	bitmask of BIT(NL80211_BAND_*) as described in %enum
+ *	nl80211_band.  For instance, for NL80211_BAND_2GHZ, bit 0
+ *	would be set.  This attribute is used with
+ *	%NL80211_CMD_START_NAN and %NL80211_CMD_CHANGE_NAN_CONFIG, and
+ *	it is optional.  If no bands are set, it means don't-care and
+ *	the device will decide what to use.
  * @NL80211_ATTR_NAN_FUNC: a function that can be added to NAN. See
  *	&enum nl80211_nan_func_attributes for description of this nested
  *	attribute.
@@ -2002,6 +2056,31 @@
  *	u32 attribute with an &enum nl80211_timeout_reason value. This is used,
  *	e.g., with %NL80211_CMD_CONNECT event.
  *
+ * @NL80211_ATTR_FILS_ERP_USERNAME: EAP Re-authentication Protocol (ERP)
+ *	username part of NAI used to refer keys rRK and rIK. This is used with
+ *	%NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_ERP_REALM: EAP Re-authentication Protocol (ERP) realm part
+ *	of NAI specifying the domain name of the ER server. This is used with
+ *	%NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM: Unsigned 16-bit ERP next sequence number
+ *	to use in ERP messages. This is used in generating the FILS wrapped data
+ *	for FILS authentication and is used with %NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_ERP_RRK: ERP re-authentication Root Key (rRK) for the
+ *	NAI specified by %NL80211_ATTR_FILS_ERP_USERNAME and
+ *	%NL80211_ATTR_FILS_ERP_REALM. This is used for generating rIK and rMSK
+ *	from successful FILS authentication and is used with
+ *	%NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertized by a FILS AP
+ *	identifying the scope of PMKSAs. This is used with
+ *	@NL80211_CMD_SET_PMKSA and @NL80211_CMD_DEL_PMKSA.
+ *
+ * @NL80211_ATTR_PMK: PMK for the PMKSA identified by %NL80211_ATTR_PMKID.
+ *	This is used with @NL80211_CMD_SET_PMKSA.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2397,7 +2476,7 @@
 	NL80211_ATTR_MESH_PEER_AID,
 
 	NL80211_ATTR_NAN_MASTER_PREF,
-	NL80211_ATTR_NAN_DUAL,
+	NL80211_ATTR_BANDS,
 	NL80211_ATTR_NAN_FUNC,
 	NL80211_ATTR_NAN_MATCH,
 
@@ -2413,6 +2492,14 @@
 
 	NL80211_ATTR_TIMEOUT_REASON,
 
+	NL80211_ATTR_FILS_ERP_USERNAME,
+	NL80211_ATTR_FILS_ERP_REALM,
+	NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM,
+	NL80211_ATTR_FILS_ERP_RRK,
+	NL80211_ATTR_FILS_CACHE_ID,
+
+	NL80211_ATTR_PMK,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -3932,7 +4019,10 @@
  * @__NL80211_ATTR_CQM_INVALID: invalid
  * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies
  *	the threshold for the RSSI level at which an event will be sent. Zero
- *	to disable.
+ *	to disable.  Alternatively, if %NL80211_EXT_FEATURE_CQM_RSSI_LIST is
+ *	set, multiple values can be supplied as a low-to-high sorted array of
+ *	threshold values in dBm.  Events will be sent when the RSSI value
+ *	crosses any of the thresholds.
  * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies
  *	the minimum amount the RSSI level must change after an event before a
  *	new event may be issued (to reduce effects of RSSI oscillation).
@@ -3952,6 +4042,8 @@
  *	%NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting.
  * @NL80211_ATTR_CQM_BEACON_LOSS_EVENT: flag attribute that's set in a beacon
  *	loss event
+ * @NL80211_ATTR_CQM_RSSI_LEVEL: the RSSI value in dBm that triggered the
+ *	RSSI threshold event.
  * @__NL80211_ATTR_CQM_AFTER_LAST: internal
  * @NL80211_ATTR_CQM_MAX: highest key attribute
  */
@@ -3965,6 +4057,7 @@
 	NL80211_ATTR_CQM_TXE_PKTS,
 	NL80211_ATTR_CQM_TXE_INTVL,
 	NL80211_ATTR_CQM_BEACON_LOSS_EVENT,
+	NL80211_ATTR_CQM_RSSI_LEVEL,
 
 	/* keep last */
 	__NL80211_ATTR_CQM_AFTER_LAST,
@@ -4740,6 +4833,11 @@
  * @NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI: The driver supports sched_scan
  *	for reporting BSSs with better RSSI than the current connected BSS
  *	(%NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI).
+ * @NL80211_EXT_FEATURE_CQM_RSSI_LIST: With this driver the
+ *	%NL80211_ATTR_CQM_RSSI_THOLD attribute accepts a list of zero or more
+ *	RSSI threshold values to monitor rather than exactly one threshold.
+ * @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD: Driver SME supports FILS shared key
+ *	authentication with %NL80211_CMD_CONNECT.
  *
  * @NUM_NL80211_EXT_FEATURES: number of extended features.
  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
@@ -4758,6 +4856,8 @@
 	NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA,
 	NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED,
 	NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI,
+	NL80211_EXT_FEATURE_CQM_RSSI_LIST,
+	NL80211_EXT_FEATURE_FILS_SK_OFFLOAD,
 
 	/* add new features before the definition below */
 	NUM_NL80211_EXT_FEATURES,
@@ -4893,12 +4993,17 @@
  *	change to the channel status.
  * @NL80211_RADAR_NOP_FINISHED: The Non-Occupancy Period for this channel is
  *	over, channel becomes usable.
+ * @NL80211_RADAR_PRE_CAC_EXPIRED: Channel Availability Check done on this
+ *	non-operating channel is expired and no longer valid. New CAC must
+ *	be done on this channel before starting the operation. This is not
+ *	applicable for ETSI dfs domain where pre-CAC is valid for ever.
  */
 enum nl80211_radar_event {
 	NL80211_RADAR_DETECTED,
 	NL80211_RADAR_CAC_FINISHED,
 	NL80211_RADAR_CAC_ABORTED,
 	NL80211_RADAR_NOP_FINISHED,
+	NL80211_RADAR_PRE_CAC_EXPIRED,
 };
 
 /**
@@ -5068,21 +5173,6 @@
 };
 
 /**
- * enum nl80211_nan_dual_band_conf - NAN dual band configuration
- *
- * Defines the NAN dual band mode of operation
- *
- * @NL80211_NAN_BAND_DEFAULT: device default mode
- * @NL80211_NAN_BAND_2GHZ: 2.4GHz mode
- * @NL80211_NAN_BAND_5GHZ: 5GHz mode
-  */
-enum nl80211_nan_dual_band_conf {
-	NL80211_NAN_BAND_DEFAULT	= 1 << 0,
-	NL80211_NAN_BAND_2GHZ		= 1 << 1,
-	NL80211_NAN_BAND_5GHZ		= 1 << 2,
-};
-
-/**
  * enum nl80211_nan_function_type - NAN function type
  *
  * Defines the function type of a NAN function
diff --git a/src/eap_common/eap_sim_common.c b/src/eap_common/eap_sim_common.c
index 2adc3b3..6290c35 100644
--- a/src/eap_common/eap_sim_common.c
+++ b/src/eap_common/eap_sim_common.c
@@ -175,7 +175,7 @@
 	    mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN)
 		return -1;
 
-	tmp = os_malloc(wpabuf_len(req));
+	tmp = os_memdup(wpabuf_head(req), wpabuf_len(req));
 	if (tmp == NULL)
 		return -1;
 
@@ -185,7 +185,6 @@
 	len[1] = extra_len;
 
 	/* HMAC-SHA1-128 */
-	os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req));
 	os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN);
 	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - msg",
 		    tmp, wpabuf_len(req));
@@ -370,7 +369,7 @@
 	    mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN)
 		return -1;
 
-	tmp = os_malloc(wpabuf_len(req));
+	tmp = os_memdup(wpabuf_head(req), wpabuf_len(req));
 	if (tmp == NULL)
 		return -1;
 
@@ -380,7 +379,6 @@
 	len[1] = extra_len;
 
 	/* HMAC-SHA-256-128 */
-	os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req));
 	os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN);
 	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - msg",
 		    tmp, wpabuf_len(req));
@@ -943,10 +941,9 @@
 		return NULL;
 	}
 
-	decrypted = os_malloc(encr_data_len);
+	decrypted = os_memdup(encr_data, encr_data_len);
 	if (decrypted == NULL)
 		return NULL;
-	os_memcpy(decrypted, encr_data, encr_data_len);
 
 	if (aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len)) {
 		os_free(decrypted);
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index bc90c7a..0043707 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -121,15 +121,17 @@
 
 
 /**
- * eap_allowed_method - Check whether EAP method is allowed
+ * eap_config_allowed_method - Check whether EAP method is allowed
  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @config: EAP configuration
  * @vendor: Vendor-Id for expanded types or 0 = IETF for legacy types
  * @method: EAP type
  * Returns: 1 = allowed EAP method, 0 = not allowed
  */
-int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method)
+static int eap_config_allowed_method(struct eap_sm *sm,
+				     struct eap_peer_config *config,
+				     int vendor, u32 method)
 {
-	struct eap_peer_config *config = eap_get_config(sm);
 	int i;
 	struct eap_method_type *m;
 
@@ -146,6 +148,57 @@
 }
 
 
+/**
+ * eap_allowed_method - Check whether EAP method is allowed
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @vendor: Vendor-Id for expanded types or 0 = IETF for legacy types
+ * @method: EAP type
+ * Returns: 1 = allowed EAP method, 0 = not allowed
+ */
+int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method)
+{
+	return eap_config_allowed_method(sm, eap_get_config(sm), vendor,
+					 method);
+}
+
+
+#if defined(PCSC_FUNCS) || defined(CONFIG_EAP_PROXY)
+static int eap_sm_append_3gpp_realm(struct eap_sm *sm, char *imsi,
+				    size_t max_len, size_t *imsi_len,
+				    int mnc_len)
+{
+	char *pos, mnc[4];
+
+	if (*imsi_len + 36 > max_len) {
+		wpa_printf(MSG_WARNING, "No room for realm in IMSI buffer");
+		return -1;
+	}
+
+	if (mnc_len != 2 && mnc_len != 3)
+		mnc_len = 3;
+
+	if (mnc_len == 2) {
+		mnc[0] = '0';
+		mnc[1] = imsi[3];
+		mnc[2] = imsi[4];
+	} else if (mnc_len == 3) {
+		mnc[0] = imsi[3];
+		mnc[1] = imsi[4];
+		mnc[2] = imsi[5];
+	}
+	mnc[3] = '\0';
+
+	pos = imsi + *imsi_len;
+	pos += os_snprintf(pos, imsi + max_len - pos,
+			   "@wlan.mnc%s.mcc%c%c%c.3gppnetwork.org",
+			   mnc, imsi[0], imsi[1], imsi[2]);
+	*imsi_len = pos - imsi;
+
+	return 0;
+}
+#endif /* PCSC_FUNCS || CONFIG_EAP_PROXY */
+
+
 /*
  * This state initializes state machine variables when the machine is
  * activated (portEnabled = TRUE). This is also used when re-starting
@@ -371,9 +424,8 @@
 
 #ifdef CONFIG_ERP
 
-static char * eap_home_realm(struct eap_sm *sm)
+static char * eap_get_realm(struct eap_sm *sm, struct eap_peer_config *config)
 {
-	struct eap_peer_config *config = eap_get_config(sm);
 	char *realm;
 	size_t i, realm_len;
 
@@ -413,7 +465,51 @@
 		}
 	}
 
-	return os_strdup("");
+#ifdef CONFIG_EAP_PROXY
+	/* When identity is not provided in the config, build the realm from
+	 * IMSI for eap_proxy based methods.
+	 */
+	if (!config->identity && !config->anonymous_identity &&
+	    sm->eapol_cb->get_imsi &&
+	    (eap_config_allowed_method(sm, config, EAP_VENDOR_IETF,
+				       EAP_TYPE_SIM) ||
+	     eap_config_allowed_method(sm, config, EAP_VENDOR_IETF,
+				       EAP_TYPE_AKA) ||
+	     eap_config_allowed_method(sm, config, EAP_VENDOR_IETF,
+				       EAP_TYPE_AKA_PRIME))) {
+		char imsi[100];
+		size_t imsi_len;
+		int mnc_len, pos;
+
+		wpa_printf(MSG_DEBUG, "EAP: Build realm from IMSI (eap_proxy)");
+		mnc_len = sm->eapol_cb->get_imsi(sm->eapol_ctx, config->sim_num,
+						 imsi, &imsi_len);
+		if (mnc_len < 0)
+			return NULL;
+
+		pos = imsi_len + 1; /* points to the beginning of the realm */
+		if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len,
+					     mnc_len) < 0) {
+			wpa_printf(MSG_WARNING, "Could not append realm");
+			return NULL;
+		}
+
+		realm = os_strdup(&imsi[pos]);
+		if (!realm)
+			return NULL;
+
+		wpa_printf(MSG_DEBUG, "EAP: Generated realm '%s'", realm);
+		return realm;
+	}
+#endif /* CONFIG_EAP_PROXY */
+
+	return NULL;
+}
+
+
+static char * eap_home_realm(struct eap_sm *sm)
+{
+	return eap_get_realm(sm, eap_get_config(sm));
 }
 
 
@@ -469,6 +565,89 @@
 	}
 }
 
+
+int eap_peer_update_erp_next_seq_num(struct eap_sm *sm, u16 next_seq_num)
+{
+	struct eap_erp_key *erp;
+	char *home_realm;
+
+	home_realm = eap_home_realm(sm);
+	if (!home_realm || os_strlen(home_realm) == 0) {
+		os_free(home_realm);
+		return -1;
+	}
+
+	erp = eap_erp_get_key(sm, home_realm);
+	if (!erp) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Failed to find ERP key for realm: %s",
+			   home_realm);
+		os_free(home_realm);
+		return -1;
+	}
+
+	if ((u32) next_seq_num < erp->next_seq) {
+		/* Sequence number has wrapped around, clear this ERP
+		 * info and do a full auth next time.
+		 */
+		eap_peer_erp_free_key(erp);
+	} else {
+		erp->next_seq = (u32) next_seq_num;
+	}
+
+	os_free(home_realm);
+	return 0;
+}
+
+
+int eap_peer_get_erp_info(struct eap_sm *sm, struct eap_peer_config *config,
+			  const u8 **username, size_t *username_len,
+			  const u8 **realm, size_t *realm_len,
+			  u16 *erp_next_seq_num, const u8 **rrk,
+			  size_t *rrk_len)
+{
+	struct eap_erp_key *erp;
+	char *home_realm;
+	char *pos;
+
+	if (config)
+		home_realm = eap_get_realm(sm, config);
+	else
+		home_realm = eap_home_realm(sm);
+	if (!home_realm || os_strlen(home_realm) == 0) {
+		os_free(home_realm);
+		return -1;
+	}
+
+	erp = eap_erp_get_key(sm, home_realm);
+	os_free(home_realm);
+	if (!erp)
+		return -1;
+
+	if (erp->next_seq >= 65536)
+		return -1; /* SEQ has range of 0..65535 */
+
+	pos = os_strchr(erp->keyname_nai, '@');
+	if (!pos)
+		return -1; /* this cannot really happen */
+	*username_len = pos - erp->keyname_nai;
+	*username = (u8 *) erp->keyname_nai;
+
+	pos++;
+	*realm_len = os_strlen(pos);
+	*realm = (u8 *) pos;
+
+	*erp_next_seq_num = (u16) erp->next_seq;
+
+	*rrk_len = erp->rRK_len;
+	*rrk = erp->rRK;
+
+	if (*username_len == 0 || *realm_len == 0 || *rrk_len == 0)
+		return -1;
+
+	return 0;
+}
+
 #endif /* CONFIG_ERP */
 
 
@@ -483,11 +662,15 @@
 }
 
 
-static void eap_peer_erp_init(struct eap_sm *sm)
+void eap_peer_erp_init(struct eap_sm *sm, u8 *ext_session_id,
+		       size_t ext_session_id_len, u8 *ext_emsk,
+		       size_t ext_emsk_len)
 {
 #ifdef CONFIG_ERP
 	u8 *emsk = NULL;
 	size_t emsk_len = 0;
+	u8 *session_id = NULL;
+	size_t session_id_len = 0;
 	u8 EMSKname[EAP_EMSK_NAME_LEN];
 	u8 len[2], ctx[3];
 	char *realm;
@@ -517,7 +700,13 @@
 	if (erp == NULL)
 		goto fail;
 
-	emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len);
+	if (ext_emsk) {
+		emsk = ext_emsk;
+		emsk_len = ext_emsk_len;
+	} else {
+		emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len);
+	}
+
 	if (!emsk || emsk_len == 0 || emsk_len > ERP_MAX_KEY_LEN) {
 		wpa_printf(MSG_DEBUG,
 			   "EAP: No suitable EMSK available for ERP");
@@ -526,10 +715,23 @@
 
 	wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len);
 
+	if (ext_session_id) {
+		session_id = ext_session_id;
+		session_id_len = ext_session_id_len;
+	} else {
+		session_id = sm->eapSessionId;
+		session_id_len = sm->eapSessionIdLen;
+	}
+
+	if (!session_id || session_id_len == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: No suitable session id available for ERP");
+		goto fail;
+	}
+
 	WPA_PUT_BE16(len, EAP_EMSK_NAME_LEN);
-	if (hmac_sha256_kdf(sm->eapSessionId, sm->eapSessionIdLen, "EMSK",
-			    len, sizeof(len),
-			    EMSKname, EAP_EMSK_NAME_LEN) < 0) {
+	if (hmac_sha256_kdf(session_id, session_id_len, "EMSK", len,
+			    sizeof(len), EMSKname, EAP_EMSK_NAME_LEN) < 0) {
 		wpa_printf(MSG_DEBUG, "EAP: Could not derive EMSKname");
 		goto fail;
 	}
@@ -566,6 +768,7 @@
 	erp = NULL;
 fail:
 	bin_clear_free(emsk, emsk_len);
+	bin_clear_free(ext_session_id, ext_session_id_len);
 	bin_clear_free(erp, sizeof(*erp));
 	os_free(realm);
 #endif /* CONFIG_ERP */
@@ -707,8 +910,6 @@
 
 	if (sm->m->isKeyAvailable && sm->m->getKey &&
 	    sm->m->isKeyAvailable(sm, sm->eap_method_priv)) {
-		struct eap_peer_config *config = eap_get_config(sm);
-
 		eap_sm_free_key(sm);
 		sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv,
 					       &sm->eapKeyDataLen);
@@ -721,8 +922,6 @@
 			wpa_hexdump(MSG_DEBUG, "EAP: Session-Id",
 				    sm->eapSessionId, sm->eapSessionIdLen);
 		}
-		if (config->erp && sm->m->get_emsk && sm->eapSessionId)
-			eap_peer_erp_init(sm);
 	}
 }
 
@@ -820,6 +1019,8 @@
  */
 SM_STATE(EAP, SUCCESS)
 {
+	struct eap_peer_config *config = eap_get_config(sm);
+
 	SM_ENTRY(EAP, SUCCESS);
 	if (sm->eapKeyData != NULL)
 		sm->eapKeyAvailable = TRUE;
@@ -842,6 +1043,11 @@
 
 	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
 		"EAP authentication completed successfully");
+
+	if (config->erp && sm->m->get_emsk && sm->eapSessionId &&
+	    sm->m->isKeyAvailable &&
+	    sm->m->isKeyAvailable(sm, sm->eap_method_priv))
+		eap_peer_erp_init(sm, NULL, 0, NULL, 0);
 }
 
 
@@ -1292,48 +1498,6 @@
 }
 
 
-static int eap_sm_append_3gpp_realm(struct eap_sm *sm, char *imsi,
-				    size_t max_len, size_t *imsi_len)
-{
-	int mnc_len;
-	char *pos, mnc[4];
-
-	if (*imsi_len + 36 > max_len) {
-		wpa_printf(MSG_WARNING, "No room for realm in IMSI buffer");
-		return -1;
-	}
-
-	/* MNC (2 or 3 digits) */
-	mnc_len = scard_get_mnc_len(sm->scard_ctx);
-	if (mnc_len < 0)
-		mnc_len = mnc_len_from_imsi(imsi);
-	if (mnc_len < 0) {
-		wpa_printf(MSG_INFO, "Failed to get MNC length from (U)SIM "
-			   "assuming 3");
-		mnc_len = 3;
-	}
-
-	if (mnc_len == 2) {
-		mnc[0] = '0';
-		mnc[1] = imsi[3];
-		mnc[2] = imsi[4];
-	} else if (mnc_len == 3) {
-		mnc[0] = imsi[3];
-		mnc[1] = imsi[4];
-		mnc[2] = imsi[5];
-	}
-	mnc[3] = '\0';
-
-	pos = imsi + *imsi_len;
-	pos += os_snprintf(pos, imsi + max_len - pos,
-			   "@wlan.mnc%s.mcc%c%c%c.3gppnetwork.org",
-			   mnc, imsi[0], imsi[1], imsi[2]);
-	*imsi_len = pos - imsi;
-
-	return 0;
-}
-
-
 static int eap_sm_imsi_identity(struct eap_sm *sm,
 				struct eap_peer_config *conf)
 {
@@ -1341,7 +1505,7 @@
 	char imsi[100];
 	size_t imsi_len;
 	struct eap_method_type *m = conf->eap_methods;
-	int i;
+	int i, mnc_len;
 
 	imsi_len = sizeof(imsi);
 	if (scard_get_imsi(sm->scard_ctx, imsi, &imsi_len)) {
@@ -1356,7 +1520,18 @@
 		return -1;
 	}
 
-	if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len) < 0) {
+	/* MNC (2 or 3 digits) */
+	mnc_len = scard_get_mnc_len(sm->scard_ctx);
+	if (mnc_len < 0)
+		mnc_len = mnc_len_from_imsi(imsi);
+	if (mnc_len < 0) {
+		wpa_printf(MSG_INFO, "Failed to get MNC length from (U)SIM "
+			   "assuming 3");
+		mnc_len = 3;
+	}
+
+	if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len,
+				     mnc_len) < 0) {
 		wpa_printf(MSG_WARNING, "Could not add realm to SIM identity");
 		return -1;
 	}
diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h
index 932584f..b5591a0 100644
--- a/src/eap_peer/eap.h
+++ b/src/eap_peer/eap.h
@@ -260,6 +260,16 @@
 	 */
 	void (*eap_proxy_notify_sim_status)(void *ctx,
 					    enum eap_proxy_sim_state sim_state);
+
+	/**
+	 * get_imsi - Get the IMSI value from eap_proxy
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * @sim_num: SIM/USIM number to get the IMSI value for
+	 * @imsi: Buffer for IMSI value
+	 * @len: Buffer for returning IMSI length in octets
+	 * Returns: MNC length (2 or 3) or -1 on error
+	 */
+	int (*get_imsi)(void *ctx, int sim_num, char *imsi, size_t *len);
 #endif /* CONFIG_EAP_PROXY */
 
 	/**
@@ -358,6 +368,14 @@
 void eap_peer_erp_free_keys(struct eap_sm *sm);
 struct wpabuf * eap_peer_build_erp_reauth_start(struct eap_sm *sm, u8 eap_id);
 void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr, size_t len);
+int eap_peer_get_erp_info(struct eap_sm *sm, struct eap_peer_config *config,
+			  const u8 **username, size_t *username_len,
+			  const u8 **realm, size_t *realm_len, u16 *erp_seq_num,
+			  const u8 **rrk, size_t *rrk_len);
+int eap_peer_update_erp_next_seq_num(struct eap_sm *sm, u16 seq_num);
+void eap_peer_erp_init(struct eap_sm *sm, u8 *ext_session_id,
+		       size_t ext_session_id_len, u8 *ext_emsk,
+		       size_t ext_emsk_len);
 
 #endif /* IEEE8021X_EAPOL */
 
diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c
index 4188817..f7e3cd6 100644
--- a/src/eap_peer/eap_aka.c
+++ b/src/eap_peer/eap_aka.c
@@ -54,6 +54,8 @@
 	size_t network_name_len;
 	u16 kdf;
 	int kdf_negotiation;
+	u16 last_kdf_attrs[EAP_AKA_PRIME_KDF_MAX];
+	size_t last_kdf_count;
 };
 
 
@@ -415,15 +417,14 @@
 
 	if (attr->next_reauth_id) {
 		os_free(data->reauth_id);
-		data->reauth_id = os_malloc(attr->next_reauth_id_len);
+		data->reauth_id = os_memdup(attr->next_reauth_id,
+					    attr->next_reauth_id_len);
 		if (data->reauth_id == NULL) {
 			wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for "
 				   "next reauth_id");
 			data->reauth_id_len = 0;
 			return -1;
 		}
-		os_memcpy(data->reauth_id, attr->next_reauth_id,
-			  attr->next_reauth_id_len);
 		data->reauth_id_len = attr->next_reauth_id_len;
 		wpa_hexdump_ascii(MSG_DEBUG,
 				  "EAP-AKA: (encr) AT_NEXT_REAUTH_ID",
@@ -575,7 +576,7 @@
 
 
 static struct wpabuf * eap_aka_synchronization_failure(
-	struct eap_aka_data *data, u8 id)
+	struct eap_aka_data *data, u8 id, struct eap_sim_attrs *attr)
 {
 	struct eap_sim_msg *msg;
 
@@ -589,6 +590,15 @@
 	wpa_printf(MSG_DEBUG, "   AT_AUTS");
 	eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts,
 			     EAP_AKA_AUTS_LEN);
+	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+		size_t i;
+
+		for (i = 0; i < attr->kdf_count; i++) {
+			wpa_printf(MSG_DEBUG, "   AT_KDF");
+			eap_sim_msg_add(msg, EAP_SIM_AT_KDF, attr->kdf[i],
+					NULL, 0);
+		}
+	}
 	return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
 }
 
@@ -822,9 +832,13 @@
 	size_t i;
 
 	for (i = 0; i < attr->kdf_count; i++) {
-		if (attr->kdf[i] == EAP_AKA_PRIME_KDF)
+		if (attr->kdf[i] == EAP_AKA_PRIME_KDF) {
+			os_memcpy(data->last_kdf_attrs, attr->kdf,
+				  sizeof(u16) * attr->kdf_count);
+			data->last_kdf_count = attr->kdf_count;
 			return eap_aka_prime_kdf_select(data, id,
 							EAP_AKA_PRIME_KDF);
+		}
 	}
 
 	/* No matching KDF found - fail authentication as if AUTN had been
@@ -845,26 +859,32 @@
 	 * of the selected KDF into the beginning of the list. */
 
 	if (data->kdf_negotiation) {
+		/* When the peer receives the new EAP-Request/AKA'-Challenge
+		 * message, must check only requested change occurred in the
+		 * list of AT_KDF attributes. If there are any other changes,
+		 * the peer must behave like the case that AT_MAC had been
+		 * incorrect and authentication is failed. These are defined in
+		 * EAP-AKA' specification RFC 5448, Section 3.2. */
 		if (attr->kdf[0] != data->kdf) {
 			wpa_printf(MSG_WARNING, "EAP-AKA': The server did not "
 				   "accept the selected KDF");
-			return 0;
+			return -1;
+		}
+
+		if (attr->kdf_count > EAP_AKA_PRIME_KDF_MAX ||
+		    attr->kdf_count != data->last_kdf_count + 1) {
+			wpa_printf(MSG_WARNING,
+				   "EAP-AKA': The length of KDF attributes is wrong");
+			return -1;
 		}
 
 		for (i = 1; i < attr->kdf_count; i++) {
-			if (attr->kdf[i] == data->kdf)
-				break;
+			if (attr->kdf[i] != data->last_kdf_attrs[i - 1]) {
+				wpa_printf(MSG_WARNING,
+					   "EAP-AKA': The KDF attributes except selected KDF are not same as original one");
+				return -1;
+			}
 		}
-		if (i == attr->kdf_count &&
-		    attr->kdf_count < EAP_AKA_PRIME_KDF_MAX) {
-			wpa_printf(MSG_WARNING, "EAP-AKA': The server did not "
-				   "duplicate the selected KDF");
-			return 0;
-		}
-
-		/* TODO: should check that the list is identical to the one
-		 * used in the previous Challenge message apart from the added
-		 * entry in the beginning. */
 	}
 
 	for (i = data->kdf ? 1 : 0; i < attr->kdf_count; i++) {
@@ -913,22 +933,25 @@
 			return eap_aka_authentication_reject(data, id);
 		}
 		os_free(data->network_name);
-		data->network_name = os_malloc(attr->kdf_input_len);
+		data->network_name = os_memdup(attr->kdf_input,
+					       attr->kdf_input_len);
 		if (data->network_name == NULL) {
 			wpa_printf(MSG_WARNING, "EAP-AKA': No memory for "
 				   "storing Network Name");
 			return eap_aka_authentication_reject(data, id);
 		}
-		os_memcpy(data->network_name, attr->kdf_input,
-			  attr->kdf_input_len);
 		data->network_name_len = attr->kdf_input_len;
 		wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': Network Name "
 				  "(AT_KDF_INPUT)",
 				  data->network_name, data->network_name_len);
 		/* TODO: check Network Name per 3GPP.33.402 */
 
-		if (!eap_aka_prime_kdf_valid(data, attr))
+		res = eap_aka_prime_kdf_valid(data, attr);
+		if (res == 0)
 			return eap_aka_authentication_reject(data, id);
+		else if (res == -1)
+			return eap_aka_client_error(
+				data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET);
 
 		if (attr->kdf[0] != EAP_AKA_PRIME_KDF)
 			return eap_aka_prime_kdf_neg(data, id, attr);
@@ -971,7 +994,7 @@
 	} else if (res == -2) {
 		wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication "
 			   "failed (AUTN seq# -> AUTS)");
-		return eap_aka_synchronization_failure(data, id);
+		return eap_aka_synchronization_failure(data, id, attr);
 	} else if (res > 0) {
 		wpa_printf(MSG_DEBUG, "EAP-AKA: Wait for external USIM processing");
 		return NULL;
@@ -1442,12 +1465,11 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+	key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN);
 	if (key == NULL)
 		return NULL;
 
 	*len = EAP_SIM_KEYING_DATA_LEN;
-	os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
 
 	return key;
 }
@@ -1483,12 +1505,11 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
 
 	*len = EAP_EMSK_LEN;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 
 	return key;
 }
diff --git a/src/eap_peer/eap_eke.c b/src/eap_peer/eap_eke.c
index f899f65..0de7d6c 100644
--- a/src/eap_peer/eap_eke.c
+++ b/src/eap_peer/eap_eke.c
@@ -85,12 +85,11 @@
 
 	identity = eap_get_config_identity(sm, &identity_len);
 	if (identity) {
-		data->peerid = os_malloc(identity_len);
+		data->peerid = os_memdup(identity, identity_len);
 		if (data->peerid == NULL) {
 			eap_eke_deinit(sm, data);
 			return NULL;
 		}
-		os_memcpy(data->peerid, identity, identity_len);
 		data->peerid_len = identity_len;
 	}
 
@@ -310,12 +309,11 @@
 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Server Identity",
 			  pos, end - pos);
 	os_free(data->serverid);
-	data->serverid = os_malloc(end - pos);
+	data->serverid = os_memdup(pos, end - pos);
 	if (data->serverid == NULL) {
 		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
-	os_memcpy(data->serverid, pos, end - pos);
 	data->serverid_len = end - pos;
 
 	wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-ID/Response");
@@ -717,10 +715,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_MSK_LEN);
+	key = os_memdup(data->msk, EAP_MSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->msk, EAP_MSK_LEN);
 	*len = EAP_MSK_LEN;
 
 	return key;
@@ -735,10 +732,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 	*len = EAP_EMSK_LEN;
 
 	return key;
diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c
index e4b0c10..74cec7d 100644
--- a/src/eap_peer/eap_fast.c
+++ b/src/eap_peer/eap_fast.c
@@ -1749,12 +1749,11 @@
 	if (!data->success)
 		return NULL;
 
-	key = os_malloc(EAP_FAST_KEY_LEN);
+	key = os_memdup(data->key_data, EAP_FAST_KEY_LEN);
 	if (key == NULL)
 		return NULL;
 
 	*len = EAP_FAST_KEY_LEN;
-	os_memcpy(key, data->key_data, EAP_FAST_KEY_LEN);
 
 	return key;
 }
@@ -1768,12 +1767,11 @@
 	if (!data->success || !data->session_id)
 		return NULL;
 
-	id = os_malloc(data->id_len);
+	id = os_memdup(data->session_id, data->id_len);
 	if (id == NULL)
 		return NULL;
 
 	*len = data->id_len;
-	os_memcpy(id, data->session_id, data->id_len);
 
 	return id;
 }
@@ -1787,12 +1785,11 @@
 	if (!data->success)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
 
 	*len = EAP_EMSK_LEN;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 
 	return key;
 }
diff --git a/src/eap_peer/eap_fast_pac.c b/src/eap_peer/eap_fast_pac.c
index c815860..7d674c8 100644
--- a/src/eap_peer/eap_fast_pac.c
+++ b/src/eap_peer/eap_fast_pac.c
@@ -114,10 +114,9 @@
 			     const u8 *src, size_t src_len)
 {
 	if (src) {
-		*dst = os_malloc(src_len);
+		*dst = os_memdup(src, src_len);
 		if (*dst == NULL)
 			return -1;
-		os_memcpy(*dst, src, src_len);
 		*dst_len = src_len;
 	}
 	return 0;
@@ -720,19 +719,17 @@
 
 		if (type == PAC_TYPE_A_ID) {
 			os_free(pac->a_id);
-			pac->a_id = os_malloc(len);
+			pac->a_id = os_memdup(pos, len);
 			if (pac->a_id == NULL)
 				break;
-			os_memcpy(pac->a_id, pos, len);
 			pac->a_id_len = len;
 		}
 
 		if (type == PAC_TYPE_A_ID_INFO) {
 			os_free(pac->a_id_info);
-			pac->a_id_info = os_malloc(len);
+			pac->a_id_info = os_memdup(pos, len);
 			if (pac->a_id_info == NULL)
 				break;
-			os_memcpy(pac->a_id_info, pos, len);
 			pac->a_id_info_len = len;
 		}
 
@@ -820,10 +817,9 @@
 		if (val > end - pos)
 			goto parse_fail;
 		pac->pac_opaque_len = val;
-		pac->pac_opaque = os_malloc(pac->pac_opaque_len);
+		pac->pac_opaque = os_memdup(pos, pac->pac_opaque_len);
 		if (pac->pac_opaque == NULL)
 			goto parse_fail;
-		os_memcpy(pac->pac_opaque, pos, pac->pac_opaque_len);
 		pos += pac->pac_opaque_len;
 		if (2 > end - pos)
 			goto parse_fail;
@@ -832,10 +828,9 @@
 		if (val > end - pos)
 			goto parse_fail;
 		pac->pac_info_len = val;
-		pac->pac_info = os_malloc(pac->pac_info_len);
+		pac->pac_info = os_memdup(pos, pac->pac_info_len);
 		if (pac->pac_info == NULL)
 			goto parse_fail;
-		os_memcpy(pac->pac_info, pos, pac->pac_info_len);
 		pos += pac->pac_info_len;
 		eap_fast_pac_get_a_id(pac);
 
diff --git a/src/eap_peer/eap_gpsk.c b/src/eap_peer/eap_gpsk.c
index 177cbcc..f9c4d37 100644
--- a/src/eap_peer/eap_gpsk.c
+++ b/src/eap_peer/eap_gpsk.c
@@ -96,12 +96,11 @@
 
 	identity = eap_get_config_identity(sm, &identity_len);
 	if (identity) {
-		data->id_peer = os_malloc(identity_len);
+		data->id_peer = os_memdup(identity, identity_len);
 		if (data->id_peer == NULL) {
 			eap_gpsk_deinit(sm, data);
 			return NULL;
 		}
-		os_memcpy(data->id_peer, identity, identity_len);
 		data->id_peer_len = identity_len;
 	}
 
@@ -117,12 +116,11 @@
 		}
 	}
 
-	data->psk = os_malloc(password_len);
+	data->psk = os_memdup(password, password_len);
 	if (data->psk == NULL) {
 		eap_gpsk_deinit(sm, data);
 		return NULL;
 	}
-	os_memcpy(data->psk, password, password_len);
 	data->psk_len = password_len;
 
 	return data;
@@ -158,12 +156,11 @@
 		return NULL;
 	}
 	os_free(data->id_server);
-	data->id_server = os_malloc(alen);
+	data->id_server = os_memdup(pos, alen);
 	if (data->id_server == NULL) {
 		wpa_printf(MSG_DEBUG, "EAP-GPSK: No memory for ID_Server");
 		return NULL;
 	}
-	os_memcpy(data->id_server, pos, alen);
 	data->id_server_len = alen;
 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server",
 			  data->id_server, data->id_server_len);
@@ -722,10 +719,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_MSK_LEN);
+	key = os_memdup(data->msk, EAP_MSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->msk, EAP_MSK_LEN);
 	*len = EAP_MSK_LEN;
 
 	return key;
@@ -740,10 +736,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 	*len = EAP_EMSK_LEN;
 
 	return key;
@@ -758,10 +753,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	sid = os_malloc(data->id_len);
+	sid = os_memdup(data->session_id, data->id_len);
 	if (sid == NULL)
 		return NULL;
-	os_memcpy(sid, data->session_id, data->id_len);
 	*len = data->id_len;
 
 	return sid;
diff --git a/src/eap_peer/eap_ikev2.c b/src/eap_peer/eap_ikev2.c
index 390f0ec..6ddf508 100644
--- a/src/eap_peer/eap_ikev2.c
+++ b/src/eap_peer/eap_ikev2.c
@@ -83,18 +83,16 @@
 	if (data->ikev2.key_pad == NULL)
 		goto failed;
 	data->ikev2.key_pad_len = 21;
-	data->ikev2.IDr = os_malloc(identity_len);
+	data->ikev2.IDr = os_memdup(identity, identity_len);
 	if (data->ikev2.IDr == NULL)
 		goto failed;
-	os_memcpy(data->ikev2.IDr, identity, identity_len);
 	data->ikev2.IDr_len = identity_len;
 
 	password = eap_get_config_password(sm, &password_len);
 	if (password) {
-		data->ikev2.shared_secret = os_malloc(password_len);
+		data->ikev2.shared_secret = os_memdup(password, password_len);
 		if (data->ikev2.shared_secret == NULL)
 			goto failed;
-		os_memcpy(data->ikev2.shared_secret, password, password_len);
 		data->ikev2.shared_secret_len = password_len;
 	}
 
diff --git a/src/eap_peer/eap_leap.c b/src/eap_peer/eap_leap.c
index ff6fa4a..233b9ee 100644
--- a/src/eap_peer/eap_leap.c
+++ b/src/eap_peer/eap_leap.c
@@ -115,10 +115,14 @@
 	wpabuf_put_u8(resp, 0); /* unused */
 	wpabuf_put_u8(resp, LEAP_RESPONSE_LEN);
 	rpos = wpabuf_put(resp, LEAP_RESPONSE_LEN);
-	if (pwhash)
-		challenge_response(challenge, password, rpos);
-	else
-		nt_challenge_response(challenge, password, password_len, rpos);
+	if ((pwhash && challenge_response(challenge, password, rpos)) ||
+	    (!pwhash &&
+	     nt_challenge_response(challenge, password, password_len, rpos))) {
+		wpa_printf(MSG_DEBUG, "EAP-LEAP: Failed to derive response");
+		ret->ignore = TRUE;
+		wpabuf_free(resp);
+		return NULL;
+	}
 	os_memcpy(data->peer_response, rpos, LEAP_RESPONSE_LEN);
 	wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response",
 		    rpos, LEAP_RESPONSE_LEN);
@@ -239,7 +243,10 @@
 			return NULL;
 		}
 	}
-	challenge_response(data->ap_challenge, pw_hash_hash, expected);
+	if (challenge_response(data->ap_challenge, pw_hash_hash, expected)) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
 
 	ret->methodState = METHOD_DONE;
 	ret->allowNotifications = FALSE;
diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c
index ce2227d..877495c 100644
--- a/src/eap_peer/eap_mschapv2.c
+++ b/src/eap_peer/eap_mschapv2.c
@@ -109,23 +109,21 @@
 		return NULL;
 
 	if (sm->peer_challenge) {
-		data->peer_challenge = os_malloc(MSCHAPV2_CHAL_LEN);
+		data->peer_challenge = os_memdup(sm->peer_challenge,
+						 MSCHAPV2_CHAL_LEN);
 		if (data->peer_challenge == NULL) {
 			eap_mschapv2_deinit(sm, data);
 			return NULL;
 		}
-		os_memcpy(data->peer_challenge, sm->peer_challenge,
-			  MSCHAPV2_CHAL_LEN);
 	}
 
 	if (sm->auth_challenge) {
-		data->auth_challenge = os_malloc(MSCHAPV2_CHAL_LEN);
+		data->auth_challenge = os_memdup(sm->auth_challenge,
+						 MSCHAPV2_CHAL_LEN);
 		if (data->auth_challenge == NULL) {
 			eap_mschapv2_deinit(sm, data);
 			return NULL;
 		}
-		os_memcpy(data->auth_challenge, sm->auth_challenge,
-			  MSCHAPV2_CHAL_LEN);
 	}
 
 	data->phase2 = sm->init_phase2;
@@ -567,11 +565,11 @@
 	if (pwhash) {
 		u8 new_password_hash[16];
 		if (nt_password_hash(new_password, new_password_len,
-				     new_password_hash))
+				     new_password_hash) ||
+		    nt_password_hash_encrypted_with_block(password,
+							  new_password_hash,
+							  cp->encr_hash))
 			goto fail;
-		nt_password_hash_encrypted_with_block(password,
-						      new_password_hash,
-						      cp->encr_hash);
 	} else {
 		if (old_nt_password_hash_encrypted_with_new_nt_password_hash(
 			    new_password, new_password_len,
diff --git a/src/eap_peer/eap_pax.c b/src/eap_peer/eap_pax.c
index a7012d2..3cef1c8 100644
--- a/src/eap_peer/eap_pax.c
+++ b/src/eap_peer/eap_pax.c
@@ -69,12 +69,11 @@
 		return NULL;
 	data->state = PAX_INIT;
 
-	data->cid = os_malloc(identity_len);
+	data->cid = os_memdup(identity, identity_len);
 	if (data->cid == NULL) {
 		eap_pax_deinit(sm, data);
 		return NULL;
 	}
-	os_memcpy(data->cid, identity, identity_len);
 	data->cid_len = identity_len;
 
 	os_memcpy(data->ak, password, EAP_PAX_AK_LEN);
diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c
index 2ff6076..34075b1 100644
--- a/src/eap_peer/eap_peap.c
+++ b/src/eap_peer/eap_peap.c
@@ -1272,12 +1272,11 @@
 	if (data->session_id == NULL || !data->phase2_success)
 		return NULL;
 
-	id = os_malloc(data->id_len);
+	id = os_memdup(data->session_id, data->id_len);
 	if (id == NULL)
 		return NULL;
 
 	*len = data->id_len;
-	os_memcpy(id, data->session_id, data->id_len);
 
 	return id;
 }
diff --git a/src/eap_peer/eap_proxy.h b/src/eap_peer/eap_proxy.h
index 7205fad..9d8e570 100644
--- a/src/eap_peer/eap_proxy.h
+++ b/src/eap_peer/eap_proxy.h
@@ -40,10 +40,16 @@
 int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen,
 			    int verbose);
 
-int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf,
-		       size_t *imsi_len);
+int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, int sim_num,
+		       char *imsi_buf, size_t *imsi_len);
 
 int eap_proxy_notify_config(struct eap_proxy_sm *sm,
 			    struct eap_peer_config *config);
 
+u8 * eap_proxy_get_eap_session_id(struct eap_proxy_sm *sm, size_t *len);
+
+u8 * eap_proxy_get_emsk(struct eap_proxy_sm *sm, size_t *len);
+
+void eap_proxy_sm_abort(struct eap_proxy_sm *sm);
+
 #endif /* EAP_PROXY_H */
diff --git a/src/eap_peer/eap_proxy_dummy.c b/src/eap_peer/eap_proxy_dummy.c
index 08009ca..2cc05c9 100644
--- a/src/eap_peer/eap_proxy_dummy.c
+++ b/src/eap_peer/eap_proxy_dummy.c
@@ -63,8 +63,8 @@
 }
 
 
-int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf,
-		       size_t *imsi_len)
+int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, int sim_num,
+		       char *imsi_buf, size_t *imsi_len)
 {
 	return -1;
 }
@@ -75,3 +75,20 @@
 {
 	return -1;
 }
+
+
+u8 * eap_proxy_get_eap_session_id(struct eap_proxy_sm *sm, size_t *len)
+{
+	return NULL;
+}
+
+
+u8 * eap_proxy_get_emsk(struct eap_proxy_sm *sm, size_t *len)
+{
+	return NULL;
+}
+
+
+void eap_proxy_sm_abort(struct eap_proxy_sm *sm)
+{
+}
diff --git a/src/eap_peer/eap_psk.c b/src/eap_peer/eap_psk.c
index ac18c15..eea9430 100644
--- a/src/eap_peer/eap_psk.c
+++ b/src/eap_peer/eap_psk.c
@@ -116,14 +116,13 @@
 	os_memcpy(data->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN);
 	os_free(data->id_s);
 	data->id_s_len = len - sizeof(*hdr1);
-	data->id_s = os_malloc(data->id_s_len);
+	data->id_s = os_memdup(hdr1 + 1, data->id_s_len);
 	if (data->id_s == NULL) {
 		wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory for "
 			   "ID_S (len=%lu)", (unsigned long) data->id_s_len);
 		ret->ignore = TRUE;
 		return NULL;
 	}
-	os_memcpy(data->id_s, (u8 *) (hdr1 + 1), data->id_s_len);
 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S",
 			  data->id_s, data->id_s_len);
 
@@ -273,13 +272,12 @@
 		    wpabuf_head(reqData), 5);
 	wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - cipher msg", msg, left);
 
-	decrypted = os_malloc(left);
+	decrypted = os_memdup(msg, left);
 	if (decrypted == NULL) {
 		ret->methodState = METHOD_DONE;
 		ret->decision = DECISION_FAIL;
 		return NULL;
 	}
-	os_memcpy(decrypted, msg, left);
 
 	if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce),
 				wpabuf_head(reqData),
@@ -425,12 +423,11 @@
 	if (data->state != PSK_DONE)
 		return NULL;
 
-	key = os_malloc(EAP_MSK_LEN);
+	key = os_memdup(data->msk, EAP_MSK_LEN);
 	if (key == NULL)
 		return NULL;
 
 	*len = EAP_MSK_LEN;
-	os_memcpy(key, data->msk, EAP_MSK_LEN);
 
 	return key;
 }
@@ -466,12 +463,11 @@
 	if (data->state != PSK_DONE)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
 
 	*len = EAP_EMSK_LEN;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 
 	return key;
 }
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
index 662347b..ec277ac 100644
--- a/src/eap_peer/eap_pwd.c
+++ b/src/eap_peer/eap_pwd.c
@@ -183,11 +183,10 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_MSK_LEN);
+	key = os_memdup(data->msk, EAP_MSK_LEN);
 	if (key == NULL)
 		return NULL;
 
-	os_memcpy(key, data->msk, EAP_MSK_LEN);
 	*len = EAP_MSK_LEN;
 
 	return key;
@@ -202,11 +201,10 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	id = os_malloc(1 + SHA256_MAC_LEN);
+	id = os_memdup(data->session_id, 1 + SHA256_MAC_LEN);
 	if (id == NULL)
 		return NULL;
 
-	os_memcpy(id, data->session_id, 1 + SHA256_MAC_LEN);
 	*len = 1 + SHA256_MAC_LEN;
 
 	return id;
diff --git a/src/eap_peer/eap_sake.c b/src/eap_peer/eap_sake.c
index 330febb..0a6ce25 100644
--- a/src/eap_peer/eap_sake.c
+++ b/src/eap_peer/eap_sake.c
@@ -85,12 +85,11 @@
 
 	identity = eap_get_config_identity(sm, &identity_len);
 	if (identity) {
-		data->peerid = os_malloc(identity_len);
+		data->peerid = os_memdup(identity, identity_len);
 		if (data->peerid == NULL) {
 			eap_sake_deinit(sm, data);
 			return NULL;
 		}
-		os_memcpy(data->peerid, identity, identity_len);
 		data->peerid_len = identity_len;
 	}
 
@@ -230,10 +229,9 @@
 	if (attr.serverid) {
 		wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-SAKE: SERVERID",
 				  attr.serverid, attr.serverid_len);
-		data->serverid = os_malloc(attr.serverid_len);
+		data->serverid = os_memdup(attr.serverid, attr.serverid_len);
 		if (data->serverid == NULL)
 			return NULL;
-		os_memcpy(data->serverid, attr.serverid, attr.serverid_len);
 		data->serverid_len = attr.serverid_len;
 	}
 
@@ -450,10 +448,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_MSK_LEN);
+	key = os_memdup(data->msk, EAP_MSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->msk, EAP_MSK_LEN);
 	*len = EAP_MSK_LEN;
 
 	return key;
@@ -490,10 +487,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 	*len = EAP_EMSK_LEN;
 
 	return key;
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
index 95ecdf7..25f592c 100644
--- a/src/eap_peer/eap_sim.c
+++ b/src/eap_peer/eap_sim.c
@@ -437,15 +437,14 @@
 
 	if (attr->next_reauth_id) {
 		os_free(data->reauth_id);
-		data->reauth_id = os_malloc(attr->next_reauth_id_len);
+		data->reauth_id = os_memdup(attr->next_reauth_id,
+					    attr->next_reauth_id_len);
 		if (data->reauth_id == NULL) {
 			wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for "
 				   "next reauth_id");
 			data->reauth_id_len = 0;
 			return -1;
 		}
-		os_memcpy(data->reauth_id, attr->next_reauth_id,
-			  attr->next_reauth_id_len);
 		data->reauth_id_len = attr->next_reauth_id_len;
 		wpa_hexdump_ascii(MSG_DEBUG,
 				  "EAP-SIM: (encr) AT_NEXT_REAUTH_ID",
@@ -640,14 +639,13 @@
 	}
 
 	os_free(data->ver_list);
-	data->ver_list = os_malloc(attr->version_list_len);
+	data->ver_list = os_memdup(attr->version_list, attr->version_list_len);
 	if (data->ver_list == NULL) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to allocate "
 			   "memory for version list");
 		return eap_sim_client_error(data, id,
 					    EAP_SIM_UNABLE_TO_PROCESS_PACKET);
 	}
-	os_memcpy(data->ver_list, attr->version_list, attr->version_list_len);
 	data->ver_list_len = attr->version_list_len;
 	pos = data->ver_list;
 	for (i = 0; i < data->ver_list_len / 2; i++) {
@@ -1186,12 +1184,11 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+	key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN);
 	if (key == NULL)
 		return NULL;
 
 	*len = EAP_SIM_KEYING_DATA_LEN;
-	os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
 
 	return key;
 }
@@ -1228,12 +1225,11 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
 
 	*len = EAP_EMSK_LEN;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 
 	return key;
 }
diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c
index ca2354f..c1820a4 100644
--- a/src/eap_peer/eap_tls.c
+++ b/src/eap_peer/eap_tls.c
@@ -338,12 +338,11 @@
 	if (data->key_data == NULL)
 		return NULL;
 
-	key = os_malloc(EAP_TLS_KEY_LEN);
+	key = os_memdup(data->key_data, EAP_TLS_KEY_LEN);
 	if (key == NULL)
 		return NULL;
 
 	*len = EAP_TLS_KEY_LEN;
-	os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
 
 	return key;
 }
@@ -357,12 +356,11 @@
 	if (data->key_data == NULL)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
 
 	*len = EAP_EMSK_LEN;
-	os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
 
 	return key;
 }
@@ -376,12 +374,11 @@
 	if (data->session_id == NULL)
 		return NULL;
 
-	id = os_malloc(data->id_len);
+	id = os_memdup(data->session_id, data->id_len);
 	if (id == NULL)
 		return NULL;
 
 	*len = data->id_len;
-	os_memcpy(id, data->session_id, data->id_len);
 
 	return id;
 }
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
index 0dcb9c1..b3d4aba 100644
--- a/src/eap_peer/eap_tls_common.c
+++ b/src/eap_peer/eap_tls_common.c
@@ -84,6 +84,14 @@
 		params->flags |= TLS_CONN_EXT_CERT_CHECK;
 	if (os_strstr(txt, "tls_ext_cert_check=0"))
 		params->flags &= ~TLS_CONN_EXT_CERT_CHECK;
+	if (os_strstr(txt, "tls_suiteb=1"))
+		params->flags |= TLS_CONN_SUITEB;
+	if (os_strstr(txt, "tls_suiteb=0"))
+		params->flags &= ~TLS_CONN_SUITEB;
+	if (os_strstr(txt, "tls_suiteb_no_ecdh=1"))
+		params->flags |= TLS_CONN_SUITEB_NO_ECDH;
+	if (os_strstr(txt, "tls_suiteb_no_ecdh=0"))
+		params->flags &= ~TLS_CONN_SUITEB_NO_ECDH;
 }
 
 
diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c
index 3354b2d..f18788c 100644
--- a/src/eap_peer/eap_ttls.c
+++ b/src/eap_peer/eap_ttls.c
@@ -624,12 +624,28 @@
 	os_memset(pos, 0, 24); /* LM-Response */
 	pos += 24;
 	if (pwhash) {
-		challenge_response(challenge, password, pos); /* NT-Response */
+		/* NT-Response */
+		if (challenge_response(challenge, password, pos)) {
+			wpa_printf(MSG_ERROR,
+				   "EAP-TTLS/MSCHAP: Failed derive password hash");
+			wpabuf_free(msg);
+			os_free(challenge);
+			return -1;
+		}
+
 		wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password hash",
 				password, 16);
 	} else {
-		nt_challenge_response(challenge, password, password_len,
-				      pos); /* NT-Response */
+		/* NT-Response */
+		if (nt_challenge_response(challenge, password, password_len,
+					  pos)) {
+			wpa_printf(MSG_ERROR,
+				   "EAP-TTLS/MSCHAP: Failed derive password");
+			wpabuf_free(msg);
+			os_free(challenge);
+			return -1;
+		}
+
 		wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password",
 				      password, password_len);
 	}
@@ -870,13 +886,12 @@
 {
 	wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message");
 	if (parse->eapdata == NULL) {
-		parse->eapdata = os_malloc(dlen);
+		parse->eapdata = os_memdup(dpos, dlen);
 		if (parse->eapdata == NULL) {
 			wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate "
 				   "memory for Phase 2 EAP data");
 			return -1;
 		}
-		os_memcpy(parse->eapdata, dpos, dlen);
 		parse->eap_len = dlen;
 	} else {
 		u8 *neweap = os_realloc(parse->eapdata, parse->eap_len + dlen);
@@ -1745,12 +1760,11 @@
 	if (data->key_data == NULL || !data->phase2_success)
 		return NULL;
 
-	key = os_malloc(EAP_TLS_KEY_LEN);
+	key = os_memdup(data->key_data, EAP_TLS_KEY_LEN);
 	if (key == NULL)
 		return NULL;
 
 	*len = EAP_TLS_KEY_LEN;
-	os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
 
 	return key;
 }
@@ -1764,12 +1778,11 @@
 	if (data->session_id == NULL || !data->phase2_success)
 		return NULL;
 
-	id = os_malloc(data->id_len);
+	id = os_memdup(data->session_id, data->id_len);
 	if (id == NULL)
 		return NULL;
 
 	*len = data->id_len;
-	os_memcpy(id, data->session_id, data->id_len);
 
 	return id;
 }
@@ -1783,12 +1796,11 @@
 	if (data->key_data == NULL)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
 
 	*len = EAP_EMSK_LEN;
-	os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
 
 	return key;
 }
diff --git a/src/eap_peer/ikev2.c b/src/eap_peer/ikev2.c
index ca6502e..7bd97b1 100644
--- a/src/eap_peer/ikev2.c
+++ b/src/eap_peer/ikev2.c
@@ -476,10 +476,9 @@
 	wpa_printf(MSG_DEBUG, "IKEV2: IDi ID Type %d", id_type);
 	wpa_hexdump_ascii(MSG_DEBUG, "IKEV2: IDi", idi, idi_len);
 	os_free(data->IDi);
-	data->IDi = os_malloc(idi_len);
+	data->IDi = os_memdup(idi, idi_len);
 	if (data->IDi == NULL)
 		return -1;
-	os_memcpy(data->IDi, idi, idi_len);
 	data->IDi_len = idi_len;
 	data->IDi_type = id_type;
 
diff --git a/src/eap_peer/tncc.c b/src/eap_peer/tncc.c
index 0c5caa7..a9bafe2 100644
--- a/src/eap_peer/tncc.c
+++ b/src/eap_peer/tncc.c
@@ -126,12 +126,10 @@
 
 	imc = tnc_imc[imcID];
 	os_free(imc->supported_types);
-	imc->supported_types =
-		os_malloc(typeCount * sizeof(TNC_MessageType));
+	imc->supported_types = os_memdup(supportedTypes,
+					 typeCount * sizeof(TNC_MessageType));
 	if (imc->supported_types == NULL)
 		return TNC_RESULT_FATAL;
-	os_memcpy(imc->supported_types, supportedTypes,
-		  typeCount * sizeof(TNC_MessageType));
 	imc->num_supported_types = typeCount;
 
 	return TNC_RESULT_SUCCESS;
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index 93eab62..c67fa82 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -132,6 +132,7 @@
 	size_t server_id_len;
 	int erp;
 	unsigned int tls_session_lifetime;
+	unsigned int tls_flags;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	u32 tls_test_flags;
diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h
index c90443d..3d6f8d5 100644
--- a/src/eap_server/eap_i.h
+++ b/src/eap_server/eap_i.h
@@ -211,6 +211,7 @@
 	Boolean try_initiate_reauth;
 	int erp;
 	unsigned int tls_session_lifetime;
+	unsigned int tls_flags;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	u32 tls_test_flags;
diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c
index 1b571cf..9706e25 100644
--- a/src/eap_server/eap_server.c
+++ b/src/eap_server/eap_server.c
@@ -1868,6 +1868,7 @@
 	sm->server_id_len = conf->server_id_len;
 	sm->erp = conf->erp;
 	sm->tls_session_lifetime = conf->tls_session_lifetime;
+	sm->tls_flags = conf->tls_flags;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	sm->tls_test_flags = conf->tls_test_flags;
diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c
index a8bb5ea..1750211 100644
--- a/src/eap_server/eap_server_aka.c
+++ b/src/eap_server/eap_server_aka.c
@@ -1261,10 +1261,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+	key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
 	*len = EAP_SIM_KEYING_DATA_LEN;
 	return key;
 }
@@ -1278,10 +1277,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 	*len = EAP_EMSK_LEN;
 	return key;
 }
diff --git a/src/eap_server/eap_server_eke.c b/src/eap_server/eap_server_eke.c
index 1eba8f5..71580bf 100644
--- a/src/eap_server/eap_server_eke.c
+++ b/src/eap_server/eap_server_eke.c
@@ -467,13 +467,12 @@
 
 	data->peerid_type = *pos++;
 	os_free(data->peerid);
-	data->peerid = os_malloc(end - pos);
+	data->peerid = os_memdup(pos, end - pos);
 	if (data->peerid == NULL) {
 		wpa_printf(MSG_INFO, "EAP-EKE: Failed to allocate memory for peerid");
 		eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 		return;
 	}
-	os_memcpy(data->peerid, pos, end - pos);
 	data->peerid_len = end - pos;
 	wpa_printf(MSG_DEBUG, "EAP-EKE: Peer IDType %u", data->peerid_type);
 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Peer Identity",
@@ -731,10 +730,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_MSK_LEN);
+	key = os_memdup(data->msk, EAP_MSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->msk, EAP_MSK_LEN);
 	*len = EAP_MSK_LEN;
 
 	return key;
@@ -749,10 +747,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 	*len = EAP_EMSK_LEN;
 
 	return key;
diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c
index f6d7e32..fa0342f 100644
--- a/src/eap_server/eap_server_fast.c
+++ b/src/eap_server/eap_server_fast.c
@@ -471,12 +471,11 @@
 		eap_fast_reset(sm, data);
 		return NULL;
 	}
-	data->srv_id = os_malloc(sm->eap_fast_a_id_len);
+	data->srv_id = os_memdup(sm->eap_fast_a_id, sm->eap_fast_a_id_len);
 	if (data->srv_id == NULL) {
 		eap_fast_reset(sm, data);
 		return NULL;
 	}
-	os_memcpy(data->srv_id, sm->eap_fast_a_id, sm->eap_fast_a_id_len);
 	data->srv_id_len = sm->eap_fast_a_id_len;
 
 	if (sm->eap_fast_a_id_info == NULL) {
diff --git a/src/eap_server/eap_server_gpsk.c b/src/eap_server/eap_server_gpsk.c
index 94e74ec..fb3d117 100644
--- a/src/eap_server/eap_server_gpsk.c
+++ b/src/eap_server/eap_server_gpsk.c
@@ -269,13 +269,12 @@
 		return;
 	}
 	os_free(data->id_peer);
-	data->id_peer = os_malloc(alen);
+	data->id_peer = os_memdup(pos, alen);
 	if (data->id_peer == NULL) {
 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Not enough memory to store "
 			   "%d-octet ID_Peer", alen);
 		return;
 	}
-	os_memcpy(data->id_peer, pos, alen);
 	data->id_peer_len = alen;
 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer",
 			  data->id_peer, data->id_peer_len);
@@ -575,10 +574,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_MSK_LEN);
+	key = os_memdup(data->msk, EAP_MSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->msk, EAP_MSK_LEN);
 	*len = EAP_MSK_LEN;
 
 	return key;
@@ -593,10 +591,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 	*len = EAP_EMSK_LEN;
 
 	return key;
@@ -618,10 +615,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	sid = os_malloc(data->id_len);
+	sid = os_memdup(data->session_id, data->id_len);
 	if (sid == NULL)
 		return NULL;
-	os_memcpy(sid, data->session_id, data->id_len);
 	*len = data->id_len;
 
 	return sid;
diff --git a/src/eap_server/eap_server_gtc.c b/src/eap_server/eap_server_gtc.c
index 193a851..fcccbcb 100644
--- a/src/eap_server/eap_server_gtc.c
+++ b/src/eap_server/eap_server_gtc.c
@@ -141,12 +141,11 @@
 		} else {
 			os_free(sm->identity);
 			sm->identity_len = pos2 - pos;
-			sm->identity = os_malloc(sm->identity_len);
+			sm->identity = os_memdup(pos, sm->identity_len);
 			if (sm->identity == NULL) {
 				data->state = FAILURE;
 				return;
 			}
-			os_memcpy(sm->identity, pos, sm->identity_len);
 		}
 
 		if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
diff --git a/src/eap_server/eap_server_ikev2.c b/src/eap_server/eap_server_ikev2.c
index 3a249d1..1833419 100644
--- a/src/eap_server/eap_server_ikev2.c
+++ b/src/eap_server/eap_server_ikev2.c
@@ -103,10 +103,9 @@
 	data->ikev2.proposal.encr = ENCR_AES_CBC;
 	data->ikev2.proposal.dh = DH_GROUP2_1024BIT_MODP;
 
-	data->ikev2.IDi = os_malloc(sm->server_id_len);
+	data->ikev2.IDi = os_memdup(sm->server_id, sm->server_id_len);
 	if (data->ikev2.IDi == NULL)
 		goto failed;
-	os_memcpy(data->ikev2.IDi, sm->server_id, sm->server_id_len);
 	data->ikev2.IDi_len = sm->server_id_len;
 
 	data->ikev2.get_shared_secret = eap_ikev2_get_shared_secret;
diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c
index 460cd9c..6c47bb6 100644
--- a/src/eap_server/eap_server_mschapv2.c
+++ b/src/eap_server/eap_server_mschapv2.c
@@ -71,13 +71,12 @@
 	}
 
 	if (sm->peer_challenge) {
-		data->peer_challenge = os_malloc(CHALLENGE_LEN);
+		data->peer_challenge = os_memdup(sm->peer_challenge,
+						 CHALLENGE_LEN);
 		if (data->peer_challenge == NULL) {
 			os_free(data);
 			return NULL;
 		}
-		os_memcpy(data->peer_challenge, sm->peer_challenge,
-			  CHALLENGE_LEN);
 	}
 
 	return data;
diff --git a/src/eap_server/eap_server_pax.c b/src/eap_server/eap_server_pax.c
index 782b8c3..3257789 100644
--- a/src/eap_server/eap_server_pax.c
+++ b/src/eap_server/eap_server_pax.c
@@ -327,13 +327,12 @@
 	}
 	data->cid_len = cid_len;
 	os_free(data->cid);
-	data->cid = os_malloc(data->cid_len);
+	data->cid = os_memdup(pos + 2, data->cid_len);
 	if (data->cid == NULL) {
 		wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for "
 			   "CID");
 		return;
 	}
-	os_memcpy(data->cid, pos + 2, data->cid_len);
 	pos += 2 + data->cid_len;
 	left -= 2 + data->cid_len;
 	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID",
diff --git a/src/eap_server/eap_server_psk.c b/src/eap_server/eap_server_psk.c
index 857d421..0eab893 100644
--- a/src/eap_server/eap_server_psk.c
+++ b/src/eap_server/eap_server_psk.c
@@ -236,13 +236,12 @@
 	left -= sizeof(*resp);
 
 	os_free(data->id_p);
-	data->id_p = os_malloc(left);
+	data->id_p = os_memdup(cpos, left);
 	if (data->id_p == NULL) {
 		wpa_printf(MSG_INFO, "EAP-PSK: Failed to allocate memory for "
 			   "ID_P");
 		return;
 	}
-	os_memcpy(data->id_p, cpos, left);
 	data->id_p_len = left;
 	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PSK: ID_P",
 			  data->id_p, data->id_p_len);
@@ -371,10 +370,9 @@
 	pos += 16;
 	left -= 16;
 
-	decrypted = os_malloc(left);
+	decrypted = os_memdup(pos, left);
 	if (decrypted == NULL)
 		return;
-	os_memcpy(decrypted, pos, left);
 
 	if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce),
 				wpabuf_head(respData), 22, decrypted, left,
@@ -450,10 +448,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_MSK_LEN);
+	key = os_memdup(data->msk, EAP_MSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->msk, EAP_MSK_LEN);
 	*len = EAP_MSK_LEN;
 
 	return key;
@@ -468,10 +465,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 	*len = EAP_EMSK_LEN;
 
 	return key;
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
index c60539f..68f0af9 100644
--- a/src/eap_server/eap_server_pwd.c
+++ b/src/eap_server/eap_server_pwd.c
@@ -1035,11 +1035,10 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_MSK_LEN);
+	key = os_memdup(data->msk, EAP_MSK_LEN);
 	if (key == NULL)
 		return NULL;
 
-	os_memcpy(key, data->msk, EAP_MSK_LEN);
 	*len = EAP_MSK_LEN;
 
 	return key;
@@ -1054,11 +1053,10 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
 
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 	*len = EAP_EMSK_LEN;
 
 	return key;
@@ -1087,11 +1085,10 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	id = os_malloc(1 + SHA256_MAC_LEN);
+	id = os_memdup(data->session_id, 1 + SHA256_MAC_LEN);
 	if (id == NULL)
 		return NULL;
 
-	os_memcpy(id, data->session_id, 1 + SHA256_MAC_LEN);
 	*len = 1 + SHA256_MAC_LEN;
 
 	return id;
diff --git a/src/eap_server/eap_server_sake.c b/src/eap_server/eap_server_sake.c
index 84d0e0b..66183f5 100644
--- a/src/eap_server/eap_server_sake.c
+++ b/src/eap_server/eap_server_sake.c
@@ -326,10 +326,9 @@
 	data->peerid = NULL;
 	data->peerid_len = 0;
 	if (attr.peerid) {
-		data->peerid = os_malloc(attr.peerid_len);
+		data->peerid = os_memdup(attr.peerid, attr.peerid_len);
 		if (data->peerid == NULL)
 			return;
-		os_memcpy(data->peerid, attr.peerid, attr.peerid_len);
 		data->peerid_len = attr.peerid_len;
 	}
 
@@ -460,10 +459,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_MSK_LEN);
+	key = os_memdup(data->msk, EAP_MSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->msk, EAP_MSK_LEN);
 	*len = EAP_MSK_LEN;
 
 	return key;
@@ -478,10 +476,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 	*len = EAP_EMSK_LEN;
 
 	return key;
diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c
index 3a6ed79..10637d4 100644
--- a/src/eap_server/eap_server_sim.c
+++ b/src/eap_server/eap_server_sim.c
@@ -787,10 +787,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+	key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
 	*len = EAP_SIM_KEYING_DATA_LEN;
 	return key;
 }
@@ -804,10 +803,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 	*len = EAP_EMSK_LEN;
 	return key;
 }
diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c
index 6909695..3c9027b 100644
--- a/src/eap_server/eap_server_tls_common.c
+++ b/src/eap_server/eap_server_tls_common.c
@@ -47,7 +47,7 @@
 			    int verify_peer, int eap_type)
 {
 	u8 session_ctx[8];
-	unsigned int flags = 0;
+	unsigned int flags = sm->tls_flags;
 
 	if (sm->ssl_ctx == NULL) {
 		wpa_printf(MSG_ERROR, "TLS context not initialized - cannot use TLS-based EAP method");
diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c
index a53633f..b14996b 100644
--- a/src/eap_server/eap_server_ttls.c
+++ b/src/eap_server/eap_server_ttls.c
@@ -228,14 +228,13 @@
 		if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) {
 			wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message");
 			if (parse->eap == NULL) {
-				parse->eap = os_malloc(dlen);
+				parse->eap = os_memdup(dpos, dlen);
 				if (parse->eap == NULL) {
 					wpa_printf(MSG_WARNING, "EAP-TTLS: "
 						   "failed to allocate memory "
 						   "for Phase 2 EAP data");
 					goto fail;
 				}
-				os_memcpy(parse->eap, dpos, dlen);
 				parse->eap_len = dlen;
 			} else {
 				u8 *neweap = os_realloc(parse->eap,
@@ -372,7 +371,7 @@
 
 static struct wpabuf * eap_ttls_build_start(struct eap_sm *sm,
 					    struct eap_ttls_data *data, u8 id)
-{	
+{
 	struct wpabuf *req;
 
 	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, 1,
@@ -666,11 +665,14 @@
 	}
 	os_free(chal);
 
-	if (sm->user->password_hash)
-		challenge_response(challenge, sm->user->password, nt_response);
-	else
-		nt_challenge_response(challenge, sm->user->password,
-				      sm->user->password_len, nt_response);
+	if ((sm->user->password_hash &&
+	     challenge_response(challenge, sm->user->password, nt_response)) ||
+	    (!sm->user->password_hash &&
+	     nt_challenge_response(challenge, sm->user->password,
+				   sm->user->password_len, nt_response))) {
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
 
 	if (os_memcmp_const(nt_response, response + 2 + 24, 24) == 0) {
 		wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response");
@@ -1051,12 +1053,11 @@
 		}
 
 		os_free(sm->identity);
-		sm->identity = os_malloc(parse.user_name_len);
+		sm->identity = os_memdup(parse.user_name, parse.user_name_len);
 		if (sm->identity == NULL) {
 			eap_ttls_state(data, FAILURE);
 			goto done;
 		}
-		os_memcpy(sm->identity, parse.user_name, parse.user_name_len);
 		sm->identity_len = parse.user_name_len;
 		if (eap_user_get(sm, parse.user_name, parse.user_name_len, 1)
 		    != 0) {
diff --git a/src/eap_server/ikev2.c b/src/eap_server/ikev2.c
index 5385cd8..0e9210e 100644
--- a/src/eap_server/ikev2.c
+++ b/src/eap_server/ikev2.c
@@ -544,10 +544,9 @@
 		}
 		os_free(data->IDr);
 	}
-	data->IDr = os_malloc(idr_len);
+	data->IDr = os_memdup(idr, idr_len);
 	if (data->IDr == NULL)
 		return -1;
-	os_memcpy(data->IDr, idr, idr_len);
 	data->IDr_len = idr_len;
 	data->IDr_type = id_type;
 
@@ -1147,10 +1146,9 @@
 			return NULL;
 	} else {
 		os_free(data->shared_secret);
-		data->shared_secret = os_malloc(secret_len);
+		data->shared_secret = os_memdup(secret, secret_len);
 		if (data->shared_secret == NULL)
 			return NULL;
-		os_memcpy(data->shared_secret, secret, secret_len);
 		data->shared_secret_len = secret_len;
 	}
 
diff --git a/src/eap_server/tncs.c b/src/eap_server/tncs.c
index cfcbd3e..942a195 100644
--- a/src/eap_server/tncs.c
+++ b/src/eap_server/tncs.c
@@ -161,12 +161,10 @@
 	if (imv == NULL)
 		return TNC_RESULT_INVALID_PARAMETER;
 	os_free(imv->supported_types);
-	imv->supported_types =
-		os_malloc(typeCount * sizeof(TNC_MessageType));
+	imv->supported_types = os_memdup(supportedTypes,
+					 typeCount * sizeof(TNC_MessageType));
 	if (imv->supported_types == NULL)
 		return TNC_RESULT_FATAL;
-	os_memcpy(imv->supported_types, supportedTypes,
-		  typeCount * sizeof(TNC_MessageType));
 	imv->num_supported_types = typeCount;
 
 	return TNC_RESULT_SUCCESS;
diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c
index ff673bb..36074d3 100644
--- a/src/eapol_auth/eapol_auth_sm.c
+++ b/src/eapol_auth/eapol_auth_sm.c
@@ -848,6 +848,7 @@
 	eap_conf.server_id_len = eapol->conf.server_id_len;
 	eap_conf.erp = eapol->conf.erp;
 	eap_conf.tls_session_lifetime = eapol->conf.tls_session_lifetime;
+	eap_conf.tls_flags = eapol->conf.tls_flags;
 	sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf);
 	if (sm->eap == NULL) {
 		eapol_auth_free(sm);
@@ -1197,30 +1198,27 @@
 	dst->server_id = src->server_id;
 	dst->server_id_len = src->server_id_len;
 	if (src->eap_req_id_text) {
-		dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len);
+		dst->eap_req_id_text = os_memdup(src->eap_req_id_text,
+						 src->eap_req_id_text_len);
 		if (dst->eap_req_id_text == NULL)
 			return -1;
-		os_memcpy(dst->eap_req_id_text, src->eap_req_id_text,
-			  src->eap_req_id_text_len);
 		dst->eap_req_id_text_len = src->eap_req_id_text_len;
 	} else {
 		dst->eap_req_id_text = NULL;
 		dst->eap_req_id_text_len = 0;
 	}
 	if (src->pac_opaque_encr_key) {
-		dst->pac_opaque_encr_key = os_malloc(16);
+		dst->pac_opaque_encr_key = os_memdup(src->pac_opaque_encr_key,
+						     16);
 		if (dst->pac_opaque_encr_key == NULL)
 			goto fail;
-		os_memcpy(dst->pac_opaque_encr_key, src->pac_opaque_encr_key,
-			  16);
 	} else
 		dst->pac_opaque_encr_key = NULL;
 	if (src->eap_fast_a_id) {
-		dst->eap_fast_a_id = os_malloc(src->eap_fast_a_id_len);
+		dst->eap_fast_a_id = os_memdup(src->eap_fast_a_id,
+					       src->eap_fast_a_id_len);
 		if (dst->eap_fast_a_id == NULL)
 			goto fail;
-		os_memcpy(dst->eap_fast_a_id, src->eap_fast_a_id,
-			  src->eap_fast_a_id_len);
 		dst->eap_fast_a_id_len = src->eap_fast_a_id_len;
 	} else
 		dst->eap_fast_a_id = NULL;
@@ -1249,6 +1247,7 @@
 	dst->erp_send_reauth_start = src->erp_send_reauth_start;
 	dst->erp = src->erp;
 	dst->tls_session_lifetime = src->tls_session_lifetime;
+	dst->tls_flags = src->tls_flags;
 
 	return 0;
 
diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h
index e1974e4..44f3f31 100644
--- a/src/eapol_auth/eapol_auth_sm.h
+++ b/src/eapol_auth/eapol_auth_sm.h
@@ -28,6 +28,7 @@
 	char *erp_domain; /* a copy of this will be allocated */
 	int erp; /* Whether ERP is enabled on authentication server */
 	unsigned int tls_session_lifetime;
+	unsigned int tls_flags;
 	u8 *pac_opaque_encr_key;
 	u8 *eap_fast_a_id;
 	size_t eap_fast_a_id_len;
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index e727005..8e4f0e4 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -16,6 +16,7 @@
 #include "crypto/md5.h"
 #include "common/eapol_common.h"
 #include "eap_peer/eap.h"
+#include "eap_peer/eap_config.h"
 #include "eap_peer/eap_proxy.h"
 #include "eapol_supp_sm.h"
 
@@ -492,9 +493,20 @@
 #ifdef CONFIG_EAP_PROXY
 	if (sm->use_eap_proxy) {
 		if (eap_proxy_key_available(sm->eap_proxy)) {
+			u8 *session_id, *emsk;
+			size_t session_id_len, emsk_len;
+
 			/* New key received - clear IEEE 802.1X EAPOL-Key replay
 			 * counter */
 			sm->replay_counter_valid = FALSE;
+
+			session_id = eap_proxy_get_eap_session_id(
+				sm->eap_proxy, &session_id_len);
+			emsk = eap_proxy_get_emsk(sm->eap_proxy, &emsk_len);
+			if (sm->config->erp && session_id && emsk)
+				eap_peer_erp_init(sm->eap, session_id,
+						  session_id_len, emsk,
+						  emsk_len);
 		}
 		return;
 	}
@@ -899,6 +911,9 @@
 	wpabuf_free(sm->eapReqData);
 	sm->eapReqData = NULL;
 	eap_sm_abort(sm->eap);
+#ifdef CONFIG_EAP_PROXY
+	eap_proxy_sm_abort(sm->eap_proxy);
+#endif /* CONFIG_EAP_PROXY */
 }
 
 
@@ -2050,6 +2065,7 @@
 #ifdef CONFIG_EAP_PROXY
 	eapol_sm_eap_proxy_cb,
 	eapol_sm_eap_proxy_notify_sim_status,
+	eapol_sm_get_eap_proxy_imsi,
 #endif /* CONFIG_EAP_PROXY */
 	eapol_sm_set_anon_id
 };
@@ -2158,11 +2174,13 @@
 
 
 #ifdef CONFIG_EAP_PROXY
-int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len)
+int eapol_sm_get_eap_proxy_imsi(void *ctx, int sim_num, char *imsi, size_t *len)
 {
+	struct eapol_sm *sm = ctx;
+
 	if (sm->eap_proxy == NULL)
 		return -1;
-	return eap_proxy_get_imsi(sm->eap_proxy, imsi, len);
+	return eap_proxy_get_imsi(sm->eap_proxy, sim_num, imsi, len);
 }
 #endif /* CONFIG_EAP_PROXY */
 
@@ -2195,3 +2213,33 @@
 	eap_peer_finish(sm->eap, (const struct eap_hdr *) buf, len);
 #endif /* CONFIG_ERP */
 }
+
+
+int eapol_sm_update_erp_next_seq_num(struct eapol_sm *sm, u16 next_seq_num)
+{
+#ifdef CONFIG_ERP
+	if (!sm)
+		return -1;
+	return eap_peer_update_erp_next_seq_num(sm->eap, next_seq_num);
+#else /* CONFIG_ERP */
+	return -1;
+#endif /* CONFIG_ERP */
+}
+
+
+int eapol_sm_get_erp_info(struct eapol_sm *sm, struct eap_peer_config *config,
+			  const u8 **username, size_t *username_len,
+			  const u8 **realm, size_t *realm_len,
+			  u16 *erp_next_seq_num, const u8 **rrk,
+			  size_t *rrk_len)
+{
+#ifdef CONFIG_ERP
+	if (!sm)
+		return -1;
+	return eap_peer_get_erp_info(sm->eap, config, username, username_len,
+				     realm, realm_len, erp_next_seq_num, rrk,
+				     rrk_len);
+#else /* CONFIG_ERP */
+	return -1;
+#endif /* CONFIG_ERP */
+}
diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h
index cb06e9a..a25c799 100644
--- a/src/eapol_supp/eapol_supp_sm.h
+++ b/src/eapol_supp/eapol_supp_sm.h
@@ -339,7 +339,15 @@
 struct wpabuf * eapol_sm_build_erp_reauth_start(struct eapol_sm *sm);
 void eapol_sm_process_erp_finish(struct eapol_sm *sm, const u8 *buf,
 				 size_t len);
-int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len);
+int eapol_sm_get_eap_proxy_imsi(void *ctx, int sim_num, char *imsi,
+				size_t *len);
+int eapol_sm_update_erp_next_seq_num(struct eapol_sm *sm, u16 next_seq_num);
+int eapol_sm_get_erp_info(struct eapol_sm *sm, struct eap_peer_config *config,
+			  const u8 **username, size_t *username_len,
+			  const u8 **realm, size_t *realm_len,
+			  u16 *erp_next_seq_num, const u8 **rrk,
+			  size_t *rrk_len);
+
 #else /* IEEE8021X_EAPOL */
 static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
 {
@@ -458,6 +466,19 @@
 					       const u8 *buf, size_t len)
 {
 }
+static inline int eapol_sm_update_erp_next_seq_num(struct eapol_sm *sm,
+						   u16 next_seq_num)
+{
+	return -1;
+}
+static inline int
+eapol_sm_get_erp_info(struct eapol_sm *sm, struct eap_peer_config *config,
+		      const u8 **username, size_t *username_len,
+		      const u8 **realm, size_t *realm_len,
+		      u16 *erp_next_seq_num, const u8 **rrk, size_t *rrk_len)
+{
+	return -1;
+}
 #endif /* IEEE8021X_EAPOL */
 
 #endif /* EAPOL_SUPP_SM_H */
diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c
index a7a300e..65b4906 100644
--- a/src/l2_packet/l2_packet_linux.c
+++ b/src/l2_packet/l2_packet_linux.c
@@ -96,6 +96,9 @@
 		   const u8 *buf, size_t len)
 {
 	int ret;
+
+	if (TEST_FAIL())
+		return -1;
 	if (l2 == NULL)
 		return -1;
 	if (l2->l2_hdr) {
@@ -458,6 +461,9 @@
 {
 	const struct sock_fprog *sock_filter;
 
+	if (TEST_FAIL())
+		return -1;
+
 	switch (type) {
 	case L2_PACKET_FILTER_DHCP:
 		sock_filter = &dhcp_sock_filter;
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index 14d6279..1a31ef2 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -1010,8 +1010,16 @@
 	}
 	p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
 
-	if (p2p->find_type == P2P_FIND_PROGRESSIVE &&
-	    (freq = p2p_get_next_prog_freq(p2p)) > 0) {
+	if (p2p->find_pending_full &&
+	    (p2p->find_type == P2P_FIND_PROGRESSIVE ||
+	     p2p->find_type == P2P_FIND_START_WITH_FULL)) {
+		type = P2P_SCAN_FULL;
+		p2p_dbg(p2p, "Starting search (pending full scan)");
+		p2p->find_pending_full = 0;
+	} else if ((p2p->find_type == P2P_FIND_PROGRESSIVE &&
+	    (freq = p2p_get_next_prog_freq(p2p)) > 0) ||
+	    (p2p->find_type == P2P_FIND_START_WITH_FULL &&
+	     (freq = p2p->find_specified_freq) > 0)) {
 		type = P2P_SCAN_SOCIAL_PLUS_ONE;
 		p2p_dbg(p2p, "Starting search (+ freq %u)", freq);
 	} else {
@@ -1173,12 +1181,11 @@
 
 	p2p_free_req_dev_types(p2p);
 	if (req_dev_types && num_req_dev_types) {
-		p2p->req_dev_types = os_malloc(num_req_dev_types *
+		p2p->req_dev_types = os_memdup(req_dev_types,
+					       num_req_dev_types *
 					       WPS_DEV_TYPE_LEN);
 		if (p2p->req_dev_types == NULL)
 			return -1;
-		os_memcpy(p2p->req_dev_types, req_dev_types,
-			  num_req_dev_types * WPS_DEV_TYPE_LEN);
 		p2p->num_req_dev_types = num_req_dev_types;
 	}
 
@@ -1235,7 +1242,12 @@
 		p2p->pending_listen_freq = 0;
 	}
 	p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+	p2p->find_pending_full = 0;
 	p2p->find_type = type;
+	if (freq != 2412 && freq != 2437 && freq != 2462 && freq != 60480)
+		p2p->find_specified_freq = freq;
+	else
+		p2p->find_specified_freq = 0;
 	p2p_device_clear_reported(p2p);
 	os_memset(p2p->sd_query_no_ack, 0, ETH_ALEN);
 	p2p_set_state(p2p, P2P_SEARCH);
@@ -1251,7 +1263,7 @@
 		if (freq > 0) {
 			/*
 			 * Start with the specified channel and then move to
-			 * social channels only scans.
+			 * scans for social channels and this specific channel.
 			 */
 			res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx,
 						 P2P_SCAN_SPECIFIC, freq,
@@ -1280,6 +1292,9 @@
 	if (res != 0 && p2p->p2p_scan_running) {
 		p2p_dbg(p2p, "Failed to start p2p_scan - another p2p_scan was already running");
 		/* wait for the previous p2p_scan to complete */
+		if (type == P2P_FIND_PROGRESSIVE ||
+		    (type == P2P_FIND_START_WITH_FULL && freq == 0))
+			p2p->find_pending_full = 1;
 		res = 0; /* do not report failure */
 	} else if (res != 0) {
 		p2p_dbg(p2p, "Failed to start p2p_scan");
@@ -2993,6 +3008,7 @@
 	wpabuf_free(p2p->wfd_dev_info);
 	wpabuf_free(p2p->wfd_assoc_bssid);
 	wpabuf_free(p2p->wfd_coupled_sink_info);
+	wpabuf_free(p2p->wfd_r2_dev_info);
 #endif /* CONFIG_WIFI_DISPLAY */
 
 	eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
@@ -4817,11 +4833,10 @@
 	struct p2p_channel *n;
 
 	if (pref_chan) {
-		n = os_malloc(num_pref_chan * sizeof(struct p2p_channel));
+		n = os_memdup(pref_chan,
+			      num_pref_chan * sizeof(struct p2p_channel));
 		if (n == NULL)
 			return -1;
-		os_memcpy(n, pref_chan,
-			  num_pref_chan * sizeof(struct p2p_channel));
 	} else
 		n = NULL;
 
@@ -5143,6 +5158,20 @@
 }
 
 
+int p2p_set_wfd_r2_dev_info(struct p2p_data *p2p, const struct wpabuf *elem)
+{
+	wpabuf_free(p2p->wfd_r2_dev_info);
+	if (elem) {
+		p2p->wfd_r2_dev_info = wpabuf_dup(elem);
+		if (p2p->wfd_r2_dev_info == NULL)
+			return -1;
+	} else
+		p2p->wfd_r2_dev_info = NULL;
+
+	return 0;
+}
+
+
 int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem)
 {
 	wpabuf_free(p2p->wfd_assoc_bssid);
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 70d3a90..fac5ce0 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -2266,6 +2266,7 @@
 int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie);
 int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie);
 int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem);
+int p2p_set_wfd_r2_dev_info(struct p2p_data *p2p, const struct wpabuf *elem);
 int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem);
 int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p,
 				  const struct wpabuf *elem);
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index 051b4e3..16c28a0 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -367,6 +367,8 @@
 		return NULL;
 	if (group->p2p->wfd_dev_info)
 		wpabuf_put_buf(wfd_subelems, group->p2p->wfd_dev_info);
+	if (group->p2p->wfd_r2_dev_info)
+		wpabuf_put_buf(wfd_subelems, group->p2p->wfd_r2_dev_info);
 	if (group->p2p->wfd_assoc_bssid)
 		wpabuf_put_buf(wfd_subelems,
 			       group->p2p->wfd_assoc_bssid);
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index ce69932..6a4d751 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -437,9 +437,11 @@
 	int inv_persistent;
 
 	enum p2p_discovery_type find_type;
+	int find_specified_freq;
 	unsigned int last_p2p_find_timeout;
 	u8 last_prog_scan_class;
 	u8 last_prog_scan_chan;
+	unsigned int find_pending_full:1;
 	int p2p_scan_running;
 	enum p2p_after_scan {
 		P2P_AFTER_SCAN_NOTHING,
@@ -545,6 +547,7 @@
 	struct wpabuf *wfd_dev_info;
 	struct wpabuf *wfd_assoc_bssid;
 	struct wpabuf *wfd_coupled_sink_info;
+	struct wpabuf *wfd_r2_dev_info;
 #endif /* CONFIG_WIFI_DISPLAY */
 
 	u16 authorized_oob_dev_pw_id;
diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c
index d2fb4b5..b9e753f 100644
--- a/src/p2p/p2p_sd.c
+++ b/src/p2p/p2p_sd.c
@@ -625,6 +625,7 @@
 	u8 dialog_token;
 	size_t frag_len, max_len;
 	int more = 0;
+	unsigned int wait_time = 200;
 
 	wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Request", data, len);
 	if (len < 1)
@@ -677,12 +678,13 @@
 		p2p_dbg(p2p, "All fragments of SD response sent");
 		wpabuf_free(p2p->sd_resp);
 		p2p->sd_resp = NULL;
+		wait_time = 0; /* no more SD frames in the sequence */
 	}
 
 	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
 	if (p2p_send_action(p2p, rx_freq, sa, p2p->cfg->dev_addr,
 			    p2p->cfg->dev_addr,
-			    wpabuf_head(resp), wpabuf_len(resp), 200) < 0)
+			    wpabuf_head(resp), wpabuf_len(resp), wait_time) < 0)
 		p2p_dbg(p2p, "Failed to send Action frame");
 
 	wpabuf_free(resp);
diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c
index 3f9e53d..cad0292 100644
--- a/src/pae/ieee802_1x_kay.c
+++ b/src/pae/ieee802_1x_kay.c
@@ -532,6 +532,7 @@
 		ieee802_1x_delete_receive_sa(participant->kay, psa);
 
 	dl_list_del(&psc->list);
+	secy_delete_receive_sc(participant->kay, psc);
 	os_free(psc);
 }
 
@@ -632,6 +633,8 @@
 	struct receive_sc *rxsc;
 
 	peer = ieee802_1x_kay_get_potential_peer(participant, mi);
+	if (!peer)
+		return NULL;
 
 	rxsc = ieee802_1x_kay_init_receive_sc(&participant->current_peer_sci);
 	if (!rxsc)
@@ -961,21 +964,19 @@
 		body_len = get_mka_param_body_len(hdr);
 		body_type = get_mka_param_body_type(hdr);
 
-		if (body_type != MKA_LIVE_PEER_LIST &&
-		    body_type != MKA_POTENTIAL_PEER_LIST)
-			continue;
-
-		ieee802_1x_mka_dump_peer_body(
-			(struct ieee802_1x_mka_peer_body *)pos);
-
-		if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) {
+		if (left_len < (MKA_HDR_LEN + MKA_ALIGN_LENGTH(body_len) + DEFAULT_ICV_LEN)) {
 			wpa_printf(MSG_ERROR,
 				   "KaY: MKA Peer Packet Body Length (%zu bytes) is less than the Parameter Set Header Length (%zu bytes) + the Parameter Set Body Length (%zu bytes) + %d bytes of ICV",
 				   left_len, MKA_HDR_LEN,
-				   body_len, DEFAULT_ICV_LEN);
-			continue;
+				   MKA_ALIGN_LENGTH(body_len),
+				   DEFAULT_ICV_LEN);
+			return FALSE;
 		}
 
+		if (body_type != MKA_LIVE_PEER_LIST &&
+		    body_type != MKA_POTENTIAL_PEER_LIST)
+			continue;
+
 		if ((body_len % 16) != 0) {
 			wpa_printf(MSG_ERROR,
 				   "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets",
@@ -983,6 +984,9 @@
 			continue;
 		}
 
+		ieee802_1x_mka_dump_peer_body(
+			(struct ieee802_1x_mka_peer_body *)pos);
+
 		for (i = 0; i < body_len;
 		     i += sizeof(struct ieee802_1x_mka_peer_id)) {
 			const struct ieee802_1x_mka_peer_id *peer_mi;
@@ -2363,7 +2367,6 @@
 				if (sci_equal(&rxsc->sci, &peer->sci)) {
 					ieee802_1x_kay_deinit_receive_sc(
 						participant, rxsc);
-					secy_delete_receive_sc(kay, rxsc);
 				}
 			}
 			dl_list_del(&peer->list);
@@ -2546,6 +2549,7 @@
 	dl_list_for_each_safe(psa, tmp, &psc->sa_list, struct transmit_sa, list)
 		ieee802_1x_delete_transmit_sa(participant->kay, psa);
 
+	secy_delete_transmit_sc(participant->kay, psc);
 	os_free(psc);
 }
 
@@ -3015,7 +3019,7 @@
 				   "KaY: MKA Peer Packet Body Length (%zu bytes) is less than the Parameter Set Header Length (%zu bytes) + the Parameter Set Body Length (%zu bytes) + %d bytes of ICV",
 				   left_len, MKA_HDR_LEN,
 				   body_len, DEFAULT_ICV_LEN);
-			continue;
+			return -1;
 		}
 
 		if (handled[body_type])
@@ -3097,6 +3101,7 @@
 	kay = os_zalloc(sizeof(*kay));
 	if (!kay) {
 		wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
+		os_free(ctx);
 		return NULL;
 	}
 
@@ -3131,10 +3136,8 @@
 	dl_list_init(&kay->participant_list);
 
 	if (policy != DO_NOT_SECURE &&
-	    secy_get_capability(kay, &kay->macsec_capable) < 0) {
-		os_free(kay);
-		return NULL;
-	}
+	    secy_get_capability(kay, &kay->macsec_capable) < 0)
+		goto error;
 
 	if (policy == DO_NOT_SECURE ||
 	    kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) {
@@ -3161,16 +3164,17 @@
 	wpa_printf(MSG_DEBUG, "KaY: state machine created");
 
 	/* Initialize the SecY must be prio to CP, as CP will control SecY */
-	secy_init_macsec(kay);
+	if (secy_init_macsec(kay) < 0) {
+		wpa_printf(MSG_DEBUG, "KaY: Could not initialize MACsec");
+		goto error;
+	}
 
 	wpa_printf(MSG_DEBUG, "KaY: secy init macsec done");
 
 	/* init CP */
 	kay->cp = ieee802_1x_cp_sm_init(kay);
-	if (kay->cp == NULL) {
-		ieee802_1x_kay_deinit(kay);
-		return NULL;
-	}
+	if (kay->cp == NULL)
+		goto error;
 
 	if (policy == DO_NOT_SECURE) {
 		ieee802_1x_cp_connect_authenticated(kay->cp);
@@ -3181,12 +3185,15 @@
 		if (kay->l2_mka == NULL) {
 			wpa_printf(MSG_WARNING,
 				   "KaY: Failed to initialize L2 packet processing for MKA packet");
-			ieee802_1x_kay_deinit(kay);
-			return NULL;
+			goto error;
 		}
 	}
 
 	return kay;
+
+error:
+	ieee802_1x_kay_deinit(kay);
+	return NULL;
 }
 
 
@@ -3433,10 +3440,8 @@
 		rxsc = dl_list_entry(participant->rxsc_list.next,
 				     struct receive_sc, list);
 		ieee802_1x_kay_deinit_receive_sc(participant, rxsc);
-		secy_delete_receive_sc(kay, rxsc);
 	}
 	ieee802_1x_kay_deinit_transmit_sc(participant, participant->txsc);
-	secy_delete_transmit_sc(kay, participant->txsc);
 
 	os_memset(&participant->cak, 0, sizeof(participant->cak));
 	os_memset(&participant->kek, 0, sizeof(participant->kek));
diff --git a/src/radius/radius.c b/src/radius/radius.c
index 9ddc19b..fc98ad6 100644
--- a/src/radius/radius.c
+++ b/src/radius/radius.c
@@ -631,6 +631,9 @@
 	size_t buf_needed;
 	struct radius_attr_hdr *attr;
 
+	if (TEST_FAIL())
+		return NULL;
+
 	if (data_len > RADIUS_MAX_ATTR_LEN) {
 		wpa_printf(MSG_ERROR, "radius_msg_add_attr: too long attribute (%lu bytes)",
 		       (unsigned long) data_len);
@@ -960,10 +963,9 @@
 			}
 
 			len = vhdr->vendor_length - sizeof(*vhdr);
-			data = os_malloc(len);
+			data = os_memdup(pos + sizeof(*vhdr), len);
 			if (data == NULL)
 				return NULL;
-			os_memcpy(data, pos + sizeof(*vhdr), len);
 			if (alen)
 				*alen = len;
 			return data;
@@ -1040,12 +1042,11 @@
 		return NULL;
 	}
 
-	res = os_malloc(plain[0]);
+	res = os_memdup(plain + 1, plain[0]);
 	if (res == NULL) {
 		os_free(plain);
 		return NULL;
 	}
-	os_memcpy(res, plain + 1, plain[0]);
 	if (reslen)
 		*reslen = plain[0];
 	os_free(plain);
@@ -1594,10 +1595,9 @@
 		goto out;
 
 	/* alloc writable memory for decryption */
-	buf = os_malloc(fdlen);
+	buf = os_memdup(fdata, fdlen);
 	if (buf == NULL)
 		goto out;
-	os_memcpy(buf, fdata, fdlen);
 	buflen = fdlen;
 
 	/* init pointers */
@@ -1684,12 +1684,11 @@
 	dst->count = 0;
 
 	for (i = 0; i < src->count; i++) {
-		dst->attr[i].data = os_malloc(src->attr[i].len);
+		dst->attr[i].data = os_memdup(src->attr[i].data,
+					      src->attr[i].len);
 		if (dst->attr[i].data == NULL)
 			break;
 		dst->count++;
-		os_memcpy(dst->attr[i].data, src->attr[i].data,
-			  src->attr[i].len);
 		dst->attr[i].len = src->attr[i].len;
 	}
 
diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c
index 8a3d7e0..ed24c19 100644
--- a/src/radius/radius_das.c
+++ b/src/radius/radius_das.c
@@ -373,13 +373,12 @@
 	os_memcpy(&das->client_addr, conf->client_addr,
 		  sizeof(das->client_addr));
 
-	das->shared_secret = os_malloc(conf->shared_secret_len);
+	das->shared_secret = os_memdup(conf->shared_secret,
+				       conf->shared_secret_len);
 	if (das->shared_secret == NULL) {
 		radius_das_deinit(das);
 		return NULL;
 	}
-	os_memcpy(das->shared_secret, conf->shared_secret,
-		  conf->shared_secret_len);
 	das->shared_secret_len = conf->shared_secret_len;
 
 	das->sock = radius_das_open_socket(conf->port);
diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
index e8bef45..c76bb22 100644
--- a/src/radius/radius_server.c
+++ b/src/radius/radius_server.c
@@ -272,6 +272,8 @@
 
 	unsigned int tls_session_lifetime;
 
+	unsigned int tls_flags;
+
 	/**
 	 * wps - Wi-Fi Protected Setup context
 	 *
@@ -662,14 +664,14 @@
 
 	sess->username = os_malloc(user_len * 4 + 1);
 	if (sess->username == NULL) {
-		radius_server_session_free(data, sess);
+		radius_server_session_remove(data, sess);
 		return NULL;
 	}
 	printf_encode(sess->username, user_len * 4 + 1, user, user_len);
 
 	sess->nas_ip = os_strdup(from_addr);
 	if (sess->nas_ip == NULL) {
-		radius_server_session_free(data, sess);
+		radius_server_session_remove(data, sess);
 		return NULL;
 	}
 
@@ -696,13 +698,14 @@
 	eap_conf.server_id_len = os_strlen(data->server_id);
 	eap_conf.erp = data->erp;
 	eap_conf.tls_session_lifetime = data->tls_session_lifetime;
+	eap_conf.tls_flags = data->tls_flags;
 	radius_server_testing_options(sess, &eap_conf);
 	sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb,
 				       &eap_conf);
 	if (sess->eap == NULL) {
 		RADIUS_DEBUG("Failed to initialize EAP state machine for the "
 			     "new session");
-		radius_server_session_free(data, sess);
+		radius_server_session_remove(data, sess);
 		return NULL;
 	}
 	sess->eap_if = eap_get_interface(sess->eap);
@@ -1754,6 +1757,7 @@
 	data->erp = conf->erp;
 	data->erp_domain = conf->erp_domain;
 	data->tls_session_lifetime = conf->tls_session_lifetime;
+	data->tls_flags = conf->tls_flags;
 
 	if (conf->subscr_remediation_url) {
 		data->subscr_remediation_url =
diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h
index 7a25802..996f00e 100644
--- a/src/radius/radius_server.h
+++ b/src/radius/radius_server.h
@@ -172,6 +172,8 @@
 
 	unsigned int tls_session_lifetime;
 
+	unsigned int tls_flags;
+
 	/**
 	 * wps - Wi-Fi Protected Setup context
 	 *
diff --git a/src/rsn_supp/Makefile b/src/rsn_supp/Makefile
index d5e61fe..c2d81f2 100644
--- a/src/rsn_supp/Makefile
+++ b/src/rsn_supp/Makefile
@@ -10,7 +10,6 @@
 
 CFLAGS += -DCONFIG_IEEE80211W
 CFLAGS += -DCONFIG_IEEE80211R
-CFLAGS += -DCONFIG_PEERKEY
 CFLAGS += -DCONFIG_TDLS
 CFLAGS += -DCONFIG_WNM
 CFLAGS += -DIEEE8021X_EAPOL
@@ -18,7 +17,6 @@
 LIB_OBJS= \
 	pmksa_cache.o \
 	wpa_ft.o \
-	peerkey.o \
 	tdls.o \
 	preauth.o \
 	wpa.o \
diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c
deleted file mode 100644
index ce338f8..0000000
--- a/src/rsn_supp/peerkey.c
+++ /dev/null
@@ -1,1166 +0,0 @@
-/*
- * WPA Supplicant - PeerKey for Direct Link Setup (DLS)
- * Copyright (c) 2006-2015, 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"
-
-#ifdef CONFIG_PEERKEY
-
-#include "common.h"
-#include "eloop.h"
-#include "crypto/sha1.h"
-#include "crypto/sha256.h"
-#include "crypto/random.h"
-#include "common/ieee802_11_defs.h"
-#include "wpa.h"
-#include "wpa_i.h"
-#include "wpa_ie.h"
-#include "peerkey.h"
-
-
-static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len)
-{
-	os_memcpy(pos, ie, ie_len);
-	return pos + ie_len;
-}
-
-
-static u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len)
-{
-	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
-	*pos++ = RSN_SELECTOR_LEN + data_len;
-	RSN_SELECTOR_PUT(pos, kde);
-	pos += RSN_SELECTOR_LEN;
-	os_memcpy(pos, data, data_len);
-	pos += data_len;
-	return pos;
-}
-
-
-static void wpa_supplicant_smk_timeout(void *eloop_ctx, void *timeout_ctx)
-{
-#if 0
-	struct wpa_sm *sm = eloop_ctx;
-	struct wpa_peerkey *peerkey = timeout_ctx;
-#endif
-	/* TODO: time out SMK and any STK that was generated using this SMK */
-}
-
-
-static void wpa_supplicant_peerkey_free(struct wpa_sm *sm,
-					struct wpa_peerkey *peerkey)
-{
-	eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey);
-	os_free(peerkey);
-}
-
-
-static int wpa_supplicant_send_smk_error(struct wpa_sm *sm, const u8 *dst,
-					 const u8 *peer,
-					 u16 mui, u16 error_type, int ver)
-{
-	size_t rlen;
-	struct wpa_eapol_key *err;
-	struct rsn_error_kde error;
-	u8 *rbuf, *pos, *mic;
-	size_t kde_len, mic_len = 16;
-	u16 key_info;
-
-	kde_len = 2 + RSN_SELECTOR_LEN + sizeof(error);
-	if (peer)
-		kde_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN;
-
-	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
-				  NULL, sizeof(*err) + mic_len + 2 + kde_len,
-				  &rlen, (void *) &err);
-	if (rbuf == NULL)
-		return -1;
-	mic = (u8 *) (err + 1);
-
-	err->type = EAPOL_KEY_TYPE_RSN;
-	key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
-		WPA_KEY_INFO_SECURE | WPA_KEY_INFO_ERROR |
-		WPA_KEY_INFO_REQUEST;
-	WPA_PUT_BE16(err->key_info, key_info);
-	WPA_PUT_BE16(err->key_length, 0);
-	os_memcpy(err->replay_counter, sm->request_counter,
-		  WPA_REPLAY_COUNTER_LEN);
-	inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
-
-	WPA_PUT_BE16(mic + mic_len, (u16) kde_len);
-	pos = mic + mic_len + 2;
-
-	if (peer) {
-		/* Peer MAC Address KDE */
-		pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN);
-	}
-
-	/* Error KDE */
-	error.mui = host_to_be16(mui);
-	error.error_type = host_to_be16(error_type);
-	wpa_add_kde(pos, RSN_KEY_DATA_ERROR, (u8 *) &error, sizeof(error));
-
-	if (peer) {
-		wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error (peer "
-			   MACSTR " mui %d error_type %d)",
-			   MAC2STR(peer), mui, error_type);
-	} else {
-		wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error "
-			   "(mui %d error_type %d)", mui, error_type);
-	}
-
-	wpa_eapol_key_send(sm, &sm->ptk, ver, dst, ETH_P_EAPOL, rbuf, rlen,
-			   mic);
-
-	return 0;
-}
-
-
-static int wpa_supplicant_send_smk_m3(struct wpa_sm *sm,
-				      const unsigned char *src_addr,
-				      const struct wpa_eapol_key *key,
-				      int ver, struct wpa_peerkey *peerkey)
-{
-	size_t rlen;
-	struct wpa_eapol_key *reply;
-	u8 *rbuf, *pos, *mic;
-	size_t kde_len, mic_len = 16;
-	u16 key_info;
-
-	/* KDEs: Peer RSN IE, Initiator MAC Address, Initiator Nonce */
-	kde_len = peerkey->rsnie_p_len +
-		2 + RSN_SELECTOR_LEN + ETH_ALEN +
-		2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN;
-
-	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
-				  NULL, sizeof(*reply) + mic_len + 2 + kde_len,
-				  &rlen, (void *) &reply);
-	if (rbuf == NULL)
-		return -1;
-
-	reply->type = EAPOL_KEY_TYPE_RSN;
-	key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
-		WPA_KEY_INFO_SECURE;
-	WPA_PUT_BE16(reply->key_info, key_info);
-	WPA_PUT_BE16(reply->key_length, 0);
-	os_memcpy(reply->replay_counter, key->replay_counter,
-		  WPA_REPLAY_COUNTER_LEN);
-
-	os_memcpy(reply->key_nonce, peerkey->pnonce, WPA_NONCE_LEN);
-
-	mic = (u8 *) (reply + 1);
-	WPA_PUT_BE16(mic + mic_len, (u16) kde_len);
-	pos = mic + mic_len + 2;
-
-	/* Peer RSN IE */
-	pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len);
-
-	/* Initiator MAC Address KDE */
-	pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peerkey->addr, ETH_ALEN);
-
-	/* Initiator Nonce */
-	wpa_add_kde(pos, RSN_KEY_DATA_NONCE, peerkey->inonce, WPA_NONCE_LEN);
-
-	wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK M3");
-	wpa_eapol_key_send(sm, &sm->ptk, ver, src_addr, ETH_P_EAPOL, rbuf, rlen,
-			   mic);
-
-	return 0;
-}
-
-
-static int wpa_supplicant_process_smk_m2(
-	struct wpa_sm *sm, const unsigned char *src_addr,
-	const struct wpa_eapol_key *key, const u8 *key_data,
-	size_t key_data_len, int ver)
-{
-	struct wpa_peerkey *peerkey;
-	struct wpa_eapol_ie_parse kde;
-	struct wpa_ie_data ie;
-	int cipher;
-	struct rsn_ie_hdr *hdr;
-	u8 *pos;
-
-	wpa_printf(MSG_DEBUG, "RSN: Received SMK M2");
-
-	if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
-		wpa_printf(MSG_INFO, "RSN: SMK handshake not allowed for "
-			   "the current network");
-		return -1;
-	}
-
-	if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0) {
-		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M2");
-		return -1;
-	}
-
-	if (kde.rsn_ie == NULL || kde.mac_addr == NULL ||
-	    kde.mac_addr_len < ETH_ALEN) {
-		wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in "
-			   "SMK M2");
-		return -1;
-	}
-
-	wpa_printf(MSG_DEBUG, "RSN: SMK M2 - SMK initiator " MACSTR,
-		   MAC2STR(kde.mac_addr));
-
-	if (kde.rsn_ie_len > PEERKEY_MAX_IE_LEN) {
-		wpa_printf(MSG_INFO, "RSN: Too long Initiator RSN IE in SMK "
-			   "M2");
-		return -1;
-	}
-
-	if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
-		wpa_printf(MSG_INFO, "RSN: Failed to parse RSN IE in SMK M2");
-		return -1;
-	}
-
-	cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher &
-					  sm->allowed_pairwise_cipher, 0);
-	if (cipher < 0) {
-		wpa_printf(MSG_INFO, "RSN: No acceptable cipher in SMK M2");
-		wpa_supplicant_send_smk_error(sm, src_addr, kde.mac_addr,
-					      STK_MUI_SMK, STK_ERR_CPHR_NS,
-					      ver);
-		return -1;
-	}
-	wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey",
-		   wpa_cipher_txt(cipher));
-
-	/* TODO: find existing entry and if found, use that instead of adding
-	 * a new one; how to handle the case where both ends initiate at the
-	 * same time? */
-	peerkey = os_zalloc(sizeof(*peerkey));
-	if (peerkey == NULL)
-		return -1;
-	os_memcpy(peerkey->addr, kde.mac_addr, ETH_ALEN);
-	os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN);
-	os_memcpy(peerkey->rsnie_i, kde.rsn_ie, kde.rsn_ie_len);
-	peerkey->rsnie_i_len = kde.rsn_ie_len;
-	peerkey->cipher = cipher;
-	peerkey->akmp = ie.key_mgmt;
-
-	if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-			"WPA: Failed to get random data for PNonce");
-		wpa_supplicant_peerkey_free(sm, peerkey);
-		return -1;
-	}
-
-	hdr = (struct rsn_ie_hdr *) peerkey->rsnie_p;
-	hdr->elem_id = WLAN_EID_RSN;
-	WPA_PUT_LE16(hdr->version, RSN_VERSION);
-	pos = (u8 *) (hdr + 1);
-	/* Group Suite can be anything for SMK RSN IE; receiver will just
-	 * ignore it. */
-	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
-	pos += RSN_SELECTOR_LEN;
-	/* Include only the selected cipher in pairwise cipher suite */
-	WPA_PUT_LE16(pos, 1);
-	pos += 2;
-	RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, cipher));
-	pos += RSN_SELECTOR_LEN;
-
-	hdr->len = (pos - peerkey->rsnie_p) - 2;
-	peerkey->rsnie_p_len = pos - peerkey->rsnie_p;
-	wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake",
-		    peerkey->rsnie_p, peerkey->rsnie_p_len);
-
-	wpa_supplicant_send_smk_m3(sm, src_addr, key, ver, peerkey);
-
-	peerkey->next = sm->peerkey;
-	sm->peerkey = peerkey;
-
-	return 0;
-}
-
-
-/**
- * rsn_smkid - Derive SMK identifier
- * @smk: Station master key (32 bytes)
- * @pnonce: Peer Nonce
- * @mac_p: Peer MAC address
- * @inonce: Initiator Nonce
- * @mac_i: Initiator MAC address
- * @akmp: Negotiated AKM
- *
- * 8.5.1.4 Station to station (STK) key hierarchy
- * SMKID = HMAC-SHA1-128(SMK, "SMK Name" || PNonce || MAC_P || INonce || MAC_I)
- */
-static void rsn_smkid(const u8 *smk, const u8 *pnonce, const u8 *mac_p,
-		      const u8 *inonce, const u8 *mac_i, u8 *smkid,
-		      int akmp)
-{
-	char *title = "SMK Name";
-	const u8 *addr[5];
-	const size_t len[5] = { 8, WPA_NONCE_LEN, ETH_ALEN, WPA_NONCE_LEN,
-				ETH_ALEN };
-	unsigned char hash[SHA256_MAC_LEN];
-
-	addr[0] = (u8 *) title;
-	addr[1] = pnonce;
-	addr[2] = mac_p;
-	addr[3] = inonce;
-	addr[4] = mac_i;
-
-#ifdef CONFIG_IEEE80211W
-	if (wpa_key_mgmt_sha256(akmp))
-		hmac_sha256_vector(smk, PMK_LEN, 5, addr, len, hash);
-	else
-#endif /* CONFIG_IEEE80211W */
-		hmac_sha1_vector(smk, PMK_LEN, 5, addr, len, hash);
-	os_memcpy(smkid, hash, PMKID_LEN);
-}
-
-
-static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm,
-					   struct wpa_peerkey *peerkey)
-{
-	size_t mlen;
-	struct wpa_eapol_key *msg;
-	u8 *mbuf, *mic;
-	size_t kde_len, mic_len = 16;
-	u16 key_info, ver;
-
-	kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
-
-	mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
-				  sizeof(*msg) + mic_len + 2 + kde_len, &mlen,
-				  (void *) &msg);
-	if (mbuf == NULL)
-		return;
-
-	mic = (u8 *) (msg + 1);
-	msg->type = EAPOL_KEY_TYPE_RSN;
-
-	if (peerkey->cipher != WPA_CIPHER_TKIP)
-		ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
-	else
-		ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
-
-	key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK;
-	WPA_PUT_BE16(msg->key_info, key_info);
-
-	if (peerkey->cipher != WPA_CIPHER_TKIP)
-		WPA_PUT_BE16(msg->key_length, 16);
-	else
-		WPA_PUT_BE16(msg->key_length, 32);
-
-	os_memcpy(msg->replay_counter, peerkey->replay_counter,
-		  WPA_REPLAY_COUNTER_LEN);
-	inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN);
-
-	WPA_PUT_BE16(mic + mic_len, kde_len);
-	wpa_add_kde(mic + mic_len + 2, RSN_KEY_DATA_PMKID,
-		    peerkey->smkid, PMKID_LEN);
-
-	if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-			"RSN: Failed to get random data for INonce (STK)");
-		os_free(mbuf);
-		return;
-	}
-	wpa_hexdump(MSG_DEBUG, "RSN: INonce for STK 4-Way Handshake",
-		    peerkey->inonce, WPA_NONCE_LEN);
-	os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN);
-
-	wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 1/4 to " MACSTR,
-		   MAC2STR(peerkey->addr));
-	wpa_eapol_key_send(sm, NULL, ver, peerkey->addr, ETH_P_EAPOL,
-			   mbuf, mlen, NULL);
-}
-
-
-static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm,
-					   struct wpa_peerkey *peerkey)
-{
-	size_t mlen;
-	struct wpa_eapol_key *msg;
-	u8 *mbuf, *pos, *mic;
-	size_t kde_len, mic_len = 16;
-	u16 key_info, ver;
-	be32 lifetime;
-
-	kde_len = peerkey->rsnie_i_len +
-		2 + RSN_SELECTOR_LEN + sizeof(lifetime);
-
-	mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
-				  sizeof(*msg) + mic_len + 2 + kde_len, &mlen,
-				  (void *) &msg);
-	if (mbuf == NULL)
-		return;
-
-	mic = (u8 *) (msg + 1);
-	msg->type = EAPOL_KEY_TYPE_RSN;
-
-	if (peerkey->cipher != WPA_CIPHER_TKIP)
-		ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
-	else
-		ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
-
-	key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK |
-		WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
-	WPA_PUT_BE16(msg->key_info, key_info);
-
-	if (peerkey->cipher != WPA_CIPHER_TKIP)
-		WPA_PUT_BE16(msg->key_length, 16);
-	else
-		WPA_PUT_BE16(msg->key_length, 32);
-
-	os_memcpy(msg->replay_counter, peerkey->replay_counter,
-		  WPA_REPLAY_COUNTER_LEN);
-	inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN);
-
-	WPA_PUT_BE16(mic + mic_len, kde_len);
-	pos = mic + mic_len + 2;
-	pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len);
-	lifetime = host_to_be32(peerkey->lifetime);
-	wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
-		    (u8 *) &lifetime, sizeof(lifetime));
-
-	os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN);
-
-	wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 3/4 to " MACSTR,
-		   MAC2STR(peerkey->addr));
-	wpa_eapol_key_send(sm, &peerkey->stk, ver, peerkey->addr, ETH_P_EAPOL,
-			   mbuf, mlen, mic);
-}
-
-
-static int wpa_supplicant_process_smk_m4(struct wpa_peerkey *peerkey,
-					 struct wpa_eapol_ie_parse *kde)
-{
-	wpa_printf(MSG_DEBUG, "RSN: Received SMK M4 (Initiator " MACSTR ")",
-		   MAC2STR(kde->mac_addr));
-
-	if (os_memcmp(kde->smk + PMK_LEN, peerkey->pnonce, WPA_NONCE_LEN) != 0)
-	{
-		wpa_printf(MSG_INFO, "RSN: PNonce in SMK KDE does not "
-			   "match with the one used in SMK M3");
-		return -1;
-	}
-
-	if (os_memcmp(kde->nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) {
-		wpa_printf(MSG_INFO, "RSN: INonce in SMK M4 did not "
-			   "match with the one received in SMK M2");
-		return -1;
-	}
-
-	return 0;
-}
-
-
-static int wpa_supplicant_process_smk_m5(struct wpa_sm *sm,
-					 const unsigned char *src_addr,
-					 const struct wpa_eapol_key *key,
-					 int ver,
-					 struct wpa_peerkey *peerkey,
-					 struct wpa_eapol_ie_parse *kde)
-{
-	int cipher;
-	struct wpa_ie_data ie;
-
-	wpa_printf(MSG_DEBUG, "RSN: Received SMK M5 (Peer " MACSTR ")",
-		   MAC2STR(kde->mac_addr));
-	if (kde->rsn_ie == NULL || kde->rsn_ie_len > PEERKEY_MAX_IE_LEN ||
-	    wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0) {
-		wpa_printf(MSG_INFO, "RSN: No RSN IE in SMK M5");
-		/* TODO: abort negotiation */
-		return -1;
-	}
-
-	if (os_memcmp(key->key_nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) {
-		wpa_printf(MSG_INFO, "RSN: Key Nonce in SMK M5 does "
-			   "not match with INonce used in SMK M1");
-		return -1;
-	}
-
-	if (os_memcmp(kde->smk + PMK_LEN, peerkey->inonce, WPA_NONCE_LEN) != 0)
-	{
-		wpa_printf(MSG_INFO, "RSN: INonce in SMK KDE does not "
-			   "match with the one used in SMK M1");
-		return -1;
-	}
-
-	os_memcpy(peerkey->rsnie_p, kde->rsn_ie, kde->rsn_ie_len);
-	peerkey->rsnie_p_len = kde->rsn_ie_len;
-	os_memcpy(peerkey->pnonce, kde->nonce, WPA_NONCE_LEN);
-
-	cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher &
-					  sm->allowed_pairwise_cipher, 0);
-	if (cipher < 0) {
-		wpa_printf(MSG_INFO, "RSN: SMK Peer STA " MACSTR " selected "
-			   "unacceptable cipher", MAC2STR(kde->mac_addr));
-		wpa_supplicant_send_smk_error(sm, src_addr, kde->mac_addr,
-					      STK_MUI_SMK, STK_ERR_CPHR_NS,
-					      ver);
-		/* TODO: abort negotiation */
-		return -1;
-	}
-	wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey",
-		   wpa_cipher_txt(cipher));
-	peerkey->cipher = cipher;
-
-	return 0;
-}
-
-
-static int wpa_supplicant_process_smk_m45(
-	struct wpa_sm *sm, const unsigned char *src_addr,
-	const struct wpa_eapol_key *key, const u8 *key_data,
-	size_t key_data_len, int ver)
-{
-	struct wpa_peerkey *peerkey;
-	struct wpa_eapol_ie_parse kde;
-	u32 lifetime;
-
-	if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
-		wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for "
-			   "the current network");
-		return -1;
-	}
-
-	if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0) {
-		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M4/M5");
-		return -1;
-	}
-
-	if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN ||
-	    kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN ||
-	    kde.smk == NULL || kde.smk_len < PMK_LEN + WPA_NONCE_LEN ||
-	    kde.lifetime == NULL || kde.lifetime_len < 4) {
-		wpa_printf(MSG_INFO, "RSN: No MAC Address, Nonce, SMK, or "
-			   "Lifetime KDE in SMK M4/M5");
-		return -1;
-	}
-
-	for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
-		if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) == 0 &&
-		    os_memcmp(peerkey->initiator ? peerkey->inonce :
-			   peerkey->pnonce,
-			   key->key_nonce, WPA_NONCE_LEN) == 0)
-			break;
-	}
-	if (peerkey == NULL) {
-		wpa_printf(MSG_INFO, "RSN: No matching SMK handshake found "
-			   "for SMK M4/M5: peer " MACSTR,
-			   MAC2STR(kde.mac_addr));
-		return -1;
-	}
-
-	if (peerkey->initiator) {
-		if (wpa_supplicant_process_smk_m5(sm, src_addr, key, ver,
-						  peerkey, &kde) < 0)
-			return -1;
-	} else {
-		if (wpa_supplicant_process_smk_m4(peerkey, &kde) < 0)
-			return -1;
-	}
-
-	os_memcpy(peerkey->smk, kde.smk, PMK_LEN);
-	peerkey->smk_complete = 1;
-	wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", peerkey->smk, PMK_LEN);
-	lifetime = WPA_GET_BE32(kde.lifetime);
-	wpa_printf(MSG_DEBUG, "RSN: SMK lifetime %u seconds", lifetime);
-	if (lifetime > 1000000000)
-		lifetime = 1000000000; /* avoid overflowing eloop time */
-	peerkey->lifetime = lifetime;
-	eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout,
-			       sm, peerkey);
-
-	if (peerkey->initiator) {
-		rsn_smkid(peerkey->smk, peerkey->pnonce, peerkey->addr,
-			  peerkey->inonce, sm->own_addr, peerkey->smkid,
-			  peerkey->akmp);
-		wpa_supplicant_send_stk_1_of_4(sm, peerkey);
-	} else {
-		rsn_smkid(peerkey->smk, peerkey->pnonce, sm->own_addr,
-			  peerkey->inonce, peerkey->addr, peerkey->smkid,
-			  peerkey->akmp);
-	}
-	wpa_hexdump(MSG_DEBUG, "RSN: SMKID", peerkey->smkid, PMKID_LEN);
-
-	return 0;
-}
-
-
-static int wpa_supplicant_process_smk_error(
-	struct wpa_sm *sm, const unsigned char *src_addr,
-	const u8 *key_data, size_t key_data_len)
-{
-	struct wpa_eapol_ie_parse kde;
-	struct rsn_error_kde error;
-	u8 peer[ETH_ALEN];
-	u16 error_type;
-
-	wpa_printf(MSG_DEBUG, "RSN: Received SMK Error");
-
-	if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
-		wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for "
-			   "the current network");
-		return -1;
-	}
-
-	if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0) {
-		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error");
-		return -1;
-	}
-
-	if (kde.error == NULL || kde.error_len < sizeof(error)) {
-		wpa_printf(MSG_INFO, "RSN: No Error KDE in SMK Error");
-		return -1;
-	}
-
-	if (kde.mac_addr && kde.mac_addr_len >= ETH_ALEN)
-		os_memcpy(peer, kde.mac_addr, ETH_ALEN);
-	else
-		os_memset(peer, 0, ETH_ALEN);
-	os_memcpy(&error, kde.error, sizeof(error));
-	error_type = be_to_host16(error.error_type);
-	wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-		"RSN: SMK Error KDE received: MUI %d error_type %d peer "
-		MACSTR,
-		be_to_host16(error.mui), error_type,
-		MAC2STR(peer));
-
-	if (kde.mac_addr &&
-	    (error_type == STK_ERR_STA_NR || error_type == STK_ERR_STA_NRSN ||
-	     error_type == STK_ERR_CPHR_NS)) {
-		struct wpa_peerkey *peerkey;
-
-		for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
-			if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) ==
-			    0)
-				break;
-		}
-		if (peerkey == NULL) {
-			wpa_printf(MSG_DEBUG, "RSN: No matching SMK handshake "
-				   "found for SMK Error");
-			return -1;
-		}
-		/* TODO: abort SMK/STK handshake and remove all related keys */
-	}
-
-	return 0;
-}
-
-
-static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm,
-					      struct wpa_peerkey *peerkey,
-					      const struct wpa_eapol_key *key,
-					      u16 ver, const u8 *key_data,
-					      size_t key_data_len)
-{
-	struct wpa_eapol_ie_parse ie;
-	size_t kde_buf_len;
-	struct wpa_ptk *stk;
-	u8 buf[8], *kde_buf, *pos;
-	be32 lifetime;
-
-	wpa_printf(MSG_DEBUG, "RSN: RX message 1 of STK 4-Way Handshake from "
-		   MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
-
-	os_memset(&ie, 0, sizeof(ie));
-
-	/* RSN: msg 1/4 should contain SMKID for the selected SMK */
-	wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", key_data, key_data_len);
-	if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0 ||
-	    ie.pmkid == NULL) {
-		wpa_printf(MSG_DEBUG, "RSN: No SMKID in STK 1/4");
-		return;
-	}
-	if (os_memcmp_const(ie.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
-		wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 1/4",
-			    ie.pmkid, PMKID_LEN);
-		return;
-	}
-
-	if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-			"RSN: Failed to get random data for PNonce");
-		return;
-	}
-	wpa_hexdump(MSG_DEBUG, "WPA: Renewed PNonce",
-		    peerkey->pnonce, WPA_NONCE_LEN);
-
-	/* Calculate STK which will be stored as a temporary STK until it has
-	 * been verified when processing message 3/4. */
-	stk = &peerkey->tstk;
-	wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion",
-		       sm->own_addr, peerkey->addr,
-		       peerkey->pnonce, key->key_nonce,
-		       stk, peerkey->akmp, peerkey->cipher);
-	/* Supplicant: swap tx/rx Mic keys */
-	os_memcpy(buf, &stk->tk[16], 8);
-	os_memcpy(&stk->tk[16], &stk->tk[24], 8);
-	os_memcpy(&stk->tk[24], buf, 8);
-	peerkey->tstk_set = 1;
-
-	kde_buf_len = peerkey->rsnie_p_len +
-		2 + RSN_SELECTOR_LEN + sizeof(lifetime) +
-		2 + RSN_SELECTOR_LEN + PMKID_LEN;
-	kde_buf = os_malloc(kde_buf_len);
-	if (kde_buf == NULL)
-		return;
-	pos = kde_buf;
-	pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len);
-	lifetime = host_to_be32(peerkey->lifetime);
-	pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
-			  (u8 *) &lifetime, sizeof(lifetime));
-	wpa_add_kde(pos, RSN_KEY_DATA_PMKID, peerkey->smkid, PMKID_LEN);
-
-	if (wpa_supplicant_send_2_of_4(sm, peerkey->addr, key, ver,
-				       peerkey->pnonce, kde_buf, kde_buf_len,
-				       stk) < 0) {
-		wpa_printf(MSG_INFO, "RSN: Failed to send STK message 2/4");
-		os_free(kde_buf);
-		return;
-	}
-	os_free(kde_buf);
-
-	os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN);
-}
-
-
-static void wpa_supplicant_update_smk_lifetime(struct wpa_sm *sm,
-					       struct wpa_peerkey *peerkey,
-					       struct wpa_eapol_ie_parse *kde)
-{
-	u32 lifetime;
-
-	if (kde->lifetime == NULL || kde->lifetime_len < sizeof(lifetime))
-		return;
-
-	lifetime = WPA_GET_BE32(kde->lifetime);
-
-	if (lifetime >= peerkey->lifetime) {
-		wpa_printf(MSG_DEBUG, "RSN: Peer used SMK lifetime %u seconds "
-			   "which is larger than or equal to own value %u "
-			   "seconds - ignored", lifetime, peerkey->lifetime);
-		return;
-	}
-
-	wpa_printf(MSG_DEBUG, "RSN: Peer used shorter SMK lifetime %u seconds "
-		   "(own was %u seconds) - updated",
-		   lifetime, peerkey->lifetime);
-	peerkey->lifetime = lifetime;
-
-	eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey);
-	eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout,
-			       sm, peerkey);
-}
-
-
-static void wpa_supplicant_process_stk_2_of_4(struct wpa_sm *sm,
-					      struct wpa_peerkey *peerkey,
-					      const struct wpa_eapol_key *key,
-					      u16 ver, const u8 *key_data,
-					      size_t key_data_len)
-{
-	struct wpa_eapol_ie_parse kde;
-
-	wpa_printf(MSG_DEBUG, "RSN: RX message 2 of STK 4-Way Handshake from "
-		   MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
-
-	os_memset(&kde, 0, sizeof(kde));
-
-	/* RSN: msg 2/4 should contain SMKID for the selected SMK and RSN IE
-	 * from the peer. It may also include Lifetime KDE. */
-	wpa_hexdump(MSG_DEBUG, "RSN: msg 2/4 key data", key_data, key_data_len);
-	if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0 ||
-	    kde.pmkid == NULL || kde.rsn_ie == NULL) {
-		wpa_printf(MSG_DEBUG, "RSN: No SMKID or RSN IE in STK 2/4");
-		return;
-	}
-
-	if (os_memcmp_const(kde.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
-		wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 2/4",
-			    kde.pmkid, PMKID_LEN);
-		return;
-	}
-
-	if (kde.rsn_ie_len != peerkey->rsnie_p_len ||
-	    os_memcmp(kde.rsn_ie, peerkey->rsnie_p, kde.rsn_ie_len) != 0) {
-		wpa_printf(MSG_INFO, "RSN: Peer RSN IE in SMK and STK "
-			   "handshakes did not match");
-		wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in SMK handshake",
-			    peerkey->rsnie_p, peerkey->rsnie_p_len);
-		wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in STK handshake",
-			    kde.rsn_ie, kde.rsn_ie_len);
-		return;
-	}
-
-	wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde);
-
-	wpa_supplicant_send_stk_3_of_4(sm, peerkey);
-	os_memcpy(peerkey->pnonce, key->key_nonce, WPA_NONCE_LEN);
-}
-
-
-static void wpa_supplicant_process_stk_3_of_4(struct wpa_sm *sm,
-					      struct wpa_peerkey *peerkey,
-					      const struct wpa_eapol_key *key,
-					      u16 ver, const u8 *key_data,
-					      size_t key_data_len)
-{
-	struct wpa_eapol_ie_parse kde;
-	size_t key_len;
-	const u8 *_key;
-	u8 key_buf[32], rsc[6];
-
-	wpa_printf(MSG_DEBUG, "RSN: RX message 3 of STK 4-Way Handshake from "
-		   MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
-
-	os_memset(&kde, 0, sizeof(kde));
-
-	/* RSN: msg 3/4 should contain Initiator RSN IE. It may also include
-	 * Lifetime KDE. */
-	wpa_hexdump(MSG_DEBUG, "RSN: msg 3/4 key data", key_data, key_data_len);
-	if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0) {
-		wpa_printf(MSG_DEBUG, "RSN: Failed to parse key data in "
-			   "STK 3/4");
-		return;
-	}
-
-	if (kde.rsn_ie_len != peerkey->rsnie_i_len ||
-	    os_memcmp(kde.rsn_ie, peerkey->rsnie_i, kde.rsn_ie_len) != 0) {
-		wpa_printf(MSG_INFO, "RSN: Initiator RSN IE in SMK and STK "
-			   "handshakes did not match");
-		wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in SMK "
-			    "handshake",
-			    peerkey->rsnie_i, peerkey->rsnie_i_len);
-		wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in STK "
-			    "handshake",
-			    kde.rsn_ie, kde.rsn_ie_len);
-		return;
-	}
-
-	if (os_memcmp(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
-		wpa_printf(MSG_WARNING, "RSN: INonce from message 1 of STK "
-			   "4-Way Handshake differs from 3 of STK 4-Way "
-			   "Handshake - drop packet (src=" MACSTR ")",
-			   MAC2STR(peerkey->addr));
-		wpa_hexdump(MSG_DEBUG, "RSN: INonce from message 1",
-			    peerkey->inonce, WPA_NONCE_LEN);
-		wpa_hexdump(MSG_DEBUG, "RSN: INonce from message 3",
-			    key->key_nonce, WPA_NONCE_LEN);
-		return;
-	}
-
-	wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde);
-
-	if (wpa_supplicant_send_4_of_4(sm, peerkey->addr, key, ver,
-				       WPA_GET_BE16(key->key_info),
-				       &peerkey->stk) < 0) {
-		wpa_printf(MSG_INFO, "RSN: Failed to send STK message 4/4");
-		return;
-	}
-
-	_key = peerkey->stk.tk;
-	if (peerkey->cipher == WPA_CIPHER_TKIP) {
-		/* Swap Tx/Rx keys for Michael MIC */
-		os_memcpy(key_buf, _key, 16);
-		os_memcpy(key_buf + 16, _key + 24, 8);
-		os_memcpy(key_buf + 24, _key + 16, 8);
-		_key = key_buf;
-		key_len = 32;
-	} else
-		key_len = 16;
-
-	os_memset(rsc, 0, 6);
-	if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1,
-			   rsc, sizeof(rsc), _key, key_len) < 0) {
-		os_memset(key_buf, 0, sizeof(key_buf));
-		wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the "
-			   "driver.");
-		return;
-	}
-	os_memset(key_buf, 0, sizeof(key_buf));
-}
-
-
-static void wpa_supplicant_process_stk_4_of_4(struct wpa_sm *sm,
-					      struct wpa_peerkey *peerkey,
-					      const struct wpa_eapol_key *key,
-					      u16 ver)
-{
-	u8 rsc[6];
-
-	wpa_printf(MSG_DEBUG, "RSN: RX message 4 of STK 4-Way Handshake from "
-		   MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
-
-	os_memset(rsc, 0, 6);
-	if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1,
-			   rsc, sizeof(rsc), peerkey->stk.tk,
-			   peerkey->cipher == WPA_CIPHER_TKIP ? 32 : 16) < 0) {
-		wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the "
-			   "driver.");
-		return;
-	}
-}
-
-
-/**
- * peerkey_verify_eapol_key_mic - Verify PeerKey MIC
- * @sm: Pointer to WPA state machine data from wpa_sm_init()
- * @peerkey: Pointer to the PeerKey data for the peer
- * @key: Pointer to the EAPOL-Key frame header
- * @ver: Version bits from EAPOL-Key Key Info
- * @buf: Pointer to the beginning of EAPOL-Key frame
- * @len: Length of the EAPOL-Key frame
- * Returns: 0 on success, -1 on failure
- */
-int peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
-				 struct wpa_peerkey *peerkey,
-				 struct wpa_eapol_key *key, u16 ver,
-				 const u8 *buf, size_t len)
-{
-	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN], *mic_pos;
-	size_t mic_len = 16;
-	int ok = 0;
-
-	if (peerkey->initiator && !peerkey->stk_set) {
-		wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion",
-			       sm->own_addr, peerkey->addr,
-			       peerkey->inonce, key->key_nonce,
-			       &peerkey->stk, peerkey->akmp, peerkey->cipher);
-		peerkey->stk_set = 1;
-	}
-
-	mic_pos = (u8 *) (key + 1);
-	os_memcpy(mic, mic_pos, mic_len);
-	if (peerkey->tstk_set) {
-		os_memset(mic_pos, 0, mic_len);
-		wpa_eapol_key_mic(peerkey->tstk.kck, peerkey->tstk.kck_len,
-				  sm->key_mgmt, ver, buf, len, mic_pos);
-		if (os_memcmp_const(mic, mic_pos, mic_len) != 0) {
-			wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
-				   "when using TSTK - ignoring TSTK");
-		} else {
-			ok = 1;
-			peerkey->tstk_set = 0;
-			peerkey->stk_set = 1;
-			os_memcpy(&peerkey->stk, &peerkey->tstk,
-				  sizeof(peerkey->stk));
-			os_memset(&peerkey->tstk, 0, sizeof(peerkey->tstk));
-		}
-	}
-
-	if (!ok && peerkey->stk_set) {
-		os_memset(mic_pos, 0, mic_len);
-		wpa_eapol_key_mic(peerkey->stk.kck, peerkey->stk.kck_len,
-				  sm->key_mgmt, ver, buf, len, mic_pos);
-		if (os_memcmp_const(mic, mic_pos, mic_len) != 0) {
-			wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
-				   "- dropping packet");
-			return -1;
-		}
-		ok = 1;
-	}
-
-	if (!ok) {
-		wpa_printf(MSG_WARNING, "RSN: Could not verify EAPOL-Key MIC "
-			   "- dropping packet");
-		return -1;
-	}
-
-	os_memcpy(peerkey->replay_counter, key->replay_counter,
-		  WPA_REPLAY_COUNTER_LEN);
-	peerkey->replay_counter_set = 1;
-	return 0;
-}
-
-
-/**
- * wpa_sm_stkstart - Send EAPOL-Key Request for STK handshake (STK M1)
- * @sm: Pointer to WPA state machine data from wpa_sm_init()
- * @peer: MAC address of the peer STA
- * Returns: 0 on success, or -1 on failure
- *
- * Send an EAPOL-Key Request to the current authenticator to start STK
- * handshake with the peer.
- */
-int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer)
-{
-	size_t rlen, kde_len, mic_len;
-	struct wpa_eapol_key *req;
-	int key_info, ver;
-	u8 bssid[ETH_ALEN], *rbuf, *pos, *count_pos, *mic;
-	u16 count;
-	struct rsn_ie_hdr *hdr;
-	struct wpa_peerkey *peerkey;
-	struct wpa_ie_data ie;
-
-	if (sm->proto != WPA_PROTO_RSN || !sm->ptk_set || !sm->peerkey_enabled)
-		return -1;
-
-	if (sm->ap_rsn_ie &&
-	    wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &ie) == 0 &&
-	    !(ie.capabilities & WPA_CAPABILITY_PEERKEY_ENABLED)) {
-		wpa_printf(MSG_DEBUG, "RSN: Current AP does not support STK");
-		return -1;
-	}
-
-	mic_len = wpa_mic_len(sm->key_mgmt);
-	if (sm->pairwise_cipher != WPA_CIPHER_TKIP)
-		ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
-	else
-		ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
-
-	if (wpa_sm_get_bssid(sm, bssid) < 0) {
-		wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key "
-			   "SMK M1");
-		return -1;
-	}
-
-	/* TODO: find existing entry and if found, use that instead of adding
-	 * a new one */
-	peerkey = os_zalloc(sizeof(*peerkey));
-	if (peerkey == NULL)
-		return -1;
-	peerkey->initiator = 1;
-	os_memcpy(peerkey->addr, peer, ETH_ALEN);
-	peerkey->akmp = sm->key_mgmt;
-
-	/* SMK M1:
-	 * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce,
-	 *           MIC=MIC, DataKDs=(RSNIE_I, MAC_P KDE))
-	 */
-
-	hdr = (struct rsn_ie_hdr *) peerkey->rsnie_i;
-	hdr->elem_id = WLAN_EID_RSN;
-	WPA_PUT_LE16(hdr->version, RSN_VERSION);
-	pos = (u8 *) (hdr + 1);
-	/* Group Suite can be anything for SMK RSN IE; receiver will just
-	 * ignore it. */
-	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
-	pos += RSN_SELECTOR_LEN;
-	count_pos = pos;
-	pos += 2;
-
-	count = rsn_cipher_put_suites(pos, sm->allowed_pairwise_cipher);
-	pos += count * RSN_SELECTOR_LEN;
-	WPA_PUT_LE16(count_pos, count);
-
-	hdr->len = (pos - peerkey->rsnie_i) - 2;
-	peerkey->rsnie_i_len = pos - peerkey->rsnie_i;
-	wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake",
-		    peerkey->rsnie_i, peerkey->rsnie_i_len);
-
-	kde_len = peerkey->rsnie_i_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN;
-
-	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
-				  sizeof(*req) + mic_len + 2 + kde_len, &rlen,
-				  (void *) &req);
-	if (rbuf == NULL) {
-		wpa_supplicant_peerkey_free(sm, peerkey);
-		return -1;
-	}
-
-	req->type = EAPOL_KEY_TYPE_RSN;
-	key_info = WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
-		WPA_KEY_INFO_SECURE | WPA_KEY_INFO_REQUEST | ver;
-	WPA_PUT_BE16(req->key_info, key_info);
-	WPA_PUT_BE16(req->key_length, 0);
-	os_memcpy(req->replay_counter, sm->request_counter,
-		  WPA_REPLAY_COUNTER_LEN);
-	inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
-
-	if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-			"WPA: Failed to get random data for INonce");
-		os_free(rbuf);
-		wpa_supplicant_peerkey_free(sm, peerkey);
-		return -1;
-	}
-	os_memcpy(req->key_nonce, peerkey->inonce, WPA_NONCE_LEN);
-	wpa_hexdump(MSG_DEBUG, "WPA: INonce for SMK handshake",
-		    req->key_nonce, WPA_NONCE_LEN);
-
-	mic = pos = (u8 *) (req + 1);
-	pos += mic_len;
-	WPA_PUT_BE16(pos, (u16) kde_len);
-	pos += 2;
-
-	/* Initiator RSN IE */
-	pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len);
-	/* Peer MAC address KDE */
-	wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN);
-
-	wpa_printf(MSG_INFO, "RSN: Sending EAPOL-Key SMK M1 Request (peer "
-		   MACSTR ")", MAC2STR(peer));
-	wpa_eapol_key_send(sm, &sm->ptk, ver, bssid, ETH_P_EAPOL, rbuf, rlen,
-			   mic);
-
-	peerkey->next = sm->peerkey;
-	sm->peerkey = peerkey;
-
-	return 0;
-}
-
-
-/**
- * peerkey_deinit - Free PeerKey values
- * @sm: Pointer to WPA state machine data from wpa_sm_init()
- */
-void peerkey_deinit(struct wpa_sm *sm)
-{
-	struct wpa_peerkey *prev, *peerkey = sm->peerkey;
-	while (peerkey) {
-		prev = peerkey;
-		peerkey = peerkey->next;
-		wpa_supplicant_peerkey_free(sm, prev);
-	}
-	sm->peerkey = NULL;
-}
-
-
-void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
-			   struct wpa_eapol_key *key, u16 key_info, u16 ver,
-			   const u8 *key_data, size_t key_data_len)
-{
-	if ((key_info & (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) ==
-	    (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) {
-		/* 3/4 STK 4-Way Handshake */
-		wpa_supplicant_process_stk_3_of_4(sm, peerkey, key, ver,
-						  key_data, key_data_len);
-	} else if (key_info & WPA_KEY_INFO_ACK) {
-		/* 1/4 STK 4-Way Handshake */
-		wpa_supplicant_process_stk_1_of_4(sm, peerkey, key, ver,
-						  key_data, key_data_len);
-	} else if (key_info & WPA_KEY_INFO_SECURE) {
-		/* 4/4 STK 4-Way Handshake */
-		wpa_supplicant_process_stk_4_of_4(sm, peerkey, key, ver);
-	} else {
-		/* 2/4 STK 4-Way Handshake */
-		wpa_supplicant_process_stk_2_of_4(sm, peerkey, key, ver,
-						  key_data, key_data_len);
-	}
-}
-
-
-void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
-			  struct wpa_eapol_key *key, const u8 *key_data,
-			  size_t key_data_len,
-			  u16 key_info, u16 ver)
-{
-	if (key_info & WPA_KEY_INFO_ERROR) {
-		/* SMK Error */
-		wpa_supplicant_process_smk_error(sm, src_addr, key_data,
-						 key_data_len);
-	} else if (key_info & WPA_KEY_INFO_ACK) {
-		/* SMK M2 */
-		wpa_supplicant_process_smk_m2(sm, src_addr, key, key_data,
-					      key_data_len, ver);
-	} else {
-		/* SMK M4 or M5 */
-		wpa_supplicant_process_smk_m45(sm, src_addr, key, key_data,
-					       key_data_len, ver);
-	}
-}
-
-#endif /* CONFIG_PEERKEY */
diff --git a/src/rsn_supp/peerkey.h b/src/rsn_supp/peerkey.h
deleted file mode 100644
index 02e12e9..0000000
--- a/src/rsn_supp/peerkey.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * WPA Supplicant - PeerKey for Direct Link Setup (DLS)
- * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#ifndef PEERKEY_H
-#define PEERKEY_H
-
-#define PEERKEY_MAX_IE_LEN 80
-struct wpa_peerkey {
-	struct wpa_peerkey *next;
-	int initiator; /* whether this end was initator for SMK handshake */
-	u8 addr[ETH_ALEN]; /* other end MAC address */
-	u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */
-	u8 pnonce[WPA_NONCE_LEN]; /* Peer Nonce */
-	u8 rsnie_i[PEERKEY_MAX_IE_LEN]; /* Initiator RSN IE */
-	size_t rsnie_i_len;
-	u8 rsnie_p[PEERKEY_MAX_IE_LEN]; /* Peer RSN IE */
-	size_t rsnie_p_len;
-	u8 smk[PMK_LEN];
-	int smk_complete;
-	u8 smkid[PMKID_LEN];
-	u32 lifetime;
-	int cipher; /* Selected cipher (WPA_CIPHER_*) */
-	u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
-	int replay_counter_set;
-	int akmp;
-
-	struct wpa_ptk stk, tstk;
-	int stk_set, tstk_set;
-};
-
-
-#ifdef CONFIG_PEERKEY
-
-int peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
-				 struct wpa_peerkey *peerkey,
-				 struct wpa_eapol_key *key, u16 ver,
-				 const u8 *buf, size_t len);
-void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
-			   struct wpa_eapol_key *key, u16 key_info, u16 ver,
-			   const u8 *key_data, size_t key_data_len);
-void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
-			  struct wpa_eapol_key *key, const u8 *key_data,
-			  size_t key_data_len, u16 key_info, u16 ver);
-void peerkey_deinit(struct wpa_sm *sm);
-
-#else /* CONFIG_PEERKEY */
-
-static inline int
-peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
-			     struct wpa_peerkey *peerkey,
-			     struct wpa_eapol_key *key, u16 ver,
-			     const u8 *buf, size_t len)
-{
-	return -1;
-}
-
-static inline void
-peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
-		      struct wpa_eapol_key *key, u16 key_info, u16 ver,
-		      const u8 *key_data, size_t key_data_len)
-{
-}
-
-static inline void
-peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
-		     struct wpa_eapol_key *key, const u8 *key_data,
-		     size_t key_data_len, u16 key_info, u16 ver)
-{
-}
-
-static inline void peerkey_deinit(struct wpa_sm *sm)
-{
-}
-
-#endif /* CONFIG_PEERKEY */
-
-#endif /* PEERKEY_H */
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index f723bb0..f5024f2 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -44,7 +44,9 @@
 				   enum pmksa_free_reason reason)
 {
 	wpa_sm_remove_pmkid(pmksa->sm, entry->network_ctx, entry->aa,
-			    entry->pmkid);
+			    entry->pmkid,
+			    entry->fils_cache_id_set ? entry->fils_cache_id :
+			    NULL);
 	pmksa->pmksa_count--;
 	pmksa->free_cb(entry, pmksa->ctx, reason);
 	_pmksa_cache_free_entry(entry);
@@ -117,6 +119,7 @@
  * @spa: Supplicant address
  * @network_ctx: Network configuration context for this PMK
  * @akmp: WPA_KEY_MGMT_* used in key derivation
+ * @cache_id: Pointer to FILS Cache Identifier or %NULL if not advertised
  * Returns: Pointer to the added PMKSA cache entry or %NULL on error
  *
  * This function create a PMKSA entry for a new PMK and adds it to the PMKSA
@@ -127,7 +130,8 @@
 struct rsn_pmksa_cache_entry *
 pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
 		const u8 *pmkid, const u8 *kck, size_t kck_len,
-		const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
+		const u8 *aa, const u8 *spa, void *network_ctx, int akmp,
+		const u8 *cache_id)
 {
 	struct rsn_pmksa_cache_entry *entry;
 	struct os_reltime now;
@@ -150,13 +154,16 @@
 	else if (wpa_key_mgmt_suite_b(akmp))
 		rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
 	else
-		rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
-			  wpa_key_mgmt_sha256(akmp));
+		rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp);
 	os_get_reltime(&now);
 	entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime;
 	entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime *
 		pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100;
 	entry->akmp = akmp;
+	if (cache_id) {
+		entry->fils_cache_id_set = 1;
+		os_memcpy(entry->fils_cache_id, cache_id, FILS_CACHE_ID_LEN);
+	}
 	os_memcpy(entry->aa, aa, ETH_ALEN);
 	entry->network_ctx = network_ctx;
 
@@ -257,8 +264,9 @@
 	pmksa->pmksa_count++;
 	wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR
 		   " network_ctx=%p", MAC2STR(entry->aa), entry->network_ctx);
-	wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa,
-			 entry->pmkid);
+	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);
 
 	return entry;
 }
@@ -358,16 +366,19 @@
 			const u8 *aa)
 {
 	struct rsn_pmksa_cache_entry *new_entry;
+	os_time_t old_expiration = old_entry->expiration;
 
 	new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len,
 				    NULL, NULL, 0,
 				    aa, pmksa->sm->own_addr,
-				    old_entry->network_ctx, old_entry->akmp);
+				    old_entry->network_ctx, old_entry->akmp,
+				    old_entry->fils_cache_id_set ?
+				    old_entry->fils_cache_id : NULL);
 	if (new_entry == NULL)
 		return NULL;
 
 	/* TODO: reorder entries based on expiration time? */
-	new_entry->expiration = old_entry->expiration;
+	new_entry->expiration = old_expiration;
 	new_entry->opportunistic = 1;
 
 	return new_entry;
@@ -410,6 +421,24 @@
 }
 
 
+static struct rsn_pmksa_cache_entry *
+pmksa_cache_get_fils_cache_id(struct rsn_pmksa_cache *pmksa,
+			      const void *network_ctx, const u8 *cache_id)
+{
+	struct rsn_pmksa_cache_entry *entry;
+
+	for (entry = pmksa->pmksa; entry; entry = entry->next) {
+		if (network_ctx == entry->network_ctx &&
+		    entry->fils_cache_id_set &&
+		    os_memcmp(cache_id, entry->fils_cache_id,
+			      FILS_CACHE_ID_LEN) == 0)
+			return entry;
+	}
+
+	return NULL;
+}
+
+
 /**
  * pmksa_cache_get_current - Get the current used PMKSA entry
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -442,11 +471,12 @@
  * @bssid: BSSID for PMKSA or %NULL if not used
  * @network_ctx: Network configuration context
  * @try_opportunistic: Whether to allow opportunistic PMKSA caching
+ * @fils_cache_id: Pointer to FILS Cache Identifier or %NULL if not used
  * Returns: 0 if PMKSA was found or -1 if no matching entry was found
  */
 int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
 			    const u8 *bssid, void *network_ctx,
-			    int try_opportunistic)
+			    int try_opportunistic, const u8 *fils_cache_id)
 {
 	struct rsn_pmksa_cache *pmksa = sm->pmksa;
 	wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p "
@@ -457,6 +487,10 @@
 	if (bssid)
 		wpa_printf(MSG_DEBUG, "RSN: Search for BSSID " MACSTR,
 			   MAC2STR(bssid));
+	if (fils_cache_id)
+		wpa_printf(MSG_DEBUG,
+			   "RSN: Search for FILS Cache Identifier %02x%02x",
+			   fils_cache_id[0], fils_cache_id[1]);
 
 	sm->cur_pmksa = NULL;
 	if (pmkid)
@@ -469,6 +503,10 @@
 		sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa,
 							      network_ctx,
 							      bssid);
+	if (sm->cur_pmksa == NULL && fils_cache_id)
+		sm->cur_pmksa = pmksa_cache_get_fils_cache_id(pmksa,
+							      network_ctx,
+							      fils_cache_id);
 	if (sm->cur_pmksa) {
 		wpa_hexdump(MSG_DEBUG, "RSN: PMKSA cache entry found - PMKID",
 			    sm->cur_pmksa->pmkid, PMKID_LEN);
@@ -495,11 +533,20 @@
 	char *pos = buf;
 	struct rsn_pmksa_cache_entry *entry;
 	struct os_reltime now;
+	int cache_id_used = 0;
+
+	for (entry = pmksa->pmksa; entry; entry = entry->next) {
+		if (entry->fils_cache_id_set) {
+			cache_id_used = 1;
+			break;
+		}
+	}
 
 	os_get_reltime(&now);
 	ret = os_snprintf(pos, buf + len - pos,
 			  "Index / AA / PMKID / expiration (in seconds) / "
-			  "opportunistic\n");
+			  "opportunistic%s\n",
+			  cache_id_used ? " / FILS Cache Identifier" : "");
 	if (os_snprintf_error(buf + len - pos, ret))
 		return pos - buf;
 	pos += ret;
@@ -514,12 +561,24 @@
 		pos += ret;
 		pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
 					PMKID_LEN);
-		ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
+		ret = os_snprintf(pos, buf + len - pos, " %d %d",
 				  (int) (entry->expiration - now.sec),
 				  entry->opportunistic);
 		if (os_snprintf_error(buf + len - pos, ret))
 			return pos - buf;
 		pos += ret;
+		if (entry->fils_cache_id_set) {
+			ret = os_snprintf(pos, buf + len - pos, " %02x%02x",
+					  entry->fils_cache_id[0],
+					  entry->fils_cache_id[1]);
+			if (os_snprintf_error(buf + len - pos, ret))
+				return pos - buf;
+			pos += ret;
+		}
+		ret = os_snprintf(pos, buf + len - pos, "\n");
+		if (os_snprintf_error(buf + len - pos, ret))
+			return pos - buf;
+		pos += ret;
 		entry = entry->next;
 	}
 	return pos - buf;
diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h
index adc251a..f9a72a6 100644
--- a/src/rsn_supp/pmksa_cache.h
+++ b/src/rsn_supp/pmksa_cache.h
@@ -21,6 +21,14 @@
 	int akmp; /* WPA_KEY_MGMT_* */
 	u8 aa[ETH_ALEN];
 
+	/*
+	 * If FILS Cache Identifier is included (fils_cache_id_set), this PMKSA
+	 * cache entry is applicable to all BSSs (any BSSID/aa[]) that
+	 * advertise the same FILS Cache Identifier within the same ESS.
+	 */
+	u8 fils_cache_id[2];
+	unsigned int fils_cache_id_set:1;
+
 	os_time_t reauth_time;
 
 	/**
@@ -59,7 +67,8 @@
 struct rsn_pmksa_cache_entry *
 pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
 		const u8 *pmkid, const u8 *kck, size_t kck_len,
-		const u8 *aa, const u8 *spa, void *network_ctx, int akmp);
+		const u8 *aa, const u8 *spa, void *network_ctx, int akmp,
+		const u8 *cache_id);
 struct rsn_pmksa_cache_entry *
 pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa,
 		      struct rsn_pmksa_cache_entry *entry);
@@ -67,7 +76,7 @@
 void pmksa_cache_clear_current(struct wpa_sm *sm);
 int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
 			    const u8 *bssid, void *network_ctx,
-			    int try_opportunistic);
+			    int try_opportunistic, const u8 *fils_cache_id);
 struct rsn_pmksa_cache_entry *
 pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa,
 			      void *network_ctx, const u8 *aa);
@@ -123,7 +132,8 @@
 static inline struct rsn_pmksa_cache_entry *
 pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
 		const u8 *pmkid, const u8 *kck, size_t kck_len,
-		const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
+		const u8 *aa, const u8 *spa, void *network_ctx, int akmp,
+		const u8 *cache_id)
 {
 	return NULL;
 }
@@ -135,7 +145,8 @@
 static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
 					  const u8 *bssid,
 					  void *network_ctx,
-					  int try_opportunistic)
+					  int try_opportunistic,
+					  const u8 *fils_cache_id)
 {
 	return -1;
 }
diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c
index e83d073..d4276b9 100644
--- a/src/rsn_supp/preauth.c
+++ b/src/rsn_supp/preauth.c
@@ -97,7 +97,7 @@
 					NULL, 0,
 					sm->preauth_bssid, sm->own_addr,
 					sm->network_ctx,
-					WPA_KEY_MGMT_IEEE8021X);
+					WPA_KEY_MGMT_IEEE8021X, NULL);
 		} else {
 			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 				"RSN: failed to get master session key from "
@@ -342,7 +342,8 @@
 		/* Some drivers (e.g., NDIS) expect to get notified about the
 		 * PMKIDs again, so report the existing data now. */
 		if (p) {
-			wpa_sm_add_pmkid(sm, NULL, candidate->bssid, p->pmkid);
+			wpa_sm_add_pmkid(sm, NULL, candidate->bssid, p->pmkid,
+					 NULL, p->pmk, p->pmk_len);
 		}
 
 		dl_list_del(&candidate->list);
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index c2ed952..5e350ed 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -303,10 +303,9 @@
 	peer->sm_tmr.peer_capab = peer_capab;
 	peer->sm_tmr.buf_len = msg_len;
 	os_free(peer->sm_tmr.buf);
-	peer->sm_tmr.buf = os_malloc(msg_len);
+	peer->sm_tmr.buf = os_memdup(msg, msg_len);
 	if (peer->sm_tmr.buf == NULL)
 		return -1;
-	os_memcpy(peer->sm_tmr.buf, msg, msg_len);
 
 	wpa_printf(MSG_DEBUG, "TDLS: Retry timeout registered "
 		   "(action_code=%u)", action_code);
@@ -413,8 +412,9 @@
 	size_t len[2];
 	u8 data[3 * ETH_ALEN];
 
-	/* IEEE Std 802.11z-2010 8.5.9.1:
-	 * TPK-Key-Input = SHA-256(min(SNonce, ANonce) || max(SNonce, ANonce))
+	/* IEEE Std 802.11-2016 12.7.9.2:
+	 * TPK-Key-Input = Hash(min(SNonce, ANonce) || max(SNonce, ANonce))
+	 * Hash = SHA-256 for TDLS
 	 */
 	len[0] = WPA_NONCE_LEN;
 	len[1] = WPA_NONCE_LEN;
@@ -432,11 +432,8 @@
 			key_input, SHA256_MAC_LEN);
 
 	/*
-	 * TPK-Key-Data = KDF-N_KEY(TPK-Key-Input, "TDLS PMK",
-	 *	min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID || N_KEY)
-	 * TODO: is N_KEY really included in KDF Context and if so, in which
-	 * presentation format (little endian 16-bit?) is it used? It gets
-	 * added by the KDF anyway..
+	 * TPK = KDF-Hash-Length(TPK-Key-Input, "TDLS PMK",
+	 *	min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID)
 	 */
 
 	if (os_memcmp(own_addr, peer->addr, ETH_ALEN) < 0) {
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index af72298..15b0e60 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - WPA state machine and EAPOL-Key processing
- * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
  * Copyright(c) 2015 Intel Deutschland GmbH
  *
  * This software may be distributed under the terms of the BSD license.
@@ -15,6 +15,9 @@
 #include "crypto/crypto.h"
 #include "crypto/random.h"
 #include "crypto/aes_siv.h"
+#include "crypto/sha256.h"
+#include "crypto/sha384.h"
+#include "crypto/sha512.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "eap_common/eap_defs.h"
@@ -25,7 +28,6 @@
 #include "pmksa_cache.h"
 #include "wpa_i.h"
 #include "wpa_ie.h"
-#include "peerkey.h"
 
 
 static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
@@ -48,8 +50,11 @@
 		       u8 *msg, size_t msg_len, u8 *key_mic)
 {
 	int ret = -1;
-	size_t mic_len = wpa_mic_len(sm->key_mgmt);
+	size_t mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
 
+	wpa_printf(MSG_DEBUG, "WPA: Send EAPOL-Key frame to " MACSTR
+		   " ver=%d mic_len=%d key_mgmt=0x%x",
+		   MAC2STR(dest), ver, (int) mic_len, sm->key_mgmt);
 	if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) {
 		/*
 		 * Association event was not yet received; try to fetch
@@ -193,7 +198,7 @@
 		return;
 	}
 
-	mic_len = wpa_mic_len(sm->key_mgmt);
+	mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
 	hdrlen = sizeof(*reply) + mic_len + 2;
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
 				  hdrlen, &rlen, (void *) &reply);
@@ -315,6 +320,13 @@
 		}
 		if (res == 0) {
 			struct rsn_pmksa_cache_entry *sa = NULL;
+			const u8 *fils_cache_id = NULL;
+
+#ifdef CONFIG_FILS
+			if (sm->fils_cache_id_set)
+				fils_cache_id = sm->fils_cache_id;
+#endif /* CONFIG_FILS */
+
 			wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state "
 					"machines", sm->pmk, pmk_len);
 			sm->pmk_len = pmk_len;
@@ -327,7 +339,8 @@
 						     NULL, 0,
 						     src_addr, sm->own_addr,
 						     sm->network_ctx,
-						     sm->key_mgmt);
+						     sm->key_mgmt,
+						     fils_cache_id);
 			}
 			if (!sm->cur_pmksa && pmkid &&
 			    pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL))
@@ -457,7 +470,7 @@
 
 	wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len);
 
-	mic_len = wpa_mic_len(sm->key_mgmt);
+	mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
 	hdrlen = sizeof(*reply) + mic_len + 2;
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
 				  NULL, hdrlen + wpa_ie_len,
@@ -572,7 +585,8 @@
 	/* Calculate PTK which will be stored as a temporary PTK until it has
 	 * been verified when processing message 3/4. */
 	ptk = &sm->tptk;
-	wpa_derive_ptk(sm, src_addr, key, ptk);
+	if (wpa_derive_ptk(sm, src_addr, key, ptk) < 0)
+		goto failed;
 	if (sm->pairwise_cipher == WPA_CIPHER_TKIP) {
 		u8 buf[8];
 		/* Supplicant: swap tx/rx Mic keys */
@@ -643,7 +657,9 @@
 			sm, addr, MLME_SETPROTECTION_PROTECT_TYPE_RX_TX,
 			MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
 		eapol_sm_notify_portValid(sm->eapol, TRUE);
-		if (wpa_key_mgmt_wpa_psk(sm->key_mgmt))
+		if (wpa_key_mgmt_wpa_psk(sm->key_mgmt) ||
+		    sm->key_mgmt == WPA_KEY_MGMT_DPP ||
+		    sm->key_mgmt == WPA_KEY_MGMT_OWE)
 			eapol_sm_notify_eap_success(sm->eapol, TRUE);
 		/*
 		 * Start preauthentication after a short wait to avoid a
@@ -710,6 +726,11 @@
 
 	alg = wpa_cipher_to_alg(sm->pairwise_cipher);
 	keylen = wpa_cipher_key_len(sm->pairwise_cipher);
+	if (keylen <= 0 || (unsigned int) keylen != sm->ptk.tk_len) {
+		wpa_printf(MSG_DEBUG, "WPA: TK length mismatch: %d != %lu",
+			   keylen, (long unsigned int) sm->ptk.tk_len);
+		return -1;
+	}
 	rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
 
 	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
@@ -730,6 +751,7 @@
 
 	/* TK is not needed anymore in supplicant */
 	os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
+	sm->ptk.tk_len = 0;
 	sm->ptk.installed = 1;
 
 	if (sm->wpa_ptk_rekey) {
@@ -1277,7 +1299,7 @@
 	struct wpa_eapol_key *reply;
 	u8 *rbuf, *key_mic;
 
-	mic_len = wpa_mic_len(sm->key_mgmt);
+	mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
 	hdrlen = sizeof(*reply) + mic_len + 2;
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
 				  hdrlen, &rlen, (void *) &reply);
@@ -1427,7 +1449,7 @@
 		sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, NULL,
 				     sm->ptk.kck, sm->ptk.kck_len,
 				     sm->bssid, sm->own_addr,
-				     sm->network_ctx, sm->key_mgmt);
+				     sm->network_ctx, sm->key_mgmt, NULL);
 		if (!sm->cur_pmksa)
 			sm->cur_pmksa = sa;
 	}
@@ -1586,7 +1608,7 @@
 	struct wpa_eapol_key *reply;
 	u8 *rbuf, *key_mic;
 
-	mic_len = wpa_mic_len(sm->key_mgmt);
+	mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
 	hdrlen = sizeof(*reply) + mic_len + 2;
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
 				  hdrlen, &rlen, (void *) &reply);
@@ -1600,6 +1622,8 @@
 	key_info |= ver | WPA_KEY_INFO_SECURE;
 	if (mic_len)
 		key_info |= WPA_KEY_INFO_MIC;
+	else
+		key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
 	WPA_PUT_BE16(reply->key_info, key_info);
 	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
 		WPA_PUT_BE16(reply->key_length, 0);
@@ -1628,7 +1652,7 @@
 	struct wpa_gtk_data gd;
 	const u8 *key_rsc;
 
-	if (!sm->msg_3_of_4_ok) {
+	if (!sm->msg_3_of_4_ok && !wpa_fils_is_completed(sm)) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"WPA: Group Key Handshake started prior to completion of 4-way handshake");
 		goto failed;
@@ -1695,14 +1719,15 @@
 {
 	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
 	int ok = 0;
-	size_t mic_len = wpa_mic_len(sm->key_mgmt);
+	size_t mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
 
 	os_memcpy(mic, key + 1, mic_len);
 	if (sm->tptk_set) {
 		os_memset(key + 1, 0, mic_len);
-		wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len, sm->key_mgmt,
-				  ver, buf, len, (u8 *) (key + 1));
-		if (os_memcmp_const(mic, key + 1, mic_len) != 0) {
+		if (wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len,
+				      sm->key_mgmt,
+				      ver, buf, len, (u8 *) (key + 1)) < 0 ||
+		    os_memcmp_const(mic, key + 1, mic_len) != 0) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: Invalid EAPOL-Key MIC "
 				"when using TPTK - ignoring TPTK");
@@ -1712,14 +1737,23 @@
 			sm->ptk_set = 1;
 			os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk));
 			os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+			/*
+			 * This assures the same TPTK in sm->tptk can never be
+			 * copied twice to sm->pkt as the new PTK. In
+			 * combination with the installed flag in the wpa_ptk
+			 * struct, this assures the same PTK is only installed
+			 * once.
+			 */
+			sm->renew_snonce = 1;
 		}
 	}
 
 	if (!ok && sm->ptk_set) {
 		os_memset(key + 1, 0, mic_len);
-		wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len, sm->key_mgmt,
-				  ver, buf, len, (u8 *) (key + 1));
-		if (os_memcmp_const(mic, key + 1, mic_len) != 0) {
+		if (wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len,
+				      sm->key_mgmt,
+				      ver, buf, len, (u8 *) (key + 1)) < 0 ||
+		    os_memcmp_const(mic, key + 1, mic_len) != 0) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: Invalid EAPOL-Key MIC - "
 				"dropping packet");
@@ -1766,6 +1800,8 @@
 		return -1;
 #else /* CONFIG_NO_RC4 */
 		u8 ek[32];
+
+		wpa_printf(MSG_DEBUG, "WPA: Decrypt Key Data using RC4");
 		os_memcpy(ek, key->key_iv, 16);
 		os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len);
 		if (rc4_skip(ek, 32, 256, key_data, *key_data_len)) {
@@ -1778,9 +1814,15 @@
 #endif /* CONFIG_NO_RC4 */
 	} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
 		   ver == WPA_KEY_INFO_TYPE_AES_128_CMAC ||
+		   sm->key_mgmt == WPA_KEY_MGMT_OWE ||
+		   sm->key_mgmt == WPA_KEY_MGMT_DPP ||
 		   sm->key_mgmt == WPA_KEY_MGMT_OSEN ||
 		   wpa_key_mgmt_suite_b(sm->key_mgmt)) {
 		u8 *buf;
+
+		wpa_printf(MSG_DEBUG,
+			   "WPA: Decrypt Key Data using AES-UNWRAP (KEK length %u)",
+			   (unsigned int) sm->ptk.kek_len);
 		if (*key_data_len < 8 || *key_data_len % 8) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: Unsupported AES-WRAP len %u",
@@ -1962,7 +2004,6 @@
 	u16 key_info, ver;
 	u8 *tmp = NULL;
 	int ret = -1;
-	struct wpa_peerkey *peerkey = NULL;
 	u8 *mic, *key_data;
 	size_t mic_len, keyhdrlen;
 
@@ -1970,7 +2011,7 @@
 	sm->ft_completed = 0;
 #endif /* CONFIG_IEEE80211R */
 
-	mic_len = wpa_mic_len(sm->key_mgmt);
+	mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
 	keyhdrlen = sizeof(*key) + mic_len + 2;
 
 	if (len < sizeof(*hdr) + keyhdrlen) {
@@ -2018,10 +2059,9 @@
 	 * Make a copy of the frame since we need to modify the buffer during
 	 * MAC validation and Key Data decryption.
 	 */
-	tmp = os_malloc(data_len);
+	tmp = os_memdup(buf, data_len);
 	if (tmp == NULL)
 		goto out;
-	os_memcpy(tmp, buf, data_len);
 	key = (struct wpa_eapol_key *) (tmp + sizeof(struct ieee802_1x_hdr));
 	mic = (u8 *) (key + 1);
 	key_data = mic + mic_len + 2;
@@ -2056,6 +2096,8 @@
 	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
 	    !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
 	    !wpa_key_mgmt_fils(sm->key_mgmt) &&
+	    sm->key_mgmt != WPA_KEY_MGMT_OWE &&
+	    sm->key_mgmt != WPA_KEY_MGMT_DPP &&
 	    sm->key_mgmt != WPA_KEY_MGMT_OSEN) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"WPA: Unsupported EAPOL-Key descriptor version %d",
@@ -2072,7 +2114,9 @@
 	}
 
 	if ((wpa_key_mgmt_suite_b(sm->key_mgmt) ||
-	     wpa_key_mgmt_fils(sm->key_mgmt)) &&
+	     wpa_key_mgmt_fils(sm->key_mgmt) ||
+	     sm->key_mgmt == WPA_KEY_MGMT_DPP ||
+	     sm->key_mgmt == WPA_KEY_MGMT_OWE) &&
 	    ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"RSN: Unsupported EAPOL-Key descriptor version %d (expected AKM defined = 0)",
@@ -2106,6 +2150,8 @@
 	if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
 	    !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
 	    !wpa_key_mgmt_fils(sm->key_mgmt) &&
+	    sm->key_mgmt != WPA_KEY_MGMT_OWE &&
+	    sm->key_mgmt != WPA_KEY_MGMT_DPP &&
 	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"WPA: CCMP is used, but EAPOL-Key "
@@ -2133,44 +2179,7 @@
 		goto out;
 	}
 
-#ifdef CONFIG_PEERKEY
-	for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
-		if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0)
-			break;
-	}
-
-	if (!(key_info & WPA_KEY_INFO_SMK_MESSAGE) && peerkey) {
-		if (!peerkey->initiator && peerkey->replay_counter_set &&
-		    os_memcmp(key->replay_counter, peerkey->replay_counter,
-			      WPA_REPLAY_COUNTER_LEN) <= 0) {
-			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-				"RSN: EAPOL-Key Replay Counter did not "
-				"increase (STK) - dropping packet");
-			goto out;
-		} else if (peerkey->initiator) {
-			u8 _tmp[WPA_REPLAY_COUNTER_LEN];
-			os_memcpy(_tmp, key->replay_counter,
-				  WPA_REPLAY_COUNTER_LEN);
-			inc_byte_array(_tmp, WPA_REPLAY_COUNTER_LEN);
-			if (os_memcmp(_tmp, peerkey->replay_counter,
-				      WPA_REPLAY_COUNTER_LEN) != 0) {
-				wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
-					"RSN: EAPOL-Key Replay "
-					"Counter did not match (STK) - "
-					"dropping packet");
-				goto out;
-			}
-		}
-	}
-
-	if (peerkey && peerkey->initiator && (key_info & WPA_KEY_INFO_ACK)) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-			"RSN: Ack bit in key_info from STK peer");
-		goto out;
-	}
-#endif /* CONFIG_PEERKEY */
-
-	if (!peerkey && sm->rx_replay_counter_set &&
+	if (sm->rx_replay_counter_set &&
 	    os_memcmp(key->replay_counter, sm->rx_replay_counter,
 		      WPA_REPLAY_COUNTER_LEN) <= 0) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
@@ -2179,11 +2188,13 @@
 		goto out;
 	}
 
-	if (!(key_info & (WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE))
-#ifdef CONFIG_PEERKEY
-	    && (peerkey == NULL || !peerkey->initiator)
-#endif /* CONFIG_PEERKEY */
-		) {
+	if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"WPA: Unsupported SMK bit in key_info");
+		goto out;
+	}
+
+	if (!(key_info & WPA_KEY_INFO_ACK)) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"WPA: No Ack bit in key_info");
 		goto out;
@@ -2195,17 +2206,10 @@
 		goto out;
 	}
 
-	if ((key_info & WPA_KEY_INFO_MIC) && !peerkey &&
+	if ((key_info & WPA_KEY_INFO_MIC) &&
 	    wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len))
 		goto out;
 
-#ifdef CONFIG_PEERKEY
-	if ((key_info & WPA_KEY_INFO_MIC) && peerkey &&
-	    peerkey_verify_eapol_key_mic(sm, peerkey, key, ver, tmp,
-					 data_len))
-		goto out;
-#endif /* CONFIG_PEERKEY */
-
 #ifdef CONFIG_FILS
 	if (!mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
 		if (wpa_supp_aead_decrypt(sm, tmp, data_len, &key_data_len))
@@ -2228,12 +2232,8 @@
 				"non-zero key index");
 			goto out;
 		}
-		if (peerkey) {
-			/* PeerKey 4-Way Handshake */
-			peerkey_rx_eapol_4way(sm, peerkey, key, key_info, ver,
-					      key_data, key_data_len);
-		} else if (key_info & (WPA_KEY_INFO_MIC |
-				       WPA_KEY_INFO_ENCR_KEY_DATA)) {
+		if (key_info & (WPA_KEY_INFO_MIC |
+				WPA_KEY_INFO_ENCR_KEY_DATA)) {
 			/* 3/4 4-Way Handshake */
 			wpa_supplicant_process_3_of_4(sm, key, ver, key_data,
 						      key_data_len);
@@ -2243,10 +2243,6 @@
 						      ver, key_data,
 						      key_data_len);
 		}
-	} else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
-		/* PeerKey SMK Handshake */
-		peerkey_rx_eapol_smk(sm, src_addr, key, key_data, key_data_len,
-				     key_info, ver);
 	} else {
 		if ((mic_len && (key_info & WPA_KEY_INFO_MIC)) ||
 		    (!mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA))) {
@@ -2488,13 +2484,21 @@
 	os_free(sm->ap_rsn_ie);
 	wpa_sm_drop_sa(sm);
 	os_free(sm->ctx);
-	peerkey_deinit(sm);
 #ifdef CONFIG_IEEE80211R
 	os_free(sm->assoc_resp_ies);
 #endif /* CONFIG_IEEE80211R */
 #ifdef CONFIG_TESTING_OPTIONS
 	wpabuf_free(sm->test_assoc_ie);
 #endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_FILS_SK_PFS
+	crypto_ecdh_deinit(sm->fils_ecdh);
+#endif /* CONFIG_FILS_SK_PFS */
+#ifdef CONFIG_FILS
+	wpabuf_free(sm->fils_ft_ies);
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+	crypto_ecdh_deinit(sm->owe_ecdh);
+#endif /* CONFIG_OWE */
 	os_free(sm);
 }
 
@@ -2588,7 +2592,6 @@
 {
 	eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL);
 	eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
-	peerkey_deinit(sm);
 	rsn_preauth_deinit(sm);
 	pmksa_cache_clear_current(sm);
 	if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE)
@@ -2607,6 +2610,7 @@
 	wpa_sm_drop_sa(sm);
 
 	sm->msg_3_of_4_ok = 0;
+	os_memset(sm->bssid, 0, ETH_ALEN);
 }
 
 
@@ -2626,6 +2630,8 @@
 	if (sm == NULL)
 		return;
 
+	wpa_hexdump_key(MSG_DEBUG, "WPA: Set PMK based on external data",
+			pmk, pmk_len);
 	sm->pmk_len = pmk_len;
 	os_memcpy(sm->pmk, pmk, pmk_len);
 
@@ -2638,7 +2644,7 @@
 	if (bssid) {
 		pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0,
 				bssid, sm->own_addr,
-				sm->network_ctx, sm->key_mgmt);
+				sm->network_ctx, sm->key_mgmt, NULL);
 	}
 }
 
@@ -2656,11 +2662,15 @@
 		return;
 
 	if (sm->cur_pmksa) {
+		wpa_hexdump_key(MSG_DEBUG,
+				"WPA: Set PMK based on current PMKSA",
+				sm->cur_pmksa->pmk, sm->cur_pmksa->pmk_len);
 		sm->pmk_len = sm->cur_pmksa->pmk_len;
 		os_memcpy(sm->pmk, sm->cur_pmksa->pmk, sm->pmk_len);
 	} else {
-		sm->pmk_len = PMK_LEN;
-		os_memset(sm->pmk, 0, PMK_LEN);
+		wpa_printf(MSG_DEBUG, "WPA: No current PMKSA - clear PMK");
+		sm->pmk_len = 0;
+		os_memset(sm->pmk, 0, PMK_LEN_MAX);
 	}
 }
 
@@ -2708,7 +2718,6 @@
 
 	if (config) {
 		sm->network_ctx = config->network_ctx;
-		sm->peerkey_enabled = config->peerkey_enabled;
 		sm->allowed_pairwise_cipher = config->allowed_pairwise_cipher;
 		sm->proactive_key_caching = config->proactive_key_caching;
 		sm->eap_workaround = config->eap_workaround;
@@ -2721,9 +2730,17 @@
 		sm->wpa_ptk_rekey = config->wpa_ptk_rekey;
 		sm->p2p = config->p2p;
 		sm->wpa_rsc_relaxation = config->wpa_rsc_relaxation;
+#ifdef CONFIG_FILS
+		if (config->fils_cache_id) {
+			sm->fils_cache_id_set = 1;
+			os_memcpy(sm->fils_cache_id, config->fils_cache_id,
+				  FILS_CACHE_ID_LEN);
+		} else {
+			sm->fils_cache_id_set = 0;
+		}
+#endif /* CONFIG_FILS */
 	} else {
 		sm->network_ctx = NULL;
-		sm->peerkey_enabled = 0;
 		sm->allowed_pairwise_cipher = 0;
 		sm->proactive_key_caching = 0;
 		sm->eap_workaround = 0;
@@ -2876,9 +2893,12 @@
 		    >= 0 &&
 		    rsn.capabilities & (WPA_CAPABILITY_MFPR |
 					WPA_CAPABILITY_MFPC)) {
-			ret = os_snprintf(pos, end - pos, "pmf=%d\n",
+			ret = os_snprintf(pos, end - pos, "pmf=%d\n"
+					  "mgmt_group_cipher=%s\n",
 					  (rsn.capabilities &
-					   WPA_CAPABILITY_MFPR) ? 2 : 1);
+					   WPA_CAPABILITY_MFPR) ? 2 : 1,
+					  wpa_cipher_txt(
+						  sm->mgmt_group_cipher));
 			if (os_snprintf_error(end - pos, ret))
 				return pos - buf;
 			pos += ret;
@@ -2944,11 +2964,10 @@
 		 * the correct version of the IE even if PMKSA caching is
 		 * aborted (which would remove PMKID from IE generation).
 		 */
-		sm->assoc_wpa_ie = os_malloc(*wpa_ie_len);
+		sm->assoc_wpa_ie = os_memdup(wpa_ie, *wpa_ie_len);
 		if (sm->assoc_wpa_ie == NULL)
 			return -1;
 
-		os_memcpy(sm->assoc_wpa_ie, wpa_ie, *wpa_ie_len);
 		sm->assoc_wpa_ie_len = *wpa_ie_len;
 	} else {
 		wpa_hexdump(MSG_DEBUG,
@@ -2984,11 +3003,10 @@
 		sm->assoc_wpa_ie_len = 0;
 	} else {
 		wpa_hexdump(MSG_DEBUG, "WPA: set own WPA/RSN IE", ie, len);
-		sm->assoc_wpa_ie = os_malloc(len);
+		sm->assoc_wpa_ie = os_memdup(ie, len);
 		if (sm->assoc_wpa_ie == NULL)
 			return -1;
 
-		os_memcpy(sm->assoc_wpa_ie, ie, len);
 		sm->assoc_wpa_ie_len = len;
 	}
 
@@ -3019,11 +3037,10 @@
 		sm->ap_wpa_ie_len = 0;
 	} else {
 		wpa_hexdump(MSG_DEBUG, "WPA: set AP WPA IE", ie, len);
-		sm->ap_wpa_ie = os_malloc(len);
+		sm->ap_wpa_ie = os_memdup(ie, len);
 		if (sm->ap_wpa_ie == NULL)
 			return -1;
 
-		os_memcpy(sm->ap_wpa_ie, ie, len);
 		sm->ap_wpa_ie_len = len;
 	}
 
@@ -3054,11 +3071,10 @@
 		sm->ap_rsn_ie_len = 0;
 	} else {
 		wpa_hexdump(MSG_DEBUG, "WPA: set AP RSN IE", ie, len);
-		sm->ap_rsn_ie = os_malloc(len);
+		sm->ap_rsn_ie = os_memdup(ie, len);
 		if (sm->ap_rsn_ie == NULL)
 			return -1;
 
-		os_memcpy(sm->ap_rsn_ie, ie, len);
 		sm->ap_rsn_ie_len = len;
 	}
 
@@ -3111,6 +3127,23 @@
 }
 
 
+void wpa_sm_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
+			    const u8 *pmkid, const u8 *bssid,
+			    const u8 *fils_cache_id)
+{
+	sm->cur_pmksa = pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0,
+					bssid, sm->own_addr, sm->network_ctx,
+					sm->key_mgmt, fils_cache_id);
+}
+
+
+int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid,
+			const void *network_ctx)
+{
+	return pmksa_cache_get(sm->pmksa, bssid, NULL, network_ctx) != NULL;
+}
+
+
 void wpa_sm_drop_sa(struct wpa_sm *sm)
 {
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK");
@@ -3213,27 +3246,6 @@
 #endif /* CONFIG_WNM */
 
 
-#ifdef CONFIG_PEERKEY
-int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr,
-			    const u8 *buf, size_t len)
-{
-	struct wpa_peerkey *peerkey;
-
-	for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
-		if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0)
-			break;
-	}
-
-	if (!peerkey)
-		return 0;
-
-	wpa_sm_rx_eapol(sm, src_addr, buf, len);
-
-	return 1;
-}
-#endif /* CONFIG_PEERKEY */
-
-
 #ifdef CONFIG_P2P
 
 int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf)
@@ -3278,20 +3290,29 @@
 
 
 #ifdef CONFIG_TESTING_OPTIONS
+
 void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf)
 {
 	wpabuf_free(sm->test_assoc_ie);
 	sm->test_assoc_ie = buf;
 }
+
+
+const u8 * wpa_sm_get_anonce(struct wpa_sm *sm)
+{
+	return sm->anonce;
+}
+
 #endif /* CONFIG_TESTING_OPTIONS */
 
 
 #ifdef CONFIG_FILS
 
-struct wpabuf * fils_build_auth(struct wpa_sm *sm)
+struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md)
 {
 	struct wpabuf *buf = NULL;
 	struct wpabuf *erp_msg;
+	struct wpabuf *pub = NULL;
 
 	erp_msg = eapol_sm_build_erp_reauth_start(sm->eapol);
 	if (!erp_msg && !sm->cur_pmksa) {
@@ -3319,7 +3340,28 @@
 	wpa_hexdump(MSG_DEBUG, "FILS: Generated FILS Session",
 		    sm->fils_session, FILS_SESSION_LEN);
 
-	buf = wpabuf_alloc(1000 + sm->assoc_wpa_ie_len);
+#ifdef CONFIG_FILS_SK_PFS
+	sm->fils_dh_group = dh_group;
+	if (dh_group) {
+		crypto_ecdh_deinit(sm->fils_ecdh);
+		sm->fils_ecdh = crypto_ecdh_init(dh_group);
+		if (!sm->fils_ecdh) {
+			wpa_printf(MSG_INFO,
+				   "FILS: Could not initialize ECDH with group %d",
+				   dh_group);
+			goto fail;
+		}
+		pub = crypto_ecdh_get_pubkey(sm->fils_ecdh, 1);
+		if (!pub)
+			goto fail;
+		wpa_hexdump_buf(MSG_DEBUG, "FILS: Element (DH public key)",
+				pub);
+		sm->fils_dh_elem_len = wpabuf_len(pub);
+	}
+#endif /* CONFIG_FILS_SK_PFS */
+
+	buf = wpabuf_alloc(1000 + sm->assoc_wpa_ie_len +
+			   (pub ? wpabuf_len(pub) : 0));
 	if (!buf)
 		goto fail;
 
@@ -3331,16 +3373,31 @@
 	/* Status Code */
 	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
 
-	/* TODO: Finite Cyclic Group when using PK or PFS */
-	/* TODO: Element when using PK or PFS */
+	/* TODO: FILS PK */
+#ifdef CONFIG_FILS_SK_PFS
+	if (dh_group) {
+		/* Finite Cyclic Group */
+		wpabuf_put_le16(buf, dh_group);
+		/* Element */
+		wpabuf_put_buf(buf, pub);
+	}
+#endif /* CONFIG_FILS_SK_PFS */
 
 	/* RSNE */
 	wpa_hexdump(MSG_DEBUG, "FILS: RSNE in FILS Authentication frame",
 		    sm->assoc_wpa_ie, sm->assoc_wpa_ie_len);
 	wpabuf_put_data(buf, sm->assoc_wpa_ie, sm->assoc_wpa_ie_len);
 
-	/* TODO: MDE when using FILS for FT initial association */
-	/* TODO: FTE when using FILS for FT initial association */
+	if (md) {
+		/* MDE when using FILS for FT initial association */
+		struct rsn_mdie *mdie;
+
+		wpabuf_put_u8(buf, WLAN_EID_MOBILITY_DOMAIN);
+		wpabuf_put_u8(buf, sizeof(*mdie));
+		mdie = wpabuf_put(buf, sizeof(*mdie));
+		os_memcpy(mdie->mobility_domain, md, MOBILITY_DOMAIN_ID_LEN);
+		mdie->ft_capab = 0;
+	}
 
 	/* FILS Nonce */
 	wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
@@ -3377,11 +3434,13 @@
 
 fail:
 	wpabuf_free(erp_msg);
+	wpabuf_free(pub);
 	return buf;
 }
 
 
-int fils_process_auth(struct wpa_sm *sm, const u8 *data, size_t len)
+int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
+		      size_t len)
 {
 	const u8 *pos, *end;
 	struct ieee802_11_elems elems;
@@ -3390,19 +3449,69 @@
 	u8 ick[FILS_ICK_MAX_LEN];
 	size_t ick_len;
 	int res;
+	struct wpabuf *dh_ss = NULL;
+	const u8 *g_sta = NULL;
+	size_t g_sta_len = 0;
+	const u8 *g_ap = NULL;
+	size_t g_ap_len = 0;
+	struct wpabuf *pub = NULL;
+
+	os_memcpy(sm->bssid, bssid, ETH_ALEN);
 
 	wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields",
 		    data, len);
 	pos = data;
 	end = data + len;
 
-	/* TODO: Finite Cyclic Group when using PK or PFS */
-	/* TODO: Element when using PK or PFS */
+	/* TODO: FILS PK */
+#ifdef CONFIG_FILS_SK_PFS
+	if (sm->fils_dh_group) {
+		u16 group;
+
+		/* Using FILS PFS */
+
+		/* Finite Cyclic Group */
+		if (end - pos < 2) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: No room for Finite Cyclic Group");
+			goto fail;
+		}
+		group = WPA_GET_LE16(pos);
+		pos += 2;
+		if (group != sm->fils_dh_group) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Unexpected change in Finite Cyclic Group: %u (expected %u)",
+				   group, sm->fils_dh_group);
+			goto fail;
+		}
+
+		/* Element */
+		if ((size_t) (end - pos) < sm->fils_dh_elem_len) {
+			wpa_printf(MSG_DEBUG, "FILS: No room for Element");
+			goto fail;
+		}
+
+		if (!sm->fils_ecdh) {
+			wpa_printf(MSG_DEBUG, "FILS: No ECDH state available");
+			goto fail;
+		}
+		dh_ss = crypto_ecdh_set_peerkey(sm->fils_ecdh, 1, pos,
+						sm->fils_dh_elem_len);
+		if (!dh_ss) {
+			wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed");
+			goto fail;
+		}
+		wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", dh_ss);
+		g_ap = pos;
+		g_ap_len = sm->fils_dh_elem_len;
+		pos += sm->fils_dh_elem_len;
+	}
+#endif /* CONFIG_FILS_SK_PFS */
 
 	wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos);
 	if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) {
 		wpa_printf(MSG_DEBUG, "FILS: Could not parse elements");
-		return -1;
+		goto fail;
 	}
 
 	/* RSNE */
@@ -3412,18 +3521,63 @@
 	    wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
 				 &rsn) < 0) {
 		wpa_printf(MSG_DEBUG, "FILS: No RSN element");
-		return -1;
+		goto fail;
 	}
 
 	if (!elems.fils_nonce) {
 		wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
-		return -1;
+		goto fail;
 	}
 	os_memcpy(sm->fils_anonce, elems.fils_nonce, FILS_NONCE_LEN);
 	wpa_hexdump(MSG_DEBUG, "FILS: ANonce", sm->fils_anonce, FILS_NONCE_LEN);
 
-	/* TODO: MDE when using FILS+FT */
-	/* TODO: FTE when using FILS+FT */
+	if (wpa_key_mgmt_ft(sm->key_mgmt)) {
+		struct wpa_ft_ies parse;
+
+		if (!elems.mdie || !elems.ftie) {
+			wpa_printf(MSG_DEBUG, "FILS+FT: No MDE or FTE");
+			goto fail;
+		}
+
+		if (wpa_ft_parse_ies(pos, end - pos, &parse) < 0) {
+			wpa_printf(MSG_DEBUG, "FILS+FT: Failed to parse IEs");
+			goto fail;
+		}
+
+		if (!parse.r0kh_id) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS+FT: No R0KH-ID subelem in FTE");
+			goto fail;
+		}
+		os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len);
+		sm->r0kh_id_len = parse.r0kh_id_len;
+		wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: R0KH-ID",
+				  sm->r0kh_id, sm->r0kh_id_len);
+
+		if (!parse.r1kh_id) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS+FT: No R1KH-ID subelem in FTE");
+			goto fail;
+		}
+		os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
+		wpa_hexdump(MSG_DEBUG, "FILS+FT: R1KH-ID",
+			    sm->r1kh_id, FT_R1KH_ID_LEN);
+
+		/* TODO: Check MDE and FTE payload */
+
+		wpabuf_free(sm->fils_ft_ies);
+		sm->fils_ft_ies = wpabuf_alloc(2 + elems.mdie_len +
+					       2 + elems.ftie_len);
+		if (!sm->fils_ft_ies)
+			goto fail;
+		wpabuf_put_data(sm->fils_ft_ies, elems.mdie - 2,
+				2 + elems.mdie_len);
+		wpabuf_put_data(sm->fils_ft_ies, elems.ftie - 2,
+				2 + elems.ftie_len);
+	} else {
+		wpabuf_free(sm->fils_ft_ies);
+		sm->fils_ft_ies = NULL;
+	}
 
 	/* PMKID List */
 	if (rsn.pmkid && rsn.num_pmkid > 0) {
@@ -3432,7 +3586,7 @@
 
 		if (rsn.num_pmkid != 1) {
 			wpa_printf(MSG_DEBUG, "FILS: Invalid PMKID selection");
-			return -1;
+			goto fail;
 		}
 		wpa_hexdump(MSG_DEBUG, "FILS: PMKID", rsn.pmkid, PMKID_LEN);
 		if (os_memcmp(sm->cur_pmksa->pmkid, rsn.pmkid, PMKID_LEN) != 0)
@@ -3440,7 +3594,7 @@
 			wpa_printf(MSG_DEBUG, "FILS: PMKID mismatch");
 			wpa_hexdump(MSG_DEBUG, "FILS: Expected PMKID",
 				    sm->cur_pmksa->pmkid, PMKID_LEN);
-			return -1;
+			goto fail;
 		}
 		wpa_printf(MSG_DEBUG,
 			   "FILS: Matching PMKID - continue using PMKSA caching");
@@ -3455,7 +3609,7 @@
 	/* FILS Session */
 	if (!elems.fils_session) {
 		wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
-		return -1;
+		goto fail;
 	}
 	wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session,
 		    FILS_SESSION_LEN);
@@ -3464,7 +3618,7 @@
 		wpa_printf(MSG_DEBUG, "FILS: Session mismatch");
 		wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
 			    sm->fils_session, FILS_SESSION_LEN);
-		return -1;
+		goto fail;
 	}
 
 	/* FILS Wrapped Data */
@@ -3478,7 +3632,7 @@
 		eapol_sm_process_erp_finish(sm->eapol, elems.fils_wrapped_data,
 					    elems.fils_wrapped_data_len);
 		if (eapol_sm_failed(sm->eapol))
-			return -1;
+			goto fail;
 
 		rmsk_len = ERP_MAX_KEY_LEN;
 		res = eapol_sm_get_key(sm->eapol, rmsk, rmsk_len);
@@ -3487,18 +3641,26 @@
 			res = eapol_sm_get_key(sm->eapol, rmsk, rmsk_len);
 		}
 		if (res)
-			return -1;
+			goto fail;
 
 		res = fils_rmsk_to_pmk(sm->key_mgmt, rmsk, rmsk_len,
-				       sm->fils_nonce, sm->fils_anonce, NULL, 0,
+				       sm->fils_nonce, sm->fils_anonce,
+				       dh_ss ? wpabuf_head(dh_ss) : NULL,
+				       dh_ss ? wpabuf_len(dh_ss) : 0,
 				       sm->pmk, &sm->pmk_len);
 		os_memset(rmsk, 0, sizeof(rmsk));
+
+		/* Don't use DHss in PTK derivation if PMKSA caching is not
+		 * used. */
+		wpabuf_clear_free(dh_ss);
+		dh_ss = NULL;
+
 		if (res)
-			return -1;
+			goto fail;
 
 		if (!sm->fils_erp_pmkid_set) {
 			wpa_printf(MSG_DEBUG, "FILS: PMKID not available");
-			return -1;
+			goto fail;
 		}
 		wpa_hexdump(MSG_DEBUG, "FILS: PMKID", sm->fils_erp_pmkid,
 			    PMKID_LEN);
@@ -3506,37 +3668,174 @@
 		sm->cur_pmksa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len,
 						sm->fils_erp_pmkid, NULL, 0,
 						sm->bssid, sm->own_addr,
-						sm->network_ctx, sm->key_mgmt);
+						sm->network_ctx, sm->key_mgmt,
+						NULL);
 	}
 
 	if (!sm->cur_pmksa) {
 		wpa_printf(MSG_DEBUG,
 			   "FILS: No remaining options to continue FILS authentication");
-		return -1;
+		goto fail;
 	}
 
 	if (fils_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr, sm->bssid,
-			    sm->fils_nonce, sm->fils_anonce, &sm->ptk,
-			    ick, &ick_len, sm->key_mgmt, sm->pairwise_cipher) <
-	    0) {
+			    sm->fils_nonce, sm->fils_anonce,
+			    dh_ss ? wpabuf_head(dh_ss) : NULL,
+			    dh_ss ? wpabuf_len(dh_ss) : 0,
+			    &sm->ptk, ick, &ick_len,
+			    sm->key_mgmt, sm->pairwise_cipher,
+			    sm->fils_ft, &sm->fils_ft_len) < 0) {
 		wpa_printf(MSG_DEBUG, "FILS: Failed to derive PTK");
-		return -1;
+		goto fail;
 	}
+
+	wpabuf_clear_free(dh_ss);
+	dh_ss = NULL;
+
 	sm->ptk_set = 1;
 	sm->tptk_set = 0;
 	os_memset(&sm->tptk, 0, sizeof(sm->tptk));
 
+#ifdef CONFIG_FILS_SK_PFS
+	if (sm->fils_dh_group) {
+		if (!sm->fils_ecdh) {
+			wpa_printf(MSG_INFO, "FILS: ECDH not initialized");
+			goto fail;
+		}
+		pub = crypto_ecdh_get_pubkey(sm->fils_ecdh, 1);
+		if (!pub)
+			goto fail;
+		wpa_hexdump_buf(MSG_DEBUG, "FILS: gSTA", pub);
+		g_sta = wpabuf_head(pub);
+		g_sta_len = wpabuf_len(pub);
+		if (!g_ap) {
+			wpa_printf(MSG_INFO, "FILS: gAP not available");
+			goto fail;
+		}
+		wpa_hexdump(MSG_DEBUG, "FILS: gAP", g_ap, g_ap_len);
+	}
+#endif /* CONFIG_FILS_SK_PFS */
+
 	res = fils_key_auth_sk(ick, ick_len, sm->fils_nonce,
 			       sm->fils_anonce, sm->own_addr, sm->bssid,
-			       NULL, 0, NULL, 0, /* TODO: SK+PFS */
+			       g_sta, g_sta_len, g_ap, g_ap_len,
 			       sm->key_mgmt, sm->fils_key_auth_sta,
 			       sm->fils_key_auth_ap,
 			       &sm->fils_key_auth_len);
+	wpabuf_free(pub);
 	os_memset(ick, 0, sizeof(ick));
 	return res;
+fail:
+	wpabuf_free(pub);
+	wpabuf_clear_free(dh_ss);
+	return -1;
 }
 
 
+#ifdef CONFIG_IEEE80211R
+static int fils_ft_build_assoc_req_rsne(struct wpa_sm *sm, struct wpabuf *buf)
+{
+	struct rsn_ie_hdr *rsnie;
+	u16 capab;
+	u8 *pos;
+
+	/* RSNIE[PMKR0Name/PMKR1Name] */
+	rsnie = wpabuf_put(buf, sizeof(*rsnie));
+	rsnie->elem_id = WLAN_EID_RSN;
+	WPA_PUT_LE16(rsnie->version, RSN_VERSION);
+
+	/* Group Suite Selector */
+	if (!wpa_cipher_valid_group(sm->group_cipher)) {
+		wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)",
+			   sm->group_cipher);
+		return -1;
+	}
+	pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
+	RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
+						  sm->group_cipher));
+
+	/* Pairwise Suite Count */
+	wpabuf_put_le16(buf, 1);
+
+	/* Pairwise Suite List */
+	if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
+		wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)",
+			   sm->pairwise_cipher);
+		return -1;
+	}
+	pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
+	RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
+						  sm->pairwise_cipher));
+
+	/* Authenticated Key Management Suite Count */
+	wpabuf_put_le16(buf, 1);
+
+	/* Authenticated Key Management Suite List */
+	pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
+	if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256)
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
+	else if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384)
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
+	else {
+		wpa_printf(MSG_WARNING,
+			   "FILS+FT: Invalid key management type (%d)",
+			   sm->key_mgmt);
+		return -1;
+	}
+
+	/* RSN Capabilities */
+	capab = 0;
+#ifdef CONFIG_IEEE80211W
+	if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC)
+		capab |= WPA_CAPABILITY_MFPC;
+#endif /* CONFIG_IEEE80211W */
+	wpabuf_put_le16(buf, capab);
+
+	/* PMKID Count */
+	wpabuf_put_le16(buf, 1);
+
+	/* PMKID List [PMKR1Name] */
+	wpa_hexdump_key(MSG_DEBUG, "FILS+FT: XXKey (FILS-FT)",
+			sm->fils_ft, sm->fils_ft_len);
+	wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: SSID", sm->ssid, sm->ssid_len);
+	wpa_hexdump(MSG_DEBUG, "FILS+FT: MDID",
+		    sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN);
+	wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: R0KH-ID",
+			  sm->r0kh_id, sm->r0kh_id_len);
+	if (wpa_derive_pmk_r0(sm->fils_ft, sm->fils_ft_len, sm->ssid,
+			      sm->ssid_len, sm->mobility_domain,
+			      sm->r0kh_id, sm->r0kh_id_len, sm->own_addr,
+			      sm->pmk_r0, sm->pmk_r0_name) < 0) {
+		wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMK-R0");
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0", sm->pmk_r0, PMK_LEN);
+	wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name",
+		    sm->pmk_r0_name, WPA_PMK_NAME_LEN);
+	wpa_printf(MSG_DEBUG, "FILS+FT: R1KH-ID: " MACSTR,
+		   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) < 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);
+
+#ifdef CONFIG_IEEE80211W
+	if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
+		/* Management Group Cipher Suite */
+		pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+	}
+#endif /* CONFIG_IEEE80211W */
+
+	rsnie->len = ((u8 *) wpabuf_put(buf, 0) - (u8 *) rsnie) - 2;
+	return 0;
+}
+#endif /* CONFIG_IEEE80211R */
+
+
 struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek,
 				     size_t *kek_len, const u8 **snonce,
 				     const u8 **anonce,
@@ -3548,12 +3847,30 @@
 	unsigned int i;
 
 	len = 1000;
+#ifdef CONFIG_IEEE80211R
+	if (sm->fils_ft_ies)
+		len += wpabuf_len(sm->fils_ft_ies);
+	if (wpa_key_mgmt_ft(sm->key_mgmt))
+		len += 256;
+#endif /* CONFIG_IEEE80211R */
 	for (i = 0; hlp && i < num_hlp; i++)
 		len += 10 + wpabuf_len(hlp[i]);
 	buf = wpabuf_alloc(len);
 	if (!buf)
 		return NULL;
 
+#ifdef CONFIG_IEEE80211R
+	if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->fils_ft_ies) {
+		/* MDE and FTE when using FILS+FT */
+		wpabuf_put_buf(buf, sm->fils_ft_ies);
+		/* RSNE with PMKR1Name in PMKID field */
+		if (fils_ft_build_assoc_req_rsne(sm, buf) < 0) {
+			wpabuf_free(buf);
+			return NULL;
+		}
+	}
+#endif /* CONFIG_IEEE80211R */
+
 	/* FILS Session */
 	wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
 	wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); /* Length */
@@ -3706,6 +4023,12 @@
 		return -1;
 	}
 
+	if (sm->fils_completed) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Association has already been completed for this FILS authentication - ignore unexpected retransmission");
+		return -1;
+	}
+
 	wpa_hexdump(MSG_DEBUG, "FILS: (Re)Association Response frame",
 		    resp, len);
 
@@ -3797,7 +4120,7 @@
 	os_memcpy(gd.gtk, kde.gtk + 2, kde.gtk_len - 2);
 
 	wpa_printf(MSG_DEBUG, "FILS: Set GTK to driver");
-	if (wpa_supplicant_install_gtk(sm, &gd, elems.key_delivery) < 0) {
+	if (wpa_supplicant_install_gtk(sm, &gd, elems.key_delivery, 0) < 0) {
 		wpa_printf(MSG_DEBUG, "FILS: Failed to set GTK");
 		goto fail;
 	}
@@ -3809,6 +4132,11 @@
 
 	alg = wpa_cipher_to_alg(sm->pairwise_cipher);
 	keylen = wpa_cipher_key_len(sm->pairwise_cipher);
+	if (keylen <= 0 || (unsigned int) keylen != sm->ptk.tk_len) {
+		wpa_printf(MSG_DEBUG, "FILS: TK length mismatch: %u != %lu",
+			   keylen, (long unsigned int) sm->ptk.tk_len);
+		goto fail;
+	}
 	rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
 	wpa_hexdump_key(MSG_DEBUG, "FILS: Set TK to driver",
 			sm->ptk.tk, keylen);
@@ -3825,6 +4153,7 @@
 	 * takes care of association frame encryption/decryption. */
 	/* TK is not needed anymore in supplicant */
 	os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
+	sm->ptk.tk_len = 0;
 	sm->ptk.installed = 1;
 
 	/* FILS HLP Container */
@@ -3840,6 +4169,13 @@
 	return -1;
 }
 
+
+void wpa_sm_set_reset_fils_completed(struct wpa_sm *sm, int set)
+{
+	if (sm)
+		sm->fils_completed = !!set;
+}
+
 #endif /* CONFIG_FILS */
 
 
@@ -3851,3 +4187,219 @@
 	return 0;
 #endif /* CONFIG_FILS */
 }
+
+
+#ifdef CONFIG_OWE
+
+struct wpabuf * owe_build_assoc_req(struct wpa_sm *sm, u16 group)
+{
+	struct wpabuf *ie = NULL, *pub = NULL;
+	size_t prime_len;
+
+	if (group == 19)
+		prime_len = 32;
+	else if (group == 20)
+		prime_len = 48;
+	else if (group == 21)
+		prime_len = 66;
+	else
+		return NULL;
+
+	crypto_ecdh_deinit(sm->owe_ecdh);
+	sm->owe_ecdh = crypto_ecdh_init(group);
+	if (!sm->owe_ecdh)
+		goto fail;
+	sm->owe_group = group;
+	pub = crypto_ecdh_get_pubkey(sm->owe_ecdh, 0);
+	pub = wpabuf_zeropad(pub, prime_len);
+	if (!pub)
+		goto fail;
+
+	ie = wpabuf_alloc(5 + wpabuf_len(pub));
+	if (!ie)
+		goto fail;
+	wpabuf_put_u8(ie, WLAN_EID_EXTENSION);
+	wpabuf_put_u8(ie, 1 + 2 + wpabuf_len(pub));
+	wpabuf_put_u8(ie, WLAN_EID_EXT_OWE_DH_PARAM);
+	wpabuf_put_le16(ie, group);
+	wpabuf_put_buf(ie, pub);
+	wpabuf_free(pub);
+	wpa_hexdump_buf(MSG_DEBUG, "OWE: Diffie-Hellman Parameter element",
+			ie);
+
+	return ie;
+fail:
+	wpabuf_free(pub);
+	crypto_ecdh_deinit(sm->owe_ecdh);
+	sm->owe_ecdh = NULL;
+	return NULL;
+}
+
+
+int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid,
+			   const u8 *resp_ies, size_t resp_ies_len)
+{
+	struct ieee802_11_elems elems;
+	u16 group;
+	struct wpabuf *secret, *pub, *hkey;
+	int res;
+	u8 prk[SHA512_MAC_LEN], pmkid[SHA512_MAC_LEN];
+	const char *info = "OWE Key Generation";
+	const u8 *addr[2];
+	size_t len[2];
+	size_t hash_len, prime_len;
+	struct wpa_ie_data data;
+
+	if (!resp_ies ||
+	    ieee802_11_parse_elems(resp_ies, resp_ies_len, &elems, 1) ==
+	    ParseFailed) {
+		wpa_printf(MSG_INFO,
+			   "OWE: Could not parse Association Response frame elements");
+		return -1;
+	}
+
+	if (sm->cur_pmksa && elems.rsn_ie &&
+	    wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, 2 + elems.rsn_ie_len,
+				 &data) == 0 &&
+	    data.num_pmkid == 1 && data.pmkid &&
+	    os_memcmp(sm->cur_pmksa->pmkid, data.pmkid, PMKID_LEN) == 0) {
+		wpa_printf(MSG_DEBUG, "OWE: Use PMKSA caching");
+		wpa_sm_set_pmk_from_pmksa(sm);
+		return 0;
+	}
+
+	if (!elems.owe_dh) {
+		wpa_printf(MSG_INFO,
+			   "OWE: No Diffie-Hellman Parameter element found in Association Response frame");
+		return -1;
+	}
+
+	group = WPA_GET_LE16(elems.owe_dh);
+	if (group != sm->owe_group) {
+		wpa_printf(MSG_INFO,
+			   "OWE: Unexpected Diffie-Hellman group in response: %u",
+			   group);
+		return -1;
+	}
+
+	if (!sm->owe_ecdh) {
+		wpa_printf(MSG_INFO, "OWE: No ECDH state available");
+		return -1;
+	}
+
+	if (group == 19)
+		prime_len = 32;
+	else if (group == 20)
+		prime_len = 48;
+	else if (group == 21)
+		prime_len = 66;
+	else
+		return -1;
+
+	secret = crypto_ecdh_set_peerkey(sm->owe_ecdh, 0,
+					 elems.owe_dh + 2,
+					 elems.owe_dh_len - 2);
+	secret = wpabuf_zeropad(secret, prime_len);
+	if (!secret) {
+		wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key");
+		return -1;
+	}
+	wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret);
+
+	/* prk = HKDF-extract(C | A | group, z) */
+
+	pub = crypto_ecdh_get_pubkey(sm->owe_ecdh, 0);
+	if (!pub) {
+		wpabuf_clear_free(secret);
+		return -1;
+	}
+
+	/* PMKID = Truncate-128(Hash(C | A)) */
+	addr[0] = wpabuf_head(pub);
+	len[0] = wpabuf_len(pub);
+	addr[1] = elems.owe_dh + 2;
+	len[1] = elems.owe_dh_len - 2;
+	if (group == 19) {
+		res = sha256_vector(2, addr, len, pmkid);
+		hash_len = SHA256_MAC_LEN;
+	} else if (group == 20) {
+		res = sha384_vector(2, addr, len, pmkid);
+		hash_len = SHA384_MAC_LEN;
+	} else if (group == 21) {
+		res = sha512_vector(2, addr, len, pmkid);
+		hash_len = SHA512_MAC_LEN;
+	} else {
+		res = -1;
+		hash_len = 0;
+	}
+	pub = wpabuf_zeropad(pub, prime_len);
+	if (res < 0 || !pub) {
+		wpabuf_free(pub);
+		wpabuf_clear_free(secret);
+		return -1;
+	}
+
+	hkey = wpabuf_alloc(wpabuf_len(pub) + elems.owe_dh_len - 2 + 2);
+	if (!hkey) {
+		wpabuf_free(pub);
+		wpabuf_clear_free(secret);
+		return -1;
+	}
+
+	wpabuf_put_buf(hkey, pub); /* C */
+	wpabuf_free(pub);
+	wpabuf_put_data(hkey, elems.owe_dh + 2, elems.owe_dh_len - 2); /* A */
+	wpabuf_put_le16(hkey, sm->owe_group); /* group */
+	if (group == 19)
+		res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey),
+				  wpabuf_head(secret), wpabuf_len(secret), prk);
+	else if (group == 20)
+		res = hmac_sha384(wpabuf_head(hkey), wpabuf_len(hkey),
+				  wpabuf_head(secret), wpabuf_len(secret), prk);
+	else if (group == 21)
+		res = hmac_sha512(wpabuf_head(hkey), wpabuf_len(hkey),
+				  wpabuf_head(secret), wpabuf_len(secret), prk);
+	wpabuf_clear_free(hkey);
+	wpabuf_clear_free(secret);
+	if (res < 0)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, hash_len);
+
+	/* PMK = HKDF-expand(prk, "OWE Key Generation", n) */
+
+	if (group == 19)
+		res = hmac_sha256_kdf(prk, hash_len, NULL, (const u8 *) info,
+				      os_strlen(info), sm->pmk, hash_len);
+	else if (group == 20)
+		res = hmac_sha384_kdf(prk, hash_len, NULL, (const u8 *) info,
+				      os_strlen(info), sm->pmk, hash_len);
+	else if (group == 21)
+		res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info,
+				      os_strlen(info), sm->pmk, hash_len);
+	os_memset(prk, 0, SHA512_MAC_LEN);
+	if (res < 0)
+		return -1;
+	sm->pmk_len = hash_len;
+
+	wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sm->pmk, sm->pmk_len);
+	wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN);
+	pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, pmkid, NULL, 0,
+			bssid, sm->own_addr, sm->network_ctx, sm->key_mgmt,
+			NULL);
+
+	return 0;
+}
+
+#endif /* CONFIG_OWE */
+
+
+void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id)
+{
+#ifdef CONFIG_FILS
+	if (sm && fils_cache_id) {
+		sm->fils_cache_id_set = 1;
+		os_memcpy(sm->fils_cache_id, fils_cache_id, FILS_CACHE_ID_LEN);
+	}
+#endif /* CONFIG_FILS */
+}
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index bde8c78..81c0171 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -39,9 +39,10 @@
 	u8 * (*alloc_eapol)(void *ctx, u8 type, const void *data, u16 data_len,
 			    size_t *msg_len, void **data_pos);
 	int (*add_pmkid)(void *ctx, void *network_ctx, const u8 *bssid,
-			 const u8 *pmkid);
+			 const u8 *pmkid, const u8 *fils_cache_id,
+			 const u8 *pmk, size_t pmk_len);
 	int (*remove_pmkid)(void *ctx, void *network_ctx, const u8 *bssid,
-			    const u8 *pmkid);
+			    const u8 *pmkid, const u8 *fils_cache_id);
 	void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob);
 	const struct wpa_config_blob * (*get_config_blob)(void *ctx,
 							  const char *name);
@@ -99,7 +100,6 @@
 
 struct rsn_supp_config {
 	void *network_ctx;
-	int peerkey_enabled;
 	int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */
 	int proactive_key_caching;
 	int eap_workaround;
@@ -109,6 +109,7 @@
 	int wpa_ptk_rekey;
 	int p2p;
 	int wpa_rsc_relaxation;
+	const u8 *fils_cache_id;
 };
 
 #ifndef CONFIG_NO_WPA
@@ -155,6 +156,11 @@
 struct rsn_pmksa_cache_entry *
 wpa_sm_pmksa_cache_add_entry(struct wpa_sm *sm,
 			     struct rsn_pmksa_cache_entry * entry);
+void wpa_sm_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
+			    const u8 *pmkid, const u8 *bssid,
+			    const u8 *fils_cache_id);
+int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid,
+			const void *network_ctx);
 void wpa_sm_drop_sa(struct wpa_sm *sm);
 int wpa_sm_has_ptk(struct wpa_sm *sm);
 
@@ -168,6 +174,7 @@
 void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm,
 			    const u8 *ptk_kck, size_t ptk_kck_len,
 			    const u8 *ptk_kek, size_t ptk_kek_len);
+int wpa_fils_is_completed(struct wpa_sm *sm);
 
 #else /* CONFIG_NO_WPA */
 
@@ -335,24 +342,12 @@
 {
 }
 
-#endif /* CONFIG_NO_WPA */
-
-#ifdef CONFIG_PEERKEY
-int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer);
-int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr,
-			    const u8 *buf, size_t len);
-#else /* CONFIG_PEERKEY */
-static inline int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer)
-{
-	return -1;
-}
-
-static inline int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr,
-					  const u8 *buf, size_t len)
+static inline int wpa_fils_is_completed(struct wpa_sm *sm)
 {
 	return 0;
 }
-#endif /* CONFIG_PEERKEY */
+
+#endif /* CONFIG_NO_WPA */
 
 #ifdef CONFIG_IEEE80211R
 
@@ -433,15 +428,23 @@
 
 int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf);
 void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf);
+const u8 * wpa_sm_get_anonce(struct wpa_sm *sm);
 
-struct wpabuf * fils_build_auth(struct wpa_sm *sm);
-int fils_process_auth(struct wpa_sm *sm, const u8 *data, size_t len);
+struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md);
+int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
+		      size_t len);
 struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek,
 				     size_t *kek_len, const u8 **snonce,
 				     const u8 **anonce,
 				     const struct wpabuf **hlp,
 				     unsigned int num_hlp);
 int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len);
-int wpa_fils_is_completed(struct wpa_sm *sm);
+
+struct wpabuf * owe_build_assoc_req(struct wpa_sm *sm, u16 group);
+int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid,
+			   const u8 *resp_ies, size_t resp_ies_len);
+
+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);
 
 #endif /* WPA_H */
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index d45bb45..1ff7afe 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -205,6 +205,12 @@
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
 	else if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE)
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
+#ifdef CONFIG_FILS
+	else if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256)
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
+	else if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384)
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
+#endif /* CONFIG_FILS */
 	else {
 		wpa_printf(MSG_WARNING, "FT: Invalid key management type (%d)",
 			   sm->key_mgmt);
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index b061638..e8da194 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -1,6 +1,6 @@
 /*
  * Internal WPA/RSN supplicant state machine definitions
- * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -11,7 +11,6 @@
 
 #include "utils/list.h"
 
-struct wpa_peerkey;
 struct wpa_tdls_peer;
 struct wpa_eapol_key;
 
@@ -57,7 +56,6 @@
 	int fast_reauth; /* whether EAP fast re-authentication is enabled */
 
 	void *network_ctx;
-	int peerkey_enabled;
 	int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */
 	int proactive_key_caching;
 	int eap_workaround;
@@ -94,9 +92,6 @@
 	u8 *ap_wpa_ie, *ap_rsn_ie;
 	size_t ap_wpa_ie_len, ap_rsn_ie_len;
 
-#ifdef CONFIG_PEERKEY
-	struct wpa_peerkey *peerkey;
-#endif /* CONFIG_PEERKEY */
 #ifdef CONFIG_TDLS
 	struct wpa_tdls_peer *tdls;
 	int tdls_prohibited;
@@ -154,8 +149,21 @@
 	size_t fils_key_auth_len;
 	unsigned int fils_completed:1;
 	unsigned int fils_erp_pmkid_set:1;
+	unsigned int fils_cache_id_set:1;
 	u8 fils_erp_pmkid[PMKID_LEN];
+	u8 fils_cache_id[FILS_CACHE_ID_LEN];
+	struct crypto_ecdh *fils_ecdh;
+	int fils_dh_group;
+	size_t fils_dh_elem_len;
+	struct wpabuf *fils_ft_ies;
+	u8 fils_ft[FILS_FT_MAX_LEN];
+	size_t fils_ft_len;
 #endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+	struct crypto_ecdh *owe_ecdh;
+	u16 owe_group;
+#endif /* CONFIG_OWE */
 };
 
 
@@ -228,17 +236,22 @@
 }
 
 static inline int wpa_sm_add_pmkid(struct wpa_sm *sm, void *network_ctx,
-				   const u8 *bssid, const u8 *pmkid)
+				   const u8 *bssid, const u8 *pmkid,
+				   const u8 *cache_id, const u8 *pmk,
+				   size_t pmk_len)
 {
 	WPA_ASSERT(sm->ctx->add_pmkid);
-	return sm->ctx->add_pmkid(sm->ctx->ctx, network_ctx, bssid, pmkid);
+	return sm->ctx->add_pmkid(sm->ctx->ctx, network_ctx, bssid, pmkid,
+				  cache_id, pmk, pmk_len);
 }
 
 static inline int wpa_sm_remove_pmkid(struct wpa_sm *sm, void *network_ctx,
-				      const u8 *bssid, const u8 *pmkid)
+				      const u8 *bssid, const u8 *pmkid,
+				      const u8 *cache_id)
 {
 	WPA_ASSERT(sm->ctx->remove_pmkid);
-	return sm->ctx->remove_pmkid(sm->ctx->ctx, network_ctx, bssid, pmkid);
+	return sm->ctx->remove_pmkid(sm->ctx->ctx, network_ctx, bssid, pmkid,
+				     cache_id);
 }
 
 static inline int wpa_sm_mlme_setprotection(struct wpa_sm *sm, const u8 *addr,
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index 3be3087..d649058 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -192,6 +192,14 @@
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
 #endif /* CONFIG_IEEE80211R */
 #endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+	} else if (key_mgmt & WPA_KEY_MGMT_OWE) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OWE);
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+	} else if (key_mgmt & WPA_KEY_MGMT_DPP) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP);
+#endif /* CONFIG_DPP */
 	} else {
 		wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
 			   key_mgmt);
@@ -417,44 +425,6 @@
 		return 0;
 	}
 
-#ifdef CONFIG_PEERKEY
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) {
-		ie->smk = pos + 2 + RSN_SELECTOR_LEN;
-		ie->smk_len = pos[1] - RSN_SELECTOR_LEN;
-		wpa_hexdump_key(MSG_DEBUG, "WPA: SMK in EAPOL-Key",
-				pos, pos[1] + 2);
-		return 0;
-	}
-
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) {
-		ie->nonce = pos + 2 + RSN_SELECTOR_LEN;
-		ie->nonce_len = pos[1] - RSN_SELECTOR_LEN;
-		wpa_hexdump(MSG_DEBUG, "WPA: Nonce in EAPOL-Key",
-			    pos, pos[1] + 2);
-		return 0;
-	}
-
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) {
-		ie->lifetime = pos + 2 + RSN_SELECTOR_LEN;
-		ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN;
-		wpa_hexdump(MSG_DEBUG, "WPA: Lifetime in EAPOL-Key",
-			    pos, pos[1] + 2);
-		return 0;
-	}
-
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) {
-		ie->error = pos + 2 + RSN_SELECTOR_LEN;
-		ie->error_len = pos[1] - RSN_SELECTOR_LEN;
-		wpa_hexdump(MSG_DEBUG, "WPA: Error in EAPOL-Key",
-			    pos, pos[1] + 2);
-		return 0;
-	}
-#endif /* CONFIG_PEERKEY */
-
 #ifdef CONFIG_IEEE80211W
 	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
 	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h
index fe95af0..0e72af5 100644
--- a/src/rsn_supp/wpa_ie.h
+++ b/src/rsn_supp/wpa_ie.h
@@ -21,16 +21,6 @@
 	size_t gtk_len;
 	const u8 *mac_addr;
 	size_t mac_addr_len;
-#ifdef CONFIG_PEERKEY
-	const u8 *smk;
-	size_t smk_len;
-	const u8 *nonce;
-	size_t nonce_len;
-	const u8 *lifetime;
-	size_t lifetime_len;
-	const u8 *error;
-	size_t error_len;
-#endif /* CONFIG_PEERKEY */
 #ifdef CONFIG_IEEE80211W
 	const u8 *igtk;
 	size_t igtk_len;
diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c
index 244c3cb..e66f1a9 100644
--- a/src/tls/tlsv1_client_read.c
+++ b/src/tls/tlsv1_client_read.c
@@ -685,10 +685,9 @@
 			    pos, conn->dh_p_len);
 		goto fail;
 	}
-	conn->dh_p = os_malloc(conn->dh_p_len);
+	conn->dh_p = os_memdup(pos, conn->dh_p_len);
 	if (conn->dh_p == NULL)
 		goto fail;
-	os_memcpy(conn->dh_p, pos, conn->dh_p_len);
 	pos += conn->dh_p_len;
 	wpa_hexdump(MSG_DEBUG, "TLSv1: DH p (prime)",
 		    conn->dh_p, conn->dh_p_len);
@@ -700,10 +699,9 @@
 	if (val == 0 || val > (size_t) (end - pos))
 		goto fail;
 	conn->dh_g_len = val;
-	conn->dh_g = os_malloc(conn->dh_g_len);
+	conn->dh_g = os_memdup(pos, conn->dh_g_len);
 	if (conn->dh_g == NULL)
 		goto fail;
-	os_memcpy(conn->dh_g, pos, conn->dh_g_len);
 	pos += conn->dh_g_len;
 	wpa_hexdump(MSG_DEBUG, "TLSv1: DH g (generator)",
 		    conn->dh_g, conn->dh_g_len);
@@ -717,10 +715,9 @@
 	if (val == 0 || val > (size_t) (end - pos))
 		goto fail;
 	conn->dh_ys_len = val;
-	conn->dh_ys = os_malloc(conn->dh_ys_len);
+	conn->dh_ys = os_memdup(pos, conn->dh_ys_len);
 	if (conn->dh_ys == NULL)
 		goto fail;
-	os_memcpy(conn->dh_ys, pos, conn->dh_ys_len);
 	pos += conn->dh_ys_len;
 	wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)",
 		    conn->dh_ys, conn->dh_ys_len);
diff --git a/src/tls/tlsv1_cred.c b/src/tls/tlsv1_cred.c
index 52c1ae0..842e5dd 100644
--- a/src/tls/tlsv1_cred.c
+++ b/src/tls/tlsv1_cred.c
@@ -1166,10 +1166,9 @@
 	if (hdr.length == 0)
 		return -1;
 	os_free(cred->dh_p);
-	cred->dh_p = os_malloc(hdr.length);
+	cred->dh_p = os_memdup(hdr.payload, hdr.length);
 	if (cred->dh_p == NULL)
 		return -1;
-	os_memcpy(cred->dh_p, hdr.payload, hdr.length);
 	cred->dh_p_len = hdr.length;
 	pos = hdr.payload + hdr.length;
 
@@ -1188,10 +1187,9 @@
 	if (hdr.length == 0)
 		return -1;
 	os_free(cred->dh_g);
-	cred->dh_g = os_malloc(hdr.length);
+	cred->dh_g = os_memdup(hdr.payload, hdr.length);
 	if (cred->dh_g == NULL)
 		return -1;
-	os_memcpy(cred->dh_g, hdr.payload, hdr.length);
 	cred->dh_g_len = hdr.length;
 
 	return 0;
diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c
index 0d6ee67..f80c9a3 100644
--- a/src/tls/x509v3.c
+++ b/src/tls/x509v3.c
@@ -274,13 +274,12 @@
 		 */
 	}
 	os_free(cert->public_key);
-	cert->public_key = os_malloc(hdr.length - 1);
+	cert->public_key = os_memdup(pos + 1, hdr.length - 1);
 	if (cert->public_key == NULL) {
 		wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for "
 			   "public key");
 		return -1;
 	}
-	os_memcpy(cert->public_key, pos + 1, hdr.length - 1);
 	cert->public_key_len = hdr.length - 1;
 	wpa_hexdump(MSG_MSGDUMP, "X509: subjectPublicKey",
 		    cert->public_key, cert->public_key_len);
@@ -925,10 +924,9 @@
 	/* iPAddress OCTET STRING */
 	wpa_hexdump(MSG_MSGDUMP, "X509: altName - iPAddress", pos, len);
 	os_free(name->ip);
-	name->ip = os_malloc(len);
+	name->ip = os_memdup(pos, len);
 	if (name->ip == NULL)
 		return -1;
-	os_memcpy(name->ip, pos, len);
 	name->ip_len = len;
 	return 0;
 }
@@ -1700,14 +1698,13 @@
 		return NULL;
 	}
 	os_free(cert->sign_value);
-	cert->sign_value = os_malloc(hdr.length - 1);
+	cert->sign_value = os_memdup(pos + 1, hdr.length - 1);
 	if (cert->sign_value == NULL) {
 		wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for "
 			   "signatureValue");
 		x509_certificate_free(cert);
 		return NULL;
 	}
-	os_memcpy(cert->sign_value, pos + 1, hdr.length - 1);
 	cert->sign_value_len = hdr.length - 1;
 	wpa_hexdump(MSG_MSGDUMP, "X509: signature",
 		    cert->sign_value, cert->sign_value_len);
diff --git a/src/utils/base64.c b/src/utils/base64.c
index d44f290..8eb4ba1 100644
--- a/src/utils/base64.c
+++ b/src/utils/base64.c
@@ -13,21 +13,14 @@
 
 static const unsigned char base64_table[65] =
 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const unsigned char base64_url_table[65] =
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
 
-/**
- * base64_encode - Base64 encode
- * @src: Data to be encoded
- * @len: Length of the data to be encoded
- * @out_len: Pointer to output length variable, or %NULL if not used
- * Returns: Allocated buffer of out_len bytes of encoded data,
- * or %NULL on failure
- *
- * Caller is responsible for freeing the returned buffer. Returned buffer is
- * nul terminated to make it easier to use as a C string. The nul terminator is
- * not included in out_len.
- */
-unsigned char * base64_encode(const unsigned char *src, size_t len,
-			      size_t *out_len)
+
+static unsigned char * base64_gen_encode(const unsigned char *src, size_t len,
+					 size_t *out_len,
+					 const unsigned char *table,
+					 int add_pad)
 {
 	unsigned char *out, *pos;
 	const unsigned char *end, *in;
@@ -35,7 +28,8 @@
 	int line_len;
 
 	olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
-	olen += olen / 72; /* line feeds */
+	if (add_pad)
+		olen += olen / 72; /* line feeds */
 	olen++; /* nul termination */
 	if (olen < len)
 		return NULL; /* integer overflow */
@@ -48,35 +42,35 @@
 	pos = out;
 	line_len = 0;
 	while (end - in >= 3) {
-		*pos++ = base64_table[(in[0] >> 2) & 0x3f];
-		*pos++ = base64_table[(((in[0] & 0x03) << 4) |
-				       (in[1] >> 4)) & 0x3f];
-		*pos++ = base64_table[(((in[1] & 0x0f) << 2) |
-				       (in[2] >> 6)) & 0x3f];
-		*pos++ = base64_table[in[2] & 0x3f];
+		*pos++ = table[(in[0] >> 2) & 0x3f];
+		*pos++ = table[(((in[0] & 0x03) << 4) | (in[1] >> 4)) & 0x3f];
+		*pos++ = table[(((in[1] & 0x0f) << 2) | (in[2] >> 6)) & 0x3f];
+		*pos++ = table[in[2] & 0x3f];
 		in += 3;
 		line_len += 4;
-		if (line_len >= 72) {
+		if (add_pad && line_len >= 72) {
 			*pos++ = '\n';
 			line_len = 0;
 		}
 	}
 
 	if (end - in) {
-		*pos++ = base64_table[(in[0] >> 2) & 0x3f];
+		*pos++ = table[(in[0] >> 2) & 0x3f];
 		if (end - in == 1) {
-			*pos++ = base64_table[((in[0] & 0x03) << 4) & 0x3f];
-			*pos++ = '=';
+			*pos++ = table[((in[0] & 0x03) << 4) & 0x3f];
+			if (add_pad)
+				*pos++ = '=';
 		} else {
-			*pos++ = base64_table[(((in[0] & 0x03) << 4) |
-					       (in[1] >> 4)) & 0x3f];
-			*pos++ = base64_table[((in[1] & 0x0f) << 2) & 0x3f];
+			*pos++ = table[(((in[0] & 0x03) << 4) |
+					(in[1] >> 4)) & 0x3f];
+			*pos++ = table[((in[1] & 0x0f) << 2) & 0x3f];
 		}
-		*pos++ = '=';
+		if (add_pad)
+			*pos++ = '=';
 		line_len += 4;
 	}
 
-	if (line_len)
+	if (add_pad && line_len)
 		*pos++ = '\n';
 
 	*pos = '\0';
@@ -86,26 +80,18 @@
 }
 
 
-/**
- * base64_decode - Base64 decode
- * @src: Data to be decoded
- * @len: Length of the data to be decoded
- * @out_len: Pointer to output length variable
- * Returns: Allocated buffer of out_len bytes of decoded data,
- * or %NULL on failure
- *
- * Caller is responsible for freeing the returned buffer.
- */
-unsigned char * base64_decode(const unsigned char *src, size_t len,
-			      size_t *out_len)
+static unsigned char * base64_gen_decode(const unsigned char *src, size_t len,
+					 size_t *out_len,
+					 const unsigned char *table)
 {
 	unsigned char dtable[256], *out, *pos, block[4], tmp;
 	size_t i, count, olen;
 	int pad = 0;
+	size_t extra_pad;
 
 	os_memset(dtable, 0x80, 256);
 	for (i = 0; i < sizeof(base64_table) - 1; i++)
-		dtable[base64_table[i]] = (unsigned char) i;
+		dtable[table[i]] = (unsigned char) i;
 	dtable['='] = 0;
 
 	count = 0;
@@ -114,21 +100,28 @@
 			count++;
 	}
 
-	if (count == 0 || count % 4)
+	if (count == 0)
 		return NULL;
+	extra_pad = (4 - count % 4) % 4;
 
-	olen = count / 4 * 3;
+	olen = (count + extra_pad) / 4 * 3;
 	pos = out = os_malloc(olen);
 	if (out == NULL)
 		return NULL;
 
 	count = 0;
-	for (i = 0; i < len; i++) {
-		tmp = dtable[src[i]];
+	for (i = 0; i < len + extra_pad; i++) {
+		unsigned char val;
+
+		if (i >= len)
+			val = '=';
+		else
+			val = src[i];
+		tmp = dtable[val];
 		if (tmp == 0x80)
 			continue;
 
-		if (src[i] == '=')
+		if (val == '=')
 			pad++;
 		block[count] = tmp;
 		count++;
@@ -155,3 +148,53 @@
 	*out_len = pos - out;
 	return out;
 }
+
+
+/**
+ * base64_encode - Base64 encode
+ * @src: Data to be encoded
+ * @len: Length of the data to be encoded
+ * @out_len: Pointer to output length variable, or %NULL if not used
+ * Returns: Allocated buffer of out_len bytes of encoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer. Returned buffer is
+ * nul terminated to make it easier to use as a C string. The nul terminator is
+ * not included in out_len.
+ */
+unsigned char * base64_encode(const unsigned char *src, size_t len,
+			      size_t *out_len)
+{
+	return base64_gen_encode(src, len, out_len, base64_table, 1);
+}
+
+
+unsigned char * base64_url_encode(const unsigned char *src, size_t len,
+				  size_t *out_len, int add_pad)
+{
+	return base64_gen_encode(src, len, out_len, base64_url_table, add_pad);
+}
+
+
+/**
+ * base64_decode - Base64 decode
+ * @src: Data to be decoded
+ * @len: Length of the data to be decoded
+ * @out_len: Pointer to output length variable
+ * Returns: Allocated buffer of out_len bytes of decoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer.
+ */
+unsigned char * base64_decode(const unsigned char *src, size_t len,
+			      size_t *out_len)
+{
+	return base64_gen_decode(src, len, out_len, base64_table);
+}
+
+
+unsigned char * base64_url_decode(const unsigned char *src, size_t len,
+				  size_t *out_len)
+{
+	return base64_gen_decode(src, len, out_len, base64_url_table);
+}
diff --git a/src/utils/base64.h b/src/utils/base64.h
index aa21fd0..5a72c3e 100644
--- a/src/utils/base64.h
+++ b/src/utils/base64.h
@@ -13,5 +13,9 @@
 			      size_t *out_len);
 unsigned char * base64_decode(const unsigned char *src, size_t len,
 			      size_t *out_len);
+unsigned char * base64_url_encode(const unsigned char *src, size_t len,
+				  size_t *out_len, int add_pad);
+unsigned char * base64_url_decode(const unsigned char *src, size_t len,
+				  size_t *out_len);
 
 #endif /* BASE64_H */
diff --git a/src/utils/common.h b/src/utils/common.h
index 8842864..46e96a6 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -53,6 +53,15 @@
 }
 #endif /* __APPLE__ */
 
+#ifdef __rtems__
+#include <rtems/endian.h>
+#define __BYTE_ORDER BYTE_ORDER
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#define __BIG_ENDIAN BIG_ENDIAN
+#define bswap_16 CPU_swap_u16
+#define bswap_32 CPU_swap_u32
+#endif /* __rtems__ */
+
 #ifdef CONFIG_NATIVE_WINDOWS
 #include <winsock.h>
 
@@ -331,6 +340,9 @@
 #ifndef ETH_P_RRB
 #define ETH_P_RRB 0x890D
 #endif /* ETH_P_RRB */
+#ifndef ETH_P_OUI
+#define ETH_P_OUI 0x88B7
+#endif /* ETH_P_OUI */
 
 
 #ifdef __GNUC__
diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c
index a06aae8..58519ea 100644
--- a/src/utils/http_curl.c
+++ b/src/utils/http_curl.c
@@ -486,12 +486,11 @@
 		return;
 
 	n->hash_len = ASN1_STRING_length(hash->hashValue);
-	n->hash = os_malloc(n->hash_len);
+	n->hash = os_memdup(ASN1_STRING_data(hash->hashValue), n->hash_len);
 	if (n->hash == NULL) {
 		os_free(n->alg_oid);
 		return;
 	}
-	os_memcpy(n->hash, ASN1_STRING_data(hash->hashValue), n->hash_len);
 
 	len = ASN1_STRING_length(uri);
 	n->uri = os_malloc(len + 1);
@@ -987,7 +986,7 @@
 
 	ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
 					 SSL_get_ex_data_X509_STORE_CTX_idx());
-	ssl_ctx = ssl->ctx;
+	ssl_ctx = SSL_get_SSL_CTX(ssl);
 	ctx = SSL_CTX_get_app_data(ssl_ctx);
 
 	wpa_printf(MSG_DEBUG, "curl_cb_ssl_verify, preverify_ok: %d",
@@ -1095,7 +1094,7 @@
 {
 	struct http_ctx *ctx = arg;
 	const unsigned char *p;
-	int len, status, reason;
+	int len, status, reason, res;
 	OCSP_RESPONSE *rsp;
 	OCSP_BASICRESP *basic;
 	OCSP_CERTID *id;
@@ -1200,17 +1199,36 @@
 		return 0;
 	}
 
-	id = OCSP_cert_to_id(NULL, ctx->peer_cert, ctx->peer_issuer);
+	id = OCSP_cert_to_id(EVP_sha256(), ctx->peer_cert, ctx->peer_issuer);
 	if (!id) {
-		wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier");
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Could not create OCSP certificate identifier (SHA256)");
 		OCSP_BASICRESP_free(basic);
 		OCSP_RESPONSE_free(rsp);
 		ctx->last_err = "Could not create OCSP certificate identifier";
 		return 0;
 	}
 
-	if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at,
-				   &this_update, &next_update)) {
+	res = OCSP_resp_find_status(basic, id, &status, &reason, &produced_at,
+				    &this_update, &next_update);
+	if (!res) {
+		id = OCSP_cert_to_id(NULL, ctx->peer_cert, ctx->peer_issuer);
+		if (!id) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: Could not create OCSP certificate identifier (SHA1)");
+			OCSP_BASICRESP_free(basic);
+			OCSP_RESPONSE_free(rsp);
+			ctx->last_err =
+				"Could not create OCSP certificate identifier";
+			return 0;
+		}
+
+		res = OCSP_resp_find_status(basic, id, &status, &reason,
+					    &produced_at, &this_update,
+					    &next_update);
+	}
+
+	if (!res) {
 		wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s",
 			   (ctx->ocsp == MANDATORY_OCSP) ? "" :
 			   " (OCSP not required)");
diff --git a/src/utils/json.c b/src/utils/json.c
new file mode 100644
index 0000000..eae627d
--- /dev/null
+++ b/src/utils/json.c
@@ -0,0 +1,567 @@
+/*
+ * JavaScript Object Notation (JSON) parser (RFC7159)
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "base64.h"
+#include "json.h"
+
+#define JSON_MAX_DEPTH 10
+#define JSON_MAX_TOKENS 500
+
+
+void json_escape_string(char *txt, size_t maxlen, const char *data, size_t len)
+{
+	char *end = txt + maxlen;
+	size_t i;
+
+	for (i = 0; i < len; i++) {
+		if (txt + 4 >= end)
+			break;
+
+		switch (data[i]) {
+		case '\"':
+			*txt++ = '\\';
+			*txt++ = '\"';
+			break;
+		case '\\':
+			*txt++ = '\\';
+			*txt++ = '\\';
+			break;
+		case '\n':
+			*txt++ = '\\';
+			*txt++ = 'n';
+			break;
+		case '\r':
+			*txt++ = '\\';
+			*txt++ = 'r';
+			break;
+		case '\t':
+			*txt++ = '\\';
+			*txt++ = 't';
+			break;
+		default:
+			if (data[i] >= 32 && data[i] <= 126) {
+				*txt++ = data[i];
+			} else {
+				txt += os_snprintf(txt, end - txt, "\\u%04x",
+						   data[i]);
+			}
+			break;
+		}
+	}
+
+	*txt = '\0';
+}
+
+
+static char * json_parse_string(const char **json_pos, const char *end)
+{
+	const char *pos = *json_pos;
+	char *str, *spos, *s_end;
+	size_t max_len, buf_len;
+	u8 bin[2];
+
+	pos++; /* skip starting quote */
+
+	max_len = end - pos + 1;
+	buf_len = max_len > 10 ? 10 : max_len;
+	str = os_malloc(buf_len);
+	if (!str)
+		return NULL;
+	spos = str;
+	s_end = str + buf_len;
+
+	for (; pos < end; pos++) {
+		if (buf_len < max_len && s_end - spos < 3) {
+			char *tmp;
+			int idx;
+
+			idx = spos - str;
+			buf_len *= 2;
+			if (buf_len > max_len)
+				buf_len = max_len;
+			tmp = os_realloc(str, buf_len);
+			if (!tmp)
+				goto fail;
+			str = tmp;
+			spos = str + idx;
+			s_end = str + buf_len;
+		}
+
+		switch (*pos) {
+		case '\"': /* end string */
+			*spos = '\0';
+			/* caller will move to the next position */
+			*json_pos = pos;
+			return str;
+		case '\\':
+			pos++;
+			switch (*pos) {
+			case '"':
+			case '\\':
+			case '/':
+				*spos++ = *pos;
+				break;
+			case 'n':
+				*spos++ = '\n';
+				break;
+			case 'r':
+				*spos++ = '\r';
+				break;
+			case 't':
+				*spos++ = '\t';
+				break;
+			case 'u':
+				if (end - pos < 5 ||
+				    hexstr2bin(pos + 1, bin, 2) < 0 ||
+				    bin[1] == 0x00) {
+					wpa_printf(MSG_DEBUG,
+						   "JSON: Invalid \\u escape");
+					goto fail;
+				}
+				if (bin[0] == 0x00) {
+					*spos++ = bin[1];
+				} else {
+					*spos++ = bin[0];
+					*spos++ = bin[1];
+				}
+				pos += 4;
+				break;
+			default:
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Unknown escape '%c'", *pos);
+				goto fail;
+			}
+			break;
+		default:
+			*spos++ = *pos;
+			break;
+		}
+	}
+
+fail:
+	os_free(str);
+	return NULL;
+}
+
+
+static int json_parse_number(const char **json_pos, const char *end,
+			     int *ret_val)
+{
+	const char *pos = *json_pos;
+	size_t len;
+	char *str;
+
+	for (; pos < end; pos++) {
+		if (*pos != '-' && (*pos < '0' || *pos > '9')) {
+			pos--;
+			break;
+		}
+	}
+	if (pos < *json_pos)
+		return -1;
+	len = pos - *json_pos + 1;
+	str = os_malloc(len + 1);
+	if (!str)
+		return -1;
+	os_memcpy(str, *json_pos, len);
+	str[len] = '\0';
+
+	*ret_val = atoi(str);
+	os_free(str);
+	*json_pos = pos;
+	return 0;
+}
+
+
+static int json_check_tree_state(struct json_token *token)
+{
+	if (!token)
+		return 0;
+	if (json_check_tree_state(token->child) < 0 ||
+	    json_check_tree_state(token->sibling) < 0)
+		return -1;
+	if (token->state != JSON_COMPLETED) {
+		wpa_printf(MSG_DEBUG,
+			   "JSON: Unexpected token state %d (name=%s type=%d)",
+			   token->state, token->name ? token->name : "N/A",
+			   token->type);
+		return -1;
+	}
+	return 0;
+}
+
+
+static struct json_token * json_alloc_token(unsigned int *tokens)
+{
+	(*tokens)++;
+	if (*tokens > JSON_MAX_TOKENS) {
+		wpa_printf(MSG_DEBUG, "JSON: Maximum token limit exceeded");
+		return NULL;
+	}
+	return os_zalloc(sizeof(struct json_token));
+}
+
+
+struct json_token * json_parse(const char *data, size_t data_len)
+{
+	struct json_token *root = NULL, *curr_token = NULL, *token = NULL;
+	const char *pos, *end;
+	char *str;
+	int num;
+	unsigned int depth = 0;
+	unsigned int tokens = 0;
+
+	pos = data;
+	end = data + data_len;
+
+	for (; pos < end; pos++) {
+		switch (*pos) {
+		case '[': /* start array */
+		case '{': /* start object */
+			if (!curr_token) {
+				token = json_alloc_token(&tokens);
+				if (!token)
+					goto fail;
+			} else if (curr_token->state == JSON_WAITING_VALUE) {
+				token = curr_token;
+			} else if (curr_token->parent &&
+				   curr_token->parent->type == JSON_ARRAY &&
+				   curr_token->parent->state == JSON_STARTED &&
+				   curr_token->state == JSON_EMPTY) {
+				token = curr_token;
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Invalid state for start array/object");
+				goto fail;
+			}
+			depth++;
+			if (depth > JSON_MAX_DEPTH) {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Max depth exceeded");
+				goto fail;
+			}
+			token->type = *pos == '[' ? JSON_ARRAY : JSON_OBJECT;
+			token->state = JSON_STARTED;
+			token->child = json_alloc_token(&tokens);
+			if (!token->child)
+				goto fail;
+			curr_token = token->child;
+			curr_token->parent = token;
+			curr_token->state = JSON_EMPTY;
+			break;
+		case ']': /* end array */
+		case '}': /* end object */
+			if (!curr_token || !curr_token->parent ||
+			    curr_token->parent->state != JSON_STARTED) {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Invalid state for end array/object");
+				goto fail;
+			}
+			depth--;
+			curr_token = curr_token->parent;
+			if ((*pos == ']' &&
+			     curr_token->type != JSON_ARRAY) ||
+			    (*pos == '}' &&
+			     curr_token->type != JSON_OBJECT)) {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Array/Object mismatch");
+				goto fail;
+			}
+			if (curr_token->child->state == JSON_EMPTY &&
+			    !curr_token->child->child &&
+			    !curr_token->child->sibling) {
+				/* Remove pending child token since the
+				 * array/object was empty. */
+				json_free(curr_token->child);
+				curr_token->child = NULL;
+			}
+			curr_token->state = JSON_COMPLETED;
+			break;
+		case '\"': /* string */
+			str = json_parse_string(&pos, end);
+			if (!str)
+				goto fail;
+			if (!curr_token) {
+				token = json_alloc_token(&tokens);
+				if (!token)
+					goto fail;
+				token->type = JSON_STRING;
+				token->string = str;
+				token->state = JSON_COMPLETED;
+			} else if (curr_token->parent &&
+				   curr_token->parent->type == JSON_ARRAY &&
+				   curr_token->parent->state == JSON_STARTED &&
+				   curr_token->state == JSON_EMPTY) {
+				curr_token->string = str;
+				curr_token->state = JSON_COMPLETED;
+				curr_token->type = JSON_STRING;
+				wpa_printf(MSG_MSGDUMP,
+					   "JSON: String value: '%s'",
+					   curr_token->string);
+			} else if (curr_token->state == JSON_EMPTY) {
+				curr_token->type = JSON_VALUE;
+				curr_token->name = str;
+				curr_token->state = JSON_STARTED;
+			} else if (curr_token->state == JSON_WAITING_VALUE) {
+				curr_token->string = str;
+				curr_token->state = JSON_COMPLETED;
+				curr_token->type = JSON_STRING;
+				wpa_printf(MSG_MSGDUMP,
+					   "JSON: String value: '%s' = '%s'",
+					   curr_token->name,
+					   curr_token->string);
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Invalid state for a string");
+				os_free(str);
+				goto fail;
+			}
+			break;
+		case ' ':
+		case '\t':
+		case '\r':
+		case '\n':
+			/* ignore whitespace */
+			break;
+		case ':': /* name/value separator */
+			if (!curr_token || curr_token->state != JSON_STARTED)
+				goto fail;
+			curr_token->state = JSON_WAITING_VALUE;
+			break;
+		case ',': /* member separator */
+			if (!curr_token)
+				goto fail;
+			curr_token->sibling = json_alloc_token(&tokens);
+			if (!curr_token->sibling)
+				goto fail;
+			curr_token->sibling->parent = curr_token->parent;
+			curr_token = curr_token->sibling;
+			curr_token->state = JSON_EMPTY;
+			break;
+		case 't': /* true */
+		case 'f': /* false */
+		case 'n': /* null */
+			if (!((end - pos >= 4 &&
+			       os_strncmp(pos, "true", 4) == 0) ||
+			      (end - pos >= 5 &&
+			       os_strncmp(pos, "false", 5) == 0) ||
+			      (end - pos >= 4 &&
+			       os_strncmp(pos, "null", 4) == 0))) {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Invalid literal name");
+				goto fail;
+			}
+			if (!curr_token) {
+				token = json_alloc_token(&tokens);
+				if (!token)
+					goto fail;
+				curr_token = token;
+			} else if (curr_token->state == JSON_WAITING_VALUE) {
+				wpa_printf(MSG_MSGDUMP,
+					   "JSON: Literal name: '%s' = %c",
+					   curr_token->name, *pos);
+			} else if (curr_token->parent &&
+				   curr_token->parent->type == JSON_ARRAY &&
+				   curr_token->parent->state == JSON_STARTED &&
+				   curr_token->state == JSON_EMPTY) {
+				wpa_printf(MSG_MSGDUMP,
+					   "JSON: Literal name: %c", *pos);
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Invalid state for a literal name");
+				goto fail;
+			}
+			switch (*pos) {
+			case 't':
+				curr_token->type = JSON_BOOLEAN;
+				curr_token->number = 1;
+				pos += 3;
+				break;
+			case 'f':
+				curr_token->type = JSON_BOOLEAN;
+				curr_token->number = 0;
+				pos += 4;
+				break;
+			case 'n':
+				curr_token->type = JSON_NULL;
+				pos += 3;
+				break;
+			}
+			curr_token->state = JSON_COMPLETED;
+			break;
+		case '-':
+		case '0':
+		case '1':
+		case '2':
+		case '3':
+		case '4':
+		case '5':
+		case '6':
+		case '7':
+		case '8':
+		case '9':
+			/* number */
+			if (json_parse_number(&pos, end, &num) < 0)
+				goto fail;
+			if (!curr_token) {
+				token = json_alloc_token(&tokens);
+				if (!token)
+					goto fail;
+				token->type = JSON_NUMBER;
+				token->number = num;
+				token->state = JSON_COMPLETED;
+			} else if (curr_token->state == JSON_WAITING_VALUE) {
+				curr_token->number = num;
+				curr_token->state = JSON_COMPLETED;
+				curr_token->type = JSON_NUMBER;
+				wpa_printf(MSG_MSGDUMP,
+					   "JSON: Number value: '%s' = '%d'",
+					   curr_token->name,
+					   curr_token->number);
+			} else if (curr_token->parent &&
+				   curr_token->parent->type == JSON_ARRAY &&
+				   curr_token->parent->state == JSON_STARTED &&
+				   curr_token->state == JSON_EMPTY) {
+				curr_token->number = num;
+				curr_token->state = JSON_COMPLETED;
+				curr_token->type = JSON_NUMBER;
+				wpa_printf(MSG_MSGDUMP,
+					   "JSON: Number value: %d",
+					   curr_token->number);
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Invalid state for a number");
+				goto fail;
+			}
+			break;
+		default:
+			wpa_printf(MSG_DEBUG,
+				   "JSON: Unexpected JSON character: %c", *pos);
+			goto fail;
+		}
+
+		if (!root)
+			root = token;
+		if (!curr_token)
+			curr_token = token;
+	}
+
+	if (json_check_tree_state(root) < 0) {
+		wpa_printf(MSG_DEBUG, "JSON: Incomplete token in the tree");
+		goto fail;
+	}
+
+	return root;
+fail:
+	wpa_printf(MSG_DEBUG, "JSON: Parsing failed");
+	json_free(root);
+	return NULL;
+}
+
+
+void json_free(struct json_token *json)
+{
+	if (!json)
+		return;
+	json_free(json->child);
+	json_free(json->sibling);
+	os_free(json->name);
+	os_free(json->string);
+	os_free(json);
+}
+
+
+struct json_token * json_get_member(struct json_token *json, const char *name)
+{
+	struct json_token *token, *ret = NULL;
+
+	if (!json || json->type != JSON_OBJECT)
+		return NULL;
+	/* Return last matching entry */
+	for (token = json->child; token; token = token->sibling) {
+		if (token->name && os_strcmp(token->name, name) == 0)
+			ret = token;
+	}
+	return ret;
+}
+
+
+struct wpabuf * json_get_member_base64url(struct json_token *json,
+					  const char *name)
+{
+	struct json_token *token;
+	unsigned char *buf;
+	size_t buflen;
+	struct wpabuf *ret;
+
+	token = json_get_member(json, name);
+	if (!token || token->type != JSON_STRING)
+		return NULL;
+	buf = base64_url_decode((const unsigned char *) token->string,
+				os_strlen(token->string), &buflen);
+	if (!buf)
+		return NULL;
+	ret = wpabuf_alloc_ext_data(buf, buflen);
+	if (!ret)
+		os_free(buf);
+
+	return ret;
+}
+
+
+static const char * json_type_str(enum json_type type)
+{
+	switch (type) {
+	case JSON_VALUE:
+		return "VALUE";
+	case JSON_OBJECT:
+		return "OBJECT";
+	case JSON_ARRAY:
+		return "ARRAY";
+	case JSON_STRING:
+		return "STRING";
+	case JSON_NUMBER:
+		return "NUMBER";
+	case JSON_BOOLEAN:
+		return "BOOLEAN";
+	case JSON_NULL:
+		return "NULL";
+	}
+	return "??";
+}
+
+
+static void json_print_token(struct json_token *token, int depth,
+			     char *buf, size_t buflen)
+{
+	size_t len;
+	int ret;
+
+	if (!token)
+		return;
+	len = os_strlen(buf);
+	ret = os_snprintf(buf + len, buflen - len, "[%d:%s:%s]",
+			  depth, json_type_str(token->type),
+			  token->name ? token->name : "");
+	if (os_snprintf_error(buflen - len, ret)) {
+		buf[len] = '\0';
+		return;
+	}
+	json_print_token(token->child, depth + 1, buf, buflen);
+	json_print_token(token->sibling, depth, buf, buflen);
+}
+
+
+void json_print_tree(struct json_token *root, char *buf, size_t buflen)
+{
+	buf[0] = '\0';
+	json_print_token(root, 1, buf, buflen);
+}
diff --git a/src/utils/json.h b/src/utils/json.h
new file mode 100644
index 0000000..8faa95d
--- /dev/null
+++ b/src/utils/json.h
@@ -0,0 +1,42 @@
+/*
+ * JavaScript Object Notation (JSON) parser (RFC7159)
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef JSON_H
+#define JSON_H
+
+struct json_token {
+	enum json_type {
+		JSON_VALUE,
+		JSON_OBJECT,
+		JSON_ARRAY,
+		JSON_STRING,
+		JSON_NUMBER,
+		JSON_BOOLEAN,
+		JSON_NULL,
+	} type;
+	enum json_parsing_state {
+		JSON_EMPTY,
+		JSON_STARTED,
+		JSON_WAITING_VALUE,
+		JSON_COMPLETED,
+	} state;
+	char *name;
+	char *string;
+	int number;
+	struct json_token *parent, *child, *sibling;
+};
+
+void json_escape_string(char *txt, size_t maxlen, const char *data, size_t len);
+struct json_token * json_parse(const char *data, size_t data_len);
+void json_free(struct json_token *json);
+struct json_token * json_get_member(struct json_token *json, const char *name);
+struct wpabuf * json_get_member_base64url(struct json_token *json,
+					  const char *name);
+void json_print_tree(struct json_token *root, char *buf, size_t buflen);
+
+#endif /* JSON_H */
diff --git a/src/utils/os.h b/src/utils/os.h
index e8f0b79..21ba5c3 100644
--- a/src/utils/os.h
+++ b/src/utils/os.h
@@ -614,6 +614,18 @@
  */
 int os_memcmp_const(const void *a, const void *b, size_t len);
 
+
+/**
+ * os_memdup - Allocate duplicate of passed memory chunk
+ * @src: Source buffer to duplicate
+ * @len: Length of source buffer
+ * Returns: %NULL if allocation failed, copy of src buffer otherwise
+ *
+ * This function allocates a memory block like os_malloc() would, and
+ * copies the given source buffer into it.
+ */
+void * os_memdup(const void *src, size_t len);
+
 /**
  * os_exec - Execute an external program
  * @program: Path to the program
diff --git a/src/utils/os_none.c b/src/utils/os_none.c
index 0c3214d..e74f206 100644
--- a/src/utils/os_none.c
+++ b/src/utils/os_none.c
@@ -114,6 +114,12 @@
 }
 
 
+void * os_memdup(const void *src, size_t n)
+{
+	return NULL;
+}
+
+
 #ifdef OS_NO_C_LIB_DEFINES
 void * os_malloc(size_t size)
 {
diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c
index b516cc4..3f6388d 100644
--- a/src/utils/os_unix.c
+++ b/src/utils/os_unix.c
@@ -528,6 +528,16 @@
 }
 
 
+void * os_memdup(const void *src, size_t len)
+{
+	void *r = os_malloc(len);
+
+	if (r)
+		os_memcpy(r, src, len);
+	return r;
+}
+
+
 #ifdef WPA_TRACE
 
 #if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
@@ -560,6 +570,8 @@
 		i++;
 	if (i < res && os_strcmp(func[i], "os_strdup") == 0)
 		i++;
+	if (i < res && os_strcmp(func[i], "os_memdup") == 0)
+		i++;
 
 	pos = wpa_trace_fail_func;
 
diff --git a/src/utils/os_win32.c b/src/utils/os_win32.c
index dea27b9..f9e4b30 100644
--- a/src/utils/os_win32.c
+++ b/src/utils/os_win32.c
@@ -283,3 +283,13 @@
 {
 	return -1;
 }
+
+
+void * os_memdup(const void *src, size_t len)
+{
+	void *r = os_malloc(len);
+
+	if (r)
+		os_memcpy(r, src, len);
+	return r;
+}
diff --git a/src/utils/trace.c b/src/utils/trace.c
index d72cf60..e0b5b0b 100644
--- a/src/utils/trace.c
+++ b/src/utils/trace.c
@@ -6,6 +6,10 @@
  * See README for more details.
  */
 
+#ifdef WPA_TRACE_BFD
+#define _GNU_SOURCE
+#include <link.h>
+#endif /* WPA_TRACE_BCD */
 #include "includes.h"
 
 #include "common.h"
@@ -25,6 +29,28 @@
 static char *prg_fname = NULL;
 static bfd *cached_abfd = NULL;
 static asymbol **syms = NULL;
+static unsigned long start_offset;
+static int start_offset_looked_up;
+
+
+static int callback(struct dl_phdr_info *info, size_t size, void *data)
+{
+	/*
+	 * dl_iterate_phdr(3):
+	 * "The first object visited by callback is the main program."
+	 */
+	start_offset = info->dlpi_addr;
+
+	/*
+	 * dl_iterate_phdr(3):
+	 * "The dl_iterate_phdr() function walks through the list of an
+	 *  application's shared objects and calls the function callback
+	 *  once for each object, until either all shared objects have
+	 *  been processed or callback returns a nonzero value."
+	 */
+	return 1;
+}
+
 
 static void get_prg_fname(void)
 {
@@ -160,7 +186,7 @@
 	if (abfd == NULL)
 		return;
 
-	data.pc = (bfd_hostptr_t) pc;
+	data.pc = (bfd_hostptr_t) (pc - start_offset);
 	data.found = FALSE;
 	bfd_map_over_sections(abfd, find_addr_sect, &data);
 
@@ -201,7 +227,7 @@
 	if (abfd == NULL)
 		return NULL;
 
-	data.pc = (bfd_hostptr_t) pc;
+	data.pc = (bfd_hostptr_t) (pc - start_offset);
 	data.found = FALSE;
 	bfd_map_over_sections(abfd, find_addr_sect, &data);
 
@@ -233,6 +259,11 @@
 		wpa_printf(MSG_INFO, "Failed to read symbols");
 		return;
 	}
+
+	if (!start_offset_looked_up) {
+		dl_iterate_phdr(callback, NULL);
+		start_offset_looked_up = 1;
+	}
 }
 
 
@@ -268,7 +299,7 @@
 	for (i = 0; i < btrace_num; i++) {
 		struct bfd_data data;
 
-		data.pc = (bfd_hostptr_t) btrace_res[i];
+		data.pc = (bfd_hostptr_t) (btrace_res[i] - start_offset);
 		data.found = FALSE;
 		bfd_map_over_sections(abfd, find_addr_sect, &data);
 
diff --git a/src/utils/utils_module_tests.c b/src/utils/utils_module_tests.c
index abdb79c..1b8ff82 100644
--- a/src/utils/utils_module_tests.c
+++ b/src/utils/utils_module_tests.c
@@ -16,6 +16,7 @@
 #include "utils/base64.h"
 #include "utils/ip_addr.h"
 #include "utils/eloop.h"
+#include "utils/json.h"
 #include "utils/module_tests.h"
 
 
@@ -839,6 +840,85 @@
 }
 
 
+#ifdef CONFIG_JSON
+struct json_test_data {
+	const char *json;
+	const char *tree;
+};
+
+static const struct json_test_data json_test_cases[] = {
+	{ "{}", "[1:OBJECT:]" },
+	{ "[]", "[1:ARRAY:]" },
+	{ "{", NULL },
+	{ "[", NULL },
+	{ "}", NULL },
+	{ "]", NULL },
+	{ "[[]]", "[1:ARRAY:][2:ARRAY:]" },
+	{ "{\"t\":\"test\"}", "[1:OBJECT:][2:STRING:t]" },
+	{ "{\"t\":123}", "[1:OBJECT:][2:NUMBER:t]" },
+	{ "{\"t\":true}", "[1:OBJECT:][2:BOOLEAN:t]" },
+	{ "{\"t\":false}", "[1:OBJECT:][2:BOOLEAN:t]" },
+	{ "{\"t\":null}", "[1:OBJECT:][2:NULL:t]" },
+	{ "{\"t\":truetrue}", NULL },
+	{ "\"test\"", "[1:STRING:]" },
+	{ "123", "[1:NUMBER:]" },
+	{ "true", "[1:BOOLEAN:]" },
+	{ "false", "[1:BOOLEAN:]" },
+	{ "null", "[1:NULL:]" },
+	{ "truetrue", NULL },
+	{ " {\t\n\r\"a\"\n:\r1\n,\n\"b\":3\n}\n",
+	  "[1:OBJECT:][2:NUMBER:a][2:NUMBER:b]" },
+	{ ",", NULL },
+	{ "{,}", NULL },
+	{ "[,]", NULL },
+	{ ":", NULL },
+	{ "{:}", NULL },
+	{ "[:]", NULL },
+	{ "{ \"\\u005c\" : \"\\u005c\" }", "[1:OBJECT:][2:STRING:\\]" },
+	{ "[{},{}]", "[1:ARRAY:][2:OBJECT:][2:OBJECT:]" },
+	{ "[1,2]", "[1:ARRAY:][2:NUMBER:][2:NUMBER:]" },
+	{ "[\"1\",\"2\"]", "[1:ARRAY:][2:STRING:][2:STRING:]" },
+	{ "[true,false]", "[1:ARRAY:][2:BOOLEAN:][2:BOOLEAN:]" },
+};
+#endif /* CONFIG_JSON */
+
+
+static int json_tests(void)
+{
+#ifdef CONFIG_JSON
+	unsigned int i;
+	struct json_token *root;
+	char buf[1000];
+
+	wpa_printf(MSG_INFO, "JSON tests");
+
+	for (i = 0; i < ARRAY_SIZE(json_test_cases); i++) {
+		const struct json_test_data *test = &json_test_cases[i];
+		int res = 0;
+
+		root = json_parse(test->json, os_strlen(test->json));
+		if ((root && !test->tree) || (!root && test->tree)) {
+			wpa_printf(MSG_INFO, "JSON test %u failed", i);
+			res = -1;
+		} else if (root) {
+			json_print_tree(root, buf, sizeof(buf));
+			if (os_strcmp(buf, test->tree) != 0) {
+				wpa_printf(MSG_INFO,
+					   "JSON test %u tree mismatch: %s %s",
+					   i, buf, test->tree);
+				res = -1;
+			}
+		}
+		json_free(root);
+		if (res < 0)
+			return -1;
+
+	}
+#endif /* CONFIG_JSON */
+	return 0;
+}
+
+
 int utils_module_tests(void)
 {
 	int ret = 0;
@@ -855,6 +935,7 @@
 	    wpabuf_tests() < 0 ||
 	    ip_addr_tests() < 0 ||
 	    eloop_tests() < 0 ||
+	    json_tests() < 0 ||
 	    int_array_tests() < 0)
 		ret = -1;
 
diff --git a/src/utils/uuid.c b/src/utils/uuid.c
index 0f224f9..98e43d0 100644
--- a/src/utils/uuid.c
+++ b/src/utils/uuid.c
@@ -9,6 +9,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "crypto/sha256.h"
 #include "uuid.h"
 
 int uuid_str2bin(const char *str, u8 *bin)
@@ -69,3 +70,27 @@
 			return 0;
 	return 1;
 }
+
+
+int uuid_random(u8 *uuid)
+{
+	struct os_time t;
+	u8 hash[SHA256_MAC_LEN];
+
+	/* Use HMAC-SHA256 and timestamp as context to avoid exposing direct
+	 * os_get_random() output in the UUID field. */
+	os_get_time(&t);
+	if (os_get_random(uuid, UUID_LEN) < 0 ||
+	    hmac_sha256(uuid, UUID_LEN, (const u8 *) &t, sizeof(t), hash) < 0)
+		return -1;
+
+	os_memcpy(uuid, hash, UUID_LEN);
+
+	/* Version: 4 = random */
+	uuid[6] = (4 << 4) | (uuid[6] & 0x0f);
+
+	/* Variant specified in RFC 4122 */
+	uuid[8] = 0x80 | (uuid[8] & 0x3f);
+
+	return 0;
+}
diff --git a/src/utils/uuid.h b/src/utils/uuid.h
index 5e860cb..6e20210 100644
--- a/src/utils/uuid.h
+++ b/src/utils/uuid.h
@@ -14,5 +14,6 @@
 int uuid_str2bin(const char *str, u8 *bin);
 int uuid_bin2str(const u8 *bin, char *str, size_t max_len);
 int is_nil_uuid(const u8 *uuid);
+int uuid_random(u8 *uuid);
 
 #endif /* UUID_H */
diff --git a/src/utils/xml-utils.c b/src/utils/xml-utils.c
index 4916d29..a37a92d 100644
--- a/src/utils/xml-utils.c
+++ b/src/utils/xml-utils.c
@@ -246,10 +246,8 @@
 			xml_node_create_text(ctx, tnds, NULL, "Path", uri);
 
 		val = get_val(ctx, node);
-		if (val) {
-			xml_node_create_text(ctx, tnds, NULL, "Value", val);
-			xml_node_get_text_free(ctx, val);
-		}
+		xml_node_create_text(ctx, tnds, NULL, "Value", val ? val : "");
+		xml_node_get_text_free(ctx, val);
 
 		new_uri = add_path(uri, name);
 		node_to_tnds(ctx, new_uri ? out : tnds, node, new_uri);
diff --git a/src/wps/wps.c b/src/wps/wps.c
index fade6b6..8d22827 100644
--- a/src/wps/wps.c
+++ b/src/wps/wps.c
@@ -51,12 +51,11 @@
 	}
 	if (cfg->pin) {
 		data->dev_pw_id = cfg->dev_pw_id;
-		data->dev_password = os_malloc(cfg->pin_len);
+		data->dev_password = os_memdup(cfg->pin, cfg->pin_len);
 		if (data->dev_password == NULL) {
 			os_free(data);
 			return NULL;
 		}
-		os_memcpy(data->dev_password, cfg->pin, cfg->pin_len);
 		data->dev_password_len = cfg->pin_len;
 		wpa_hexdump_key(MSG_DEBUG, "WPS: AP PIN dev_password",
 				data->dev_password, data->dev_password_len);
@@ -75,14 +74,12 @@
 
 		data->dev_pw_id = cfg->wps->ap_nfc_dev_pw_id;
 		data->dev_password =
-			os_malloc(wpabuf_len(cfg->wps->ap_nfc_dev_pw));
+			os_memdup(wpabuf_head(cfg->wps->ap_nfc_dev_pw),
+				  wpabuf_len(cfg->wps->ap_nfc_dev_pw));
 		if (data->dev_password == NULL) {
 			os_free(data);
 			return NULL;
 		}
-		os_memcpy(data->dev_password,
-			  wpabuf_head(cfg->wps->ap_nfc_dev_pw),
-			  wpabuf_len(cfg->wps->ap_nfc_dev_pw));
 		data->dev_password_len = wpabuf_len(cfg->wps->ap_nfc_dev_pw);
 		wpa_hexdump_key(MSG_DEBUG, "WPS: NFC dev_password",
 			    data->dev_password, data->dev_password_len);
@@ -124,15 +121,14 @@
 
 	if (cfg->new_ap_settings) {
 		data->new_ap_settings =
-			os_malloc(sizeof(*data->new_ap_settings));
+			os_memdup(cfg->new_ap_settings,
+				  sizeof(*data->new_ap_settings));
 		if (data->new_ap_settings == NULL) {
 			bin_clear_free(data->dev_password,
 				       data->dev_password_len);
 			os_free(data);
 			return NULL;
 		}
-		os_memcpy(data->new_ap_settings, cfg->new_ap_settings,
-			  sizeof(*data->new_ap_settings));
 	}
 
 	if (cfg->peer_addr)
diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c
index b840acd..affd6a4 100644
--- a/src/wps/wps_er.c
+++ b/src/wps/wps_er.c
@@ -322,11 +322,10 @@
 	if (!s)
 		return -1;
 
-	ap->ap_settings = os_malloc(sizeof(*ap->ap_settings));
+	ap->ap_settings = os_memdup(&s->ap_settings, sizeof(*ap->ap_settings));
 	if (ap->ap_settings == NULL)
 		return -1;
 
-	os_memcpy(ap->ap_settings, &s->ap_settings, sizeof(*ap->ap_settings));
 	wpa_printf(MSG_DEBUG, "WPS ER: Use cached AP settings");
 	return 0;
 }
@@ -1958,10 +1957,9 @@
 	}
 
 	os_free(ap->ap_settings);
-	ap->ap_settings = os_malloc(sizeof(*cred));
+	ap->ap_settings = os_memdup(cred, sizeof(*cred));
 	if (ap->ap_settings == NULL)
 		return -1;
-	os_memcpy(ap->ap_settings, cred, sizeof(*cred));
 	ap->ap_settings->cred_attr = NULL;
 	wpa_printf(MSG_DEBUG, "WPS ER: Updated local AP settings based set "
 		   "config request");
@@ -2018,10 +2016,9 @@
 	}
 
 	os_free(ap->ap_settings);
-	ap->ap_settings = os_malloc(sizeof(*cred));
+	ap->ap_settings = os_memdup(cred, sizeof(*cred));
 	if (ap->ap_settings == NULL)
 		return -1;
-	os_memcpy(ap->ap_settings, cred, sizeof(*cred));
 	ap->ap_settings->cred_attr = NULL;
 
 	if (wps_er_send_get_device_info(ap, wps_er_ap_config_m1) < 0)
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index fac8bd8..379925e 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -748,12 +748,11 @@
 		p->wildcard_uuid = 1;
 	else
 		os_memcpy(p->uuid, uuid, WPS_UUID_LEN);
-	p->pin = os_malloc(pin_len);
+	p->pin = os_memdup(pin, pin_len);
 	if (p->pin == NULL) {
 		os_free(p);
 		return -1;
 	}
-	os_memcpy(p->pin, pin, pin_len);
 	p->pin_len = pin_len;
 
 	if (timeout) {
@@ -881,6 +880,7 @@
 					const u8 *uuid, size_t *pin_len)
 {
 	struct wps_uuid_pin *pin, *found = NULL;
+	int wildcard = 0;
 
 	wps_registrar_expire_pins(reg);
 
@@ -900,7 +900,7 @@
 			    pin->wildcard_uuid == 2) {
 				wpa_printf(MSG_DEBUG, "WPS: Found a wildcard "
 					   "PIN. Assigned it for this UUID-E");
-				pin->wildcard_uuid++;
+				wildcard = 1;
 				os_memcpy(pin->uuid, uuid, WPS_UUID_LEN);
 				found = pin;
 				break;
@@ -922,6 +922,8 @@
 	}
 	*pin_len = found->pin_len;
 	found->flags |= PIN_LOCKED;
+	if (wildcard)
+		found->wildcard_uuid++;
 	return found->pin;
 }
 
@@ -1404,10 +1406,9 @@
 		return -1;
 	}
 
-	wps->dev_password = os_malloc(pin_len);
+	wps->dev_password = os_memdup(pin, pin_len);
 	if (wps->dev_password == NULL)
 		return -1;
-	os_memcpy(wps->dev_password, pin, pin_len);
 	wps->dev_password_len = pin_len;
 
 	return 0;
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 4ca4281..116270f 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -22,6 +22,27 @@
 # Set Android log name
 L_CFLAGS += -DANDROID_LOG_NAME=\"wpa_supplicant\"
 
+L_CFLAGS += -Wall -Werror
+
+# Keep sometimes uninitialized warnings
+L_CFLAGS += -Wno-error-sometimes-uninitialized
+
+# Disable incompatible pointer type warnings
+L_CFLAGS += -Wno-incompatible-pointer-types
+L_CFLAGS += -Wno-incompatible-pointer-types-discards-qualifiers
+
+# Disable extraneous parentheses warnings
+L_CFLAGS += -Wno-parentheses-equality
+
+# Disable sign compare warnings
+L_CFLAGS += -Wno-sign-compare
+
+# Disable unused function warnings
+L_CFLAGS += -Wno-unused-function
+
+# Disable unused variable warnings
+L_CFLAGS += -Wno-unused-variable
+
 # Disable unused parameter warnings
 L_CFLAGS += -Wno-unused-parameter
 
@@ -231,8 +252,6 @@
 NEED_80211_COMMON=y
 NEED_SHA256=y
 NEED_AES_SIV=y
-NEED_AES_OMAC1=y
-NEED_AES_CTR=y
 CONFIG_SAE=y
 CONFIG_AP=y
 L_CFLAGS += -DCONFIG_MESH
@@ -248,10 +267,45 @@
 NEED_DH_GROUPS=y
 endif
 
+ifdef CONFIG_DPP
+L_CFLAGS += -DCONFIG_DPP
+OBJS += src/common/dpp.c
+OBJS += dpp_supplicant.c
+NEED_AES_SIV=y
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_HMAC_SHA512_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+NEED_SHA512=y
+NEED_JSON=y
+NEED_GAS_SERVER=y
+NEED_BASE64=y
+endif
+
+ifdef CONFIG_OWE
+L_CFLAGS += -DCONFIG_OWE
+NEED_ECC=y
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_HMAC_SHA512_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+NEED_SHA512=y
+endif
+
 ifdef CONFIG_FILS
 L_CFLAGS += -DCONFIG_FILS
 NEED_SHA384=y
 NEED_AES_SIV=y
+ifdef CONFIG_FILS_SK_PFS
+L_CFLAGS += -DCONFIG_FILS_SK_PFS
+NEED_ECC=y
+endif
+endif
+
+ifdef CONFIG_MBO
+CONFIG_WNM=y
 endif
 
 ifdef CONFIG_WNM
@@ -270,10 +324,6 @@
 L_CFLAGS += -DCONFIG_TDLS_TESTING
 endif
 
-ifdef CONFIG_PEERKEY
-L_CFLAGS += -DCONFIG_PEERKEY
-endif
-
 ifdef CONFIG_PMKSA_CACHE_EXTERNAL
 L_CFLAGS += -DCONFIG_PMKSA_CACHE_EXTERNAL
 endif
@@ -282,7 +332,6 @@
 OBJS += src/rsn_supp/wpa.c
 OBJS += src/rsn_supp/preauth.c
 OBJS += src/rsn_supp/pmksa_cache.c
-OBJS += src/rsn_supp/peerkey.c
 OBJS += src/rsn_supp/wpa_ie.c
 OBJS += src/common/wpa_common.c
 NEED_AES=y
@@ -834,7 +883,8 @@
 OBJS += src/ap/ieee802_11_he.c
 endif
 endif
-ifdef CONFIG_WNM
+ifdef CONFIG_WNM_AP
+L_CFLAGS += -DCONFIG_WNM_AP
 OBJS += src/ap/wnm_ap.c
 endif
 ifdef CONFIG_MBO
@@ -875,6 +925,10 @@
 OBJS += src/ap/wps_hostapd.c
 OBJS += src/eap_server/eap_server_wsc.c
 endif
+ifdef CONFIG_DPP
+OBJS += src/ap/dpp_hostapd.c
+OBJS += src/ap/gas_query_ap.c
+endif
 ifdef CONFIG_INTERWORKING
 OBJS += src/ap/gas_serv.c
 endif
@@ -888,6 +942,10 @@
 L_CFLAGS += -DCONFIG_MBO
 endif
 
+ifdef CONFIG_TESTING_OPTIONS
+L_CFLAGS += -DCONFIG_TESTING_OPTIONS
+endif
+
 ifdef NEED_RSN_AUTHENTICATOR
 L_CFLAGS += -DCONFIG_NO_RADIUS
 NEED_AES_WRAP=y
@@ -898,9 +956,6 @@
 L_CFLAGS += -DCONFIG_IEEE80211R_AP
 OBJS += src/ap/wpa_auth_ft.c
 endif
-ifdef CONFIG_PEERKEY
-OBJS += src/ap/peerkey_auth.c
-endif
 endif
 
 ifdef CONFIG_ACS
@@ -1000,6 +1055,10 @@
 LIBS += -ldl
 LIBS_p += -ldl
 endif
+ifndef CONFIG_TLS_DEFAULT_CIPHERS
+CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW"
+endif
+L_CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
 endif
 
 ifeq ($(CONFIG_TLS), gnutls)
@@ -1160,6 +1219,12 @@
 ifdef NEED_AES_EAX
 AESOBJS += src/crypto/aes-eax.c
 NEED_AES_CTR=y
+NEED_AES_OMAC1=y
+endif
+ifdef NEED_AES_SIV
+AESOBJS += src/crypto/aes-siv.c
+NEED_AES_CTR=y
+NEED_AES_OMAC1=y
 endif
 ifdef NEED_AES_CTR
 AESOBJS += src/crypto/aes-ctr.c
@@ -1192,9 +1257,6 @@
 AESOBJS += src/crypto/aes-internal-enc.c
 endif
 endif
-ifdef NEED_AES_SIV
-AESOBJS += src/crypto/aes-siv.c
-endif
 ifdef NEED_AES
 OBJS += $(AESOBJS)
 endif
@@ -1290,6 +1352,14 @@
 L_CFLAGS += -DCONFIG_HMAC_SHA256_KDF
 SHA256OBJS += src/crypto/sha256-kdf.c
 endif
+ifdef NEED_HMAC_SHA384_KDF
+L_CFLAGS += -DCONFIG_HMAC_SHA384_KDF
+SHA256OBJS += src/crypto/sha384-kdf.c
+endif
+ifdef NEED_HMAC_SHA512_KDF
+L_CFLAGS += -DCONFIG_HMAC_SHA512_KDF
+SHA256OBJS += src/crypto/sha512-kdf.c
+endif
 OBJS += $(SHA256OBJS)
 endif
 ifdef NEED_SHA384
@@ -1299,6 +1369,13 @@
 endif
 OBJS += src/crypto/sha384-prf.c
 endif
+ifdef NEED_SHA512
+L_CFLAGS += -DCONFIG_SHA512
+ifneq ($(CONFIG_TLS), openssl)
+OBJS += src/crypto/sha512.c
+endif
+OBJS += src/crypto/sha512-prf.c
+endif
 
 ifdef NEED_DH_GROUPS
 OBJS += src/crypto/dh_groups.c
@@ -1522,6 +1599,12 @@
 L_CFLAGS += -DCONFIG_EXT_PASSWORD
 endif
 
+ifdef NEED_GAS_SERVER
+OBJS += src/common/gas_server.c
+L_CFLAGS += -DCONFIG_GAS_SERVER
+NEED_GAS=y
+endif
+
 ifdef NEED_GAS
 OBJS += src/common/gas.c
 OBJS += gas_query.c
@@ -1534,6 +1617,11 @@
 L_CFLAGS += -DCONFIG_OFFCHANNEL
 endif
 
+ifdef NEED_JSON
+OBJS += src/utils/json.c
+L_CFLAGS += -DCONFIG_JSON
+endif
+
 OBJS += src/drivers/driver_common.c
 
 OBJS += wpa_supplicant.c events.c blacklist.c wpas_glue.c scan.c
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 7bcb7e4..65205d8 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -261,8 +261,6 @@
 NEED_80211_COMMON=y
 NEED_SHA256=y
 NEED_AES_SIV=y
-NEED_AES_OMAC1=y
-NEED_AES_CTR=y
 CONFIG_SAE=y
 CONFIG_AP=y
 CFLAGS += -DCONFIG_MESH
@@ -278,10 +276,45 @@
 NEED_DH_GROUPS=y
 endif
 
+ifdef CONFIG_DPP
+CFLAGS += -DCONFIG_DPP
+OBJS += ../src/common/dpp.o
+OBJS += dpp_supplicant.o
+NEED_AES_SIV=y
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_HMAC_SHA512_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+NEED_SHA512=y
+NEED_JSON=y
+NEED_GAS_SERVER=y
+NEED_BASE64=y
+endif
+
+ifdef CONFIG_OWE
+CFLAGS += -DCONFIG_OWE
+NEED_ECC=y
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_HMAC_SHA512_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+NEED_SHA512=y
+endif
+
 ifdef CONFIG_FILS
 CFLAGS += -DCONFIG_FILS
 NEED_SHA384=y
 NEED_AES_SIV=y
+ifdef CONFIG_FILS_SK_PFS
+CFLAGS += -DCONFIG_FILS_SK_PFS
+NEED_ECC=y
+endif
+endif
+
+ifdef CONFIG_MBO
+CONFIG_WNM=y
 endif
 
 ifdef CONFIG_WNM
@@ -300,10 +333,6 @@
 CFLAGS += -DCONFIG_TDLS_TESTING
 endif
 
-ifdef CONFIG_PEERKEY
-CFLAGS += -DCONFIG_PEERKEY
-endif
-
 ifdef CONFIG_PMKSA_CACHE_EXTERNAL
 CFLAGS += -DCONFIG_PMKSA_CACHE_EXTERNAL
 endif
@@ -312,7 +341,6 @@
 OBJS += ../src/rsn_supp/wpa.o
 OBJS += ../src/rsn_supp/preauth.o
 OBJS += ../src/rsn_supp/pmksa_cache.o
-OBJS += ../src/rsn_supp/peerkey.o
 OBJS += ../src/rsn_supp/wpa_ie.o
 OBJS += ../src/common/wpa_common.o
 NEED_AES=y
@@ -810,6 +838,20 @@
 endif
 endif
 
+ifdef CONFIG_MACSEC
+CFLAGS += -DCONFIG_MACSEC
+CONFIG_IEEE8021X_EAPOL=y
+NEED_AES_ENCBLOCK=y
+NEED_AES_UNWRAP=y
+NEED_AES_WRAP=y
+NEED_AES_OMAC1=y
+OBJS += wpas_kay.o
+OBJS += ../src/pae/ieee802_1x_cp.o
+OBJS += ../src/pae/ieee802_1x_kay.o
+OBJS += ../src/pae/ieee802_1x_key.o
+OBJS += ../src/pae/ieee802_1x_secy_ops.o
+endif
+
 ifdef CONFIG_IEEE8021X_EAPOL
 # IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication)
 CFLAGS += -DIEEE8021X_EAPOL
@@ -822,19 +864,6 @@
 endif
 endif
 
-ifdef CONFIG_MACSEC
-CFLAGS += -DCONFIG_MACSEC
-NEED_AES_ENCBLOCK=y
-NEED_AES_UNWRAP=y
-NEED_AES_WRAP=y
-NEED_AES_OMAC1=y
-OBJS += wpas_kay.o
-OBJS += ../src/pae/ieee802_1x_cp.o
-OBJS += ../src/pae/ieee802_1x_kay.o
-OBJS += ../src/pae/ieee802_1x_key.o
-OBJS += ../src/pae/ieee802_1x_secy_ops.o
-endif
-
 ifdef CONFIG_AP
 NEED_EAP_COMMON=y
 NEED_RSN_AUTHENTICATOR=y
@@ -872,7 +901,8 @@
 OBJS += ../src/ap/ieee802_11_he.o
 endif
 endif
-ifdef CONFIG_WNM
+ifdef CONFIG_WNM_AP
+CFLAGS += -DCONFIG_WNM_AP
 OBJS += ../src/ap/wnm_ap.o
 endif
 ifdef CONFIG_MBO
@@ -913,6 +943,10 @@
 OBJS += ../src/ap/wps_hostapd.o
 OBJS += ../src/eap_server/eap_server_wsc.o
 endif
+ifdef CONFIG_DPP
+OBJS += ../src/ap/dpp_hostapd.o
+OBJS += ../src/ap/gas_query_ap.o
+endif
 ifdef CONFIG_INTERWORKING
 OBJS += ../src/ap/gas_serv.o
 endif
@@ -936,9 +970,6 @@
 CFLAGS += -DCONFIG_IEEE80211R_AP
 OBJS += ../src/ap/wpa_auth_ft.o
 endif
-ifdef CONFIG_PEERKEY
-OBJS += ../src/ap/peerkey_auth.o
-endif
 endif
 
 ifdef CONFIG_ACS
@@ -1043,6 +1074,10 @@
 LIBS += -ldl
 LIBS_p += -ldl
 endif
+ifndef CONFIG_TLS_DEFAULT_CIPHERS
+CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW"
+endif
+CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
 endif
 
 ifeq ($(CONFIG_TLS), gnutls)
@@ -1145,6 +1180,48 @@
 endif
 endif
 
+ifeq ($(CONFIG_TLS), linux)
+OBJS += ../src/crypto/crypto_linux.o
+OBJS_p += ../src/crypto/crypto_linux.o
+ifdef TLS_FUNCS
+OBJS += ../src/crypto/crypto_internal-rsa.o
+OBJS += ../src/crypto/tls_internal.o
+OBJS += ../src/tls/tlsv1_common.o
+OBJS += ../src/tls/tlsv1_record.o
+OBJS += ../src/tls/tlsv1_cred.o
+OBJS += ../src/tls/tlsv1_client.o
+OBJS += ../src/tls/tlsv1_client_write.o
+OBJS += ../src/tls/tlsv1_client_read.o
+OBJS += ../src/tls/tlsv1_client_ocsp.o
+OBJS += ../src/tls/asn1.o
+OBJS += ../src/tls/rsa.o
+OBJS += ../src/tls/x509v3.o
+OBJS += ../src/tls/pkcs1.o
+OBJS += ../src/tls/pkcs5.o
+OBJS += ../src/tls/pkcs8.o
+NEED_SHA256=y
+NEED_BASE64=y
+NEED_TLS_PRF=y
+ifdef CONFIG_TLSV12
+NEED_TLS_PRF_SHA256=y
+endif
+NEED_MODEXP=y
+NEED_CIPHER=y
+CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
+endif
+ifdef NEED_MODEXP
+OBJS += ../src/crypto/crypto_internal-modexp.o
+OBJS += ../src/tls/bignum.o
+CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+CFLAGS += -DLTM_FAST
+endif
+CONFIG_INTERNAL_DH_GROUP5=y
+ifdef NEED_FIPS186_2_PRF
+OBJS += ../src/crypto/fips_prf_internal.o
+OBJS += ../src/crypto/sha1-internal.o
+endif
+endif
+
 ifeq ($(CONFIG_TLS), none)
 ifdef TLS_FUNCS
 OBJS += ../src/crypto/tls_none.o
@@ -1199,11 +1276,19 @@
 endif
 
 ifdef NEED_INTERNAL_AES_WRAP
+ifneq ($(CONFIG_TLS), linux)
 AESOBJS += ../src/crypto/aes-unwrap.o
 endif
+endif
 ifdef NEED_AES_EAX
 AESOBJS += ../src/crypto/aes-eax.o
 NEED_AES_CTR=y
+NEED_AES_OMAC1=y
+endif
+ifdef NEED_AES_SIV
+AESOBJS += ../src/crypto/aes-siv.o
+NEED_AES_CTR=y
+NEED_AES_OMAC1=y
 endif
 ifdef NEED_AES_CTR
 AESOBJS += ../src/crypto/aes-ctr.o
@@ -1216,11 +1301,10 @@
 ifdef CONFIG_OPENSSL_CMAC
 CFLAGS += -DCONFIG_OPENSSL_CMAC
 else
+ifneq ($(CONFIG_TLS), linux)
 AESOBJS += ../src/crypto/aes-omac1.o
 endif
 endif
-ifdef NEED_AES_SIV
-AESOBJS += ../src/crypto/aes-siv.o
 endif
 ifdef NEED_AES_WRAP
 NEED_AES_ENC=y
@@ -1231,9 +1315,11 @@
 ifdef NEED_AES_CBC
 NEED_AES_ENC=y
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 AESOBJS += ../src/crypto/aes-cbc.o
 endif
 endif
+endif
 ifdef NEED_AES_ENC
 ifdef CONFIG_INTERNAL_AES
 AESOBJS += ../src/crypto/aes-internal-enc.o
@@ -1245,8 +1331,10 @@
 
 ifdef NEED_SHA1
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 SHA1OBJS += ../src/crypto/sha1.o
 endif
+endif
 SHA1OBJS += ../src/crypto/sha1-prf.o
 ifdef CONFIG_INTERNAL_SHA1
 SHA1OBJS += ../src/crypto/sha1-internal.o
@@ -1271,9 +1359,11 @@
 
 ifndef CONFIG_FIPS
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 MD5OBJS += ../src/crypto/md5.o
 endif
 endif
+endif
 ifdef NEED_MD5
 ifdef CONFIG_INTERNAL_MD5
 MD5OBJS += ../src/crypto/md5-internal.o
@@ -1312,8 +1402,10 @@
 ifdef NEED_SHA256
 CFLAGS += -DCONFIG_SHA256
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 SHA256OBJS += ../src/crypto/sha256.o
 endif
+endif
 SHA256OBJS += ../src/crypto/sha256-prf.o
 ifdef CONFIG_INTERNAL_SHA256
 SHA256OBJS += ../src/crypto/sha256-internal.o
@@ -1333,15 +1425,34 @@
 CFLAGS += -DCONFIG_HMAC_SHA256_KDF
 OBJS += ../src/crypto/sha256-kdf.o
 endif
+ifdef NEED_HMAC_SHA384_KDF
+CFLAGS += -DCONFIG_HMAC_SHA384_KDF
+OBJS += ../src/crypto/sha384-kdf.o
+endif
+ifdef NEED_HMAC_SHA512_KDF
+CFLAGS += -DCONFIG_HMAC_SHA512_KDF
+OBJS += ../src/crypto/sha512-kdf.o
+endif
 OBJS += $(SHA256OBJS)
 endif
 ifdef NEED_SHA384
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 OBJS += ../src/crypto/sha384.o
 endif
+endif
 CFLAGS += -DCONFIG_SHA384
 OBJS += ../src/crypto/sha384-prf.o
 endif
+ifdef NEED_SHA512
+ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
+OBJS += ../src/crypto/sha512.o
+endif
+endif
+CFLAGS += -DCONFIG_SHA512
+OBJS += ../src/crypto/sha512-prf.o
+endif
 
 ifdef NEED_DH_GROUPS
 OBJS += ../src/crypto/dh_groups.o
@@ -1590,6 +1701,12 @@
 CFLAGS += -DCONFIG_EXT_PASSWORD
 endif
 
+ifdef NEED_GAS_SERVER
+OBJS += ../src/common/gas_server.o
+CFLAGS += -DCONFIG_GAS_SERVER
+NEED_GAS=y
+endif
+
 ifdef NEED_GAS
 OBJS += ../src/common/gas.o
 OBJS += gas_query.o
@@ -1602,6 +1719,11 @@
 CFLAGS += -DCONFIG_OFFCHANNEL
 endif
 
+ifdef NEED_JSON
+OBJS += ../src/utils/json.o
+CFLAGS += -DCONFIG_JSON
+endif
+
 ifdef CONFIG_MODULE_TESTS
 CFLAGS += -DCONFIG_MODULE_TESTS
 OBJS += wpas_module_tests.o
diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config
index 1679347..70450ac 100644
--- a/wpa_supplicant/android.config
+++ b/wpa_supplicant/android.config
@@ -276,9 +276,6 @@
 # bridge interfaces (commit 'bridge: respect RFC2863 operational state')').
 #CONFIG_NO_LINUX_PACKET_SOCKET_WAR=y
 
-# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
-CONFIG_PEERKEY=y
-
 # IEEE 802.11w (management frame protection), also known as PMF
 # Driver support is also needed for IEEE 802.11w.
 CONFIG_IEEE80211W=y
@@ -304,6 +301,10 @@
 # will be used)
 #CONFIG_TLSV12=y
 
+# Select which ciphers to use by default with OpenSSL if the user does not
+# specify them.
+#CONFIG_TLS_DEFAULT_CIPHERS="DEFAULT:!EXP:!LOW"
+
 # If CONFIG_TLS=internal is used, additional library and include paths are
 # needed for LibTomMath. Alternatively, an integrated, minimal version of
 # LibTomMath can be used. See beginning of libtommath.c for details on benefits
@@ -541,4 +542,8 @@
 # channels (experimental)
 #CONFIG_BGSCAN_LEARN=y
 
+# Opportunistic Wireless Encryption (OWE)
+# Experimental implementation of draft-harkins-owe-07.txt
+#CONFIG_OWE=y
+
 include $(wildcard $(LOCAL_PATH)/android_config_*.inc)
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 1721d6a..6668d58 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -46,16 +46,35 @@
 
 #ifdef CONFIG_IEEE80211N
 static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s,
+			     struct wpa_ssid *ssid,
 			     struct hostapd_config *conf,
 			     struct hostapd_hw_modes *mode)
 {
 #ifdef CONFIG_P2P
 	u8 center_chan = 0;
 	u8 channel = conf->channel;
+#endif /* CONFIG_P2P */
 
 	if (!conf->secondary_channel)
 		goto no_vht;
 
+	/* Use the maximum oper channel width if it's given. */
+	if (ssid->max_oper_chwidth)
+		conf->vht_oper_chwidth = ssid->max_oper_chwidth;
+
+	ieee80211_freq_to_chan(ssid->vht_center_freq2,
+			       &conf->vht_oper_centr_freq_seg1_idx);
+
+	if (!ssid->p2p_group) {
+		if (!ssid->vht_center_freq1 ||
+		    conf->vht_oper_chwidth == VHT_CHANWIDTH_USE_HT)
+			goto no_vht;
+		ieee80211_freq_to_chan(ssid->vht_center_freq1,
+				       &conf->vht_oper_centr_freq_seg0_idx);
+		return;
+	}
+
+#ifdef CONFIG_P2P
 	switch (conf->vht_oper_chwidth) {
 	case VHT_CHANWIDTH_80MHZ:
 	case VHT_CHANWIDTH_80P80MHZ:
@@ -84,14 +103,11 @@
 
 	conf->vht_oper_centr_freq_seg0_idx = center_chan;
 	return;
+#endif /* CONFIG_P2P */
 
 no_vht:
 	conf->vht_oper_centr_freq_seg0_idx =
-		channel + conf->secondary_channel * 2;
-#else /* CONFIG_P2P */
-	conf->vht_oper_centr_freq_seg0_idx =
 		conf->channel + conf->secondary_channel * 2;
-#endif /* CONFIG_P2P */
 	conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT;
 }
 #endif /* CONFIG_IEEE80211N */
@@ -131,27 +147,37 @@
 		}
 
 #ifdef CONFIG_HT_OVERRIDES
-		if (ssid->disable_ht) {
+		if (ssid->disable_ht)
+			ssid->ht = 0;
+#endif /* CONFIG_HT_OVERRIDES */
+
+		if (!ssid->ht) {
 			conf->ieee80211n = 0;
 			conf->ht_capab = 0;
 			no_ht = 1;
 		}
-#endif /* CONFIG_HT_OVERRIDES */
 
 		if (!no_ht && mode && mode->ht_capab) {
 			conf->ieee80211n = 1;
 #ifdef CONFIG_P2P
-			if (conf->hw_mode == HOSTAPD_MODE_IEEE80211A &&
+			if (ssid->p2p_group &&
+			    conf->hw_mode == HOSTAPD_MODE_IEEE80211A &&
 			    (mode->ht_capab &
 			     HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
 			    ssid->ht40)
 				conf->secondary_channel =
 					wpas_p2p_get_ht40_mode(wpa_s, mode,
 							       conf->channel);
+#endif /* CONFIG_P2P */
+
+			if (!ssid->p2p_group &&
+			    (mode->ht_capab &
+			     HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+				conf->secondary_channel = ssid->ht40;
+
 			if (conf->secondary_channel)
 				conf->ht_capab |=
 					HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
-#endif /* CONFIG_P2P */
 
 			/*
 			 * white-list capabilities that won't cause issues
@@ -169,7 +195,7 @@
 			if (mode->vht_capab && ssid->vht) {
 				conf->ieee80211ac = 1;
 				conf->vht_capab |= mode->vht_capab;
-				wpas_conf_ap_vht(wpa_s, conf, mode);
+				wpas_conf_ap_vht(wpa_s, ssid, conf, mode);
 			}
 		}
 	}
@@ -318,17 +344,34 @@
 		for (i = 0; i < NUM_WEP_KEYS; i++) {
 			if (ssid->wep_key_len[i] == 0)
 				continue;
-			wep->key[i] = os_malloc(ssid->wep_key_len[i]);
+			wep->key[i] = os_memdup(ssid->wep_key[i],
+						ssid->wep_key_len[i]);
 			if (wep->key[i] == NULL)
 				return -1;
-			os_memcpy(wep->key[i], ssid->wep_key[i],
-				  ssid->wep_key_len[i]);
 			wep->len[i] = ssid->wep_key_len[i];
 		}
 		wep->idx = ssid->wep_tx_keyidx;
 		wep->keys_set = 1;
 	}
 
+	if (wpa_s->conf->go_interworking) {
+		wpa_printf(MSG_DEBUG,
+			   "P2P: Enable Interworking with access_network_type: %d",
+			   wpa_s->conf->go_access_network_type);
+		bss->interworking = wpa_s->conf->go_interworking;
+		bss->access_network_type = wpa_s->conf->go_access_network_type;
+		bss->internet = wpa_s->conf->go_internet;
+		if (wpa_s->conf->go_venue_group) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Venue group: %d  Venue type: %d",
+				   wpa_s->conf->go_venue_group,
+				   wpa_s->conf->go_venue_type);
+			bss->venue_group = wpa_s->conf->go_venue_group;
+			bss->venue_type = wpa_s->conf->go_venue_type;
+			bss->venue_info_set = 1;
+		}
+	}
+
 	if (ssid->ap_max_inactivity)
 		bss->ap_max_inactivity = ssid->ap_max_inactivity;
 
@@ -694,13 +737,6 @@
 		return -1;
 	}
 
-	/* Use the maximum oper channel width if it's given. */
-	if (ssid->max_oper_chwidth)
-		conf->vht_oper_chwidth = ssid->max_oper_chwidth;
-
-	ieee80211_freq_to_chan(ssid->vht_center_freq2,
-			       &conf->vht_oper_centr_freq_seg1_idx);
-
 	os_memcpy(wpa_s->ap_iface->conf->wmm_ac_params,
 		  wpa_s->conf->wmm_ac_params,
 		  sizeof(wpa_s->conf->wmm_ac_params));
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 914bd5d..708b58a 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -1317,3 +1317,19 @@
 	*rates = r;
 	return len;
 }
+
+
+#ifdef CONFIG_FILS
+const u8 * wpa_bss_get_fils_cache_id(struct wpa_bss *bss)
+{
+	const u8 *ie;
+
+	if (bss) {
+		ie = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION);
+		if (ie && ie[1] >= 4 && WPA_GET_LE16(ie + 2) & BIT(7))
+			return ie + 4;
+	}
+
+	return NULL;
+}
+#endif /* CONFIG_FILS */
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index 84505fa..37d9fb6 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -145,6 +145,7 @@
 int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates);
 struct wpa_bss_anqp * wpa_bss_anqp_alloc(void);
 int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss);
+const u8 * wpa_bss_get_fils_cache_id(struct wpa_bss *bss);
 
 static inline int bss_is_dmg(const struct wpa_bss *bss)
 {
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 58ecf19..a0d480e 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -397,6 +397,50 @@
 #endif /* NO_CONFIG_WRITE */
 
 
+static int wpa_config_parse_bssid_hint(const struct parse_data *data,
+				       struct wpa_ssid *ssid, int line,
+				       const char *value)
+{
+	if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 ||
+	    os_strcmp(value, "any") == 0) {
+		ssid->bssid_hint_set = 0;
+		wpa_printf(MSG_MSGDUMP, "BSSID hint any");
+		return 0;
+	}
+	if (hwaddr_aton(value, ssid->bssid_hint)) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid BSSID hint '%s'.",
+			   line, value);
+		return -1;
+	}
+	ssid->bssid_hint_set = 1;
+	wpa_hexdump(MSG_MSGDUMP, "BSSID hint", ssid->bssid_hint, ETH_ALEN);
+	return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_bssid_hint(const struct parse_data *data,
+					  struct wpa_ssid *ssid)
+{
+	char *value;
+	int res;
+
+	if (!ssid->bssid_hint_set)
+		return NULL;
+
+	value = os_malloc(20);
+	if (!value)
+		return NULL;
+	res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->bssid_hint));
+	if (os_snprintf_error(20, res)) {
+		os_free(value);
+		return NULL;
+	}
+	return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
 static int wpa_config_parse_bssid_blacklist(const struct parse_data *data,
 					    struct wpa_ssid *ssid, int line,
 					    const char *value)
@@ -732,6 +776,14 @@
 			val |= WPA_KEY_MGMT_FT_FILS_SHA384;
 #endif /* CONFIG_IEEE80211R */
 #endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+		else if (os_strcmp(start, "OWE") == 0)
+			val |= WPA_KEY_MGMT_OWE;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+		else if (os_strcmp(start, "DPP") == 0)
+			val |= WPA_KEY_MGMT_DPP;
+#endif /* CONFIG_DPP */
 		else {
 			wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
 				   line, start);
@@ -934,6 +986,47 @@
 	}
 #endif /* CONFIG_SUITEB192 */
 
+#ifdef CONFIG_FILS
+	if (ssid->key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
+		ret = os_snprintf(pos, end - pos, "%sFILS-SHA256",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+	if (ssid->key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
+		ret = os_snprintf(pos, end - pos, "%sFILS-SHA384",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+#ifdef CONFIG_IEEE80211R
+	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
+		ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA256",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
+		ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA384",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_FILS */
+
 	if (pos == buf) {
 		os_free(buf);
 		buf = NULL;
@@ -1055,6 +1148,40 @@
 #endif /* NO_CONFIG_WRITE */
 
 
+static int wpa_config_parse_group_mgmt(const struct parse_data *data,
+				       struct wpa_ssid *ssid, int line,
+				       const char *value)
+{
+	int val;
+
+	val = wpa_config_parse_cipher(line, value);
+	if (val == -1)
+		return -1;
+
+	if (val & ~WPA_ALLOWED_GROUP_MGMT_CIPHERS) {
+		wpa_printf(MSG_ERROR,
+			   "Line %d: not allowed group management cipher (0x%x).",
+			   line, val);
+		return -1;
+	}
+
+	if (ssid->group_mgmt_cipher == val)
+		return 1;
+	wpa_printf(MSG_MSGDUMP, "group_mgmt: 0x%x", val);
+	ssid->group_mgmt_cipher = val;
+	return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_group_mgmt(const struct parse_data *data,
+					  struct wpa_ssid *ssid)
+{
+	return wpa_config_write_cipher(ssid->group_mgmt_cipher);
+}
+#endif /* NO_CONFIG_WRITE */
+
+
 static int wpa_config_parse_auth_alg(const struct parse_data *data,
 				     struct wpa_ssid *ssid, int line,
 				     const char *value)
@@ -1892,6 +2019,24 @@
 #endif /* CONFIG_MACSEC */
 
 
+static int wpa_config_parse_peerkey(const struct parse_data *data,
+				    struct wpa_ssid *ssid, int line,
+				    const char *value)
+{
+	wpa_printf(MSG_INFO, "NOTE: Obsolete peerkey parameter ignored");
+	return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_peerkey(const struct parse_data *data,
+				       struct wpa_ssid *ssid)
+{
+	return NULL;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
 /* Helper macros for network block parser */
 
 #ifdef OFFSET
@@ -1983,20 +2128,28 @@
 	{ STR_RANGE(ssid, 0, SSID_MAX_LEN) },
 	{ INT_RANGE(scan_ssid, 0, 1) },
 	{ FUNC(bssid) },
+	{ FUNC(bssid_hint) },
 	{ FUNC(bssid_blacklist) },
 	{ FUNC(bssid_whitelist) },
 	{ FUNC_KEY(psk) },
 	{ INT(mem_only_psk) },
+	{ STR_KEY(sae_password) },
 	{ FUNC(proto) },
 	{ FUNC(key_mgmt) },
 	{ INT(bg_scan_period) },
 	{ FUNC(pairwise) },
 	{ FUNC(group) },
+	{ FUNC(group_mgmt) },
 	{ FUNC(auth_alg) },
 	{ FUNC(scan_freq) },
 	{ FUNC(freq_list) },
+	{ INT_RANGE(ht, 0, 1) },
+	{ INT_RANGE(vht, 0, 1) },
+	{ INT_RANGE(ht40, -1, 1) },
 	{ INT_RANGE(max_oper_chwidth, VHT_CHANWIDTH_USE_HT,
 		    VHT_CHANWIDTH_80P80MHZ) },
+	{ INT(vht_center_freq1) },
+	{ INT(vht_center_freq2) },
 #ifdef IEEE8021X_EAPOL
 	{ FUNC(eap) },
 	{ STR_LENe(identity) },
@@ -2057,6 +2210,7 @@
 #ifdef CONFIG_MESH
 	{ INT_RANGE(mode, 0, 5) },
 	{ INT_RANGE(no_auto_peer, 0, 1) },
+	{ INT_RANGE(mesh_rssi_threshold, -255, 1) },
 #else /* CONFIG_MESH */
 	{ INT_RANGE(mode, 0, 4) },
 #endif /* CONFIG_MESH */
@@ -2066,7 +2220,7 @@
 #ifdef CONFIG_IEEE80211W
 	{ INT_RANGE(ieee80211w, 0, 2) },
 #endif /* CONFIG_IEEE80211W */
-	{ INT_RANGE(peerkey, 0, 1) },
+	{ FUNC(peerkey) /* obsolete - removed */ },
 	{ INT_RANGE(mixed_cell, 0, 1) },
 	{ INT_RANGE(frequency, 0, 65000) },
 	{ INT_RANGE(fixed_freq, 0, 1) },
@@ -2138,6 +2292,14 @@
 	{ INT_RANGE(mac_addr, 0, 2) },
 	{ INT_RANGE(pbss, 0, 2) },
 	{ INT_RANGE(wps_disabled, 0, 1) },
+	{ INT_RANGE(fils_dh_group, 0, 65535) },
+#ifdef CONFIG_DPP
+	{ STR(dpp_connector) },
+	{ STR_LEN(dpp_netaccesskey) },
+	{ INT(dpp_netaccesskey_expiry) },
+	{ STR_LEN(dpp_csign) },
+#endif /* CONFIG_DPP */
+	{ INT_RANGE(owe_group, 0, 65535) },
 };
 
 #undef OFFSET
@@ -2307,6 +2469,7 @@
 	os_free(ssid->ssid);
 	str_clear_free(ssid->passphrase);
 	os_free(ssid->ext_psk);
+	str_clear_free(ssid->sae_password);
 #ifdef IEEE8021X_EAPOL
 	eap_peer_config_free(&ssid->eap);
 #endif /* IEEE8021X_EAPOL */
@@ -2323,6 +2486,9 @@
 #ifdef CONFIG_MESH
 	os_free(ssid->mesh_basic_rates);
 #endif /* CONFIG_MESH */
+	os_free(ssid->dpp_connector);
+	bin_clear_free(ssid->dpp_netaccesskey, ssid->dpp_netaccesskey_len);
+	os_free(ssid->dpp_csign);
 	while ((psk = dl_list_first(&ssid->psk_list, struct psk_list_entry,
 				    list))) {
 		dl_list_del(&psk->list);
@@ -2576,6 +2742,7 @@
 	ssid->group_cipher = DEFAULT_GROUP;
 	ssid->key_mgmt = DEFAULT_KEY_MGMT;
 	ssid->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
+	ssid->ht = 1;
 #ifdef IEEE8021X_EAPOL
 	ssid->eapol_flags = DEFAULT_EAPOL_FLAGS;
 	ssid->eap_workaround = DEFAULT_EAP_WORKAROUND;
@@ -2587,6 +2754,7 @@
 	ssid->dot11MeshRetryTimeout = DEFAULT_MESH_RETRY_TIMEOUT;
 	ssid->dot11MeshConfirmTimeout = DEFAULT_MESH_CONFIRM_TIMEOUT;
 	ssid->dot11MeshHoldingTimeout = DEFAULT_MESH_HOLDING_TIMEOUT;
+	ssid->mesh_rssi_threshold = DEFAULT_MESH_RSSI_THRESHOLD;
 #endif /* CONFIG_MESH */
 #ifdef CONFIG_HT_OVERRIDES
 	ssid->disable_ht = DEFAULT_DISABLE_HT;
@@ -3742,6 +3910,9 @@
 
 #ifdef CONFIG_MBO
 	config->mbo_cell_capa = DEFAULT_MBO_CELL_CAPA;
+	config->disassoc_imminent_rssi_threshold =
+		DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD;
+	config->oce = DEFAULT_OCE_SUPPORT;
 #endif /* CONFIG_MBO */
 
 	if (ctrl_interface)
@@ -4354,6 +4525,7 @@
 	{ FUNC_NO_VAR(load_dynamic_eap), 0 },
 #ifdef CONFIG_WPS
 	{ FUNC(uuid), CFG_CHANGED_UUID },
+	{ INT_RANGE(auto_uuid, 0, 1), 0 },
 	{ STR_RANGE(device_name, 0, WPS_DEV_NAME_MAX_LEN),
 	  CFG_CHANGED_DEVICE_NAME },
 	{ STR_RANGE(manufacturer, 0, 64), CFG_CHANGED_WPS_STRING },
@@ -4410,6 +4582,11 @@
 	{ INT_RANGE(interworking, 0, 1), 0 },
 	{ FUNC(hessid), 0 },
 	{ INT_RANGE(access_network_type, 0, 15), 0 },
+	{ INT_RANGE(go_interworking, 0, 1), 0 },
+	{ INT_RANGE(go_access_network_type, 0, 15), 0 },
+	{ INT_RANGE(go_internet, 0, 1), 0 },
+	{ INT_RANGE(go_venue_group, 0, 255), 0 },
+	{ INT_RANGE(go_venue_type, 0, 255), 0 },
 	{ INT_RANGE(pbc_in_m1, 0, 1), 0 },
 	{ STR(autoscan), 0 },
 	{ INT_RANGE(wps_nfc_dev_pw_id, 0x10, 0xffff),
@@ -4430,9 +4607,10 @@
 	{ FUNC(freq_list), 0 },
 	{ INT(scan_cur_freq), 0 },
 	{ INT(sched_scan_interval), 0 },
+	{ INT(sched_scan_start_delay), 0 },
 	{ INT(tdls_external_control), 0},
 	{ STR(osu_dir), 0 },
-	{ STR(wowlan_triggers), 0 },
+	{ STR(wowlan_triggers), CFG_CHANGED_WOWLAN_TRIGGERS },
 	{ INT(p2p_search_delay), 0},
 	{ INT(mac_addr), 0 },
 	{ INT(rand_addr_lifetime), 0 },
@@ -4453,12 +4631,15 @@
 	{ STR(non_pref_chan), 0 },
 	{ INT_RANGE(mbo_cell_capa, MBO_CELL_CAPA_AVAILABLE,
 		    MBO_CELL_CAPA_NOT_SUPPORTED), 0 },
-#endif /*CONFIG_MBO */
+	{ INT_RANGE(disassoc_imminent_rssi_threshold, -120, 0), 0 },
+	{ INT_RANGE(oce, 0, 3), 0 },
+#endif /* CONFIG_MBO */
 	{ INT(gas_address3), 0 },
 	{ INT_RANGE(ftm_responder, 0, 1), 0 },
 	{ INT_RANGE(ftm_initiator, 0, 1), 0 },
 	{ INT(gas_rand_addr_lifetime), 0 },
 	{ INT_RANGE(gas_rand_mac_addr, 0, 2), 0 },
+	{ INT_RANGE(dpp_config_processing, 0, 2), 0 },
 };
 
 #undef FUNC
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 2f2bb01..07b67e6 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -41,6 +41,8 @@
 #define DEFAULT_P2P_GO_CTWINDOW 0
 #define DEFAULT_WPA_RSC_RELAXATION 1
 #define DEFAULT_MBO_CELL_CAPA MBO_CELL_CAPA_NOT_SUPPORTED
+#define DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD -75
+#define DEFAULT_OCE_SUPPORT OCE_STA
 
 #include "config_ssid.h"
 #include "wps/wps.h"
@@ -334,6 +336,7 @@
 #define CFG_CHANGED_NFC_PASSWORD_TOKEN BIT(15)
 #define CFG_CHANGED_P2P_PASSPHRASE_LEN BIT(16)
 #define CFG_CHANGED_SCHED_SCAN_PLANS BIT(17)
+#define CFG_CHANGED_WOWLAN_TRIGGERS BIT(18)
 
 /**
  * struct wpa_config - wpa_supplicant configuration data
@@ -625,6 +628,13 @@
 	u8 uuid[16];
 
 	/**
+	 * auto_uuid - Automatic UUID behavior
+	 * 0 = generate static value based on the local MAC address (default)
+	 * 1 = generate a random UUID every time wpa_supplicant starts
+	 */
+	int auto_uuid;
+
+	/**
 	 * device_name - Device Name (WPS)
 	 * User-friendly description of device; up to 32 octets encoded in
 	 * UTF-8
@@ -872,6 +882,34 @@
 	 */
 	int access_network_type;
 
+	 /**
+	  * go_interworking - Whether Interworking for P2P GO is enabled
+	  */
+	int go_interworking;
+
+	/**
+	 * go_access_network_type - P2P GO Access Network Type
+	 *
+	 * This indicates which access network type to advertise if Interworking
+	 * is enabled for P2P GO.
+	 */
+	int go_access_network_type;
+
+	/**
+	 * go_internet - Interworking: Internet connectivity (0 or 1)
+	 */
+	int go_internet;
+
+	/**
+	 * go_venue_group - Interworking: Venue group
+	 */
+	int go_venue_group;
+
+	/**
+	 * go_venue_type: Interworking: Venue type
+	 */
+	int go_venue_type;
+
 	/**
 	 * hessid - Homogenous ESS identifier
 	 *
@@ -1096,6 +1134,15 @@
 	unsigned int sched_scan_interval;
 
 	/**
+	 * sched_scan_start_delay - Schedule scan start delay before first scan
+	 *
+	 * Delay (in seconds) before scheduling first scan plan cycle. The
+	 * driver may ignore this parameter and start immediately (or at any
+	 * other time), if this feature is not supported.
+	 */
+	unsigned int sched_scan_start_delay;
+
+	/**
 	 * tdls_external_control - External control for TDLS setup requests
 	 *
 	 * Enable TDLS mode where external programs are given the control
@@ -1291,6 +1338,19 @@
 	 * mbo_cell_capa - Cellular capabilities for MBO
 	 */
 	enum mbo_cellular_capa mbo_cell_capa;
+
+	/**
+	 * disassoc_imminent_rssi_threshold - RSSI threshold of candidate AP
+	 * when disassociation imminent is set.
+	 */
+	int disassoc_imminent_rssi_threshold;
+
+	/**
+	 * oce - Enable OCE in STA and/or STA-CFON mode
+	 *  - Set BIT(0) to enable OCE in non-AP STA mode
+	 *  - Set BIT(1) to enable OCE in STA-CFON mode
+	 */
+	unsigned int oce;
 #endif /* CONFIG_MBO */
 
 	/**
@@ -1343,6 +1403,21 @@
 	 * 2 = like 1, but maintain OUI (with local admin bit set)
 	 */
 	int gas_rand_mac_addr;
+
+	/**
+	 * dpp_config_processing - How to process DPP configuration
+	 *
+	 * 0 = report received configuration to an external program for
+	 *	processing; do not generate any network profile internally
+	 * 1 = report received configuration to an external program and generate
+	 *	a network profile internally, but do not automatically connect
+	 *	to the created (disabled) profile; the network profile id is
+	 *	reported to external programs
+	 * 2 = report received configuration to an external program, generate
+	 *	a network profile internally, try to connect to the created
+	 *	profile automatically
+	 */
+	int dpp_config_processing;
 };
 
 
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 84a1ee9..6b7abe2 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -312,7 +312,7 @@
 		encoded_len += len;
 	}
 
-	if (!end) {
+	if (!end || !encoded) {
 		wpa_printf(MSG_ERROR, "Line %d: blob was not terminated "
 			   "properly", *line);
 		os_free(encoded);
@@ -503,6 +503,17 @@
 }
 
 
+static void write_bssid_hint(FILE *f, struct wpa_ssid *ssid)
+{
+	char *value = wpa_config_get(ssid, "bssid_hint");
+
+	if (!value)
+		return;
+	fprintf(f, "\tbssid_hint=%s\n", value);
+	os_free(value);
+}
+
+
 static void write_psk(FILE *f, struct wpa_ssid *ssid)
 {
 	char *value;
@@ -582,6 +593,22 @@
 }
 
 
+static void write_group_mgmt(FILE *f, struct wpa_ssid *ssid)
+{
+	char *value;
+
+	if (!ssid->group_mgmt_cipher)
+		return;
+
+	value = wpa_config_get(ssid, "group_mgmt");
+	if (!value)
+		return;
+	if (value[0])
+		fprintf(f, "\tgroup_mgmt=%s\n", value);
+	os_free(value);
+}
+
+
 static void write_auth_alg(FILE *f, struct wpa_ssid *ssid)
 {
 	char *value;
@@ -713,15 +740,18 @@
 	STR(ssid);
 	INT(scan_ssid);
 	write_bssid(f, ssid);
+	write_bssid_hint(f, ssid);
 	write_str(f, "bssid_blacklist", ssid);
 	write_str(f, "bssid_whitelist", ssid);
 	write_psk(f, ssid);
 	INT(mem_only_psk);
+	STR(sae_password);
 	write_proto(f, ssid);
 	write_key_mgmt(f, ssid);
 	INT_DEF(bg_scan_period, DEFAULT_BG_SCAN_PERIOD);
 	write_pairwise(f, ssid);
 	write_group(f, ssid);
+	write_group_mgmt(f, ssid);
 	write_auth_alg(f, ssid);
 	STR(bgscan);
 	STR(autoscan);
@@ -790,11 +820,16 @@
 #endif /* CONFIG_ACS */
 	write_int(f, "proactive_key_caching", ssid->proactive_key_caching, -1);
 	INT(disabled);
-	INT(peerkey);
 	INT(mixed_cell);
+	INT(vht);
+	INT_DEF(ht, 1);
+	INT(ht40);
 	INT(max_oper_chwidth);
+	INT(vht_center_freq1);
+	INT(vht_center_freq2);
 	INT(pbss);
 	INT(wps_disabled);
+	INT(fils_dh_group);
 #ifdef CONFIG_IEEE80211W
 	write_int(f, "ieee80211w", ssid->ieee80211w,
 		  MGMT_FRAME_PROTECTION_DEFAULT);
@@ -826,10 +861,18 @@
 	INT_DEF(dot11MeshRetryTimeout, DEFAULT_MESH_RETRY_TIMEOUT);
 	INT_DEF(dot11MeshConfirmTimeout, DEFAULT_MESH_CONFIRM_TIMEOUT);
 	INT_DEF(dot11MeshHoldingTimeout, DEFAULT_MESH_HOLDING_TIMEOUT);
+	INT_DEF(mesh_rssi_threshold, DEFAULT_MESH_RSSI_THRESHOLD);
 #endif /* CONFIG_MESH */
 	INT(wpa_ptk_rekey);
 	INT(group_rekey);
 	INT(ignore_broadcast_ssid);
+#ifdef CONFIG_DPP
+	STR(dpp_connector);
+	STR(dpp_netaccesskey);
+	INT(dpp_netaccesskey_expiry);
+	STR(dpp_csign);
+#endif /* CONFIG_DPP */
+	INT(owe_group);
 #ifdef CONFIG_HT_OVERRIDES
 	INT_DEF(disable_ht, DEFAULT_DISABLE_HT);
 	INT_DEF(disable_ht40, DEFAULT_DISABLE_HT40);
@@ -1082,6 +1125,8 @@
 		uuid_bin2str(config->uuid, buf, sizeof(buf));
 		fprintf(f, "uuid=%s\n", buf);
 	}
+	if (config->auto_uuid)
+		fprintf(f, "auto_uuid=%d\n", config->auto_uuid);
 	if (config->device_name)
 		fprintf(f, "device_name=%s\n", config->device_name);
 	if (config->manufacturer)
@@ -1247,6 +1292,17 @@
 	if (config->access_network_type != DEFAULT_ACCESS_NETWORK_TYPE)
 		fprintf(f, "access_network_type=%d\n",
 			config->access_network_type);
+	if (config->go_interworking)
+		fprintf(f, "go_interworking=%d\n", config->go_interworking);
+	if (config->go_access_network_type)
+		fprintf(f, "go_access_network_type=%d\n",
+			config->go_access_network_type);
+	if (config->go_internet)
+		fprintf(f, "go_internet=%d\n", config->go_internet);
+	if (config->go_venue_group)
+		fprintf(f, "go_venue_group=%d\n", config->go_venue_group);
+	if (config->go_venue_type)
+		fprintf(f, "go_venue_type=%d\n", config->go_venue_type);
 #endif /* CONFIG_INTERWORKING */
 	if (config->pbc_in_m1)
 		fprintf(f, "pbc_in_m1=%d\n", config->pbc_in_m1);
@@ -1320,6 +1376,10 @@
 		fprintf(f, "sched_scan_interval=%u\n",
 			config->sched_scan_interval);
 
+	if (config->sched_scan_start_delay)
+		fprintf(f, "sched_scan_start_delay=%u\n",
+			config->sched_scan_start_delay);
+
 	if (config->external_sim)
 		fprintf(f, "external_sim=%d\n", config->external_sim);
 
@@ -1394,6 +1454,12 @@
 		fprintf(f, "non_pref_chan=%s\n", config->non_pref_chan);
 	if (config->mbo_cell_capa != DEFAULT_MBO_CELL_CAPA)
 		fprintf(f, "mbo_cell_capa=%u\n", config->mbo_cell_capa);
+	if (config->disassoc_imminent_rssi_threshold !=
+	    DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD)
+		fprintf(f, "disassoc_imminent_rssi_threshold=%d\n",
+			config->disassoc_imminent_rssi_threshold);
+	if (config->oce != DEFAULT_OCE_SUPPORT)
+		fprintf(f, "oce=%u\n", config->oce);
 #endif /* CONFIG_MBO */
 
 	if (config->gas_address3)
@@ -1419,6 +1485,9 @@
 			config->gas_rand_addr_lifetime);
 	if (config->gas_rand_mac_addr)
 		fprintf(f, "gas_rand_mac_addr=%d\n", config->gas_rand_mac_addr);
+	if (config->dpp_config_processing)
+		fprintf(f, "dpp_config_processing=%d\n",
+			config->dpp_config_processing);
 
 }
 
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 69ace37..83d657d 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -28,6 +28,7 @@
 #define DEFAULT_MESH_RETRY_TIMEOUT 40
 #define DEFAULT_MESH_CONFIRM_TIMEOUT 40
 #define DEFAULT_MESH_HOLDING_TIMEOUT 40
+#define DEFAULT_MESH_RSSI_THRESHOLD 1 /* no change */
 #define DEFAULT_DISABLE_HT 0
 #define DEFAULT_DISABLE_HT40 0
 #define DEFAULT_DISABLE_SGI 0
@@ -146,6 +147,19 @@
 	int bssid_set;
 
 	/**
+	 * bssid_hint - BSSID hint
+	 *
+	 * If set, this is configured to the driver as a preferred initial BSSID
+	 * while connecting to this network.
+	 */
+	u8 bssid_hint[ETH_ALEN];
+
+	/**
+	 * bssid_hint_set - Whether BSSID hint is configured for this network
+	 */
+	int bssid_hint_set;
+
+	/**
 	 * go_p2p_dev_addr - GO's P2P Device Address or all zeros if not set
 	 */
 	u8 go_p2p_dev_addr[ETH_ALEN];
@@ -170,6 +184,16 @@
 	char *passphrase;
 
 	/**
+	 * sae_password - SAE password
+	 *
+	 * This parameter can be used to set a password for SAE. By default, the
+	 * passphrase value is used if this separate parameter is not used, but
+	 * passphrase follows the WPA-PSK constraints (8..63 characters) even
+	 * though SAE passwords do not have such constraints.
+	 */
+	char *sae_password;
+
+	/**
 	 * ext_psk - PSK/passphrase name in external storage
 	 *
 	 * If this is set, PSK/passphrase will be fetched from external storage
@@ -196,6 +220,15 @@
 	int group_cipher;
 
 	/**
+	 * group_mgmt_cipher - Bitfield of allowed group management ciphers
+	 *
+	 * This is a bitfield of WPA_CIPHER_AES_128_CMAC and WPA_CIPHER_BIP_*
+	 * values. If 0, no constraint is used for the cipher, i.e., whatever
+	 * the AP uses is accepted.
+	 */
+	int group_mgmt_cipher;
+
+	/**
 	 * key_mgmt - Bitfield of allowed key management protocols
 	 *
 	 * WPA_KEY_MGMT_*
@@ -392,17 +425,6 @@
 	int disabled_for_connect;
 
 	/**
-	 * peerkey -  Whether PeerKey handshake for direct links is allowed
-	 *
-	 * This is only used when both RSN/WPA2 and IEEE 802.11e (QoS) are
-	 * enabled.
-	 *
-	 * 0 = disabled (default)
-	 * 1 = enabled
-	 */
-	int peerkey;
-
-	/**
 	 * id_str - Network identifier string for external scripts
 	 *
 	 * This value is passed to external ctrl_iface monitors in
@@ -470,12 +492,14 @@
 	int dot11MeshConfirmTimeout; /* msec */
 	int dot11MeshHoldingTimeout; /* msec */
 
+	int ht;
 	int ht40;
 
 	int vht;
 
 	u8 max_oper_chwidth;
 
+	unsigned int vht_center_freq1;
 	unsigned int vht_center_freq2;
 
 	/**
@@ -806,12 +830,72 @@
 	int no_auto_peer;
 
 	/**
+	 * mesh_rssi_threshold - Set mesh parameter mesh_rssi_threshold (dBm)
+	 *
+	 * -255..-1 = threshold value in dBm
+	 * 0 = not using RSSI threshold
+	 * 1 = do not change driver default
+	 */
+	int mesh_rssi_threshold;
+
+	/**
 	 * wps_disabled - WPS disabled in AP mode
 	 *
 	 * 0 = WPS enabled and configured (default)
 	 * 1 = WPS disabled
 	 */
 	int wps_disabled;
+
+	/**
+	 * fils_dh_group - FILS DH Group
+	 *
+	 * 0 = PFS disabled with FILS shared key authentication
+	 * 1-65535 DH Group to use for FILS PFS
+	 */
+	int fils_dh_group;
+
+	/**
+	 * dpp_connector - DPP Connector (signedConnector as string)
+	 */
+	char *dpp_connector;
+
+	/**
+	 * dpp_netaccesskey - DPP netAccessKey (own private key)
+	 */
+	u8 *dpp_netaccesskey;
+
+	/**
+	 * dpp_netaccesskey_len - DPP netAccessKey length in octets
+	 */
+	size_t dpp_netaccesskey_len;
+
+	/**
+	 * net_access_key_expiry - DPP netAccessKey expiry in UNIX time stamp
+	 *
+	 * 0 indicates no expiration.
+	 */
+	unsigned int dpp_netaccesskey_expiry;
+
+	/**
+	 * dpp_csign - C-sign-key (Configurator public key)
+	 */
+	u8 *dpp_csign;
+
+	/**
+	 * dpp_csign_len - C-sign-key length in octets
+	 */
+	size_t dpp_csign_len;
+
+	/**
+	 * owe_group - OWE DH Group
+	 *
+	 * 0 = use default (19)
+	 * 1-65535 DH Group to use for OWE
+	 *
+	 * Groups 19 (NIST P-256), 20 (NIST P-384), and 21 (NIST P-521) are
+	 * currently supported.
+	 */
+	int owe_group;
 };
 
 #endif /* CONFIG_SSID_H */
diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c
index 82ba3b0..24f496b 100644
--- a/wpa_supplicant/config_winreg.c
+++ b/wpa_supplicant/config_winreg.c
@@ -99,13 +99,12 @@
 			break;
 		}
 		blob->name = os_strdup((char *) name);
-		blob->data = os_malloc(datalen);
+		blob->data = os_memdup(data, datalen);
 		if (blob->name == NULL || blob->data == NULL) {
 			wpa_config_free_blob(blob);
 			errors++;
 			break;
 		}
-		os_memcpy(blob->data, data, datalen);
 		blob->len = datalen;
 
 		wpa_config_set_blob(config, blob);
@@ -234,6 +233,7 @@
 #ifdef CONFIG_WPS
 	if (wpa_config_read_global_uuid(config, hk))
 		errors++;
+	wpa_config_read_reg_dword(hk, TEXT("auto_uuid"), &config->auto_uuid);
 	config->device_name = wpa_config_read_reg_string(
 		hk, TEXT("device_name"));
 	config->manufacturer = wpa_config_read_reg_string(
@@ -580,6 +580,8 @@
 		uuid_bin2str(config->uuid, buf, sizeof(buf));
 		wpa_config_write_reg_string(hk, "uuid", buf);
 	}
+	wpa_config_write_reg_dword(hk, TEXT("auto_uuid"), config->auto_uuid,
+				   0);
 	wpa_config_write_reg_string(hk, "device_name", config->device_name);
 	wpa_config_write_reg_string(hk, "manufacturer", config->manufacturer);
 	wpa_config_write_reg_string(hk, "model_name", config->model_name);
@@ -868,6 +870,7 @@
 	INT(scan_ssid);
 	write_bssid(netw, ssid);
 	write_psk(netw, ssid);
+	STR(sae_password);
 	write_proto(netw, ssid);
 	write_key_mgmt(netw, ssid);
 	write_pairwise(netw, ssid);
@@ -924,7 +927,6 @@
 	write_int(netw, "proactive_key_caching", ssid->proactive_key_caching,
 		  -1);
 	INT(disabled);
-	INT(peerkey);
 #ifdef CONFIG_IEEE80211W
 	write_int(netw, "ieee80211w", ssid->ieee80211w,
 		  MGMT_FRAME_PROTECTION_DEFAULT);
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 761d917..c4d8dfe 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -52,6 +52,7 @@
 #include "offchannel.h"
 #include "drivers/driver.h"
 #include "mesh.h"
+#include "dpp_supplicant.h"
 
 static int wpa_supplicant_global_iface_list(struct wpa_global *global,
 					    char *buf, int len);
@@ -61,6 +62,29 @@
 static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s,
 					char *val);
 
+
+#ifdef CONFIG_FILS
+
+static int wpa_is_fils_supported(struct wpa_supplicant *wpa_s)
+{
+	return (((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+		 (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_FILS)) ||
+		(!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+		 (wpa_s->drv_flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD)));
+}
+
+
+#ifdef CONFIG_FILS_SK_PFS
+static int wpa_is_fils_sk_pfs_supported(struct wpa_supplicant *wpa_s)
+{
+	return (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+		(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_FILS);
+}
+#endif /* CONFIG_FILS_SK_PFS */
+
+#endif /* CONFIG_FILS */
+
+
 static int set_bssid_filter(struct wpa_supplicant *wpa_s, char *val)
 {
 	char *pos;
@@ -576,6 +600,11 @@
 		ret = set_disallow_aps(wpa_s, value);
 	} else if (os_strcasecmp(cmd, "no_keep_alive") == 0) {
 		wpa_s->no_keep_alive = !!atoi(value);
+#ifdef CONFIG_DPP
+	} else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) {
+		os_free(wpa_s->dpp_configurator_params);
+		wpa_s->dpp_configurator_params = os_strdup(value);
+#endif /* CONFIG_DPP */
 #ifdef CONFIG_TESTING_OPTIONS
 	} else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) {
 		wpa_s->ext_mgmt_frame_handling = !!atoi(value);
@@ -597,6 +626,8 @@
 		wpa_s->ignore_auth_resp = !!atoi(value);
 	} else if (os_strcasecmp(cmd, "ignore_assoc_disallow") == 0) {
 		wpa_s->ignore_assoc_disallow = !!atoi(value);
+		wpa_drv_ignore_assoc_disallow(wpa_s,
+					      wpa_s->ignore_assoc_disallow);
 	} else if (os_strcasecmp(cmd, "reject_btm_req_reason") == 0) {
 		wpa_s->reject_btm_req_reason = atoi(value);
 	} else if (os_strcasecmp(cmd, "get_pref_freq_list_override") == 0) {
@@ -605,6 +636,26 @@
 			wpa_s->get_pref_freq_list_override = NULL;
 		else
 			wpa_s->get_pref_freq_list_override = os_strdup(value);
+	} else if (os_strcasecmp(cmd, "sae_commit_override") == 0) {
+		wpabuf_free(wpa_s->sae_commit_override);
+		if (value[0] == '\0')
+			wpa_s->sae_commit_override = NULL;
+		else
+			wpa_s->sae_commit_override = wpabuf_parse_bin(value);
+#ifdef CONFIG_DPP
+	} else if (os_strcasecmp(cmd, "dpp_config_obj_override") == 0) {
+		os_free(wpa_s->dpp_config_obj_override);
+		wpa_s->dpp_config_obj_override = os_strdup(value);
+	} else if (os_strcasecmp(cmd, "dpp_discovery_override") == 0) {
+		os_free(wpa_s->dpp_discovery_override);
+		wpa_s->dpp_discovery_override = os_strdup(value);
+	} else if (os_strcasecmp(cmd, "dpp_groups_override") == 0) {
+		os_free(wpa_s->dpp_groups_override);
+		wpa_s->dpp_groups_override = os_strdup(value);
+	} else if (os_strcasecmp(cmd,
+				 "dpp_ignore_netaccesskey_mismatch") == 0) {
+		wpa_s->dpp_ignore_netaccesskey_mismatch = atoi(value);
+#endif /* CONFIG_DPP */
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifndef CONFIG_NO_CONFIG_BLOBS
 	} else if (os_strcmp(cmd, "blob") == 0) {
@@ -621,6 +672,25 @@
 		}
 	} else if (os_strcasecmp(cmd, "mbo_cell_capa") == 0) {
 		wpas_mbo_update_cell_capa(wpa_s, atoi(value));
+	} else if (os_strcasecmp(cmd, "oce") == 0) {
+		wpa_s->conf->oce = atoi(value);
+		if (wpa_s->conf->oce) {
+			if ((wpa_s->conf->oce & OCE_STA) &&
+			    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA))
+				wpa_s->enable_oce = OCE_STA;
+
+			if ((wpa_s->conf->oce & OCE_STA_CFON) &&
+			    (wpa_s->drv_flags &
+			     WPA_DRIVER_FLAGS_OCE_STA_CFON)) {
+				/* TODO: Need to add STA-CFON support */
+				wpa_printf(MSG_ERROR,
+					   "OCE STA-CFON feature is not yet supported");
+				return -1;
+			}
+		} else {
+			wpa_s->enable_oce = 0;
+		}
+		wpa_supplicant_set_default_scan_ies(wpa_s);
 #endif /* CONFIG_MBO */
 	} else if (os_strcasecmp(cmd, "lci") == 0) {
 		ret = wpas_ctrl_iface_set_lci(wpa_s, value);
@@ -632,6 +702,8 @@
 		ret = wpas_ctrl_set_relative_band_adjust(wpa_s, value);
 	} else if (os_strcasecmp(cmd, "ric_ies") == 0) {
 		ret = wpas_ctrl_iface_set_ric_ies(wpa_s, value);
+	} else if (os_strcasecmp(cmd, "roaming") == 0) {
+		ret = wpa_drv_roaming(wpa_s, atoi(value), NULL);
 	} else {
 		value[-1] = '=';
 		ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
@@ -677,6 +749,12 @@
 #endif /* CONFIG_TESTING_GET_GTK */
 	} else if (os_strcmp(cmd, "tls_library") == 0) {
 		res = tls_get_library_version(buf, buflen);
+#ifdef CONFIG_TESTING_OPTIONS
+	} else if (os_strcmp(cmd, "anonce") == 0) {
+		return wpa_snprintf_hex(buf, buflen,
+					wpa_sm_get_anonce(wpa_s->wpa),
+					WPA_NONCE_LEN);
+#endif /* CONFIG_TESTING_OPTIONS */
 	} else {
 		res = wpa_config_get_value(cmd, wpa_s->conf, buf, buflen);
 	}
@@ -710,27 +788,6 @@
 #endif /* IEEE8021X_EAPOL */
 
 
-#ifdef CONFIG_PEERKEY
-/* MLME-STKSTART.request(peer) */
-static int wpa_supplicant_ctrl_iface_stkstart(
-	struct wpa_supplicant *wpa_s, char *addr)
-{
-	u8 peer[ETH_ALEN];
-
-	if (hwaddr_aton(addr, peer)) {
-		wpa_printf(MSG_DEBUG, "CTRL_IFACE STKSTART: invalid "
-			   "address '%s'", addr);
-		return -1;
-	}
-
-	wpa_printf(MSG_DEBUG, "CTRL_IFACE STKSTART " MACSTR,
-		   MAC2STR(peer));
-
-	return wpa_sm_stkstart(wpa_s->wpa, peer);
-}
-#endif /* CONFIG_PEERKEY */
-
-
 #ifdef CONFIG_TDLS
 
 static int wpa_supplicant_ctrl_iface_tdls_discover(
@@ -2578,6 +2635,26 @@
 #endif /* CONFIG_IEEE80211R */
 #endif /* CONFIG_FILS */
 
+#ifdef CONFIG_OWE
+	if (data.key_mgmt & WPA_KEY_MGMT_OWE) {
+		ret = os_snprintf(pos, end - pos, "%sOWE",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+#endif /* CONFIG_OWE */
+
+#ifdef CONFIG_DPP
+	if (data.key_mgmt & WPA_KEY_MGMT_DPP) {
+		ret = os_snprintf(pos, end - pos, "%sDPP",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+#endif /* CONFIG_DPP */
+
 	if (data.key_mgmt & WPA_KEY_MGMT_OSEN) {
 		ret = os_snprintf(pos, end - pos, "%sOSEN",
 				  pos == start ? "" : "+");
@@ -2653,7 +2730,7 @@
 {
 	char *pos, *end;
 	int ret;
-	const u8 *ie, *ie2, *osen_ie, *p2p, *mesh;
+	const u8 *ie, *ie2, *osen_ie, *p2p, *mesh, *owe;
 
 	mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID);
 	p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
@@ -2684,6 +2761,14 @@
 	if (osen_ie)
 		pos = wpa_supplicant_ie_txt(pos, end, "OSEN",
 					    osen_ie, 2 + osen_ie[1]);
+	owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
+	if (owe) {
+		ret = os_snprintf(pos, end - pos,
+				  ie2 ? "[OWE-TRANS]" : "[OWE-TRANS-OPEN]");
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
 	pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
 	if (!ie && !ie2 && !osen_ie && (bss->caps & IEEE80211_CAP_PRIVACY)) {
 		ret = os_snprintf(pos, end - pos, "[WEP]");
@@ -2984,9 +3069,8 @@
 	if (pos) {
 		int *freqs = freq_range_to_channel_list(wpa_s, pos + 6);
 		if (freqs) {
-			wpa_s->scan_req = MANUAL_SCAN_REQ;
-			os_free(wpa_s->manual_scan_freqs);
-			wpa_s->manual_scan_freqs = freqs;
+			os_free(wpa_s->select_network_scan_freqs);
+			wpa_s->select_network_scan_freqs = freqs;
 		}
 	}
 
@@ -3161,6 +3245,7 @@
 		return 0; /* No change to the previously configured value */
 
 	if (os_strcmp(name, "bssid") != 0 &&
+	    os_strcmp(name, "bssid_hint") != 0 &&
 	    os_strcmp(name, "priority") != 0) {
 		wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
 
@@ -3796,6 +3881,50 @@
 		pos += ret;
 	}
 #endif /* CONFIG_SUITEB192 */
+#ifdef CONFIG_OWE
+	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_OWE) {
+		ret = os_snprintf(pos, end - pos, " OWE");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_DPP) {
+		ret = os_snprintf(pos, end - pos, " DPP");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_DPP */
+#ifdef CONFIG_FILS
+	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256) {
+		ret = os_snprintf(pos, end - pos, " FILS-SHA256");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384) {
+		ret = os_snprintf(pos, end - pos, " FILS-SHA384");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#ifdef CONFIG_IEEE80211R
+	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256) {
+		ret = os_snprintf(pos, end - pos, " FT-FILS-SHA256");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384) {
+		ret = os_snprintf(pos, end - pos, " FT-FILS-SHA384");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_FILS */
 
 	return pos - buf;
 }
@@ -3898,6 +4027,26 @@
 	}
 #endif /* CONFIG_SAE */
 
+#ifdef CONFIG_FILS
+	if (wpa_is_fils_supported(wpa_s)) {
+		ret = os_snprintf(pos, end - pos, "%sFILS_SK_WITHOUT_PFS",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+#ifdef CONFIG_FILS_SK_PFS
+	if (wpa_is_fils_sk_pfs_supported(wpa_s)) {
+		ret = os_snprintf(pos, end - pos, "%sFILS_SK_WITH_PFS",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_FILS_SK_PFS */
+#endif /* CONFIG_FILS */
+
 	return pos - buf;
 }
 
@@ -4156,12 +4305,23 @@
 #endif /* CONFIG_ACS */
 
 #ifdef CONFIG_FILS
-	if (os_strcmp(field, "fils") == 0 &&
-	    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_FILS)) {
-		res = os_snprintf(buf, buflen, "FILS");
-		if (os_snprintf_error(buflen, res))
-			return -1;
-		return res;
+	if (os_strcmp(field, "fils") == 0) {
+#ifdef CONFIG_FILS_SK_PFS
+		if (wpa_is_fils_supported(wpa_s) &&
+		    wpa_is_fils_sk_pfs_supported(wpa_s)) {
+			res = os_snprintf(buf, buflen, "FILS FILS-SK-PFS");
+			if (os_snprintf_error(buflen, res))
+				return -1;
+			return res;
+		}
+#endif /* CONFIG_FILS_SK_PFS */
+
+		if (wpa_is_fils_supported(wpa_s)) {
+			res = os_snprintf(buf, buflen, "FILS");
+			if (os_snprintf_error(buflen, res))
+				return -1;
+			return res;
+		}
 	}
 #endif /* CONFIG_FILS */
 
@@ -4285,7 +4445,7 @@
 	size_t i;
 	int ret;
 	char *pos, *end;
-	const u8 *ie, *ie2, *osen_ie, *mesh;
+	const u8 *ie, *ie2, *osen_ie, *mesh, *owe;
 
 	pos = buf;
 	end = buf + buflen;
@@ -4409,6 +4569,15 @@
 		if (osen_ie)
 			pos = wpa_supplicant_ie_txt(pos, end, "OSEN",
 						    osen_ie, 2 + osen_ie[1]);
+		owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
+		if (owe) {
+			ret = os_snprintf(
+				pos, end - pos,
+				ie2 ? "[OWE-TRANS]" : "[OWE-TRANS-OPEN]");
+			if (os_snprintf_error(end - pos, ret))
+				return 0;
+			pos += ret;
+		}
 		pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
 		if (!ie && !ie2 && !osen_ie &&
 		    (bss->caps & IEEE80211_CAP_PRIVACY)) {
@@ -6506,6 +6675,12 @@
 {
 	os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
 	wpa_s->force_long_sd = 0;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	os_free(wpa_s->get_pref_freq_list_override);
+	wpa_s->get_pref_freq_list_override = NULL;
+#endif /* CONFIG_TESTING_OPTIONS */
+
 	wpas_p2p_stop_find(wpa_s);
 	wpa_s->parent->p2ps_method_config_any = 0;
 	if (wpa_s->global->p2p)
@@ -6713,7 +6888,7 @@
 	u16 id[MAX_ANQP_INFO_ID];
 	size_t num_id = 0;
 	u32 subtypes = 0;
-	int get_cell_pref = 0;
+	u32 mbo_subtypes = 0;
 
 	used = hwaddr_aton2(dst, dst_addr);
 	if (used < 0)
@@ -6734,9 +6909,10 @@
 		} else if (os_strncmp(pos, "mbo:", 4) == 0) {
 #ifdef CONFIG_MBO
 			int num = atoi(pos + 4);
-			if (num != MBO_ANQP_SUBTYPE_CELL_CONN_PREF)
+
+			if (num <= 0 || num > MAX_MBO_ANQP_SUBTYPE)
 				return -1;
-			get_cell_pref = 1;
+			mbo_subtypes |= BIT(num);
 #else /* CONFIG_MBO */
 			return -1;
 #endif /* CONFIG_MBO */
@@ -6751,11 +6927,11 @@
 		pos++;
 	}
 
-	if (num_id == 0)
+	if (num_id == 0 && !subtypes && !mbo_subtypes)
 		return -1;
 
 	return anqp_send_req(wpa_s, dst_addr, id, num_id, subtypes,
-			     get_cell_pref);
+			     mbo_subtypes);
 }
 
 
@@ -7157,26 +7333,25 @@
 static int wpas_ctrl_iface_wnm_bss_query(struct wpa_supplicant *wpa_s, char *cmd)
 {
 	int query_reason, list = 0;
+	char *btm_candidates = NULL;
 
 	query_reason = atoi(cmd);
 
 	cmd = os_strchr(cmd, ' ');
 	if (cmd) {
-		cmd++;
-		if (os_strncmp(cmd, "list", 4) == 0) {
+		if (os_strncmp(cmd, " list", 5) == 0)
 			list = 1;
-		} else {
-			wpa_printf(MSG_DEBUG, "WNM Query: Invalid option %s",
-				   cmd);
-			return -1;
-		}
+		else
+			btm_candidates = cmd;
 	}
 
 	wpa_printf(MSG_DEBUG,
 		   "CTRL_IFACE: WNM_BSS_QUERY query_reason=%d%s",
 		   query_reason, list ? " candidate list" : "");
 
-	return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason, list);
+	return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason,
+						  btm_candidates,
+						  list);
 }
 
 #endif /* CONFIG_WNM */
@@ -7531,6 +7706,10 @@
 	wpa_s->after_wps = 0;
 	wpa_s->known_wps_freq = 0;
 
+#ifdef CONFIG_DPP
+	wpas_dpp_deinit(wpa_s);
+#endif /* CONFIG_DPP */
+
 #ifdef CONFIG_TDLS
 #ifdef CONFIG_TDLS_TESTING
 	tdls_testing = 0;
@@ -7592,15 +7771,20 @@
 	wpa_s->p2p_go_csa_on_inv = 0;
 	wpa_s->ignore_auth_resp = 0;
 	wpa_s->ignore_assoc_disallow = 0;
+	wpa_s->testing_resend_assoc = 0;
 	wpa_s->reject_btm_req_reason = 0;
 	wpa_sm_set_test_assoc_ie(wpa_s->wpa, NULL);
 	os_free(wpa_s->get_pref_freq_list_override);
 	wpa_s->get_pref_freq_list_override = NULL;
+	wpabuf_free(wpa_s->sae_commit_override);
+	wpa_s->sae_commit_override = NULL;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	wpa_s->disconnected = 0;
 	os_free(wpa_s->next_scan_freqs);
 	wpa_s->next_scan_freqs = NULL;
+	os_free(wpa_s->select_network_scan_freqs);
+	wpa_s->select_network_scan_freqs = NULL;
 
 	wpa_bss_flush(wpa_s);
 	if (!dl_list_empty(&wpa_s->bss)) {
@@ -7918,6 +8102,19 @@
 			goto done;
 		}
 
+		pos = os_strstr(params, "bssid=");
+		if (pos) {
+			u8 bssid[ETH_ALEN];
+
+			pos += 6;
+			if (hwaddr_aton(pos, bssid)) {
+				wpa_printf(MSG_ERROR, "Invalid BSSID %s", pos);
+				*reply_len = -1;
+				goto done;
+			}
+			os_memcpy(wpa_s->next_scan_bssid, bssid, ETH_ALEN);
+		}
+
 		pos = params;
 		while (pos && *pos != '\0') {
 			if (os_strncmp(pos, "ssid ", 5) == 0) {
@@ -8717,6 +8914,79 @@
 	return 0;
 }
 
+
+static int wpas_ctrl_reset_pn(struct wpa_supplicant *wpa_s)
+{
+	u8 zero[WPA_TK_MAX_LEN];
+
+	if (wpa_s->last_tk_alg == WPA_ALG_NONE)
+		return -1;
+
+	wpa_printf(MSG_INFO, "TESTING: Reset PN");
+	os_memset(zero, 0, sizeof(zero));
+
+	/* First, use a zero key to avoid any possible duplicate key avoidance
+	 * in the driver. */
+	if (wpa_drv_set_key(wpa_s, wpa_s->last_tk_alg, wpa_s->last_tk_addr,
+			    wpa_s->last_tk_key_idx, 1, zero, 6,
+			    zero, wpa_s->last_tk_len) < 0)
+		return -1;
+
+	/* Set the previously configured key to reset its TSC/RSC */
+	return wpa_drv_set_key(wpa_s, wpa_s->last_tk_alg, wpa_s->last_tk_addr,
+			       wpa_s->last_tk_key_idx, 1, zero, 6,
+			       wpa_s->last_tk, wpa_s->last_tk_len);
+}
+
+
+static int wpas_ctrl_key_request(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	const char *pos = cmd;
+	int error, pairwise;
+
+	error = atoi(pos);
+	pos = os_strchr(pos, ' ');
+	if (!pos)
+		return -1;
+	pairwise = atoi(pos);
+	wpa_sm_key_request(wpa_s->wpa, error, pairwise);
+	return 0;
+}
+
+
+static int wpas_ctrl_resend_assoc(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_SME
+	struct wpa_driver_associate_params params;
+	int ret;
+
+	os_memset(&params, 0, sizeof(params));
+	params.bssid = wpa_s->bssid;
+	params.ssid = wpa_s->sme.ssid;
+	params.ssid_len = wpa_s->sme.ssid_len;
+	params.freq.freq = wpa_s->sme.freq;
+	if (wpa_s->last_assoc_req_wpa_ie) {
+		params.wpa_ie = wpabuf_head(wpa_s->last_assoc_req_wpa_ie);
+		params.wpa_ie_len = wpabuf_len(wpa_s->last_assoc_req_wpa_ie);
+	}
+	params.pairwise_suite = wpa_s->pairwise_cipher;
+	params.group_suite = wpa_s->group_cipher;
+	params.mgmt_group_suite = wpa_s->mgmt_group_cipher;
+	params.key_mgmt_suite = wpa_s->key_mgmt;
+	params.wpa_proto = wpa_s->wpa_proto;
+	params.mgmt_frame_protection = wpa_s->sme.mfp;
+	params.rrm_used = wpa_s->rrm.rrm_used;
+	if (wpa_s->sme.prev_bssid_set)
+		params.prev_bssid = wpa_s->sme.prev_bssid;
+	wpa_printf(MSG_INFO, "TESTING: Resend association request");
+	ret = wpa_drv_associate(wpa_s, &params);
+	wpa_s->testing_resend_assoc = 1;
+	return ret;
+#else /* CONFIG_SME */
+	return -1;
+#endif /* CONFIG_SME */
+}
+
 #endif /* CONFIG_TESTING_OPTIONS */
 
 
@@ -9164,6 +9434,7 @@
 	 * Entry format:
 	 * <BSSID> <PMKID> <PMK> <reauth_time in seconds>
 	 * <expiration in seconds> <akmp> <opportunistic>
+	 * [FILS Cache Identifier]
 	 */
 
 	for (entry = wpa_sm_pmksa_cache_head(wpa_s->wpa); entry;
@@ -9198,6 +9469,15 @@
 			break;
 		pos2 += ret;
 
+		if (entry->fils_cache_id_set) {
+			ret = os_snprintf(pos2, end - pos2, " %02x%02x",
+					  entry->fils_cache_id[0],
+					  entry->fils_cache_id[1]);
+			if (os_snprintf_error(end - pos2, ret))
+				break;
+			pos2 += ret;
+		}
+
 		ret = os_snprintf(pos2, end - pos2, "\n");
 		if (os_snprintf_error(end - pos2, ret))
 			break;
@@ -9218,12 +9498,13 @@
 	char *pos, *pos2;
 	int ret = -1;
 	struct os_reltime now;
-	int reauth_time = 0, expiration = 0;
+	int reauth_time = 0, expiration = 0, i;
 
 	/*
 	 * Entry format:
 	 * <network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds>
 	 * <expiration in seconds> <akmp> <opportunistic>
+	 * [FILS Cache Identifier]
 	 */
 
 	ssid = wpa_config_get_network(wpa_s->conf, atoi(cmd));
@@ -9271,6 +9552,21 @@
 	if (sscanf(pos, "%d %d %d %d", &reauth_time, &expiration,
 		   &entry->akmp, &entry->opportunistic) != 4)
 		goto fail;
+	for (i = 0; i < 4; i++) {
+		pos = os_strchr(pos, ' ');
+		if (!pos) {
+			if (i < 3)
+				goto fail;
+			break;
+		}
+		pos++;
+	}
+	if (pos) {
+		if (hexstr2bin(pos, entry->fils_cache_id,
+			       FILS_CACHE_ID_LEN) < 0)
+			goto fail;
+		entry->fils_cache_id_set = 1;
+	}
 	os_get_reltime(&now);
 	entry->expiration = now.sec + expiration;
 	entry->reauth_time = now.sec + reauth_time;
@@ -9377,7 +9673,9 @@
 	int reply_len;
 
 	if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 ||
-	    os_strncmp(buf, "SET_NETWORK ", 12) == 0) {
+	    os_strncmp(buf, "SET_NETWORK ", 12) == 0 ||
+	    os_strncmp(buf, "PMKSA_ADD ", 10) == 0 ||
+	    os_strncmp(buf, "MESH_PMKSA_ADD ", 15) == 0) {
 		if (wpa_debug_show_keys)
 			wpa_dbg(wpa_s, MSG_DEBUG,
 				"Control interface command '%s'", buf);
@@ -9386,7 +9684,9 @@
 				"Control interface command '%s [REMOVED]'",
 				os_strncmp(buf, WPA_CTRL_RSP,
 					   os_strlen(WPA_CTRL_RSP)) == 0 ?
-				WPA_CTRL_RSP : "SET_NETWORK");
+				WPA_CTRL_RSP :
+				(os_strncmp(buf, "SET_NETWORK ", 12) == 0 ?
+				 "SET_NETWORK" : "key-add"));
 	} else if (os_strncmp(buf, "WPS_NFC_TAG_READ", 16) == 0 ||
 		   os_strncmp(buf, "NFC_REPORT_HANDOVER", 19) == 0) {
 		wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface",
@@ -9482,11 +9782,6 @@
 		if (wpa_supplicant_ctrl_iface_preauth(wpa_s, buf + 8))
 			reply_len = -1;
 #endif /* IEEE8021X_EAPOL */
-#ifdef CONFIG_PEERKEY
-	} else if (os_strncmp(buf, "STKSTART ", 9) == 0) {
-		if (wpa_supplicant_ctrl_iface_stkstart(wpa_s, buf + 9))
-			reply_len = -1;
-#endif /* CONFIG_PEERKEY */
 #ifdef CONFIG_IEEE80211R
 	} else if (os_strncmp(buf, "FT_DS ", 6) == 0) {
 		if (wpa_supplicant_ctrl_iface_ft_ds(wpa_s, buf + 6))
@@ -10063,6 +10358,15 @@
 	} else if (os_strncmp(buf, "TEST_ASSOC_IE ", 14) == 0) {
 		if (wpas_ctrl_test_assoc_ie(wpa_s, buf + 14) < 0)
 			reply_len = -1;
+	} else if (os_strcmp(buf, "RESET_PN") == 0) {
+		if (wpas_ctrl_reset_pn(wpa_s) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "KEY_REQUEST ", 12) == 0) {
+		if (wpas_ctrl_key_request(wpa_s, buf + 12) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "RESEND_ASSOC") == 0) {
+		if (wpas_ctrl_resend_assoc(wpa_s) < 0)
+			reply_len = -1;
 #endif /* CONFIG_TESTING_OPTIONS */
 	} else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) {
 		if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0)
@@ -10091,6 +10395,86 @@
 	} else if (os_strcmp(buf, "FILS_HLP_REQ_FLUSH") == 0) {
 		wpas_flush_fils_hlp_req(wpa_s);
 #endif /* CONFIG_FILS */
+#ifdef CONFIG_DPP
+	} else if (os_strncmp(buf, "DPP_QR_CODE ", 12) == 0) {
+		int res;
+
+		res = wpas_dpp_qr_code(wpa_s, buf + 12);
+		if (res < 0) {
+			reply_len = -1;
+		} else {
+			reply_len = os_snprintf(reply, reply_size, "%d", res);
+			if (os_snprintf_error(reply_size, reply_len))
+				reply_len = -1;
+		}
+	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_GEN ", 18) == 0) {
+		int res;
+
+		res = wpas_dpp_bootstrap_gen(wpa_s, buf + 18);
+		if (res < 0) {
+			reply_len = -1;
+		} else {
+			reply_len = os_snprintf(reply, reply_size, "%d", res);
+			if (os_snprintf_error(reply_size, reply_len))
+				reply_len = -1;
+		}
+	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_REMOVE ", 21) == 0) {
+		if (wpas_dpp_bootstrap_remove(wpa_s, buf + 21) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_GET_URI ", 22) == 0) {
+		const char *uri;
+
+		uri = wpas_dpp_bootstrap_get_uri(wpa_s, atoi(buf + 22));
+		if (!uri) {
+			reply_len = -1;
+		} else {
+			reply_len = os_snprintf(reply, reply_size, "%s", uri);
+			if (os_snprintf_error(reply_size, reply_len))
+				reply_len = -1;
+		}
+	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_INFO ", 19) == 0) {
+		reply_len = wpas_dpp_bootstrap_info(wpa_s, atoi(buf + 19),
+						    reply, reply_size);
+	} else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) {
+		if (wpas_dpp_auth_init(wpa_s, buf + 13) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DPP_LISTEN ", 11) == 0) {
+		if (wpas_dpp_listen(wpa_s, buf + 11) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "DPP_STOP_LISTEN") == 0) {
+		wpas_dpp_listen_stop(wpa_s);
+	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_ADD", 20) == 0) {
+		int res;
+
+		res = wpas_dpp_configurator_add(wpa_s, buf + 20);
+		if (res < 0) {
+			reply_len = -1;
+		} else {
+			reply_len = os_snprintf(reply, reply_size, "%d", res);
+			if (os_snprintf_error(reply_size, reply_len))
+				reply_len = -1;
+		}
+	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_REMOVE ", 24) == 0) {
+		if (wpas_dpp_configurator_remove(wpa_s, buf + 24) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_SIGN ", 22) == 0) {
+		if (wpas_dpp_configurator_sign(wpa_s, buf + 22) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) {
+		int res;
+
+		res = wpas_dpp_pkex_add(wpa_s, buf + 12);
+		if (res < 0) {
+			reply_len = -1;
+		} else {
+			reply_len = os_snprintf(reply, reply_size, "%d", res);
+			if (os_snprintf_error(reply_size, reply_len))
+				reply_len = -1;
+		}
+	} else if (os_strncmp(buf, "DPP_PKEX_REMOVE ", 16) == 0) {
+		if (wpas_dpp_pkex_remove(wpa_s, buf + 16) < 0)
+			reply_len = -1;
+#endif /* CONFIG_DPP */
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
@@ -10409,6 +10793,7 @@
 #ifdef ANDROID
 		"DRIVER ",
 #endif /* ANDROID */
+		"GET_CAPABILITY ",
 		"GET_NETWORK ",
 		"REMOVE_NETWORK ",
 		"P2P_FIND ",
diff --git a/wpa_supplicant/ctrl_iface_named_pipe.c b/wpa_supplicant/ctrl_iface_named_pipe.c
index 54e0e2f..9c0a47e 100644
--- a/wpa_supplicant/ctrl_iface_named_pipe.c
+++ b/wpa_supplicant/ctrl_iface_named_pipe.c
@@ -319,13 +319,12 @@
 	}
 
 	os_free(dst->rsp_buf);
-	dst->rsp_buf = os_malloc(send_len);
+	dst->rsp_buf = os_memdup(send_buf, send_len);
 	if (dst->rsp_buf == NULL) {
 		ctrl_close_pipe(dst);
 		os_free(reply);
 		return;
 	}
-	os_memcpy(dst->rsp_buf, send_buf, send_len);
 	os_free(reply);
 
 	if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap,
@@ -739,13 +738,12 @@
 	}
 
 	os_free(dst->rsp_buf);
-	dst->rsp_buf = os_malloc(send_len);
+	dst->rsp_buf = os_memdup(send_buf, send_len);
 	if (dst->rsp_buf == NULL) {
 		global_close_pipe(dst);
 		os_free(reply);
 		return;
 	}
-	os_memcpy(dst->rsp_buf, send_buf, send_len);
 	os_free(reply);
 
 	if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap,
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index 0c355f7..8115f77 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -793,6 +793,144 @@
 
 #endif /* CONFIG_WPS */
 
+
+#ifdef CONFIG_MESH
+
+void wpas_dbus_signal_mesh_group_started(struct wpa_supplicant *wpa_s,
+					 struct wpa_ssid *ssid)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (!iface || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_MESH,
+				      "MeshGroupStarted");
+	if (!msg)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_byte_array(&dict_iter, "SSID",
+					     (const char *) ssid->ssid,
+					     ssid->ssid_len) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
+void wpas_dbus_signal_mesh_group_removed(struct wpa_supplicant *wpa_s,
+					 const u8 *meshid, u8 meshid_len,
+					 int reason)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (!iface || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_MESH,
+				      "MeshGroupRemoved");
+	if (!msg)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_byte_array(&dict_iter, "SSID",
+					     (const char *) meshid,
+					     meshid_len) ||
+	    !wpa_dbus_dict_append_int32(&dict_iter, "DisconnectReason",
+					reason) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
+void wpas_dbus_signal_mesh_peer_connected(struct wpa_supplicant *wpa_s,
+					  const u8 *peer_addr)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (!iface || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_MESH,
+				      "MeshPeerConnected");
+	if (!msg)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_byte_array(&dict_iter, "PeerAddress",
+					     (const char *) peer_addr,
+					     ETH_ALEN) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
+void wpas_dbus_signal_mesh_peer_disconnected(struct wpa_supplicant *wpa_s,
+					     const u8 *peer_addr, int reason)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (!iface || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_MESH,
+				      "MeshPeerDisconnected");
+	if (!msg)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_byte_array(&dict_iter, "PeerAddress",
+					     (const char *) peer_addr,
+					     ETH_ALEN) ||
+	    !wpa_dbus_dict_append_int32(&dict_iter, "DisconnectReason",
+					reason) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+#endif /* CONFIG_MESH */
+
+
 void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s,
 				    int depth, const char *subject,
 				    const char *altsubject[],
@@ -3086,6 +3224,20 @@
 		  END_ARGS
 	  }
 	},
+	{ "TDLSChannelSwitch", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_tdls_channel_switch,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "TDLSCancelChannelSwitch", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_tdls_cancel_channel_switch,
+	  {
+		  { "peer_address", "s", ARG_IN },
+		  END_ARGS
+	  }
+	},
 #endif /* CONFIG_TDLS */
 	{ "VendorElemAdd", WPAS_DBUS_NEW_IFACE_INTERFACE,
 	  (WPADBusMethodHandler) wpas_dbus_handler_vendor_elem_add,
@@ -3119,6 +3271,12 @@
 	  }
 	},
 #endif /* CONFIG_NO_CONFIG_WRITE */
+	{ "AbortScan", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_abort_scan,
+	  {
+		  END_ARGS
+	  }
+	},
 	{ NULL, NULL, NULL, { END_ARGS } }
 };
 
@@ -3325,6 +3483,18 @@
 	  NULL,
 	  NULL
 	},
+#ifdef CONFIG_MESH
+	{ "MeshPeers", WPAS_DBUS_NEW_IFACE_MESH, "aay",
+	  wpas_dbus_getter_mesh_peers,
+	  NULL,
+	  NULL
+	},
+	{ "MeshGroup", WPAS_DBUS_NEW_IFACE_MESH, "ay",
+	  wpas_dbus_getter_mesh_group,
+	  NULL,
+	  NULL
+	},
+#endif /* CONFIG_MESH */
 	{ NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
@@ -3602,6 +3772,32 @@
 		  END_ARGS
 	  }
 	},
+#ifdef CONFIG_MESH
+	{ "MeshGroupStarted", WPAS_DBUS_NEW_IFACE_MESH,
+	  {
+		  { "args", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "MeshGroupRemoved", WPAS_DBUS_NEW_IFACE_MESH,
+	  {
+		  { "args", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "MeshPeerConnected", WPAS_DBUS_NEW_IFACE_MESH,
+	  {
+		  { "args", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "MeshPeerDisconnected", WPAS_DBUS_NEW_IFACE_MESH,
+	  {
+		  { "args", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+#endif /* CONFIG_MESH */
 	{ NULL, NULL, { END_ARGS } }
 };
 
diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h
index bd0e074..e68acb7 100644
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -65,6 +65,8 @@
 #define WPAS_DBUS_NEW_IFACE_P2PDEVICE	\
 		WPAS_DBUS_NEW_IFACE_INTERFACE ".P2PDevice"
 
+#define WPAS_DBUS_NEW_IFACE_MESH WPAS_DBUS_NEW_IFACE_INTERFACE ".Mesh"
+
 /*
  * Groups correspond to P2P groups where this device is a GO (owner)
  */
@@ -239,6 +241,15 @@
 					      const u8 *sa, const u8 *dev_addr,
 					      const u8 *bssid, int id,
 					      int op_freq);
+void wpas_dbus_signal_mesh_group_started(struct wpa_supplicant *wpa_s,
+					 struct wpa_ssid *ssid);
+void wpas_dbus_signal_mesh_group_removed(struct wpa_supplicant *wpa_s,
+					 const u8 *meshid, u8 meshid_len,
+					 int reason);
+void wpas_dbus_signal_mesh_peer_connected(struct wpa_supplicant *wpa_s,
+					  const u8 *peer_addr);
+void wpas_dbus_signal_mesh_peer_disconnected(struct wpa_supplicant *wpa_s,
+					     const u8 *peer_addr, int reason);
 
 #else /* CONFIG_CTRL_IFACE_DBUS_NEW */
 
@@ -554,6 +565,31 @@
 {
 }
 
+static inline
+void wpas_dbus_signal_mesh_group_started(struct wpa_supplicant *wpa_s,
+					 struct wpa_ssid *ssid)
+{
+}
+
+static inline
+void wpas_dbus_signal_mesh_group_removed(struct wpa_supplicant *wpa_s,
+					 const u8 *meshid, u8 meshid_len,
+					 int reason)
+{
+}
+
+static inline
+void wpas_dbus_signal_mesh_peer_connected(struct wpa_supplicant *wpa_s,
+					  const u8 *peer_addr)
+{
+}
+
+static inline
+void wpas_dbus_signal_mesh_peer_disconnected(struct wpa_supplicant *wpa_s,
+					     const u8 *peer_addr, int reason)
+{
+}
+
 #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
 
 #endif /* CTRL_IFACE_DBUS_H_NEW */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index e6f356b..564c868 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -28,6 +28,10 @@
 #include "dbus_dict_helpers.h"
 #include "dbus_common_i.h"
 #include "drivers/driver.h"
+#ifdef CONFIG_MESH
+#include "ap/hostapd.h"
+#include "ap/sta_info.h"
+#endif /* CONFIG_MESH */
 
 static const char * const debug_strings[] = {
 	"excessive", "msgdump", "debug", "info", "warning", "error", NULL
@@ -1076,12 +1080,11 @@
 		}
 
 		if (len != 0) {
-			ssid = os_malloc(len);
+			ssid = os_memdup(val, len);
 			if (ssid == NULL) {
 				*reply = wpas_dbus_error_no_memory(message);
 				return -1;
 			}
-			os_memcpy(ssid, val, len);
 		} else {
 			/* Allow zero-length SSIDs */
 			ssid = NULL;
@@ -1420,6 +1423,27 @@
 }
 
 
+/*
+ * wpas_dbus_handler_abort_scan - Request an ongoing scan to be aborted
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: Abort failed or no scan in progress DBus error message on failure
+ * or NULL otherwise.
+ *
+ * Handler function for "AbortScan" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_abort_scan(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s)
+{
+	if (wpas_abort_ongoing_scan(wpa_s) < 0)
+		return dbus_message_new_error(
+			message, WPAS_DBUS_ERROR_IFACE_SCAN_ERROR,
+			"Abort failed or no scan in progress");
+
+	return NULL;
+}
+
+
 /**
  * wpas_dbus_handler_signal_poll - Request immediate signal properties
  * @message: Pointer to incoming dbus message
@@ -1927,13 +1951,12 @@
 		goto err;
 	}
 
-	blob->data = os_malloc(blob_len);
+	blob->data = os_memdup(blob_data, blob_len);
 	blob->name = os_strdup(blob_name);
 	if (!blob->data || !blob->name) {
 		reply = wpas_dbus_error_no_memory(message);
 		goto err;
 	}
-	os_memcpy(blob->data, blob_data, blob_len);
 	blob->len = blob_len;
 
 	wpa_config_set_blob(wpa_s->conf, blob);
@@ -2294,6 +2317,156 @@
 	return NULL;
 }
 
+/*
+ * wpas_dbus_handler_tdls_channel_switch - Enable channel switching with TDLS peer
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "TDLSChannelSwitch" method call of network interface.
+ */
+DBusMessage *
+wpas_dbus_handler_tdls_channel_switch(DBusMessage *message,
+				      struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter	iter, iter_dict;
+	struct wpa_dbus_dict_entry entry;
+	u8 peer[ETH_ALEN];
+	struct hostapd_freq_params freq_params;
+	u8 oper_class = 0;
+	int ret;
+	int is_peer_present = 0;
+
+	if (!wpa_tdls_is_external_setup(wpa_s->wpa)) {
+		wpa_printf(MSG_INFO,
+			   "tdls_chanswitch: Only supported with external setup");
+		return wpas_dbus_error_unknown_error(message, "TDLS is not using external setup");
+	}
+
+	os_memset(&freq_params, 0, sizeof(freq_params));
+
+	dbus_message_iter_init(message, &iter);
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		return wpas_dbus_error_invalid_args(message, NULL);
+
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			return wpas_dbus_error_invalid_args(message, NULL);
+
+		if (os_strcmp(entry.key, "PeerAddress") == 0 &&
+		    entry.type == DBUS_TYPE_STRING) {
+			if (hwaddr_aton(entry.str_value, peer)) {
+				wpa_printf(MSG_DEBUG,
+					   "tdls_chanswitch: Invalid address '%s'",
+					   entry.str_value);
+				wpa_dbus_dict_entry_clear(&entry);
+				return wpas_dbus_error_invalid_args(message,
+								    NULL);
+			}
+
+			is_peer_present = 1;
+		} else if (os_strcmp(entry.key, "OperClass") == 0 &&
+			   entry.type == DBUS_TYPE_BYTE) {
+			oper_class = entry.byte_value;
+		} else if (os_strcmp(entry.key, "Frequency") == 0 &&
+			   entry.type == DBUS_TYPE_UINT32) {
+			freq_params.freq = entry.uint32_value;
+		} else if (os_strcmp(entry.key, "SecChannelOffset") == 0 &&
+			   entry.type == DBUS_TYPE_UINT32) {
+			freq_params.sec_channel_offset = entry.uint32_value;
+		} else if (os_strcmp(entry.key, "CenterFrequency1") == 0 &&
+			   entry.type == DBUS_TYPE_UINT32) {
+			freq_params.center_freq1 = entry.uint32_value;
+		} else if (os_strcmp(entry.key, "CenterFrequency2") == 0 &&
+			   entry.type == DBUS_TYPE_UINT32) {
+			freq_params.center_freq2 = entry.uint32_value;
+		} else if (os_strcmp(entry.key, "Bandwidth") == 0 &&
+			   entry.type == DBUS_TYPE_UINT32) {
+			freq_params.bandwidth = entry.uint32_value;
+		} else if (os_strcmp(entry.key, "HT") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			freq_params.ht_enabled = entry.bool_value;
+		} else if (os_strcmp(entry.key, "VHT") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			freq_params.vht_enabled = entry.bool_value;
+		} else {
+			wpa_dbus_dict_entry_clear(&entry);
+			return wpas_dbus_error_invalid_args(message, NULL);
+		}
+
+		wpa_dbus_dict_entry_clear(&entry);
+	}
+
+	if (oper_class == 0) {
+		wpa_printf(MSG_INFO,
+			   "tdls_chanswitch: Invalid op class provided");
+		return wpas_dbus_error_invalid_args(
+			message, "Invalid op class provided");
+	}
+
+	if (freq_params.freq == 0) {
+		wpa_printf(MSG_INFO,
+			   "tdls_chanswitch: Invalid freq provided");
+		return wpas_dbus_error_invalid_args(message,
+						    "Invalid freq provided");
+	}
+
+	if (is_peer_present == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "tdls_chanswitch: peer address not provided");
+		return wpas_dbus_error_invalid_args(
+			message, "peer address not provided");
+	}
+
+	wpa_printf(MSG_DEBUG, "dbus: TDLS_CHAN_SWITCH " MACSTR
+		   " OP CLASS %d FREQ %d CENTER1 %d CENTER2 %d BW %d SEC_OFFSET %d%s%s",
+		   MAC2STR(peer), oper_class, freq_params.freq,
+		   freq_params.center_freq1, freq_params.center_freq2,
+		   freq_params.bandwidth, freq_params.sec_channel_offset,
+		   freq_params.ht_enabled ? " HT" : "",
+		   freq_params.vht_enabled ? " VHT" : "");
+
+	ret = wpa_tdls_enable_chan_switch(wpa_s->wpa, peer, oper_class,
+					  &freq_params);
+	if (ret)
+		return wpas_dbus_error_unknown_error(
+			message, "error processing TDLS channel switch");
+
+	return NULL;
+}
+
+/*
+ * wpas_dbus_handler_tdls_cancel_channel_switch - Disable channel switching with TDLS peer
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "TDLSCancelChannelSwitch" method call of network
+ * interface.
+ */
+DBusMessage *
+wpas_dbus_handler_tdls_cancel_channel_switch(DBusMessage *message,
+					     struct wpa_supplicant *wpa_s)
+{
+	u8 peer[ETH_ALEN];
+	DBusMessage *error_reply;
+	int ret;
+
+	if (get_peer_hwaddr_helper(message, __func__, peer, &error_reply) < 0)
+		return error_reply;
+
+	wpa_printf(MSG_DEBUG, "dbus: TDLS_CANCEL_CHAN_SWITCH " MACSTR,
+		   MAC2STR(peer));
+
+	ret = wpa_tdls_disable_chan_switch(wpa_s->wpa, peer);
+	if (ret)
+		return wpas_dbus_error_unknown_error(
+			message, "error canceling TDLS channel switch");
+
+	return NULL;
+}
+
 #endif /* CONFIG_TDLS */
 
 
@@ -2673,6 +2846,11 @@
 	     !wpa_s->conf->p2p_disabled &&
 	     !wpa_dbus_dict_string_array_add_element(
 		     &iter_array, "p2p")) ||
+#ifdef CONFIG_MESH
+	    (res >= 0 && (capa.flags & WPA_DRIVER_FLAGS_MESH) &&
+	     !wpa_dbus_dict_string_array_add_element(
+		     &iter_array, "mesh")) ||
+#endif /* CONFIG_MESH */
 	    !wpa_dbus_dict_end_string_array(&iter_dict,
 					    &iter_dict_entry,
 					    &iter_dict_val,
@@ -4624,3 +4802,100 @@
 	return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
 				      "Not found");
 }
+
+
+#ifdef CONFIG_MESH
+
+/**
+ * wpas_dbus_getter_mesh_peers - Get connected mesh peers
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "MeshPeers" property.
+ */
+dbus_bool_t wpas_dbus_getter_mesh_peers(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	struct hostapd_data *hapd;
+	struct sta_info *sta;
+	DBusMessageIter variant_iter, array_iter;
+	int i;
+	DBusMessageIter inner_array_iter;
+
+	if (!wpa_s->ifmsh)
+		return FALSE;
+	hapd = wpa_s->ifmsh->bss[0];
+
+	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+					      DBUS_TYPE_ARRAY_AS_STRING
+					      DBUS_TYPE_ARRAY_AS_STRING
+					      DBUS_TYPE_BYTE_AS_STRING,
+					      &variant_iter) ||
+	    !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+					      DBUS_TYPE_ARRAY_AS_STRING
+					      DBUS_TYPE_BYTE_AS_STRING,
+					      &array_iter))
+		return FALSE;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (!dbus_message_iter_open_container(
+			    &array_iter, DBUS_TYPE_ARRAY,
+			    DBUS_TYPE_BYTE_AS_STRING,
+			    &inner_array_iter))
+			return FALSE;
+
+		for (i = 0; i < ETH_ALEN; i++) {
+			if (!dbus_message_iter_append_basic(&inner_array_iter,
+							    DBUS_TYPE_BYTE,
+							    &(sta->addr[i])))
+				return FALSE;
+		}
+
+		if (!dbus_message_iter_close_container(
+			    &array_iter, &inner_array_iter))
+			return FALSE;
+	}
+
+	if (!dbus_message_iter_close_container(&variant_iter, &array_iter) ||
+	    !dbus_message_iter_close_container(iter, &variant_iter))
+		return FALSE;
+
+	return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_mesh_group - Get mesh group
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "MeshGroup" property.
+ */
+dbus_bool_t wpas_dbus_getter_mesh_group(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+	if (!wpa_s->ifmsh || !ssid)
+		return FALSE;
+
+	if (!wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+						    (char *) ssid->ssid,
+						    ssid->ssid_len, error)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: error constructing reply", __func__);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+#endif /* CONFIG_MESH */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index 3b8f096..26652ad 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -74,6 +74,9 @@
 DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
 				     struct wpa_supplicant *wpa_s);
 
+DBusMessage * wpas_dbus_handler_abort_scan(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s);
+
 DBusMessage * wpas_dbus_handler_signal_poll(DBusMessage *message,
 					    struct wpa_supplicant *wpa_s);
 
@@ -205,6 +208,9 @@
 DECLARE_ACCESSOR(wpas_dbus_getter_wps_device_device_type);
 DECLARE_ACCESSOR(wpas_dbus_setter_wps_device_device_type);
 
+DECLARE_ACCESSOR(wpas_dbus_getter_mesh_peers);
+DECLARE_ACCESSOR(wpas_dbus_getter_mesh_group);
+
 DBusMessage * wpas_dbus_handler_tdls_discover(DBusMessage *message,
 					      struct wpa_supplicant *wpa_s);
 DBusMessage * wpas_dbus_handler_tdls_setup(DBusMessage *message,
@@ -213,6 +219,12 @@
 					    struct wpa_supplicant *wpa_s);
 DBusMessage * wpas_dbus_handler_tdls_teardown(DBusMessage *message,
 					      struct wpa_supplicant *wpa_s);
+DBusMessage *
+wpas_dbus_handler_tdls_channel_switch(DBusMessage *message,
+				      struct wpa_supplicant *wpa_s);
+DBusMessage *
+wpas_dbus_handler_tdls_cancel_channel_switch(DBusMessage *message,
+					     struct wpa_supplicant *wpa_s);
 
 DBusMessage * wpas_dbus_handler_vendor_elem_add(DBusMessage *message,
 						struct wpa_supplicant *wpa_s);
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index 1e37e27..450023e 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -288,9 +288,6 @@
 # bridge interfaces (commit 'bridge: respect RFC2863 operational state')').
 #CONFIG_NO_LINUX_PACKET_SOCKET_WAR=y
 
-# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
-CONFIG_PEERKEY=y
-
 # IEEE 802.11w (management frame protection), also known as PMF
 # Driver support is also needed for IEEE 802.11w.
 #CONFIG_IEEE80211W=y
@@ -299,6 +296,7 @@
 # openssl = OpenSSL (default)
 # gnutls = GnuTLS
 # internal = Internal TLSv1 implementation (experimental)
+# linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
 # none = Empty template
 #CONFIG_TLS=openssl
 
@@ -316,6 +314,10 @@
 # will be used)
 #CONFIG_TLSV12=y
 
+# Select which ciphers to use by default with OpenSSL if the user does not
+# specify them.
+#CONFIG_TLS_DEFAULT_CIPHERS="DEFAULT:!EXP:!LOW"
+
 # If CONFIG_TLS=internal is used, additional library and include paths are
 # needed for LibTomMath. Alternatively, an integrated, minimal version of
 # LibTomMath can be used. See beginning of libtommath.c for details on benefits
@@ -557,6 +559,8 @@
 # Note: This is an experimental and not yet complete implementation. This
 # should not be enabled for production use.
 #CONFIG_FILS=y
+# FILS shared key authentication with PFS
+#CONFIG_FILS_SK_PFS=y
 
 # Support RSN on IBSS networks
 # This is needed to be able to use mode=1 network profile with proto=RSN and
@@ -580,3 +584,7 @@
 # Learn channels used by the network and try to avoid bgscans on other
 # channels (experimental)
 #CONFIG_BGSCAN_LEARN=y
+
+# Opportunistic Wireless Encryption (OWE)
+# Experimental implementation of draft-harkins-owe-07.txt
+#CONFIG_OWE=y
diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c
new file mode 100644
index 0000000..bf29f19
--- /dev/null
+++ b/wpa_supplicant/dpp_supplicant.c
@@ -0,0 +1,2025 @@
+/*
+ * wpa_supplicant - DPP
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/dpp.h"
+#include "common/gas.h"
+#include "common/gas_server.h"
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "wpa_supplicant_i.h"
+#include "config.h"
+#include "driver_i.h"
+#include "offchannel.h"
+#include "gas_query.h"
+#include "bss.h"
+#include "scan.h"
+#include "notify.h"
+#include "dpp_supplicant.h"
+
+
+static int wpas_dpp_listen_start(struct wpa_supplicant *wpa_s,
+				 unsigned int freq);
+static void wpas_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx);
+static void wpas_dpp_auth_success(struct wpa_supplicant *wpa_s, int initiator);
+static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s,
+			       unsigned int freq, const u8 *dst,
+			       const u8 *src, const u8 *bssid,
+			       const u8 *data, size_t data_len,
+			       enum offchannel_send_action_result result);
+
+static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+/* Use a hardcoded Transaction ID 1 in Peer Discovery frames since there is only
+ * a single transaction in progress at any point in time. */
+static const u8 TRANSACTION_ID = 1;
+
+
+static struct dpp_configurator *
+dpp_configurator_get_id(struct wpa_supplicant *wpa_s, unsigned int id)
+{
+	struct dpp_configurator *conf;
+
+	dl_list_for_each(conf, &wpa_s->dpp_configurator,
+			 struct dpp_configurator, list) {
+		if (conf->id == id)
+			return conf;
+	}
+	return NULL;
+}
+
+
+static unsigned int wpas_dpp_next_id(struct wpa_supplicant *wpa_s)
+{
+	struct dpp_bootstrap_info *bi;
+	unsigned int max_id = 0;
+
+	dl_list_for_each(bi, &wpa_s->dpp_bootstrap, struct dpp_bootstrap_info,
+			 list) {
+		if (bi->id > max_id)
+			max_id = bi->id;
+	}
+	return max_id + 1;
+}
+
+
+/**
+ * wpas_dpp_qr_code - Parse and add DPP bootstrapping info from a QR Code
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @cmd: DPP URI read from a QR Code
+ * Returns: Identifier of the stored info or -1 on failure
+ */
+int wpas_dpp_qr_code(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	struct dpp_bootstrap_info *bi;
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+	bi = dpp_parse_qr_code(cmd);
+	if (!bi)
+		return -1;
+
+	bi->id = wpas_dpp_next_id(wpa_s);
+	dl_list_add(&wpa_s->dpp_bootstrap, &bi->list);
+
+	if (auth && auth->response_pending &&
+	    dpp_notify_new_qr_code(auth, bi) == 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Sending out pending authentication response");
+		offchannel_send_action(wpa_s, auth->curr_freq,
+				       auth->peer_mac_addr, wpa_s->own_addr,
+				       broadcast,
+				       wpabuf_head(auth->resp_msg),
+				       wpabuf_len(auth->resp_msg),
+				       500, wpas_dpp_tx_status, 0);
+	}
+
+	return bi->id;
+}
+
+
+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 wpas_dpp_bootstrap_gen(struct wpa_supplicant *wpa_s, 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 = wpas_dpp_next_id(wpa_s);
+	dl_list_add(&wpa_s->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 wpa_supplicant *wpa_s, unsigned int id)
+{
+	struct dpp_bootstrap_info *bi;
+
+	dl_list_for_each(bi, &wpa_s->dpp_bootstrap, struct dpp_bootstrap_info,
+			 list) {
+		if (bi->id == id)
+			return bi;
+	}
+	return NULL;
+}
+
+
+static int dpp_bootstrap_del(struct wpa_supplicant *wpa_s, unsigned int id)
+{
+	struct dpp_bootstrap_info *bi, *tmp;
+	int found = 0;
+
+	dl_list_for_each_safe(bi, tmp, &wpa_s->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 wpas_dpp_bootstrap_remove(struct wpa_supplicant *wpa_s, 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(wpa_s, id_val);
+}
+
+
+const char * wpas_dpp_bootstrap_get_uri(struct wpa_supplicant *wpa_s,
+					unsigned int id)
+{
+	struct dpp_bootstrap_info *bi;
+
+	bi = dpp_bootstrap_get_id(wpa_s, id);
+	if (!bi)
+		return NULL;
+	return bi->uri;
+}
+
+
+int wpas_dpp_bootstrap_info(struct wpa_supplicant *wpa_s, int id,
+			    char *reply, int reply_size)
+{
+	struct dpp_bootstrap_info *bi;
+
+	bi = dpp_bootstrap_get_id(wpa_s, 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 wpas_dpp_tx_status(struct wpa_supplicant *wpa_s,
+			       unsigned int freq, const u8 *dst,
+			       const u8 *src, const u8 *bssid,
+			       const u8 *data, size_t data_len,
+			       enum offchannel_send_action_result result)
+{
+	wpa_printf(MSG_DEBUG, "DPP: TX status: freq=%u dst=" MACSTR
+		   " result=%s",
+		   freq, MAC2STR(dst),
+		   result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" :
+		   (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" :
+		    "FAILED"));
+
+	if (!wpa_s->dpp_auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore TX status since there is no ongoing authentication exchange");
+		return;
+	}
+
+	if (wpa_s->dpp_auth->remove_on_tx_status) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Terminate authentication exchange due to an earlier error");
+		eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
+		offchannel_send_action_done(wpa_s);
+		dpp_auth_deinit(wpa_s->dpp_auth);
+		wpa_s->dpp_auth = NULL;
+		return;
+	}
+
+	if (wpa_s->dpp_auth_ok_on_ack)
+		wpas_dpp_auth_success(wpa_s, 1);
+
+	if (!is_broadcast_ether_addr(dst) &&
+	    result != OFFCHANNEL_SEND_ACTION_SUCCESS) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unicast DPP Action frame was not ACKed");
+		/* TODO: In case of DPP Authentication Request frame, move to
+		 * the next channel immediately */
+	}
+}
+
+
+static void wpas_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	if (!wpa_s->dpp_auth)
+		return;
+	wpa_printf(MSG_DEBUG, "DPP: Continue reply wait on channel %u MHz",
+		   wpa_s->dpp_auth->curr_freq);
+	wpas_dpp_listen_start(wpa_s, wpa_s->dpp_auth->curr_freq);
+}
+
+
+static void wpas_dpp_set_testing_options(struct wpa_supplicant *wpa_s,
+					 struct dpp_authentication *auth)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	if (wpa_s->dpp_config_obj_override)
+		auth->config_obj_override =
+			os_strdup(wpa_s->dpp_config_obj_override);
+	if (wpa_s->dpp_discovery_override)
+		auth->discovery_override =
+			os_strdup(wpa_s->dpp_discovery_override);
+	if (wpa_s->dpp_groups_override)
+		auth->groups_override =
+			os_strdup(wpa_s->dpp_groups_override);
+	auth->ignore_netaccesskey_mismatch =
+		wpa_s->dpp_ignore_netaccesskey_mismatch;
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
+static void wpas_dpp_set_configurator(struct wpa_supplicant *wpa_s,
+				      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;
+
+	if (!cmd)
+		return;
+
+	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;
+	}
+
+	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")) {
+			conf_sta->dpp = 0;
+			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->dpp = 1;
+		} else {
+			goto fail;
+		}
+	}
+
+	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")) {
+			conf_ap->dpp = 0;
+			if (psk_set) {
+				os_memcpy(conf_ap->psk, psk, PMK_LEN);
+			} else {
+				conf_ap->passphrase = os_strdup(pass);
+				if (!conf_ap->passphrase)
+					goto fail;
+			}
+		} else if (os_strstr(cmd, " conf=ap-dpp")) {
+			conf_ap->dpp = 1;
+		} else {
+			goto fail;
+		}
+	}
+
+	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) {
+		pos += 14;
+		conf = dpp_configurator_get_id(wpa_s, 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;
+	return;
+
+fail:
+	wpa_printf(MSG_DEBUG, "DPP: Failed to set configurator parameters");
+	dpp_configuration_free(conf_sta);
+	dpp_configuration_free(conf_ap);
+}
+
+
+int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	const char *pos;
+	struct dpp_bootstrap_info *peer_bi, *own_bi = NULL;
+	const u8 *dst;
+	int res;
+	int configurator = 1;
+	unsigned int wait_time;
+
+	wpa_s->dpp_gas_client = 0;
+
+	pos = os_strstr(cmd, " peer=");
+	if (!pos)
+		return -1;
+	pos += 6;
+	peer_bi = dpp_bootstrap_get_id(wpa_s, atoi(pos));
+	if (!peer_bi) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Could not find bootstrapping info for the identified peer");
+		return -1;
+	}
+
+	pos = os_strstr(cmd, " own=");
+	if (pos) {
+		pos += 5;
+		own_bi = dpp_bootstrap_get_id(wpa_s, atoi(pos));
+		if (!own_bi) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Could not find bootstrapping info for the identified local entry");
+			return -1;
+		}
+
+		if (peer_bi->curve != own_bi->curve) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Mismatching curves in bootstrapping info (peer=%s own=%s)",
+				   peer_bi->curve->name, own_bi->curve->name);
+			return -1;
+		}
+	}
+
+	pos = os_strstr(cmd, " role=");
+	if (pos) {
+		pos += 6;
+		if (os_strncmp(pos, "configurator", 12) == 0)
+			configurator = 1;
+		else if (os_strncmp(pos, "enrollee", 8) == 0)
+			configurator = 0;
+		else
+			goto fail;
+	}
+
+	pos = os_strstr(cmd, " netrole=");
+	if (pos) {
+		pos += 9;
+		wpa_s->dpp_netrole_ap = os_strncmp(pos, "ap", 2) == 0;
+	}
+
+	if (wpa_s->dpp_auth) {
+		eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
+		offchannel_send_action_done(wpa_s);
+		dpp_auth_deinit(wpa_s->dpp_auth);
+	}
+	wpa_s->dpp_auth = dpp_auth_init(wpa_s, peer_bi, own_bi, configurator);
+	if (!wpa_s->dpp_auth)
+		goto fail;
+	wpas_dpp_set_testing_options(wpa_s, wpa_s->dpp_auth);
+	wpas_dpp_set_configurator(wpa_s, wpa_s->dpp_auth, cmd);
+
+	/* TODO: Support iteration over all frequencies and filtering of
+	 * frequencies based on locally enabled channels that allow initiation
+	 * of transmission. */
+	if (peer_bi->num_freq > 0)
+		wpa_s->dpp_auth->curr_freq = peer_bi->freq[0];
+	else
+		wpa_s->dpp_auth->curr_freq = 2412;
+
+	if (is_zero_ether_addr(peer_bi->mac_addr)) {
+		dst = broadcast;
+	} else {
+		dst = peer_bi->mac_addr;
+		os_memcpy(wpa_s->dpp_auth->peer_mac_addr, peer_bi->mac_addr,
+			  ETH_ALEN);
+	}
+	wpa_s->dpp_auth_ok_on_ack = 0;
+	eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
+	wait_time = wpa_s->max_remain_on_chan;
+	if (wait_time > 2000)
+		wait_time = 2000;
+	eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
+			       wpas_dpp_reply_wait_timeout,
+			       wpa_s, NULL);
+	res = offchannel_send_action(wpa_s, wpa_s->dpp_auth->curr_freq,
+				     dst, wpa_s->own_addr, broadcast,
+				     wpabuf_head(wpa_s->dpp_auth->req_msg),
+				     wpabuf_len(wpa_s->dpp_auth->req_msg),
+				     wait_time, wpas_dpp_tx_status, 0);
+
+	return res;
+fail:
+	return -1;
+}
+
+
+struct wpas_dpp_listen_work {
+	unsigned int freq;
+	unsigned int duration;
+	struct wpabuf *probe_resp_ie;
+};
+
+
+static void wpas_dpp_listen_work_free(struct wpas_dpp_listen_work *lwork)
+{
+	if (!lwork)
+		return;
+	os_free(lwork);
+}
+
+
+static void wpas_dpp_listen_work_done(struct wpa_supplicant *wpa_s)
+{
+	struct wpas_dpp_listen_work *lwork;
+
+	if (!wpa_s->dpp_listen_work)
+		return;
+
+	lwork = wpa_s->dpp_listen_work->ctx;
+	wpas_dpp_listen_work_free(lwork);
+	radio_work_done(wpa_s->dpp_listen_work);
+	wpa_s->dpp_listen_work = NULL;
+}
+
+
+static void dpp_start_listen_cb(struct wpa_radio_work *work, int deinit)
+{
+	struct wpa_supplicant *wpa_s = work->wpa_s;
+	struct wpas_dpp_listen_work *lwork = work->ctx;
+
+	if (deinit) {
+		if (work->started) {
+			wpa_s->dpp_listen_work = NULL;
+			wpas_dpp_listen_stop(wpa_s);
+		}
+		wpas_dpp_listen_work_free(lwork);
+		return;
+	}
+
+	wpa_s->dpp_listen_work = work;
+
+	wpa_s->dpp_pending_listen_freq = lwork->freq;
+
+	if (wpa_drv_remain_on_channel(wpa_s, lwork->freq,
+				      wpa_s->max_remain_on_chan) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to request the driver to remain on channel (%u MHz) for listen",
+			   lwork->freq);
+		wpas_dpp_listen_work_done(wpa_s);
+		wpa_s->dpp_pending_listen_freq = 0;
+		return;
+	}
+	wpa_s->off_channel_freq = 0;
+	wpa_s->roc_waiting_drv_freq = lwork->freq;
+}
+
+
+static int wpas_dpp_listen_start(struct wpa_supplicant *wpa_s,
+				 unsigned int freq)
+{
+	struct wpas_dpp_listen_work *lwork;
+
+	if (wpa_s->dpp_listen_work) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Reject start_listen since dpp_listen_work already exists");
+		return -1;
+	}
+
+	if (wpa_s->dpp_listen_freq)
+		wpas_dpp_listen_stop(wpa_s);
+	wpa_s->dpp_listen_freq = freq;
+
+	lwork = os_zalloc(sizeof(*lwork));
+	if (!lwork)
+		return -1;
+	lwork->freq = freq;
+
+	if (radio_add_work(wpa_s, freq, "dpp-listen", 0, dpp_start_listen_cb,
+			   lwork) < 0) {
+		wpas_dpp_listen_work_free(lwork);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int wpas_dpp_listen(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	int freq;
+
+	freq = atoi(cmd);
+	if (freq <= 0)
+		return -1;
+
+	if (os_strstr(cmd, " role=configurator"))
+		wpa_s->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR;
+	else if (os_strstr(cmd, " role=enrollee"))
+		wpa_s->dpp_allowed_roles = DPP_CAPAB_ENROLLEE;
+	else
+		wpa_s->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR |
+			DPP_CAPAB_ENROLLEE;
+	wpa_s->dpp_qr_mutual = os_strstr(cmd, " qr=mutual") != NULL;
+	wpa_s->dpp_netrole_ap = os_strstr(cmd, " netrole=ap") != NULL;
+	if (wpa_s->dpp_listen_freq == (unsigned int) freq) {
+		wpa_printf(MSG_DEBUG, "DPP: Already listening on %u MHz",
+			   freq);
+		return 0;
+	}
+
+	return wpas_dpp_listen_start(wpa_s, freq);
+}
+
+
+void wpas_dpp_listen_stop(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->dpp_listen_freq)
+		return;
+
+	wpa_printf(MSG_DEBUG, "DPP: Stop listen on %u MHz",
+		   wpa_s->dpp_listen_freq);
+	wpa_drv_cancel_remain_on_channel(wpa_s);
+	wpa_s->dpp_listen_freq = 0;
+	wpas_dpp_listen_work_done(wpa_s);
+}
+
+
+void wpas_dpp_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+				   unsigned int freq)
+{
+	if (!wpa_s->dpp_listen_freq && !wpa_s->dpp_pending_listen_freq)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: remain-on-channel callback (off_channel_freq=%u dpp_pending_listen_freq=%d roc_waiting_drv_freq=%d freq=%u)",
+		   wpa_s->off_channel_freq, wpa_s->dpp_pending_listen_freq,
+		   wpa_s->roc_waiting_drv_freq, freq);
+	if (wpa_s->off_channel_freq &&
+	    wpa_s->off_channel_freq == wpa_s->dpp_pending_listen_freq) {
+		wpa_printf(MSG_DEBUG, "DPP: Listen on %u MHz started", freq);
+		wpa_s->dpp_pending_listen_freq = 0;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore remain-on-channel callback (off_channel_freq=%u dpp_pending_listen_freq=%d freq=%u)",
+			   wpa_s->off_channel_freq,
+			   wpa_s->dpp_pending_listen_freq, freq);
+	}
+}
+
+
+void wpas_dpp_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+					  unsigned int freq)
+{
+	wpas_dpp_listen_work_done(wpa_s);
+
+	if (wpa_s->dpp_auth && !wpa_s->dpp_gas_client) {
+		/* Continue listen with a new remain-on-channel */
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Continue wait on %u MHz for the ongoing DPP provisioning session",
+			   wpa_s->dpp_auth->curr_freq);
+		wpas_dpp_listen_start(wpa_s, wpa_s->dpp_auth->curr_freq);
+		return;
+	}
+
+	if (wpa_s->dpp_listen_freq) {
+		/* Continue listen with a new remain-on-channel */
+		wpas_dpp_listen_start(wpa_s, wpa_s->dpp_listen_freq);
+	}
+}
+
+
+static void wpas_dpp_rx_auth_req(struct wpa_supplicant *wpa_s, const u8 *src,
+				 const u8 *hdr, const u8 *buf, size_t len,
+				 unsigned int freq)
+{
+	const u8 *r_bootstrap, *i_bootstrap, *wrapped_data;
+	u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len;
+	struct dpp_bootstrap_info *bi, *own_bi = NULL, *peer_bi = NULL;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR,
+		   MAC2STR(src));
+
+	wrapped_data = dpp_get_attr(buf, len, DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing required Wrapped data attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped data",
+		    wrapped_data, wrapped_data_len);
+
+	r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+				   &r_bootstrap_len);
+	if (!r_bootstrap || r_bootstrap > wrapped_data ||
+	    r_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+		    r_bootstrap, r_bootstrap_len);
+
+	i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+				   &i_bootstrap_len);
+	if (!i_bootstrap || i_bootstrap > wrapped_data ||
+	    i_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Initiator Bootstrapping Key Hash attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash",
+		    i_bootstrap, i_bootstrap_len);
+
+	/* Try to find own and peer bootstrapping key matches based on the
+	 * received hash values */
+	dl_list_for_each(bi, &wpa_s->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;
+	}
+
+	if (!own_bi) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No matching own bootstrapping key found - ignore message");
+		return;
+	}
+
+	if (wpa_s->dpp_auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Already in DPP authentication exchange - ignore new one");
+		return;
+	}
+
+	wpa_s->dpp_gas_client = 0;
+	wpa_s->dpp_auth_ok_on_ack = 0;
+	wpa_s->dpp_auth = dpp_auth_req_rx(wpa_s, wpa_s->dpp_allowed_roles,
+					  wpa_s->dpp_qr_mutual,
+					  peer_bi, own_bi, freq, hdr, buf,
+					  wrapped_data, wrapped_data_len);
+	if (!wpa_s->dpp_auth) {
+		wpa_printf(MSG_DEBUG, "DPP: No response generated");
+		return;
+	}
+	wpas_dpp_set_testing_options(wpa_s, wpa_s->dpp_auth);
+	wpas_dpp_set_configurator(wpa_s, wpa_s->dpp_auth,
+				  wpa_s->dpp_configurator_params);
+	os_memcpy(wpa_s->dpp_auth->peer_mac_addr, src, ETH_ALEN);
+
+	offchannel_send_action(wpa_s, wpa_s->dpp_auth->curr_freq,
+			       src, wpa_s->own_addr, broadcast,
+			       wpabuf_head(wpa_s->dpp_auth->resp_msg),
+			       wpabuf_len(wpa_s->dpp_auth->resp_msg),
+			       500, wpas_dpp_tx_status, 0);
+}
+
+
+static void wpas_dpp_start_gas_server(struct wpa_supplicant *wpa_s)
+{
+	/* TODO: stop wait and start ROC */
+}
+
+
+static struct wpa_ssid * wpas_dpp_add_network(struct wpa_supplicant *wpa_s,
+					      struct dpp_authentication *auth)
+{
+	struct wpa_ssid *ssid;
+
+	ssid = wpa_config_add_network(wpa_s->conf);
+	if (!ssid)
+		return NULL;
+	wpas_notify_network_added(wpa_s, ssid);
+	wpa_config_set_network_defaults(ssid);
+	ssid->disabled = 1;
+
+	ssid->ssid = os_malloc(auth->ssid_len);
+	if (!ssid->ssid)
+		goto fail;
+	os_memcpy(ssid->ssid, auth->ssid, auth->ssid_len);
+	ssid->ssid_len = auth->ssid_len;
+
+	if (auth->connector) {
+		ssid->key_mgmt = WPA_KEY_MGMT_DPP;
+		ssid->dpp_connector = os_strdup(auth->connector);
+		if (!ssid->dpp_connector)
+			goto fail;
+	}
+
+	if (auth->c_sign_key) {
+		ssid->dpp_csign = os_malloc(wpabuf_len(auth->c_sign_key));
+		if (!ssid->dpp_csign)
+			goto fail;
+		os_memcpy(ssid->dpp_csign, wpabuf_head(auth->c_sign_key),
+			  wpabuf_len(auth->c_sign_key));
+		ssid->dpp_csign_len = wpabuf_len(auth->c_sign_key);
+	}
+
+	if (auth->net_access_key) {
+		ssid->dpp_netaccesskey =
+			os_malloc(wpabuf_len(auth->net_access_key));
+		if (!ssid->dpp_netaccesskey)
+			goto fail;
+		os_memcpy(ssid->dpp_netaccesskey,
+			  wpabuf_head(auth->net_access_key),
+			  wpabuf_len(auth->net_access_key));
+		ssid->dpp_netaccesskey_len = wpabuf_len(auth->net_access_key);
+		ssid->dpp_netaccesskey_expiry = auth->net_access_key_expiry;
+	}
+
+	if (!auth->connector) {
+		ssid->key_mgmt = WPA_KEY_MGMT_PSK;
+		if (auth->passphrase[0]) {
+			if (wpa_config_set_quoted(ssid, "psk",
+						  auth->passphrase) < 0)
+				goto fail;
+			wpa_config_update_psk(ssid);
+			ssid->export_keys = 1;
+		} else {
+			ssid->psk_set = auth->psk_set;
+			os_memcpy(ssid->psk, auth->psk, PMK_LEN);
+		}
+	}
+
+	return ssid;
+fail:
+	wpas_notify_network_removed(wpa_s, ssid);
+	wpa_config_remove_network(wpa_s->conf, ssid->id);
+	return NULL;
+}
+
+
+static void wpas_dpp_process_config(struct wpa_supplicant *wpa_s,
+				    struct dpp_authentication *auth)
+{
+	struct wpa_ssid *ssid;
+
+	if (wpa_s->conf->dpp_config_processing < 1)
+		return;
+
+	ssid = wpas_dpp_add_network(wpa_s, auth);
+	if (!ssid)
+		return;
+
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_NETWORK_ID "%d", ssid->id);
+	if (wpa_s->conf->dpp_config_processing < 2)
+		return;
+
+	wpa_printf(MSG_DEBUG, "DPP: Trying to connect to the new network");
+	ssid->disabled = 0;
+	wpa_s->disconnected = 0;
+	wpa_s->reassociate = 1;
+	wpa_s->scan_runs = 0;
+	wpa_s->normal_scans = 0;
+	wpa_supplicant_cancel_sched_scan(wpa_s);
+	wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static void wpas_dpp_handle_config_obj(struct wpa_supplicant *wpa_s,
+				       struct dpp_authentication *auth)
+{
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_RECEIVED);
+	if (auth->ssid_len)
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONFOBJ_SSID "%s",
+			wpa_ssid_txt(auth->ssid, auth->ssid_len));
+	if (auth->connector) {
+		/* TODO: Save the Connector and consider using a command
+		 * to fetch the value instead of sending an event with
+		 * it. The Connector could end up being larger than what
+		 * most clients are ready to receive as an event
+		 * message. */
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONNECTOR "%s",
+			auth->connector);
+	}
+	if (auth->c_sign_key) {
+		char *hex;
+		size_t hexlen;
+
+		hexlen = 2 * wpabuf_len(auth->c_sign_key) + 1;
+		hex = os_malloc(hexlen);
+		if (hex) {
+			wpa_snprintf_hex(hex, hexlen,
+					 wpabuf_head(auth->c_sign_key),
+					 wpabuf_len(auth->c_sign_key));
+			wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_C_SIGN_KEY "%s",
+				hex);
+			os_free(hex);
+		}
+	}
+	if (auth->net_access_key) {
+		char *hex;
+		size_t hexlen;
+
+		hexlen = 2 * wpabuf_len(auth->net_access_key) + 1;
+		hex = os_malloc(hexlen);
+		if (hex) {
+			wpa_snprintf_hex(hex, hexlen,
+					 wpabuf_head(auth->net_access_key),
+					 wpabuf_len(auth->net_access_key));
+			if (auth->net_access_key_expiry)
+				wpa_msg(wpa_s, MSG_INFO,
+					DPP_EVENT_NET_ACCESS_KEY "%s %lu", hex,
+					(long unsigned)
+					auth->net_access_key_expiry);
+			else
+				wpa_msg(wpa_s, MSG_INFO,
+					DPP_EVENT_NET_ACCESS_KEY "%s", hex);
+			os_free(hex);
+		}
+	}
+
+	wpas_dpp_process_config(wpa_s, auth);
+}
+
+
+static void wpas_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
+				 enum gas_query_result result,
+				 const struct wpabuf *adv_proto,
+				 const struct wpabuf *resp, u16 status_code)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	const u8 *pos;
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+	if (!auth || !auth->auth_success) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+		return;
+	}
+	if (!resp || status_code != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "DPP: GAS query did not succeed");
+		goto fail;
+	}
+
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response adv_proto",
+			adv_proto);
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response (GAS response)",
+			resp);
+
+	if (wpabuf_len(adv_proto) != 10 ||
+	    !(pos = wpabuf_head(adv_proto)) ||
+	    pos[0] != WLAN_EID_ADV_PROTO ||
+	    pos[1] != 8 ||
+	    pos[3] != WLAN_EID_VENDOR_SPECIFIC ||
+	    pos[4] != 5 ||
+	    WPA_GET_BE24(&pos[5]) != OUI_WFA ||
+	    pos[8] != 0x1a ||
+	    pos[9] != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Not a DPP Advertisement Protocol ID");
+		goto fail;
+	}
+
+	if (dpp_conf_resp_rx(auth, resp) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
+		goto fail;
+	}
+
+	wpas_dpp_handle_config_obj(wpa_s, auth);
+	dpp_auth_deinit(wpa_s->dpp_auth);
+	wpa_s->dpp_auth = NULL;
+	return;
+
+fail:
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED);
+	dpp_auth_deinit(wpa_s->dpp_auth);
+	wpa_s->dpp_auth = NULL;
+}
+
+
+static void wpas_dpp_start_gas_client(struct wpa_supplicant *wpa_s)
+{
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+	struct wpabuf *buf, *conf_req;
+	char json[100];
+	int res;
+
+	wpa_s->dpp_gas_client = 1;
+	os_snprintf(json, sizeof(json),
+		    "{\"name\":\"Test\","
+		    "\"wi-fi_tech\":\"infra\","
+		    "\"netRole\":\"%s\"}",
+		    wpa_s->dpp_netrole_ap ? "ap" : "sta");
+	wpa_printf(MSG_DEBUG, "DPP: GAS Config Attributes: %s", json);
+
+	offchannel_send_action_done(wpa_s);
+	wpas_dpp_listen_stop(wpa_s);
+
+	conf_req = dpp_build_conf_req(auth, json);
+	if (!conf_req) {
+		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);
+
+	res = gas_query_req(wpa_s->gas, auth->peer_mac_addr, auth->curr_freq,
+			    buf, wpas_dpp_gas_resp_cb, wpa_s);
+	if (res < 0) {
+		wpa_msg(wpa_s, MSG_DEBUG, "GAS: Failed to send Query Request");
+		wpabuf_free(buf);
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: GAS query started with dialog token %u", res);
+	}
+}
+
+
+static void wpas_dpp_auth_success(struct wpa_supplicant *wpa_s, int initiator)
+{
+	wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=%d", initiator);
+
+	if (wpa_s->dpp_auth->configurator)
+		wpas_dpp_start_gas_server(wpa_s);
+	else
+		wpas_dpp_start_gas_client(wpa_s);
+}
+
+
+static void wpas_dpp_rx_auth_resp(struct wpa_supplicant *wpa_s, const u8 *src,
+				  const u8 *hdr, const u8 *buf, size_t len)
+{
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+	struct wpabuf *msg;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication Response from " MACSTR,
+		   MAC2STR(src));
+
+	if (!auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Authentication in progress - drop");
+		return;
+	}
+
+	if (!is_zero_ether_addr(auth->peer_mac_addr) &&
+	    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;
+	}
+
+	eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
+
+	msg = dpp_auth_resp_rx(auth, hdr, buf, len);
+	if (!msg) {
+		if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Start wait for full response");
+			offchannel_send_action_done(wpa_s);
+			wpas_dpp_listen_start(wpa_s, auth->curr_freq);
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "DPP: No confirm generated");
+		return;
+	}
+	os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
+
+	offchannel_send_action(wpa_s, auth->curr_freq,
+			       src, wpa_s->own_addr, broadcast,
+			       wpabuf_head(msg), wpabuf_len(msg),
+			       500, wpas_dpp_tx_status, 0);
+	wpabuf_free(msg);
+	wpa_s->dpp_auth_ok_on_ack = 1;
+}
+
+
+static void wpas_dpp_rx_auth_conf(struct wpa_supplicant *wpa_s, const u8 *src,
+				  const u8 *hdr, const u8 *buf, size_t len)
+{
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation from " MACSTR,
+		   MAC2STR(src));
+
+	if (!auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Authentication in progress - drop");
+		return;
+	}
+
+	if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+			   MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+		return;
+	}
+
+	if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
+		return;
+	}
+
+	wpas_dpp_auth_success(wpa_s, 0);
+}
+
+
+static void wpas_dpp_rx_peer_disc_resp(struct wpa_supplicant *wpa_s,
+				       const u8 *src,
+				       const u8 *buf, size_t len)
+{
+	struct wpa_ssid *ssid;
+	const u8 *connector, *trans_id;
+	u16 connector_len, trans_id_len;
+	struct dpp_introduction intro;
+	struct rsn_pmksa_cache_entry *entry;
+	struct os_time now;
+	struct os_reltime rnow;
+	os_time_t expiry;
+	unsigned int seconds;
+
+	wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Response from " MACSTR,
+		   MAC2STR(src));
+	if (is_zero_ether_addr(wpa_s->dpp_intro_bssid) ||
+	    os_memcmp(src, wpa_s->dpp_intro_bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Not waiting for response from "
+			   MACSTR " - drop", MAC2STR(src));
+		return;
+	}
+	offchannel_send_action_done(wpa_s);
+
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+		if (ssid == wpa_s->dpp_intro_network)
+			break;
+	}
+	if (!ssid || !ssid->dpp_connector || !ssid->dpp_netaccesskey ||
+	    !ssid->dpp_csign) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Profile not found for network introduction");
+		return;
+	}
+
+	trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
+			       &trans_id_len);
+	if (!trans_id || trans_id_len != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include Transaction ID");
+		goto fail;
+	}
+	if (trans_id[0] != TRANSACTION_ID) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore frame with unexpected Transaction ID %u",
+			   trans_id[0]);
+		goto fail;
+	}
+
+	connector = dpp_get_attr(buf, len, DPP_ATTR_CONNECTOR, &connector_len);
+	if (!connector) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include its Connector");
+		return;
+	}
+
+	if (dpp_peer_intro(&intro, ssid->dpp_connector,
+			   ssid->dpp_netaccesskey,
+			   ssid->dpp_netaccesskey_len,
+			   ssid->dpp_csign,
+			   ssid->dpp_csign_len,
+			   connector, connector_len, &expiry) < 0) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Network Introduction protocol resulted in failure");
+		goto fail;
+	}
+
+	entry = os_zalloc(sizeof(*entry));
+	if (!entry)
+		goto fail;
+	os_memcpy(entry->aa, src, ETH_ALEN);
+	os_memcpy(entry->pmkid, intro.pmkid, PMKID_LEN);
+	os_memcpy(entry->pmk, intro.pmk, intro.pmk_len);
+	entry->pmk_len = intro.pmk_len;
+	entry->akmp = WPA_KEY_MGMT_DPP;
+	if (expiry) {
+		os_get_time(&now);
+		seconds = expiry - now.sec;
+	} else {
+		seconds = 86400 * 7;
+	}
+	os_get_reltime(&rnow);
+	entry->expiration = rnow.sec + seconds;
+	entry->reauth_time = rnow.sec + seconds;
+	entry->network_ctx = ssid;
+	wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry);
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Try connection again after successful network introduction");
+	if (wpa_supplicant_fast_associate(wpa_s) != 1) {
+		wpa_supplicant_cancel_sched_scan(wpa_s);
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+	}
+fail:
+	os_memset(&intro, 0, sizeof(intro));
+}
+
+
+static void
+wpas_dpp_tx_pkex_status(struct wpa_supplicant *wpa_s,
+			unsigned int freq, const u8 *dst,
+			const u8 *src, const u8 *bssid,
+			const u8 *data, size_t data_len,
+			enum offchannel_send_action_result result)
+{
+	wpa_printf(MSG_DEBUG, "DPP: TX status: freq=%u dst=" MACSTR
+		   " result=%s (PKEX)",
+		   freq, MAC2STR(dst),
+		   result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" :
+		   (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" :
+		    "FAILED"));
+	/* TODO: Time out wait for response more quickly in error cases? */
+}
+
+
+static void
+wpas_dpp_rx_pkex_exchange_req(struct wpa_supplicant *wpa_s, const u8 *src,
+			      const u8 *buf, size_t len, unsigned int freq)
+{
+	struct wpabuf *msg;
+	unsigned int wait_time;
+
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request from " MACSTR,
+		   MAC2STR(src));
+
+	/* TODO: Support multiple PKEX codes by iterating over all the enabled
+	 * values here */
+
+	if (!wpa_s->dpp_pkex_code || !wpa_s->dpp_pkex_bi) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No PKEX code configured - ignore request");
+		return;
+	}
+
+	if (wpa_s->dpp_pkex) {
+		/* TODO: Support parallel operations */
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Already in PKEX session - ignore new request");
+		return;
+	}
+
+	wpa_s->dpp_pkex = dpp_pkex_rx_exchange_req(wpa_s->dpp_pkex_bi,
+						   wpa_s->own_addr, src,
+						   wpa_s->dpp_pkex_identifier,
+						   wpa_s->dpp_pkex_code,
+						   buf, len);
+	if (!wpa_s->dpp_pkex) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to process the request - ignore it");
+		return;
+	}
+
+	msg = wpa_s->dpp_pkex->exchange_resp;
+	wait_time = wpa_s->max_remain_on_chan;
+	if (wait_time > 2000)
+		wait_time = 2000;
+	offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr,
+			       broadcast,
+			       wpabuf_head(msg), wpabuf_len(msg),
+			       wait_time, wpas_dpp_tx_pkex_status, 0);
+}
+
+
+static void
+wpas_dpp_rx_pkex_exchange_resp(struct wpa_supplicant *wpa_s, const u8 *src,
+			       const u8 *buf, size_t len, unsigned int freq)
+{
+	struct wpabuf *msg;
+	unsigned int wait_time;
+
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response from " MACSTR,
+		   MAC2STR(src));
+
+	/* TODO: Support multiple PKEX codes by iterating over all the enabled
+	 * values here */
+
+	if (!wpa_s->dpp_pkex || !wpa_s->dpp_pkex->initiator ||
+	    wpa_s->dpp_pkex->exchange_done) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
+		return;
+	}
+
+	os_memcpy(wpa_s->dpp_pkex->peer_mac, src, ETH_ALEN);
+	msg = dpp_pkex_rx_exchange_resp(wpa_s->dpp_pkex, buf, len);
+	if (!msg) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Request to " MACSTR,
+		   MAC2STR(src));
+
+	wait_time = wpa_s->max_remain_on_chan;
+	if (wait_time > 2000)
+		wait_time = 2000;
+	offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr,
+			       broadcast,
+			       wpabuf_head(msg), wpabuf_len(msg),
+			       wait_time, wpas_dpp_tx_pkex_status, 0);
+	wpabuf_free(msg);
+}
+
+
+static void
+wpas_dpp_rx_pkex_commit_reveal_req(struct wpa_supplicant *wpa_s, const u8 *src,
+				   const u8 *hdr, const u8 *buf, size_t len,
+				   unsigned int freq)
+{
+	struct wpabuf *msg;
+	unsigned int wait_time;
+	struct dpp_pkex *pkex = wpa_s->dpp_pkex;
+	struct dpp_bootstrap_info *bi;
+
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Request from " MACSTR,
+		   MAC2STR(src));
+
+	if (!pkex || pkex->initiator || !pkex->exchange_done) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
+		return;
+	}
+
+	msg = dpp_pkex_rx_commit_reveal_req(pkex, hdr, buf, len);
+	if (!msg) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to process the request");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Response to "
+		   MACSTR, MAC2STR(src));
+
+	wait_time = wpa_s->max_remain_on_chan;
+	if (wait_time > 2000)
+		wait_time = 2000;
+	offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr,
+			       broadcast,
+			       wpabuf_head(msg), wpabuf_len(msg),
+			       wait_time, wpas_dpp_tx_pkex_status, 0);
+	wpabuf_free(msg);
+
+	bi = os_zalloc(sizeof(*bi));
+	if (!bi)
+		return;
+	bi->id = wpas_dpp_next_id(wpa_s);
+	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);
+	wpa_s->dpp_pkex = NULL;
+	if (dpp_bootstrap_key_hash(bi) < 0) {
+		dpp_bootstrap_info_free(bi);
+		return;
+	}
+	dl_list_add(&wpa_s->dpp_bootstrap, &bi->list);
+}
+
+
+static void
+wpas_dpp_rx_pkex_commit_reveal_resp(struct wpa_supplicant *wpa_s, const u8 *src,
+				    const u8 *hdr, const u8 *buf, size_t len,
+				    unsigned int freq)
+{
+	int res;
+	struct dpp_bootstrap_info *bi, *own_bi;
+	struct dpp_pkex *pkex = wpa_s->dpp_pkex;
+	char cmd[500];
+
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Response from " MACSTR,
+		   MAC2STR(src));
+
+	if (!pkex || !pkex->initiator || !pkex->exchange_done) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
+		return;
+	}
+
+	res = dpp_pkex_rx_commit_reveal_resp(pkex, hdr, buf, len);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
+		return;
+	}
+
+	own_bi = pkex->own_bi;
+
+	bi = os_zalloc(sizeof(*bi));
+	if (!bi)
+		return;
+	bi->id = wpas_dpp_next_id(wpa_s);
+	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);
+	wpa_s->dpp_pkex = NULL;
+	if (dpp_bootstrap_key_hash(bi) < 0) {
+		dpp_bootstrap_info_free(bi);
+		return;
+	}
+	dl_list_add(&wpa_s->dpp_bootstrap, &bi->list);
+
+	os_snprintf(cmd, sizeof(cmd), " peer=%u %s",
+		    bi->id,
+		    wpa_s->dpp_pkex_auth_cmd ? wpa_s->dpp_pkex_auth_cmd : "");
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Start authentication after PKEX with parameters: %s",
+		   cmd);
+	if (wpas_dpp_auth_init(wpa_s, cmd) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Authentication initialization failed");
+		return;
+	}
+}
+
+
+void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src,
+			const u8 *buf, size_t len, unsigned int freq)
+{
+	u8 crypto_suite;
+	enum dpp_public_action_frame_type type;
+	const u8 *hdr;
+
+	if (len < DPP_HDR_LEN)
+		return;
+	if (WPA_GET_BE24(buf) != OUI_WFA || buf[3] != DPP_OUI_TYPE)
+		return;
+	hdr = buf;
+	buf += 4;
+	len -= 4;
+	crypto_suite = *buf++;
+	type = *buf++;
+	len -= 2;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Received DPP Public Action frame crypto suite %u type %d from "
+		   MACSTR " freq=%u",
+		   crypto_suite, type, MAC2STR(src), freq);
+	if (crypto_suite != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: Unsupported crypto suite %u",
+			   crypto_suite);
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes", buf, len);
+	if (dpp_check_attrs(buf, len) < 0)
+		return;
+
+	switch (type) {
+	case DPP_PA_AUTHENTICATION_REQ:
+		wpas_dpp_rx_auth_req(wpa_s, src, hdr, buf, len, freq);
+		break;
+	case DPP_PA_AUTHENTICATION_RESP:
+		wpas_dpp_rx_auth_resp(wpa_s, src, hdr, buf, len);
+		break;
+	case DPP_PA_AUTHENTICATION_CONF:
+		wpas_dpp_rx_auth_conf(wpa_s, src, hdr, buf, len);
+		break;
+	case DPP_PA_PEER_DISCOVERY_RESP:
+		wpas_dpp_rx_peer_disc_resp(wpa_s, src, buf, len);
+		break;
+	case DPP_PA_PKEX_EXCHANGE_REQ:
+		wpas_dpp_rx_pkex_exchange_req(wpa_s, src, buf, len, freq);
+		break;
+	case DPP_PA_PKEX_EXCHANGE_RESP:
+		wpas_dpp_rx_pkex_exchange_resp(wpa_s, src, buf, len, freq);
+		break;
+	case DPP_PA_PKEX_COMMIT_REVEAL_REQ:
+		wpas_dpp_rx_pkex_commit_reveal_req(wpa_s, src, hdr, buf, len,
+						   freq);
+		break;
+	case DPP_PA_PKEX_COMMIT_REVEAL_RESP:
+		wpas_dpp_rx_pkex_commit_reveal_resp(wpa_s, src, hdr, buf, len,
+						    freq);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignored unsupported frame subtype %d", type);
+		break;
+	}
+}
+
+
+static struct wpabuf *
+wpas_dpp_gas_req_handler(void *ctx, const u8 *sa, const u8 *query,
+			 size_t query_len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+	struct wpabuf *resp;
+
+	wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR,
+		   MAC2STR(sa));
+	if (!auth || !auth->auth_success ||
+	    os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+		return NULL;
+	}
+	wpa_hexdump(MSG_DEBUG,
+		    "DPP: Received Configuration Request (GAS Query Request)",
+		    query, query_len);
+	resp = dpp_conf_req_rx(auth, query, query_len);
+	if (!resp)
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED);
+	return resp;
+}
+
+
+static void
+wpas_dpp_gas_status_handler(void *ctx, struct wpabuf *resp, int ok)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+	if (!auth) {
+		wpabuf_free(resp);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Configuration exchange completed (ok=%d)",
+		   ok);
+	eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
+	offchannel_send_action_done(wpa_s);
+	wpas_dpp_listen_stop(wpa_s);
+	if (ok)
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_SENT);
+	else
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED);
+	dpp_auth_deinit(wpa_s->dpp_auth);
+	wpa_s->dpp_auth = NULL;
+	wpabuf_free(resp);
+}
+
+
+static unsigned int wpas_dpp_next_configurator_id(struct wpa_supplicant *wpa_s)
+{
+	struct dpp_configurator *conf;
+	unsigned int max_id = 0;
+
+	dl_list_for_each(conf, &wpa_s->dpp_configurator,
+			 struct dpp_configurator, list) {
+		if (conf->id > max_id)
+			max_id = conf->id;
+	}
+	return max_id + 1;
+}
+
+
+int wpas_dpp_configurator_add(struct wpa_supplicant *wpa_s, 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 = wpas_dpp_next_configurator_id(wpa_s);
+	dl_list_add(&wpa_s->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 wpa_supplicant *wpa_s, unsigned int id)
+{
+	struct dpp_configurator *conf, *tmp;
+	int found = 0;
+
+	dl_list_for_each_safe(conf, tmp, &wpa_s->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 wpas_dpp_configurator_remove(struct wpa_supplicant *wpa_s, 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(wpa_s, id_val);
+}
+
+
+int wpas_dpp_configurator_sign(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	struct dpp_authentication *auth;
+	int ret = -1;
+	char *curve = NULL;
+
+	auth = os_zalloc(sizeof(*auth));
+	if (!auth)
+		return -1;
+
+	curve = get_param(cmd, " curve=");
+	wpas_dpp_set_configurator(wpa_s, auth, cmd);
+
+	if (dpp_configurator_own_config(auth, curve) == 0) {
+		wpas_dpp_handle_config_obj(wpa_s, auth);
+		ret = 0;
+	}
+
+	dpp_auth_deinit(auth);
+	os_free(curve);
+
+	return ret;
+}
+
+
+static void
+wpas_dpp_tx_introduction_status(struct wpa_supplicant *wpa_s,
+				unsigned int freq, const u8 *dst,
+				const u8 *src, const u8 *bssid,
+				const u8 *data, size_t data_len,
+				enum offchannel_send_action_result result)
+{
+	wpa_printf(MSG_DEBUG, "DPP: TX status: freq=%u dst=" MACSTR
+		   " result=%s (DPP Peer Discovery Request)",
+		   freq, MAC2STR(dst),
+		   result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" :
+		   (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" :
+		    "FAILED"));
+	/* TODO: Time out wait for response more quickly in error cases? */
+}
+
+
+int wpas_dpp_check_connect(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+			   struct wpa_bss *bss)
+{
+	struct os_time now;
+	struct wpabuf *msg;
+	unsigned int wait_time;
+
+	if (!(ssid->key_mgmt & WPA_KEY_MGMT_DPP) || !bss)
+		return 0; /* Not using DPP AKM - continue */
+	if (wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid))
+		return 0; /* PMKSA exists for DPP AKM - continue */
+
+	if (!ssid->dpp_connector || !ssid->dpp_netaccesskey ||
+	    !ssid->dpp_csign) {
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_MISSING_CONNECTOR
+			"missing %s",
+			!ssid->dpp_connector ? "Connector" :
+			(!ssid->dpp_netaccesskey ? "netAccessKey" :
+			 "C-sign-key"));
+		return -1;
+	}
+
+	os_get_time(&now);
+
+	if (ssid->dpp_netaccesskey_expiry &&
+	    ssid->dpp_netaccesskey_expiry < now.sec) {
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_MISSING_CONNECTOR
+			"netAccessKey expired");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Starting network introduction protocol to derive PMKSA for "
+		   MACSTR, MAC2STR(bss->bssid));
+
+	msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_REQ,
+			    5 + 4 + os_strlen(ssid->dpp_connector));
+	if (!msg)
+		return -1;
+
+	/* Transaction ID */
+	wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, TRANSACTION_ID);
+
+	/* DPP Connector */
+	wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+	wpabuf_put_le16(msg, os_strlen(ssid->dpp_connector));
+	wpabuf_put_str(msg, ssid->dpp_connector);
+
+	/* TODO: Timeout on AP response */
+	wait_time = wpa_s->max_remain_on_chan;
+	if (wait_time > 2000)
+		wait_time = 2000;
+	offchannel_send_action(wpa_s, bss->freq, bss->bssid, wpa_s->own_addr,
+			       broadcast,
+			       wpabuf_head(msg), wpabuf_len(msg),
+			       wait_time, wpas_dpp_tx_introduction_status, 0);
+	wpabuf_free(msg);
+
+	/* Request this connection attempt to terminate - new one will be
+	 * started when network introduction protocol completes */
+	os_memcpy(wpa_s->dpp_intro_bssid, bss->bssid, ETH_ALEN);
+	wpa_s->dpp_intro_network = ssid;
+	return 1;
+}
+
+
+int wpas_dpp_pkex_add(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	struct dpp_bootstrap_info *own_bi;
+	const char *pos, *end;
+	unsigned int wait_time;
+
+	pos = os_strstr(cmd, " own=");
+	if (!pos)
+		return -1;
+	pos += 5;
+	own_bi = dpp_bootstrap_get_id(wpa_s, atoi(pos));
+	if (!own_bi) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Identified bootstrap info not found");
+		return -1;
+	}
+	if (own_bi->type != DPP_BOOTSTRAP_PKEX) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Identified bootstrap info not for PKEX");
+		return -1;
+	}
+	wpa_s->dpp_pkex_bi = own_bi;
+
+	os_free(wpa_s->dpp_pkex_identifier);
+	wpa_s->dpp_pkex_identifier = NULL;
+	pos = os_strstr(cmd, " identifier=");
+	if (pos) {
+		pos += 12;
+		end = os_strchr(pos, ' ');
+		if (!end)
+			return -1;
+		wpa_s->dpp_pkex_identifier = os_malloc(end - pos + 1);
+		if (!wpa_s->dpp_pkex_identifier)
+			return -1;
+		os_memcpy(wpa_s->dpp_pkex_identifier, pos, end - pos);
+		wpa_s->dpp_pkex_identifier[end - pos] = '\0';
+	}
+
+	pos = os_strstr(cmd, " code=");
+	if (!pos)
+		return -1;
+	os_free(wpa_s->dpp_pkex_code);
+	wpa_s->dpp_pkex_code = os_strdup(pos + 6);
+	if (!wpa_s->dpp_pkex_code)
+		return -1;
+
+	if (os_strstr(cmd, " init=1")) {
+		struct wpabuf *msg;
+
+		wpa_printf(MSG_DEBUG, "DPP: Initiating PKEX");
+		dpp_pkex_free(wpa_s->dpp_pkex);
+		wpa_s->dpp_pkex = dpp_pkex_init(own_bi, wpa_s->own_addr,
+						wpa_s->dpp_pkex_identifier,
+						wpa_s->dpp_pkex_code);
+		if (!wpa_s->dpp_pkex)
+			return -1;
+
+		msg = wpa_s->dpp_pkex->exchange_req;
+		wait_time = wpa_s->max_remain_on_chan;
+		if (wait_time > 2000)
+			wait_time = 2000;
+		/* TODO: Which channel to use? */
+		offchannel_send_action(wpa_s, 2437, broadcast, wpa_s->own_addr,
+				       broadcast,
+				       wpabuf_head(msg), wpabuf_len(msg),
+				       wait_time, wpas_dpp_tx_pkex_status, 0);
+	}
+
+	/* TODO: Support multiple PKEX info entries */
+
+	os_free(wpa_s->dpp_pkex_auth_cmd);
+	wpa_s->dpp_pkex_auth_cmd = os_strdup(cmd);
+
+	return 1;
+}
+
+
+int wpas_dpp_pkex_remove(struct wpa_supplicant *wpa_s, 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;
+	}
+
+	if ((id_val != 0 && id_val != 1) || !wpa_s->dpp_pkex_code)
+		return -1;
+
+	/* TODO: Support multiple PKEX entries */
+	os_free(wpa_s->dpp_pkex_code);
+	wpa_s->dpp_pkex_code = NULL;
+	os_free(wpa_s->dpp_pkex_identifier);
+	wpa_s->dpp_pkex_identifier = NULL;
+	os_free(wpa_s->dpp_pkex_auth_cmd);
+	wpa_s->dpp_pkex_auth_cmd = NULL;
+	wpa_s->dpp_pkex_bi = NULL;
+	/* TODO: Remove dpp_pkex only if it is for the identified PKEX code */
+	dpp_pkex_free(wpa_s->dpp_pkex);
+	wpa_s->dpp_pkex = NULL;
+	return 0;
+}
+
+
+int wpas_dpp_init(struct wpa_supplicant *wpa_s)
+{
+	u8 adv_proto_id[7];
+
+	adv_proto_id[0] = WLAN_EID_VENDOR_SPECIFIC;
+	adv_proto_id[1] = 5;
+	WPA_PUT_BE24(&adv_proto_id[2], OUI_WFA);
+	adv_proto_id[5] = DPP_OUI_TYPE;
+	adv_proto_id[6] = 0x01;
+
+	if (gas_server_register(wpa_s->gas_server, adv_proto_id,
+				sizeof(adv_proto_id), wpas_dpp_gas_req_handler,
+				wpas_dpp_gas_status_handler, wpa_s) < 0)
+		return -1;
+	dl_list_init(&wpa_s->dpp_bootstrap);
+	dl_list_init(&wpa_s->dpp_configurator);
+	wpa_s->dpp_init_done = 1;
+	return 0;
+}
+
+
+void wpas_dpp_deinit(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	os_free(wpa_s->dpp_config_obj_override);
+	wpa_s->dpp_config_obj_override = NULL;
+	os_free(wpa_s->dpp_discovery_override);
+	wpa_s->dpp_discovery_override = NULL;
+	os_free(wpa_s->dpp_groups_override);
+	wpa_s->dpp_groups_override = NULL;
+	wpa_s->dpp_ignore_netaccesskey_mismatch = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (!wpa_s->dpp_init_done)
+		return;
+	eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
+	offchannel_send_action_done(wpa_s);
+	wpas_dpp_listen_stop(wpa_s);
+	dpp_bootstrap_del(wpa_s, 0);
+	dpp_configurator_del(wpa_s, 0);
+	dpp_auth_deinit(wpa_s->dpp_auth);
+	wpa_s->dpp_auth = NULL;
+	wpas_dpp_pkex_remove(wpa_s, "*");
+	wpa_s->dpp_pkex = NULL;
+	os_memset(wpa_s->dpp_intro_bssid, 0, ETH_ALEN);
+	os_free(wpa_s->dpp_configurator_params);
+	wpa_s->dpp_configurator_params = NULL;
+}
diff --git a/wpa_supplicant/dpp_supplicant.h b/wpa_supplicant/dpp_supplicant.h
new file mode 100644
index 0000000..05a466d
--- /dev/null
+++ b/wpa_supplicant/dpp_supplicant.h
@@ -0,0 +1,38 @@
+/*
+ * wpa_supplicant - DPP
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DPP_SUPPLICANT_H
+#define DPP_SUPPLICANT_H
+
+int wpas_dpp_qr_code(struct wpa_supplicant *wpa_s, const char *cmd);
+int wpas_dpp_bootstrap_gen(struct wpa_supplicant *wpa_s, const char *cmd);
+int wpas_dpp_bootstrap_remove(struct wpa_supplicant *wpa_s, const char *id);
+const char * wpas_dpp_bootstrap_get_uri(struct wpa_supplicant *wpa_s,
+					unsigned int id);
+int wpas_dpp_bootstrap_info(struct wpa_supplicant *wpa_s, int id,
+			    char *reply, int reply_size);
+int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd);
+int wpas_dpp_listen(struct wpa_supplicant *wpa_s, const char *cmd);
+void wpas_dpp_listen_stop(struct wpa_supplicant *wpa_s);
+void wpas_dpp_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+				   unsigned int freq);
+void wpas_dpp_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+					  unsigned int freq);
+void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src,
+			const u8 *buf, size_t len, unsigned int freq);
+int wpas_dpp_configurator_add(struct wpa_supplicant *wpa_s, const char *cmd);
+int wpas_dpp_configurator_remove(struct wpa_supplicant *wpa_s, const char *id);
+int wpas_dpp_configurator_sign(struct wpa_supplicant *wpa_s, const char *cmd);
+int wpas_dpp_pkex_add(struct wpa_supplicant *wpa_s, const char *cmd);
+int wpas_dpp_pkex_remove(struct wpa_supplicant *wpa_s, const char *id);
+int wpas_dpp_init(struct wpa_supplicant *wpa_s);
+void wpas_dpp_deinit(struct wpa_supplicant *wpa_s);
+int wpas_dpp_check_connect(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+			   struct wpa_bss *bss);
+
+#endif /* DPP_SUPPLICANT_H */
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 0af63c9..e131653 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -189,20 +189,19 @@
 }
 
 static inline int wpa_drv_add_pmkid(struct wpa_supplicant *wpa_s,
-				    const u8 *bssid, const u8 *pmkid)
+				    struct wpa_pmkid_params *params)
 {
 	if (wpa_s->driver->add_pmkid) {
-		return wpa_s->driver->add_pmkid(wpa_s->drv_priv, bssid, pmkid);
+		return wpa_s->driver->add_pmkid(wpa_s->drv_priv, params);
 	}
 	return -1;
 }
 
 static inline int wpa_drv_remove_pmkid(struct wpa_supplicant *wpa_s,
-				       const u8 *bssid, const u8 *pmkid)
+				       struct wpa_pmkid_params *params)
 {
 	if (wpa_s->driver->remove_pmkid) {
-		return wpa_s->driver->remove_pmkid(wpa_s->drv_priv, bssid,
-						   pmkid);
+		return wpa_s->driver->remove_pmkid(wpa_s->drv_priv, params);
 	}
 	return -1;
 }
@@ -276,11 +275,12 @@
 
 static inline struct hostapd_hw_modes *
 wpa_drv_get_hw_feature_data(struct wpa_supplicant *wpa_s, u16 *num_modes,
-			    u16 *flags)
+			    u16 *flags, u8 *dfs_domain)
 {
 	if (wpa_s->driver->get_hw_feature_data)
 		return wpa_s->driver->get_hw_feature_data(wpa_s->drv_priv,
-							  num_modes, flags);
+							  num_modes, flags,
+							  dfs_domain);
 	return NULL;
 }
 
@@ -989,4 +989,43 @@
 					    tdls_external_control);
 }
 
+static inline struct wpa_bss_candidate_info *
+wpa_drv_get_bss_trans_status(struct wpa_supplicant *wpa_s,
+			     struct wpa_bss_trans_info *params)
+{
+	if (!wpa_s->driver->get_bss_transition_status)
+		return NULL;
+	return wpa_s->driver->get_bss_transition_status(wpa_s->drv_priv,
+							params);
+}
+
+static inline int wpa_drv_ignore_assoc_disallow(struct wpa_supplicant *wpa_s,
+						int val)
+{
+	if (!wpa_s->driver->ignore_assoc_disallow)
+		return -1;
+	return wpa_s->driver->ignore_assoc_disallow(wpa_s->drv_priv, val);
+}
+
+static inline int wpa_drv_set_bssid_blacklist(struct wpa_supplicant *wpa_s,
+					      unsigned int num_bssid,
+					      const u8 *bssids)
+{
+	if (!wpa_s->driver->set_bssid_blacklist)
+		return -1;
+	return wpa_s->driver->set_bssid_blacklist(wpa_s->drv_priv, num_bssid,
+						  bssids);
+}
+
+static inline int wpa_drv_update_connect_params(
+	struct wpa_supplicant *wpa_s,
+	struct wpa_driver_associate_params *params,
+	enum wpa_drv_update_connect_params_mask mask)
+{
+	if (!wpa_s->driver->update_connect_params)
+		return -1;
+	return wpa_s->driver->update_connect_params(wpa_s->drv_priv, params,
+						    mask);
+}
+
 #endif /* DRIVER_I_H */
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index bc3c90e..63cf773 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -28,6 +28,7 @@
 #include "notify.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
+#include "common/gas_server.h"
 #include "crypto/random.h"
 #include "blacklist.h"
 #include "wpas_glue.h"
@@ -46,6 +47,7 @@
 #include "mesh.h"
 #include "mesh_mpm.h"
 #include "wmm_ac.h"
+#include "dpp_supplicant.h"
 
 
 #ifndef CONFIG_NO_SCAN_PROCESSING
@@ -54,8 +56,7 @@
 #endif /* CONFIG_NO_SCAN_PROCESSING */
 
 
-static int wpas_temp_disabled(struct wpa_supplicant *wpa_s,
-			      struct wpa_ssid *ssid)
+int wpas_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
 {
 	struct os_reltime now;
 
@@ -302,7 +303,9 @@
 
 	eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
 	eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
-	if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt))
+	if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_OWE ||
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_DPP)
 		eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
 	wpa_s->ap_ies_from_associnfo = 0;
 	wpa_s->current_ssid = NULL;
@@ -311,6 +314,11 @@
 
 	wpas_rrm_reset(wpa_s);
 	wpa_s->wnmsleep_used = 0;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	wpa_s->last_tk_alg = WPA_ALG_NONE;
+	os_memset(wpa_s->last_tk, 0, sizeof(wpa_s->last_tk));
+#endif /* CONFIG_TESTING_OPTIONS */
 }
 
 
@@ -327,7 +335,7 @@
 	for (i = 0; i < ie.num_pmkid; i++) {
 		pmksa_set = pmksa_cache_set_current(wpa_s->wpa,
 						    ie.pmkid + i * PMKID_LEN,
-						    NULL, NULL, 0);
+						    NULL, NULL, 0, NULL);
 		if (pmksa_set == 0) {
 			eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
 			break;
@@ -562,6 +570,14 @@
 			break;
 		}
 
+		if (ssid->group_mgmt_cipher &&
+		    !(ie.mgmt_group_cipher & ssid->group_mgmt_cipher)) {
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip RSN IE - group mgmt cipher mismatch");
+			break;
+		}
+
 		if (!(ie.key_mgmt & ssid->key_mgmt)) {
 			if (debug_print)
 				wpa_dbg(wpa_s, MSG_DEBUG,
@@ -868,6 +884,80 @@
 }
 
 
+static void owe_trans_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+			   const u8 **ret_ssid, size_t *ret_ssid_len)
+{
+#ifdef CONFIG_OWE
+	const u8 *owe, *pos, *end, *bssid;
+	u8 ssid_len;
+	struct wpa_bss *open_bss;
+
+	owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
+	if (!owe || !wpa_bss_get_ie(bss, WLAN_EID_RSN))
+		return;
+
+	pos = owe + 6;
+	end = owe + 2 + owe[1];
+
+	if (end - pos < ETH_ALEN + 1)
+		return;
+	bssid = pos;
+	pos += ETH_ALEN;
+	ssid_len = *pos++;
+	if (end - pos < ssid_len || ssid_len > SSID_MAX_LEN)
+		return;
+
+	/* Match the profile SSID against the OWE transition mode SSID on the
+	 * open network. */
+	wpa_dbg(wpa_s, MSG_DEBUG, "OWE: transition mode BSSID: " MACSTR
+		" SSID: %s", MAC2STR(bssid), wpa_ssid_txt(pos, ssid_len));
+	*ret_ssid = pos;
+	*ret_ssid_len = ssid_len;
+
+	if (bss->ssid_len > 0)
+		return;
+
+	open_bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
+	if (!open_bss)
+		return;
+	if (ssid_len != open_bss->ssid_len ||
+	    os_memcmp(pos, open_bss->ssid, ssid_len) != 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"OWE: transition mode SSID mismatch: %s",
+			wpa_ssid_txt(open_bss->ssid, open_bss->ssid_len));
+		return;
+	}
+
+	owe = wpa_bss_get_vendor_ie(open_bss, OWE_IE_VENDOR_TYPE);
+	if (!owe || wpa_bss_get_ie(open_bss, WLAN_EID_RSN)) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"OWE: transition mode open BSS unexpected info");
+		return;
+	}
+
+	pos = owe + 6;
+	end = owe + 2 + owe[1];
+
+	if (end - pos < ETH_ALEN + 1)
+		return;
+	if (os_memcmp(pos, bss->bssid, ETH_ALEN) != 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"OWE: transition mode BSSID mismatch: " MACSTR,
+			MAC2STR(pos));
+		return;
+	}
+	pos += ETH_ALEN;
+	ssid_len = *pos++;
+	if (end - pos < ssid_len || ssid_len > SSID_MAX_LEN)
+		return;
+	wpa_dbg(wpa_s, MSG_DEBUG, "OWE: learned transition mode OWE SSID: %s",
+		wpa_ssid_txt(pos, ssid_len));
+	os_memcpy(bss->ssid, pos, ssid_len);
+	bss->ssid_len = ssid_len;
+#endif /* CONFIG_OWE */
+}
+
+
 struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
 				     int i, struct wpa_bss *bss,
 				     struct wpa_ssid *group,
@@ -882,6 +972,8 @@
 #ifdef CONFIG_MBO
 	const u8 *assoc_disallow;
 #endif /* CONFIG_MBO */
+	const u8 *match_ssid;
+	size_t match_ssid_len;
 
 	ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
 	wpa_ie_len = ie ? ie[1] : 0;
@@ -931,7 +1023,11 @@
 		}
 	}
 
-	if (bss->ssid_len == 0) {
+	match_ssid = bss->ssid;
+	match_ssid_len = bss->ssid_len;
+	owe_trans_ssid(wpa_s, bss, &match_ssid, &match_ssid_len);
+
+	if (match_ssid_len == 0) {
 		if (debug_print)
 			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - SSID not known");
 		return NULL;
@@ -943,7 +1039,7 @@
 		return NULL;
 	}
 
-	if (disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
+	if (disallowed_ssid(wpa_s, match_ssid, match_ssid_len)) {
 		if (debug_print)
 			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - SSID disallowed");
 		return NULL;
@@ -998,8 +1094,8 @@
 			check_ssid = 0;
 
 		if (check_ssid &&
-		    (bss->ssid_len != ssid->ssid_len ||
-		     os_memcmp(bss->ssid, ssid->ssid, bss->ssid_len) != 0)) {
+		    (match_ssid_len != ssid->ssid_len ||
+		     os_memcmp(match_ssid, ssid->ssid, match_ssid_len) != 0)) {
 			if (debug_print)
 				wpa_dbg(wpa_s, MSG_DEBUG,
 					"   skip - SSID mismatch");
@@ -1209,6 +1305,19 @@
 #endif /* CONFIG_TESTING_OPTIONS */
 #endif /* CONFIG_MBO */
 
+#ifdef CONFIG_DPP
+		if ((ssid->key_mgmt & WPA_KEY_MGMT_DPP) &&
+		    !wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid) &&
+		    (!ssid->dpp_connector ||
+		     !ssid->dpp_netaccesskey ||
+		     !ssid->dpp_csign)) {
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - no PMKSA entry for DPP");
+			continue;
+		}
+#endif /* CONFIG_DPP */
+
 		/* Matching configuration found */
 		return ssid;
 	}
@@ -2213,7 +2322,8 @@
 
 #ifdef CONFIG_FILS
 #ifdef CONFIG_SME
-	if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS &&
+	if ((wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS ||
+	     wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS_SK_PFS) &&
 	    (!data->assoc_info.resp_frame ||
 	     fils_process_assoc_resp(wpa_s->wpa,
 				     data->assoc_info.resp_frame,
@@ -2222,8 +2332,24 @@
 		return -1;
 	}
 #endif /* CONFIG_SME */
+
+	/* Additional processing for FILS when SME is in driver */
+	if (wpa_s->auth_alg == WPA_AUTH_ALG_FILS &&
+	    !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))
+		wpa_sm_set_reset_fils_completed(wpa_s->wpa, 1);
 #endif /* CONFIG_FILS */
 
+#ifdef CONFIG_OWE
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_OWE &&
+	    (wpa_drv_get_bssid(wpa_s, bssid) < 0 ||
+	     owe_process_assoc_resp(wpa_s->wpa, bssid,
+				    data->assoc_info.resp_ies,
+				    data->assoc_info.resp_ies_len) < 0)) {
+		wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_UNSPECIFIED);
+		return -1;
+	}
+#endif /* CONFIG_OWE */
+
 #ifdef CONFIG_IEEE80211R
 #ifdef CONFIG_SME
 	if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FT) {
@@ -2521,7 +2647,9 @@
 		eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
 		eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
 	}
-	if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || ft_completed ||
+	if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_DPP ||
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_OWE || ft_completed ||
 	    already_authorized)
 		eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
 	/* 802.1X::portControl = Auto */
@@ -2636,6 +2764,16 @@
 		if (wpa_s->reassoc_same_bss)
 			wmm_ac_restore_tspecs(wpa_s);
 	}
+
+#ifdef CONFIG_FILS
+	if (wpa_key_mgmt_fils(wpa_s->key_mgmt)) {
+		struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, bssid);
+		const u8 *fils_cache_id = wpa_bss_get_fils_cache_id(bss);
+
+		if (fils_cache_id)
+			wpa_sm_set_fils_cache_id(wpa_s->wpa, fils_cache_id);
+	}
+#endif /* CONFIG_FILS */
 }
 
 
@@ -3027,18 +3165,6 @@
 }
 
 
-#ifdef CONFIG_PEERKEY
-static void
-wpa_supplicant_event_stkstart(struct wpa_supplicant *wpa_s,
-			      union wpa_event_data *data)
-{
-	if (data == NULL)
-		return;
-	wpa_sm_stkstart(wpa_s->wpa, data->stkstart.peer);
-}
-#endif /* CONFIG_PEERKEY */
-
-
 #ifdef CONFIG_TDLS
 static void wpa_supplicant_event_tdls(struct wpa_supplicant *wpa_s,
 				      union wpa_event_data *data)
@@ -3401,6 +3527,7 @@
 	struct wpa_supplicant *wpa_s, struct channel_list_changed *info)
 {
 	struct wpa_supplicant *ifs;
+	u8 dfs_domain;
 
 	/*
 	 * To allow backwards compatibility with higher level layers that
@@ -3425,7 +3552,7 @@
 			   ifs->ifname);
 		free_hw_features(ifs);
 		ifs->hw.modes = wpa_drv_get_hw_feature_data(
-			ifs, &ifs->hw.num_modes, &ifs->hw.flags);
+			ifs, &ifs->hw.num_modes, &ifs->hw.flags, &dfs_domain);
 
 		/* Restart PNO/sched_scan with updated channel list */
 		if (ifs->pno) {
@@ -3500,6 +3627,15 @@
 		return;
 #endif /* CONFIG_GAS */
 
+#ifdef CONFIG_GAS_SERVER
+	if ((mgmt->u.action.category == WLAN_ACTION_PUBLIC ||
+	     mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL) &&
+	    gas_server_rx(wpa_s->gas_server, mgmt->da, mgmt->sa, mgmt->bssid,
+			  mgmt->u.action.category,
+			  payload, plen, freq) == 0)
+		return;
+#endif /* CONFIG_GAS_SERVER */
+
 #ifdef CONFIG_TDLS
 	if (category == WLAN_ACTION_PUBLIC && plen >= 4 &&
 	    payload[0] == WLAN_TDLS_DISCOVERY_RESPONSE) {
@@ -3528,6 +3664,7 @@
 	if (category == WLAN_ACTION_RADIO_MEASUREMENT &&
 	    payload[0] == WLAN_RRM_RADIO_MEASUREMENT_REQUEST) {
 		wpas_rrm_handle_radio_measurement_request(wpa_s, mgmt->sa,
+							  mgmt->da,
 							  payload + 1,
 							  plen - 1);
 		return;
@@ -3554,6 +3691,18 @@
 	}
 #endif /* CONFIG_FST */
 
+#ifdef CONFIG_DPP
+	if (category == WLAN_ACTION_PUBLIC && plen >= 5 &&
+	    payload[0] == WLAN_PA_VENDOR_SPECIFIC &&
+	    WPA_GET_BE24(&payload[1]) == OUI_WFA &&
+	    payload[4] == DPP_OUI_TYPE) {
+		payload++;
+		plen--;
+		wpas_dpp_rx_action(wpa_s, mgmt->sa, payload, plen, freq);
+		return;
+	}
+#endif /* CONFIG_DPP */
+
 	wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
 			   category, payload, plen, freq);
 	if (wpa_s->ifmsh)
@@ -3611,6 +3760,30 @@
 			       data->assoc_info.ptk_kck_len,
 			       data->assoc_info.ptk_kek,
 			       data->assoc_info.ptk_kek_len);
+#ifdef CONFIG_FILS
+	if (wpa_s->auth_alg == WPA_AUTH_ALG_FILS) {
+		struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, wpa_s->bssid);
+		const u8 *fils_cache_id = wpa_bss_get_fils_cache_id(bss);
+
+		/* Update ERP next sequence number */
+		eapol_sm_update_erp_next_seq_num(
+			wpa_s->eapol, data->assoc_info.fils_erp_next_seq_num);
+
+		if (data->assoc_info.fils_pmk && data->assoc_info.fils_pmkid) {
+			/* Add the new PMK and PMKID to the PMKSA cache */
+			wpa_sm_pmksa_cache_add(wpa_s->wpa,
+					       data->assoc_info.fils_pmk,
+					       data->assoc_info.fils_pmk_len,
+					       data->assoc_info.fils_pmkid,
+					       wpa_s->bssid, fils_cache_id);
+		} else if (data->assoc_info.fils_pmkid) {
+			/* Update the current PMKSA used for this connection */
+			pmksa_cache_set_current(wpa_s->wpa,
+						data->assoc_info.fils_pmkid,
+						NULL, NULL, 0, NULL);
+		}
+	}
+#endif /* CONFIG_FILS */
 }
 
 
@@ -3618,7 +3791,6 @@
 			  union wpa_event_data *data)
 {
 	struct wpa_supplicant *wpa_s = ctx;
-	char buf[100];
 	int resched;
 
 	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED &&
@@ -3668,9 +3840,17 @@
 				   "EVENT_ASSOC - ignore_auth_resp active!");
 			break;
 		}
+		if (wpa_s->testing_resend_assoc) {
+			wpa_printf(MSG_INFO,
+				   "EVENT_DEAUTH - testing_resend_assoc");
+			break;
+		}
 #endif /* CONFIG_TESTING_OPTIONS */
 		wpa_supplicant_event_assoc(wpa_s, data);
-		if (data && data->assoc_info.authorized)
+		if (data &&
+		    (data->assoc_info.authorized ||
+		     (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+		      wpa_fils_is_completed(wpa_s->wpa))))
 			wpa_supplicant_event_assoc_auth(wpa_s, data);
 		if (data) {
 			wpa_msg(wpa_s, MSG_INFO,
@@ -3689,6 +3869,11 @@
 				   "EVENT_DEAUTH - ignore_auth_resp active!");
 			break;
 		}
+		if (wpa_s->testing_resend_assoc) {
+			wpa_printf(MSG_INFO,
+				   "EVENT_DEAUTH - testing_resend_assoc");
+			break;
+		}
 #endif /* CONFIG_TESTING_OPTIONS */
 		wpas_event_deauth(wpa_s,
 				  data ? &data->deauth_info : NULL);
@@ -3761,11 +3946,6 @@
 	case EVENT_PMKID_CANDIDATE:
 		wpa_supplicant_event_pmkid_candidate(wpa_s, data);
 		break;
-#ifdef CONFIG_PEERKEY
-	case EVENT_STKSTART:
-		wpa_supplicant_event_stkstart(wpa_s, data);
-		break;
-#endif /* CONFIG_PEERKEY */
 #ifdef CONFIG_TDLS
 	case EVENT_TDLS:
 		wpa_supplicant_event_tdls(wpa_s, data);
@@ -3787,24 +3967,23 @@
 		break;
 #endif /* CONFIG_IBSS_RSN */
 	case EVENT_ASSOC_REJECT:
-		if (data->assoc_reject.timeout_reason)
-			os_snprintf(buf, sizeof(buf), "=%s",
-				    data->assoc_reject.timeout_reason);
-		else
-			buf[0] = '\0';
 		if (data->assoc_reject.bssid)
 			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
-				"bssid=" MACSTR	" status_code=%u%s%s",
+				"bssid=" MACSTR	" status_code=%u%s%s%s",
 				MAC2STR(data->assoc_reject.bssid),
 				data->assoc_reject.status_code,
 				data->assoc_reject.timed_out ? " timeout" : "",
-				buf);
+				data->assoc_reject.timeout_reason ? "=" : "",
+				data->assoc_reject.timeout_reason ?
+				data->assoc_reject.timeout_reason : "");
 		else
 			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
-				"status_code=%u%s%s",
+				"status_code=%u%s%s%s",
 				data->assoc_reject.status_code,
 				data->assoc_reject.timed_out ? " timeout" : "",
-				buf);
+				data->assoc_reject.timeout_reason ? "=" : "",
+				data->assoc_reject.timeout_reason ?
+				data->assoc_reject.timeout_reason : "");
 		wpa_s->assoc_status_code = data->assoc_reject.status_code;
 		wpa_s->assoc_timed_out = data->assoc_reject.timed_out;
 		wpas_notify_assoc_status_code(wpa_s);
@@ -3812,6 +3991,15 @@
 			sme_event_assoc_reject(wpa_s, data);
 		else {
 			const u8 *bssid = data->assoc_reject.bssid;
+
+#ifdef CONFIG_FILS
+			/* Update ERP next sequence number */
+			if (wpa_s->auth_alg == WPA_AUTH_ALG_FILS)
+				eapol_sm_update_erp_next_seq_num(
+				      wpa_s->eapol,
+				      data->assoc_reject.fils_erp_next_seq_num);
+#endif /* CONFIG_FILS */
+
 			if (bssid == NULL || is_zero_ether_addr(bssid))
 				bssid = wpa_s->pending_bssid;
 			wpas_connection_failed(wpa_s, bssid);
@@ -4107,6 +4295,10 @@
 #endif /* CONFIG_OFFCHANNEL */
 		wpas_p2p_cancel_remain_on_channel_cb(
 			wpa_s, data->remain_on_channel.freq);
+#ifdef CONFIG_DPP
+		wpas_dpp_cancel_remain_on_channel_cb(
+			wpa_s, data->remain_on_channel.freq);
+#endif /* CONFIG_DPP */
 		break;
 	case EVENT_EAPOL_RX:
 		wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src,
diff --git a/wpa_supplicant/examples/dpp-qrcode.py b/wpa_supplicant/examples/dpp-qrcode.py
new file mode 100644
index 0000000..e2a00c9
--- /dev/null
+++ b/wpa_supplicant/examples/dpp-qrcode.py
@@ -0,0 +1,130 @@
+#!/usr/bin/python
+#
+# Example Android logcat to wpa_supplicant wrapper for QR Code scans
+# Copyright (c) 2017, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import os
+import sys
+import argparse
+import logging
+import qrcode
+
+scriptsdir = os.path.dirname(os.path.realpath(sys.modules[__name__].__file__))
+sys.path.append(os.path.join(scriptsdir, '..', '..', 'wpaspy'))
+
+import wpaspy
+
+wpas_ctrl = '/var/run/wpa_supplicant'
+
+def wpas_connect():
+    ifaces = []
+    if os.path.isdir(wpas_ctrl):
+        try:
+            ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
+        except OSError, error:
+            print "Could not find wpa_supplicant: ", error
+            return None
+
+    if len(ifaces) < 1:
+        print "No wpa_supplicant control interface found"
+        return None
+
+    for ctrl in ifaces:
+        try:
+            wpas = wpaspy.Ctrl(ctrl)
+            return wpas
+        except Exception, e:
+            pass
+    return None
+
+def dpp_logcat():
+    for line in iter(sys.stdin.readline, ''):
+        if "ResultHandler: Launching intent: Intent" not in line:
+            continue
+        if "act=android.intent.action.VIEW" not in line:
+            continue
+        uri = None
+        for val in line.split(' '):
+            if val.startswith('dat='):
+                uri = val.split('=', 1)[1]
+                break
+        if not uri:
+            continue
+        if not uri.startswith('DPP:'):
+            continue
+        print "Found DPP bootstrap info URI:"
+        print uri
+        wpas = wpas_connect()
+        if not wpas:
+            print "Could not connect to wpa_supplicant"
+            print
+            continue
+        res = wpas.request("DPP_QR_CODE " + uri);
+        try:
+            id = int(res)
+        except ValueError:
+            print "QR Code URI rejected"
+            continue
+        print "QR Code URI accepted - ID=%d" % id
+        print wpas.request("DPP_BOOTSTRAP_INFO %d" % id)
+        del wpas
+
+def dpp_display(curve):
+        wpas = wpas_connect()
+        if not wpas:
+            print "Could not connect to wpa_supplicant"
+            return
+        res = wpas.request("STATUS")
+        addr = None
+        for line in res.splitlines():
+            if line.startswith("address="):
+                addr = line.split('=')[1]
+                break
+        cmd = "DPP_BOOTSTRAP_GEN type=qrcode"
+        cmd += " chan=81/1"
+        if addr:
+            cmd += " mac=" + addr.replace(':','')
+        if curve:
+            cmd += " curve=" + curve
+        res = wpas.request(cmd)
+        try:
+            id = int(res)
+        except ValueError:
+            print "Failed to generate bootstrap info URI"
+            return
+        print "Bootstrap information - ID=%d" % id
+        print wpas.request("DPP_BOOTSTRAP_INFO %d" % id)
+        uri = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % id)
+        print uri
+        print "ID=%d" % id
+        qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_M,
+                           border=3)
+        qr.add_data(uri, optimize=5)
+        qr.print_ascii(tty=True)
+        print "ID=%d" % id
+        del wpas
+
+def main():
+    parser = argparse.ArgumentParser(description='Android logcat to wpa_supplicant integration for DPP QR Code operations')
+    parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
+                        action='store_const', dest='loglevel',
+                        help='verbose debug output')
+    parser.add_argument('--curve', '-c',
+                        help='set a specific curve (P-256, P-384, P-521, BP-256R1, BP-384R1, BP-512R1) for key generation')
+    parser.add_argument('command', choices=['logcat',
+                                            'display'],
+                        nargs='?')
+    args = parser.parse_args()
+
+    logging.basicConfig(level=args.loglevel)
+
+    if args.command == "logcat":
+        dpp_logcat()
+    elif args.command == "display":
+        dpp_display(args.curve)
+
+if __name__ == '__main__':
+    main()
diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c
index db481a5..91cf19a 100644
--- a/wpa_supplicant/gas_query.c
+++ b/wpa_supplicant/gas_query.c
@@ -243,10 +243,17 @@
 	}
 	os_get_reltime(&query->last_oper);
 
-	if (result == OFFCHANNEL_SEND_ACTION_SUCCESS) {
+	if (result == OFFCHANNEL_SEND_ACTION_SUCCESS ||
+	    result == OFFCHANNEL_SEND_ACTION_NO_ACK) {
 		eloop_cancel_timeout(gas_query_timeout, gas, query);
-		eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
-				       gas_query_timeout, gas, query);
+		if (result == OFFCHANNEL_SEND_ACTION_NO_ACK) {
+			wpa_printf(MSG_DEBUG, "GAS: No ACK to GAS request");
+			eloop_register_timeout(0, 250000,
+					       gas_query_timeout, gas, query);
+		} else {
+			eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
+					       gas_query_timeout, gas, query);
+		}
 		if (query->wait_comeback && !query->retry) {
 			eloop_cancel_timeout(gas_query_rx_comeback_timeout,
 					     gas, query);
diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c
index 858636f..f9b7e4c 100644
--- a/wpa_supplicant/hs20_supplicant.c
+++ b/wpa_supplicant/hs20_supplicant.c
@@ -430,10 +430,9 @@
 	dl_list_for_each(icon, &wpa_s->icon_head, struct icon_entry, list) {
 		if (icon->dialog_token == dialog_token && !icon->image &&
 		    os_memcmp(icon->bssid, sa, ETH_ALEN) == 0) {
-			icon->image = os_malloc(slen);
+			icon->image = os_memdup(pos, slen);
 			if (!icon->image)
 				return -1;
-			os_memcpy(icon->image, pos, slen);
 			icon->image_len = slen;
 			hs20_remove_duplicate_icons(wpa_s, icon);
 			wpa_msg(wpa_s, MSG_INFO,
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index 954061a..00919d1 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -259,9 +259,13 @@
 
 
 static const u8 * auth_get_psk(void *ctx, const u8 *addr,
-			       const u8 *p2p_dev_addr, const u8 *prev_psk)
+			       const u8 *p2p_dev_addr, const u8 *prev_psk,
+			       size_t *psk_len)
 {
 	struct ibss_rsn *ibss_rsn = ctx;
+
+	if (psk_len)
+		*psk_len = PMK_LEN;
 	wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)",
 		   __func__, MAC2STR(addr), prev_psk);
 	if (prev_psk)
@@ -458,7 +462,7 @@
 				"\x00\x0f\xac\x04"
 				"\x01\x00\x00\x0f\xac\x04"
 				"\x01\x00\x00\x0f\xac\x02"
-				"\x00\x00", 22, NULL, 0) !=
+				"\x00\x00", 22, NULL, 0, NULL, 0) !=
 	    WPA_IE_OK) {
 		wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
 		return -1;
@@ -760,10 +764,9 @@
 	if (supp < 0)
 		return -1;
 
-	tmp = os_malloc(len);
+	tmp = os_memdup(buf, len);
 	if (tmp == NULL)
 		return -1;
-	os_memcpy(tmp, buf, len);
 	if (supp) {
 		peer->authentication_status |= IBSS_RSN_AUTH_EAPOL_BY_PEER;
 		wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Supplicant from "
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index fd2b02e..4c79356 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -106,10 +106,12 @@
 	if (buf == NULL)
 		return NULL;
 
-	len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST);
-	for (i = 0; i < num_ids; i++)
-		wpabuf_put_le16(buf, info_ids[i]);
-	gas_anqp_set_element_len(buf, len_pos);
+	if (num_ids > 0) {
+		len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST);
+		for (i = 0; i < num_ids; i++)
+			wpabuf_put_le16(buf, info_ids[i]);
+		gas_anqp_set_element_len(buf, len_pos);
+	}
 	if (extra)
 		wpabuf_put_buf(buf, extra);
 
@@ -1769,9 +1771,10 @@
 	switch (eap->method) {
 	case EAP_TYPE_TTLS:
 		if (eap->inner_method) {
-			os_snprintf(buf, sizeof(buf), "\"autheap=%s\"",
-				    eap_get_name(EAP_VENDOR_IETF,
-						 eap->inner_method));
+			name = eap_get_name(EAP_VENDOR_IETF, eap->inner_method);
+			if (!name)
+				goto fail;
+			os_snprintf(buf, sizeof(buf), "\"autheap=%s\"", name);
 			if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
 				goto fail;
 			break;
@@ -1894,7 +1897,7 @@
 		size_t len;
 		wpa_msg(wpa_s, MSG_DEBUG,
 			"Interworking: IMSI not available - try to read again through eap_proxy");
-		wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol,
+		wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, -1,
 							     wpa_s->imsi,
 							     &len);
 		if (wpa_s->mnc_len > 0) {
@@ -2694,7 +2697,7 @@
 
 int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
 		  u16 info_ids[], size_t num_ids, u32 subtypes,
-		  int get_cell_pref)
+		  u32 mbo_subtypes)
 {
 	struct wpabuf *buf;
 	struct wpabuf *extra_buf = NULL;
@@ -2728,13 +2731,14 @@
 #endif /* CONFIG_HS20 */
 
 #ifdef CONFIG_MBO
-	if (get_cell_pref) {
+	if (mbo_subtypes) {
 		struct wpabuf *mbo;
 
-		mbo = mbo_build_anqp_buf(wpa_s, bss);
+		mbo = mbo_build_anqp_buf(wpa_s, bss, mbo_subtypes);
 		if (mbo) {
 			if (wpabuf_resize(&extra_buf, wpabuf_len(mbo))) {
 				wpabuf_free(extra_buf);
+				wpabuf_free(mbo);
 				return -1;
 			}
 			wpabuf_put_buf(extra_buf, mbo);
@@ -2805,9 +2809,7 @@
 {
 	const u8 *pos = data;
 	struct wpa_bss_anqp *anqp = NULL;
-#ifdef CONFIG_HS20
 	u8 type;
-#endif /* CONFIG_HS20 */
 
 	if (bss)
 		anqp = bss->anqp;
@@ -2910,7 +2912,6 @@
 			return;
 
 		switch (WPA_GET_BE24(pos)) {
-#ifdef CONFIG_HS20
 		case OUI_WFA:
 			pos += 3;
 			slen -= 3;
@@ -2921,19 +2922,26 @@
 			slen--;
 
 			switch (type) {
+#ifdef CONFIG_HS20
 			case HS20_ANQP_OUI_TYPE:
 				hs20_parse_rx_hs20_anqp_resp(wpa_s, bss, sa,
 							     pos, slen,
 							     dialog_token);
 				break;
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+			case MBO_ANQP_OUI_TYPE:
+				mbo_parse_rx_anqp_resp(wpa_s, bss, sa,
+						       pos, slen);
+				break;
+#endif /* CONFIG_MBO */
 			default:
 				wpa_msg(wpa_s, MSG_DEBUG,
-					"HS20: Unsupported ANQP vendor type %u",
+					"ANQP: Unsupported ANQP vendor type %u",
 					type);
 				break;
 			}
 			break;
-#endif /* CONFIG_HS20 */
 		default:
 			wpa_msg(wpa_s, MSG_DEBUG,
 				"Interworking: Unsupported vendor-specific ANQP OUI %06x",
diff --git a/wpa_supplicant/interworking.h b/wpa_supplicant/interworking.h
index 3d22292..37ee2e9 100644
--- a/wpa_supplicant/interworking.h
+++ b/wpa_supplicant/interworking.h
@@ -13,7 +13,7 @@
 
 int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
 		  u16 info_ids[], size_t num_ids, u32 subtypes,
-		  int get_cell_pref);
+		  u32 mbo_subtypes);
 void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
 		  enum gas_query_result result,
 		  const struct wpabuf *adv_proto,
diff --git a/wpa_supplicant/mbo.c b/wpa_supplicant/mbo.c
index d20ae5e..129d205 100644
--- a/wpa_supplicant/mbo.c
+++ b/wpa_supplicant/mbo.c
@@ -154,7 +154,8 @@
 	struct wpabuf *mbo;
 	int res;
 
-	if (len < MBO_IE_HEADER + 3 + 7)
+	if (len < MBO_IE_HEADER + 3 + 7 +
+	    ((wpa_s->enable_oce & OCE_STA) ? 3 : 0))
 		return 0;
 
 	/* Leave room for the MBO IE header */
@@ -173,9 +174,16 @@
 	wpabuf_put_u8(mbo, 1);
 	wpabuf_put_u8(mbo, wpa_s->conf->mbo_cell_capa);
 
+	/* Add OCE capability indication attribute if OCE is enabled */
+	if (wpa_s->enable_oce & OCE_STA) {
+		wpabuf_put_u8(mbo, OCE_ATTR_ID_CAPA_IND);
+		wpabuf_put_u8(mbo, 1);
+		wpabuf_put_u8(mbo, OCE_RELEASE);
+	}
+
 	res = mbo_add_ie(buf, len, wpabuf_head_u8(mbo), wpabuf_len(mbo));
 	if (!res)
-		wpa_printf(MSG_ERROR, "Failed to add MBO IE");
+		wpa_printf(MSG_ERROR, "Failed to add MBO/OCE IE");
 
 	wpabuf_free(mbo);
 	return res;
@@ -277,11 +285,10 @@
 		   non_pref_chan ? non_pref_chan : "N/A");
 
 	/*
-	 * The shortest channel configuration is 10 characters - commas, 3
-	 * colons, and 4 values that one of them (oper_class) is 2 digits or
-	 * more.
+	 * The shortest channel configuration is 7 characters - 3 colons and
+	 * 4 values.
 	 */
-	if (!non_pref_chan || os_strlen(non_pref_chan) < 10)
+	if (!non_pref_chan || os_strlen(non_pref_chan) < 7)
 		goto update;
 
 	cmd = os_strdup(non_pref_chan);
@@ -369,21 +376,30 @@
 
 void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie)
 {
+	u8 *len;
+
 	wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
-	wpabuf_put_u8(ie, 7);
+	len = wpabuf_put(ie, 1);
+
 	wpabuf_put_be24(ie, OUI_WFA);
 	wpabuf_put_u8(ie, MBO_OUI_TYPE);
 
 	wpabuf_put_u8(ie, MBO_ATTR_ID_CELL_DATA_CAPA);
 	wpabuf_put_u8(ie, 1);
 	wpabuf_put_u8(ie, wpa_s->conf->mbo_cell_capa);
+	if (wpa_s->enable_oce & OCE_STA) {
+		wpabuf_put_u8(ie, OCE_ATTR_ID_CAPA_IND);
+		wpabuf_put_u8(ie, 1);
+		wpabuf_put_u8(ie, OCE_RELEASE);
+	}
+	*len = (u8 *) wpabuf_put(ie, 0) - len - 1;
 }
 
 
 void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie,
 			   size_t len)
 {
-	const u8 *pos, *cell_pref = NULL, *reason = NULL;
+	const u8 *pos, *cell_pref = NULL;
 	u8 id, elen;
 	u16 disallowed_sec = 0;
 
@@ -418,7 +434,8 @@
 			if (elen != 1)
 				goto fail;
 
-			reason = pos;
+			wpa_s->wnm_mbo_trans_reason_present = 1;
+			wpa_s->wnm_mbo_transition_reason = *pos;
 			break;
 		case MBO_ATTR_ID_ASSOC_RETRY_DELAY:
 			if (elen != 2)
@@ -432,6 +449,9 @@
 			} else if (wpa_s->wnm_mode &
 				   WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
 				disallowed_sec = WPA_GET_LE16(pos);
+				wpa_printf(MSG_DEBUG,
+					   "MBO: Association retry delay: %u",
+					   disallowed_sec);
 			} else {
 				wpa_printf(MSG_DEBUG,
 					   "MBO: Association retry delay attribute not in disassoc imminent mode");
@@ -461,9 +481,9 @@
 		wpa_msg(wpa_s, MSG_INFO, MBO_CELL_PREFERENCE "preference=%u",
 			*cell_pref);
 
-	if (reason)
+	if (wpa_s->wnm_mbo_trans_reason_present)
 		wpa_msg(wpa_s, MSG_INFO, MBO_TRANSITION_REASON "reason=%u",
-			*reason);
+			wpa_s->wnm_mbo_transition_reason);
 
 	if (disallowed_sec && wpa_s->current_bss)
 		wpa_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid,
@@ -515,10 +535,11 @@
 
 
 struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s,
-				   struct wpa_bss *bss)
+				   struct wpa_bss *bss, u32 mbo_subtypes)
 {
 	struct wpabuf *anqp_buf;
 	u8 *len_pos;
+	u8 i;
 
 	if (!wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE)) {
 		wpa_printf(MSG_INFO, "MBO: " MACSTR
@@ -527,7 +548,8 @@
 		return NULL;
 	}
 
-	anqp_buf = wpabuf_alloc(10);
+	/* Allocate size for the maximum case - all MBO subtypes are set */
+	anqp_buf = wpabuf_alloc(9 + MAX_MBO_ANQP_SUBTYPE);
 	if (!anqp_buf)
 		return NULL;
 
@@ -535,8 +557,43 @@
 	wpabuf_put_be24(anqp_buf, OUI_WFA);
 	wpabuf_put_u8(anqp_buf, MBO_ANQP_OUI_TYPE);
 
-	wpabuf_put_u8(anqp_buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF);
+	wpabuf_put_u8(anqp_buf, MBO_ANQP_SUBTYPE_QUERY_LIST);
+
+	/* The first valid MBO subtype is 1 */
+	for (i = 1; i <= MAX_MBO_ANQP_SUBTYPE; i++) {
+		if (mbo_subtypes & BIT(i))
+			wpabuf_put_u8(anqp_buf, i);
+	}
+
 	gas_anqp_set_element_len(anqp_buf, len_pos);
 
 	return anqp_buf;
 }
+
+
+void mbo_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
+			    struct wpa_bss *bss, const u8 *sa,
+			    const u8 *data, size_t slen)
+{
+	const u8 *pos = data;
+	u8 subtype;
+
+	if (slen < 1)
+		return;
+
+	subtype = *pos++;
+	slen--;
+
+	switch (subtype) {
+	case MBO_ANQP_SUBTYPE_CELL_CONN_PREF:
+		if (slen < 1)
+			break;
+		wpa_msg(wpa_s, MSG_INFO, RX_MBO_ANQP MACSTR
+			" cell_conn_pref=%u", MAC2STR(sa), *pos);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "MBO: Unsupported ANQP subtype %u",
+			   subtype);
+		break;
+	}
+}
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
index 2ca81a3..c0c8f91 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -84,6 +84,7 @@
 			MESH_CONF_SEC_AMPE;
 	else
 		conf->security |= MESH_CONF_SEC_NONE;
+#ifdef CONFIG_IEEE80211W
 	conf->ieee80211w = ssid->ieee80211w;
 	if (conf->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT) {
 		if (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP)
@@ -91,6 +92,7 @@
 		else
 			conf->ieee80211w = NO_MGMT_FRAME_PROTECTION;
 	}
+#endif /* CONFIG_IEEE80211W */
 
 	cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher, 0);
 	if (cipher < 0 || cipher == WPA_CIPHER_TKIP) {
@@ -257,11 +259,10 @@
 		 * advertised in beacons match the one in peering frames, sigh.
 		 */
 		if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G) {
-			conf->basic_rates = os_malloc(sizeof(basic_rates_erp));
+			conf->basic_rates = os_memdup(basic_rates_erp,
+						      sizeof(basic_rates_erp));
 			if (!conf->basic_rates)
 				goto out_free;
-			os_memcpy(conf->basic_rates, basic_rates_erp,
-				  sizeof(basic_rates_erp));
 		}
 	} else {
 		rate_len = 0;
@@ -304,11 +305,10 @@
 			wpas_mesh_copy_groups(bss, wpa_s);
 		} else {
 			bss->conf->sae_groups =
-				os_malloc(sizeof(default_groups));
+				os_memdup(default_groups,
+					  sizeof(default_groups));
 			if (!bss->conf->sae_groups)
 				goto out_free;
-			os_memcpy(bss->conf->sae_groups, default_groups,
-				  sizeof(default_groups));
 		}
 
 		len = os_strlen(ssid->passphrase);
@@ -413,6 +413,10 @@
 	else if (wpa_s->conf->dtim_period > 0)
 		params.dtim_period = wpa_s->conf->dtim_period;
 	params.conf.max_peer_links = wpa_s->conf->max_peer_links;
+	if (ssid->mesh_rssi_threshold < DEFAULT_MESH_RSSI_THRESHOLD) {
+		params.conf.rssi_threshold = ssid->mesh_rssi_threshold;
+		params.conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD;
+	}
 
 	if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
 		params.flags |= WPA_DRIVER_MESH_FLAG_SAE_AUTH;
diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c
index f152044..bc3cc5e 100644
--- a/wpa_supplicant/mesh_mpm.c
+++ b/wpa_supplicant/mesh_mpm.c
@@ -20,6 +20,7 @@
 #include "driver_i.h"
 #include "mesh_mpm.h"
 #include "mesh_rsn.h"
+#include "notify.h"
 
 struct mesh_peer_mgmt_ie {
 	const u8 *proto_id; /* Mesh Peering Protocol Identifier (2 octets) */
@@ -857,6 +858,9 @@
 	/* Send ctrl event */
 	wpa_msg(wpa_s, MSG_INFO, MESH_PEER_CONNECTED MACSTR,
 		MAC2STR(sta->addr));
+
+	/* Send D-Bus event */
+	wpas_notify_mesh_peer_connected(wpa_s, sta->addr);
 }
 
 
@@ -1009,6 +1013,10 @@
 			wpa_msg(wpa_s, MSG_INFO, MESH_PEER_DISCONNECTED MACSTR,
 				MAC2STR(sta->addr));
 
+			/* Send D-Bus event */
+			wpas_notify_mesh_peer_disconnected(wpa_s, sta->addr,
+							   reason);
+
 			hapd->num_plinks--;
 
 			mesh_mpm_send_plink_action(wpa_s, sta,
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index 628382c..25dcde5 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -75,12 +75,15 @@
 
 
 static const u8 *auth_get_psk(void *ctx, const u8 *addr,
-			      const u8 *p2p_dev_addr, const u8 *prev_psk)
+			      const u8 *p2p_dev_addr, const u8 *prev_psk,
+			      size_t *psk_len)
 {
 	struct mesh_rsn *mesh_rsn = ctx;
 	struct hostapd_data *hapd = mesh_rsn->wpa_s->ifmsh->bss[0];
 	struct sta_info *sta = ap_get_sta(hapd, addr);
 
+	if (psk_len)
+		*psk_len = PMK_LEN;
 	wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)",
 		   __func__, MAC2STR(addr), prev_psk);
 
@@ -314,7 +317,12 @@
 				     struct wpa_ssid *ssid,
 				     struct sta_info *sta)
 {
-	if (ssid->passphrase == NULL) {
+	const char *password;
+
+	password = ssid->sae_password;
+	if (!password)
+		password = ssid->passphrase;
+	if (!password) {
 		wpa_msg(wpa_s, MSG_DEBUG, "SAE: No password available");
 		return -1;
 	}
@@ -325,8 +333,8 @@
 	}
 
 	return sae_prepare_commit(wpa_s->own_addr, sta->addr,
-				  (u8 *) ssid->passphrase,
-				  os_strlen(ssid->passphrase), sta->sae);
+				  (u8 *) password, os_strlen(password),
+				  sta->sae);
 }
 
 
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 9464c4b..a5db82c 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -972,3 +972,49 @@
 							url);
 #endif /* CONFIG_HS20 */
 }
+
+
+#ifdef CONFIG_MESH
+
+void wpas_notify_mesh_group_started(struct wpa_supplicant *wpa_s,
+				    struct wpa_ssid *ssid)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_signal_mesh_group_started(wpa_s, ssid);
+}
+
+
+void wpas_notify_mesh_group_removed(struct wpa_supplicant *wpa_s,
+				    const u8 *meshid, u8 meshid_len,
+				    int reason_code)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_signal_mesh_group_removed(wpa_s, meshid, meshid_len,
+					    reason_code);
+}
+
+
+void wpas_notify_mesh_peer_connected(struct wpa_supplicant *wpa_s,
+				     const u8 *peer_addr)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_signal_mesh_peer_connected(wpa_s, peer_addr);
+}
+
+
+void wpas_notify_mesh_peer_disconnected(struct wpa_supplicant *wpa_s,
+					const u8 *peer_addr, int reason_code)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_signal_mesh_peer_disconnected(wpa_s, peer_addr, reason_code);
+}
+
+#endif /* CONFIG_MESH */
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index 9c98524..26b07f5 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -145,6 +145,15 @@
 void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s,
 					 const u8 *sa, const u8 *go_dev_addr,
 					 const u8 *bssid, int id, int op_freq);
+void wpas_notify_mesh_group_started(struct wpa_supplicant *wpa_s,
+				    struct wpa_ssid *ssid);
+void wpas_notify_mesh_group_removed(struct wpa_supplicant *wpa_s,
+				    const u8 *meshid, u8 meshid_len,
+				    int reason_code);
+void wpas_notify_mesh_peer_connected(struct wpa_supplicant *wpa_s,
+				     const u8 *peer_addr);
+void wpas_notify_mesh_peer_disconnected(struct wpa_supplicant *wpa_s,
+					const u8 *peer_addr, int reason_code);
 void wpas_notify_anqp_query_done(struct wpa_supplicant *wpa_s, const u8* bssid,
 				 const char* result,
 				 const struct wpa_bss_anqp *anqp);
diff --git a/wpa_supplicant/offchannel.c b/wpa_supplicant/offchannel.c
index 26d41a4..b74be7d 100644
--- a/wpa_supplicant/offchannel.c
+++ b/wpa_supplicant/offchannel.c
@@ -310,6 +310,8 @@
 
 		iface = wpas_get_tx_interface(wpa_s, src);
 		wpa_s->action_tx_wait_time = wait_time;
+		if (wait_time)
+			wpa_s->action_tx_wait_time_used = 1;
 
 		ret = wpa_drv_send_action(
 			iface, wpa_s->pending_action_freq,
@@ -398,13 +400,14 @@
 	wpabuf_free(wpa_s->pending_action_tx);
 	wpa_s->pending_action_tx = NULL;
 	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX &&
-	    wpa_s->action_tx_wait_time)
+	    (wpa_s->action_tx_wait_time || wpa_s->action_tx_wait_time_used))
 		wpa_drv_send_action_cancel_wait(wpa_s);
 	else if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
 		wpa_drv_cancel_remain_on_channel(wpa_s);
 		wpa_s->off_channel_freq = 0;
 		wpa_s->roc_waiting_drv_freq = 0;
 	}
+	wpa_s->action_tx_wait_time_used = 0;
 }
 
 
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index a341549..0eccd52 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -1320,6 +1320,10 @@
 	if (wpa_s->p2p_go_group_formation_completed) {
 		wpa_s->global->p2p_group_formation = NULL;
 		wpa_s->p2p_in_provisioning = 0;
+	} else if (wpa_s->p2p_in_provisioning && !success) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"P2P: Stop provisioning state due to failure");
+		wpa_s->p2p_in_provisioning = 0;
 	}
 	wpa_s->p2p_in_invitation = 0;
 	wpa_s->group_formation_reported = 1;
@@ -1998,6 +2002,11 @@
 		d->wps_nfc_dh_pubkey = wpabuf_dup(s->wps_nfc_dh_pubkey);
 	}
 	d->p2p_cli_probe = s->p2p_cli_probe;
+	d->go_interworking = s->go_interworking;
+	d->go_access_network_type = s->go_access_network_type;
+	d->go_internet = s->go_internet;
+	d->go_venue_group = s->go_venue_group;
+	d->go_venue_type = s->go_venue_type;
 }
 
 
@@ -5626,8 +5635,10 @@
 		if (!res && size > 0) {
 			i = 0;
 			while (i < size &&
-			       wpas_p2p_disallowed_freq(wpa_s->global,
-							pref_freq_list[i])) {
+			       (!p2p_supported_freq(wpa_s->global->p2p,
+						    pref_freq_list[i]) ||
+				wpas_p2p_disallowed_freq(wpa_s->global,
+							 pref_freq_list[i]))) {
 				wpa_printf(MSG_DEBUG,
 					   "P2P: preferred_freq_list[%d]=%d is disallowed",
 					   i, pref_freq_list[i]);
@@ -5729,30 +5740,6 @@
 {
 	unsigned int i, r;
 
-	/* first try some random selection of the social channels */
-	if (os_get_random((u8 *) &r, sizeof(r)) < 0)
-		return;
-
-	for (i = 0; i < 3; i++) {
-		params->freq = 2412 + ((r + i) % 3) * 25;
-		if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
-			goto out;
-	}
-
-	/* try all other channels in operating class 81 */
-	for (i = 0; i < 11; i++) {
-		params->freq = 2412 + i * 5;
-
-		/* skip social channels; covered in the previous loop */
-		if (params->freq == 2412 ||
-		    params->freq == 2437 ||
-		    params->freq == 2462)
-			continue;
-
-		if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
-			goto out;
-	}
-
 	/* try all channels in operating class 115 */
 	for (i = 0; i < 4; i++) {
 		params->freq = 5180 + i * 20;
@@ -5787,6 +5774,30 @@
 			goto out;
 	}
 
+	/* try some random selection of the social channels */
+	if (os_get_random((u8 *) &r, sizeof(r)) < 0)
+		return;
+
+	for (i = 0; i < 3; i++) {
+		params->freq = 2412 + ((r + i) % 3) * 25;
+		if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
+			goto out;
+	}
+
+	/* try all other channels in operating class 81 */
+	for (i = 0; i < 11; i++) {
+		params->freq = 2412 + i * 5;
+
+		/* skip social channels; covered in the previous loop */
+		if (params->freq == 2412 ||
+		    params->freq == 2437 ||
+		    params->freq == 2462)
+			continue;
+
+		if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
+			goto out;
+	}
+
 	params->freq = 0;
 	wpa_printf(MSG_DEBUG, "P2P: No 2.4, 5, or 60 GHz channel allowed");
 	return;
@@ -5846,12 +5857,30 @@
 
 	/* try using the forced freq */
 	if (freq) {
-		if (!wpas_p2p_supported_freq_go(wpa_s, channels, freq)) {
+		if (wpas_p2p_disallowed_freq(wpa_s->global, freq) ||
+		    !freq_included(wpa_s, channels, freq)) {
 			wpa_printf(MSG_DEBUG,
-				   "P2P: Forced GO freq %d MHz not accepted",
+				   "P2P: Forced GO freq %d MHz disallowed",
 				   freq);
 			goto fail;
 		}
+		if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
+			if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+			    ieee80211_is_dfs(freq)) {
+				/*
+				 * If freq is a DFS channel and DFS is offloaded
+				 * to the driver, allow P2P GO to use it.
+				 */
+				wpa_printf(MSG_DEBUG,
+					   "P2P: %s: The forced channel for GO (%u MHz) requires DFS and DFS is offloaded",
+					   __func__, freq);
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "P2P: The forced channel for GO (%u MHz) is not supported for P2P uses",
+					   freq);
+				goto fail;
+			}
+		}
 
 		for (i = 0; i < num; i++) {
 			if (freqs[i].freq == freq) {
@@ -6090,24 +6119,7 @@
 	if (wpas_p2p_init_go_params(wpa_s, &params, freq, vht_center_freq2,
 				    ht40, vht, max_oper_chwidth, NULL))
 		return -1;
-	if (params.freq &&
-	    !p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) {
-		if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
-		    ieee80211_is_dfs(params.freq)) {
-			/*
-			 * If freq is a DFS channel and DFS is offloaded to the
-			 * driver, allow P2P GO to use it.
-			 */
-			wpa_printf(MSG_DEBUG,
-				   "P2P: %s: The forced channel for GO (%u MHz) is DFS, and DFS is offloaded to driver",
-				__func__, params.freq);
-		} else {
-			wpa_printf(MSG_DEBUG,
-				   "P2P: The selected channel for GO (%u MHz) is not supported for P2P uses",
-				   params.freq);
-			return -1;
-		}
-	}
+
 	p2p_go_params(wpa_s->global->p2p, &params);
 	params.persistent_group = persistent_group;
 
@@ -6583,8 +6595,14 @@
 	wpa_s->p2p_long_listen = 0;
 
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL ||
-	    wpa_s->p2p_in_provisioning)
+	    wpa_s->p2p_in_provisioning) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Reject p2p_find operation%s%s",
+			(wpa_s->global->p2p_disabled || !wpa_s->global->p2p) ?
+			" (P2P disabled)" : "",
+			wpa_s->p2p_in_provisioning ?
+			" (p2p_in_provisioning)" : "");
 		return -1;
+	}
 
 	wpa_supplicant_cancel_sched_scan(wpa_s);
 
@@ -9065,16 +9083,20 @@
 	unsigned int i, invalid_freq = 0, policy_move = 0, flags = 0;
 	unsigned int timeout;
 	int freq;
+	int dfs_offload;
 
 	wpas_p2p_go_update_common_freqs(wpa_s);
 
 	freq = wpa_s->current_ssid->frequency;
+	dfs_offload = (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+		ieee80211_is_dfs(freq);
 	for (i = 0, invalid_freq = 0; i < num; i++) {
 		if (freqs[i].freq == freq) {
 			flags = freqs[i].flags;
 
 			/* The channel is invalid, must change it */
-			if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
+			if (!p2p_supported_freq_go(wpa_s->global->p2p, freq) &&
+			    !dfs_offload) {
 				wpa_dbg(wpa_s, MSG_DEBUG,
 					"P2P: Freq=%d MHz no longer valid for GO",
 					freq);
@@ -9084,7 +9106,7 @@
 			/* Freq is not used by any other station interface */
 			continue;
 		} else if (!p2p_supported_freq(wpa_s->global->p2p,
-					       freqs[i].freq)) {
+					       freqs[i].freq) && !dfs_offload) {
 			/* Freq is not valid for P2P use cases */
 			continue;
 		} else if (wpa_s->conf->p2p_go_freq_change_policy ==
diff --git a/wpa_supplicant/preauth_test.c b/wpa_supplicant/preauth_test.c
index 6ae239f..a213a30 100644
--- a/wpa_supplicant/preauth_test.c
+++ b/wpa_supplicant/preauth_test.c
@@ -144,7 +144,9 @@
 
 
 static int wpa_supplicant_add_pmkid(void *wpa_s, void *network_ctx,
-				    const u8 *bssid, const u8 *pmkid)
+				    const u8 *bssid, const u8 *pmkid,
+				    const u8 *fils_cache_id,
+				    const u8 *pmk, size_t pmk_len)
 {
 	printf("%s - not implemented\n", __func__);
 	return -1;
@@ -152,7 +154,8 @@
 
 
 static int wpa_supplicant_remove_pmkid(void *wpa_s, void *network_ctx,
-				       const u8 *bssid, const u8 *pmkid)
+				       const u8 *bssid, const u8 *pmkid,
+				       const u8 *fils_cache_id)
 {
 	printf("%s - not implemented\n", __func__);
 	return -1;
@@ -344,8 +347,8 @@
 	if (preauth_test.auth_timed_out)
 		ret = -2;
 	else {
-		ret = pmksa_cache_set_current(wpa_s.wpa, NULL, bssid, NULL, 0)
-			? 0 : -3;
+		ret = pmksa_cache_set_current(wpa_s.wpa, NULL, bssid, NULL, 0,
+					      NULL) ? 0 : -3;
 	}
 
 	test_eapol_clean(&wpa_s);
diff --git a/wpa_supplicant/rrm.c b/wpa_supplicant/rrm.c
index 5be917c..f4fbfa7 100644
--- a/wpa_supplicant/rrm.c
+++ b/wpa_supplicant/rrm.c
@@ -355,7 +355,8 @@
 	return 0;
 
 reject:
-	if (wpas_rrm_report_elem(buf, req->token,
+	if (!is_multicast_ether_addr(wpa_s->rrm.dst_addr) &&
+	    wpas_rrm_report_elem(buf, req->token,
 				 MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE,
 				 req->type, NULL, 0) < 0) {
 		wpa_printf(MSG_DEBUG, "RRM: Failed to add report element");
@@ -409,7 +410,8 @@
 			pos = next;
 		}
 
-		next += next[1] + 2;
+		if (len)
+			next += next[1] + 2;
 	}
 #undef MPDU_REPORT_LEN
 }
@@ -627,6 +629,7 @@
 	if (ext_freqs) {
 		int_array_concat(&freqs, ext_freqs);
 		os_free(ext_freqs);
+		int_array_sort_unique(freqs);
 	}
 
 	return freqs;
@@ -793,7 +796,7 @@
 	u8 *ie = (u8 *) (bss + 1);
 	size_t ie_len = bss->ie_len + bss->beacon_ie_len;
 	int ret;
-	u8 buf[2000];
+	u8 *buf;
 	struct rrm_measurement_beacon_report *rep;
 
 	if (os_memcmp(data->bssid, broadcast_ether_addr, ETH_ALEN) != 0 &&
@@ -805,10 +808,18 @@
 	     os_memcmp(data->ssid, bss->ssid, bss->ssid_len) != 0))
 		return 0;
 
+	/* Maximum element length: beacon report element + reported frame body
+	 * subelement + all IEs of the reported beacon */
+	buf = os_malloc(sizeof(*rep) + 14 + ie_len);
+	if (!buf)
+		return -1;
+
 	rep = (struct rrm_measurement_beacon_report *) buf;
 	if (wpas_get_op_chan_phy(bss->freq, ie, ie_len, &rep->op_class,
-				 &rep->channel, &rep->report_info) < 0)
-		return 0;
+				 &rep->channel, &rep->report_info) < 0) {
+		ret = 0;
+		goto out;
+	}
 
 	rep->start_time = host_to_le64(start);
 	rep->duration = host_to_le16(data->scan_params.duration);
@@ -819,15 +830,17 @@
 	rep->parent_tsf = host_to_le32(parent_tsf);
 
 	ret = wpas_beacon_rep_add_frame_body(data->eids, data->report_detail,
-					     bss, rep->variable,
-					     sizeof(buf) - sizeof(*rep));
+					     bss, rep->variable, 14 + ie_len);
 	if (ret < 0)
-		return -1;
+		goto out;
 
-	return wpas_rrm_report_elem(wpa_buf, wpa_s->beacon_rep_data.token,
-				    MEASUREMENT_REPORT_MODE_ACCEPT,
-				    MEASURE_TYPE_BEACON, buf,
-				    ret + sizeof(*rep));
+	ret = wpas_rrm_report_elem(wpa_buf, wpa_s->beacon_rep_data.token,
+				   MEASUREMENT_REPORT_MODE_ACCEPT,
+				   MEASURE_TYPE_BEACON, buf,
+				   ret + sizeof(*rep));
+out:
+	os_free(buf);
+	return ret;
 }
 
 
@@ -858,21 +871,24 @@
 }
 
 
-static void wpas_rrm_refuse_request(struct wpa_supplicant *wpa_s)
+void wpas_rrm_refuse_request(struct wpa_supplicant *wpa_s)
 {
-	struct wpabuf *buf = NULL;
+	if (!is_multicast_ether_addr(wpa_s->rrm.dst_addr)) {
+		struct wpabuf *buf = NULL;
 
-	if (wpas_rrm_report_elem(&buf, wpa_s->beacon_rep_data.token,
-				 MEASUREMENT_REPORT_MODE_REJECT_REFUSED,
-				 MEASURE_TYPE_BEACON, NULL, 0)) {
-		wpa_printf(MSG_ERROR, "RRM: Memory allocation failed");
+		if (wpas_rrm_report_elem(&buf, wpa_s->beacon_rep_data.token,
+					 MEASUREMENT_REPORT_MODE_REJECT_REFUSED,
+					 MEASURE_TYPE_BEACON, NULL, 0)) {
+			wpa_printf(MSG_ERROR, "RRM: Memory allocation failed");
+			wpabuf_free(buf);
+			return;
+		}
+
+		wpas_rrm_send_msr_report(wpa_s, buf);
 		wpabuf_free(buf);
-		return;
 	}
 
-	wpas_rrm_send_msr_report(wpa_s, buf);
 	wpas_clear_beacon_rep_data(wpa_s);
-	wpabuf_free(buf);
 }
 
 
@@ -952,7 +968,7 @@
 		    BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS) {
 			wpa_printf(MSG_DEBUG, "Invalid reporting detail: %u",
 				   subelem[0]);
-			return 0;
+			return -1;
 		}
 
 		break;
@@ -1019,6 +1035,7 @@
 	u32 interval_usec;
 	u32 _rand;
 	int ret = 0, res;
+	u8 reject_mode;
 
 	if (len < sizeof(*req))
 		return -1;
@@ -1052,9 +1069,12 @@
 
 		res = wpas_rm_handle_beacon_req_subelem(
 			wpa_s, data, subelems[0], subelems[1], &subelems[2]);
-		if (res != 1) {
+		if (res < 0) {
 			ret = res;
 			goto out;
+		} else if (!res) {
+			reject_mode = MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE;
+			goto out_reject;
 		}
 
 		elems_len -= 2 + subelems[1];
@@ -1072,7 +1092,8 @@
 		req->variable, len - sizeof(*req));
 	if (!params->freqs) {
 		wpa_printf(MSG_DEBUG, "Beacon request: No valid channels");
-		goto out;
+		reject_mode = MEASUREMENT_REPORT_MODE_REJECT_REFUSED;
+		goto out_reject;
 	}
 
 	params->duration = le_to_host16(req->duration);
@@ -1096,6 +1117,13 @@
 	eloop_register_timeout(0, interval_usec, wpas_rrm_scan_timeout, wpa_s,
 			       NULL);
 	return 1;
+out_reject:
+	if (!is_multicast_ether_addr(wpa_s->rrm.dst_addr) &&
+	    wpas_rrm_report_elem(buf, elem_token, reject_mode,
+				 MEASURE_TYPE_BEACON, NULL, 0) < 0) {
+		wpa_printf(MSG_DEBUG, "RRM: Failed to add report element");
+		ret = -1;
+	}
 out:
 	wpas_clear_beacon_rep_data(wpa_s);
 	return ret;
@@ -1153,7 +1181,8 @@
 	}
 
 reject:
-	if (wpas_rrm_report_elem(buf, req->token,
+	if (!is_multicast_ether_addr(wpa_s->rrm.dst_addr) &&
+	    wpas_rrm_report_elem(buf, req->token,
 				 MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE,
 				 req->type, NULL, 0) < 0) {
 		wpa_printf(MSG_DEBUG, "RRM: Failed to add report element");
@@ -1214,7 +1243,7 @@
 
 
 void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s,
-					       const u8 *src,
+					       const u8 *src, const u8 *dst,
 					       const u8 *frame, size_t len)
 {
 	struct wpabuf *report;
@@ -1238,6 +1267,7 @@
 	}
 
 	wpa_s->rrm.token = *frame;
+	os_memcpy(wpa_s->rrm.dst_addr, dst, ETH_ALEN);
 
 	/* Number of repetitions is not supported */
 
@@ -1365,6 +1395,10 @@
 			continue;
 		}
 
+		/*
+		 * Don't report results that were not received during the
+		 * current measurement.
+		 */
 		if (!(wpa_s->drv_rrm_flags &
 		      WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT)) {
 			struct os_reltime update_time, diff;
@@ -1391,14 +1425,10 @@
 					   (unsigned int) diff.usec);
 				continue;
 			}
-		}
-
-		/*
-		 * Don't report results that were not received during the
-		 * current measurement.
-		 */
-		if (info->scan_start_tsf > scan_res->res[i]->parent_tsf)
+		} else if (info->scan_start_tsf >
+			   scan_res->res[i]->parent_tsf) {
 			continue;
+		}
 
 		if (wpas_add_beacon_rep(wpa_s, &buf, bss, info->scan_start_tsf,
 					scan_res->res[i]->parent_tsf) < 0)
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index 3a100cd..104b258 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -117,9 +117,19 @@
 static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s,
 				     struct wpa_ssid *ssid)
 {
+	int min_temp_disabled = 0;
+
 	while (ssid) {
-		if (!wpas_network_disabled(wpa_s, ssid))
-			break;
+		if (!wpas_network_disabled(wpa_s, ssid)) {
+			int temp_disabled = wpas_temp_disabled(wpa_s, ssid);
+
+			if (temp_disabled <= 0)
+				break;
+
+			if (!min_temp_disabled ||
+			    temp_disabled < min_temp_disabled)
+				min_temp_disabled = temp_disabled;
+		}
 		ssid = ssid->next;
 	}
 
@@ -128,7 +138,7 @@
 		wpa_dbg(wpa_s, MSG_DEBUG, "wpa_supplicant_assoc_try: Reached "
 			"end of scan list - go back to beginning");
 		wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
-		wpa_supplicant_req_scan(wpa_s, 0, 0);
+		wpa_supplicant_req_scan(wpa_s, min_temp_disabled, 0);
 		return;
 	}
 	if (ssid->next) {
@@ -190,7 +200,8 @@
 	wpa_scan_free_params(params);
 	work->ctx = NULL;
 	if (ret) {
-		int retry = wpa_s->last_scan_req != MANUAL_SCAN_REQ;
+		int retry = wpa_s->last_scan_req != MANUAL_SCAN_REQ &&
+			!wpa_s->beacon_rep_data.token;
 
 		if (wpa_s->disconnected)
 			retry = 0;
@@ -208,7 +219,14 @@
 			/* Restore scan_req since we will try to scan again */
 			wpa_s->scan_req = wpa_s->last_scan_req;
 			wpa_supplicant_req_scan(wpa_s, 1, 0);
+		} else if (wpa_s->scan_res_handler) {
+			/* Clear the scan_res_handler */
+			wpa_s->scan_res_handler = NULL;
 		}
+
+		if (wpa_s->beacon_rep_data.token)
+			wpas_rrm_refuse_request(wpa_s);
+
 		return;
 	}
 
@@ -458,8 +476,8 @@
 		wpabuf_put_data(default_ies, ext_capab, ext_capab_len);
 
 #ifdef CONFIG_MBO
-	/* Send cellular capabilities for potential MBO STAs */
-	if (wpabuf_resize(&default_ies, 9) == 0)
+	/* Send MBO and OCE capabilities */
+	if (wpabuf_resize(&default_ies, 12) == 0)
 		wpas_mbo_scan_ie(wpa_s, default_ies);
 #endif /* CONFIG_MBO */
 
@@ -540,8 +558,8 @@
 #endif /* CONFIG_FST */
 
 #ifdef CONFIG_MBO
-	/* Send cellular capabilities for potential MBO STAs */
-	if (wpabuf_resize(&extra_ie, 9) == 0)
+	/* Send MBO and OCE capabilities */
+	if (wpabuf_resize(&extra_ie, 12) == 0)
 		wpas_mbo_scan_ie(wpa_s, extra_ie);
 #endif /* CONFIG_MBO */
 
@@ -1018,6 +1036,13 @@
 		wpa_s->manual_scan_freqs = NULL;
 	}
 
+	if (params.freqs == NULL && wpa_s->select_network_scan_freqs) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Limit select_network scan to specified channels");
+		params.freqs = wpa_s->select_network_scan_freqs;
+		wpa_s->select_network_scan_freqs = NULL;
+	}
+
 	if (params.freqs == NULL && wpa_s->next_scan_freqs) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on previously "
 			"generated frequency list");
@@ -1502,13 +1527,18 @@
 		params.sched_scan_plans_num = 1;
 	}
 
+	params.sched_scan_start_delay = wpa_s->conf->sched_scan_start_delay;
+
 	if (ssid || !wpa_s->first_sched_scan) {
 		wpa_dbg(wpa_s, MSG_DEBUG,
-			"Starting sched scan: interval %u timeout %d",
+			"Starting sched scan after %u seconds: interval %u timeout %d",
+			params.sched_scan_start_delay,
 			params.sched_scan_plans[0].interval,
 			wpa_s->sched_scan_timeout);
 	} else {
-		wpa_dbg(wpa_s, MSG_DEBUG, "Starting sched scan (no timeout)");
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Starting sched scan after %u seconds (no timeout)",
+			params.sched_scan_start_delay);
 	}
 
 	wpa_setband_scan_freqs(wpa_s, scan_params);
@@ -2331,11 +2361,10 @@
 
 	for (i = 0; i < src->num_ssids; i++) {
 		if (src->ssids[i].ssid) {
-			n = os_malloc(src->ssids[i].ssid_len);
+			n = os_memdup(src->ssids[i].ssid,
+				      src->ssids[i].ssid_len);
 			if (n == NULL)
 				goto failed;
-			os_memcpy(n, src->ssids[i].ssid,
-				  src->ssids[i].ssid_len);
 			params->ssids[i].ssid = n;
 			params->ssids[i].ssid_len = src->ssids[i].ssid_len;
 		}
@@ -2343,30 +2372,26 @@
 	params->num_ssids = src->num_ssids;
 
 	if (src->extra_ies) {
-		n = os_malloc(src->extra_ies_len);
+		n = os_memdup(src->extra_ies, src->extra_ies_len);
 		if (n == NULL)
 			goto failed;
-		os_memcpy(n, src->extra_ies, src->extra_ies_len);
 		params->extra_ies = n;
 		params->extra_ies_len = src->extra_ies_len;
 	}
 
 	if (src->freqs) {
 		int len = int_array_len(src->freqs);
-		params->freqs = os_malloc((len + 1) * sizeof(int));
+		params->freqs = os_memdup(src->freqs, (len + 1) * sizeof(int));
 		if (params->freqs == NULL)
 			goto failed;
-		os_memcpy(params->freqs, src->freqs, (len + 1) * sizeof(int));
 	}
 
 	if (src->filter_ssids) {
-		params->filter_ssids = os_malloc(sizeof(*params->filter_ssids) *
+		params->filter_ssids = os_memdup(src->filter_ssids,
+						 sizeof(*params->filter_ssids) *
 						 src->num_filter_ssids);
 		if (params->filter_ssids == NULL)
 			goto failed;
-		os_memcpy(params->filter_ssids, src->filter_ssids,
-			  sizeof(*params->filter_ssids) *
-			  src->num_filter_ssids);
 		params->num_filter_ssids = src->num_filter_ssids;
 	}
 
@@ -2379,14 +2404,12 @@
 
 	if (src->sched_scan_plans_num > 0) {
 		params->sched_scan_plans =
-			os_malloc(sizeof(*src->sched_scan_plans) *
+			os_memdup(src->sched_scan_plans,
+				  sizeof(*src->sched_scan_plans) *
 				  src->sched_scan_plans_num);
 		if (!params->sched_scan_plans)
 			goto failed;
 
-		os_memcpy(params->sched_scan_plans, src->sched_scan_plans,
-			  sizeof(*src->sched_scan_plans) *
-			  src->sched_scan_plans_num);
 		params->sched_scan_plans_num = src->sched_scan_plans_num;
 	}
 
@@ -2411,10 +2434,9 @@
 	if (src->bssid) {
 		u8 *bssid;
 
-		bssid = os_malloc(ETH_ALEN);
+		bssid = os_memdup(src->bssid, ETH_ALEN);
 		if (!bssid)
 			goto failed;
-		os_memcpy(bssid, src->bssid, ETH_ALEN);
 		params->bssid = bssid;
 	}
 
@@ -2586,6 +2608,8 @@
 		params.sched_scan_plans_num = 1;
 	}
 
+	params.sched_scan_start_delay = wpa_s->conf->sched_scan_start_delay;
+
 	if (params.freqs == NULL && wpa_s->manual_sched_scan_freqs) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Limit sched scan to specified channels");
 		params.freqs = wpa_s->manual_sched_scan_freqs;
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index beb9d6e..da0e8eb 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -87,8 +87,25 @@
 {
 	struct wpabuf *buf;
 	size_t len;
+	const char *password;
 
-	if (ssid->passphrase == NULL) {
+#ifdef CONFIG_TESTING_OPTIONS
+	if (wpa_s->sae_commit_override) {
+		wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override");
+		buf = wpabuf_alloc(4 + wpabuf_len(wpa_s->sae_commit_override));
+		if (!buf)
+			return NULL;
+		wpabuf_put_le16(buf, 1); /* Transaction seq# */
+		wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+		wpabuf_put_buf(buf, wpa_s->sae_commit_override);
+		return buf;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	password = ssid->sae_password;
+	if (!password)
+		password = ssid->passphrase;
+	if (!password) {
 		wpa_printf(MSG_DEBUG, "SAE: No password available");
 		return NULL;
 	}
@@ -99,8 +116,7 @@
 	}
 
 	if (sae_prepare_commit(wpa_s->own_addr, bssid,
-			       (u8 *) ssid->passphrase,
-			       os_strlen(ssid->passphrase),
+			       (u8 *) password, os_strlen(password),
 			       &wpa_s->sme.sae) < 0) {
 		wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
 		return NULL;
@@ -208,9 +224,9 @@
 #ifdef CONFIG_IEEE80211R
 	const u8 *ie;
 #endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211R
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS)
 	const u8 *md = NULL;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R || CONFIG_FILS */
 	int i, bssid_changed;
 	struct wpabuf *resp = NULL;
 	u8 ext_capab[18];
@@ -301,13 +317,19 @@
 	     wpa_bss_get_ie(bss, WLAN_EID_RSN)) &&
 	    wpa_key_mgmt_wpa(ssid->key_mgmt)) {
 		int try_opportunistic;
+		const u8 *cache_id = NULL;
+
 		try_opportunistic = (ssid->proactive_key_caching < 0 ?
 				     wpa_s->conf->okc :
 				     ssid->proactive_key_caching) &&
 			(ssid->proto & WPA_PROTO_RSN);
+#ifdef CONFIG_FILS
+		if (wpa_key_mgmt_fils(ssid->key_mgmt))
+			cache_id = wpa_bss_get_fils_cache_id(bss);
+#endif /* CONFIG_FILS */
 		if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
 					    wpa_s->current_ssid,
-					    try_opportunistic) == 0)
+					    try_opportunistic, cache_id) == 0)
 			eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
 		wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
 		if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
@@ -367,7 +389,12 @@
 		wpa_ft_prepare_auth_request(wpa_s->wpa, ie);
 	}
 
-	if (md && wpa_key_mgmt_ft(ssid->key_mgmt)) {
+	if (md && !wpa_key_mgmt_ft(ssid->key_mgmt))
+		md = NULL;
+	if (md) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "SME: FT mobility domain %02x%02x",
+			md[0], md[1]);
+
 		if (wpa_s->sme.assoc_req_ie_len + 5 <
 		    sizeof(wpa_s->sme.assoc_req_ie)) {
 			struct rsn_mdie *mdie;
@@ -516,8 +543,8 @@
 
 #ifdef CONFIG_SAE
 	if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE &&
-	    pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0) == 0)
-	{
+	    pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0,
+				    NULL) == 0) {
 		wpa_dbg(wpa_s, MSG_DEBUG,
 			"PMKSA cache entry found - try to use PMKSA caching instead of new SAE authentication");
 		params.auth_alg = WPA_AUTH_ALG_OPEN;
@@ -551,18 +578,63 @@
 	 * networks). */
 	if (params.auth_alg == WPA_AUTH_ALG_OPEN &&
 	    wpa_key_mgmt_fils(ssid->key_mgmt)) {
+		const u8 *indic;
+		u16 fils_info;
+
+		/*
+		 * Check FILS Indication element (FILS Information field) bits
+		 * indicating supported authentication algorithms against local
+		 * configuration (ssid->fils_dh_group). Try to use FILS
+		 * authentication only if the AP supports the combination in the
+		 * network profile. */
+		indic = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION);
+		if (!indic || indic[1] < 2) {
+			wpa_printf(MSG_DEBUG, "SME: " MACSTR
+				   " does not include FILS Indication element - cannot use FILS authentication with it",
+				   MAC2STR(bss->bssid));
+			goto no_fils;
+		}
+
+		fils_info = WPA_GET_LE16(indic + 2);
+		if (ssid->fils_dh_group == 0 && !(fils_info & BIT(9))) {
+			wpa_printf(MSG_DEBUG, "SME: " MACSTR
+				   " does not support FILS SK without PFS - cannot use FILS authentication with it",
+				   MAC2STR(bss->bssid));
+			goto no_fils;
+		}
+		if (ssid->fils_dh_group != 0 && !(fils_info & BIT(10))) {
+			wpa_printf(MSG_DEBUG, "SME: " MACSTR
+				   " does not support FILS SK with PFS - cannot use FILS authentication with it",
+				   MAC2STR(bss->bssid));
+			goto no_fils;
+		}
+
 		if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
-					    ssid, 0) == 0)
+					    ssid, 0,
+					    wpa_bss_get_fils_cache_id(bss)) ==
+		    0)
 			wpa_printf(MSG_DEBUG,
 				   "SME: Try to use FILS with PMKSA caching");
-		resp = fils_build_auth(wpa_s->wpa);
+		resp = fils_build_auth(wpa_s->wpa, ssid->fils_dh_group, md);
 		if (resp) {
-			params.auth_alg = WPA_AUTH_ALG_FILS;
+			int auth_alg;
+
+			if (ssid->fils_dh_group)
+				wpa_printf(MSG_DEBUG,
+					   "SME: Try to use FILS SK authentication with PFS (DH Group %u)",
+					   ssid->fils_dh_group);
+			else
+				wpa_printf(MSG_DEBUG,
+					   "SME: Try to use FILS SK authentication without PFS");
+			auth_alg = ssid->fils_dh_group ?
+				WPA_AUTH_ALG_FILS_SK_PFS : WPA_AUTH_ALG_FILS;
+			params.auth_alg = auth_alg;
 			params.auth_data = wpabuf_head(resp);
 			params.auth_data_len = wpabuf_len(resp);
-			wpa_s->sme.auth_alg = WPA_AUTH_ALG_FILS;
+			wpa_s->sme.auth_alg = auth_alg;
 		}
 	}
+no_fils:
 #endif /* CONFIG_FILS */
 
 	wpa_supplicant_cancel_sched_scan(wpa_s);
@@ -572,6 +644,7 @@
 		" (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid),
 		wpa_ssid_txt(params.ssid, params.ssid_len), params.freq);
 
+	eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
 	wpa_clear_keys(wpa_s, bss->bssid);
 	wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);
 	if (old_ssid != wpa_s->current_ssid)
@@ -959,9 +1032,29 @@
 #endif /* CONFIG_IEEE80211R */
 
 #ifdef CONFIG_FILS
-	if (data->auth.auth_type == WLAN_AUTH_FILS_SK) {
-		if (fils_process_auth(wpa_s->wpa, data->auth.ies,
-				      data->auth.ies_len) < 0) {
+	if (data->auth.auth_type == WLAN_AUTH_FILS_SK ||
+	    data->auth.auth_type == WLAN_AUTH_FILS_SK_PFS) {
+		u16 expect_auth_type;
+
+		expect_auth_type = wpa_s->sme.auth_alg ==
+			WPA_AUTH_ALG_FILS_SK_PFS ? WLAN_AUTH_FILS_SK_PFS :
+			WLAN_AUTH_FILS_SK;
+		if (data->auth.auth_type != expect_auth_type) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"SME: FILS Authentication response used different auth alg (%u; expected %u)",
+				data->auth.auth_type, expect_auth_type);
+			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid="
+				MACSTR
+				" reason=%d locally_generated=1",
+				MAC2STR(wpa_s->pending_bssid),
+				WLAN_REASON_DEAUTH_LEAVING);
+			wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+			wpa_supplicant_mark_disassoc(wpa_s);
+			return;
+		}
+
+		if (fils_process_auth(wpa_s->wpa, wpa_s->pending_bssid,
+				      data->auth.ies, data->auth.ies_len) < 0) {
 			wpa_dbg(wpa_s, MSG_DEBUG,
 				"SME: FILS Authentication response processing failed");
 			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid="
@@ -981,6 +1074,24 @@
 }
 
 
+#ifdef CONFIG_FILS
+#ifdef CONFIG_IEEE80211R
+static void remove_ie(u8 *buf, size_t *len, u8 eid)
+{
+	u8 *pos, *next, *end;
+
+	pos = (u8 *) get_ie(buf, *len, eid);
+	if (pos) {
+		next = pos + 2 + pos[1];
+		end = buf + *len;
+		*len -= 2 + pos[1];
+		os_memmove(pos, next, end - next);
+	}
+}
+#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_FILS */
+
+
 void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
 		   const u8 *bssid, u16 auth_type)
 {
@@ -1001,7 +1112,8 @@
 	os_memset(&params, 0, sizeof(params));
 
 #ifdef CONFIG_FILS
-	if (auth_type == WLAN_AUTH_FILS_SK) {
+	if (auth_type == WLAN_AUTH_FILS_SK ||
+	    auth_type == WLAN_AUTH_FILS_SK_PFS) {
 		struct wpabuf *buf;
 		const u8 *snonce, *anonce;
 		const unsigned int max_hlp = 20;
@@ -1035,6 +1147,30 @@
 			wpabuf_free(hlp[i]);
 		if (!buf)
 			return;
+		wpa_hexdump(MSG_DEBUG, "FILS: assoc_req before FILS elements",
+			    wpa_s->sme.assoc_req_ie,
+			    wpa_s->sme.assoc_req_ie_len);
+#ifdef CONFIG_IEEE80211R
+		if (wpa_key_mgmt_ft(wpa_s->key_mgmt)) {
+			/* Remove RSNE and MDE to allow them to be overridden
+			 * with FILS+FT specific values from
+			 * fils_build_assoc_req(). */
+			remove_ie(wpa_s->sme.assoc_req_ie,
+				  &wpa_s->sme.assoc_req_ie_len,
+				  WLAN_EID_RSN);
+			wpa_hexdump(MSG_DEBUG,
+				    "FILS: assoc_req after RSNE removal",
+				    wpa_s->sme.assoc_req_ie,
+				    wpa_s->sme.assoc_req_ie_len);
+			remove_ie(wpa_s->sme.assoc_req_ie,
+				  &wpa_s->sme.assoc_req_ie_len,
+				  WLAN_EID_MOBILITY_DOMAIN);
+			wpa_hexdump(MSG_DEBUG,
+				    "FILS: assoc_req after MDE removal",
+				    wpa_s->sme.assoc_req_ie,
+				    wpa_s->sme.assoc_req_ie_len);
+		}
+#endif /* CONFIG_IEEE80211R */
 		/* TODO: Make wpa_s->sme.assoc_req_ie use dynamic allocation */
 		if (wpa_s->sme.assoc_req_ie_len + wpabuf_len(buf) >
 		    sizeof(wpa_s->sme.assoc_req_ie)) {
@@ -1047,6 +1183,9 @@
 			  wpabuf_head(buf), wpabuf_len(buf));
 		wpa_s->sme.assoc_req_ie_len += wpabuf_len(buf);
 		wpabuf_free(buf);
+		wpa_hexdump(MSG_DEBUG, "FILS: assoc_req after FILS elements",
+			    wpa_s->sme.assoc_req_ie,
+			    wpa_s->sme.assoc_req_ie_len);
 
 		os_memcpy(nonces, snonce, FILS_NONCE_LEN);
 		os_memcpy(nonces + FILS_NONCE_LEN, anonce, FILS_NONCE_LEN);
@@ -1055,6 +1194,40 @@
 	}
 #endif /* CONFIG_FILS */
 
+#ifdef CONFIG_OWE
+#ifdef CONFIG_TESTING_OPTIONS
+	if (get_ie_ext(wpa_s->sme.assoc_req_ie, wpa_s->sme.assoc_req_ie_len,
+		       WLAN_EID_EXT_OWE_DH_PARAM)) {
+		wpa_printf(MSG_INFO, "TESTING: Override OWE DH element");
+	} else
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (auth_type == WLAN_AUTH_OPEN &&
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_OWE) {
+		struct wpabuf *owe_ie;
+		u16 group = OWE_DH_GROUP;
+
+		if (wpa_s->current_ssid && wpa_s->current_ssid->owe_group)
+			group = wpa_s->current_ssid->owe_group;
+		owe_ie = owe_build_assoc_req(wpa_s->wpa, group);
+		if (!owe_ie) {
+			wpa_printf(MSG_ERROR,
+				   "OWE: Failed to build IE for Association Request frame");
+			return;
+		}
+		if (wpa_s->sme.assoc_req_ie_len + wpabuf_len(owe_ie) >
+		    sizeof(wpa_s->sme.assoc_req_ie)) {
+			wpa_printf(MSG_ERROR,
+				   "OWE: Not enough buffer room for own Association Request frame elements");
+			wpabuf_free(owe_ie);
+			return;
+		}
+		os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
+			  wpabuf_head(owe_ie), wpabuf_len(owe_ie));
+		wpa_s->sme.assoc_req_ie_len += wpabuf_len(owe_ie);
+		wpabuf_free(owe_ie);
+	}
+#endif /* CONFIG_OWE */
+
 	params.bssid = bssid;
 	params.ssid = wpa_s->sme.ssid;
 	params.ssid_len = wpa_s->sme.ssid_len;
@@ -1066,6 +1239,7 @@
 	params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
 	params.pairwise_suite = wpa_s->pairwise_cipher;
 	params.group_suite = wpa_s->group_cipher;
+	params.mgmt_group_suite = wpa_s->mgmt_group_cipher;
 	params.key_mgmt_suite = wpa_s->key_mgmt;
 	params.wpa_proto = wpa_s->wpa_proto;
 #ifdef CONFIG_HT_OVERRIDES
@@ -1140,6 +1314,14 @@
 
 	eloop_register_timeout(SME_ASSOC_TIMEOUT, 0, sme_assoc_timer, wpa_s,
 			       NULL);
+
+#ifdef CONFIG_TESTING_OPTIONS
+	wpabuf_free(wpa_s->last_assoc_req_wpa_ie);
+	wpa_s->last_assoc_req_wpa_ie = NULL;
+	if (params.wpa_ie)
+		wpa_s->last_assoc_req_wpa_ie =
+			wpabuf_alloc_copy(params.wpa_ie, params.wpa_ie_len);
+#endif /* CONFIG_TESTING_OPTIONS */
 }
 
 
@@ -1158,10 +1340,9 @@
 	os_memcpy(wpa_s->sme.mobility_domain, md, MOBILITY_DOMAIN_ID_LEN);
 	wpa_hexdump(MSG_DEBUG, "SME: FT IEs", ies, ies_len);
 	os_free(wpa_s->sme.ft_ies);
-	wpa_s->sme.ft_ies = os_malloc(ies_len);
+	wpa_s->sme.ft_ies = os_memdup(ies, ies_len);
 	if (wpa_s->sme.ft_ies == NULL)
 		return -1;
-	os_memcpy(wpa_s->sme.ft_ies, ies, ies_len);
 	wpa_s->sme.ft_ies_len = ies_len;
 	return 0;
 }
diff --git a/wpa_supplicant/wifi_display.c b/wpa_supplicant/wifi_display.c
index c363b21..c94e461 100644
--- a/wpa_supplicant/wifi_display.c
+++ b/wpa_supplicant/wifi_display.c
@@ -86,6 +86,7 @@
 		p2p_set_wfd_ie_prov_disc_resp(global->p2p, NULL);
 		p2p_set_wfd_ie_go_neg(global->p2p, NULL);
 		p2p_set_wfd_dev_info(global->p2p, NULL);
+		p2p_set_wfd_r2_dev_info(global->p2p, NULL);
 		p2p_set_wfd_assoc_bssid(global->p2p, NULL);
 		p2p_set_wfd_coupled_sink_info(global->p2p, NULL);
 		return 0;
@@ -93,6 +94,8 @@
 
 	p2p_set_wfd_dev_info(global->p2p,
 			     global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
+	p2p_set_wfd_r2_dev_info(
+		global->p2p, global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO]);
 	p2p_set_wfd_assoc_bssid(
 		global->p2p,
 		global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]);
@@ -133,6 +136,11 @@
 	if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
 		len += wpabuf_len(global->wfd_subelem[
 					  WFD_SUBELEM_DEVICE_INFO]);
+
+	if (global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO])
+		len += wpabuf_len(global->wfd_subelem[
+					  WFD_SUBELEM_R2_DEVICE_INFO]);
+
 	if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
 		len += wpabuf_len(global->wfd_subelem[
 					  WFD_SUBELEM_ASSOCIATED_BSSID]);
@@ -151,6 +159,11 @@
 	if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
 		wpabuf_put_buf(buf,
 			       global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
+
+	if (global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO])
+		wpabuf_put_buf(buf,
+			       global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO]);
+
 	if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
 		wpabuf_put_buf(buf, global->wfd_subelem[
 				       WFD_SUBELEM_ASSOCIATED_BSSID]);
diff --git a/wpa_supplicant/wmm_ac.c b/wpa_supplicant/wmm_ac.c
index 5625d36..a88cc46 100644
--- a/wpa_supplicant/wmm_ac.c
+++ b/wpa_supplicant/wmm_ac.c
@@ -87,13 +87,10 @@
 	}
 
 	/* copy tspec */
-	_tspec = os_malloc(sizeof(*_tspec));
+	_tspec = os_memdup(tspec, sizeof(*_tspec));
 	if (!_tspec)
 		return -1;
 
-	/* store the admitted TSPEC */
-	os_memcpy(_tspec, tspec, sizeof(*_tspec));
-
 	if (dir != WMM_AC_DIR_DOWNLINK) {
 		ret = wpa_drv_add_ts(wpa_s, tsid, addr, up, admitted_time);
 		wpa_printf(MSG_DEBUG,
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index bd0b517..28346ea 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -13,6 +13,7 @@
 #include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
 #include "rsn_supp/wpa.h"
+#include "config.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
 #include "scan.h"
@@ -84,12 +85,11 @@
 	/* TFS IE(s) */
 	if (tfs_req) {
 		wnmtfs_ie_len = wpabuf_len(tfs_req);
-		wnmtfs_ie = os_malloc(wnmtfs_ie_len);
+		wnmtfs_ie = os_memdup(wpabuf_head(tfs_req), wnmtfs_ie_len);
 		if (wnmtfs_ie == NULL) {
 			os_free(wnmsleep_ie);
 			return -1;
 		}
-		os_memcpy(wnmtfs_ie, wpabuf_head(tfs_req), wnmtfs_ie_len);
 	} else {
 		wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
 		if (wnmtfs_ie == NULL) {
@@ -501,10 +501,128 @@
 }
 
 
-static struct wpa_bss *
-compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs)
+static void wnm_clear_acceptable(struct wpa_supplicant *wpa_s)
 {
+	unsigned int i;
 
+	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++)
+		wpa_s->wnm_neighbor_report_elements[i].acceptable = 0;
+}
+
+
+static struct wpa_bss * get_first_acceptable(struct wpa_supplicant *wpa_s)
+{
+	unsigned int i;
+	struct neighbor_report *nei;
+
+	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+		nei = &wpa_s->wnm_neighbor_report_elements[i];
+		if (nei->acceptable)
+			return wpa_bss_get_bssid(wpa_s, nei->bssid);
+	}
+
+	return NULL;
+}
+
+
+#ifdef CONFIG_MBO
+static struct wpa_bss *
+get_mbo_transition_candidate(struct wpa_supplicant *wpa_s,
+			     enum mbo_transition_reject_reason *reason)
+{
+	struct wpa_bss *target = NULL;
+	struct wpa_bss_trans_info params;
+	struct wpa_bss_candidate_info *info = NULL;
+	struct neighbor_report *nei = wpa_s->wnm_neighbor_report_elements;
+	u8 *first_candidate_bssid = NULL, *pos;
+	unsigned int i;
+
+	params.mbo_transition_reason = wpa_s->wnm_mbo_transition_reason;
+	params.n_candidates = 0;
+	params.bssid = os_calloc(wpa_s->wnm_num_neighbor_report, ETH_ALEN);
+	if (!params.bssid)
+		return NULL;
+
+	pos = params.bssid;
+	for (i = 0; i < wpa_s->wnm_num_neighbor_report; nei++, i++) {
+		if (nei->is_first)
+			first_candidate_bssid = nei->bssid;
+		if (!nei->acceptable)
+			continue;
+		os_memcpy(pos, nei->bssid, ETH_ALEN);
+		pos += ETH_ALEN;
+		params.n_candidates++;
+	}
+
+	if (!params.n_candidates)
+		goto end;
+
+	info = wpa_drv_get_bss_trans_status(wpa_s, &params);
+	if (!info) {
+		/* If failed to get candidate BSS transition status from driver,
+		 * get the first acceptable candidate from wpa_supplicant.
+		 */
+		target = wpa_bss_get_bssid(wpa_s, params.bssid);
+		goto end;
+	}
+
+	/* Get the first acceptable candidate from driver */
+	for (i = 0; i < info->num; i++) {
+		if (info->candidates[i].is_accept) {
+			target = wpa_bss_get_bssid(wpa_s,
+						   info->candidates[i].bssid);
+			goto end;
+		}
+	}
+
+	/* If Disassociation Imminent is set and driver rejects all the
+	 * candidate select first acceptable candidate which has
+	 * rssi > disassoc_imminent_rssi_threshold
+	 */
+	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
+		for (i = 0; i < info->num; i++) {
+			target = wpa_bss_get_bssid(wpa_s,
+						   info->candidates[i].bssid);
+			if (target &&
+			    (target->level <
+			     wpa_s->conf->disassoc_imminent_rssi_threshold))
+				continue;
+			goto end;
+		}
+	}
+
+	/* While sending BTM reject use reason code of the first candidate
+	 * received in BTM request frame
+	 */
+	if (reason) {
+		for (i = 0; i < info->num; i++) {
+			if (first_candidate_bssid &&
+			    os_memcmp(first_candidate_bssid,
+				      info->candidates[i].bssid, ETH_ALEN) == 0)
+			{
+				*reason = info->candidates[i].reject_reason;
+				break;
+			}
+		}
+	}
+
+	target = NULL;
+
+end:
+	os_free(params.bssid);
+	if (info) {
+		os_free(info->candidates);
+		os_free(info);
+	}
+	return target;
+}
+#endif /* CONFIG_MBO */
+
+
+static struct wpa_bss *
+compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs,
+			      enum mbo_transition_reject_reason *reason)
+{
 	u8 i;
 	struct wpa_bss *bss = wpa_s->current_bss;
 	struct wpa_bss *target;
@@ -515,6 +633,8 @@
 	wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d",
 		   MAC2STR(wpa_s->bssid), bss->level);
 
+	wnm_clear_acceptable(wpa_s);
+
 	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
 		struct neighbor_report *nei;
 
@@ -591,14 +711,26 @@
 			continue;
 		}
 
+		nei->acceptable = 1;
+	}
+
+#ifdef CONFIG_MBO
+	if (wpa_s->wnm_mbo_trans_reason_present)
+		target = get_mbo_transition_candidate(wpa_s, reason);
+	else
+		target = get_first_acceptable(wpa_s);
+#else /* CONFIG_MBO */
+	target = get_first_acceptable(wpa_s);
+#endif /* CONFIG_MBO */
+
+	if (target) {
 		wpa_printf(MSG_DEBUG,
 			   "WNM: Found an acceptable preferred transition candidate BSS "
 			   MACSTR " (RSSI %d)",
-			   MAC2STR(nei->bssid), target->level);
-		return target;
+			   MAC2STR(target->bssid), target->level);
 	}
 
-	return NULL;
+	return target;
 }
 
 
@@ -651,36 +783,40 @@
 }
 
 
-static int wnm_add_nei_rep(u8 *buf, size_t len, const u8 *bssid, u32 bss_info,
-			   u8 op_class, u8 chan, u8 phy_type, u8 pref)
+static int wnm_add_nei_rep(struct wpabuf **buf, const u8 *bssid,
+			   u32 bss_info, u8 op_class, u8 chan, u8 phy_type,
+			   u8 pref)
 {
-	u8 *pos = buf;
-
-	if (len < 18) {
+	if (wpabuf_len(*buf) + 18 >
+	    IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN) {
 		wpa_printf(MSG_DEBUG,
-			   "WNM: Not enough room for Neighbor Report element");
+			   "WNM: No room in frame for Neighbor Report element");
 		return -1;
 	}
 
-	*pos++ = WLAN_EID_NEIGHBOR_REPORT;
+	if (wpabuf_resize(buf, 18) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Failed to allocate memory for Neighbor Report element");
+		return -1;
+	}
+
+	wpabuf_put_u8(*buf, WLAN_EID_NEIGHBOR_REPORT);
 	/* length: 13 for basic neighbor report + 3 for preference subelement */
-	*pos++ = 16;
-	os_memcpy(pos, bssid, ETH_ALEN);
-	pos += ETH_ALEN;
-	WPA_PUT_LE32(pos, bss_info);
-	pos += 4;
-	*pos++ = op_class;
-	*pos++ = chan;
-	*pos++ = phy_type;
-	*pos++ = WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE;
-	*pos++ = 1;
-	*pos++ = pref;
-	return pos - buf;
+	wpabuf_put_u8(*buf, 16);
+	wpabuf_put_data(*buf, bssid, ETH_ALEN);
+	wpabuf_put_le32(*buf, bss_info);
+	wpabuf_put_u8(*buf, op_class);
+	wpabuf_put_u8(*buf, chan);
+	wpabuf_put_u8(*buf, phy_type);
+	wpabuf_put_u8(*buf, WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE);
+	wpabuf_put_u8(*buf, 1);
+	wpabuf_put_u8(*buf, pref);
+	return 0;
 }
 
 
 static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s,
-			       struct wpa_bss *bss, u8 *buf, size_t len,
+			       struct wpa_bss *bss, struct wpabuf **buf,
 			       u8 pref)
 {
 	const u8 *ie;
@@ -729,20 +865,19 @@
 
 	info = wnm_get_bss_info(wpa_s, bss);
 
-	return wnm_add_nei_rep(buf, len, bss->bssid, info, op_class, chan,
-			       phy_type, pref);
+	return wnm_add_nei_rep(buf, bss->bssid, info, op_class, chan, phy_type,
+			       pref);
 }
 
 
-static int wnm_add_cand_list(struct wpa_supplicant *wpa_s, u8 *buf, size_t len)
+static void wnm_add_cand_list(struct wpa_supplicant *wpa_s, struct wpabuf **buf)
 {
-	u8 *pos = buf;
 	unsigned int i, pref = 255;
 	struct os_reltime now;
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
 
 	if (!ssid)
-		return 0;
+		return;
 
 	/*
 	 * TODO: Define when scan results are no longer valid for the candidate
@@ -750,7 +885,7 @@
 	 */
 	os_get_reltime(&now);
 	if (os_reltime_expired(&now, &wpa_s->last_scan, 10))
-		return 0;
+		return;
 
 	wpa_printf(MSG_DEBUG,
 		   "WNM: Add candidate list to BSS Transition Management Response frame");
@@ -759,92 +894,99 @@
 		int res;
 
 		if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1, 0)) {
-			res = wnm_nei_rep_add_bss(wpa_s, bss, pos, len, pref--);
+			res = wnm_nei_rep_add_bss(wpa_s, bss, buf, pref--);
 			if (res == -2)
 				continue; /* could not build entry for BSS */
 			if (res < 0)
 				break; /* no more room for candidates */
 			if (pref == 1)
 				break;
-
-			pos += res;
-			len -= res;
 		}
 	}
 
-	wpa_hexdump(MSG_DEBUG,
-		    "WNM: BSS Transition Management Response candidate list",
-		    buf, pos - buf);
-
-	return pos - buf;
+	wpa_hexdump_buf(MSG_DEBUG,
+			"WNM: BSS Transition Management Response candidate list",
+			*buf);
 }
 
 
+#define BTM_RESP_MIN_SIZE	5 + ETH_ALEN
+
 static void wnm_send_bss_transition_mgmt_resp(
 	struct wpa_supplicant *wpa_s, u8 dialog_token,
-	enum bss_trans_mgmt_status_code status, u8 delay,
-	const u8 *target_bssid)
+	enum bss_trans_mgmt_status_code status,
+	enum mbo_transition_reject_reason reason,
+	u8 delay, const u8 *target_bssid)
 {
-	u8 buf[2000], *pos;
-	struct ieee80211_mgmt *mgmt;
-	size_t len;
+	struct wpabuf *buf;
 	int res;
 
-	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response "
-		   "to " MACSTR " dialog_token=%u status=%u delay=%d",
-		   MAC2STR(wpa_s->bssid), dialog_token, status, delay);
+	wpa_printf(MSG_DEBUG,
+		   "WNM: Send BSS Transition Management Response to " MACSTR
+		   " dialog_token=%u status=%u reason=%u delay=%d",
+		   MAC2STR(wpa_s->bssid), dialog_token, status, reason, delay);
 	if (!wpa_s->current_bss) {
 		wpa_printf(MSG_DEBUG,
 			   "WNM: Current BSS not known - drop response");
 		return;
 	}
 
-	mgmt = (struct ieee80211_mgmt *) buf;
-	os_memset(&buf, 0, sizeof(buf));
-	os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
-	os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
-	os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
-	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-					   WLAN_FC_STYPE_ACTION);
-	mgmt->u.action.category = WLAN_ACTION_WNM;
-	mgmt->u.action.u.bss_tm_resp.action = WNM_BSS_TRANS_MGMT_RESP;
-	mgmt->u.action.u.bss_tm_resp.dialog_token = dialog_token;
-	mgmt->u.action.u.bss_tm_resp.status_code = status;
-	mgmt->u.action.u.bss_tm_resp.bss_termination_delay = delay;
-	pos = mgmt->u.action.u.bss_tm_resp.variable;
+	buf = wpabuf_alloc(BTM_RESP_MIN_SIZE);
+	if (!buf) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Failed to allocate memory for BTM response");
+		return;
+	}
+
+	wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+	wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_RESP);
+	wpabuf_put_u8(buf, dialog_token);
+	wpabuf_put_u8(buf, status);
+	wpabuf_put_u8(buf, delay);
 	if (target_bssid) {
-		os_memcpy(pos, target_bssid, ETH_ALEN);
-		pos += ETH_ALEN;
+		wpabuf_put_data(buf, target_bssid, ETH_ALEN);
 	} else if (status == WNM_BSS_TM_ACCEPT) {
 		/*
 		 * P802.11-REVmc clarifies that the Target BSSID field is always
 		 * present when status code is zero, so use a fake value here if
 		 * no BSSID is yet known.
 		 */
-		os_memset(pos, 0, ETH_ALEN);
-		pos += ETH_ALEN;
+		wpabuf_put_data(buf, "\0\0\0\0\0\0", ETH_ALEN);
 	}
 
 	if (status == WNM_BSS_TM_ACCEPT)
-		pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos);
+		wnm_add_cand_list(wpa_s, &buf);
 
 #ifdef CONFIG_MBO
-	if (status != WNM_BSS_TM_ACCEPT) {
-		pos += wpas_mbo_ie_bss_trans_reject(
-			wpa_s, pos, buf + sizeof(buf) - pos,
-			MBO_TRANSITION_REJECT_REASON_UNSPECIFIED);
+	if (status != WNM_BSS_TM_ACCEPT &&
+	    wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) {
+		u8 mbo[10];
+		size_t ret;
+
+		ret = wpas_mbo_ie_bss_trans_reject(wpa_s, mbo, sizeof(mbo),
+						   reason);
+		if (ret) {
+			if (wpabuf_resize(&buf, ret) < 0) {
+				wpabuf_free(buf);
+				wpa_printf(MSG_DEBUG,
+					   "WNM: Failed to allocate memory for MBO IE");
+				return;
+			}
+
+			wpabuf_put_data(buf, mbo, ret);
+		}
 	}
 #endif /* CONFIG_MBO */
 
-	len = pos - (u8 *) &mgmt->u.action.category;
-
 	res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
 				  wpa_s->own_addr, wpa_s->bssid,
-				  &mgmt->u.action.category, len, 0);
+				  wpabuf_head_u8(buf), wpabuf_len(buf), 0);
 	if (res < 0) {
 		wpa_printf(MSG_DEBUG,
 			   "WNM: Failed to send BSS Transition Management Response");
 	}
+
+	wpabuf_free(buf);
 }
 
 
@@ -863,10 +1005,10 @@
 		wpa_s->wnm_reply = 0;
 		wpa_printf(MSG_DEBUG,
 			   "WNM: Sending successful BSS Transition Management Response");
-		wnm_send_bss_transition_mgmt_resp(wpa_s,
-						  wpa_s->wnm_dialog_token,
-						  WNM_BSS_TM_ACCEPT,
-						  0, bss->bssid);
+		wnm_send_bss_transition_mgmt_resp(
+			wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_ACCEPT,
+			MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
+			bss->bssid);
 	}
 
 	if (bss == wpa_s->current_bss) {
@@ -888,6 +1030,8 @@
 	struct wpa_bss *bss;
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
 	enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED;
+	enum mbo_transition_reject_reason reason =
+		MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
 
 	if (!wpa_s->wnm_neighbor_report_elements)
 		return 0;
@@ -909,7 +1053,7 @@
 	}
 
 	/* Compare the Neighbor Report and scan results */
-	bss = compare_scan_neighbor_results(wpa_s, 0);
+	bss = compare_scan_neighbor_results(wpa_s, 0, &reason);
 	if (!bss) {
 		wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found");
 		status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
@@ -930,7 +1074,7 @@
 		wpa_s->wnm_reply = 0;
 		wnm_send_bss_transition_mgmt_resp(wpa_s,
 						  wpa_s->wnm_dialog_token,
-						  status, 0, NULL);
+						  status, reason, 0, NULL);
 	}
 	wnm_deallocate_memory(wpa_s);
 
@@ -1118,7 +1262,7 @@
 		return 0;
 	}
 
-	bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE);
+	bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE, NULL);
 	if (!bss) {
 		wpa_dbg(wpa_s, MSG_DEBUG,
 			"WNM: Comparison of scan results against transition candidates did not find matches");
@@ -1144,6 +1288,11 @@
 	if (end - pos < 5)
 		return;
 
+#ifdef CONFIG_MBO
+	wpa_s->wnm_mbo_trans_reason_present = 0;
+	wpa_s->wnm_mbo_transition_reason = 0;
+#endif /* CONFIG_MBO */
+
 	if (wpa_s->current_bss)
 		beacon_int = wpa_s->current_bss->beacon_int;
 	else
@@ -1166,10 +1315,10 @@
 		wpa_printf(MSG_INFO,
 			   "WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d",
 			   wpa_s->reject_btm_req_reason);
-		wnm_send_bss_transition_mgmt_resp(wpa_s,
-						  wpa_s->wnm_dialog_token,
-						  wpa_s->reject_btm_req_reason,
-						  0, NULL);
+		wnm_send_bss_transition_mgmt_resp(
+			wpa_s, wpa_s->wnm_dialog_token,
+			wpa_s->reject_btm_req_reason,
+			MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
 		return;
 	}
 #endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */
@@ -1248,6 +1397,15 @@
 					wpa_s->wnm_num_neighbor_report];
 				wnm_parse_neighbor_report(wpa_s, pos, len, rep);
 				wpa_s->wnm_num_neighbor_report++;
+#ifdef CONFIG_MBO
+				if (wpa_s->wnm_mbo_trans_reason_present &&
+				    wpa_s->wnm_num_neighbor_report == 1) {
+					rep->is_first = 1;
+					wpa_printf(MSG_DEBUG,
+						   "WNM: First transition candidate is "
+						   MACSTR, MAC2STR(rep->bssid));
+				}
+#endif /* CONFIG_MBO */
 			}
 
 			pos += len;
@@ -1259,7 +1417,8 @@
 			wnm_send_bss_transition_mgmt_resp(
 				wpa_s, wpa_s->wnm_dialog_token,
 				WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
-				0, NULL);
+				MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
+				NULL);
 			return;
 		}
 
@@ -1322,19 +1481,21 @@
 			wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates");
 			status = WNM_BSS_TM_REJECT_UNSPECIFIED;
 		}
-		wnm_send_bss_transition_mgmt_resp(wpa_s,
-						  wpa_s->wnm_dialog_token,
-						  status, 0, NULL);
+		wnm_send_bss_transition_mgmt_resp(
+			wpa_s, wpa_s->wnm_dialog_token, status,
+			MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
 	}
 }
 
 
+#define BTM_QUERY_MIN_SIZE	4
+
 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
-				       u8 query_reason, int cand_list)
+				       u8 query_reason,
+				       const char *btm_candidates,
+				       int cand_list)
 {
-	u8 buf[2000], *pos;
-	struct ieee80211_mgmt *mgmt;
-	size_t len;
+	struct wpabuf *buf;
 	int ret;
 
 	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to "
@@ -1342,28 +1503,43 @@
 		   MAC2STR(wpa_s->bssid), query_reason,
 		   cand_list ? " candidate list" : "");
 
-	mgmt = (struct ieee80211_mgmt *) buf;
-	os_memset(&buf, 0, sizeof(buf));
-	os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
-	os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
-	os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
-	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-					   WLAN_FC_STYPE_ACTION);
-	mgmt->u.action.category = WLAN_ACTION_WNM;
-	mgmt->u.action.u.bss_tm_query.action = WNM_BSS_TRANS_MGMT_QUERY;
-	mgmt->u.action.u.bss_tm_query.dialog_token = 1;
-	mgmt->u.action.u.bss_tm_query.query_reason = query_reason;
-	pos = mgmt->u.action.u.bss_tm_query.variable;
+	buf = wpabuf_alloc(BTM_QUERY_MIN_SIZE);
+	if (!buf)
+		return -1;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+	wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_QUERY);
+	wpabuf_put_u8(buf, 1);
+	wpabuf_put_u8(buf, query_reason);
 
 	if (cand_list)
-		pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos);
+		wnm_add_cand_list(wpa_s, &buf);
 
-	len = pos - (u8 *) &mgmt->u.action.category;
+	if (btm_candidates) {
+		const size_t max_len = 1000;
+
+		ret = wpabuf_resize(&buf, max_len);
+		if (ret < 0) {
+			wpabuf_free(buf);
+			return ret;
+		}
+
+		ret = ieee802_11_parse_candidate_list(btm_candidates,
+						      wpabuf_put(buf, 0),
+						      max_len);
+		if (ret < 0) {
+			wpabuf_free(buf);
+			return ret;
+		}
+
+		wpabuf_put(buf, ret);
+	}
 
 	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
 				  wpa_s->own_addr, wpa_s->bssid,
-				  &mgmt->u.action.category, len, 0);
+				  wpabuf_head_u8(buf), wpabuf_len(buf), 0);
 
+	wpabuf_free(buf);
 	return ret;
 }
 
diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h
index 81d8153..02cd1cd 100644
--- a/wpa_supplicant/wnm_sta.h
+++ b/wpa_supplicant/wnm_sta.h
@@ -43,6 +43,10 @@
 	unsigned int rm_capab_present:1;
 	unsigned int bearing_present:1;
 	unsigned int bss_term_present:1;
+	unsigned int acceptable:1;
+#ifdef CONFIG_MBO
+	unsigned int is_first:1;
+#endif /* CONFIG_MBO */
 	struct measurement_pilot *meas_pilot;
 	struct multiple_bssid *mul_bssid;
 	int freq;
@@ -56,7 +60,10 @@
 			      const struct ieee80211_mgmt *mgmt, size_t len);
 
 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
-				       u8 query_reason, int cand_list);
+				       u8 query_reason,
+				       const char *btm_candidates,
+				       int cand_list);
+
 void wnm_deallocate_memory(struct wpa_supplicant *wpa_s);
 
 
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 964311c..6b345af 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -479,6 +479,8 @@
 		"hs20",
 #endif /* CONFIG_HS20 */
 		"interworking", "hessid", "access_network_type", "pbc_in_m1",
+		"go_interworking", "go_access_network_type", "go_internet",
+		"go_venue_group", "go_venue_type",
 		"autoscan", "wps_nfc_dev_pw_id", "wps_nfc_dh_pubkey",
 		"wps_nfc_dh_privkey", "wps_nfc_dev_pw", "ext_password_backend",
 		"p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
@@ -574,10 +576,13 @@
 		"hs20",
 #endif /* CONFIG_HS20 */
 		"interworking", "access_network_type", "pbc_in_m1", "autoscan",
+		"go_interworking", "go_access_network_type", "go_internet",
+		"go_venue_group", "go_venue_type",
 		"wps_nfc_dev_pw_id", "ext_password_backend",
 		"p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
 		"dtim_period", "beacon_int", "ignore_old_scan_res",
 		"scan_cur_freq", "sched_scan_interval",
+		"sched_scan_start_delay",
 		"tdls_external_control", "osu_dir", "wowlan_triggers",
 		"p2p_search_delay", "mac_addr", "rand_addr_lifetime",
 		"preassoc_mac_addr", "key_mgmt_offload", "passive_scan",
@@ -677,13 +682,6 @@
 }
 
 
-static int wpa_cli_cmd_stkstart(struct wpa_ctrl *ctrl, int argc,
-				char *argv[])
-{
-	return wpa_cli_cmd(ctrl, "STKSTART", 1, argc, argv);
-}
-
-
 static int wpa_cli_cmd_ft_ds(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	return wpa_cli_cmd(ctrl, "FT_DS", 1, argc, argv);
@@ -1370,7 +1368,8 @@
 	"ssid", "scan_ssid", "bssid", "bssid_blacklist",
 	"bssid_whitelist", "psk", "proto", "key_mgmt",
 	"bg_scan_period", "pairwise", "group", "auth_alg", "scan_freq",
-	"freq_list", "max_oper_chwidth",
+	"freq_list", "max_oper_chwidth", "ht40", "vht", "vht_center_freq1",
+	"vht_center_freq2", "ht",
 #ifdef IEEE8021X_EAPOL
 	"eap", "identity", "anonymous_identity", "password", "ca_cert",
 	"ca_path", "client_cert", "private_key", "private_key_passwd",
@@ -1390,7 +1389,7 @@
 	"eap_workaround", "pac_file", "fragment_size", "ocsp",
 #endif /* IEEE8021X_EAPOL */
 #ifdef CONFIG_MESH
-	"mode", "no_auto_peer",
+	"mode", "no_auto_peer", "mesh_rssi_threshold",
 #else /* CONFIG_MESH */
 	"mode",
 #endif /* CONFIG_MESH */
@@ -1398,7 +1397,7 @@
 #ifdef CONFIG_IEEE80211W
 	"ieee80211w",
 #endif /* CONFIG_IEEE80211W */
-	"peerkey", "mixed_cell", "frequency", "fixed_freq",
+	"mixed_cell", "frequency", "fixed_freq",
 #ifdef CONFIG_MESH
 	"mesh_basic_rates", "dot11MeshMaxRetries",
 	"dot11MeshRetryTimeout", "dot11MeshConfirmTimeout",
@@ -1821,7 +1820,8 @@
 	}
 
 	buf[len] = '\0';
-	if (os_memcmp(buf, "FAIL", 4) == 0)
+	if (os_memcmp(buf, "FAIL", 4) == 0 ||
+	    os_memcmp(buf, "UNKNOWN COMMAND", 15) == 0)
 		return -1;
 	if (print)
 		printf("%s", buf);
@@ -2831,6 +2831,94 @@
 }
 
 
+#ifdef CONFIG_DPP
+
+static int wpa_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_QR_CODE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_bootstrap_gen(struct wpa_ctrl *ctrl, int argc,
+					 char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_BOOTSTRAP_GEN", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_bootstrap_remove(struct wpa_ctrl *ctrl, int argc,
+					    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_BOOTSTRAP_REMOVE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_bootstrap_get_uri(struct wpa_ctrl *ctrl, int argc,
+					     char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_BOOTSTRAP_GET_URI", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_bootstrap_info(struct wpa_ctrl *ctrl, int argc,
+					  char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_BOOTSTRAP_INFO", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_auth_init(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_AUTH_INIT", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_listen(struct wpa_ctrl *ctrl, int argc,
+				  char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_LISTEN", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_stop_listen(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "DPP_STOP_LISTEN");
+}
+
+
+static int wpa_cli_cmd_dpp_configurator_add(struct wpa_ctrl *ctrl, int argc,
+					    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_CONFIGURATOR_ADD", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_configurator_remove(struct wpa_ctrl *ctrl, int argc,
+					       char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_CONFIGURATOR_REMOVE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_pkex_add(struct wpa_ctrl *ctrl, int argc,
+				    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_PKEX_ADD", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_pkex_remove(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_PKEX_REMOVE", 1, argc, argv);
+}
+
+#endif /* CONFIG_DPP */
+
+
 enum wpa_cli_cmd_flags {
 	cli_cmd_flag_none		= 0x00,
 	cli_cmd_flag_sensitive		= 0x01
@@ -3072,9 +3160,6 @@
 	{ "bss_flush", wpa_cli_cmd_bss_flush, NULL,
 	  cli_cmd_flag_none,
 	  "<value> = set BSS flush age (0 by default)" },
-	{ "stkstart", wpa_cli_cmd_stkstart, NULL,
-	  cli_cmd_flag_none,
-	  "<addr> = request STK negotiation with <addr>" },
 	{ "ft_ds", wpa_cli_cmd_ft_ds, wpa_cli_complete_bss,
 	  cli_cmd_flag_none,
 	  "<addr> = request over-the-DS FT with <addr>" },
@@ -3409,7 +3494,9 @@
 	{ "wnm_sleep", wpa_cli_cmd_wnm_sleep, NULL, cli_cmd_flag_none,
 	  "<enter/exit> [interval=#] = enter/exit WNM-Sleep mode" },
 	{ "wnm_bss_query", wpa_cli_cmd_wnm_bss_query, NULL, cli_cmd_flag_none,
-	  "<query reason> [list] = Send BSS Transition Management Query" },
+	  "<query reason> [list]"
+	  " [neighbor=<BSSID>,<BSSID information>,<operating class>,<channel number>,<PHY type>[,<hexdump of optional subelements>]"
+	  " = Send BSS Transition Management Query" },
 #endif /* CONFIG_WNM */
 	{ "raw", wpa_cli_cmd_raw, NULL, cli_cmd_flag_sensitive,
 	  "<params..> = Sent unprocessed command" },
@@ -3444,6 +3531,41 @@
 	{ "p2p_lo_stop", wpa_cli_cmd_p2p_lo_stop, NULL,
 	  cli_cmd_flag_none,
 	  "= stop P2P listen offload" },
+#ifdef CONFIG_DPP
+	{ "dpp_qr_code", wpa_cli_cmd_dpp_qr_code, NULL, cli_cmd_flag_none,
+	  "report a scanned DPP URI from a QR Code" },
+	{ "dpp_bootstrap_gen", wpa_cli_cmd_dpp_bootstrap_gen, NULL,
+	  cli_cmd_flag_sensitive,
+	  "type=<qrcode> [chan=..] [mac=..] [info=..] [curve=..] [key=..] = generate DPP bootstrap information" },
+	{ "dpp_bootstrap_remove", wpa_cli_cmd_dpp_bootstrap_remove, NULL,
+	  cli_cmd_flag_none,
+	  "*|<id> = remove DPP bootstrap information" },
+	{ "dpp_bootstrap_get_uri", wpa_cli_cmd_dpp_bootstrap_get_uri, NULL,
+	  cli_cmd_flag_none,
+	  "<id> = get DPP bootstrap URI" },
+	{ "dpp_bootstrap_info", wpa_cli_cmd_dpp_bootstrap_info, NULL,
+	  cli_cmd_flag_none,
+	  "<id> = show DPP bootstrap information" },
+	{ "dpp_auth_init", wpa_cli_cmd_dpp_auth_init, NULL, cli_cmd_flag_none,
+	  "peer=<id> [own=<id>] = initiate DPP bootstrapping" },
+	{ "dpp_listen", wpa_cli_cmd_dpp_listen, NULL, cli_cmd_flag_none,
+	  "<freq in MHz> = start DPP listen" },
+	{ "dpp_stop_listen", wpa_cli_cmd_dpp_stop_listen, NULL,
+	  cli_cmd_flag_none,
+	  "= stop DPP listen" },
+	{ "dpp_configurator_add", wpa_cli_cmd_dpp_configurator_add, NULL,
+	  cli_cmd_flag_sensitive,
+	  "[curve=..] [key=..] = add DPP configurator" },
+	{ "dpp_configurator_remove", wpa_cli_cmd_dpp_configurator_remove, NULL,
+	  cli_cmd_flag_none,
+	  "*|<id> = remove DPP configurator" },
+	{ "dpp_pkex_add", wpa_cli_cmd_dpp_pkex_add, NULL,
+	  cli_cmd_flag_sensitive,
+	  "add PKEX code" },
+	{ "dpp_pkex_remove", wpa_cli_cmd_dpp_pkex_remove, NULL,
+	  cli_cmd_flag_none,
+	  "*|<id> = remove DPP pkex information" },
+#endif /* CONFIG_DPP */
 	{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
 };
 
@@ -3762,6 +3884,10 @@
 		wpa_cli_exec(action_file, ifname, pos);
 	} else if (str_starts(pos, WPS_EVENT_SUCCESS)) {
 		wpa_cli_exec(action_file, ifname, pos);
+	} else if (str_starts(pos, WPS_EVENT_ACTIVE)) {
+		wpa_cli_exec(action_file, ifname, pos);
+	} else if (str_starts(pos, WPS_EVENT_TIMEOUT)) {
+		wpa_cli_exec(action_file, ifname, pos);
 	} else if (str_starts(pos, WPS_EVENT_FAIL)) {
 		wpa_cli_exec(action_file, ifname, pos);
 	} else if (str_starts(pos, AP_STA_CONNECTED)) {
diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
index 46cb95e..b3ad45e 100644
--- a/wpa_supplicant/wpa_priv.c
+++ b/wpa_supplicant/wpa_priv.c
@@ -1081,12 +1081,6 @@
 				    &data->pmkid_candidate,
 				    sizeof(struct pmkid_candidate));
 		break;
-	case EVENT_STKSTART:
-		if (data == NULL)
-			return;
-		wpa_priv_send_event(iface, PRIVSEP_EVENT_STKSTART,
-				    &data->stkstart.peer, ETH_ALEN);
-		break;
 	case EVENT_FT_RESPONSE:
 		wpa_priv_send_ft_response(iface, data);
 		break;
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 9aaedb3..f8e5bf7 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -38,6 +38,7 @@
 #include "common/wpa_ctrl.h"
 #include "common/ieee802_11_defs.h"
 #include "common/hw_features_common.h"
+#include "common/gas_server.h"
 #include "p2p/p2p.h"
 #include "fst/fst.h"
 #include "blacklist.h"
@@ -59,6 +60,11 @@
 #include "wnm_sta.h"
 #include "wpas_kay.h"
 #include "mesh.h"
+#include "dpp_supplicant.h"
+#ifdef CONFIG_MESH
+#include "ap/ap_config.h"
+#include "ap/hostapd.h"
+#endif /* CONFIG_MESH */
 
 const char *const wpa_supplicant_version =
 "wpa_supplicant v" VERSION_STR "\n"
@@ -112,6 +118,13 @@
 "\n";
 #endif /* CONFIG_NO_STDOUT_DEBUG */
 
+
+static void wpa_bss_tmp_disallow_timeout(void *eloop_ctx, void *timeout_ctx);
+#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL)
+static void wpas_update_fils_connect_params(struct wpa_supplicant *wpa_s);
+#endif /* CONFIG_FILS && IEEE8021X_EAPOL */
+
+
 /* Configure default/group WEP keys for static WEP */
 int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
 {
@@ -415,6 +428,7 @@
 
 	dl_list_for_each_safe(bss, prev, &wpa_s->bss_tmp_disallowed,
 			      struct wpa_bss_tmp_disallowed, list) {
+		eloop_cancel_timeout(wpa_bss_tmp_disallow_timeout, wpa_s, bss);
 		dl_list_del(&bss->list);
 		os_free(bss);
 	}
@@ -455,6 +469,8 @@
 	wpa_s->l2_test = NULL;
 	os_free(wpa_s->get_pref_freq_list_override);
 	wpa_s->get_pref_freq_list_override = NULL;
+	wpabuf_free(wpa_s->last_assoc_req_wpa_ie);
+	wpa_s->last_assoc_req_wpa_ie = NULL;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	if (wpa_s->conf != NULL) {
@@ -527,6 +543,8 @@
 
 	os_free(wpa_s->manual_scan_freqs);
 	wpa_s->manual_scan_freqs = NULL;
+	os_free(wpa_s->select_network_scan_freqs);
+	wpa_s->select_network_scan_freqs = NULL;
 
 	os_free(wpa_s->manual_sched_scan_freqs);
 	wpa_s->manual_sched_scan_freqs = NULL;
@@ -545,6 +563,8 @@
 		radio_remove_works(wpa_s, "gas-query", 0);
 	gas_query_deinit(wpa_s->gas);
 	wpa_s->gas = NULL;
+	gas_server_deinit(wpa_s->gas_server);
+	wpa_s->gas_server = NULL;
 
 	free_hw_features(wpa_s);
 
@@ -623,6 +643,10 @@
 
 	wpabuf_free(wpa_s->ric_ies);
 	wpa_s->ric_ies = NULL;
+
+#ifdef CONFIG_DPP
+	wpas_dpp_deinit(wpa_s);
+#endif /* CONFIG_DPP */
 }
 
 
@@ -836,12 +860,24 @@
 
 	if (state == WPA_COMPLETED && wpa_s->new_connection) {
 		struct wpa_ssid *ssid = wpa_s->current_ssid;
+		int fils_hlp_sent = 0;
+
+#ifdef CONFIG_SME
+		if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+		    wpa_auth_alg_fils(wpa_s->sme.auth_alg))
+			fils_hlp_sent = 1;
+#endif /* CONFIG_SME */
+		if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+		    wpa_auth_alg_fils(wpa_s->auth_alg))
+			fils_hlp_sent = 1;
+
 #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
 		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to "
-			MACSTR " completed [id=%d id_str=%s]",
+			MACSTR " completed [id=%d id_str=%s%s]",
 			MAC2STR(wpa_s->bssid),
 			ssid ? ssid->id : -1,
-			ssid && ssid->id_str ? ssid->id_str : "");
+			ssid && ssid->id_str ? ssid->id_str : "",
+			fils_hlp_sent ? " FILS_HLP_SENT" : "");
 #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 		wpas_clear_temp_disabled(wpa_s, ssid, 1);
 		wpa_blacklist_clear(wpa_s);
@@ -856,6 +892,11 @@
 		wpas_p2p_completed(wpa_s);
 
 		sme_sched_obss_scan(wpa_s, 1);
+
+#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL)
+		if (!fils_hlp_sent && ssid && ssid->eap.erp)
+			wpas_update_fils_connect_params(wpa_s);
+#endif /* CONFIG_FILS && IEEE8021X_EAPOL */
 	} else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING ||
 		   state == WPA_ASSOCIATED) {
 		wpa_s->new_connection = 1;
@@ -996,7 +1037,9 @@
 	 * TODO: should notify EAPOL SM about changes in opensc_engine_path,
 	 * pkcs11_engine_path, pkcs11_module_path, openssl_ciphers.
 	 */
-	if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) {
+	if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_OWE ||
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_DPP) {
 		/*
 		 * Clear forced success to clear EAP state for next
 		 * authentication.
@@ -1200,9 +1243,24 @@
 			ie.pairwise_cipher = ssid->pairwise_cipher;
 			ie.key_mgmt = ssid->key_mgmt;
 #ifdef CONFIG_IEEE80211W
-			ie.mgmt_group_cipher =
-				ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION ?
-				WPA_CIPHER_AES_128_CMAC : 0;
+			ie.mgmt_group_cipher = 0;
+			if (ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+				if (ssid->group_mgmt_cipher &
+				    WPA_CIPHER_BIP_GMAC_256)
+					ie.mgmt_group_cipher =
+						WPA_CIPHER_BIP_GMAC_256;
+				else if (ssid->group_mgmt_cipher &
+					 WPA_CIPHER_BIP_CMAC_256)
+					ie.mgmt_group_cipher =
+						WPA_CIPHER_BIP_CMAC_256;
+				else if (ssid->group_mgmt_cipher &
+					 WPA_CIPHER_BIP_GMAC_128)
+					ie.mgmt_group_cipher =
+						WPA_CIPHER_BIP_GMAC_128;
+				else
+					ie.mgmt_group_cipher =
+						WPA_CIPHER_AES_128_CMAC;
+			}
 #endif /* CONFIG_IEEE80211W */
 			wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Set cipher suites "
 				"based on configuration");
@@ -1332,6 +1390,16 @@
 		wpa_s->key_mgmt = WPA_KEY_MGMT_OSEN;
 		wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using KEY_MGMT OSEN");
 #endif /* CONFIG_HS20 */
+#ifdef CONFIG_OWE
+	} else if (sel & WPA_KEY_MGMT_OWE) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_OWE;
+		wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT OWE");
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+	} else if (sel & WPA_KEY_MGMT_DPP) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_DPP;
+		wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT DPP");
+#endif /* CONFIG_DPP */
 	} else {
 		wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select "
 			"authenticated key management type");
@@ -1345,6 +1413,8 @@
 
 #ifdef CONFIG_IEEE80211W
 	sel = ie.mgmt_group_cipher;
+	if (ssid->group_mgmt_cipher)
+		sel &= ssid->group_mgmt_cipher;
 	if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION ||
 	    !(ie.capabilities & WPA_CAPABILITY_MFPC))
 		sel = 0;
@@ -1387,6 +1457,10 @@
 				       NULL);
 			psk_set = 1;
 		}
+
+		if (wpa_key_mgmt_sae(ssid->key_mgmt) && ssid->sae_password)
+			psk_set = 1;
+
 #ifndef CONFIG_NO_PBKDF2
 		if (bss && ssid->bssid_set && ssid->ssid_len == 0 &&
 		    ssid->passphrase) {
@@ -1469,6 +1543,12 @@
 				"No PSK available for association");
 			return -1;
 		}
+#ifdef CONFIG_OWE
+	} else if (wpa_s->key_mgmt == WPA_KEY_MGMT_OWE) {
+		/* OWE Diffie-Hellman exchange in (Re)Association
+		 * Request/Response frames set the PMK, so do not override it
+		 * here. */
+#endif /* CONFIG_OWE */
 	} else
 		wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
 
@@ -1726,6 +1806,9 @@
 	wmm_ac_clear_saved_tspecs(wpa_s);
 	wpa_s->reassoc_same_bss = 0;
 	wpa_s->reassoc_same_ess = 0;
+#ifdef CONFIG_TESTING_OPTIONS
+	wpa_s->testing_resend_assoc = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
 
 	if (wpa_s->last_ssid == ssid) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS");
@@ -1807,6 +1890,7 @@
 		wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_STARTED "ssid=\"%s\" id=%d",
 			wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
 			ssid->id);
+		wpas_notify_mesh_group_started(wpa_s, ssid);
 #else /* CONFIG_MESH */
 		wpa_msg(wpa_s, MSG_ERROR,
 			"mesh mode support not included in the build");
@@ -1814,6 +1898,20 @@
 		return;
 	}
 
+	/*
+	 * Set WPA state machine configuration to match the selected network now
+	 * so that the information is available before wpas_start_assoc_cb()
+	 * gets called. This is needed at least for RSN pre-authentication where
+	 * candidate APs are added to a list based on scan result processing
+	 * before completion of the first association.
+	 */
+	wpa_supplicant_rsn_supp_set_config(wpa_s, ssid);
+
+#ifdef CONFIG_DPP
+	if (wpas_dpp_check_connect(wpa_s, ssid, bss) != 0)
+		return;
+#endif /* CONFIG_DPP */
+
 #ifdef CONFIG_TDLS
 	if (bss)
 		wpa_tdls_ap_ies(wpa_s->wpa, (const u8 *) (bss + 1),
@@ -2163,17 +2261,419 @@
 }
 
 
+#ifdef CONFIG_FILS
+static size_t wpas_add_fils_hlp_req(struct wpa_supplicant *wpa_s, u8 *ie_buf,
+				    size_t ie_buf_len)
+{
+	struct fils_hlp_req *req;
+	size_t rem_len, hdr_len, hlp_len, len, ie_len = 0;
+	const u8 *pos;
+	u8 *buf = ie_buf;
+
+	dl_list_for_each(req, &wpa_s->fils_hlp_req, struct fils_hlp_req,
+			 list) {
+		rem_len = ie_buf_len - ie_len;
+		pos = wpabuf_head(req->pkt);
+		hdr_len = 1 + 2 * ETH_ALEN + 6;
+		hlp_len = wpabuf_len(req->pkt);
+
+		if (rem_len < 2 + hdr_len + hlp_len) {
+			wpa_printf(MSG_ERROR,
+				   "FILS: Cannot fit HLP - rem_len=%lu to_fill=%lu",
+				   (unsigned long) rem_len,
+				   (unsigned long) (2 + hdr_len + hlp_len));
+			break;
+		}
+
+		len = (hdr_len + hlp_len) > 255 ? 255 : hdr_len + hlp_len;
+		/* Element ID */
+		*buf++ = WLAN_EID_EXTENSION;
+		/* Length */
+		*buf++ = len;
+		/* Element ID Extension */
+		*buf++ = WLAN_EID_EXT_FILS_HLP_CONTAINER;
+		/* Destination MAC address */
+		os_memcpy(buf, req->dst, ETH_ALEN);
+		buf += ETH_ALEN;
+		/* Source MAC address */
+		os_memcpy(buf, wpa_s->own_addr, ETH_ALEN);
+		buf += ETH_ALEN;
+		/* LLC/SNAP Header */
+		os_memcpy(buf, "\xaa\xaa\x03\x00\x00\x00", 6);
+		buf += 6;
+		/* HLP Packet */
+		os_memcpy(buf, pos, len - hdr_len);
+		buf += len - hdr_len;
+		pos += len - hdr_len;
+
+		hlp_len -= len - hdr_len;
+		ie_len += 2 + len;
+		rem_len -= 2 + len;
+
+		while (hlp_len) {
+			len = (hlp_len > 255) ? 255 : hlp_len;
+			if (rem_len < 2 + len)
+				break;
+			*buf++ = WLAN_EID_FRAGMENT;
+			*buf++ = len;
+			os_memcpy(buf, pos, len);
+			buf += len;
+			pos += len;
+
+			hlp_len -= len;
+			ie_len += 2 + len;
+			rem_len -= 2 + len;
+		}
+	}
+
+	return ie_len;
+}
+#endif /* CONFIG_FILS */
+
+
+static u8 * wpas_populate_assoc_ies(
+	struct wpa_supplicant *wpa_s,
+	struct wpa_bss *bss, struct wpa_ssid *ssid,
+	struct wpa_driver_associate_params *params,
+	enum wpa_drv_update_connect_params_mask *mask)
+{
+	u8 *wpa_ie;
+	size_t max_wpa_ie_len = 500;
+	size_t wpa_ie_len;
+	int algs = WPA_AUTH_ALG_OPEN;
+#ifdef CONFIG_FILS
+	const u8 *realm, *username, *rrk;
+	size_t realm_len, username_len, rrk_len;
+	u16 next_seq_num;
+	struct fils_hlp_req *req;
+
+	dl_list_for_each(req, &wpa_s->fils_hlp_req, struct fils_hlp_req,
+			 list) {
+		max_wpa_ie_len += 3 + 2 * ETH_ALEN + 6 + wpabuf_len(req->pkt) +
+				  2 + 2 * wpabuf_len(req->pkt) / 255;
+	}
+#endif /* CONFIG_FILS */
+
+	wpa_ie = os_malloc(max_wpa_ie_len);
+	if (!wpa_ie) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to allocate connect IE buffer for %lu bytes",
+			   (unsigned long) max_wpa_ie_len);
+		return NULL;
+	}
+
+	if (bss && (wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) ||
+		    wpa_bss_get_ie(bss, WLAN_EID_RSN)) &&
+	    wpa_key_mgmt_wpa(ssid->key_mgmt)) {
+		int try_opportunistic;
+		const u8 *cache_id = NULL;
+
+		try_opportunistic = (ssid->proactive_key_caching < 0 ?
+				     wpa_s->conf->okc :
+				     ssid->proactive_key_caching) &&
+			(ssid->proto & WPA_PROTO_RSN);
+#ifdef CONFIG_FILS
+		if (wpa_key_mgmt_fils(ssid->key_mgmt))
+			cache_id = wpa_bss_get_fils_cache_id(bss);
+#endif /* CONFIG_FILS */
+		if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
+					    ssid, try_opportunistic,
+					    cache_id) == 0)
+			eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
+		wpa_ie_len = max_wpa_ie_len;
+		if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
+					      wpa_ie, &wpa_ie_len)) {
+			wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
+				"key management and encryption suites");
+			os_free(wpa_ie);
+			return NULL;
+		}
+	} else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && bss &&
+		   wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) {
+		/*
+		 * Both WPA and non-WPA IEEE 802.1X enabled in configuration -
+		 * use non-WPA since the scan results did not indicate that the
+		 * AP is using WPA or WPA2.
+		 */
+		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+		wpa_ie_len = 0;
+		wpa_s->wpa_proto = 0;
+	} else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
+		wpa_ie_len = max_wpa_ie_len;
+		if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
+					      wpa_ie, &wpa_ie_len)) {
+			wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
+				"key management and encryption suites (no "
+				"scan results)");
+			os_free(wpa_ie);
+			return NULL;
+		}
+#ifdef CONFIG_WPS
+	} else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
+		struct wpabuf *wps_ie;
+		wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid));
+		if (wps_ie && wpabuf_len(wps_ie) <= max_wpa_ie_len) {
+			wpa_ie_len = wpabuf_len(wps_ie);
+			os_memcpy(wpa_ie, wpabuf_head(wps_ie), wpa_ie_len);
+		} else
+			wpa_ie_len = 0;
+		wpabuf_free(wps_ie);
+		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+		if (!bss || (bss->caps & IEEE80211_CAP_PRIVACY))
+			params->wps = WPS_MODE_PRIVACY;
+		else
+			params->wps = WPS_MODE_OPEN;
+		wpa_s->wpa_proto = 0;
+#endif /* CONFIG_WPS */
+	} else {
+		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+		wpa_ie_len = 0;
+		wpa_s->wpa_proto = 0;
+	}
+
+#ifdef IEEE8021X_EAPOL
+	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+		if (ssid->leap) {
+			if (ssid->non_leap == 0)
+				algs = WPA_AUTH_ALG_LEAP;
+			else
+				algs |= WPA_AUTH_ALG_LEAP;
+		}
+	}
+
+#ifdef CONFIG_FILS
+	/* Clear FILS association */
+	wpa_sm_set_reset_fils_completed(wpa_s->wpa, 0);
+
+	if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD) &&
+	    ssid->eap.erp && wpa_key_mgmt_fils(wpa_s->key_mgmt) &&
+	    eapol_sm_get_erp_info(wpa_s->eapol, &ssid->eap, &username,
+				  &username_len, &realm, &realm_len,
+				  &next_seq_num, &rrk, &rrk_len) == 0) {
+		algs = WPA_AUTH_ALG_FILS;
+		params->fils_erp_username = username;
+		params->fils_erp_username_len = username_len;
+		params->fils_erp_realm = realm;
+		params->fils_erp_realm_len = realm_len;
+		params->fils_erp_next_seq_num = next_seq_num;
+		params->fils_erp_rrk = rrk;
+		params->fils_erp_rrk_len = rrk_len;
+
+		if (mask)
+			*mask |= WPA_DRV_UPDATE_FILS_ERP_INFO;
+	}
+#endif /* CONFIG_FILS */
+#endif /* IEEE8021X_EAPOL */
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs);
+	if (ssid->auth_alg) {
+		algs = ssid->auth_alg;
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Overriding auth_alg selection: 0x%x", algs);
+	}
+
+#ifdef CONFIG_P2P
+	if (wpa_s->global->p2p) {
+		u8 *pos;
+		size_t len;
+		int res;
+		pos = wpa_ie + wpa_ie_len;
+		len = max_wpa_ie_len - wpa_ie_len;
+		res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len,
+					    ssid->p2p_group);
+		if (res >= 0)
+			wpa_ie_len += res;
+	}
+
+	wpa_s->cross_connect_disallowed = 0;
+	if (bss) {
+		struct wpabuf *p2p;
+		p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
+		if (p2p) {
+			wpa_s->cross_connect_disallowed =
+				p2p_get_cross_connect_disallowed(p2p);
+			wpabuf_free(p2p);
+			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: WLAN AP %s cross "
+				"connection",
+				wpa_s->cross_connect_disallowed ?
+				"disallows" : "allows");
+		}
+	}
+
+	os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info));
+#endif /* CONFIG_P2P */
+
+	if (bss) {
+		wpa_ie_len += wpas_supp_op_class_ie(wpa_s, bss->freq,
+						    wpa_ie + wpa_ie_len,
+						    max_wpa_ie_len -
+						    wpa_ie_len);
+	}
+
+	/*
+	 * Workaround: Add Extended Capabilities element only if the AP
+	 * included this element in Beacon/Probe Response frames. Some older
+	 * APs seem to have interoperability issues if this element is
+	 * included, so while the standard may require us to include the
+	 * element in all cases, it is justifiable to skip it to avoid
+	 * interoperability issues.
+	 */
+	if (ssid->p2p_group)
+		wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT);
+	else
+		wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION);
+
+	if (!bss || wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB)) {
+		u8 ext_capab[18];
+		int ext_capab_len;
+		ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
+						     sizeof(ext_capab));
+		if (ext_capab_len > 0 &&
+		    wpa_ie_len + ext_capab_len <= max_wpa_ie_len) {
+			u8 *pos = wpa_ie;
+			if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN)
+				pos += 2 + pos[1];
+			os_memmove(pos + ext_capab_len, pos,
+				   wpa_ie_len - (pos - wpa_ie));
+			wpa_ie_len += ext_capab_len;
+			os_memcpy(pos, ext_capab, ext_capab_len);
+		}
+	}
+
+#ifdef CONFIG_HS20
+	if (is_hs20_network(wpa_s, ssid, bss)) {
+		struct wpabuf *hs20;
+
+		hs20 = wpabuf_alloc(20);
+		if (hs20) {
+			int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
+			size_t len;
+
+			wpas_hs20_add_indication(hs20, pps_mo_id);
+			len = max_wpa_ie_len - wpa_ie_len;
+			if (wpabuf_len(hs20) <= len) {
+				os_memcpy(wpa_ie + wpa_ie_len,
+					  wpabuf_head(hs20), wpabuf_len(hs20));
+				wpa_ie_len += wpabuf_len(hs20);
+			}
+			wpabuf_free(hs20);
+
+			hs20_configure_frame_filters(wpa_s);
+		}
+	}
+#endif /* CONFIG_HS20 */
+
+	if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) {
+		struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
+		size_t len;
+
+		len = max_wpa_ie_len - wpa_ie_len;
+		if (wpabuf_len(buf) <= len) {
+			os_memcpy(wpa_ie + wpa_ie_len,
+				  wpabuf_head(buf), wpabuf_len(buf));
+			wpa_ie_len += wpabuf_len(buf);
+		}
+	}
+
+#ifdef CONFIG_FST
+	if (wpa_s->fst_ies) {
+		int fst_ies_len = wpabuf_len(wpa_s->fst_ies);
+
+		if (wpa_ie_len + fst_ies_len <= max_wpa_ie_len) {
+			os_memcpy(wpa_ie + wpa_ie_len,
+				  wpabuf_head(wpa_s->fst_ies), fst_ies_len);
+			wpa_ie_len += fst_ies_len;
+		}
+	}
+#endif /* CONFIG_FST */
+
+#ifdef CONFIG_MBO
+	if (bss && wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE)) {
+		int len;
+
+		len = wpas_mbo_ie(wpa_s, wpa_ie + wpa_ie_len,
+				  max_wpa_ie_len - wpa_ie_len);
+		if (len >= 0)
+			wpa_ie_len += len;
+	}
+#endif /* CONFIG_MBO */
+
+#ifdef CONFIG_FILS
+	if (algs == WPA_AUTH_ALG_FILS) {
+		size_t len;
+
+		len = wpas_add_fils_hlp_req(wpa_s, wpa_ie + wpa_ie_len,
+					    max_wpa_ie_len - wpa_ie_len);
+		wpa_ie_len += len;
+	}
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+	if (algs == WPA_AUTH_ALG_OPEN &&
+	    ssid->key_mgmt == WPA_KEY_MGMT_OWE) {
+		struct wpabuf *owe_ie;
+		u16 group = OWE_DH_GROUP;
+
+		if (ssid->owe_group)
+			group = ssid->owe_group;
+		owe_ie = owe_build_assoc_req(wpa_s->wpa, group);
+		if (owe_ie &&
+		    wpabuf_len(owe_ie) <= max_wpa_ie_len - wpa_ie_len) {
+			os_memcpy(wpa_ie + wpa_ie_len,
+				  wpabuf_head(owe_ie), wpabuf_len(owe_ie));
+			wpa_ie_len += wpabuf_len(owe_ie);
+			wpabuf_free(owe_ie);
+		}
+	}
+#endif /* CONFIG_OWE */
+
+	params->wpa_ie = wpa_ie;
+	params->wpa_ie_len = wpa_ie_len;
+	params->auth_alg = algs;
+	if (mask)
+		*mask |= WPA_DRV_UPDATE_ASSOC_IES | WPA_DRV_UPDATE_AUTH_TYPE;
+
+	return wpa_ie;
+}
+
+
+#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL)
+static void wpas_update_fils_connect_params(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_driver_associate_params params;
+	enum wpa_drv_update_connect_params_mask mask = 0;
+	u8 *wpa_ie;
+
+	if (wpa_s->auth_alg != WPA_AUTH_ALG_OPEN)
+		return; /* nothing to do */
+
+	os_memset(&params, 0, sizeof(params));
+	wpa_ie = wpas_populate_assoc_ies(wpa_s, wpa_s->current_bss,
+					 wpa_s->current_ssid, &params, &mask);
+	if (!wpa_ie)
+		return;
+
+	if (params.auth_alg != WPA_AUTH_ALG_FILS) {
+		os_free(wpa_ie);
+		return;
+	}
+
+	wpa_s->auth_alg = params.auth_alg;
+	wpa_drv_update_connect_params(wpa_s, &params, mask);
+	os_free(wpa_ie);
+}
+#endif /* CONFIG_FILS && IEEE8021X_EAPOL */
+
+
 static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
 {
 	struct wpa_connect_work *cwork = work->ctx;
 	struct wpa_bss *bss = cwork->bss;
 	struct wpa_ssid *ssid = cwork->ssid;
 	struct wpa_supplicant *wpa_s = work->wpa_s;
-	u8 wpa_ie[200];
-	size_t wpa_ie_len;
+	u8 *wpa_ie;
 	int use_crypt, ret, i, bssid_changed;
-	int algs = WPA_AUTH_ALG_OPEN;
-	unsigned int cipher_pairwise, cipher_group;
+	unsigned int cipher_pairwise, cipher_group, cipher_group_mgmt;
 	struct wpa_driver_associate_params params;
 	int wep_keys_set = 0;
 	int assoc_failed = 0;
@@ -2265,214 +2765,17 @@
 	 * previous association. */
 	wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
 
-#ifdef IEEE8021X_EAPOL
-	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
-		if (ssid->leap) {
-			if (ssid->non_leap == 0)
-				algs = WPA_AUTH_ALG_LEAP;
-			else
-				algs |= WPA_AUTH_ALG_LEAP;
-		}
+	wpa_ie = wpas_populate_assoc_ies(wpa_s, bss, ssid, &params, NULL);
+	if (!wpa_ie) {
+		wpas_connect_work_done(wpa_s);
+		return;
 	}
-#endif /* IEEE8021X_EAPOL */
-	wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs);
-	if (ssid->auth_alg) {
-		algs = ssid->auth_alg;
-		wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: "
-			"0x%x", algs);
-	}
-
-	if (bss && (wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) ||
-		    wpa_bss_get_ie(bss, WLAN_EID_RSN)) &&
-	    wpa_key_mgmt_wpa(ssid->key_mgmt)) {
-		int try_opportunistic;
-		try_opportunistic = (ssid->proactive_key_caching < 0 ?
-				     wpa_s->conf->okc :
-				     ssid->proactive_key_caching) &&
-			(ssid->proto & WPA_PROTO_RSN);
-		if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
-					    ssid, try_opportunistic) == 0)
-			eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
-		wpa_ie_len = sizeof(wpa_ie);
-		if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
-					      wpa_ie, &wpa_ie_len)) {
-			wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
-				"key management and encryption suites");
-			wpas_connect_work_done(wpa_s);
-			return;
-		}
-	} else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && bss &&
-		   wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) {
-		/*
-		 * Both WPA and non-WPA IEEE 802.1X enabled in configuration -
-		 * use non-WPA since the scan results did not indicate that the
-		 * AP is using WPA or WPA2.
-		 */
-		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
-		wpa_ie_len = 0;
-		wpa_s->wpa_proto = 0;
-	} else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
-		wpa_ie_len = sizeof(wpa_ie);
-		if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
-					      wpa_ie, &wpa_ie_len)) {
-			wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
-				"key management and encryption suites (no "
-				"scan results)");
-			wpas_connect_work_done(wpa_s);
-			return;
-		}
-#ifdef CONFIG_WPS
-	} else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
-		struct wpabuf *wps_ie;
-		wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid));
-		if (wps_ie && wpabuf_len(wps_ie) <= sizeof(wpa_ie)) {
-			wpa_ie_len = wpabuf_len(wps_ie);
-			os_memcpy(wpa_ie, wpabuf_head(wps_ie), wpa_ie_len);
-		} else
-			wpa_ie_len = 0;
-		wpabuf_free(wps_ie);
-		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
-		if (!bss || (bss->caps & IEEE80211_CAP_PRIVACY))
-			params.wps = WPS_MODE_PRIVACY;
-		else
-			params.wps = WPS_MODE_OPEN;
-		wpa_s->wpa_proto = 0;
-#endif /* CONFIG_WPS */
-	} else {
-		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
-		wpa_ie_len = 0;
-		wpa_s->wpa_proto = 0;
-	}
-
-#ifdef CONFIG_P2P
-	if (wpa_s->global->p2p) {
-		u8 *pos;
-		size_t len;
-		int res;
-		pos = wpa_ie + wpa_ie_len;
-		len = sizeof(wpa_ie) - wpa_ie_len;
-		res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len,
-					    ssid->p2p_group);
-		if (res >= 0)
-			wpa_ie_len += res;
-	}
-
-	wpa_s->cross_connect_disallowed = 0;
-	if (bss) {
-		struct wpabuf *p2p;
-		p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
-		if (p2p) {
-			wpa_s->cross_connect_disallowed =
-				p2p_get_cross_connect_disallowed(p2p);
-			wpabuf_free(p2p);
-			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: WLAN AP %s cross "
-				"connection",
-				wpa_s->cross_connect_disallowed ?
-				"disallows" : "allows");
-		}
-	}
-
-	os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info));
-#endif /* CONFIG_P2P */
-
-	if (bss) {
-		wpa_ie_len += wpas_supp_op_class_ie(wpa_s, bss->freq,
-						    wpa_ie + wpa_ie_len,
-						    sizeof(wpa_ie) -
-						    wpa_ie_len);
-	}
-
-	/*
-	 * Workaround: Add Extended Capabilities element only if the AP
-	 * included this element in Beacon/Probe Response frames. Some older
-	 * APs seem to have interoperability issues if this element is
-	 * included, so while the standard may require us to include the
-	 * element in all cases, it is justifiable to skip it to avoid
-	 * interoperability issues.
-	 */
-	if (ssid->p2p_group)
-		wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT);
-	else
-		wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION);
-
-	if (!bss || wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB)) {
-		u8 ext_capab[18];
-		int ext_capab_len;
-		ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
-						     sizeof(ext_capab));
-		if (ext_capab_len > 0) {
-			u8 *pos = wpa_ie;
-			if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN)
-				pos += 2 + pos[1];
-			os_memmove(pos + ext_capab_len, pos,
-				   wpa_ie_len - (pos - wpa_ie));
-			wpa_ie_len += ext_capab_len;
-			os_memcpy(pos, ext_capab, ext_capab_len);
-		}
-	}
-
-#ifdef CONFIG_HS20
-	if (is_hs20_network(wpa_s, ssid, bss)) {
-		struct wpabuf *hs20;
-
-		hs20 = wpabuf_alloc(20);
-		if (hs20) {
-			int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
-			size_t len;
-
-			wpas_hs20_add_indication(hs20, pps_mo_id);
-			len = sizeof(wpa_ie) - wpa_ie_len;
-			if (wpabuf_len(hs20) <= len) {
-				os_memcpy(wpa_ie + wpa_ie_len,
-					  wpabuf_head(hs20), wpabuf_len(hs20));
-				wpa_ie_len += wpabuf_len(hs20);
-			}
-			wpabuf_free(hs20);
-
-			hs20_configure_frame_filters(wpa_s);
-		}
-	}
-#endif /* CONFIG_HS20 */
-
-	if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) {
-		struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
-		size_t len;
-
-		len = sizeof(wpa_ie) - wpa_ie_len;
-		if (wpabuf_len(buf) <= len) {
-			os_memcpy(wpa_ie + wpa_ie_len,
-				  wpabuf_head(buf), wpabuf_len(buf));
-			wpa_ie_len += wpabuf_len(buf);
-		}
-	}
-
-#ifdef CONFIG_FST
-	if (wpa_s->fst_ies) {
-		int fst_ies_len = wpabuf_len(wpa_s->fst_ies);
-
-		if (wpa_ie_len + fst_ies_len <= sizeof(wpa_ie)) {
-			os_memcpy(wpa_ie + wpa_ie_len,
-				  wpabuf_head(wpa_s->fst_ies), fst_ies_len);
-			wpa_ie_len += fst_ies_len;
-		}
-	}
-#endif /* CONFIG_FST */
-
-#ifdef CONFIG_MBO
-	if (bss && wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE)) {
-		int len;
-
-		len = wpas_mbo_ie(wpa_s, wpa_ie + wpa_ie_len,
-				  sizeof(wpa_ie) - wpa_ie_len);
-		if (len >= 0)
-			wpa_ie_len += len;
-	}
-#endif /* CONFIG_MBO */
 
 	wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
 	use_crypt = 1;
 	cipher_pairwise = wpa_s->pairwise_cipher;
 	cipher_group = wpa_s->group_cipher;
+	cipher_group_mgmt = wpa_s->mgmt_group_cipher;
 	if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
 	    wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
 		if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE)
@@ -2525,6 +2828,9 @@
 		params.freq_hint = bss->freq;
 		params.pbss = bss_is_pbss(bss);
 	} else {
+		if (ssid->bssid_hint_set)
+			params.bssid_hint = ssid->bssid_hint;
+
 		params.ssid = ssid->ssid;
 		params.ssid_len = ssid->ssid_len;
 		params.pbss = (ssid->pbss != 2) ? ssid->pbss : 0;
@@ -2549,13 +2855,12 @@
 			params.beacon_int = wpa_s->conf->beacon_int;
 	}
 
-	params.wpa_ie = wpa_ie;
-	params.wpa_ie_len = wpa_ie_len;
 	params.pairwise_suite = cipher_pairwise;
 	params.group_suite = cipher_group;
+	params.mgmt_group_suite = cipher_group_mgmt;
 	params.key_mgmt_suite = wpa_s->key_mgmt;
 	params.wpa_proto = wpa_s->wpa_proto;
-	params.auth_alg = algs;
+	wpa_s->auth_alg = params.auth_alg;
 	params.mode = ssid->mode;
 	params.bg_scan_period = ssid->bg_scan_period;
 	for (i = 0; i < NUM_WEP_KEYS; i++) {
@@ -2647,6 +2952,7 @@
 			if (wpas_p2p_handle_frequency_conflicts(
 				    wpa_s, params.freq.freq, ssid) < 0) {
 				wpas_connect_work_done(wpa_s);
+				os_free(wpa_ie);
 				return;
 			}
 		}
@@ -2658,6 +2964,7 @@
 		params.prev_bssid = prev_bssid;
 
 	ret = wpa_drv_associate(wpa_s, &params);
+	os_free(wpa_ie);
 	if (ret < 0) {
 		wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
 			"failed");
@@ -2799,8 +3106,13 @@
 
 #ifdef CONFIG_MESH
 	if (wpa_s->ifmsh) {
+		struct mesh_conf *mconf;
+
+		mconf = wpa_s->ifmsh->mconf;
 		wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_REMOVED "%s",
 			wpa_s->ifname);
+		wpas_notify_mesh_group_removed(wpa_s, mconf->meshid,
+					       mconf->meshid_len, reason_code);
 		wpa_supplicant_leave_mesh(wpa_s);
 	}
 #endif /* CONFIG_MESH */
@@ -2836,8 +3148,9 @@
 		wpa_s->reassociate = 1;
 }
 
+
 /**
- * wpa_supplicant_add_network - Add a new network.
+ * wpa_supplicant_add_network - Add a new network
  * @wpa_s: wpa_supplicant structure for a network interface
  * Returns: The new network configuration or %NULL if operation failed
  *
@@ -2852,9 +3165,8 @@
 	struct wpa_ssid *ssid;
 
 	ssid = wpa_config_add_network(wpa_s->conf);
-	if (!ssid) {
+	if (!ssid)
 		return NULL;
-	}
 	wpas_notify_network_added(wpa_s, ssid);
 	ssid->disabled = 1;
 	wpa_config_set_network_defaults(ssid);
@@ -2862,6 +3174,7 @@
 	return ssid;
 }
 
+
 /**
  * wpa_supplicant_remove_network - Remove a configured network based on id
  * @wpa_s: wpa_supplicant structure for a network interface
@@ -2881,16 +3194,14 @@
 	int was_disabled;
 
 	ssid = wpa_config_get_network(wpa_s->conf, id);
-	if (ssid)
-		wpas_notify_network_removed(wpa_s, ssid);
-	if (ssid == NULL) {
+	if (!ssid)
 		return -1;
-	}
+	wpas_notify_network_removed(wpa_s, ssid);
 
 	if (wpa_s->last_ssid == ssid)
 		wpa_s->last_ssid = NULL;
 
-	if (ssid == wpa_s->current_ssid || wpa_s->current_ssid == NULL) {
+	if (ssid == wpa_s->current_ssid || !wpa_s->current_ssid) {
 #ifdef CONFIG_SME
 		wpa_s->sme.prev_bssid_set = 0;
 #endif /* CONFIG_SME */
@@ -2913,19 +3224,20 @@
 
 	was_disabled = ssid->disabled;
 
-	if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
+	if (wpa_config_remove_network(wpa_s->conf, id) < 0)
 		return -2;
-	}
 
 	if (!was_disabled && wpa_s->sched_scanning) {
-		wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to remove "
-			   "network from filters");
+		wpa_printf(MSG_DEBUG,
+			   "Stop ongoing sched_scan to remove network from filters");
 		wpa_supplicant_cancel_sched_scan(wpa_s);
 		wpa_supplicant_req_scan(wpa_s, 0, 0);
 	}
+
 	return 0;
 }
 
+
 /**
  * wpa_supplicant_enable_network - Mark a configured network as enabled
  * @wpa_s: wpa_supplicant structure for a network interface
@@ -2990,13 +3302,19 @@
 				wpas_notify_network_enabled_changed(
 					wpa_s, other_ssid);
 		}
-		if (wpa_s->current_ssid)
+		if (wpa_s->current_ssid) {
+			if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+				wpa_s->own_disconnect_req = 1;
 			wpa_supplicant_deauthenticate(
 				wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+		}
 	} else if (ssid->disabled != 2) {
-		if (ssid == wpa_s->current_ssid)
+		if (ssid == wpa_s->current_ssid) {
+			if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+				wpa_s->own_disconnect_req = 1;
 			wpa_supplicant_deauthenticate(
 				wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+		}
 
 		was_disabled = ssid->disabled;
 
@@ -3299,6 +3617,41 @@
 }
 
 
+#ifdef CONFIG_OWE
+static int owe_trans_ssid_match(struct wpa_supplicant *wpa_s, const u8 *bssid,
+				const u8 *entry_ssid, size_t entry_ssid_len)
+{
+	const u8 *owe, *pos, *end;
+	u8 ssid_len;
+	struct wpa_bss *bss;
+
+	/* Check network profile SSID aganst the SSID in the
+	 * OWE Transition Mode element. */
+
+	bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
+	if (!bss)
+		return 0;
+
+	owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
+	if (!owe)
+		return 0;
+
+	pos = owe + 6;
+	end = owe + 2 + owe[1];
+
+	if (end - pos < ETH_ALEN + 1)
+		return 0;
+	pos += ETH_ALEN;
+	ssid_len = *pos++;
+	if (end - pos < ssid_len || ssid_len > SSID_MAX_LEN)
+		return 0;
+
+	return entry_ssid_len == ssid_len &&
+		os_memcmp(pos, entry_ssid, ssid_len) == 0;
+}
+#endif /* CONFIG_OWE */
+
+
 /**
  * wpa_supplicant_get_ssid - Get a pointer to the current network structure
  * @wpa_s: Pointer to wpa_supplicant data
@@ -3347,6 +3700,15 @@
 			return entry;
 #endif /* CONFIG_WPS */
 
+#ifdef CONFIG_OWE
+		if (!wpas_network_disabled(wpa_s, entry) &&
+		    owe_trans_ssid_match(wpa_s, bssid, entry->ssid,
+		    entry->ssid_len) &&
+		    (!entry->bssid_set ||
+		     os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
+			return entry;
+#endif /* CONFIG_OWE */
+
 		if (!wpas_network_disabled(wpa_s, entry) && entry->bssid_set &&
 		    entry->ssid_len == 0 &&
 		    os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)
@@ -3454,16 +3816,6 @@
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
 
-#ifdef CONFIG_PEERKEY
-	if (wpa_s->wpa_state > WPA_ASSOCIATED && wpa_s->current_ssid &&
-	    wpa_s->current_ssid->peerkey &&
-	    !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
-	    wpa_sm_rx_eapol_peerkey(wpa_s->wpa, src_addr, buf, len) == 1) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "RSN: Processed PeerKey EAPOL-Key");
-		return;
-	}
-#endif /* CONFIG_PEERKEY */
-
 	if (wpa_s->wpa_state < WPA_ASSOCIATED ||
 	    (wpa_s->last_eapol_matches_bssid &&
 #ifdef CONFIG_AP
@@ -3574,6 +3926,8 @@
 
 	os_memcpy(wpa_s->last_eapol_src, src_addr, ETH_ALEN);
 	if (!wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) &&
+	    wpa_s->key_mgmt != WPA_KEY_MGMT_OWE &&
+	    wpa_s->key_mgmt != WPA_KEY_MGMT_DPP &&
 	    eapol_sm_rx_eapol(wpa_s->eapol, src_addr, buf, len) > 0)
 		return;
 	wpa_drv_poll(wpa_s);
@@ -4765,11 +5119,47 @@
 }
 
 
+#ifdef CONFIG_GAS_SERVER
+
+static void wpas_gas_server_tx_status(struct wpa_supplicant *wpa_s,
+				      unsigned int freq, const u8 *dst,
+				      const u8 *src, const u8 *bssid,
+				      const u8 *data, size_t data_len,
+				      enum offchannel_send_action_result result)
+{
+	wpa_printf(MSG_DEBUG, "GAS: TX status: freq=%u dst=" MACSTR
+		   " result=%s",
+		   freq, MAC2STR(dst),
+		   result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" :
+		   (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" :
+		    "FAILED"));
+	gas_server_tx_status(wpa_s->gas_server, dst, data, data_len,
+			     result == OFFCHANNEL_SEND_ACTION_SUCCESS);
+}
+
+
+static void wpas_gas_server_tx(void *ctx, int freq, const u8 *da,
+			       struct wpabuf *buf, unsigned int wait_time)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+	if (wait_time > wpa_s->max_remain_on_chan)
+		wait_time = wpa_s->max_remain_on_chan;
+
+	offchannel_send_action(wpa_s, freq, da, wpa_s->own_addr, broadcast,
+			       wpabuf_head(buf), wpabuf_len(buf),
+			       wait_time, wpas_gas_server_tx_status, 0);
+}
+
+#endif /* CONFIG_GAS_SERVER */
+
 static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
 				     struct wpa_interface *iface)
 {
 	struct wpa_driver_capa capa;
 	int capa_res;
+	u8 dfs_domain;
 
 	wpa_printf(MSG_DEBUG, "Initializing interface '%s' conf '%s' driver "
 		   "'%s' ctrl_interface '%s' bridge '%s'", iface->ifname,
@@ -4897,7 +5287,8 @@
 
 	wpa_s->hw.modes = wpa_drv_get_hw_feature_data(wpa_s,
 						      &wpa_s->hw.num_modes,
-						      &wpa_s->hw.flags);
+						      &wpa_s->hw.flags,
+						      &dfs_domain);
 	if (wpa_s->hw.modes) {
 		u16 i;
 
@@ -5007,6 +5398,19 @@
 	if (wpas_wps_init(wpa_s))
 		return -1;
 
+#ifdef CONFIG_GAS_SERVER
+	wpa_s->gas_server = gas_server_init(wpa_s, wpas_gas_server_tx);
+	if (!wpa_s->gas_server) {
+		wpa_printf(MSG_ERROR, "Failed to initialize GAS server");
+		return -1;
+	}
+#endif /* CONFIG_GAS_SERVER */
+
+#ifdef CONFIG_DPP
+	if (wpas_dpp_init(wpa_s) < 0)
+		return -1;
+#endif /* CONFIG_DPP */
+
 	if (wpa_supplicant_init_eapol(wpa_s) < 0)
 		return -1;
 	wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol);
@@ -5056,8 +5460,8 @@
 #ifdef CONFIG_EAP_PROXY
 {
 	size_t len;
-	wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, wpa_s->imsi,
-						     &len);
+	wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, -1,
+						     wpa_s->imsi, &len);
 	if (wpa_s->mnc_len > 0) {
 		wpa_s->imsi[len] = '\0';
 		wpa_printf(MSG_DEBUG, "eap_proxy: IMSI %s (MNC length %d)",
@@ -5082,6 +5486,17 @@
 	hs20_init(wpa_s);
 #endif /* CONFIG_HS20 */
 #ifdef CONFIG_MBO
+	if (wpa_s->conf->oce) {
+		if ((wpa_s->conf->oce & OCE_STA) &&
+		    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA))
+			wpa_s->enable_oce = OCE_STA;
+		if ((wpa_s->conf->oce & OCE_STA_CFON) &&
+		    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA_CFON)) {
+			/* TODO: Need to add STA-CFON support */
+			wpa_printf(MSG_ERROR,
+				   "OCE STA-CFON feature is not yet supported");
+		}
+	}
 	wpas_mbo_update_non_pref_chan(wpa_s, wpa_s->conf->non_pref_chan);
 #endif /* CONFIG_MBO */
 
@@ -5748,6 +6163,16 @@
 	if (wpa_s->conf->changed_parameters & CFG_CHANGED_SCHED_SCAN_PLANS)
 		wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans);
 
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_WOWLAN_TRIGGERS) {
+		struct wpa_driver_capa capa;
+		int res = wpa_drv_get_capa(wpa_s, &capa);
+
+		if (res == 0 && wpas_set_wowlan_triggers(wpa_s, &capa) < 0)
+			wpa_printf(MSG_ERROR,
+				   "Failed to update wowlan_triggers to '%s'",
+				   wpa_s->conf->wowlan_triggers);
+	}
+
 #ifdef CONFIG_WPS
 	wpas_wps_update_config(wpa_s);
 #endif /* CONFIG_WPS */
@@ -6062,6 +6487,7 @@
 
 	if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set &&
 	    (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk &&
+	    !(wpa_key_mgmt_sae(ssid->key_mgmt) && ssid->sae_password) &&
 	    !ssid->mem_only_psk)
 		return 1;
 
@@ -6253,6 +6679,7 @@
 		wpa_s->reattach = 0;
 }
 
+
 /**
  * wpas_request_disconnection - Request disconnection
  * @wpa_s: Pointer to the network interface
@@ -6484,18 +6911,56 @@
 }
 
 
+static int wpa_set_driver_tmp_disallow_list(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_bss_tmp_disallowed *tmp;
+	unsigned int num_bssid = 0;
+	u8 *bssids;
+	int ret;
+
+	bssids = os_malloc(dl_list_len(&wpa_s->bss_tmp_disallowed) * ETH_ALEN);
+	if (!bssids)
+		return -1;
+	dl_list_for_each(tmp, &wpa_s->bss_tmp_disallowed,
+			 struct wpa_bss_tmp_disallowed, list) {
+		os_memcpy(&bssids[num_bssid * ETH_ALEN], tmp->bssid,
+			  ETH_ALEN);
+		num_bssid++;
+	}
+	ret = wpa_drv_set_bssid_blacklist(wpa_s, num_bssid, bssids);
+	os_free(bssids);
+	return ret;
+}
+
+
+static void wpa_bss_tmp_disallow_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct wpa_bss_tmp_disallowed *tmp, *bss = timeout_ctx;
+
+	/* Make sure the bss is not already freed */
+	dl_list_for_each(tmp, &wpa_s->bss_tmp_disallowed,
+			 struct wpa_bss_tmp_disallowed, list) {
+		if (bss == tmp) {
+			dl_list_del(&tmp->list);
+			os_free(tmp);
+			wpa_set_driver_tmp_disallow_list(wpa_s);
+			break;
+		}
+	}
+}
+
+
 void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid,
 			  unsigned int sec)
 {
 	struct wpa_bss_tmp_disallowed *bss;
-	struct os_reltime until;
-
-	os_get_reltime(&until);
-	until.sec += sec;
 
 	bss = wpas_get_disallowed_bss(wpa_s, bssid);
 	if (bss) {
-		bss->disallowed_until = until;
+		eloop_cancel_timeout(wpa_bss_tmp_disallow_timeout, wpa_s, bss);
+		eloop_register_timeout(sec, 0, wpa_bss_tmp_disallow_timeout,
+				       wpa_s, bss);
 		return;
 	}
 
@@ -6506,27 +6971,20 @@
 		return;
 	}
 
-	bss->disallowed_until = until;
 	os_memcpy(bss->bssid, bssid, ETH_ALEN);
 	dl_list_add(&wpa_s->bss_tmp_disallowed, &bss->list);
+	wpa_set_driver_tmp_disallow_list(wpa_s);
+	eloop_register_timeout(sec, 0, wpa_bss_tmp_disallow_timeout,
+			       wpa_s, bss);
 }
 
 
 int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid)
 {
 	struct wpa_bss_tmp_disallowed *bss = NULL, *tmp, *prev;
-	struct os_reltime now, age;
-
-	os_get_reltime(&now);
 
 	dl_list_for_each_safe(tmp, prev, &wpa_s->bss_tmp_disallowed,
 			 struct wpa_bss_tmp_disallowed, list) {
-		if (!os_reltime_before(&now, &tmp->disallowed_until)) {
-			/* This BSS is not disallowed anymore */
-			dl_list_del(&tmp->list);
-			os_free(tmp);
-			continue;
-		}
 		if (os_memcmp(bssid, tmp->bssid, ETH_ALEN) == 0) {
 			bss = tmp;
 			break;
@@ -6535,9 +6993,5 @@
 	if (!bss)
 		return 0;
 
-	os_reltime_sub(&bss->disallowed_until, &now, &age);
-	wpa_printf(MSG_DEBUG,
-		   "BSS " MACSTR " disabled for %ld.%0ld seconds",
-		   MAC2STR(bss->bssid), age.sec, age.usec);
 	return 1;
 }
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 6faa7af..61eb38f 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -183,13 +183,13 @@
 # OpenSSL cipher string
 #
 # This is an OpenSSL specific configuration option for configuring the default
-# ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the default.
+# ciphers. If not set, the value configured at build time ("DEFAULT:!EXP:!LOW"
+# by default) is used.
 # See https://www.openssl.org/docs/apps/ciphers.html for OpenSSL documentation
 # on cipher suite configuration. This is applicable only if wpa_supplicant is
 # built to use OpenSSL.
 #openssl_ciphers=DEFAULT:!EXP:!LOW
 
-
 # Dynamic EAP methods
 # If EAP methods were built dynamically as shared object files, they need to be
 # loaded here before being used in the network blocks. By default, EAP methods
@@ -218,9 +218,15 @@
 # Wi-Fi Protected Setup (WPS) parameters
 
 # Universally Unique IDentifier (UUID; see RFC 4122) of the device
-# If not configured, UUID will be generated based on the local MAC address.
+# If not configured, UUID will be generated based on the mechanism selected with
+# the auto_uuid parameter.
 #uuid=12345678-9abc-def0-1234-56789abcdef0
 
+# Automatic UUID behavior
+# 0 = generate static value based on the local MAC address (default)
+# 1 = generate a random UUID every time wpa_supplicant starts
+#auto_uuid=0
+
 # Device Name
 # User-friendly description of device; up to 32 octets encoded in UTF-8
 #device_name=Wireless Client
@@ -436,6 +442,36 @@
 # Enable Interworking
 # interworking=1
 
+# Enable P2P GO advertisement of Interworking
+# go_interworking=1
+
+# P2P GO Interworking: Access Network Type
+# 0 = Private network
+# 1 = Private network with guest access
+# 2 = Chargeable public network
+# 3 = Free public network
+# 4 = Personal device network
+# 5 = Emergency services only network
+# 14 = Test or experimental
+# 15 = Wildcard
+#go_access_network_type=0
+
+# P2P GO Interworking: Whether the network provides connectivity to the Internet
+# 0 = Unspecified
+# 1 = Network provides connectivity to the Internet
+#go_internet=1
+
+# P2P GO Interworking: Group Venue Info (optional)
+# The available values are defined in IEEE Std 802.11-2016, 9.4.1.35.
+# Example values (group,type):
+# 0,0 = Unspecified
+# 1,7 = Convention Center
+# 1,13 = Coffee Shop
+# 2,0 = Unspecified Business
+# 7,1  Private Residence
+#go_venue_group=7
+#go_venue_type=1
+
 # Homogenous ESS identifier
 # If this is set, scans will be used to request response only from BSSes
 # belonging to the specified Homogeneous ESS. This is used only if interworking
@@ -688,7 +724,7 @@
 # Format:
 # non_pref_chan=<oper_class>:<chan>:<preference>:<reason>
 # Example:
-# non_pref_chan="81:5:10:2 81:1:0:2 81:9:0:2"
+# non_pref_chan=81:5:10:2 81:1:0:2 81:9:0:2
 
 # MBO Cellular Data Capabilities
 # 1 = Cellular data connection available
@@ -696,6 +732,13 @@
 # 3 = Not cellular capable (default)
 #mbo_cell_capa=3
 
+# Optimized Connectivity Experience (OCE)
+# oce: Enable OCE features (bitmap)
+# Set BIT(0) to Enable OCE in non-AP STA mode (default; disabled if the driver
+#	does not indicate support for OCE in STA mode)
+# Set BIT(1) to Enable OCE in STA-CFON mode
+#oce=1
+
 # network block
 #
 # Each network (usually AP's sharing the same SSID) is configured as a separate
@@ -866,6 +909,14 @@
 # WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key [IEEE 802.11]
 # If not set, this defaults to: CCMP TKIP WEP104 WEP40
 #
+# group_mgmt: list of accepted group management ciphers for RSN (PMF)
+# AES-128-CMAC = BIP-CMAC-128
+# BIP-GMAC-128
+# BIP-GMAC-256
+# BIP-CMAC-256
+# If not set, no constraint on the cipher, i.e., accept whichever cipher the AP
+# indicates.
+#
 # psk: WPA preshared key; 256-bit pre-shared key
 # The key used in WPA-PSK mode can be entered either as 64 hex-digits, i.e.,
 # 32 bytes or as an ASCII passphrase (in which case, the real PSK will be
@@ -883,6 +934,12 @@
 # 1 = do not store psk/passphrase to the configuration file
 #mem_only_psk=0
 #
+# sae_password: SAE password
+# This parameter can be used to set a password for SAE. By default, the
+# passphrase value is used if this separate parameter is not used, but
+# passphrase follows the WPA-PSK constraints (8..63 characters) even
+# though SAE passwords do not have such constraints.
+#
 # eapol_flags: IEEE 802.1X/EAPOL options (bit field)
 # Dynamic WEP key required for non-WPA mode
 # bit0 (1): require dynamically generated unicast WEP key
@@ -935,12 +992,6 @@
 # hex without quotation, e.g., 0102030405)
 # wep_tx_keyidx: Default WEP key index (TX) (0..3)
 #
-# peerkey: Whether PeerKey negotiation for direct links (IEEE 802.11e DLS) is
-# allowed. This is only used with RSN/WPA2.
-# 0 = disabled (default)
-# 1 = enabled
-#peerkey=1
-#
 # wpa_ptk_rekey: Maximum lifetime for PTK in seconds. This can be used to
 # enforce rekeying of PTK to mitigate some attacks against TKIP deficiencies.
 #
@@ -1151,6 +1202,9 @@
 #	chain when receiving CTRL-RSP-EXT_CERT_CHECK event from the control
 #	interface and report the result of the validation with
 #	CTRL-RSP_EXT_CERT_CHECK.
+# tls_suiteb=0 - do not apply Suite B 192-bit constraints on TLS (default)
+# tls_suiteb=1 - apply Suite B 192-bit constraints on TLS; this is used in
+#	particular when using Suite B with RSA keys of >= 3K (3072) bits
 #
 # Following certificate/private key fields are used in inner Phase2
 # authentication when using EAP-TTLS or EAP-PEAP.
@@ -1248,6 +1302,11 @@
 # 1 = WPS disabled
 #wps_disabled=0
 
+# FILS DH Group
+# 0 = PFS disabled with FILS shared key authentication (default)
+# 1-65535 = DH Group to use for FILS PFS
+#fils_dh_group=0
+
 # MAC address policy
 # 0 = use permanent MAC address
 # 1 = use random MAC address for each ESS connection
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 395be3d..d0172fa 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -296,7 +296,7 @@
 
 #ifdef CONFIG_WIFI_DISPLAY
 	int wifi_display;
-#define MAX_WFD_SUBELEMS 10
+#define MAX_WFD_SUBELEMS 12
 	struct wpabuf *wfd_subelem[MAX_WFD_SUBELEMS];
 #endif /* CONFIG_WIFI_DISPLAY */
 
@@ -428,6 +428,9 @@
 
 	/* token - Dialog token of the current radio measurement */
 	u8 token;
+
+	/* destination address of the current radio measurement request */
+	u8 dst_addr[ETH_ALEN];
 };
 
 enum wpa_supplicant_test_failure {
@@ -447,7 +450,6 @@
 struct wpa_bss_tmp_disallowed {
 	struct dl_list list;
 	u8 bssid[ETH_ALEN];
-	struct os_reltime disallowed_until;
 };
 
 struct beacon_rep_data {
@@ -665,6 +667,7 @@
 	struct os_reltime scan_min_time;
 	int scan_runs; /* number of scan runs since WPS was started */
 	int *next_scan_freqs;
+	int *select_network_scan_freqs;
 	int *manual_scan_freqs;
 	int *manual_sched_scan_freqs;
 	unsigned int manual_scan_passive:1;
@@ -747,6 +750,7 @@
 	int sta_uapsd;
 	int set_ap_uapsd;
 	int ap_uapsd;
+	int auth_alg;
 
 #ifdef CONFIG_SME
 	struct {
@@ -825,6 +829,7 @@
 					    result);
 	unsigned int roc_waiting_drv_freq;
 	int action_tx_wait_time;
+	int action_tx_wait_time_used;
 
 	int p2p_mgmt;
 
@@ -959,6 +964,7 @@
 	int best_overall_freq;
 
 	struct gas_query *gas;
+	struct gas_server *gas_server;
 
 #ifdef CONFIG_INTERWORKING
 	unsigned int fetch_anqp_in_progress:1;
@@ -1045,6 +1051,10 @@
 	struct neighbor_report *wnm_neighbor_report_elements;
 	struct os_reltime wnm_cand_valid_until;
 	u8 wnm_cand_from_bss[ETH_ALEN];
+#ifdef CONFIG_MBO
+	unsigned int wnm_mbo_trans_reason_present:1;
+	u8 wnm_mbo_transition_reason;
+#endif /* CONFIG_MBO */
 #endif /* CONFIG_WNM */
 
 #ifdef CONFIG_TESTING_GET_GTK
@@ -1068,6 +1078,14 @@
 	unsigned int p2p_go_csa_on_inv:1;
 	unsigned int ignore_auth_resp:1;
 	unsigned int ignore_assoc_disallow:1;
+	unsigned int testing_resend_assoc:1;
+	struct wpabuf *sae_commit_override;
+	enum wpa_alg last_tk_alg;
+	u8 last_tk_addr[ETH_ALEN];
+	int last_tk_key_idx;
+	u8 last_tk[WPA_TK_MAX_LEN];
+	size_t last_tk_len;
+	struct wpabuf *last_assoc_req_wpa_ie;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	struct wmm_ac_assoc_data *wmm_ac_assoc_info;
@@ -1096,6 +1114,14 @@
 	} *non_pref_chan;
 	size_t non_pref_chan_num;
 	u8 mbo_wnm_token;
+	/**
+	 * enable_oce - Enable OCE if it is enabled by user and device also
+	 *		supports OCE.
+	 * User can enable OCE with wpa_config's 'oce' parameter as follows -
+	 *  - Set BIT(0) to enable OCE in non-AP STA mode.
+	 *  - Set BIT(1) to enable OCE in STA-CFON mode.
+	 */
+	u8 enable_oce;
 #endif /* CONFIG_MBO */
 
 	/*
@@ -1151,6 +1177,35 @@
 
 	/* RIC elements for FT protocol */
 	struct wpabuf *ric_ies;
+
+#ifdef CONFIG_DPP
+	struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */
+	struct dl_list dpp_configurator; /* struct dpp_configurator */
+	int dpp_init_done;
+	struct dpp_authentication *dpp_auth;
+	struct wpa_radio_work *dpp_listen_work;
+	unsigned int dpp_pending_listen_freq;
+	unsigned int dpp_listen_freq;
+	u8 dpp_allowed_roles;
+	int dpp_qr_mutual;
+	int dpp_netrole_ap;
+	int dpp_auth_ok_on_ack;
+	int dpp_gas_client;
+	u8 dpp_intro_bssid[ETH_ALEN];
+	void *dpp_intro_network;
+	struct dpp_pkex *dpp_pkex;
+	struct dpp_bootstrap_info *dpp_pkex_bi;
+	char *dpp_pkex_code;
+	char *dpp_pkex_identifier;
+	char *dpp_pkex_auth_cmd;
+	char *dpp_configurator_params;
+#ifdef CONFIG_TESTING_OPTIONS
+	char *dpp_config_obj_override;
+	char *dpp_discovery_override;
+	char *dpp_groups_override;
+	unsigned int dpp_ignore_netaccesskey_mismatch:1;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_DPP */
 };
 
 
@@ -1265,12 +1320,13 @@
 						  struct wpabuf *neighbor_rep),
 				       void *cb_ctx);
 void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s,
-					       const u8 *src,
+					       const u8 *src, const u8 *dst,
 					       const u8 *frame, size_t len);
 void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
 					      const u8 *src,
 					      const u8 *frame, size_t len,
 					      int rssi);
+void wpas_rrm_refuse_request(struct wpa_supplicant *wpa_s);
 int wpas_beacon_rep_scan_process(struct wpa_supplicant *wpa_s,
 				 struct wpa_scan_results *scan_res,
 				 struct scan_info *info);
@@ -1291,7 +1347,10 @@
 				    enum mbo_transition_reject_reason reason);
 void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa);
 struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s,
-				   struct wpa_bss *bss);
+				   struct wpa_bss *bss, u32 mbo_subtypes);
+void mbo_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
+			    struct wpa_bss *bss, const u8 *sa,
+			    const u8 *data, size_t slen);
 
 /* op_classes.c */
 enum chan_allowed {
@@ -1347,6 +1406,7 @@
 int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s);
 struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
 					     struct wpa_ssid **selected_ssid);
+int wpas_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
 
 /* eap_register.c */
 int eap_register_methods(void);
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 0d753a9..e44f6af 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -146,6 +146,8 @@
 	 * extra copy here */
 
 	if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_OWE ||
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_DPP ||
 	    wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) {
 		/* Current SSID is not using IEEE 802.1X/EAP, so drop possible
 		 * EAPOL frames (mainly, EAPOL-Start) from EAPOL state
@@ -500,6 +502,16 @@
 		wpa_s->last_gtk_len = key_len;
 	}
 #endif /* CONFIG_TESTING_GET_GTK */
+#ifdef CONFIG_TESTING_OPTIONS
+	if (addr && !is_broadcast_ether_addr(addr)) {
+		wpa_s->last_tk_alg = alg;
+		os_memcpy(wpa_s->last_tk_addr, addr, ETH_ALEN);
+		wpa_s->last_tk_key_idx = key_idx;
+		if (key)
+			os_memcpy(wpa_s->last_tk, key, key_len);
+		wpa_s->last_tk_len = key_len;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 	return wpa_drv_set_key(wpa_s, alg, addr, key_idx, set_tx, seq, seq_len,
 			       key, key_len);
 }
@@ -529,30 +541,59 @@
 
 
 static int wpa_supplicant_add_pmkid(void *_wpa_s, void *network_ctx,
-				    const u8 *bssid, const u8 *pmkid)
+				    const u8 *bssid, const u8 *pmkid,
+				    const u8 *fils_cache_id,
+				    const u8 *pmk, size_t pmk_len)
 {
 	struct wpa_supplicant *wpa_s = _wpa_s;
 	struct wpa_ssid *ssid;
+	struct wpa_pmkid_params params;
 
+	os_memset(&params, 0, sizeof(params));
 	ssid = wpas_get_network_ctx(wpa_s, network_ctx);
 	if (ssid)
 		wpa_msg(wpa_s, MSG_INFO, PMKSA_CACHE_ADDED MACSTR " %d",
 			MAC2STR(bssid), ssid->id);
-	return wpa_drv_add_pmkid(wpa_s, bssid, pmkid);
+	if (ssid && fils_cache_id) {
+		params.ssid = ssid->ssid;
+		params.ssid_len = ssid->ssid_len;
+		params.fils_cache_id = fils_cache_id;
+	} else {
+		params.bssid = bssid;
+	}
+
+	params.pmkid = pmkid;
+	params.pmk = pmk;
+	params.pmk_len = pmk_len;
+
+	return wpa_drv_add_pmkid(wpa_s, &params);
 }
 
 
 static int wpa_supplicant_remove_pmkid(void *_wpa_s, void *network_ctx,
-				       const u8 *bssid, const u8 *pmkid)
+				       const u8 *bssid, const u8 *pmkid,
+				       const u8 *fils_cache_id)
 {
 	struct wpa_supplicant *wpa_s = _wpa_s;
 	struct wpa_ssid *ssid;
+	struct wpa_pmkid_params params;
 
+	os_memset(&params, 0, sizeof(params));
 	ssid = wpas_get_network_ctx(wpa_s, network_ctx);
 	if (ssid)
 		wpa_msg(wpa_s, MSG_INFO, PMKSA_CACHE_REMOVED MACSTR " %d",
 			MAC2STR(bssid), ssid->id);
-	return wpa_drv_remove_pmkid(wpa_s, bssid, pmkid);
+	if (ssid && fils_cache_id) {
+		params.ssid = ssid->ssid;
+		params.ssid_len = ssid->ssid_len;
+		params.fils_cache_id = fils_cache_id;
+	} else {
+		params.bssid = bssid;
+	}
+
+	params.pmkid = pmkid;
+
+	return wpa_drv_remove_pmkid(wpa_s, &params);
 }
 
 
@@ -900,7 +941,7 @@
 	struct wpa_supplicant *wpa_s = ctx;
 	size_t len;
 
-	wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol,
+	wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, -1,
 						     wpa_s->imsi, &len);
 	if (wpa_s->mnc_len > 0) {
 		wpa_s->imsi[len] = '\0';
@@ -1204,7 +1245,6 @@
 	if (ssid) {
 		os_memset(&conf, 0, sizeof(conf));
 		conf.network_ctx = ssid;
-		conf.peerkey_enabled = ssid->peerkey;
 		conf.allowed_pairwise_cipher = ssid->pairwise_cipher;
 #ifdef IEEE8021X_EAPOL
 		conf.proactive_key_caching = ssid->proactive_key_caching < 0 ?
@@ -1232,6 +1272,11 @@
 		}
 #endif /* CONFIG_P2P */
 		conf.wpa_rsc_relaxation = wpa_s->conf->wpa_rsc_relaxation;
+#ifdef CONFIG_FILS
+		if (wpa_key_mgmt_fils(wpa_s->key_mgmt))
+			conf.fils_cache_id =
+				wpa_bss_get_fils_cache_id(wpa_s->current_bss);
+#endif /* CONFIG_FILS */
 	}
 	wpa_sm_set_config(wpa_s->wpa, ssid ? &conf : NULL);
 }
diff --git a/wpa_supplicant/wpas_kay.c b/wpa_supplicant/wpas_kay.c
index d087e00..587e5c3 100644
--- a/wpa_supplicant/wpas_kay.c
+++ b/wpa_supplicant/wpas_kay.c
@@ -235,10 +235,9 @@
 	res = ieee802_1x_kay_init(kay_ctx, policy, ssid->macsec_port,
 				  ssid->mka_priority, wpa_s->ifname,
 				  wpa_s->own_addr);
-	if (res == NULL) {
-		os_free(kay_ctx);
+	/* ieee802_1x_kay_init() frees kay_ctx on failure */
+	if (res == NULL)
 		return -1;
-	}
 
 	wpa_s->kay = res;
 
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 228269e..bd8af83 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -1027,10 +1027,9 @@
 				continue;
 
 			os_free(ssid->ssid);
-			ssid->ssid = os_malloc(bss->ssid_len);
+			ssid->ssid = os_memdup(bss->ssid, bss->ssid_len);
 			if (ssid->ssid == NULL)
 				break;
-			os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
 			ssid->ssid_len = bss->ssid_len;
 			wpa_hexdump_ascii(MSG_DEBUG, "WPS: Picked SSID from "
 					  "scan results",
@@ -1169,6 +1168,7 @@
 		return -1;
 	if (wpa_s->wps_fragment_size)
 		ssid->eap.fragment_size = wpa_s->wps_fragment_size;
+	wpa_supplicant_wps_event(wpa_s, WPS_EV_PBC_ACTIVE, NULL);
 	eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
 			       wpa_s, NULL);
 	wpas_wps_reassoc(wpa_s, ssid, bssid, 0);
@@ -1481,6 +1481,9 @@
 					  wpa_s->global->ifaces->wps->uuid,
 					  WPS_UUID_LEN);
 			src = "from the first interface";
+		} else if (wpa_s->conf->auto_uuid == 1) {
+			uuid_random(wps->uuid);
+			src = "based on random data";
 		} else {
 			uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid);
 			src = "based on MAC address";