[wpa_supplicant] Cumulative patch from 89bbe6f87

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

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

Change-Id: I4efde950845354673694f025ca07c3dddb6039e9
diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c
index 0b596bb..30c5247 100644
--- a/src/common/common_module_tests.c
+++ b/src/common/common_module_tests.c
@@ -1,6 +1,6 @@
 /*
  * common module tests
- * Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2014-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,10 +10,12 @@
 
 #include "utils/common.h"
 #include "utils/module_tests.h"
+#include "crypto/crypto.h"
 #include "ieee802_11_common.h"
 #include "ieee802_11_defs.h"
 #include "gas.h"
 #include "wpa_common.h"
+#include "sae.h"
 
 
 struct ieee802_11_parse_test_data {
@@ -248,6 +250,179 @@
 }
 
 
+static int sae_tests(void)
+{
+#ifdef CONFIG_SAE
+	struct sae_data sae;
+	int ret = -1;
+	/* IEEE P802.11-REVmd/D2.1, Annex J.10 */
+	const u8 addr1[ETH_ALEN] = { 0x82, 0x7b, 0x91, 0x9d, 0xd4, 0xb9 };
+	const u8 addr2[ETH_ALEN] = { 0x1e, 0xec, 0x49, 0xea, 0x64, 0x88 };
+	const char *pw = "mekmitasdigoat";
+	const char *pwid = "psk4internet";
+	const u8 local_rand[] = {
+		0xa9, 0x06, 0xf6, 0x1e, 0x4d, 0x3a, 0x5d, 0x4e,
+		0xb2, 0x96, 0x5f, 0xf3, 0x4c, 0xf9, 0x17, 0xdd,
+		0x04, 0x44, 0x45, 0xc8, 0x78, 0xc1, 0x7c, 0xa5,
+		0xd5, 0xb9, 0x37, 0x86, 0xda, 0x9f, 0x83, 0xcf
+	};
+	const u8 local_mask[] = {
+		0x42, 0x34, 0xb4, 0xfb, 0x17, 0xaa, 0x43, 0x5c,
+		0x52, 0xfb, 0xfd, 0xeb, 0xe6, 0x40, 0x39, 0xb4,
+		0x34, 0x78, 0x20, 0x0e, 0x54, 0xff, 0x7b, 0x6e,
+		0x07, 0xb6, 0x9c, 0xad, 0x74, 0x15, 0x3c, 0x15
+	};
+	const u8 local_commit[] = {
+		0x13, 0x00, 0xeb, 0x3b, 0xab, 0x19, 0x64, 0xe4,
+		0xa0, 0xab, 0x05, 0x92, 0x5d, 0xdf, 0x33, 0x39,
+		0x51, 0x91, 0x38, 0xbc, 0x65, 0xd6, 0xcd, 0xc0,
+		0xf8, 0x13, 0xdd, 0x6f, 0xd4, 0x34, 0x4e, 0xb4,
+		0xbf, 0xe4, 0x4b, 0x5c, 0x21, 0x59, 0x76, 0x58,
+		0xf4, 0xe3, 0xed, 0xdf, 0xb4, 0xb9, 0x9f, 0x25,
+		0xb4, 0xd6, 0x54, 0x0f, 0x32, 0xff, 0x1f, 0xd5,
+		0xc5, 0x30, 0xc6, 0x0a, 0x79, 0x44, 0x48, 0x61,
+		0x0b, 0xc6, 0xde, 0x3d, 0x92, 0xbd, 0xbb, 0xd4,
+		0x7d, 0x93, 0x59, 0x80, 0xca, 0x6c, 0xf8, 0x98,
+		0x8a, 0xb6, 0x63, 0x0b, 0xe6, 0x76, 0x4c, 0x88,
+		0x5c, 0xeb, 0x97, 0x93, 0x97, 0x0f, 0x69, 0x52,
+		0x17, 0xee, 0xff, 0x0d, 0x21, 0x70, 0x73, 0x6b,
+		0x34, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
+		0x74
+	};
+	const u8 peer_commit[] = {
+		0x13, 0x00, 0x55, 0x64, 0xf0, 0x45, 0xb2, 0xea,
+		0x1e, 0x56, 0x6c, 0xf1, 0xdd, 0x74, 0x1f, 0x70,
+		0xd9, 0xbe, 0x35, 0xd2, 0xdf, 0x5b, 0x9a, 0x55,
+		0x02, 0x94, 0x6e, 0xe0, 0x3c, 0xf8, 0xda, 0xe2,
+		0x7e, 0x1e, 0x05, 0xb8, 0x43, 0x0e, 0xb7, 0xa9,
+		0x9e, 0x24, 0x87, 0x7c, 0xe6, 0x9b, 0xaf, 0x3d,
+		0xc5, 0x80, 0xe3, 0x09, 0x63, 0x3d, 0x6b, 0x38,
+		0x5f, 0x83, 0xee, 0x1c, 0x3e, 0xc3, 0x59, 0x1f,
+		0x1a, 0x53, 0x93, 0xc0, 0x6e, 0x80, 0x5d, 0xdc,
+		0xeb, 0x2f, 0xde, 0x50, 0x93, 0x0d, 0xd7, 0xcf,
+		0xeb, 0xb9, 0x87, 0xc6, 0xff, 0x96, 0x66, 0xaf,
+		0x16, 0x4e, 0xb5, 0x18, 0x4d, 0x8e, 0x66, 0x62,
+		0xed, 0x6a, 0xff, 0x0d, 0x21, 0x70, 0x73, 0x6b,
+		0x34, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
+		0x74
+	};
+	const u8 kck[] = {
+		0x59, 0x9d, 0x6f, 0x1e, 0x27, 0x54, 0x8b, 0xe8,
+		0x49, 0x9d, 0xce, 0xed, 0x2f, 0xec, 0xcf, 0x94,
+		0x81, 0x8c, 0xe1, 0xc7, 0x9f, 0x1b, 0x4e, 0xb3,
+		0xd6, 0xa5, 0x32, 0x28, 0xa0, 0x9b, 0xf3, 0xed
+	};
+	const u8 pmk[] = {
+		0x7a, 0xea, 0xd8, 0x6f, 0xba, 0x4c, 0x32, 0x21,
+		0xfc, 0x43, 0x7f, 0x5f, 0x14, 0xd7, 0x0d, 0x85,
+		0x4e, 0xa5, 0xd5, 0xaa, 0xc1, 0x69, 0x01, 0x16,
+		0x79, 0x30, 0x81, 0xed, 0xa4, 0xd5, 0x57, 0xc5
+	};
+	const u8 pmkid[] = {
+		0x40, 0xa0, 0x9b, 0x60, 0x17, 0xce, 0xbf, 0x00,
+		0x72, 0x84, 0x3b, 0x53, 0x52, 0xaa, 0x2b, 0x4f
+	};
+	const u8 local_confirm[] = {
+		0x01, 0x00, 0x12, 0xd9, 0xd5, 0xc7, 0x8c, 0x50,
+		0x05, 0x26, 0xd3, 0x6c, 0x41, 0xdb, 0xc5, 0x6a,
+		0xed, 0xf2, 0x91, 0x4c, 0xed, 0xdd, 0xd7, 0xca,
+		0xd4, 0xa5, 0x8c, 0x48, 0xf8, 0x3d, 0xbd, 0xe9,
+		0xfc, 0x77
+	};
+	const u8 peer_confirm[] = {
+		0x01, 0x00, 0x02, 0x87, 0x1c, 0xf9, 0x06, 0x89,
+		0x8b, 0x80, 0x60, 0xec, 0x18, 0x41, 0x43, 0xbe,
+		0x77, 0xb8, 0xc0, 0x8a, 0x80, 0x19, 0xb1, 0x3e,
+		0xb6, 0xd0, 0xae, 0xf0, 0xd8, 0x38, 0x3d, 0xfa,
+		0xc2, 0xfd
+	};
+	struct wpabuf *buf = NULL;
+	struct crypto_bignum *mask = NULL;
+
+	os_memset(&sae, 0, sizeof(sae));
+	buf = wpabuf_alloc(1000);
+	if (!buf ||
+	    sae_set_group(&sae, 19) < 0 ||
+	    sae_prepare_commit(addr1, addr2, (const u8 *) pw, os_strlen(pw),
+			       pwid, &sae) < 0)
+		goto fail;
+
+	/* Override local values based on SAE test vector */
+	crypto_bignum_deinit(sae.tmp->sae_rand, 1);
+	sae.tmp->sae_rand = crypto_bignum_init_set(local_rand,
+						   sizeof(local_rand));
+	mask = crypto_bignum_init_set(local_mask, sizeof(local_mask));
+	if (!sae.tmp->sae_rand || !mask)
+		goto fail;
+
+	if (crypto_bignum_add(sae.tmp->sae_rand, mask,
+			      sae.tmp->own_commit_scalar) < 0 ||
+	    crypto_bignum_mod(sae.tmp->own_commit_scalar, sae.tmp->order,
+			      sae.tmp->own_commit_scalar) < 0 ||
+	    crypto_ec_point_mul(sae.tmp->ec, sae.tmp->pwe_ecc, mask,
+				sae.tmp->own_commit_element_ecc) < 0 ||
+	    crypto_ec_point_invert(sae.tmp->ec,
+				   sae.tmp->own_commit_element_ecc) < 0)
+		goto fail;
+
+	/* Check that output matches the test vector */
+	sae_write_commit(&sae, buf, NULL, pwid);
+	wpa_hexdump_buf(MSG_DEBUG, "SAE: Commit message", buf);
+
+	if (wpabuf_len(buf) != sizeof(local_commit) ||
+	    os_memcmp(wpabuf_head(buf), local_commit,
+		      sizeof(local_commit)) != 0) {
+		wpa_printf(MSG_ERROR, "SAE: Mismatch in local commit");
+		goto fail;
+	}
+
+	if (sae_parse_commit(&sae, peer_commit, sizeof(peer_commit), NULL, NULL,
+		    NULL) != 0 ||
+	    sae_process_commit(&sae) < 0)
+		goto fail;
+
+	if (os_memcmp(kck, sae.tmp->kck, SAE_KCK_LEN) != 0) {
+		wpa_printf(MSG_ERROR, "SAE: Mismatch in KCK");
+		goto fail;
+	}
+
+	if (os_memcmp(pmk, sae.pmk, SAE_PMK_LEN) != 0) {
+		wpa_printf(MSG_ERROR, "SAE: Mismatch in PMK");
+		goto fail;
+	}
+
+	if (os_memcmp(pmkid, sae.pmkid, SAE_PMKID_LEN) != 0) {
+		wpa_printf(MSG_ERROR, "SAE: Mismatch in PMKID");
+		goto fail;
+	}
+
+	buf->used = 0;
+	sae.send_confirm = 1;
+	sae_write_confirm(&sae, buf);
+	wpa_hexdump_buf(MSG_DEBUG, "SAE: Confirm message", buf);
+
+	if (wpabuf_len(buf) != sizeof(local_confirm) ||
+	    os_memcmp(wpabuf_head(buf), local_confirm,
+		      sizeof(local_confirm)) != 0) {
+		wpa_printf(MSG_ERROR, "SAE: Mismatch in local confirm");
+		goto fail;
+	}
+
+	if (sae_check_confirm(&sae, peer_confirm, sizeof(peer_confirm)) < 0)
+		goto fail;
+
+	ret = 0;
+fail:
+	sae_clear_data(&sae);
+	wpabuf_free(buf);
+	crypto_bignum_deinit(mask, 1);
+	return ret;
+#else /* CONFIG_SAE */
+	return 0;
+#endif /* CONFIG_SAE */
+}
+
+
 int common_module_tests(void)
 {
 	int ret = 0;
@@ -256,6 +431,7 @@
 
 	if (ieee802_11_parse_tests() < 0 ||
 	    gas_tests() < 0 ||
+	    sae_tests() < 0 ||
 	    rsn_ie_parse_tests() < 0)
 		ret = -1;
 
diff --git a/src/common/dpp.c b/src/common/dpp.c
index bcb694b..49de476 100644
--- a/src/common/dpp.c
+++ b/src/common/dpp.c
@@ -1,6 +1,7 @@
 /*
  * DPP functionality shared between hostapd and wpa_supplicant
  * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2019, The Linux Foundation
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -18,6 +19,7 @@
 #include "common/ieee802_11_common.h"
 #include "common/ieee802_11_defs.h"
 #include "common/wpa_ctrl.h"
+#include "common/gas.h"
 #include "crypto/crypto.h"
 #include "crypto/random.h"
 #include "crypto/aes.h"
@@ -68,6 +70,11 @@
 #endif
 
 
+struct dpp_global {
+	struct dl_list bootstrap; /* struct dpp_bootstrap_info */
+	struct dl_list configurator; /* struct dpp_configurator */
+};
+
 static const struct dpp_curve_params dpp_curves[] = {
 	/* The mandatory to support and the default NIST P-256 curve needs to
 	 * be the first entry on this list. */
@@ -1537,6 +1544,9 @@
 		4 + sizeof(wrapped_data);
 	if (neg_freq > 0)
 		attr_len += 4 + 2;
+#ifdef CONFIG_DPP2
+	attr_len += 5;
+#endif /* CONFIG_DPP2 */
 #ifdef CONFIG_TESTING_OPTIONS
 	if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ)
 		attr_len += 5;
@@ -1579,6 +1589,13 @@
 		wpabuf_put_u8(msg, channel);
 	}
 
+#ifdef CONFIG_DPP2
+	/* Protocol Version */
+	wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, 2);
+#endif /* CONFIG_DPP2 */
+
 #ifdef CONFIG_TESTING_OPTIONS
 	if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ) {
 		wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
@@ -1705,6 +1722,9 @@
 	/* Build DPP Authentication Response frame attributes */
 	attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
 		4 + (pr ? wpabuf_len(pr) : 0) + 4 + sizeof(wrapped_data);
+#ifdef CONFIG_DPP2
+	attr_len += 5;
+#endif /* CONFIG_DPP2 */
 #ifdef CONFIG_TESTING_OPTIONS
 	if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP)
 		attr_len += 5;
@@ -1732,6 +1752,13 @@
 		wpabuf_put_buf(msg, pr);
 	}
 
+#ifdef CONFIG_DPP2
+	/* Protocol Version */
+	wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, 2);
+#endif /* CONFIG_DPP2 */
+
 	attr_end = wpabuf_put(msg, 0);
 
 #ifdef CONFIG_TESTING_OPTIONS
@@ -2202,8 +2229,8 @@
 }
 
 
-struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
-				   const char *json)
+static struct wpabuf * dpp_build_conf_req_attr(struct dpp_authentication *auth,
+					       const char *json)
 {
 	size_t nonce_len;
 	size_t json_len, clear_len;
@@ -2307,6 +2334,55 @@
 }
 
 
+static void dpp_write_adv_proto(struct wpabuf *buf)
+{
+	/* Advertisement Protocol IE */
+	wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
+	wpabuf_put_u8(buf, 8); /* Length */
+	wpabuf_put_u8(buf, 0x7f);
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+	wpabuf_put_u8(buf, 5);
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, DPP_OUI_TYPE);
+	wpabuf_put_u8(buf, 0x01);
+}
+
+
+static void dpp_write_gas_query(struct wpabuf *buf, struct wpabuf *query)
+{
+	/* GAS Query */
+	wpabuf_put_le16(buf, wpabuf_len(query));
+	wpabuf_put_buf(buf, query);
+}
+
+
+struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
+				   const char *json)
+{
+	struct wpabuf *buf, *conf_req;
+
+	conf_req = dpp_build_conf_req_attr(auth, json);
+	if (!conf_req) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No configuration request data available");
+		return NULL;
+	}
+
+	buf = gas_build_initial_req(0, 10 + 2 + wpabuf_len(conf_req));
+	if (!buf) {
+		wpabuf_free(conf_req);
+		return NULL;
+	}
+
+	dpp_write_adv_proto(buf);
+	dpp_write_gas_query(buf, conf_req);
+	wpabuf_free(conf_req);
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: GAS Config Request", buf);
+
+	return buf;
+}
+
+
 static void dpp_auth_success(struct dpp_authentication *auth)
 {
 	wpa_printf(MSG_DEBUG,
@@ -2893,6 +2969,10 @@
 	u16 wrapped_data_len, i_proto_len, i_nonce_len, i_capab_len,
 		i_bootstrap_len, channel_len;
 	struct dpp_authentication *auth = NULL;
+#ifdef CONFIG_DPP2
+	const u8 *version;
+	u16 version_len;
+#endif /* CONFIG_DPP2 */
 
 #ifdef CONFIG_TESTING_OPTIONS
 	if (dpp_test == DPP_TEST_STOP_AT_AUTH_REQ) {
@@ -2922,6 +3002,22 @@
 	auth->curve = own_bi->curve;
 	auth->curr_freq = freq;
 
+	auth->peer_version = 1; /* default to the first version */
+#ifdef CONFIG_DPP2
+	version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
+			       &version_len);
+	if (version) {
+		if (version_len < 1 || version[0] == 0) {
+			dpp_auth_fail(auth,
+				      "Invalid Protocol Version attribute");
+			goto fail;
+		}
+		auth->peer_version = version[0];
+		wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
+			   auth->peer_version);
+	}
+#endif /* CONFIG_DPP2 */
+
 	channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL,
 			       &channel_len);
 	if (channel) {
@@ -3450,6 +3546,10 @@
 		wrapped2_len, r_auth_len;
 	u8 r_auth2[DPP_MAX_HASH_LEN];
 	u8 role;
+#ifdef CONFIG_DPP2
+	const u8 *version;
+	u16 version_len;
+#endif /* CONFIG_DPP2 */
 
 #ifdef CONFIG_TESTING_OPTIONS
 	if (dpp_test == DPP_TEST_STOP_AT_AUTH_RESP) {
@@ -3524,6 +3624,22 @@
 		return NULL;
 	}
 
+	auth->peer_version = 1; /* default to the first version */
+#ifdef CONFIG_DPP2
+	version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
+			       &version_len);
+	if (version) {
+		if (version_len < 1 || version[0] == 0) {
+			dpp_auth_fail(auth,
+				      "Invalid Protocol Version attribute");
+			return NULL;
+		}
+		auth->peer_version = version[0];
+		wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
+			   auth->peer_version);
+	}
+#endif /* CONFIG_DPP2 */
+
 	status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
 			      &status_len);
 	if (!status || status_len < 1) {
@@ -3987,6 +4103,99 @@
 }
 
 
+static int bin_str_eq(const char *val, size_t len, const char *cmp)
+{
+	return os_strlen(cmp) == len && os_memcmp(val, cmp, len) == 0;
+}
+
+
+struct dpp_configuration * dpp_configuration_alloc(const char *type)
+{
+	struct dpp_configuration *conf;
+	const char *end;
+	size_t len;
+
+	conf = os_zalloc(sizeof(*conf));
+	if (!conf)
+		goto fail;
+
+	end = os_strchr(type, ' ');
+	if (end)
+		len = end - type;
+	else
+		len = os_strlen(type);
+
+	if (bin_str_eq(type, len, "psk"))
+		conf->akm = DPP_AKM_PSK;
+	else if (bin_str_eq(type, len, "sae"))
+		conf->akm = DPP_AKM_SAE;
+	else if (bin_str_eq(type, len, "psk-sae") ||
+		 bin_str_eq(type, len, "psk+sae"))
+		conf->akm = DPP_AKM_PSK_SAE;
+	else if (bin_str_eq(type, len, "sae-dpp") ||
+		 bin_str_eq(type, len, "dpp+sae"))
+		conf->akm = DPP_AKM_SAE_DPP;
+	else if (bin_str_eq(type, len, "psk-sae-dpp") ||
+		 bin_str_eq(type, len, "dpp+psk+sae"))
+		conf->akm = DPP_AKM_PSK_SAE_DPP;
+	else if (bin_str_eq(type, len, "dpp"))
+		conf->akm = DPP_AKM_DPP;
+	else
+		goto fail;
+
+	return conf;
+fail:
+	dpp_configuration_free(conf);
+	return NULL;
+}
+
+
+int dpp_akm_psk(enum dpp_akm akm)
+{
+	return akm == DPP_AKM_PSK || akm == DPP_AKM_PSK_SAE ||
+		akm == DPP_AKM_PSK_SAE_DPP;
+}
+
+
+int dpp_akm_sae(enum dpp_akm akm)
+{
+	return akm == DPP_AKM_SAE || akm == DPP_AKM_PSK_SAE ||
+		akm == DPP_AKM_SAE_DPP || akm == DPP_AKM_PSK_SAE_DPP;
+}
+
+
+int dpp_akm_legacy(enum dpp_akm akm)
+{
+	return akm == DPP_AKM_PSK || akm == DPP_AKM_PSK_SAE ||
+		akm == DPP_AKM_SAE;
+}
+
+
+int dpp_akm_dpp(enum dpp_akm akm)
+{
+	return akm == DPP_AKM_DPP || akm == DPP_AKM_SAE_DPP ||
+		akm == DPP_AKM_PSK_SAE_DPP;
+}
+
+
+int dpp_akm_ver2(enum dpp_akm akm)
+{
+	return akm == DPP_AKM_SAE_DPP || akm == DPP_AKM_PSK_SAE_DPP;
+}
+
+
+int dpp_configuration_valid(const struct dpp_configuration *conf)
+{
+	if (conf->ssid_len == 0)
+		return 0;
+	if (dpp_akm_psk(conf->akm) && !conf->passphrase && !conf->psk_set)
+		return 0;
+	if (dpp_akm_sae(conf->akm) && !conf->passphrase)
+		return 0;
+	return 1;
+}
+
+
 void dpp_configuration_free(struct dpp_configuration *conf)
 {
 	if (!conf)
@@ -3997,6 +4206,162 @@
 }
 
 
+static int dpp_configuration_parse(struct dpp_authentication *auth,
+				   const char *cmd)
+{
+	const char *pos, *end;
+	struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL;
+	struct dpp_configuration *conf = NULL;
+
+	pos = os_strstr(cmd, " conf=sta-");
+	if (pos) {
+		conf_sta = dpp_configuration_alloc(pos + 10);
+		if (!conf_sta)
+			goto fail;
+		conf = conf_sta;
+	}
+
+	pos = os_strstr(cmd, " conf=ap-");
+	if (pos) {
+		conf_ap = dpp_configuration_alloc(pos + 9);
+		if (!conf_ap)
+			goto fail;
+		conf = conf_ap;
+	}
+
+	if (!conf)
+		return 0;
+
+	pos = os_strstr(cmd, " ssid=");
+	if (pos) {
+		pos += 6;
+		end = os_strchr(pos, ' ');
+		conf->ssid_len = end ? (size_t) (end - pos) : os_strlen(pos);
+		conf->ssid_len /= 2;
+		if (conf->ssid_len > sizeof(conf->ssid) ||
+		    hexstr2bin(pos, conf->ssid, conf->ssid_len) < 0)
+			goto fail;
+	} else {
+#ifdef CONFIG_TESTING_OPTIONS
+		/* use a default SSID for legacy testing reasons */
+		os_memcpy(conf->ssid, "test", 4);
+		conf->ssid_len = 4;
+#else /* CONFIG_TESTING_OPTIONS */
+		goto fail;
+#endif /* CONFIG_TESTING_OPTIONS */
+	}
+
+	pos = os_strstr(cmd, " pass=");
+	if (pos) {
+		size_t pass_len;
+
+		pos += 6;
+		end = os_strchr(pos, ' ');
+		pass_len = end ? (size_t) (end - pos) : os_strlen(pos);
+		pass_len /= 2;
+		if (pass_len > 63 || pass_len < 8)
+			goto fail;
+		conf->passphrase = os_zalloc(pass_len + 1);
+		if (!conf->passphrase ||
+		    hexstr2bin(pos, (u8 *) conf->passphrase, pass_len) < 0)
+			goto fail;
+	}
+
+	pos = os_strstr(cmd, " psk=");
+	if (pos) {
+		pos += 5;
+		if (hexstr2bin(pos, conf->psk, PMK_LEN) < 0)
+			goto fail;
+		conf->psk_set = 1;
+	}
+
+	pos = os_strstr(cmd, " group_id=");
+	if (pos) {
+		size_t group_id_len;
+
+		pos += 10;
+		end = os_strchr(pos, ' ');
+		group_id_len = end ? (size_t) (end - pos) : os_strlen(pos);
+		conf->group_id = os_malloc(group_id_len + 1);
+		if (!conf->group_id)
+			goto fail;
+		os_memcpy(conf->group_id, pos, group_id_len);
+		conf->group_id[group_id_len] = '\0';
+	}
+
+	pos = os_strstr(cmd, " expiry=");
+	if (pos) {
+		long int val;
+
+		pos += 8;
+		val = strtol(pos, NULL, 0);
+		if (val <= 0)
+			goto fail;
+		conf->netaccesskey_expiry = val;
+	}
+
+	if (!dpp_configuration_valid(conf))
+		goto fail;
+
+	auth->conf_sta = conf_sta;
+	auth->conf_ap = conf_ap;
+	return 0;
+
+fail:
+	dpp_configuration_free(conf_sta);
+	dpp_configuration_free(conf_ap);
+	return -1;
+}
+
+
+static struct dpp_configurator *
+dpp_configurator_get_id(struct dpp_global *dpp, unsigned int id)
+{
+	struct dpp_configurator *conf;
+
+	if (!dpp)
+		return NULL;
+
+	dl_list_for_each(conf, &dpp->configurator,
+			 struct dpp_configurator, list) {
+		if (conf->id == id)
+			return conf;
+	}
+	return NULL;
+}
+
+
+int dpp_set_configurator(struct dpp_global *dpp, void *msg_ctx,
+			 struct dpp_authentication *auth,
+			 const char *cmd)
+{
+	const char *pos;
+
+	if (!cmd)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd);
+
+	pos = os_strstr(cmd, " configurator=");
+	if (pos) {
+		pos += 14;
+		auth->conf = dpp_configurator_get_id(dpp, atoi(pos));
+		if (!auth->conf) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Could not find the specified configurator");
+			return -1;
+		}
+	}
+
+	if (dpp_configuration_parse(auth, cmd) < 0) {
+		wpa_msg(msg_ctx, MSG_INFO,
+			"DPP: Failed to set configurator parameters");
+		return -1;
+	}
+	return 0;
+}
+
+
 void dpp_auth_deinit(struct dpp_authentication *auth)
 {
 	if (!auth)
@@ -4096,6 +4461,31 @@
 }
 
 
+static void dpp_build_legacy_cred_params(struct wpabuf *buf,
+					 struct dpp_configuration *conf)
+{
+	if (conf->passphrase && os_strlen(conf->passphrase) < 64) {
+		char pass[63 * 6 + 1];
+
+		json_escape_string(pass, sizeof(pass), conf->passphrase,
+				   os_strlen(conf->passphrase));
+		wpabuf_put_str(buf, "\"pass\":\"");
+		wpabuf_put_str(buf, pass);
+		wpabuf_put_str(buf, "\"");
+		os_memset(pass, 0, sizeof(pass));
+	} else if (conf->psk_set) {
+		char psk[2 * sizeof(conf->psk) + 1];
+
+		wpa_snprintf_hex(psk, sizeof(psk),
+				 conf->psk, sizeof(conf->psk));
+		wpabuf_put_str(buf, "\"psk_hex\":\"");
+		wpabuf_put_str(buf, psk);
+		wpabuf_put_str(buf, "\"");
+		os_memset(psk, 0, sizeof(psk));
+	}
+}
+
+
 static struct wpabuf *
 dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap,
 		       struct dpp_configuration *conf)
@@ -4116,6 +4506,8 @@
 	const EVP_MD *sign_md;
 	const BIGNUM *r, *s;
 	size_t extra_len = 1000;
+	int incl_legacy;
+	enum dpp_akm akm;
 
 	if (!auth->conf) {
 		wpa_printf(MSG_INFO,
@@ -4134,6 +4526,13 @@
 		goto fail;
 	}
 
+	akm = conf->akm;
+	if (dpp_akm_ver2(akm) && auth->peer_version < 2) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Convert DPP+legacy credential to DPP-only for peer that does not support version 2");
+		akm = DPP_AKM_DPP;
+	}
+
 #ifdef CONFIG_TESTING_OPTIONS
 	if (auth->groups_override)
 		extra_len += os_strlen(auth->groups_override);
@@ -4251,14 +4650,22 @@
 	if (!signed3)
 		goto fail;
 
+	incl_legacy = dpp_akm_psk(akm) || dpp_akm_sae(akm);
 	tailroom = 1000;
 	tailroom += 2 * curve->prime_len * 4 / 3 + os_strlen(auth->conf->kid);
 	tailroom += signed1_len + signed2_len + signed3_len;
+	if (incl_legacy)
+		tailroom += 1000;
 	buf = dpp_build_conf_start(auth, conf, tailroom);
 	if (!buf)
 		goto fail;
 
-	wpabuf_put_str(buf, "\"cred\":{\"akm\":\"dpp\",\"signedConnector\":\"");
+	wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(akm));
+	if (incl_legacy) {
+		dpp_build_legacy_cred_params(buf, conf);
+		wpabuf_put_str(buf, ",");
+	}
+	wpabuf_put_str(buf, "\"signedConnector\":\"");
 	wpabuf_put_str(buf, signed1);
 	wpabuf_put_u8(buf, '.');
 	wpabuf_put_str(buf, signed2);
@@ -4304,28 +4711,7 @@
 		return NULL;
 
 	wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(conf->akm));
-	if (conf->passphrase) {
-		char pass[63 * 6 + 1];
-
-		if (os_strlen(conf->passphrase) > 63) {
-			wpabuf_free(buf);
-			return NULL;
-		}
-
-		json_escape_string(pass, sizeof(pass), conf->passphrase,
-				   os_strlen(conf->passphrase));
-		wpabuf_put_str(buf, "\"pass\":\"");
-		wpabuf_put_str(buf, pass);
-		wpabuf_put_str(buf, "\"");
-	} else {
-		char psk[2 * sizeof(conf->psk) + 1];
-
-		wpa_snprintf_hex(psk, sizeof(psk),
-				 conf->psk, sizeof(conf->psk));
-		wpabuf_put_str(buf, "\"psk_hex\":\"");
-		wpabuf_put_str(buf, psk);
-		wpabuf_put_str(buf, "\"");
-	}
+	dpp_build_legacy_cred_params(buf, conf);
 	wpabuf_put_str(buf, "}}");
 
 	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)",
@@ -4356,7 +4742,7 @@
 		return NULL;
 	}
 
-	if (conf->akm == DPP_AKM_DPP)
+	if (dpp_akm_dpp(conf->akm))
 		return dpp_build_conf_obj_dpp(auth, ap, conf);
 	return dpp_build_conf_obj_legacy(auth, ap, conf);
 }
@@ -4380,6 +4766,7 @@
 				  wpabuf_head(conf), wpabuf_len(conf));
 	}
 	status = conf ? DPP_STATUS_OK : DPP_STATUS_CONFIGURE_FAILURE;
+	auth->conf_resp_status = status;
 
 	/* { E-nonce, configurationObject}ke */
 	clear_len = 4 + e_nonce_len;
@@ -4552,6 +4939,7 @@
 		goto fail;
 	}
 	wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
+	os_memcpy(auth->e_nonce, e_nonce, e_nonce_len);
 
 	config_attr = dpp_get_attr(unwrapped, unwrapped_len,
 				   DPP_ATTR_CONFIG_ATTR_OBJ,
@@ -4716,7 +5104,7 @@
 		os_strlcpy(auth->passphrase, pass->string,
 			   sizeof(auth->passphrase));
 	} else if (psk_hex && psk_hex->type == JSON_STRING) {
-		if (auth->akm == DPP_AKM_SAE) {
+		if (dpp_akm_sae(auth->akm) && !dpp_akm_psk(auth->akm)) {
 			wpa_printf(MSG_DEBUG,
 				   "DPP: Unexpected psk_hex with akm=sae");
 			return -1;
@@ -4734,8 +5122,7 @@
 		return -1;
 	}
 
-	if ((auth->akm == DPP_AKM_SAE || auth->akm == DPP_AKM_PSK_SAE) &&
-	    !auth->passphrase[0]) {
+	if (dpp_akm_sae(auth->akm) && !auth->passphrase[0]) {
 		wpa_printf(MSG_DEBUG, "DPP: No pass for sae found");
 		return -1;
 	}
@@ -5248,6 +5635,13 @@
 
 	os_memset(&info, 0, sizeof(info));
 
+	if (dpp_akm_psk(auth->akm) || dpp_akm_sae(auth->akm)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Legacy credential included in Connector credential");
+		if (dpp_parse_cred_legacy(auth, cred) < 0)
+			return -1;
+	}
+
 	wpa_printf(MSG_DEBUG, "DPP: Connector credential");
 
 	csign = json_get_member(cred, "csign");
@@ -5313,6 +5707,10 @@
 		return "sae";
 	case DPP_AKM_PSK_SAE:
 		return "psk+sae";
+	case DPP_AKM_SAE_DPP:
+		return "dpp+sae";
+	case DPP_AKM_PSK_SAE_DPP:
+		return "dpp+psk+sae";
 	default:
 		return "??";
 	}
@@ -5329,6 +5727,10 @@
 		return DPP_AKM_PSK_SAE;
 	if (os_strcmp(akm, "dpp") == 0)
 		return DPP_AKM_DPP;
+	if (os_strcmp(akm, "dpp+sae") == 0)
+		return DPP_AKM_SAE_DPP;
+	if (os_strcmp(akm, "dpp+psk+sae") == 0)
+		return DPP_AKM_PSK_SAE_DPP;
 	return DPP_AKM_UNKNOWN;
 }
 
@@ -5392,11 +5794,10 @@
 	}
 	auth->akm = dpp_akm_from_str(token->string);
 
-	if (auth->akm == DPP_AKM_PSK || auth->akm == DPP_AKM_SAE ||
-	    auth->akm == DPP_AKM_PSK_SAE) {
+	if (dpp_akm_legacy(auth->akm)) {
 		if (dpp_parse_cred_legacy(auth, cred) < 0)
 			goto fail;
-	} else if (auth->akm == DPP_AKM_DPP) {
+	} else if (dpp_akm_dpp(auth->akm)) {
 		if (dpp_parse_cred_dpp(auth, cred) < 0)
 			goto fail;
 	} else {
@@ -5425,6 +5826,8 @@
 	size_t unwrapped_len = 0;
 	int ret = -1;
 
+	auth->conf_resp_status = 255;
+
 	if (dpp_check_attrs(wpabuf_head(resp), wpabuf_len(resp)) < 0) {
 		dpp_auth_fail(auth, "Invalid attribute in config response");
 		return -1;
@@ -5485,6 +5888,7 @@
 			      "Missing or invalid required DPP Status attribute");
 		goto fail;
 	}
+	auth->conf_resp_status = status[0];
 	wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
 	if (status[0] != DPP_STATUS_OK) {
 		dpp_auth_fail(auth, "Configurator rejected configuration");
@@ -5511,6 +5915,146 @@
 }
 
 
+#ifdef CONFIG_DPP2
+enum dpp_status_error dpp_conf_result_rx(struct dpp_authentication *auth,
+					 const u8 *hdr,
+					 const u8 *attr_start, size_t attr_len)
+{
+	const u8 *wrapped_data, *status, *e_nonce;
+	u16 wrapped_data_len, status_len, e_nonce_len;
+	const u8 *addr[2];
+	size_t len[2];
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	enum dpp_status_error ret = 256;
+
+	wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+		dpp_auth_fail(auth,
+			      "Missing or invalid required Wrapped Data attribute");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
+		    wrapped_data, wrapped_data_len);
+
+	attr_len = wrapped_data - 4 - attr_start;
+
+	addr[0] = hdr;
+	len[0] = DPP_HDR_LEN;
+	addr[1] = attr_start;
+	len[1] = attr_len;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		goto fail;
+	if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    2, addr, len, unwrapped) < 0) {
+		dpp_auth_fail(auth, "AES-SIV decryption failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped, unwrapped_len);
+
+	if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+		dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	e_nonce = dpp_get_attr(unwrapped, unwrapped_len,
+			       DPP_ATTR_ENROLLEE_NONCE,
+			       &e_nonce_len);
+	if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
+		dpp_auth_fail(auth,
+			      "Missing or invalid Enrollee Nonce attribute");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
+	if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) {
+		dpp_auth_fail(auth, "Enrollee Nonce mismatch");
+		wpa_hexdump(MSG_DEBUG, "DPP: Expected Enrollee Nonce",
+			    auth->e_nonce, e_nonce_len);
+		goto fail;
+	}
+
+	status = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_STATUS,
+			      &status_len);
+	if (!status || status_len < 1) {
+		dpp_auth_fail(auth,
+			      "Missing or invalid required DPP Status attribute");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+	ret = status[0];
+
+fail:
+	bin_clear_free(unwrapped, unwrapped_len);
+	return ret;
+}
+#endif /* CONFIG_DPP2 */
+
+
+struct wpabuf * dpp_build_conf_result(struct dpp_authentication *auth,
+				      enum dpp_status_error status)
+{
+	struct wpabuf *msg, *clear;
+	size_t nonce_len, clear_len, attr_len;
+	const u8 *addr[2];
+	size_t len[2];
+	u8 *wrapped;
+
+	nonce_len = auth->curve->nonce_len;
+	clear_len = 5 + 4 + nonce_len;
+	attr_len = 4 + clear_len + AES_BLOCK_SIZE;
+	clear = wpabuf_alloc(clear_len);
+	msg = dpp_alloc_msg(DPP_PA_CONFIGURATION_RESULT, attr_len);
+	if (!clear || !msg)
+		return NULL;
+
+	/* DPP Status */
+	dpp_build_attr_status(clear, status);
+
+	/* E-nonce */
+	wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
+	wpabuf_put_le16(clear, nonce_len);
+	wpabuf_put_data(clear, auth->e_nonce, nonce_len);
+
+	/* OUI, OUI type, Crypto Suite, DPP frame type */
+	addr[0] = wpabuf_head_u8(msg) + 2;
+	len[0] = 3 + 1 + 1 + 1;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+	/* Attributes before Wrapped Data (none) */
+	addr[1] = wpabuf_put(msg, 0);
+	len[1] = 0;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+	/* Wrapped Data */
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+	wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+	if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+			    wpabuf_head(clear), wpabuf_len(clear),
+			    2, addr, len, wrapped) < 0)
+		goto fail;
+
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Result attributes", msg);
+	wpabuf_free(clear);
+	return msg;
+fail:
+	wpabuf_free(clear);
+	wpabuf_free(msg);
+	return NULL;
+}
+
+
 void dpp_configurator_free(struct dpp_configurator *conf)
 {
 	if (!conf)
@@ -7691,3 +8235,487 @@
 	goto out;
 }
 #endif /* CONFIG_TESTING_OPTIONS */
+
+
+#ifdef CONFIG_DPP2
+
+struct dpp_pfs * dpp_pfs_init(const u8 *net_access_key,
+			      size_t net_access_key_len)
+{
+	struct wpabuf *pub = NULL;
+	EVP_PKEY *own_key;
+	struct dpp_pfs *pfs;
+
+	pfs = os_zalloc(sizeof(*pfs));
+	if (!pfs)
+		return NULL;
+
+	own_key = dpp_set_keypair(&pfs->curve, net_access_key,
+				  net_access_key_len);
+	if (!own_key) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
+		goto fail;
+	}
+	EVP_PKEY_free(own_key);
+
+	pfs->ecdh = crypto_ecdh_init(pfs->curve->ike_group);
+	if (!pfs->ecdh)
+		goto fail;
+
+	pub = crypto_ecdh_get_pubkey(pfs->ecdh, 0);
+	pub = wpabuf_zeropad(pub, pfs->curve->prime_len);
+	if (!pub)
+		goto fail;
+
+	pfs->ie = wpabuf_alloc(5 + wpabuf_len(pub));
+	if (!pfs->ie)
+		goto fail;
+	wpabuf_put_u8(pfs->ie, WLAN_EID_EXTENSION);
+	wpabuf_put_u8(pfs->ie, 1 + 2 + wpabuf_len(pub));
+	wpabuf_put_u8(pfs->ie, WLAN_EID_EXT_OWE_DH_PARAM);
+	wpabuf_put_le16(pfs->ie, pfs->curve->ike_group);
+	wpabuf_put_buf(pfs->ie, pub);
+	wpabuf_free(pub);
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: Diffie-Hellman Parameter element",
+			pfs->ie);
+
+	return pfs;
+fail:
+	wpabuf_free(pub);
+	dpp_pfs_free(pfs);
+	return NULL;
+}
+
+
+int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len)
+{
+	if (peer_ie_len < 2)
+		return -1;
+	if (WPA_GET_LE16(peer_ie) != pfs->curve->ike_group) {
+		wpa_printf(MSG_DEBUG, "DPP: Peer used different group for PFS");
+		return -1;
+	}
+
+	pfs->secret = crypto_ecdh_set_peerkey(pfs->ecdh, 0, peer_ie + 2,
+					      peer_ie_len - 2);
+	pfs->secret = wpabuf_zeropad(pfs->secret, pfs->curve->prime_len);
+	if (!pfs->secret) {
+		wpa_printf(MSG_DEBUG, "DPP: Invalid peer DH public key");
+		return -1;
+	}
+	wpa_hexdump_buf_key(MSG_DEBUG, "DPP: DH shared secret", pfs->secret);
+	return 0;
+}
+
+
+void dpp_pfs_free(struct dpp_pfs *pfs)
+{
+	if (!pfs)
+		return;
+	crypto_ecdh_deinit(pfs->ecdh);
+	wpabuf_free(pfs->ie);
+	wpabuf_clear_free(pfs->secret);
+	os_free(pfs);
+}
+
+#endif /* CONFIG_DPP2 */
+
+
+static unsigned int dpp_next_id(struct dpp_global *dpp)
+{
+	struct dpp_bootstrap_info *bi;
+	unsigned int max_id = 0;
+
+	dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) {
+		if (bi->id > max_id)
+			max_id = bi->id;
+	}
+	return max_id + 1;
+}
+
+
+static int dpp_bootstrap_del(struct dpp_global *dpp, unsigned int id)
+{
+	struct dpp_bootstrap_info *bi, *tmp;
+	int found = 0;
+
+	if (!dpp)
+		return -1;
+
+	dl_list_for_each_safe(bi, tmp, &dpp->bootstrap,
+			      struct dpp_bootstrap_info, list) {
+		if (id && bi->id != id)
+			continue;
+		found = 1;
+		dl_list_del(&bi->list);
+		dpp_bootstrap_info_free(bi);
+	}
+
+	if (id == 0)
+		return 0; /* flush succeeds regardless of entries found */
+	return found ? 0 : -1;
+}
+
+
+struct dpp_bootstrap_info * dpp_add_qr_code(struct dpp_global *dpp,
+					    const char *uri)
+{
+	struct dpp_bootstrap_info *bi;
+
+	if (!dpp)
+		return NULL;
+
+	bi = dpp_parse_qr_code(uri);
+	if (!bi)
+		return NULL;
+
+	bi->id = dpp_next_id(dpp);
+	dl_list_add(&dpp->bootstrap, &bi->list);
+	return bi;
+}
+
+
+int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd)
+{
+	char *chan = NULL, *mac = NULL, *info = NULL, *pk = NULL, *curve = NULL;
+	char *key = NULL;
+	u8 *privkey = NULL;
+	size_t privkey_len = 0;
+	size_t len;
+	int ret = -1;
+	struct dpp_bootstrap_info *bi;
+
+	if (!dpp)
+		return -1;
+
+	bi = os_zalloc(sizeof(*bi));
+	if (!bi)
+		goto fail;
+
+	if (os_strstr(cmd, "type=qrcode"))
+		bi->type = DPP_BOOTSTRAP_QR_CODE;
+	else if (os_strstr(cmd, "type=pkex"))
+		bi->type = DPP_BOOTSTRAP_PKEX;
+	else
+		goto fail;
+
+	chan = get_param(cmd, " chan=");
+	mac = get_param(cmd, " mac=");
+	info = get_param(cmd, " info=");
+	curve = get_param(cmd, " curve=");
+	key = get_param(cmd, " key=");
+
+	if (key) {
+		privkey_len = os_strlen(key) / 2;
+		privkey = os_malloc(privkey_len);
+		if (!privkey ||
+		    hexstr2bin(key, privkey, privkey_len) < 0)
+			goto fail;
+	}
+
+	pk = dpp_keygen(bi, curve, privkey, privkey_len);
+	if (!pk)
+		goto fail;
+
+	len = 4; /* "DPP:" */
+	if (chan) {
+		if (dpp_parse_uri_chan_list(bi, chan) < 0)
+			goto fail;
+		len += 3 + os_strlen(chan); /* C:...; */
+	}
+	if (mac) {
+		if (dpp_parse_uri_mac(bi, mac) < 0)
+			goto fail;
+		len += 3 + os_strlen(mac); /* M:...; */
+	}
+	if (info) {
+		if (dpp_parse_uri_info(bi, info) < 0)
+			goto fail;
+		len += 3 + os_strlen(info); /* I:...; */
+	}
+	len += 4 + os_strlen(pk);
+	bi->uri = os_malloc(len + 1);
+	if (!bi->uri)
+		goto fail;
+	os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%sK:%s;;",
+		    chan ? "C:" : "", chan ? chan : "", chan ? ";" : "",
+		    mac ? "M:" : "", mac ? mac : "", mac ? ";" : "",
+		    info ? "I:" : "", info ? info : "", info ? ";" : "",
+		    pk);
+	bi->id = dpp_next_id(dpp);
+	dl_list_add(&dpp->bootstrap, &bi->list);
+	ret = bi->id;
+	bi = NULL;
+fail:
+	os_free(curve);
+	os_free(pk);
+	os_free(chan);
+	os_free(mac);
+	os_free(info);
+	str_clear_free(key);
+	bin_clear_free(privkey, privkey_len);
+	dpp_bootstrap_info_free(bi);
+	return ret;
+}
+
+
+struct dpp_bootstrap_info *
+dpp_bootstrap_get_id(struct dpp_global *dpp, unsigned int id)
+{
+	struct dpp_bootstrap_info *bi;
+
+	if (!dpp)
+		return NULL;
+
+	dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) {
+		if (bi->id == id)
+			return bi;
+	}
+	return NULL;
+}
+
+
+int dpp_bootstrap_remove(struct dpp_global *dpp, const char *id)
+{
+	unsigned int id_val;
+
+	if (os_strcmp(id, "*") == 0) {
+		id_val = 0;
+	} else {
+		id_val = atoi(id);
+		if (id_val == 0)
+			return -1;
+	}
+
+	return dpp_bootstrap_del(dpp, id_val);
+}
+
+
+struct dpp_bootstrap_info *
+dpp_pkex_finish(struct dpp_global *dpp, struct dpp_pkex *pkex, const u8 *peer,
+		unsigned int freq)
+{
+	struct dpp_bootstrap_info *bi;
+
+	bi = os_zalloc(sizeof(*bi));
+	if (!bi)
+		return NULL;
+	bi->id = dpp_next_id(dpp);
+	bi->type = DPP_BOOTSTRAP_PKEX;
+	os_memcpy(bi->mac_addr, peer, ETH_ALEN);
+	bi->num_freq = 1;
+	bi->freq[0] = freq;
+	bi->curve = pkex->own_bi->curve;
+	bi->pubkey = pkex->peer_bootstrap_key;
+	pkex->peer_bootstrap_key = NULL;
+	if (dpp_bootstrap_key_hash(bi) < 0) {
+		dpp_bootstrap_info_free(bi);
+		return NULL;
+	}
+	dpp_pkex_free(pkex);
+	dl_list_add(&dpp->bootstrap, &bi->list);
+	return bi;
+}
+
+
+const char * dpp_bootstrap_get_uri(struct dpp_global *dpp, unsigned int id)
+{
+	struct dpp_bootstrap_info *bi;
+
+	bi = dpp_bootstrap_get_id(dpp, id);
+	if (!bi)
+		return NULL;
+	return bi->uri;
+}
+
+
+int dpp_bootstrap_info(struct dpp_global *dpp, int id,
+		       char *reply, int reply_size)
+{
+	struct dpp_bootstrap_info *bi;
+
+	bi = dpp_bootstrap_get_id(dpp, id);
+	if (!bi)
+		return -1;
+	return os_snprintf(reply, reply_size, "type=%s\n"
+			   "mac_addr=" MACSTR "\n"
+			   "info=%s\n"
+			   "num_freq=%u\n"
+			   "curve=%s\n",
+			   dpp_bootstrap_type_txt(bi->type),
+			   MAC2STR(bi->mac_addr),
+			   bi->info ? bi->info : "",
+			   bi->num_freq,
+			   bi->curve->name);
+}
+
+
+void dpp_bootstrap_find_pair(struct dpp_global *dpp, const u8 *i_bootstrap,
+			     const u8 *r_bootstrap,
+			     struct dpp_bootstrap_info **own_bi,
+			     struct dpp_bootstrap_info **peer_bi)
+{
+	struct dpp_bootstrap_info *bi;
+
+	*own_bi = NULL;
+	*peer_bi = NULL;
+	if (!dpp)
+		return;
+
+	dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) {
+		if (!*own_bi && bi->own &&
+		    os_memcmp(bi->pubkey_hash, r_bootstrap,
+			      SHA256_MAC_LEN) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Found matching own bootstrapping information");
+			*own_bi = bi;
+		}
+
+		if (!*peer_bi && !bi->own &&
+		    os_memcmp(bi->pubkey_hash, i_bootstrap,
+			      SHA256_MAC_LEN) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Found matching peer bootstrapping information");
+			*peer_bi = bi;
+		}
+
+		if (*own_bi && *peer_bi)
+			break;
+	}
+
+}
+
+
+static unsigned int dpp_next_configurator_id(struct dpp_global *dpp)
+{
+	struct dpp_configurator *conf;
+	unsigned int max_id = 0;
+
+	dl_list_for_each(conf, &dpp->configurator, struct dpp_configurator,
+			 list) {
+		if (conf->id > max_id)
+			max_id = conf->id;
+	}
+	return max_id + 1;
+}
+
+
+int dpp_configurator_add(struct dpp_global *dpp, const char *cmd)
+{
+	char *curve = NULL;
+	char *key = NULL;
+	u8 *privkey = NULL;
+	size_t privkey_len = 0;
+	int ret = -1;
+	struct dpp_configurator *conf = NULL;
+
+	curve = get_param(cmd, " curve=");
+	key = get_param(cmd, " key=");
+
+	if (key) {
+		privkey_len = os_strlen(key) / 2;
+		privkey = os_malloc(privkey_len);
+		if (!privkey ||
+		    hexstr2bin(key, privkey, privkey_len) < 0)
+			goto fail;
+	}
+
+	conf = dpp_keygen_configurator(curve, privkey, privkey_len);
+	if (!conf)
+		goto fail;
+
+	conf->id = dpp_next_configurator_id(dpp);
+	dl_list_add(&dpp->configurator, &conf->list);
+	ret = conf->id;
+	conf = NULL;
+fail:
+	os_free(curve);
+	str_clear_free(key);
+	bin_clear_free(privkey, privkey_len);
+	dpp_configurator_free(conf);
+	return ret;
+}
+
+
+static int dpp_configurator_del(struct dpp_global *dpp, unsigned int id)
+{
+	struct dpp_configurator *conf, *tmp;
+	int found = 0;
+
+	if (!dpp)
+		return -1;
+
+	dl_list_for_each_safe(conf, tmp, &dpp->configurator,
+			      struct dpp_configurator, list) {
+		if (id && conf->id != id)
+			continue;
+		found = 1;
+		dl_list_del(&conf->list);
+		dpp_configurator_free(conf);
+	}
+
+	if (id == 0)
+		return 0; /* flush succeeds regardless of entries found */
+	return found ? 0 : -1;
+}
+
+
+int dpp_configurator_remove(struct dpp_global *dpp, const char *id)
+{
+	unsigned int id_val;
+
+	if (os_strcmp(id, "*") == 0) {
+		id_val = 0;
+	} else {
+		id_val = atoi(id);
+		if (id_val == 0)
+			return -1;
+	}
+
+	return dpp_configurator_del(dpp, id_val);
+}
+
+
+int dpp_configurator_get_key_id(struct dpp_global *dpp, unsigned int id,
+				char *buf, size_t buflen)
+{
+	struct dpp_configurator *conf;
+
+	conf = dpp_configurator_get_id(dpp, id);
+	if (!conf)
+		return -1;
+
+	return dpp_configurator_get_key(conf, buf, buflen);
+}
+
+
+struct dpp_global * dpp_global_init(void)
+{
+	struct dpp_global *dpp;
+
+	dpp = os_zalloc(sizeof(*dpp));
+	if (!dpp)
+		return NULL;
+
+	dl_list_init(&dpp->bootstrap);
+	dl_list_init(&dpp->configurator);
+
+	return dpp;
+}
+
+
+void dpp_global_clear(struct dpp_global *dpp)
+{
+	if (!dpp)
+		return;
+
+	dpp_bootstrap_del(dpp, 0);
+	dpp_configurator_del(dpp, 0);
+}
+
+
+void dpp_global_deinit(struct dpp_global *dpp)
+{
+	dpp_global_clear(dpp);
+	os_free(dpp);
+}
diff --git a/src/common/dpp.h b/src/common/dpp.h
index 2575908..5a6d8cc 100644
--- a/src/common/dpp.h
+++ b/src/common/dpp.h
@@ -1,6 +1,7 @@
 /*
  * DPP functionality shared between hostapd and wpa_supplicant
  * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2019, The Linux Foundation
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -9,12 +10,16 @@
 #ifndef DPP_H
 #define DPP_H
 
+#ifdef CONFIG_DPP
 #include <openssl/x509.h>
 
 #include "utils/list.h"
 #include "common/wpa_common.h"
 #include "crypto/sha256.h"
 
+struct crypto_ecdh;
+struct dpp_global;
+
 #define DPP_HDR_LEN (4 + 2) /* OUI, OUI Type, Crypto Suite, DPP frame type */
 
 enum dpp_public_action_frame_type {
@@ -27,6 +32,7 @@
 	DPP_PA_PKEX_EXCHANGE_RESP = 8,
 	DPP_PA_PKEX_COMMIT_REVEAL_REQ = 9,
 	DPP_PA_PKEX_COMMIT_REVEAL_RESP = 10,
+	DPP_PA_CONFIGURATION_RESULT = 11,
 };
 
 enum dpp_attribute_id {
@@ -54,6 +60,8 @@
 	DPP_ATTR_TRANSACTION_ID = 0x1016,
 	DPP_ATTR_BOOTSTRAP_INFO = 0x1017,
 	DPP_ATTR_CHANNEL = 0x1018,
+	DPP_ATTR_PROTOCOL_VERSION = 0x1019,
+	DPP_ATTR_ENVELOPED_DATA = 0x101A,
 };
 
 enum dpp_status_error {
@@ -66,6 +74,7 @@
 	DPP_STATUS_RESPONSE_PENDING = 6,
 	DPP_STATUS_INVALID_CONNECTOR = 7,
 	DPP_STATUS_NO_MATCH = 8,
+	DPP_STATUS_CONFIG_REJECTED = 9,
 };
 
 #define DPP_CAPAB_ENROLLEE BIT(0)
@@ -141,7 +150,9 @@
 	DPP_AKM_DPP,
 	DPP_AKM_PSK,
 	DPP_AKM_SAE,
-	DPP_AKM_PSK_SAE
+	DPP_AKM_PSK_SAE,
+	DPP_AKM_SAE_DPP,
+	DPP_AKM_PSK_SAE_DPP,
 };
 
 struct dpp_configuration {
@@ -158,10 +169,12 @@
 	/* For legacy configuration */
 	char *passphrase;
 	u8 psk[32];
+	int psk_set;
 };
 
 struct dpp_authentication {
 	void *msg_ctx;
+	u8 peer_version;
 	const struct dpp_curve_params *curve;
 	struct dpp_bootstrap_info *peer_bi;
 	struct dpp_bootstrap_info *own_bi;
@@ -169,6 +182,7 @@
 	u8 waiting_pubkey_hash[SHA256_MAC_LEN];
 	int response_pending;
 	enum dpp_status_error auth_resp_status;
+	enum dpp_status_error conf_resp_status;
 	u8 peer_mac_addr[ETH_ALEN];
 	u8 i_nonce[DPP_MAX_NONCE_LEN];
 	u8 r_nonce[DPP_MAX_NONCE_LEN];
@@ -204,6 +218,8 @@
 	u8 allowed_roles;
 	int configurator;
 	int remove_on_tx_status;
+	int connect_on_tx_status;
+	int waiting_conf_result;
 	int auth_success;
 	struct wpabuf *conf_req;
 	const struct wpabuf *conf_resp; /* owned by GAS server */
@@ -336,6 +352,7 @@
 	DPP_TEST_STOP_AT_AUTH_RESP = 88,
 	DPP_TEST_STOP_AT_AUTH_CONF = 89,
 	DPP_TEST_STOP_AT_CONF_REQ = 90,
+	DPP_TEST_REJECT_CONFIG = 91,
 };
 
 extern enum dpp_test_behavior dpp_test;
@@ -382,13 +399,28 @@
 		     const u8 *attr_start, size_t attr_len);
 int dpp_notify_new_qr_code(struct dpp_authentication *auth,
 			   struct dpp_bootstrap_info *peer_bi);
+struct dpp_configuration * dpp_configuration_alloc(const char *type);
+int dpp_akm_psk(enum dpp_akm akm);
+int dpp_akm_sae(enum dpp_akm akm);
+int dpp_akm_legacy(enum dpp_akm akm);
+int dpp_akm_dpp(enum dpp_akm akm);
+int dpp_akm_ver2(enum dpp_akm akm);
+int dpp_configuration_valid(const struct dpp_configuration *conf);
 void dpp_configuration_free(struct dpp_configuration *conf);
+int dpp_set_configurator(struct dpp_global *dpp, void *msg_ctx,
+			 struct dpp_authentication *auth,
+			 const char *cmd);
 void dpp_auth_deinit(struct dpp_authentication *auth);
 struct wpabuf *
 dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
 		size_t attr_len);
 int dpp_conf_resp_rx(struct dpp_authentication *auth,
 		     const struct wpabuf *resp);
+enum dpp_status_error dpp_conf_result_rx(struct dpp_authentication *auth,
+					 const u8 *hdr,
+					 const u8 *attr_start, size_t attr_len);
+struct wpabuf * dpp_build_conf_result(struct dpp_authentication *auth,
+				      enum dpp_status_error status);
 struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type,
 			      size_t len);
 const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len);
@@ -432,4 +464,42 @@
 
 char * dpp_corrupt_connector_signature(const char *connector);
 
+
+struct dpp_pfs {
+	struct crypto_ecdh *ecdh;
+	const struct dpp_curve_params *curve;
+	struct wpabuf *ie;
+	struct wpabuf *secret;
+};
+
+struct dpp_pfs * dpp_pfs_init(const u8 *net_access_key,
+			      size_t net_access_key_len);
+int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len);
+void dpp_pfs_free(struct dpp_pfs *pfs);
+
+struct dpp_bootstrap_info * dpp_add_qr_code(struct dpp_global *dpp,
+					    const char *uri);
+int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd);
+struct dpp_bootstrap_info *
+dpp_bootstrap_get_id(struct dpp_global *dpp, unsigned int id);
+int dpp_bootstrap_remove(struct dpp_global *dpp, const char *id);
+struct dpp_bootstrap_info *
+dpp_pkex_finish(struct dpp_global *dpp, struct dpp_pkex *pkex, const u8 *peer,
+		unsigned int freq);
+const char * dpp_bootstrap_get_uri(struct dpp_global *dpp, unsigned int id);
+int dpp_bootstrap_info(struct dpp_global *dpp, int id,
+		       char *reply, int reply_size);
+void dpp_bootstrap_find_pair(struct dpp_global *dpp, const u8 *i_bootstrap,
+			     const u8 *r_bootstrap,
+			     struct dpp_bootstrap_info **own_bi,
+			     struct dpp_bootstrap_info **peer_bi);
+int dpp_configurator_add(struct dpp_global *dpp, const char *cmd);
+int dpp_configurator_remove(struct dpp_global *dpp, const char *id);
+int dpp_configurator_get_key_id(struct dpp_global *dpp, unsigned int id,
+				char *buf, size_t buflen);
+struct dpp_global * dpp_global_init(void);
+void dpp_global_clear(struct dpp_global *dpp);
+void dpp_global_deinit(struct dpp_global *dpp);
+
+#endif /* CONFIG_DPP */
 #endif /* DPP_H */
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index 51e4e0c..f886551 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -270,6 +270,10 @@
 		elems->password_id = pos;
 		elems->password_id_len = elen;
 		break;
+	case WLAN_EID_EXT_HE_CAPABILITIES:
+		elems->he_capabilities = pos;
+		elems->he_capabilities_len = elen;
+		break;
 	case WLAN_EID_EXT_OCV_OCI:
 		elems->oci = pos;
 		elems->oci_len = elen;
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 4e35b79..d41bd39 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -93,6 +93,7 @@
 	const u8 *password_id;
 	const u8 *oci;
 	const u8 *multi_ap;
+	const u8 *he_capabilities;
 
 	u8 ssid_len;
 	u8 supp_rates_len;
@@ -141,6 +142,7 @@
 	u8 password_id_len;
 	u8 oci_len;
 	u8 multi_ap_len;
+	u8 he_capabilities_len;
 
 	struct mb_ies_info mb_ies;
 };
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index b285d13..adaa893 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -334,7 +334,7 @@
 #define WLAN_EID_LOCATION_PARAMETERS 82
 #define WLAN_EID_NONTRANSMITTED_BSSID_CAPA 83
 #define WLAN_EID_SSID_LIST 84
-#define WLAN_EID_MLTIPLE_BSSID_INDEX 85
+#define WLAN_EID_MULTIPLE_BSSID_INDEX 85
 #define WLAN_EID_FMS_DESCRIPTOR 86
 #define WLAN_EID_FMS_REQUEST 87
 #define WLAN_EID_FMS_RESPONSE 88
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index 6f5b87e..c34a3bc 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -507,6 +507,23 @@
  * @QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS: This command is used to query
  *	the supported AKM suite selectorss from the driver. It returns the list
  *	of supported AKMs in the attribute NL80211_ATTR_AKM_SUITES.
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE: This command is used to get firmware
+ *	state from the driver. It returns the firmware state in the attribute
+ *	QCA_WLAN_VENDOR_ATTR_FW_STATE.
+ * @QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH: This vendor subcommand
+ *	is used by the driver to flush per-peer cached statistics to user space
+ *	application. This interface is used as an event from the driver to
+ *	user space application. Attributes for this event are specified in
+ *	enum qca_wlan_vendor_attr_peer_stats_cache_params.
+ *	QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA attribute is expected to be
+ *	sent in the event.
+ * @QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG: This sub command is used to
+ *	improve the success rate of Zigbee joining network.
+ *	Due to PTA master limitation, Zigbee joining network success rate is
+ *	low while WLAN is working. The WLAN driver needs to configure some
+ *	parameters including Zigbee state and specific WLAN periods to enhance
+ *	PTA master. All these parameters are delivered by the attributes
+ *	defined in enum qca_mpta_helper_vendor_attr.
  */
 enum qca_nl80211_vendor_subcmds {
 	QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -672,6 +689,9 @@
 	QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT = 174,
 	QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG = 175,
 	QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS = 176,
+	QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE = 177,
+	QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH = 178,
+	QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG = 179,
 };
 
 enum qca_wlan_vendor_attr {
@@ -858,6 +878,12 @@
 	 * for each antenna ID, and application extract them in user space.
 	 */
 	QCA_WLAN_VENDOR_ATTR_CHAIN_EVM = 41,
+	/*
+	 * Used in QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE command to report
+	 * wlan firmware current state. FW state is an unsigned 8 bit value,
+	 * one of the values in enum qca_wlan_vendor_attr_fw_state.
+	 */
+	QCA_WLAN_VENDOR_ATTR_FW_STATE = 42,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
@@ -869,6 +895,49 @@
 	QCA_ROAMING_ALLOWED_WITHIN_ESS,
 };
 
+/**
+ * enum qca_roam_reason - Represents the reason codes for roaming. Used by
+ * QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REASON.
+ *
+ * @QCA_ROAM_REASON_UNKNOWN: Any reason that do not classify under the below
+ * reasons.
+ *
+ * @QCA_ROAM_REASON_PER: Roam triggered when packet error rates (PER) breached
+ * the configured threshold.
+ *
+ * @QCA_ROAM_REASON_BEACON_MISS: Roam triggered due to the continuous configured
+ * beacon misses from the then connected AP.
+ *
+ * @QCA_ROAM_REASON_POOR_RSSI: Roam triggered due to the poor RSSI reported
+ * by the connected AP.
+ *
+ * @QCA_ROAM_REASON_BETTER_RSSI: Roam triggered for finding a BSS with a better
+ * RSSI than the connected BSS. Here the RSSI of the current BSS is not poor.
+ *
+ * @QCA_ROAM_REASON_CONGESTION: Roam triggered considering the connected channel
+ * or environment being very noisy or congested.
+ *
+ * @QCA_ROAM_REASON_EXPLICIT_REQUEST: Roam triggered due to an explicit request
+ * from the user (user space).
+ *
+ * @QCA_ROAM_REASON_BTM: Roam triggered due to BTM Request frame received from
+ * the connected AP.
+ *
+ * @QCA_ROAM_REASON_BSS_LOAD: Roam triggered due to the channel utilization
+ * breaching out the configured threshold.
+ */
+enum qca_roam_reason {
+	QCA_ROAM_REASON_UNKNOWN,
+	QCA_ROAM_REASON_PER,
+	QCA_ROAM_REASON_BEACON_MISS,
+	QCA_ROAM_REASON_POOR_RSSI,
+	QCA_ROAM_REASON_BETTER_RSSI,
+	QCA_ROAM_REASON_CONGESTION,
+	QCA_ROAM_REASON_USER_TRIGGER,
+	QCA_ROAM_REASON_BTM,
+	QCA_ROAM_REASON_BSS_LOAD,
+};
+
 enum qca_wlan_vendor_attr_roam_auth {
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_INVALID = 0,
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID,
@@ -911,6 +980,11 @@
 	 * doing subsequent ERP based connections in the same realm.
 	 */
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM = 13,
+	/* A 16-bit unsigned value representing the reasons for the roaming.
+	 * Defined by enum qca_roam_reason.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REASON = 14,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX =
@@ -1009,6 +1083,7 @@
  *	only OCE STA-CFON functionalities.
  * @QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY: Device supports self
  *	managed regulatory.
+ * @QCA_WLAN_VENDOR_FEATURE_TWT: Device supports TWT (Target Wake Time).
  * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
  */
 enum qca_wlan_vendor_features {
@@ -1020,6 +1095,7 @@
 	QCA_WLAN_VENDOR_FEATURE_OCE_AP                  = 5,
 	QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON            = 6,
 	QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY = 7,
+	QCA_WLAN_VENDOR_FEATURE_TWT 			= 8,
 	NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
 };
 
@@ -2453,6 +2529,18 @@
 };
 
 /**
+ * enum qca_wlan_vendor_attr_fw_state - State of firmware
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_STATE_ERROR: FW is in bad state
+ * @QCA_WLAN_VENDOR_ATTR_FW_STATE_ACTIVE: FW is active
+ */
+enum qca_wlan_vendor_attr_fw_state {
+	QCA_WLAN_VENDOR_ATTR_FW_STATE_ERROR,
+	QCA_WLAN_VENDOR_ATTR_FW_STATE_ACTIVE,
+	QCA_WLAN_VENDOR_ATTR_FW_STATE_MAX
+};
+
+/**
  * BRP antenna limit mode
  *
  * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_DISABLE: Disable BRP force
@@ -3196,6 +3284,8 @@
 	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS = 18,
 	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID = 19,
 	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID = 20,
+	/* Flag attribute indicates this BSSID blacklist as a hint */
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_HINT = 21,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_AFTER_LAST,
@@ -4886,6 +4976,10 @@
 	QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_DST_MAC_ADDR,
 	/* Unsigned 32-bit value, in milli seconds */
 	QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_PERIOD,
+	/* This optional unsigned 16-bit attribute is used for specifying
+	 * ethernet protocol type. If not specified ethertype defaults to IPv4.
+	 */
+	QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_ETHER_PROTO_TYPE,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_AFTER_LAST,
@@ -5530,6 +5624,16 @@
  * @QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS: Mandatory 8-bit unsigned value
  * indicates the maximum number of space-time streams, NSTS, that
  * the STA supports in transmission and is set to NSTS - 1.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE: 8-bit unsigned value
+ * combined with the UL MU Disable subfield and the recipient's setting
+ * of the OM Control UL MU Data Disable RX Support subfield in the HE MAC
+ * capabilities to determine which HE TB PPDUs are possible by the
+ * STA to transmit.
+ * 0 - UL MU data operations are enabled by the STA.
+ * 1 - Determine which HE TB PPDU types are allowed by the STA if UL MU disable
+ * bit is not set, else UL MU Tx is suspended.
+ *
  */
 enum qca_wlan_vendor_attr_he_omi_tx {
 	QCA_WLAN_VENDOR_ATTR_HE_OMI_INVALID = 0,
@@ -5537,6 +5641,7 @@
 	QCA_WLAN_VENDOR_ATTR_HE_OMI_CH_BW = 2,
 	QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DISABLE = 3,
 	QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS = 4,
+	QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE = 5,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_HE_OMI_AFTER_LAST,
@@ -5808,6 +5913,25 @@
 	 */
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_HTC_HE_SUPP = 34,
 
+	/* 8-bit unsigned value to configure VHT support in 2.4G band.
+	 * This attribute is used to configure the testbed device.
+	 * 1-enable, 0-disable
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_2G_VHT = 35,
+
+	/* 8-bit unsigned value to configure HE testbed defaults.
+	 * This attribute is used to configure the testbed device.
+	 * 1-set the device HE capabilities to testbed defaults.
+	 * 0-reset the device HE capabilities to supported config.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SET_HE_TESTBED_DEFAULTS = 36,
+
+	/* 8-bit unsigned value to configure TWT request support.
+	 * This attribute is used to configure the testbed device.
+	 * 1-enable, 0-disable.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TWT_REQ_SUPPORT = 37,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX =
@@ -6290,13 +6414,35 @@
 	QCA_WIFI_SAP_CLASS_3_MGMT = 7,
 	QCA_WIFI_SAP_DATA = 8,
 	QCA_WIFI_SAP_ALL = 9,
+	QCA_WIFI_CASE_MAX = 31,
 	/* 32 - 63 corresponds to BT */
 	QCA_BT_A2DP = 32,
 	QCA_BT_BLE = 33,
 	QCA_BT_SCO = 34,
+	QCA_BT_CASE_MAX = 63,
 	/* 64 - 95 corresponds to Zigbee */
 	QCA_ZB_LOW = 64,
-	QCA_ZB_HIGH = 65
+	QCA_ZB_HIGH = 65,
+	QCA_ZB_CASE_MAX = 95,
+	/* 0xff is default value if the u8 profile value is not set. */
+	QCA_COEX_CONFIG_PROFILE_DEFAULT_VALUE = 255
+};
+
+/**
+ * enum qca_vendor_attr_coex_config_types - Coex configurations types.
+ * This enum defines the valid set of values of coex configuration types. These
+ * values may used by attribute
+ * %QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_RESET: Reset all the
+ *	weights to default values.
+ * @QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_START: Start to config
+ *	weights with configurability value.
+ */
+enum qca_vendor_attr_coex_config_types {
+	QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_RESET = 1,
+	QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_START = 2,
 };
 
 /**
@@ -6328,6 +6474,80 @@
 };
 
 /**
+ * enum qca_vendor_attr_coex_config_three_way - Specifies vendor coex config
+ * attributes
+ * Attributes for data used by QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG
+ *
+ * QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE: u32 attribute.
+ * Indicate config type.
+ * The config types are 32-bit values from qca_vendor_attr_coex_config_types
+ *
+ * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_1: u32 attribute.
+ *	Indicate the Priority 1 profiles.
+ *	The profiles are 8-bit values from enum qca_coex_config_profiles.
+ *	In same priority level, maximum to 4 profiles can be set here.
+ * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_2: u32 attribute.
+ *	Indicate the Priority 2 profiles.
+ *	The profiles are 8-bit values from enum qca_coex_config_profiles.
+ *	In same priority level, maximum to 4 profiles can be set here.
+ * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_3: u32 attribute.
+ *	Indicate the Priority 3 profiles.
+ *	The profiles are 8-bit values from enum qca_coex_config_profiles.
+ *	In same priority level, maximum to 4 profiles can be set here.
+ * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_4: u32 attribute.
+ *	Indicate the Priority 4 profiles.
+ *	The profiles are 8-bit values from enum qca_coex_config_profiles.
+ *	In same priority level, maximum to 4 profiles can be set here.
+ * NOTE:
+ * Limitations for QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_x priority
+ * arrangement:
+ *	1: In the same u32 attribute (priority x), the profiles enum values own
+ *	same priority level.
+ *	2: 0xff is default value if the u8 profile value is not set.
+ *	3: max to 4 rules/profiles in same priority level.
+ *	4: max to 4 priority level (priority 1 - priority 4)
+ *	5: one priority level only supports one scenario from WLAN/BT/ZB,
+ *	hybrid rules not support.
+ *	6: if WMI_COEX_CONFIG_THREE_WAY_COEX_RESET called, priority x will
+ *	remain blank to reset all parameters.
+ * For example:
+ *
+ *	If the attributes as follow:
+ *	priority 1:
+ *	------------------------------------
+ *	|  0xff  |    0   |   1   |    2   |
+ *	------------------------------------
+ *	priority 2:
+ *	-------------------------------------
+ *	|  0xff  |  0xff  |  0xff  |   32   |
+ *	-------------------------------------
+ *	priority 3:
+ *	-------------------------------------
+ *	|  0xff  |  0xff  |  0xff  |   65   |
+ *	-------------------------------------
+ *	then it means:
+ *	1: WIFI_STA_DISCOVERY, WIFI_STA_CLASS_3_MGMT and WIFI_STA_CONNECTION
+ *		owns same priority level.
+ *	2: WIFI_STA_DISCOVERY, WIFI_STA_CLASS_3_MGMT and WIFI_STA_CONNECTION
+ *		has priority over BT_A2DP and ZB_HIGH.
+ *	3: BT_A2DP has priority over ZB_HIGH.
+ */
+
+enum qca_vendor_attr_coex_config_three_way {
+	QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_INVALID = 0,
+	QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE = 1,
+	QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_1 = 2,
+	QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_2 = 3,
+	QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_3 = 4,
+	QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_4 = 5,
+
+	/* Keep last */
+	QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_AFTER_LAST,
+	QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_MAX =
+	QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_AFTER_LAST - 1,
+};
+
+/**
  * enum qca_wlan_vendor_attr_link_properties - Represent the link properties.
  *
  * @QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR: MAC address of the peer
@@ -6349,4 +6569,144 @@
 	QCA_VENDOR_ATTR_LINK_PROPERTIES_AFTER_LAST - 1,
 };
 
+/**
+ * enum qca_vendor_attr_peer_stats_cache_type - Represents peer stats cache type
+ * This enum defines the valid set of values of peer stats cache types. These
+ * values are used by attribute
+ * %QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_TX_RATE_STATS: Represents peer TX rate statistics
+ * @QCA_WLAN_VENDOR_ATTR_PEER_RX_RATE_STATS: Represents peer RX rate statistics
+ * @QCA_WLAN_VENDOR_ATTR_PEER_TX_SOJOURN_STATS: Represents peer TX sojourn
+ * statistics
+ */
+enum qca_vendor_attr_peer_stats_cache_type {
+	QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE_INVALID = 0,
+
+	QCA_WLAN_VENDOR_ATTR_PEER_TX_RATE_STATS,
+	QCA_WLAN_VENDOR_ATTR_PEER_RX_RATE_STATS,
+	QCA_WLAN_VENDOR_ATTR_PEER_TX_SOJOURN_STATS,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_peer_stats_cache_params - This enum defines
+ * attributes required for QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH
+ * Information in these attributes is used to flush peer rate statistics from
+ * the driver to user application.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE: Unsigned 32-bit attribute
+ * Indicate peer statistics cache type.
+ * The statistics types are 32-bit values from
+ * enum qca_vendor_attr_peer_stats_cache_type.
+ * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_MAC: Unsigned 8-bit array
+ * of size 6 octets, representing the peer MAC address.
+ * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA: Opaque data attribute
+ * containing buffer of statistics to send to application layer entity.
+ * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_COOKIE: Unsigned 64-bit attribute
+ * representing a cookie for peer unique session.
+ */
+enum qca_wlan_vendor_attr_peer_stats_cache_params {
+	QCA_WLAN_VENDOR_ATTR_PEER_STATS_INVALID = 0,
+
+	QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE = 1,
+	QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_MAC = 2,
+	QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA = 3,
+	QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_COOKIE = 4,
+
+	/* Keep last */
+	QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_LAST,
+	QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_MAX =
+		QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_LAST - 1
+};
+
+/**
+ * enum qca_mpta_helper_attr_zigbee_state - Current Zigbee state
+ * This enum defines all the possible states of Zigbee, which can be
+ * delivered in the QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_STATE attribute.
+ *
+ * @ZIGBEE_IDLE: Zigbee in idle state
+ * @ZIGBEE_FORM_NETWORK: Zigbee forming network
+ * @ZIGBEE_WAIT_JOIN: Zigbee waiting for joining network
+ * @ZIGBEE_JOIN: Zigbee joining network
+ * @ZIGBEE_NETWORK_UP: Zigbee network is up
+ * @ZIGBEE_HMI: Zigbee in HMI mode
+ */
+enum qca_mpta_helper_attr_zigbee_state {
+	ZIGBEE_IDLE = 0,
+	ZIGBEE_FORM_NETWORK = 1,
+	ZIGBEE_WAIT_JOIN = 2,
+	ZIGBEE_JOIN = 3,
+	ZIGBEE_NETWORK_UP = 4,
+	ZIGBEE_HMI = 5,
+};
+
+/*
+ * enum qca_mpta_helper_vendor_attr - Attributes used in vendor sub-command
+ * QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG.
+ */
+enum qca_mpta_helper_vendor_attr {
+	QCA_MPTA_HELPER_VENDOR_ATTR_INVALID = 0,
+	/* Optional attribute used to update Zigbee state.
+	 * enum qca_mpta_helper_attr_zigbee_state.
+	 * NLA_U32 attribute.
+	 */
+	QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_STATE = 1,
+	/* Optional attribute used to configure WLAN duration for Shape-OCS
+	 * during interrupt.
+	 * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_NON_WLAN_DURATION.
+	 * Value range 0 ~ 300 (ms).
+	 * NLA_U32 attribute.
+	 */
+	QCA_MPTA_HELPER_VENDOR_ATTR_INT_WLAN_DURATION = 2,
+	/* Optional attribute used to configure non-WLAN duration for Shape-OCS
+	 * during interrupt.
+	 * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_WLAN_DURATION.
+	 * Value range 0 ~ 300 (ms).
+	 * NLA_U32 attribute.
+	 */
+	QCA_MPTA_HELPER_VENDOR_ATTR_INT_NON_WLAN_DURATION  = 3,
+	/* Optional attribute used to configure WLAN duration for Shape-OCS
+	 * monitor period.
+	 * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_NON_WLAN_DURATION.
+	 * Value range 0 ~ 300 (ms)
+	 * NLA_U32 attribute
+	 */
+	QCA_MPTA_HELPER_VENDOR_ATTR_MON_WLAN_DURATION = 4,
+	/* Optional attribute used to configure non-WLAN duration for Shape-OCS
+	 * monitor period.
+	 * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_WLAN_DURATION.
+	 * Value range 0 ~ 300 (ms)
+	 * NLA_U32 attribute
+	 */
+	QCA_MPTA_HELPER_VENDOR_ATTR_MON_NON_WLAN_DURATION  = 5,
+	/* Optional attribute used to configure OCS interrupt duration.
+	 * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_OCS_DURATION.
+	 * Value range 1000 ~ 20000 (ms)
+	 * NLA_U32 attribute
+	 */
+	QCA_MPTA_HELPER_VENDOR_ATTR_INT_OCS_DURATION  = 6,
+	/* Optional attribute used to configure OCS monitor duration.
+	 * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_OCS_DURATION.
+	 * Value range 1000 ~ 20000 (ms)
+	 * NLA_U32 attribute
+	 */
+	QCA_MPTA_HELPER_VENDOR_ATTR_MON_OCS_DURATION  = 7,
+	/* Optional attribute used to notify WLAN firmware the current Zigbee
+	 * channel.
+	 * Value range 11 ~ 26
+	 * NLA_U32 attribute
+	 */
+	QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_CHAN = 8,
+	/* Optional attribute used to configure WLAN mute duration.
+	 * Value range 0 ~ 400 (ms)
+	 * NLA_U32 attribute
+	 */
+	QCA_MPTA_HELPER_VENDOR_ATTR_WLAN_MUTE_DURATION	= 9,
+
+	/* keep last */
+	QCA_MPTA_HELPER_VENDOR_ATTR_AFTER_LAST,
+	QCA_MPTA_HELPER_VENDOR_ATTR_MAX =
+		QCA_MPTA_HELPER_VENDOR_ATTR_AFTER_LAST - 1
+};
+
 #endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index 981e788..5a50294 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -9,6 +9,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "utils/const_time.h"
 #include "crypto/crypto.h"
 #include "crypto/sha256.h"
 #include "crypto/random.h"
@@ -17,10 +18,33 @@
 #include "sae.h"
 
 
+static int sae_suitable_group(int group)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	/* Allow all groups for testing purposes in non-production builds. */
+	return 1;
+#else /* CONFIG_TESTING_OPTIONS */
+	/* Enforce REVmd rules on which SAE groups are suitable for production
+	 * purposes: FFC groups whose prime is >= 3072 bits and ECC groups
+	 * defined over a prime field whose prime is >= 256 bits. Furthermore,
+	 * ECC groups defined over a characteristic 2 finite field and ECC
+	 * groups with a co-factor greater than 1 are not suitable. */
+	return group == 19 || group == 20 || group == 21 ||
+		group == 28 || group == 29 || group == 30 ||
+		group == 15 || group == 16 || group == 17 || group == 18;
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
 int sae_set_group(struct sae_data *sae, int group)
 {
 	struct sae_temporary_data *tmp;
 
+	if (!sae_suitable_group(group)) {
+		wpa_printf(MSG_DEBUG, "SAE: Reject unsuitable group %d", group);
+		return -1;
+	}
+
 	sae_clear_data(sae);
 	tmp = sae->tmp = os_zalloc(sizeof(*tmp));
 	if (tmp == NULL)
@@ -208,12 +232,14 @@
 
 static int is_quadratic_residue_blind(struct sae_data *sae,
 				      const u8 *prime, size_t bits,
-				      const struct crypto_bignum *qr,
-				      const struct crypto_bignum *qnr,
+				      const u8 *qr, const u8 *qnr,
 				      const struct crypto_bignum *y_sqr)
 {
-	struct crypto_bignum *r, *num;
+	struct crypto_bignum *r, *num, *qr_or_qnr = NULL;
 	int r_odd, check, res = -1;
+	u8 qr_or_qnr_bin[SAE_MAX_ECC_PRIME_LEN];
+	size_t prime_len = sae->tmp->prime_len;
+	unsigned int mask;
 
 	/*
 	 * Use the blinding technique to mask y_sqr while determining
@@ -224,7 +250,7 @@
 	 * r = a random number between 1 and p-1, inclusive
 	 * num = (v * r * r) modulo p
 	 */
-	r = get_rand_1_to_p_1(prime, sae->tmp->prime_len, bits, &r_odd);
+	r = get_rand_1_to_p_1(prime, prime_len, bits, &r_odd);
 	if (!r)
 		return -1;
 
@@ -234,50 +260,51 @@
 	    crypto_bignum_mulmod(num, r, sae->tmp->prime, num) < 0)
 		goto fail;
 
-	if (r_odd) {
-		/*
-		 * num = (num * qr) module p
-		 * LGR(num, p) = 1 ==> quadratic residue
-		 */
-		if (crypto_bignum_mulmod(num, qr, sae->tmp->prime, num) < 0)
-			goto fail;
-		check = 1;
-	} else {
-		/*
-		 * num = (num * qnr) module p
-		 * LGR(num, p) = -1 ==> quadratic residue
-		 */
-		if (crypto_bignum_mulmod(num, qnr, sae->tmp->prime, num) < 0)
-			goto fail;
-		check = -1;
-	}
+	/*
+	 * Need to minimize differences in handling different cases, so try to
+	 * avoid branches and timing differences.
+	 *
+	 * If r_odd:
+	 * num = (num * qr) module p
+	 * LGR(num, p) = 1 ==> quadratic residue
+	 * else:
+	 * num = (num * qnr) module p
+	 * LGR(num, p) = -1 ==> quadratic residue
+	 */
+	mask = const_time_is_zero(r_odd);
+	const_time_select_bin(mask, qnr, qr, prime_len, qr_or_qnr_bin);
+	qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, prime_len);
+	if (!qr_or_qnr ||
+	    crypto_bignum_mulmod(num, qr_or_qnr, sae->tmp->prime, num) < 0)
+		goto fail;
+	/* r_odd is 0 or 1; branchless version of check = r_odd ? 1 : -1, */
+	check = const_time_select_int(mask, -1, 1);
 
 	res = crypto_bignum_legendre(num, sae->tmp->prime);
 	if (res == -2) {
 		res = -1;
 		goto fail;
 	}
-	res = res == check;
+	/* branchless version of res = res == check
+	 * (res is -1, 0, or 1; check is -1 or 1) */
+	mask = const_time_eq(res, check);
+	res = const_time_select_int(mask, 1, 0);
 fail:
 	crypto_bignum_deinit(num, 1);
 	crypto_bignum_deinit(r, 1);
+	crypto_bignum_deinit(qr_or_qnr, 1);
 	return res;
 }
 
 
 static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
-				 const u8 *prime,
-				 const struct crypto_bignum *qr,
-				 const struct crypto_bignum *qnr,
-				 struct crypto_bignum **ret_x_cand)
+				 const u8 *prime, const u8 *qr, const u8 *qnr,
+				 u8 *pwd_value)
 {
-	u8 pwd_value[SAE_MAX_ECC_PRIME_LEN];
 	struct crypto_bignum *y_sqr, *x_cand;
 	int res;
 	size_t bits;
 
-	*ret_x_cand = NULL;
-
 	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
 
 	/* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
@@ -286,7 +313,7 @@
 			    prime, sae->tmp->prime_len, pwd_value, bits) < 0)
 		return -1;
 	if (bits % 8)
-		buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8);
+		buf_shift_right(pwd_value, sae->tmp->prime_len, 8 - bits % 8);
 	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
 			pwd_value, sae->tmp->prime_len);
 
@@ -297,31 +324,27 @@
 	if (!x_cand)
 		return -1;
 	y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand);
-	if (!y_sqr) {
-		crypto_bignum_deinit(x_cand, 1);
+	crypto_bignum_deinit(x_cand, 1);
+	if (!y_sqr)
 		return -1;
-	}
 
 	res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr);
 	crypto_bignum_deinit(y_sqr, 1);
-	if (res <= 0) {
-		crypto_bignum_deinit(x_cand, 1);
-		return res;
-	}
-
-	*ret_x_cand = x_cand;
-	return 1;
+	return res;
 }
 
 
+/* Returns -1 on fatal failure, 0 if PWE cannot be derived from the provided
+ * pwd-seed, or 1 if a valid PWE was derived from pwd-seed. */
 static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
 				 struct crypto_bignum *pwe)
 {
 	u8 pwd_value[SAE_MAX_PRIME_LEN];
 	size_t bits = sae->tmp->prime_len * 8;
 	u8 exp[1];
-	struct crypto_bignum *a, *b;
-	int res;
+	struct crypto_bignum *a, *b = NULL;
+	int res, is_val;
+	u8 pwd_value_valid;
 
 	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
 
@@ -333,16 +356,29 @@
 	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value,
 			sae->tmp->prime_len);
 
-	if (os_memcmp(pwd_value, sae->tmp->dh->prime, sae->tmp->prime_len) >= 0)
-	{
-		wpa_printf(MSG_DEBUG, "SAE: pwd-value >= p");
-		return 0;
-	}
+	/* Check whether pwd-value < p */
+	res = const_time_memcmp(pwd_value, sae->tmp->dh->prime,
+				sae->tmp->prime_len);
+	/* pwd-value >= p is invalid, so res is < 0 for the valid cases and
+	 * the negative sign can be used to fill the mask for constant time
+	 * selection */
+	pwd_value_valid = const_time_fill_msb(res);
+
+	/* If pwd-value >= p, force pwd-value to be < p and perform the
+	 * calculations anyway to hide timing difference. The derived PWE will
+	 * be ignored in that case. */
+	pwd_value[0] = const_time_select_u8(pwd_value_valid, pwd_value[0], 0);
 
 	/* PWE = pwd-value^((p-1)/r) modulo p */
 
+	res = -1;
 	a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
+	if (!a)
+		goto fail;
 
+	/* This is an optimization based on the used group that does not depend
+	 * on the password in any way, so it is fine to use separate branches
+	 * for this step without constant time operations. */
 	if (sae->tmp->dh->safe_prime) {
 		/*
 		 * r = (p-1)/2 for the group used here, so this becomes:
@@ -356,33 +392,34 @@
 		b = crypto_bignum_init_set(exp, sizeof(exp));
 		if (b == NULL ||
 		    crypto_bignum_sub(sae->tmp->prime, b, b) < 0 ||
-		    crypto_bignum_div(b, sae->tmp->order, b) < 0) {
-			crypto_bignum_deinit(b, 0);
-			b = NULL;
-		}
+		    crypto_bignum_div(b, sae->tmp->order, b) < 0)
+			goto fail;
 	}
 
-	if (a == NULL || b == NULL)
-		res = -1;
-	else
-		res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
+	if (!b)
+		goto fail;
 
-	crypto_bignum_deinit(a, 0);
-	crypto_bignum_deinit(b, 0);
+	res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
+	if (res < 0)
+		goto fail;
 
-	if (res < 0) {
-		wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE");
-		return -1;
-	}
+	/* There were no fatal errors in calculations, so determine the return
+	 * value using constant time operations. We get here for number of
+	 * invalid cases which are cleared here after having performed all the
+	 * computation. PWE is valid if pwd-value was less than prime and
+	 * PWE > 1. Start with pwd-value check first and then use constant time
+	 * operations to clear res to 0 if PWE is 0 or 1.
+	 */
+	res = const_time_select_u8(pwd_value_valid, 1, 0);
+	is_val = crypto_bignum_is_zero(pwe);
+	res = const_time_select_u8(const_time_is_zero(is_val), res, 0);
+	is_val = crypto_bignum_is_one(pwe);
+	res = const_time_select_u8(const_time_is_zero(is_val), res, 0);
 
-	/* if (PWE > 1) --> found */
-	if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) {
-		wpa_printf(MSG_DEBUG, "SAE: PWE <= 1");
-		return 0;
-	}
-
-	wpa_printf(MSG_DEBUG, "SAE: PWE found");
-	return 1;
+fail:
+	crypto_bignum_deinit(a, 1);
+	crypto_bignum_deinit(b, 1);
+	return res;
 }
 
 
@@ -431,25 +468,32 @@
 	const u8 *addr[3];
 	size_t len[3];
 	size_t num_elem;
-	u8 dummy_password[32];
-	size_t dummy_password_len;
+	u8 *dummy_password, *tmp_password;
 	int pwd_seed_odd = 0;
 	u8 prime[SAE_MAX_ECC_PRIME_LEN];
 	size_t prime_len;
-	struct crypto_bignum *x = NULL, *qr, *qnr;
+	struct crypto_bignum *x = NULL, *qr = NULL, *qnr = NULL;
+	u8 x_bin[SAE_MAX_ECC_PRIME_LEN];
+	u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN];
+	u8 qr_bin[SAE_MAX_ECC_PRIME_LEN];
+	u8 qnr_bin[SAE_MAX_ECC_PRIME_LEN];
 	size_t bits;
-	int res;
+	int res = -1;
+	u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
+		       * mask */
 
-	dummy_password_len = password_len;
-	if (dummy_password_len > sizeof(dummy_password))
-		dummy_password_len = sizeof(dummy_password);
-	if (random_get_bytes(dummy_password, dummy_password_len) < 0)
-		return -1;
+	os_memset(x_bin, 0, sizeof(x_bin));
+
+	dummy_password = os_malloc(password_len);
+	tmp_password = os_malloc(password_len);
+	if (!dummy_password || !tmp_password ||
+	    random_get_bytes(dummy_password, password_len) < 0)
+		goto fail;
 
 	prime_len = sae->tmp->prime_len;
 	if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
 				 prime_len) < 0)
-		return -1;
+		goto fail;
 	bits = crypto_ec_prime_len_bits(sae->tmp->ec);
 
 	/*
@@ -457,8 +501,10 @@
 	 * (qnr) modulo p for blinding purposes during the loop.
 	 */
 	if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits,
-			      &qr, &qnr) < 0)
-		return -1;
+			      &qr, &qnr) < 0 ||
+	    crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin), prime_len) < 0 ||
+	    crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin), prime_len) < 0)
+		goto fail;
 
 	wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
 			      password, password_len);
@@ -474,7 +520,7 @@
 	 */
 	sae_pwd_seed_key(addr1, addr2, addrs);
 
-	addr[0] = password;
+	addr[0] = tmp_password;
 	len[0] = password_len;
 	num_elem = 1;
 	if (identifier) {
@@ -491,9 +537,8 @@
 	 * attacks that attempt to determine the number of iterations required
 	 * in the loop.
 	 */
-	for (counter = 1; counter <= k || !x; counter++) {
+	for (counter = 1; counter <= k || !found; counter++) {
 		u8 pwd_seed[SHA256_MAC_LEN];
-		struct crypto_bignum *x_cand;
 
 		if (counter > 200) {
 			/* This should not happen in practice */
@@ -501,40 +546,49 @@
 			break;
 		}
 
-		wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
+		wpa_printf(MSG_DEBUG, "SAE: counter = %03u", counter);
+		const_time_select_bin(found, dummy_password, password,
+				      password_len, tmp_password);
 		if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
 				       addr, len, pwd_seed) < 0)
 			break;
 
 		res = sae_test_pwd_seed_ecc(sae, pwd_seed,
-					    prime, qr, qnr, &x_cand);
+					    prime, qr_bin, qnr_bin, x_cand_bin);
+		const_time_select_bin(found, x_bin, x_cand_bin, prime_len,
+				      x_bin);
+		pwd_seed_odd = const_time_select_u8(
+			found, pwd_seed_odd,
+			pwd_seed[SHA256_MAC_LEN - 1] & 0x01);
+		os_memset(pwd_seed, 0, sizeof(pwd_seed));
 		if (res < 0)
 			goto fail;
-		if (res > 0 && !x) {
-			wpa_printf(MSG_DEBUG,
-				   "SAE: Selected pwd-seed with counter %u",
-				   counter);
-			x = x_cand;
-			pwd_seed_odd = pwd_seed[SHA256_MAC_LEN - 1] & 0x01;
-			os_memset(pwd_seed, 0, sizeof(pwd_seed));
+		/* Need to minimize differences in handling res == 0 and 1 here
+		 * to avoid differences in timing and instruction cache access,
+		 * so use const_time_select_*() to make local copies of the
+		 * values based on whether this loop iteration was the one that
+		 * found the pwd-seed/x. */
 
-			/*
-			 * Use a dummy password for the following rounds, if
-			 * any.
-			 */
-			addr[0] = dummy_password;
-			len[0] = dummy_password_len;
-		} else if (res > 0) {
-			crypto_bignum_deinit(x_cand, 1);
-		}
+		/* found is 0 or 0xff here and res is 0 or 1. Bitwise OR of them
+		 * (with res converted to 0/0xff) handles this in constant time.
+		 */
+		found |= res * 0xff;
+		wpa_printf(MSG_DEBUG, "SAE: pwd-seed result %d found=0x%02x",
+			   res, found);
 	}
 
-	if (!x) {
+	if (!found) {
 		wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
 		res = -1;
 		goto fail;
 	}
 
+	x = crypto_bignum_init_set(x_bin, prime_len);
+	if (!x) {
+		res = -1;
+		goto fail;
+	}
+
 	if (!sae->tmp->pwe_ecc)
 		sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec);
 	if (!sae->tmp->pwe_ecc)
@@ -543,7 +597,6 @@
 		res = crypto_ec_point_solve_y_coord(sae->tmp->ec,
 						    sae->tmp->pwe_ecc, x,
 						    pwd_seed_odd);
-	crypto_bignum_deinit(x, 1);
 	if (res < 0) {
 		/*
 		 * This should not happen since we already checked that there
@@ -555,27 +608,48 @@
 fail:
 	crypto_bignum_deinit(qr, 0);
 	crypto_bignum_deinit(qnr, 0);
+	os_free(dummy_password);
+	bin_clear_free(tmp_password, password_len);
+	crypto_bignum_deinit(x, 1);
+	os_memset(x_bin, 0, sizeof(x_bin));
+	os_memset(x_cand_bin, 0, sizeof(x_cand_bin));
 
 	return res;
 }
 
 
+static int sae_modp_group_require_masking(int group)
+{
+	/* Groups for which pwd-value is likely to be >= p frequently */
+	return group == 22 || group == 23 || group == 24;
+}
+
+
 static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
 			      const u8 *addr2, const u8 *password,
 			      size_t password_len, const char *identifier)
 {
-	u8 counter;
+	u8 counter, k, sel_counter = 0;
 	u8 addrs[2 * ETH_ALEN];
 	const u8 *addr[3];
 	size_t len[3];
 	size_t num_elem;
-	int found = 0;
+	u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
+		       * mask */
+	u8 mask;
+	struct crypto_bignum *pwe;
+	size_t prime_len = sae->tmp->prime_len * 8;
+	u8 *pwe_buf;
 
-	if (sae->tmp->pwe_ffc == NULL) {
-		sae->tmp->pwe_ffc = crypto_bignum_init();
-		if (sae->tmp->pwe_ffc == NULL)
-			return -1;
-	}
+	crypto_bignum_deinit(sae->tmp->pwe_ffc, 1);
+	sae->tmp->pwe_ffc = NULL;
+
+	/* Allocate a buffer to maintain selected and candidate PWE for constant
+	 * time selection. */
+	pwe_buf = os_zalloc(prime_len * 2);
+	pwe = crypto_bignum_init();
+	if (!pwe_buf || !pwe)
+		goto fail;
 
 	wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
 			      password, password_len);
@@ -599,7 +673,9 @@
 	len[num_elem] = sizeof(counter);
 	num_elem++;
 
-	for (counter = 1; !found; counter++) {
+	k = sae_modp_group_require_masking(sae->group) ? 40 : 1;
+
+	for (counter = 1; counter <= k || !found; counter++) {
 		u8 pwd_seed[SHA256_MAC_LEN];
 		int res;
 
@@ -609,20 +685,37 @@
 			break;
 		}
 
-		wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
+		wpa_printf(MSG_DEBUG, "SAE: counter = %02u", counter);
 		if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
 				       addr, len, pwd_seed) < 0)
 			break;
-		res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc);
+		res = sae_test_pwd_seed_ffc(sae, pwd_seed, pwe);
+		/* res is -1 for fatal failure, 0 if a valid PWE was not found,
+		 * or 1 if a valid PWE was found. */
 		if (res < 0)
 			break;
-		if (res > 0) {
-			wpa_printf(MSG_DEBUG, "SAE: Use this PWE");
-			found = 1;
-		}
+		/* Store the candidate PWE into the second half of pwe_buf and
+		 * the selected PWE in the beginning of pwe_buf using constant
+		 * time selection. */
+		if (crypto_bignum_to_bin(pwe, pwe_buf + prime_len, prime_len,
+					 prime_len) < 0)
+			break;
+		const_time_select_bin(found, pwe_buf, pwe_buf + prime_len,
+				      prime_len, pwe_buf);
+		sel_counter = const_time_select_u8(found, sel_counter, counter);
+		mask = const_time_eq_u8(res, 1);
+		found = const_time_select_u8(found, found, mask);
 	}
 
-	return found ? 0 : -1;
+	if (!found)
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "SAE: Use PWE from counter = %02u", sel_counter);
+	sae->tmp->pwe_ffc = crypto_bignum_init_set(pwe_buf, prime_len);
+fail:
+	crypto_bignum_deinit(pwe, 1);
+	bin_clear_free(pwe_buf, prime_len * 2);
+	return sae->tmp->pwe_ffc ? 0 : -1;
 }
 
 
@@ -1394,23 +1487,31 @@
 
 	wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data));
 
-	if (sae->tmp == NULL) {
+	if (!sae->tmp || !sae->peer_commit_scalar ||
+	    !sae->tmp->own_commit_scalar) {
 		wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available");
 		return -1;
 	}
 
-	if (sae->tmp->ec)
+	if (sae->tmp->ec) {
+		if (!sae->tmp->peer_commit_element_ecc ||
+		    !sae->tmp->own_commit_element_ecc)
+			return -1;
 		sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar,
 				   sae->tmp->peer_commit_element_ecc,
 				   sae->tmp->own_commit_scalar,
 				   sae->tmp->own_commit_element_ecc,
 				   verifier);
-	else
+	} else {
+		if (!sae->tmp->peer_commit_element_ffc ||
+		    !sae->tmp->own_commit_element_ffc)
+			return -1;
 		sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar,
 				   sae->tmp->peer_commit_element_ffc,
 				   sae->tmp->own_commit_scalar,
 				   sae->tmp->own_commit_element_ffc,
 				   verifier);
+	}
 
 	if (os_memcmp_const(verifier, data + 2, SHA256_MAC_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch");
diff --git a/src/common/sae.h b/src/common/sae.h
index 3fbcb58..3eb6e32 100644
--- a/src/common/sae.h
+++ b/src/common/sae.h
@@ -40,6 +40,8 @@
 	struct crypto_bignum *order_buf;
 	struct wpabuf *anti_clogging_token;
 	char *pw_id;
+	int vlan_id;
+	u8 bssid[ETH_ALEN];
 };
 
 enum sae_state {
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index b47f632..ed2d1c2 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -340,14 +340,21 @@
  * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
  * PTK = PRF-X(PMK, "Pairwise key expansion",
  *             Min(AA, SA) || Max(AA, SA) ||
- *             Min(ANonce, SNonce) || Max(ANonce, SNonce))
+ *             Min(ANonce, SNonce) || Max(ANonce, SNonce)
+ *             [ || Z.x ])
+ *
+ * The optional Z.x component is used only with DPP and that part is not defined
+ * in IEEE 802.11.
  */
 int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
 		   const u8 *addr1, const u8 *addr2,
 		   const u8 *nonce1, const u8 *nonce2,
-		   struct wpa_ptk *ptk, int akmp, int cipher)
+		   struct wpa_ptk *ptk, int akmp, int cipher,
+		   const u8 *z, size_t z_len)
 {
-	u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN];
+#define MAX_Z_LEN 66 /* with NIST P-521 */
+	u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN + MAX_Z_LEN];
+	size_t data_len = 2 * ETH_ALEN + 2 * WPA_NONCE_LEN;
 	u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
 	size_t ptk_len;
 
@@ -356,6 +363,9 @@
 		return -1;
 	}
 
+	if (z_len > MAX_Z_LEN)
+		return -1;
+
 	if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) {
 		os_memcpy(data, addr1, ETH_ALEN);
 		os_memcpy(data + ETH_ALEN, addr2, ETH_ALEN);
@@ -374,6 +384,11 @@
 			  WPA_NONCE_LEN);
 	}
 
+	if (z && z_len) {
+		os_memcpy(data + 2 * ETH_ALEN + 2 * WPA_NONCE_LEN, z, z_len);
+		data_len += z_len;
+	}
+
 	ptk->kck_len = wpa_kck_len(akmp, pmk_len);
 	ptk->kek_len = wpa_kek_len(akmp, pmk_len);
 	ptk->tk_len = wpa_cipher_key_len(cipher);
@@ -388,7 +403,7 @@
 	if (wpa_key_mgmt_sha384(akmp)) {
 #if defined(CONFIG_SUITEB192) || defined(CONFIG_FILS)
 		wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)");
-		if (sha384_prf(pmk, pmk_len, label, data, sizeof(data),
+		if (sha384_prf(pmk, pmk_len, label, data, data_len,
 			       tmp, ptk_len) < 0)
 			return -1;
 #else /* CONFIG_SUITEB192 || CONFIG_FILS */
@@ -397,7 +412,7 @@
 	} else if (wpa_key_mgmt_sha256(akmp) || akmp == WPA_KEY_MGMT_OWE) {
 #if defined(CONFIG_IEEE80211W) || defined(CONFIG_SAE) || defined(CONFIG_FILS)
 		wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)");
-		if (sha256_prf(pmk, pmk_len, label, data, sizeof(data),
+		if (sha256_prf(pmk, pmk_len, label, data, data_len,
 			       tmp, ptk_len) < 0)
 			return -1;
 #else /* CONFIG_IEEE80211W or CONFIG_SAE or CONFIG_FILS */
@@ -406,17 +421,17 @@
 #ifdef CONFIG_DPP
 	} else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 32) {
 		wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)");
-		if (sha256_prf(pmk, pmk_len, label, data, sizeof(data),
+		if (sha256_prf(pmk, pmk_len, label, data, data_len,
 			       tmp, ptk_len) < 0)
 			return -1;
 	} else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 48) {
 		wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)");
-		if (sha384_prf(pmk, pmk_len, label, data, sizeof(data),
+		if (sha384_prf(pmk, pmk_len, label, data, data_len,
 			       tmp, ptk_len) < 0)
 			return -1;
 	} else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 64) {
 		wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA512)");
-		if (sha512_prf(pmk, pmk_len, label, data, sizeof(data),
+		if (sha512_prf(pmk, pmk_len, label, data, data_len,
 			       tmp, ptk_len) < 0)
 			return -1;
 	} else if (akmp == WPA_KEY_MGMT_DPP) {
@@ -426,7 +441,7 @@
 #endif /* CONFIG_DPP */
 	} else {
 		wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA1)");
-		if (sha1_prf(pmk, pmk_len, label, data, sizeof(data), tmp,
+		if (sha1_prf(pmk, pmk_len, label, data, data_len, tmp,
 			     ptk_len) < 0)
 			return -1;
 	}
@@ -435,6 +450,8 @@
 		   MAC2STR(addr1), MAC2STR(addr2));
 	wpa_hexdump(MSG_DEBUG, "WPA: Nonce1", nonce1, WPA_NONCE_LEN);
 	wpa_hexdump(MSG_DEBUG, "WPA: Nonce2", nonce2, WPA_NONCE_LEN);
+	if (z && z_len)
+		wpa_hexdump_key(MSG_DEBUG, "WPA: Z.x", z, z_len);
 	wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len);
 	wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", tmp, ptk_len);
 
@@ -451,6 +468,7 @@
 	ptk->kck2_len = 0;
 
 	os_memset(tmp, 0, sizeof(tmp));
+	os_memset(data, 0, data_len);
 	return 0;
 }
 
@@ -1209,6 +1227,7 @@
 		left = rsn_ie_len - 6;
 
 		data->group_cipher = WPA_CIPHER_GTK_NOT_USED;
+		data->has_group = 1;
 		data->key_mgmt = WPA_KEY_MGMT_OSEN;
 		data->proto = WPA_PROTO_OSEN;
 	} else {
@@ -1230,6 +1249,7 @@
 
 	if (left >= RSN_SELECTOR_LEN) {
 		data->group_cipher = rsn_selector_to_bitfield(pos);
+		data->has_group = 1;
 		if (!wpa_cipher_valid_group(data->group_cipher)) {
 			wpa_printf(MSG_DEBUG,
 				   "%s: invalid group cipher 0x%x (%08x)",
@@ -1255,6 +1275,8 @@
 				   "count %u left %u", __func__, count, left);
 			return -4;
 		}
+		if (count)
+			data->has_pairwise = 1;
 		for (i = 0; i < count; i++) {
 			data->pairwise_cipher |= rsn_selector_to_bitfield(pos);
 			pos += RSN_SELECTOR_LEN;
@@ -1473,6 +1495,15 @@
 }
 
 
+int wpa_default_rsn_cipher(int freq)
+{
+	if (freq > 56160)
+		return WPA_CIPHER_GCMP; /* DMG */
+
+	return WPA_CIPHER_CCMP;
+}
+
+
 #ifdef CONFIG_IEEE80211R
 
 /**
@@ -1760,7 +1791,7 @@
 	os_memcpy(ptk->tk, tmp + offset, ptk->tk_len);
 	offset += ptk->tk_len;
 	os_memcpy(ptk->kck2, tmp + offset, ptk->kck2_len);
-	offset = ptk->kck2_len;
+	offset += ptk->kck2_len;
 	os_memcpy(ptk->kek2, tmp + offset, ptk->kek2_len);
 
 	wpa_hexdump_key(MSG_DEBUG, "FT: KCK", ptk->kck, ptk->kck_len);
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 37b5834..e83d688 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -347,7 +347,8 @@
 int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
 		   const u8 *addr1, const u8 *addr2,
 		   const u8 *nonce1, const u8 *nonce2,
-		   struct wpa_ptk *ptk, int akmp, int cipher);
+		   struct wpa_ptk *ptk, int akmp, int cipher,
+		   const u8 *z, size_t z_len);
 int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len,
 		     const u8 *snonce, const u8 *anonce, const u8 *dh_ss,
 		     size_t dh_ss_len, u8 *pmk, size_t *pmk_len);
@@ -392,7 +393,9 @@
 struct wpa_ie_data {
 	int proto;
 	int pairwise_cipher;
+	int has_pairwise;
 	int group_cipher;
+	int has_group;
 	int key_mgmt;
 	int capabilities;
 	size_t num_pmkid;
@@ -405,6 +408,7 @@
 			 struct wpa_ie_data *data);
 int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
 			 struct wpa_ie_data *data);
+int wpa_default_rsn_cipher(int freq);
 
 void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
 	       u8 *pmkid, int akmp);