[wpa_supplicant] Cumulative patch from commit d68c0dd4d

Bug: 156933657
Test: Verify Passpoint ANQP functionality and Passpoint association
Test: Connect to Passpoint, Open, WPA2, WPA3 networks and run traffic
Test: Regression test passed (Bug: 171270733)

BYPASS_INCLUSIVE_LANGUAGE_REASON=Merged from Open source

d68c0dd4d build: lib.rules: Add common-clean
d34b33451 wpa_supplicant: Fix frequency config for VHT/HE cases
0747432ef Fix spelling of "unexpected" in messages
d720de929 hostapd: Fix typos
4c66894fa eap_peer: Add .gitignore with *.so
13256b8cf P2P: Stop old listen radio work before go to WAIT_PEER_IDLE state
0f7989d8a MSCS: Fix decapsulating subelements from MSCS descriptor
cc3d6efa8 Add QCA interface for driver to report various connect fail reason codes
39748963d build: Fix libeap_peer.a build
c3f37c35f DFS: Use helper functions for VHT/HE parameters
a72599b31 hw_features: Better debug messages for some error cases
5965c7da5 wpa_supplicant: Enable VHT and HE in default config parameters
df6745e8c wpa_supplicant: Handle HT40 and mode downgrade in AP mode
93da12fd9 mesh: Fix channel init order, disable pri/sec channel switch
7f8ac02e8 HE/VHT: Fix frequency setup with HE enabled
0f07230eb DPP2: Add privacyProtectionKey into Configurator backup/restore
a0ccc4017 DPP2: Use ppKey to decrypt E'-id on Configurator
99d7bf234 DPP2: Use the new privacy protection key to protect E-id on Enrollee
37df40845 DPP2: Copy received ppKey into wpa_supplicant network profile
a8ee2292b DPP2: Parse ppKey from Connector
2a8c92887 DPP2: Add ppKey into Connector
9c1fbff07 DPP2: Generate a privacy protection key for Configurator
1d1475845 DPP: Make dpp_keygen_configurator() a static function
1d0d8888a build: Make more library things common
f4b3d14e9 build: Make a common library build
ac1447ae9 build: Rebuild libs all the time
6c41d43f1 mesh: Stop SAE auth timer when mesh node is removed
154b18d95 build: Fix dependency file inclusion
79db311e8 macsec_linux: Fix receive-lowest-PN setting
e3b47cdf8 DPP2: Add DPP_CHIRP commands to hostapd_cli and wpa_cli
cb3b70936 P2P: Set ap_configured_cb during group reform process
0e9f62e51 P2P: Fallback to GO negotiation after running out of GO scan attempts
1a0169695 hostapd_cli: Add dpp_bootstrap_set command
7e4ed93d3 wpa_cli: Add dpp_bootstrap_set command
283eee8ee gitignore: Clean up a bit
ae0b90dfa mesh: Allow channel switch command
87098d332 build: Put archive files into build/ folder too
00b5e99b6 build: Use the new build system for fuzz tests
a49f62884 wolfSSL: Fix wrong types in tls_wolfssl.c
58c18bcf8 hostapd: Fix error message for radius_accept_attr config option
52a1b2834 nl80211: Unbreak mode processing due to presence of S1G band
4b96fafcd D-Bus: Share 'remove all networks' with CLI
2818e9ca9 wpa_supplicant: Do not retry scan if operation is not supported
c0b88d129 P2P: Limit P2P_DEVICE name to appropriate ifname size
566ea1b7c mesh: Set correct address for mesh default broadcast/multicast keys
17d6ba4c9 DBus: Add "Roam" command support
6e757bba8 Use consistent spelling of "homogeneous"
cff545720 wpa_supplicant: Clear blacklist when SSID configs change
bbbb3c04e wpa_supplicant: Add new blacklist tests
164b8dd8e wpa_supplicant: Add wpa_blacklist_update()
d53011002 wpa_supplicant: Implement time-based blacklisting
2fd35d985 wpa_supplicant: Track consecutive connection failures
6d6310701 Fix STA mode default TXOP Limit values for AC_VI and AC_VO
dcc5288e5 gitignore: Add various things
ce963433b build: Allow overriding BUILDDIR from command line
ad6e4a5c5 build: Remove hostapd vs. wpa_supplicant build checks
6acda5322 build: Add .config file to dependencies
722138cd2 build: Put object files into build/ folder
0464d5d5d build: Move config file handling into build.rules
0430bc826 build: Add a common-clean target
06a6adb54 build: Use build.rules in lib.rules
3ff115db6 build: Disable built-in rules
a41a29192 build: Pull common fragments into a build.rules file
21cc50a43 HS 2.0 server: Add a .gitignore file
a28d127b1 AP: Reflect status code in SAE reflection attack test
e8b85c078 iface match: Unspecified matched interfaces should not log driver fails
83fa0a100 op_classes: Don't report an error when there are none to add
8776551bf BSD: don't log SIOCG80211 errors during interface setup
41d20df7f D-Bus: Allow empty string in dbus network properties
4756ecabc Allow bgscan parameters to be reconfigured
922fa0997 Global parser functions to return 1 when property unchanged
a87173b1d D-Bus: Skip property update actions when wpa_config_set() returns 1
1c58317f5 D-Bus: Allow changing an interface bridge via D-Bus
14318ccff P2P: Add configuration support to disable P2P in 6 GHz band
debf3e216 OCV: Work around for misbehaving STAs that indicate OCVC=1 without OCI
d48a3a676 FT: Modify status code in FT Reassoc frame for invalid OCI channel info
0e8d569d4 DPP2: Presence Announcement notification in STA
980c4da41 DPP2: Presence Announcement notification in AP
8b667bfa1 DPP2: Presence Announcement notification
cf3d260c3 DPP2: Fix hostapd crash setting global configurator params on chirp RX
a8f304228 Document the missing ignore_broadcast_ssid network profile parameter
aa704020a DBus: Update dont_quote[] with ignore_broadcast_ssid parameter
88d3f43bd DPP2: Replace OneAsymmetricKey version number (v2 to v1)
8e5739c3a DPP2: Check channel 6 validity before adding it to chirp channel list
5c6c0d569 DPP: Fix GAS fragmentation for DPP Config Response from hostapd
a7f55f7f6 WPS: Enable SA Query checks for WPS AP
43ef227e9 P2P: Make use wpas_p2p_reconsider_moving_go timeout gets canceled
57536a567 P2P: Fix P2P interface remuval through wpa_supplicant_remove_iface()
760d10cde P2P: Include channels 149 to 161 for operating classes 128 and 130
ac882374a SAE: Fix error path handling for SSWU
e8a1e6a4a P2P: Fix a typo in a comment
fa63284af Add additional roam triggers to qca_vendor_roam_triggers
13feeaa10 Add a new status code to represent an already suspended TWT session
8175c2654 Add test configuration attr to start/stop transmitting FD frames
90e478aa0 DPP2: Use the PFS fallback if multiple key_mgmt values are enabled
cab139ebc Fix a typo in a comment
7e20502f7 hostapd: Resolved compiler uninitialized warning
e3ba0c4cd Do not start SA Query procedure without keys
a92660a00 Work around Supported Operating Classes element issues for 6 GHz
fd4a58ccd Additional attributes to QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_CONTROL
8a6a2894d Add new QCA vendor attributes to get thermal level
41f818905 SAE-PK: Add support to skip sae_pk password check for testing purposes
a71b100c3 OCV: Allow connecting MFP incapable OCV STA when OCV is disabled in AP
5ecb45a41 OCV: Use more granular error codes for OCI validation failures
10c3e58b2 DPP2: Include E-nonce in reconfig ke derivation
4ae5e459d DPP2: Move E-nonce to be outside wrapped data in Reconfig Auth Resp
0ebf5aa34 DPP2: Replace I/R-nonce with C/E-nonce in reconfiguration
99f8506d3 Add QCA_NL80211_VENDOR_SUBCMD_MBSSID_TX_VDEV_STATUS
93a73ce02 MSCS: Fix issues due to incorrect usage of wpa_hexdump_buf()
9afb68b03 OpenSSL: Allow systemwide secpolicy overrides for TLS version
c85206ba4 QCA vendor attributes for setting ANI level
d335ca953 Update QCA vendor interface for GPIO configuration
7ddb71224 DPP2: Support QR mutual auth scan-during-auth-exchange (hostapd)
c043b1e00 DPP: Remove unnecessary dpp_global_config parameters
4ecb6dd16 DPP2: Controller support in hostapd
cd17f6877 Add QCA vendor event for firmware statistics
ba3081f1d dpp-nfc: Start listen operation more completely for NFC Tag write cases
e4adbacaf GAS: Fix memory leak on some DPP error paths
8aa91282a Fix EAPOL-Key msg 1/4 processing in a corner case
96e63008f OWE: Do not add DH Params element in AssocResp with PMKSA caching
9bc881153 DPP2: Fix build without IEEE8021X_EAPOL
2caff11d7 LibreSSL: Fix build with LibreSSL versions older than 2.9.1
55a366d7a dpp-nfc: Fix recv_octets() regression
90e05626f Add test configuration to ignore SA Query timeout
a2d35b49e Fix documentation for the test configuration attributes of FT-SAE/OCV
5d2218e61 Add get_sta_info vendor attrs to get BIP failure counters for STA mode
c6a760b9c DPP: Add process_conf_obj into TCP connection data struct
7f366fcbd DPP: Add msg_ctx into TCP connection data struct
6aa7aa808 DPP2: hostapd/AP as Enrollee/Initiator over TCP
d21dde9da MSCS: Send MSCS change/remove frames only if MSCS setup exists
af8ab3208 MSCS: Parse result of MSCS setup in (Re)Association Response frames
c504ff539 MSCS: Add support to populate MSCS Descriptor IE in (Re)AssocReq
bbd3178af MSCS: Add support to process MSCS Response frames
a11804724 MSCS: Add support to send MSCS Request frames
1733e356e dpp-nfc: Fix handover client wait for receiving handover select
596d99567 SME: Process channel switch event in SME only when supplicant's SME is used
0fa274d2b Derive seg0_idx and seg1_idx for 6 GHz when processing channel switch
5644b23de QCA vendor command to update SSID
cd708e8ff Add a vendor command for medium assessment
cc9fe46b3 Add AllPlay type to the QCA vendor element
170775232 ANQP: Add support to specify frequency in ANQP_GET command
43106e122 GAS: Update source MAC address on preassoc_mac_addr randomization
1289ecf4c GAS: Ignore preassoc_mac_addr when gas_rand_mac_addr enabled
dbe485a35 SAE-PK: Check psk param also to look for SAE-PK acceptable BSS
f5388b34e Add channel TX/RX times to QCA vendor interface of LL stats
9f9c11048 Fix QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO interface documentation
096456c0c Enhancements to the TWT attributes/parameters (vendor command)
3adc1c623 DPP2: Disconnect before starting reconfiguration
574a8fa6c DPP: Do not interpret unknown channel as missing channel list for NFC
fc3efe083 DPP2: Support mutual auth with QR in scan-during-auth-exchange case
2e956c37d DPP2: Do not close TCP socket when waiting for full Auth Resp
e4e95aabb DPP2: Allow Controller to be configured to require QR mutual auth
61c249c49 Add QCA vendor attribute to configure number of TX/RX chains
86fd6755c dpp-nfc: Use --altchan value in handover server
315422196 dpp-nfc: Update listen channel based on channel list when writing a tag
66ffe9d24 DPP2: Update Reconfig Flags attribute format
5caf4e094 DPP2: Support RA/CA functionality in Controller initiated case
9304d1b3c DPP2: Regenerate Reconfig Announcement for each transmission
b591810f9 DPP2: Add DPP Status attribute into Reconfig Auth Confirm
6d0217119 DPP2: Allow iteration count to be configured for DPP_RECONFIG
c6d0e5a93 DPP2: Add E-id in Reconfig Announcement
e5be6e68c DPP2: Add Enrollee netAccessKey group into Reconfig Announcement
2a58968d3 SAE-PK: Allow SAE-PK password to be set using the psk parameter
7ca81190a SAE-PK: Allow SAE-PK style wpa_passphrase if SAE-PK is enabled with same
844ecc70a Additional TWT attributes for response path and resume
0a9d7b169 SAE-PK: Update design for fingerprint encoding into password
2f858254c Extend GET_PMK to check PMKSA cache on the AP
b28b9dfcb OCV: OCI channel override support for testing (STA)
d309dd52b Fix the documentation for QCA_WLAN_VENDOR_ATTR_CONFIG_UDP_QOS_UPGRADE
87971ff05 SAE-PK: Fix SAE confirm writing in some AP cases with transition mode
240e9af4d SAE-PK: Make no-KEK debug prints distinct
38ef655eb dpp-nfc: Report negotiated connection handover result
4d5461842 dpp-nfc: Stop only_one run after failed handover attempt
f7b5a1d34 dpp-nfc: Do not indicate no initial HS as failure if alt HR will be sent
475b34665 dpp-nfc: Improved version of HandoverServer::serve()
dc0795e4f dpp-nfc: Stop process after successful handover client completion
b00bbbfe5 dpp-nfc: Optimize HandoverClient message receiving for alternative HR case
bbfb7b9fe dpp-nfc: Use a single handover client thread
aaa8638ed dpp-nfc: Add a class for maintaining connection handover state
66d74626b dpp-nfc: Reuse the same handover client for alternative URI
6e904441c dpp-nfc: Add peer URI into the HS in testing mode
3021b14c4 dpp-nfc: Enable more verbose nfcpy debugging
7c04bab71 tests: AES-CTR encrypt test vectors
1d3e16d0b dpp-nfc: Skip P2P management interfaces
e9c192ffc dpp-nfc: Ignore (no) response to initial handover request
7d27bcb8e dpp-nfc: Do not allow more than one alternative channel proposal
6eaee933d dpp-nfc: Add test mode for negotiated connection handover
730fc307b Update documentation for vendor attributes to ignore BSSIDs during roaming
f4877083e Rename driver op for temporarily disallowed BSSIDs
f8c756c5b FT: Rename temporary blocking of nonresponsive R0KH
15018d4f4 DPP2: Fix auth termination after receiving Configurator backup
74cd38ac6 dpp-nfc: Return failure status if operation fails
7e2edfbc1 dpp-nfc: Add color and details for interactive operations
09c22bb78 dpp-nfc: Fix regression in NFC Tag writing
1e4a42c74 dpp-nfc: Detect a non-NDEF tag when trying to write
288c0ffaa dpp-nfc: Do not hardcode netrole for NFC Tag writing cases
ebd5e764f Vendor attribute to configure QoS/AC upgrade for UDP frames
d91fb3ce3 Add a vendor command to fetch the currently enabled band(s)
29e47c416 Vendor command to configure TWT
8f396ad68 Enhance the qca_set_band enum values to be used as a bitmap
cc6153a8a nl80211: Fix sending proper VLAN ID attr value when using VLAN offload
a57f98754 Fix enabling 40/80 MHz bandwidth support in the 6 GHz band
885097125 mesh: Fix peer link counting when removing a mesh peer
8632dea4a DPP2: Make sure dpp_auth gets cleared with external config processing
a7ae42296 DPP2: Do not allow reconfiguration to be started with pending auth
d93df9989 DPP2: Debug print reason for rejecting reconfiguration
5d8c5f344 SAE-PK: Fix password validation check for Sec
0ce6883f6 tests: Fix SAE-PK password module tests
c9dc075fc dpp-nfc: Fix connection handover renegotiation
d0819a11c FILS: Use FILS auth alg when connecting using PMKSA caching
70b80c31f nl80211: Do not send FILS ERP sequence number without rRK
52a325762 6 GHz: Change 6 GHz channels per IEEE P802.11ax/D6.1
5908fedc1 dpp-nfc: Support channel list negotiation
eddf22e1f dpp-nfc: Update debug print for tag-read-only operation
b62e46f69 DPP2: Fix DPP_CA_SET processing with authentication not having peer BI
4f4a52c3c DPP: Clear bootstrap entries only after clearing authentication state
67efd19e0 nl80211: Use control port TX (status) in AP mode if possible
569497bf4 nl80211: Work around misdelivered control port TX status
87065881b nl80211: Use ext ack handler for TX control port
6f19cc4d7 nl80211: Handle control port TX status events over nl80211
f7c657b79 nl80211: Add custom ack handler arguments to send_and_recv()
73ea1ad7f nl80211: Clean up SO_WIFI_STATUS error reporting
cd99a8c43 EAP-TEAP (server): Allow Phase 2 skip based on client certificate
519629392 EAP-TEAP (client): Allow Phase 2 to be skipped if certificate is used
9593ce658 OpenSSL: Provide access to peer subject and own certificate use
b5dab03a1 Convert int to bool for throughput estimate tables
b97aa038b Add WPA_EVENT_{DO,SKIP}_ROAM events
d6b450e89 Refactor wpa_supplicant_need_to_roam()
2ff5a1fdb Use lookup-table instead of macro for TX rate estimates
fa09b85c4 DPP2: Remove forgetten development time debug prints
0bbab6465 DPP2: Fix dot1x config object parsing without trustedEapServerName
8f88dcf05 DPP2: Add an automatic peer_bi entry for CSR matching if needed
b25ddfe9d DPP2: Add Enrollee name into CSR as the commonName
11aa77e00 DPP2: GAS comeback response processing for Enrollee over TCP
18e013a93 DPP2: GAS comeback request processing for Configurator over TCP
68d9586a4 DPP2: GAS Comeback Request for the TCP case
a352c7230 DPP2: Comeback delay response for certificate in over TCP case
0f9463d6e DPP2: CSR wait in Configurator when using TCP
1f86b2c24 DPP2: CSR generation in TCP Client/Enrollee
697fa0c4b DPP2: Do not try to proceed with GAS client if CSR building fails
ffc8ae507 Define a new QCA vendor attribute for Optimized Power Management
3a3eded0d DPP2: Allow CSR processing by CA/RA to reject configuration
3b60f1174 DPP2: Validate CSR on Configurator before forwarding to CA/RA
c98db9f1f DPP2: Add challengePassword into CSR
dbbb0d5b8 OpenSSL: Use EVP-based interface for ECDSA sign/verify
ace3723d9 DPP2: Enterprise provisioning (Enrollee)
6568e5d20 DPP2: Enterprise provisioning (Configurator)
4643b2fee DPP2: Enterprise provisioning definitions for dot1x AKM
812d52ae2 OpenSSL: Support EC key from private_key blob
4b834df5e OpenSSL: Support PEM encoded chain from client_cert blob
68ac45d53 GAS server: Support comeback delay from the request handler
608adae5b JSON: Add base64 helper functions
c7e6dbdad base64: Add no-LF variant for encoding
6dc2c0118 Update DFS terminology in attribute value documentation
621745917 Allow HE-without-VHT to add the Channel Switch Wrapper element
d51b1b7a6 Move hostapd_eid_wb_chsw_wrapper() to non-VHT-specific file
1f72bbbef AP: Reject association request upon invalid HE capabilities
088bef178 AP: Restrict Vendor VHT to 2.4 GHz only
6a34bd300 HE: Use device HE capability instead of HT/VHT for 6 GHz IEs
9272ebae8 nl80211: Fetch HE 6 GHz capability from the driver
f25c51a9f Sync with mac80211-next.git include/uapi/linux/nl80211.h
518be614f SAE-PK: Advertise RSNXE capability bit in STA mode
a77d6d220 SAE-PK: Update SAE confirm IE design
363dbf1ec SAE-PK: Remove requirement of SAE group matching SAE-PK (K_AP) group
2e80aeae4 WPS UPnP: Support build on OS X
f119f8a04 WPS UPnP: Fix FreeBSD build
cc2d03601 HS 2.0: Use global pmf=2 for the created network block
790026c3d Allow TX queue parameters to be configured for wpa_supplicant AP/P2P GO
c7cb42d53 Remove unused enum values
411e42673 Move local TX queue parameter parser into a common file
fcef598ea Do not try to connect with zero-length SSID
85aac526a WPS UPnP: Handle HTTP initiation failures for events more properly
f7d268864 WPS UPnP: Fix event message generation using a long URL path
5b78c8f96 WPS UPnP: Do not allow event subscriptions with URLs to other networks
e30dcda3b SAE-PK: Fix FILS Public Key element Key Type for ECDSA
4c3fbb234 SAE-PK: Check minimum password length more accurate
43a191b89 tests: Remove too short SAE-PK passwords
4ff0df39e SAE-PK: Testing functionality to allow behavior overrides
0c4ffce46 Allow transition_disable updates during the lifetime of a BSS
5f48d36b4 SAE-PK: Select SAE-PK network over SAE without PK
d654ca24d Clean up wpa_scan_res_match()
9ad010c29 SAE-PK: Allow automatic SAE-PK to be disabled
85ca13ebc wpa_cli: Add all_bss command to print all scan results (BSS entries)
215b4d8a7 FT: Do not add PMKID to the driver for FT-EAP if caching is disabled
5cf91afee QCA vendor attribute for dynamic bandwidth adjustment
1a28589b2 QCA vendor attributes for setting channel width
63653307d Add support for indicating missing driver AKM capability flags
18f3f99ac Add vendor attributes to configure testing functionality for FT/OCV/SAE
e53756a64 Fix a typo vendor attribute documentation
960e8e533 QCA vendor attribute to configure NSS
8d1cbaaff SAE-PK: Transition mode disabled indication processing
a75269529 SAE: Add sae_h2e and sae_pk to wpa_supplicant STATUS command
cc22fb1b8 SAE: Move H2E and PK flags to main sae_data
bc908daac Document more network profile parameters
1c846d647 SAE-PK: Allow SAE authentication without PK to be disabled
40240735b WPS UPnP: Do not update Beacon frames unnecessarily on subscription removal
c85b39ec5 SAE-PK: Increment the minimum password length to 9
2c7b5a2c5 tests: Skip too short SAE-PK passwords in positive testing
d777156e1 SAE-PK: Determine hash algorithm from K_AP group instead of SAE group
fb09ec87f SAE-PK: A tool for generating SAE-PK Modifier and password
b6bcd74e5 Show SAE capabilities in control interface
9bf576870 Show SAE flags in scan results
e7aeb6d8a SAE-PK: STA functionality
20ccf97b3 SAE-PK: AP functionality
00e4fbdcc tests: Module test for SAE-PK
6b9e99e57 SAE-PK: Extend SAE functionality for AP validation
b6dcbd01a SAE-PK: Identifier definitions
aed01b82d OpenSSL: Additional EC functionality for SAE-PK
8c1f61e82 OCV: Report OCI validation failures with OCV-FAILURE messages (STA)
661e66118 OCV: Allow OCI channel to be overridden for testing (AP)
d10a57f6e DPP2: Derive a separate key for enveloped data
32d3360f3 DPP: Fix a typo in a comment
5a7bcb772 OSEN: Do not send the actual BIGTK to OSEN STAs
2d6cc0e67 FT: Do not expose GTK/IGTK in FT Reassociation Response frame in OSEN
a99833789 WNM: Do not expose GTK/IGTK in WNM Sleep Mode Response frame in OSEN
d578e890e OWE: Skip beacon update of transition BSS if it is not yet enabled
88436baaa Add a vendor attribute to get OEM data
3f9a89ca1 Vendor attributes for configuring LDPC, TX STBC, RX STBC
8ee0bc622 OCV: Disconnect STAs that do not use SA Query after CSA
01ceb88c7 OCV: Report validation errors for (Re)Association Request frames
a3556d581 OCV: Report validation errors for EAPOL-Key messages in AP mode
d52067a5b OCV: Report validation errors for SA Query Request/Response in AP mode
52579be86 OCV: Move "OCV failed" prefix to callers
2d118f557 OCV: Add support to override channel info OCI element (STA)
c2080e865 Clear current PMKSA cache selection on association/roam
d9532eb70 Debug print PMK-R0/R1 and PMKR0/R1Name in the helper functions
5ab8ad4cf Vendor attributes for ssetting TX A-MSDU and RX A-MSDU parameters
f7a904a28 QCA vendor command for adding and deleting TSPEC
82867456e Vendor attributes to configure PMF protection and disassoc Tx for testing
e5e275745 Add QCA vendor interface support to configure PHY modes
db0d0b84a nl80211: Control the registration for RRM frame with driver_param

Change-Id: I07d9feb8f019a22917ffc0088126c04b7d80115a
diff --git a/src/common/Makefile b/src/common/Makefile
index ccb280e..59ba6c5 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -1,13 +1,3 @@
-all: libcommon.a
-
-clean:
-	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libcommon.a
-
-install:
-	@echo Nothing to be made.
-
-include ../lib.rules
-
 CFLAGS += -DCONFIG_IEEE80211R
 CFLAGS += -DCONFIG_HS20
 CFLAGS += -DCONFIG_SAE
@@ -21,7 +11,4 @@
 	sae.o \
 	wpa_common.o
 
-libcommon.a: $(LIB_OBJS)
-	$(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c
index a58bf66..00308d4 100644
--- a/src/common/common_module_tests.c
+++ b/src/common/common_module_tests.c
@@ -548,6 +548,95 @@
 }
 
 
+static int sae_pk_tests(void)
+{
+#ifdef CONFIG_SAE_PK
+	const char *invalid[] = { "a2bc-de3f-ghim-", "a2bcde3fghim", "", NULL };
+	struct {
+		const char *pw;
+		const u8 *val;
+	} valid[] = {
+		{ "a2bc-de3f-ghim", (u8 *) "\x06\x82\x21\x93\x65\x31\xd0\xc0" },
+		{ "aaaa-aaaa-aaaj", (u8 *) "\x00\x00\x00\x00\x00\x00\x00\x90" },
+		{ "7777-7777-777f", (u8 *) "\xff\xff\xff\xff\xff\xff\xfe\x50" },
+		{ NULL, NULL }
+	};
+	int i;
+	bool failed;
+
+	for (i = 0; invalid[i]; i++) {
+		if (sae_pk_valid_password(invalid[i])) {
+			wpa_printf(MSG_ERROR,
+				   "SAE-PK: Invalid password '%s' not recognized",
+				   invalid[i]);
+			return -1;
+		}
+	}
+
+	failed = false;
+	for (i = 0; valid[i].pw; i++) {
+		u8 *res;
+		size_t res_len;
+		char *b32;
+		const char *pw = valid[i].pw;
+		const u8 *val = valid[i].val;
+		size_t pw_len = os_strlen(pw);
+		size_t bits = (pw_len - pw_len / 5) * 5;
+		size_t bytes = (bits + 7) / 8;
+
+		if (!sae_pk_valid_password(pw)) {
+			wpa_printf(MSG_ERROR,
+				   "SAE-PK: Valid password '%s' not recognized",
+				   pw);
+			failed = true;
+			continue;
+		}
+
+		res = sae_pk_base32_decode(pw, pw_len, &res_len);
+		if (!res) {
+			wpa_printf(MSG_ERROR,
+				   "SAE-PK: Failed to decode password '%s'",
+				   valid[i].pw);
+			failed = true;
+			continue;
+		}
+		if (res_len != bytes || os_memcmp(val, res, res_len) != 0) {
+			wpa_printf(MSG_ERROR,
+				   "SAE-PK: Mismatch for decoded password '%s'",
+				   valid[i].pw);
+			wpa_hexdump(MSG_INFO, "SAE-PK: Decoded value",
+				    res, res_len);
+			wpa_hexdump(MSG_INFO, "SAE-PK: Expected value",
+				    val, bytes);
+			failed = true;
+		}
+		os_free(res);
+
+		b32 = sae_pk_base32_encode(val, bits - 5);
+		if (!b32) {
+			wpa_printf(MSG_ERROR,
+				   "SAE-PK: Failed to encode password '%s'",
+				   pw);
+			failed = true;
+			continue;
+		}
+		if (os_strcmp(b32, pw) != 0) {
+			wpa_printf(MSG_ERROR,
+				   "SAE-PK: Mismatch for password '%s'", pw);
+			wpa_printf(MSG_INFO, "SAE-PK: Encoded value: '%s'",
+				   b32);
+			failed = true;
+		}
+		os_free(b32);
+	}
+
+	return failed ? -1 : 0;
+#else /* CONFIG_SAE_PK */
+	return 0;
+#endif /* CONFIG_SAE_PK */
+}
+
+
 int common_module_tests(void)
 {
 	int ret = 0;
@@ -557,6 +646,7 @@
 	if (ieee802_11_parse_tests() < 0 ||
 	    gas_tests() < 0 ||
 	    sae_tests() < 0 ||
+	    sae_pk_tests() < 0 ||
 	    rsn_ie_parse_tests() < 0)
 		ret = -1;
 
diff --git a/src/common/dpp.c b/src/common/dpp.c
index d09f0d1..67b8f07 100644
--- a/src/common/dpp.c
+++ b/src/common/dpp.c
@@ -17,6 +17,7 @@
 #include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
 #include "common/gas.h"
+#include "eap_common/eap_defs.h"
 #include "crypto/crypto.h"
 #include "crypto/random.h"
 #include "crypto/aes.h"
@@ -244,6 +245,7 @@
 		wpa_printf(MSG_DEBUG,
 			   "DPP: URI channel-list: opclass=%d channel=%d ==> freq=%d",
 			   opclass, channel, freq);
+		bi->channels_listed = true;
 		if (freq < 0) {
 			wpa_printf(MSG_DEBUG,
 				   "DPP: Ignore unknown URI channel-list channel (opclass=%d channel=%d)",
@@ -828,6 +830,7 @@
 	const char *tech = "infra";
 	const char *dpp_name;
 	struct wpabuf *buf, *json;
+	char *csr = NULL;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	if (dpp_test == DPP_TEST_INVALID_CONFIG_ATTR_OBJ_CONF_REQ) {
@@ -844,6 +847,17 @@
 	len = 100 + name_len * 6 + 1 + int_array_len(opclasses) * 4;
 	if (mud_url && mud_url[0])
 		len += 10 + os_strlen(mud_url);
+#ifdef CONFIG_DPP2
+	if (auth->csr) {
+		size_t csr_len;
+
+		csr = base64_encode_no_lf(wpabuf_head(auth->csr),
+					  wpabuf_len(auth->csr), &csr_len);
+		if (!csr)
+			return NULL;
+		len += 30 + csr_len;
+	}
+#endif /* CONFIG_DPP2 */
 	json = wpabuf_alloc(len);
 	if (!json)
 		return NULL;
@@ -870,10 +884,15 @@
 			wpabuf_printf(json, "%s%u", i ? "," : "", opclasses[i]);
 		json_end_array(json);
 	}
+	if (csr) {
+		json_value_sep(json);
+		json_add_string(json, "pkcs10", csr);
+	}
 	json_end_object(json);
 
 	buf = dpp_build_conf_req(auth, wpabuf_head(json));
 	wpabuf_free(json);
+	os_free(csr);
 
 	return buf;
 }
@@ -916,6 +935,8 @@
 		conf->akm = DPP_AKM_PSK_SAE_DPP;
 	else if (bin_str_eq(type, len, "dpp"))
 		conf->akm = DPP_AKM_DPP;
+	else if (bin_str_eq(type, len, "dot1x"))
+		conf->akm = DPP_AKM_DOT1X;
 	else
 		goto fail;
 
@@ -978,6 +999,7 @@
 		return;
 	str_clear_free(conf->passphrase);
 	os_free(conf->group_id);
+	os_free(conf->csrattrs);
 	bin_clear_free(conf, sizeof(*conf));
 }
 
@@ -988,6 +1010,7 @@
 	const char *pos, *end;
 	struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL;
 	struct dpp_configuration *conf = NULL;
+	size_t len;
 
 	pos = os_strstr(cmd, " conf=sta-");
 	if (pos) {
@@ -1092,6 +1115,17 @@
 		conf->netaccesskey_expiry = val;
 	}
 
+	pos = os_strstr(cmd, " csrattrs=");
+	if (pos) {
+		pos += 10;
+		end = os_strchr(pos, ' ');
+		len = end ? (size_t) (end - pos) : os_strlen(pos);
+		conf->csrattrs = os_zalloc(len + 1);
+		if (!conf->csrattrs)
+			goto fail;
+		os_memcpy(conf->csrattrs, pos, len);
+	}
+
 	if (!dpp_configuration_valid(conf))
 		goto fail;
 
@@ -1247,12 +1281,28 @@
 
 		os_free(conf->connector);
 		wpabuf_free(conf->c_sign_key);
+		wpabuf_free(conf->certbag);
+		wpabuf_free(conf->certs);
+		wpabuf_free(conf->cacert);
+		os_free(conf->server_name);
+		wpabuf_free(conf->pp_key);
 	}
 #ifdef CONFIG_DPP2
 	dpp_free_asymmetric_key(auth->conf_key_pkg);
+	os_free(auth->csrattrs);
+	wpabuf_free(auth->csr);
+	wpabuf_free(auth->priv_key);
+	wpabuf_free(auth->cacert);
+	wpabuf_free(auth->certbag);
+	os_free(auth->trusted_eap_server_name);
+	wpabuf_free(auth->conf_resp_tcp);
 #endif /* CONFIG_DPP2 */
 	wpabuf_free(auth->net_access_key);
 	dpp_bootstrap_info_free(auth->tmp_own_bi);
+	if (auth->tmp_peer_bi) {
+		dl_list_del(&auth->tmp_peer_bi->list);
+		dpp_bootstrap_info_free(auth->tmp_peer_bi);
+	}
 #ifdef CONFIG_TESTING_OPTIONS
 	os_free(auth->config_obj_override);
 	os_free(auth->discovery_override);
@@ -1480,6 +1530,15 @@
 	tailroom += os_strlen(signed_conn);
 	if (incl_legacy)
 		tailroom += 1000;
+	if (akm == DPP_AKM_DOT1X) {
+		if (auth->certbag)
+			tailroom += 2 * wpabuf_len(auth->certbag);
+		if (auth->cacert)
+			tailroom += 2 * wpabuf_len(auth->cacert);
+		if (auth->trusted_eap_server_name)
+			tailroom += os_strlen(auth->trusted_eap_server_name);
+		tailroom += 1000;
+	}
 	buf = dpp_build_conf_start(auth, conf, tailroom);
 	if (!buf)
 		goto fail;
@@ -1495,6 +1554,30 @@
 		dpp_build_legacy_cred_params(buf, conf);
 		json_value_sep(buf);
 	}
+	if (akm == DPP_AKM_DOT1X) {
+		json_start_object(buf, "entCreds");
+		if (!auth->certbag)
+			goto fail;
+		json_add_base64(buf, "certBag", wpabuf_head(auth->certbag),
+				wpabuf_len(auth->certbag));
+		if (auth->cacert) {
+			json_value_sep(buf);
+			json_add_base64(buf, "caCert",
+					wpabuf_head(auth->cacert),
+					wpabuf_len(auth->cacert));
+		}
+		if (auth->trusted_eap_server_name) {
+			json_value_sep(buf);
+			json_add_string(buf, "trustedEapServerName",
+					auth->trusted_eap_server_name);
+		}
+		json_value_sep(buf);
+		json_start_array(buf, "eapMethods");
+		wpabuf_printf(buf, "%d", EAP_TYPE_TLS);
+		json_end_array(buf);
+		json_end_object(buf);
+		json_value_sep(buf);
+	}
 	wpabuf_put_str(buf, "\"signedConnector\":\"");
 	wpabuf_put_str(buf, signed_conn);
 	wpabuf_put_str(buf, "\"");
@@ -1504,6 +1587,16 @@
 		wpa_printf(MSG_DEBUG, "DPP: Failed to build csign JWK");
 		goto fail;
 	}
+#ifdef CONFIG_DPP2
+	if (auth->peer_version >= 2 && auth->conf->pp_key) {
+		json_value_sep(buf);
+		if (dpp_build_jwk(buf, "ppKey", auth->conf->pp_key, NULL,
+				  curve) < 0) {
+			wpa_printf(MSG_DEBUG, "DPP: Failed to build ppKey JWK");
+			goto fail;
+		}
+	}
+#endif /* CONFIG_DPP2 */
 
 	json_end_object(buf);
 	json_end_object(buf);
@@ -1554,7 +1647,7 @@
 
 static struct wpabuf *
 dpp_build_conf_obj(struct dpp_authentication *auth, enum dpp_netrole netrole,
-		   int idx)
+		   int idx, bool cert_req)
 {
 	struct dpp_configuration *conf = NULL;
 
@@ -1587,15 +1680,28 @@
 		return NULL;
 	}
 
+	if (conf->akm == DPP_AKM_DOT1X) {
+		if (!auth->conf) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: No Configurator data available");
+			return NULL;
+		}
+		if (!cert_req && !auth->certbag) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: No certificate data available for dot1x configuration");
+			return NULL;
+		}
+		return dpp_build_conf_obj_dpp(auth, conf);
+	}
 	if (dpp_akm_dpp(conf->akm) || (auth->peer_version >= 2 && auth->conf))
 		return dpp_build_conf_obj_dpp(auth, conf);
 	return dpp_build_conf_obj_legacy(auth, conf);
 }
 
 
-static struct wpabuf *
+struct wpabuf *
 dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
-		    u16 e_nonce_len, enum dpp_netrole netrole)
+		    u16 e_nonce_len, enum dpp_netrole netrole, bool cert_req)
 {
 	struct wpabuf *conf = NULL, *conf2 = NULL, *env_data = NULL;
 	size_t clear_len, attr_len;
@@ -1605,21 +1711,33 @@
 	size_t len[1];
 	enum dpp_status_error status;
 
+	if (auth->force_conf_resp_status != DPP_STATUS_OK) {
+		status = auth->force_conf_resp_status;
+		goto forced_status;
+	}
+
 	if (netrole == DPP_NETROLE_CONFIGURATOR) {
 #ifdef CONFIG_DPP2
 		env_data = dpp_build_enveloped_data(auth);
 #endif /* CONFIG_DPP2 */
 	} else {
-		conf = dpp_build_conf_obj(auth, netrole, 0);
+		conf = dpp_build_conf_obj(auth, netrole, 0, cert_req);
 		if (conf) {
 			wpa_hexdump_ascii(MSG_DEBUG,
 					  "DPP: configurationObject JSON",
 					  wpabuf_head(conf), wpabuf_len(conf));
-			conf2 = dpp_build_conf_obj(auth, netrole, 1);
+			conf2 = dpp_build_conf_obj(auth, netrole, 1, cert_req);
 		}
 	}
-	status = (conf || env_data) ? DPP_STATUS_OK :
-		DPP_STATUS_CONFIGURE_FAILURE;
+
+	if (conf || env_data)
+		status = DPP_STATUS_OK;
+	else if (!cert_req && netrole == DPP_NETROLE_STA && auth->conf_sta &&
+		 auth->conf_sta->akm == DPP_AKM_DOT1X && !auth->waiting_csr)
+		status = DPP_STATUS_CSR_NEEDED;
+	else
+		status = DPP_STATUS_CONFIGURE_FAILURE;
+forced_status:
 	auth->conf_resp_status = status;
 
 	/* { E-nonce, configurationObject[, sendConnStatus]}ke */
@@ -1633,6 +1751,9 @@
 	if (auth->peer_version >= 2 && auth->send_conn_status &&
 	    netrole == DPP_NETROLE_STA)
 		clear_len += 4;
+	if (status == DPP_STATUS_CSR_NEEDED && auth->conf_sta &&
+	    auth->conf_sta->csrattrs)
+		clear_len += 4 + os_strlen(auth->conf_sta->csrattrs);
 	clear = wpabuf_alloc(clear_len);
 	attr_len = 4 + 1 + 4 + clear_len + AES_BLOCK_SIZE;
 #ifdef CONFIG_TESTING_OPTIONS
@@ -1695,12 +1816,21 @@
 	}
 
 	if (auth->peer_version >= 2 && auth->send_conn_status &&
-	    netrole == DPP_NETROLE_STA) {
+	    netrole == DPP_NETROLE_STA && status == DPP_STATUS_OK) {
 		wpa_printf(MSG_DEBUG, "DPP: sendConnStatus");
 		wpabuf_put_le16(clear, DPP_ATTR_SEND_CONN_STATUS);
 		wpabuf_put_le16(clear, 0);
 	}
 
+	if (status == DPP_STATUS_CSR_NEEDED && auth->conf_sta &&
+	    auth->conf_sta->csrattrs) {
+		auth->waiting_csr = true;
+		wpa_printf(MSG_DEBUG, "DPP: CSR Attributes Request");
+		wpabuf_put_le16(clear, DPP_ATTR_CSR_ATTR_REQ);
+		wpabuf_put_le16(clear, os_strlen(auth->conf_sta->csrattrs));
+		wpabuf_put_str(clear, auth->conf_sta->csrattrs);
+	}
+
 #ifdef CONFIG_TESTING_OPTIONS
 skip_config_obj:
 	if (dpp_test == DPP_TEST_NO_STATUS_CONF_RESP) {
@@ -1771,6 +1901,7 @@
 	struct wpabuf *resp = NULL;
 	struct json_token *root = NULL, *token;
 	enum dpp_netrole netrole;
+	struct wpabuf *cert_req = NULL;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	if (dpp_test == DPP_TEST_STOP_AT_CONF_REQ) {
@@ -1879,6 +2010,7 @@
 		dpp_auth_fail(auth, "Unsupported netRole");
 		goto fail;
 	}
+	auth->e_netrole = netrole;
 
 	token = json_get_member(root, "mudurl");
 	if (token && token->type == JSON_STRING) {
@@ -1930,9 +2062,56 @@
 			txt);
 	}
 
-	resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, netrole);
+#ifdef CONFIG_DPP2
+	cert_req = json_get_member_base64(root, "pkcs10");
+	if (cert_req) {
+		char *txt;
+		int id;
+
+		wpa_hexdump_buf(MSG_DEBUG, "DPP: CertificateRequest", cert_req);
+		if (dpp_validate_csr(auth, cert_req) < 0) {
+			wpa_printf(MSG_DEBUG, "DPP: CSR is not valid");
+			auth->force_conf_resp_status = DPP_STATUS_CSR_BAD;
+			goto cont;
+		}
+
+		if (auth->peer_bi) {
+			id = auth->peer_bi->id;
+		} else if (auth->tmp_peer_bi) {
+			id = auth->tmp_peer_bi->id;
+		} else {
+			struct dpp_bootstrap_info *bi;
+
+			bi = os_zalloc(sizeof(*bi));
+			if (!bi)
+				goto fail;
+			bi->id = dpp_next_id(auth->global);
+			dl_list_add(&auth->global->bootstrap, &bi->list);
+			auth->tmp_peer_bi = bi;
+			id = bi->id;
+		}
+
+		wpa_printf(MSG_DEBUG, "DPP: CSR is valid - forward to CA/RA");
+		txt = base64_encode_no_lf(wpabuf_head(cert_req),
+					  wpabuf_len(cert_req), NULL);
+		if (!txt)
+			goto fail;
+
+		wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_CSR "peer=%d csr=%s",
+			id, txt);
+		os_free(txt);
+		auth->waiting_csr = false;
+		auth->waiting_cert = true;
+		goto fail;
+	}
+cont:
+#endif /* CONFIG_DPP2 */
+
+	resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, netrole,
+				   cert_req);
 
 fail:
+	wpabuf_free(cert_req);
 	json_free(root);
 	os_free(unwrapped);
 	return resp;
@@ -2255,6 +2434,20 @@
 }
 
 
+static void dpp_copy_ppkey(struct dpp_config_obj *conf, EVP_PKEY *ppkey)
+{
+	unsigned char *der = NULL;
+	int der_len;
+
+	der_len = i2d_PUBKEY(ppkey, &der);
+	if (der_len <= 0)
+		return;
+	wpabuf_free(conf->pp_key);
+	conf->pp_key = wpabuf_alloc_copy(der, der_len);
+	OPENSSL_free(der);
+}
+
+
 static void dpp_copy_netaccesskey(struct dpp_authentication *auth,
 				  struct dpp_config_obj *conf)
 {
@@ -2290,10 +2483,10 @@
 			      struct json_token *cred)
 {
 	struct dpp_signed_connector_info info;
-	struct json_token *token, *csign;
+	struct json_token *token, *csign, *ppkey;
 	int ret = -1;
-	EVP_PKEY *csign_pub = NULL;
-	const struct dpp_curve_params *key_curve = NULL;
+	EVP_PKEY *csign_pub = NULL, *pp_pub = NULL;
+	const struct dpp_curve_params *key_curve = NULL, *pp_curve = NULL;
 	const char *signed_connector;
 
 	os_memset(&info, 0, sizeof(info));
@@ -2320,6 +2513,21 @@
 	}
 	dpp_debug_print_key("DPP: Received C-sign-key", csign_pub);
 
+	ppkey = json_get_member(cred, "ppKey");
+	if (ppkey && ppkey->type == JSON_OBJECT) {
+		pp_pub = dpp_parse_jwk(ppkey, &pp_curve);
+		if (!pp_pub) {
+			wpa_printf(MSG_DEBUG, "DPP: Failed to parse ppKey JWK");
+			goto fail;
+		}
+		dpp_debug_print_key("DPP: Received ppKey", pp_pub);
+		if (key_curve != pp_curve) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: C-sign-key and ppKey do not use the same curve");
+			goto fail;
+		}
+	}
+
 	token = json_get_member(cred, "signedConnector");
 	if (!token || token->type != JSON_STRING) {
 		wpa_printf(MSG_DEBUG, "DPP: No signedConnector string found");
@@ -2350,17 +2558,72 @@
 	conf->connector = os_strdup(signed_connector);
 
 	dpp_copy_csign(conf, csign_pub);
+	if (pp_pub)
+		dpp_copy_ppkey(conf, pp_pub);
 	if (dpp_akm_dpp(conf->akm) || auth->peer_version >= 2)
 		dpp_copy_netaccesskey(auth, conf);
 
 	ret = 0;
 fail:
 	EVP_PKEY_free(csign_pub);
+	EVP_PKEY_free(pp_pub);
 	os_free(info.payload);
 	return ret;
 }
 
 
+#ifdef CONFIG_DPP2
+static int dpp_parse_cred_dot1x(struct dpp_authentication *auth,
+				struct dpp_config_obj *conf,
+				struct json_token *cred)
+{
+	struct json_token *ent, *name;
+
+	ent = json_get_member(cred, "entCreds");
+	if (!ent || ent->type != JSON_OBJECT) {
+		dpp_auth_fail(auth, "No entCreds in JSON");
+		return -1;
+	}
+
+	conf->certbag = json_get_member_base64(ent, "certBag");
+	if (!conf->certbag) {
+		dpp_auth_fail(auth, "No certBag in JSON");
+		return -1;
+	}
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Received certBag", conf->certbag);
+	conf->certs = dpp_pkcs7_certs(conf->certbag);
+	if (!conf->certs) {
+		dpp_auth_fail(auth, "No certificates in certBag");
+		return -1;
+	}
+
+	conf->cacert = json_get_member_base64(ent, "caCert");
+	if (conf->cacert)
+		wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Received caCert",
+				conf->cacert);
+
+	name = json_get_member(ent, "trustedEapServerName");
+	if (name &&
+	    (name->type != JSON_STRING ||
+	     has_ctrl_char((const u8 *) name->string,
+			   os_strlen(name->string)))) {
+		dpp_auth_fail(auth,
+			      "Invalid trustedEapServerName type in JSON");
+		return -1;
+	}
+	if (name && name->string) {
+		wpa_printf(MSG_DEBUG, "DPP: Received trustedEapServerName: %s",
+			   name->string);
+		conf->server_name = os_strdup(name->string);
+		if (!conf->server_name)
+			return -1;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_DPP2 */
+
+
 const char * dpp_akm_str(enum dpp_akm akm)
 {
 	switch (akm) {
@@ -2376,6 +2639,8 @@
 		return "dpp+sae";
 	case DPP_AKM_PSK_SAE_DPP:
 		return "dpp+psk+sae";
+	case DPP_AKM_DOT1X:
+		return "dot1x";
 	default:
 		return "??";
 	}
@@ -2397,6 +2662,8 @@
 		return "506F9A02+000FAC08";
 	case DPP_AKM_PSK_SAE_DPP:
 		return "506F9A02+000FAC08+000FAC02+000FAC06";
+	case DPP_AKM_DOT1X:
+		return "000FAC01+000FAC05";
 	default:
 		return "??";
 	}
@@ -2406,7 +2673,7 @@
 static enum dpp_akm dpp_akm_from_str(const char *akm)
 {
 	const char *pos;
-	int dpp = 0, psk = 0, sae = 0;
+	int dpp = 0, psk = 0, sae = 0, dot1x = 0;
 
 	if (os_strcmp(akm, "psk") == 0)
 		return DPP_AKM_PSK;
@@ -2420,6 +2687,8 @@
 		return DPP_AKM_SAE_DPP;
 	if (os_strcmp(akm, "dpp+psk+sae") == 0)
 		return DPP_AKM_PSK_SAE_DPP;
+	if (os_strcmp(akm, "dot1x") == 0)
+		return DPP_AKM_DOT1X;
 
 	pos = akm;
 	while (*pos) {
@@ -2433,6 +2702,10 @@
 			psk = 1;
 		else if (os_strncasecmp(pos, "000FAC08", 8) == 0)
 			sae = 1;
+		else if (os_strncasecmp(pos, "000FAC01", 8) == 0)
+			dot1x = 1;
+		else if (os_strncasecmp(pos, "000FAC05", 8) == 0)
+			dot1x = 1;
 		pos += 8;
 		if (*pos != '+')
 			break;
@@ -2451,6 +2724,8 @@
 		return DPP_AKM_SAE;
 	if (psk)
 		return DPP_AKM_PSK;
+	if (dot1x)
+		return DPP_AKM_DOT1X;
 
 	return DPP_AKM_UNKNOWN;
 }
@@ -2568,6 +2843,12 @@
 		   (auth->peer_version >= 2 && dpp_akm_legacy(conf->akm))) {
 		if (dpp_parse_cred_dpp(auth, conf, cred) < 0)
 			goto fail;
+#ifdef CONFIG_DPP2
+	} else if (conf->akm == DPP_AKM_DOT1X) {
+		if (dpp_parse_cred_dot1x(auth, conf, cred) < 0 ||
+		    dpp_parse_cred_dpp(auth, conf, cred) < 0)
+			goto fail;
+#endif /* CONFIG_DPP2 */
 	} else {
 		wpa_printf(MSG_DEBUG, "DPP: Unsupported akm: %s",
 			   token->string);
@@ -2584,6 +2865,20 @@
 }
 
 
+#ifdef CONFIG_DPP2
+static u8 * dpp_get_csr_attrs(const u8 *attrs, size_t attrs_len, size_t *len)
+{
+	const u8 *b64;
+	u16 b64_len;
+
+	b64 = dpp_get_attr(attrs, attrs_len, DPP_ATTR_CSR_ATTR_REQ, &b64_len);
+	if (!b64)
+		return NULL;
+	return base64_decode((const char *) b64, b64_len, len);
+}
+#endif /* CONFIG_DPP2 */
+
+
 int dpp_conf_resp_rx(struct dpp_authentication *auth,
 		     const struct wpabuf *resp)
 {
@@ -2661,6 +2956,28 @@
 	}
 	auth->conf_resp_status = status[0];
 	wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+#ifdef CONFIG_DPP2
+	if (status[0] == DPP_STATUS_CSR_NEEDED) {
+		u8 *csrattrs;
+		size_t csrattrs_len;
+
+		wpa_printf(MSG_DEBUG, "DPP: Configurator requested CSR");
+
+		csrattrs = dpp_get_csr_attrs(unwrapped, unwrapped_len,
+					     &csrattrs_len);
+		if (!csrattrs) {
+			dpp_auth_fail(auth,
+				      "Missing or invalid CSR Attributes Request attribute");
+			goto fail;
+		}
+		wpa_hexdump(MSG_DEBUG, "DPP: CsrAttrs", csrattrs, csrattrs_len);
+		os_free(auth->csrattrs);
+		auth->csrattrs = csrattrs;
+		auth->csrattrs_len = csrattrs_len;
+		ret = -2;
+		goto fail;
+	}
+#endif /* CONFIG_DPP2 */
 	if (status[0] != DPP_STATUS_OK) {
 		dpp_auth_fail(auth, "Configurator rejected configuration");
 		goto fail;
@@ -3086,6 +3403,7 @@
 	os_free(conf->kid);
 	os_free(conf->connector);
 	EVP_PKEY_free(conf->connector_key);
+	EVP_PKEY_free(conf->pp_key);
 	os_free(conf);
 }
 
@@ -3144,9 +3462,9 @@
 }
 
 
-struct dpp_configurator *
+static struct dpp_configurator *
 dpp_keygen_configurator(const char *curve, const u8 *privkey,
-			size_t privkey_len)
+			size_t privkey_len, const u8 *pp_key, size_t pp_key_len)
 {
 	struct dpp_configurator *conf;
 
@@ -3166,7 +3484,12 @@
 					      privkey_len);
 	else
 		conf->csign = dpp_gen_keypair(conf->curve);
-	if (!conf->csign)
+	if (pp_key)
+		conf->pp_key = dpp_set_keypair(&conf->curve, pp_key,
+					       pp_key_len);
+	else
+		conf->pp_key = dpp_gen_keypair(conf->curve);
+	if (!conf->csign || !conf->pp_key)
 		goto fail;
 	conf->own = 1;
 
@@ -3207,7 +3530,7 @@
 	auth->peer_protocol_key = auth->own_protocol_key;
 	dpp_copy_csign(&auth->conf_obj[0], auth->conf->csign);
 
-	conf_obj = dpp_build_conf_obj(auth, ap, 0);
+	conf_obj = dpp_build_conf_obj(auth, ap, 0, NULL);
 	if (!conf_obj) {
 		wpabuf_free(auth->conf_obj[0].c_sign_key);
 		auth->conf_obj[0].c_sign_key = NULL;
@@ -3762,11 +4085,11 @@
 	u8 op_class, channel;
 	char chan[20];
 
-	if (peer_bi->num_freq == 0)
+	if (peer_bi->num_freq == 0 && !peer_bi->channels_listed)
 		return 0; /* no channel preference/constraint */
 
 	for (i = 0; i < peer_bi->num_freq; i++) {
-		if (own_bi->num_freq == 0 ||
+		if ((own_bi->num_freq == 0 && !own_bi->channels_listed) ||
 		    freq_included(own_bi->freq, own_bi->num_freq,
 				  peer_bi->freq[i])) {
 			freq = peer_bi->freq[i];
@@ -3853,14 +4176,15 @@
 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;
+	char *key = NULL, *ppkey = NULL;
+	u8 *privkey = NULL, *pp_key = NULL;
+	size_t privkey_len = 0, pp_key_len = 0;
 	int ret = -1;
 	struct dpp_configurator *conf = NULL;
 
 	curve = get_param(cmd, " curve=");
 	key = get_param(cmd, " key=");
+	ppkey = get_param(cmd, " ppkey=");
 
 	if (key) {
 		privkey_len = os_strlen(key) / 2;
@@ -3870,7 +4194,16 @@
 			goto fail;
 	}
 
-	conf = dpp_keygen_configurator(curve, privkey, privkey_len);
+	if (ppkey) {
+		pp_key_len = os_strlen(key) / 2;
+		pp_key = os_malloc(pp_key_len);
+		if (!pp_key ||
+		    hexstr2bin(ppkey, pp_key, pp_key_len) < 0)
+			goto fail;
+	}
+
+	conf = dpp_keygen_configurator(curve, privkey, privkey_len,
+				       pp_key, pp_key_len);
 	if (!conf)
 		goto fail;
 
@@ -3881,7 +4214,9 @@
 fail:
 	os_free(curve);
 	str_clear_free(key);
+	str_clear_free(ppkey);
 	bin_clear_free(privkey, privkey_len);
+	bin_clear_free(pp_key, pp_key_len);
 	dpp_configurator_free(conf);
 	return ret;
 }
@@ -3945,12 +4280,12 @@
 				 struct dpp_asymmetric_key *key)
 {
 	struct dpp_configurator *conf;
-	const EC_KEY *eckey;
-	const EC_GROUP *group;
+	const EC_KEY *eckey, *eckey_pp;
+	const EC_GROUP *group, *group_pp;
 	int nid;
 	const struct dpp_curve_params *curve;
 
-	if (!key->csign)
+	if (!key->csign || !key->pp_key)
 		return -1;
 	eckey = EVP_PKEY_get0_EC_KEY(key->csign);
 	if (!eckey)
@@ -3964,6 +4299,18 @@
 		wpa_printf(MSG_INFO, "DPP: Unsupported group in c-sign-key");
 		return -1;
 	}
+	eckey_pp = EVP_PKEY_get0_EC_KEY(key->pp_key);
+	if (!eckey_pp)
+		return -1;
+	group_pp = EC_KEY_get0_group(eckey_pp);
+	if (!group_pp)
+		return -1;
+	if (EC_GROUP_get_curve_name(group) !=
+	    EC_GROUP_get_curve_name(group_pp)) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Mismatch in c-sign-key and ppKey groups");
+		return -1;
+	}
 
 	conf = os_zalloc(sizeof(*conf));
 	if (!conf)
@@ -3971,6 +4318,8 @@
 	conf->curve = curve;
 	conf->csign = key->csign;
 	key->csign = NULL;
+	conf->pp_key = key->pp_key;
+	key->pp_key = NULL;
 	conf->own = 1;
 	if (dpp_configurator_gen_kid(conf) < 0) {
 		dpp_configurator_free(conf);
@@ -4009,10 +4358,8 @@
 	dpp = os_zalloc(sizeof(*dpp));
 	if (!dpp)
 		return NULL;
-	dpp->msg_ctx = config->msg_ctx;
 #ifdef CONFIG_DPP2
 	dpp->cb_ctx = config->cb_ctx;
-	dpp->process_conf_obj = config->process_conf_obj;
 	dpp->remove_bi = config->remove_bi;
 #endif /* CONFIG_DPP2 */
 
@@ -4050,6 +4397,7 @@
 
 
 #ifdef CONFIG_DPP2
+
 struct wpabuf * dpp_build_presence_announcement(struct dpp_bootstrap_info *bi)
 {
 	struct wpabuf *msg;
@@ -4066,4 +4414,17 @@
 			"DPP: Presence Announcement frame attributes", msg);
 	return msg;
 }
+
+
+void dpp_notify_chirp_received(void *msg_ctx, int id, const u8 *src,
+				unsigned int freq, const u8 *hash)
+{
+	char hex[SHA256_MAC_LEN * 2 + 1];
+
+	wpa_snprintf_hex(hex, sizeof(hex), hash, SHA256_MAC_LEN);
+	wpa_msg(msg_ctx, MSG_INFO,
+		DPP_EVENT_CHIRP_RX "id=%d src=" MACSTR " freq=%u hash=%s",
+		id, MAC2STR(src), freq, hex);
+}
+
 #endif /* CONFIG_DPP2 */
diff --git a/src/common/dpp.h b/src/common/dpp.h
index d77762e..99a1ca7 100644
--- a/src/common/dpp.h
+++ b/src/common/dpp.h
@@ -21,6 +21,7 @@
 struct hostapd_ip_addr;
 struct dpp_global;
 struct json_token;
+struct dpp_reconfig_id;
 
 #ifdef CONFIG_TESTING_OPTIONS
 #define DPP_VERSION (dpp_version_override)
@@ -87,6 +88,9 @@
 	DPP_ATTR_RECONFIG_FLAGS = 0x101D,
 	DPP_ATTR_C_SIGN_KEY_HASH = 0x101E,
 	DPP_ATTR_CSR_ATTR_REQ = 0x101F,
+	DPP_ATTR_A_NONCE = 0x1020,
+	DPP_ATTR_E_PRIME_ID = 0x1021,
+	DPP_ATTR_CONFIGURATOR_NONCE = 0x1022,
 };
 
 enum dpp_status_error {
@@ -120,6 +124,7 @@
 #define DPP_MAX_NONCE_LEN 32
 #define DPP_MAX_HASH_LEN 64
 #define DPP_MAX_SHARED_SECRET_LEN 66
+#define DPP_CP_LEN 64
 
 struct dpp_curve_params {
 	const char *name;
@@ -149,6 +154,7 @@
 	char *pk;
 	unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ];
 	unsigned int num_freq;
+	bool channels_listed;
 	u8 version;
 	int own;
 	EVP_PKEY *pubkey;
@@ -196,6 +202,7 @@
 	DPP_AKM_PSK_SAE,
 	DPP_AKM_SAE_DPP,
 	DPP_AKM_PSK_SAE_DPP,
+	DPP_AKM_DOT1X,
 };
 
 enum dpp_netrole {
@@ -221,11 +228,14 @@
 	char *passphrase;
 	u8 psk[32];
 	int psk_set;
+
+	char *csrattrs;
 };
 
 struct dpp_asymmetric_key {
 	struct dpp_asymmetric_key *next;
 	EVP_PKEY *csign;
+	EVP_PKEY *pp_key;
 	char *config_template;
 	char *connector_template;
 };
@@ -241,18 +251,22 @@
 	struct dpp_bootstrap_info *peer_bi;
 	struct dpp_bootstrap_info *own_bi;
 	struct dpp_bootstrap_info *tmp_own_bi;
+	struct dpp_bootstrap_info *tmp_peer_bi;
 	u8 waiting_pubkey_hash[SHA256_MAC_LEN];
 	int response_pending;
 	int reconfig;
 	enum dpp_connector_key reconfig_connector_key;
 	enum dpp_status_error auth_resp_status;
 	enum dpp_status_error conf_resp_status;
+	enum dpp_status_error force_conf_resp_status;
 	u8 peer_mac_addr[ETH_ALEN];
 	u8 i_nonce[DPP_MAX_NONCE_LEN];
 	u8 r_nonce[DPP_MAX_NONCE_LEN];
 	u8 e_nonce[DPP_MAX_NONCE_LEN];
+	u8 c_nonce[DPP_MAX_NONCE_LEN];
 	u8 i_capab;
 	u8 r_capab;
+	enum dpp_netrole e_netrole;
 	EVP_PKEY *own_protocol_key;
 	EVP_PKEY *peer_protocol_key;
 	EVP_PKEY *reconfig_old_protocol_key;
@@ -293,6 +307,7 @@
 	bool reconfig_success;
 	struct wpabuf *conf_req;
 	const struct wpabuf *conf_resp; /* owned by GAS server */
+	struct wpabuf *conf_resp_tcp;
 	struct dpp_configuration *conf_ap;
 	struct dpp_configuration *conf2_ap;
 	struct dpp_configuration *conf_sta;
@@ -309,6 +324,11 @@
 		int psk_set;
 		enum dpp_akm akm;
 		struct wpabuf *c_sign_key;
+		struct wpabuf *certbag;
+		struct wpabuf *certs;
+		struct wpabuf *cacert;
+		char *server_name;
+		struct wpabuf *pp_key;
 	} conf_obj[DPP_MAX_CONF_OBJ];
 	unsigned int num_conf_obj;
 	struct dpp_asymmetric_key *conf_key_pkg;
@@ -319,6 +339,16 @@
 	int akm_use_selector;
 	int configurator_set;
 	u8 transaction_id;
+	u8 *csrattrs;
+	size_t csrattrs_len;
+	bool waiting_csr;
+	struct wpabuf *csr;
+	struct wpabuf *priv_key; /* DER-encoded private key used for csr */
+	bool waiting_cert;
+	char *trusted_eap_server_name;
+	struct wpabuf *cacert;
+	struct wpabuf *certbag;
+	void *cert_resp_ctx;
 #ifdef CONFIG_TESTING_OPTIONS
 	char *config_obj_override;
 	char *discovery_override;
@@ -339,6 +369,7 @@
 	const struct dpp_curve_params *curve;
 	char *connector; /* own Connector for reconfiguration */
 	EVP_PKEY *connector_key;
+	EVP_PKEY *pp_key;
 };
 
 struct dpp_introduction {
@@ -351,6 +382,7 @@
 	const struct hostapd_ip_addr *ipaddr;
 	const u8 *pkhash;
 
+	void *msg_ctx;
 	void *cb_ctx;
 	void (*tx)(void *ctx, const u8 *addr, unsigned int freq, const u8 *msg,
 		   size_t len);
@@ -362,6 +394,11 @@
 	const char *configurator_params;
 	int tcp_port;
 	u8 allowed_roles;
+	int qr_mutual;
+	enum dpp_netrole netrole;
+	void *msg_ctx;
+	void *cb_ctx;
+	int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
 };
 
 #ifdef CONFIG_TESTING_OPTIONS
@@ -519,6 +556,10 @@
 int dpp_set_configurator(struct dpp_authentication *auth, const char *cmd);
 void dpp_auth_deinit(struct dpp_authentication *auth);
 struct wpabuf *
+dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
+		    u16 e_nonce_len, enum dpp_netrole netrole,
+		    bool cert_req);
+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,
@@ -548,9 +589,6 @@
 int dpp_configurator_get_key(const struct dpp_configurator *conf, char *buf,
 			     size_t buflen);
 void dpp_configurator_free(struct dpp_configurator *conf);
-struct dpp_configurator *
-dpp_keygen_configurator(const char *curve, const u8 *privkey,
-			size_t privkey_len);
 int dpp_configurator_own_config(struct dpp_authentication *auth,
 				const char *curve, int ap);
 enum dpp_status_error
@@ -595,6 +633,11 @@
 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 wpabuf * dpp_build_csr(struct dpp_authentication *auth,
+			      const char *name);
+struct wpabuf * dpp_pkcs7_certs(const struct wpabuf *pkcs7);
+int dpp_validate_csr(struct dpp_authentication *auth, const struct wpabuf *csr);
+
 struct dpp_bootstrap_info * dpp_add_qr_code(struct dpp_global *dpp,
 					    const char *uri);
 struct dpp_bootstrap_info * dpp_add_nfc_uri(struct dpp_global *dpp,
@@ -634,14 +677,23 @@
 int dpp_controller_start(struct dpp_global *dpp,
 			 struct dpp_controller_config *config);
 void dpp_controller_stop(struct dpp_global *dpp);
+struct dpp_authentication * dpp_controller_get_auth(struct dpp_global *dpp,
+						    unsigned int id);
+void dpp_controller_new_qr_code(struct dpp_global *dpp,
+				struct dpp_bootstrap_info *bi);
 int dpp_tcp_init(struct dpp_global *dpp, struct dpp_authentication *auth,
-		 const struct hostapd_ip_addr *addr, int port);
+		 const struct hostapd_ip_addr *addr, int port,
+		 const char *name, enum dpp_netrole netrole, void *msg_ctx,
+		 void *cb_ctx,
+		 int (*process_conf_obj)(void *ctx,
+					 struct dpp_authentication *auth));
+
 struct wpabuf * dpp_build_presence_announcement(struct dpp_bootstrap_info *bi);
+void dpp_notify_chirp_received(void *msg_ctx, int id, const u8 *src,
+				unsigned int freq, const u8 *hash);
 
 struct dpp_global_config {
-	void *msg_ctx;
 	void *cb_ctx;
-	int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
 	void (*remove_bi)(void *ctx, struct dpp_bootstrap_info *bi);
 };
 
@@ -652,10 +704,15 @@
 /* dpp_reconfig.c */
 
 struct wpabuf * dpp_build_reconfig_announcement(const u8 *csign_key,
-						size_t csign_key_len);
+						size_t csign_key_len,
+						const u8 *net_access_key,
+						size_t net_access_key_len,
+						struct dpp_reconfig_id *id);
 struct dpp_authentication *
 dpp_reconfig_init(struct dpp_global *dpp, void *msg_ctx,
-		  struct dpp_configurator *conf, unsigned int freq);
+		  struct dpp_configurator *conf, unsigned int freq, u16 group,
+		  const u8 *a_nonce_attr, size_t a_nonce_len,
+		  const u8 *e_id_attr, size_t e_id_len);
 struct dpp_authentication *
 dpp_reconfig_auth_req_rx(struct dpp_global *dpp, void *msg_ctx,
 			 const char *own_connector,
@@ -669,5 +726,12 @@
 int dpp_reconfig_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
 			      const u8 *attr_start, size_t attr_len);
 
+struct dpp_reconfig_id * dpp_gen_reconfig_id(const u8 *csign_key,
+					     size_t csign_key_len,
+					     const u8 *pp_key,
+					     size_t pp_key_len);
+int dpp_update_reconfig_id(struct dpp_reconfig_id *id);
+void dpp_free_reconfig_id(struct dpp_reconfig_id *id);
+
 #endif /* CONFIG_DPP */
 #endif /* DPP_H */
diff --git a/src/common/dpp_backup.c b/src/common/dpp_backup.c
index c675c42..3b81f09 100644
--- a/src/common/dpp_backup.c
+++ b/src/common/dpp_backup.c
@@ -40,6 +40,7 @@
 		struct dpp_asymmetric_key *next = key->next;
 
 		EVP_PKEY_free(key->csign);
+		EVP_PKEY_free(key->pp_key);
 		str_clear_free(key->config_template);
 		str_clear_free(key->connector_template);
 		os_free(key);
@@ -48,35 +49,62 @@
 }
 
 
-static struct wpabuf * dpp_build_conf_params(void)
+static struct wpabuf * dpp_build_conf_params(struct dpp_configurator *conf)
 {
-	struct wpabuf *buf;
+	struct wpabuf *buf, *priv_key = NULL;
 	size_t len;
 	/* TODO: proper template values */
 	const char *conf_template = "{\"wi-fi_tech\":\"infra\",\"discovery\":{\"ssid\":\"test\"},\"cred\":{\"akm\":\"dpp\"}}";
 	const char *connector_template = NULL;
+	EC_KEY *eckey;
+	unsigned char *der = NULL;
+	int der_len;
+
+	if (!conf->pp_key)
+		return NULL;
+	eckey = EVP_PKEY_get0_EC_KEY(conf->pp_key);
+	if (!eckey)
+		return NULL;
+
+	EC_KEY_set_enc_flags(eckey, EC_PKEY_NO_PUBKEY);
+	der_len = i2d_ECPrivateKey(eckey, &der);
+	if (der_len > 0)
+		priv_key = wpabuf_alloc_copy(der, der_len);
+	OPENSSL_free(der);
+	if (!priv_key)
+		goto fail;
 
 	len = 100 + os_strlen(conf_template);
 	if (connector_template)
 		len += os_strlen(connector_template);
+	if (priv_key)
+		len += wpabuf_len(priv_key);
 	buf = wpabuf_alloc(len);
 	if (!buf)
-		return NULL;
+		goto fail;
 
 	/*
 	 * DPPConfigurationParameters ::= SEQUENCE {
+	 *    privacyProtectionKey      PrivateKey,
 	 *    configurationTemplate	UTF8String,
 	 *    connectorTemplate		UTF8String OPTIONAL}
 	 */
 
+	/* PrivateKey ::= OCTET STRING */
+	asn1_put_octet_string(buf, priv_key);
+
 	asn1_put_utf8string(buf, conf_template);
 	if (connector_template)
 		asn1_put_utf8string(buf, connector_template);
+	wpabuf_clear_free(priv_key);
 	return asn1_encaps(buf, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+fail:
+	wpabuf_clear_free(priv_key);
+	return NULL;
 }
 
 
-static struct wpabuf * dpp_build_attribute(void)
+static struct wpabuf * dpp_build_attribute(struct dpp_configurator *conf)
 {
 	struct wpabuf *conf_params, *attr;
 
@@ -88,7 +116,7 @@
 	 *    type OBJECT IDENTIFIER,
 	 *    values SET SIZE(1..MAX) OF Type
 	 */
-	conf_params = dpp_build_conf_params();
+	conf_params = dpp_build_conf_params(conf);
 	conf_params = asn1_encaps(conf_params, ASN1_CLASS_UNIVERSAL,
 				  ASN1_TAG_SET);
 	if (!conf_params)
@@ -167,7 +195,7 @@
 	alg = dpp_build_key_alg(auth->conf->curve);
 
 	/* Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } } */
-	attr = dpp_build_attribute();
+	attr = dpp_build_attribute(auth->conf);
 	attr = asn1_encaps(attr, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SET);
 	if (!priv_key || !attr || !alg)
 		goto fail;
@@ -189,7 +217,7 @@
 	if (!key)
 		goto fail;
 
-	asn1_put_integer(key, 1); /* version = v2(1) */
+	asn1_put_integer(key, 0); /* version = v1(0) */
 
 	/* PrivateKeyAlgorithmIdentifier */
 	wpabuf_put_buf(key, alg);
@@ -275,18 +303,21 @@
 	struct wpabuf *pwri = NULL, *enc_key = NULL, *key_der_alg = NULL,
 		*key_enc_alg = NULL, *salt;
 	u8 kek[DPP_MAX_HASH_LEN];
-	const u8 *key;
+	u8 key[DPP_MAX_HASH_LEN];
 	size_t key_len;
+	int res;
 
 	salt = wpabuf_alloc(64);
 	if (!salt || os_get_random(wpabuf_put(salt, 64), 64) < 0)
 		goto fail;
 	wpa_hexdump_buf(MSG_DEBUG, "DPP: PBKDF2 salt", salt);
 
-	/* TODO: For initial testing, use ke as the key. Replace this with a
-	 * new key once that has been defined. */
-	key = auth->ke;
 	key_len = auth->curve->hash_len;
+	/* password = HKDF-Expand(bk, "Enveloped Data Password", length) */
+	res = dpp_hkdf_expand(key_len, auth->bk, key_len,
+			      "Enveloped Data Password", key, key_len);
+	if (res < 0)
+		goto fail;
 	wpa_hexdump_key(MSG_DEBUG, "DPP: PBKDF2 key", key, key_len);
 
 	if (dpp_pbkdf2(hash_len, key, key_len, wpabuf_head(salt), 64, 1000,
@@ -905,7 +936,7 @@
 	/* Version ::= INTEGER { v1(0), v2(1) } (v1, ..., v2) */
 	if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
 		goto fail;
-	if (val != 1) {
+	if (val != 0 && val != 1) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Unsupported DPPAsymmetricKeyPackage version %d",
 			   val);
@@ -1056,6 +1087,7 @@
 
 	/*
 	 * DPPConfigurationParameters ::= SEQUENCE {
+	 *    privacyProtectionKey      PrivateKey,
 	 *    configurationTemplate	UTF8String,
 	 *    connectorTemplate		UTF8String OPTIONAL}
 	 */
@@ -1072,6 +1104,37 @@
 	end = pos;
 	pos = hdr.payload;
 
+	/*
+	 * PrivateKey ::= OCTET STRING
+	 *    (Contains DER encoding of ECPrivateKey)
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_OCTETSTRING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Expected OCTETSTRING (PrivateKey) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		goto fail;
+	}
+	wpa_hexdump_key(MSG_MSGDUMP, "DPP: privacyProtectionKey",
+			hdr.payload, hdr.length);
+	pos = hdr.payload + hdr.length;
+	eckey = d2i_ECPrivateKey(NULL, &hdr.payload, hdr.length);
+	if (!eckey) {
+		wpa_printf(MSG_INFO,
+			   "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	key->pp_key = EVP_PKEY_new();
+	if (!key->pp_key || EVP_PKEY_assign_EC_KEY(key->pp_key, eckey) != 1) {
+		EC_KEY_free(eckey);
+		goto fail;
+	}
+	if (wpa_debug_show_keys)
+		dpp_debug_print_key("DPP: Received privacyProtectionKey",
+				    key->pp_key);
+
 	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
 	    hdr.class != ASN1_CLASS_UNIVERSAL ||
 	    hdr.tag != ASN1_TAG_UTF8STRING) {
@@ -1151,7 +1214,7 @@
 int dpp_conf_resp_env_data(struct dpp_authentication *auth,
 			   const u8 *env_data, size_t env_data_len)
 {
-	const u8 *key;
+	u8 key[DPP_MAX_HASH_LEN];
 	size_t key_len;
 	u8 kek[DPP_MAX_HASH_LEN];
 	u8 cont_encr_key[DPP_MAX_HASH_LEN];
@@ -1167,10 +1230,12 @@
 	if (dpp_parse_enveloped_data(env_data, env_data_len, &data) < 0)
 		return -1;
 
-	/* TODO: For initial testing, use ke as the key. Replace this with a
-	 * new key once that has been defined. */
-	key = auth->ke;
 	key_len = auth->curve->hash_len;
+	/* password = HKDF-Expand(bk, "Enveloped Data Password", length) */
+	res = dpp_hkdf_expand(key_len, auth->bk, key_len,
+			      "Enveloped Data Password", key, key_len);
+	if (res < 0)
+		return -1;
 	wpa_hexdump_key(MSG_DEBUG, "DPP: PBKDF2 key", key, key_len);
 
 	if (dpp_pbkdf2(data.prf_hash_len, key, key_len, data.salt, 64, 1000,
diff --git a/src/common/dpp_crypto.c b/src/common/dpp_crypto.c
index 8bf2a74..7c48015 100644
--- a/src/common/dpp_crypto.c
+++ b/src/common/dpp_crypto.c
@@ -12,12 +12,14 @@
 #include <openssl/err.h>
 #include <openssl/asn1.h>
 #include <openssl/asn1t.h>
+#include <openssl/pem.h>
 
 #include "utils/common.h"
 #include "utils/base64.h"
 #include "utils/json.h"
 #include "common/ieee802_11_defs.h"
 #include "crypto/crypto.h"
+#include "crypto/random.h"
 #include "crypto/sha384.h"
 #include "crypto/sha512.h"
 #include "dpp.h"
@@ -129,6 +131,18 @@
 }
 
 
+const struct dpp_curve_params * dpp_get_curve_ike_group(u16 group)
+{
+	int i;
+
+	for (i = 0; dpp_curves[i].name; i++) {
+		if (dpp_curves[i].ike_group == group)
+			return &dpp_curves[i];
+	}
+	return NULL;
+}
+
+
 void dpp_debug_print_point(const char *title, const EC_GROUP *group,
 			   const EC_POINT *point)
 {
@@ -855,7 +869,7 @@
 			"DPP: bk = HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x])",
 			auth->bk, hash_len);
 
-	/* ke = HKDF-Expand(bkK, "DPP Key", length) */
+	/* ke = HKDF-Expand(bk, "DPP Key", length) */
 	res = dpp_hkdf_expand(hash_len, auth->bk, hash_len, info_ke, auth->ke,
 			      hash_len);
 	if (res < 0)
@@ -2256,6 +2270,7 @@
 	u8 prk[DPP_MAX_HASH_LEN];
 	const struct dpp_curve_params *curve;
 	int res = -1;
+	u8 nonces[2 * DPP_MAX_NONCE_LEN];
 
 	own_key = dpp_set_keypair(&auth->curve, net_access_key,
 				  net_access_key_len);
@@ -2271,7 +2286,7 @@
 
 	if (auth->curve != curve) {
 		wpa_printf(MSG_DEBUG,
-			   "DPP: Mismatching netAccessKey curves (%s != %s)",
+			   "DPP: Mismatching netAccessKey curves (own=%s != peer=%s)",
 			   auth->curve->name, curve->name);
 		goto fail;
 	}
@@ -2280,6 +2295,13 @@
 	if (!auth->own_protocol_key)
 		goto fail;
 
+	if (random_get_bytes(auth->e_nonce, auth->curve->nonce_len)) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to generate E-nonce");
+		goto fail;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "DPP: E-nonce",
+			auth->e_nonce, auth->curve->nonce_len);
+
 	/* M = { cR + pR } * CI */
 	cR = EVP_PKEY_get0_EC_KEY(own_key);
 	pR = EVP_PKEY_get0_EC_KEY(auth->own_protocol_key);
@@ -2312,10 +2334,12 @@
 		goto fail;
 	wpa_hexdump_key(MSG_DEBUG, "DPP: M.x", Mx, curve->prime_len);
 
-	/* ke = HKDF(I-nonce, "dpp reconfig key", M.x) */
+	/* ke = HKDF(C-nonce | E-nonce, "dpp reconfig key", M.x) */
 
-	/* HKDF-Extract(I-nonce, M.x) */
-	if (dpp_hmac(curve->hash_len, auth->i_nonce, curve->nonce_len,
+	/* HKDF-Extract(C-nonce | E-nonce, M.x) */
+	os_memcpy(nonces, auth->c_nonce, curve->nonce_len);
+	os_memcpy(&nonces[curve->nonce_len], auth->e_nonce, curve->nonce_len);
+	if (dpp_hmac(curve->hash_len, nonces, 2 * curve->nonce_len,
 		     Mx, curve->prime_len, prk) < 0)
 		goto fail;
 	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK", prk, curve->hash_len);
@@ -2325,7 +2349,7 @@
 			    "dpp reconfig key", auth->ke, curve->hash_len) < 0)
 		goto fail;
 	wpa_hexdump_key(MSG_DEBUG,
-			"DPP: ke = HKDF(I-nonce, \"dpp reconfig key\", M.x)",
+			"DPP: ke = HKDF(C-nonce | E-nonce, \"dpp reconfig key\", M.x)",
 			auth->ke, curve->hash_len);
 
 	res = 0;
@@ -2362,6 +2386,7 @@
 	u8 prk[DPP_MAX_HASH_LEN];
 	int res = -1;
 	const struct dpp_curve_params *curve;
+	u8 nonces[2 * DPP_MAX_NONCE_LEN];
 
 	pr = dpp_set_pubkey_point(auth->conf->connector_key,
 				  r_proto, r_proto_len);
@@ -2380,7 +2405,7 @@
 	dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
 	if (auth->curve != curve) {
 		wpa_printf(MSG_DEBUG,
-			   "DPP: Mismatching netAccessKey curves (%s != %s)",
+			   "DPP: Mismatching netAccessKey curves (own=%s != peer=%s)",
 			   auth->curve->name, curve->name);
 		goto fail;
 	}
@@ -2407,10 +2432,12 @@
 
 	wpa_hexdump_key(MSG_DEBUG, "DPP: M.x", Mx, curve->prime_len);
 
-	/* ke = HKDF(I-nonce, "dpp reconfig key", M.x) */
+	/* ke = HKDF(C-nonce | E-nonce, "dpp reconfig key", M.x) */
 
-	/* HKDF-Extract(I-nonce, M.x) */
-	if (dpp_hmac(curve->hash_len, auth->i_nonce, curve->nonce_len,
+	/* HKDF-Extract(C-nonce | E-nonce, M.x) */
+	os_memcpy(nonces, auth->c_nonce, curve->nonce_len);
+	os_memcpy(&nonces[curve->nonce_len], auth->e_nonce, curve->nonce_len);
+	if (dpp_hmac(curve->hash_len, nonces, 2 * curve->nonce_len,
 		     Mx, curve->prime_len, prk) < 0)
 		goto fail;
 	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK", prk, curve->hash_len);
@@ -2420,7 +2447,7 @@
 			    "dpp reconfig key", auth->ke, curve->hash_len) < 0)
 		goto fail;
 	wpa_hexdump_key(MSG_DEBUG,
-			"DPP: ke = HKDF(I-nonce, \"dpp reconfig key\", M.x)",
+			"DPP: ke = HKDF(C-nonce | E-nonce, \"dpp reconfig key\", M.x)",
 			auth->ke, curve->hash_len);
 
 	res = 0;
@@ -2664,6 +2691,520 @@
 	os_free(pfs);
 }
 
+
+struct wpabuf * dpp_build_csr(struct dpp_authentication *auth, const char *name)
+{
+	X509_REQ *req = NULL;
+	struct wpabuf *buf = NULL;
+	unsigned char *der;
+	int der_len;
+	EVP_PKEY *key;
+	const EVP_MD *sign_md;
+	unsigned int hash_len = auth->curve->hash_len;
+	EC_KEY *eckey;
+	BIO *out = NULL;
+	u8 cp[DPP_CP_LEN];
+	char *password;
+	size_t password_len;
+	int res;
+
+	/* TODO: use auth->csrattrs */
+
+	/* TODO: support generation of a new private key if csrAttrs requests
+	 * a specific group to be used */
+	key = auth->own_protocol_key;
+
+	eckey = EVP_PKEY_get1_EC_KEY(key);
+	if (!eckey)
+		goto fail;
+	der = NULL;
+	der_len = i2d_ECPrivateKey(eckey, &der);
+	if (der_len <= 0)
+		goto fail;
+	wpabuf_free(auth->priv_key);
+	auth->priv_key = wpabuf_alloc_copy(der, der_len);
+	OPENSSL_free(der);
+	if (!auth->priv_key)
+		goto fail;
+
+	req = X509_REQ_new();
+	if (!req || !X509_REQ_set_pubkey(req, key))
+		goto fail;
+
+	if (name) {
+		X509_NAME *n;
+
+		n = X509_REQ_get_subject_name(req);
+		if (!n)
+			goto fail;
+
+		if (X509_NAME_add_entry_by_txt(
+			    n, "CN", MBSTRING_UTF8,
+			    (const unsigned char *) name, -1, -1, 0) != 1)
+			goto fail;
+	}
+
+	/* cp = HKDF-Expand(bk, "CSR challengePassword", 64) */
+	if (dpp_hkdf_expand(hash_len, auth->bk, hash_len,
+			    "CSR challengePassword", cp, DPP_CP_LEN) < 0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG,
+			"DPP: cp = HKDF-Expand(bk, \"CSR challengePassword\", 64)",
+			cp, DPP_CP_LEN);
+	password = base64_encode_no_lf(cp, DPP_CP_LEN, &password_len);
+	forced_memzero(cp, DPP_CP_LEN);
+	if (!password)
+		goto fail;
+
+	res = X509_REQ_add1_attr_by_NID(req, NID_pkcs9_challengePassword,
+					V_ASN1_UTF8STRING,
+					(const unsigned char *) password,
+					password_len);
+	bin_clear_free(password, password_len);
+	if (!res)
+		goto fail;
+
+	/* TODO */
+
+	/* TODO: hash func selection based on csrAttrs */
+	if (hash_len == SHA256_MAC_LEN) {
+		sign_md = EVP_sha256();
+	} else if (hash_len == SHA384_MAC_LEN) {
+		sign_md = EVP_sha384();
+	} else if (hash_len == SHA512_MAC_LEN) {
+		sign_md = EVP_sha512();
+	} else {
+		wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm");
+		goto fail;
+	}
+
+	if (!X509_REQ_sign(req, key, sign_md))
+		goto fail;
+
+	der = NULL;
+	der_len = i2d_X509_REQ(req, &der);
+	if (der_len < 0)
+		goto fail;
+	buf = wpabuf_alloc_copy(der, der_len);
+	OPENSSL_free(der);
+
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: CSR", buf);
+
+fail:
+	BIO_free_all(out);
+	X509_REQ_free(req);
+	return buf;
+}
+
+
+struct wpabuf * dpp_pkcs7_certs(const struct wpabuf *pkcs7)
+{
+#ifdef OPENSSL_IS_BORINGSSL
+	CBS pkcs7_cbs;
+#else /* OPENSSL_IS_BORINGSSL */
+	PKCS7 *p7 = NULL;
+	const unsigned char *p = wpabuf_head(pkcs7);
+#endif /* OPENSSL_IS_BORINGSSL */
+	STACK_OF(X509) *certs;
+	int i, num;
+	BIO *out = NULL;
+	size_t rlen;
+	struct wpabuf *pem = NULL;
+	int res;
+
+#ifdef OPENSSL_IS_BORINGSSL
+	certs = sk_X509_new_null();
+	if (!certs)
+		goto fail;
+	CBS_init(&pkcs7_cbs, wpabuf_head(pkcs7), wpabuf_len(pkcs7));
+	if (!PKCS7_get_certificates(certs, &pkcs7_cbs)) {
+		wpa_printf(MSG_INFO, "DPP: Could not parse PKCS#7 object: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+#else /* OPENSSL_IS_BORINGSSL */
+	p7 = d2i_PKCS7(NULL, &p, wpabuf_len(pkcs7));
+	if (!p7) {
+		wpa_printf(MSG_INFO, "DPP: Could not parse PKCS#7 object: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	switch (OBJ_obj2nid(p7->type)) {
+	case NID_pkcs7_signed:
+		certs = p7->d.sign->cert;
+		break;
+	case NID_pkcs7_signedAndEnveloped:
+		certs = p7->d.signed_and_enveloped->cert;
+		break;
+	default:
+		certs = NULL;
+		break;
+	}
+#endif /* OPENSSL_IS_BORINGSSL */
+
+	if (!certs || ((num = sk_X509_num(certs)) == 0)) {
+		wpa_printf(MSG_INFO,
+			   "DPP: No certificates found in PKCS#7 object");
+		goto fail;
+	}
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		goto fail;
+
+	for (i = 0; i < num; i++) {
+		X509 *cert = sk_X509_value(certs, i);
+
+		PEM_write_bio_X509(out, cert);
+	}
+
+	rlen = BIO_ctrl_pending(out);
+	pem = wpabuf_alloc(rlen);
+	if (!pem)
+		goto fail;
+	res = BIO_read(out, wpabuf_put(pem, 0), rlen);
+	if (res <= 0) {
+		wpabuf_free(pem);
+		goto fail;
+	}
+	wpabuf_put(pem, res);
+
+fail:
+#ifdef OPENSSL_IS_BORINGSSL
+	if (certs)
+		sk_X509_pop_free(certs, X509_free);
+#else /* OPENSSL_IS_BORINGSSL */
+	PKCS7_free(p7);
+#endif /* OPENSSL_IS_BORINGSSL */
+	if (out)
+		BIO_free_all(out);
+
+	return pem;
+}
+
+
+int dpp_validate_csr(struct dpp_authentication *auth, const struct wpabuf *csr)
+{
+	X509_REQ *req;
+	const unsigned char *pos;
+	EVP_PKEY *pkey;
+	int res, loc, ret = -1;
+	X509_ATTRIBUTE *attr;
+	ASN1_TYPE *type;
+	ASN1_STRING *str;
+	unsigned char *utf8 = NULL;
+	unsigned char *cp = NULL;
+	size_t cp_len;
+	u8 exp_cp[DPP_CP_LEN];
+	unsigned int hash_len = auth->curve->hash_len;
+
+	pos = wpabuf_head(csr);
+	req = d2i_X509_REQ(NULL, &pos, wpabuf_len(csr));
+	if (!req) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to parse CSR");
+		return -1;
+	}
+
+	pkey = X509_REQ_get_pubkey(req);
+	if (!pkey) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to get public key from CSR");
+		goto fail;
+	}
+
+	res = X509_REQ_verify(req, pkey);
+	EVP_PKEY_free(pkey);
+	if (res != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: CSR does not have a valid signature");
+		goto fail;
+	}
+
+	loc = X509_REQ_get_attr_by_NID(req, NID_pkcs9_challengePassword, -1);
+	if (loc < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: CSR does not include challengePassword");
+		goto fail;
+	}
+
+	attr = X509_REQ_get_attr(req, loc);
+	if (!attr) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not get challengePassword attribute");
+		goto fail;
+	}
+
+	type = X509_ATTRIBUTE_get0_type(attr, 0);
+	if (!type) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not get challengePassword attribute type");
+		goto fail;
+	}
+
+	res = ASN1_TYPE_get(type);
+	/* This is supposed to be UTF8String, but allow other strings as well
+	 * since challengePassword is using ASCII (base64 encoded). */
+	if (res != V_ASN1_UTF8STRING && res != V_ASN1_PRINTABLESTRING &&
+	    res != V_ASN1_IA5STRING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected challengePassword attribute type %d",
+			   res);
+		goto fail;
+	}
+
+	str = X509_ATTRIBUTE_get0_data(attr, 0, res, NULL);
+	if (!str) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not get ASN.1 string for challengePassword");
+		goto fail;
+	}
+
+	res = ASN1_STRING_to_UTF8(&utf8, str);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not get UTF8 version of challengePassword");
+		goto fail;
+	}
+
+	cp = base64_decode((const char *) utf8, res, &cp_len);
+	OPENSSL_free(utf8);
+	if (!cp) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not base64 decode challengePassword");
+		goto fail;
+	}
+	if (cp_len != DPP_CP_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected cp length (%zu) in CSR challengePassword",
+			   cp_len);
+		goto fail;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "DPP: cp from CSR challengePassword",
+			cp, cp_len);
+
+	/* cp = HKDF-Expand(bk, "CSR challengePassword", 64) */
+	if (dpp_hkdf_expand(hash_len, auth->bk, hash_len,
+			    "CSR challengePassword", exp_cp, DPP_CP_LEN) < 0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG,
+			"DPP: cp = HKDF-Expand(bk, \"CSR challengePassword\", 64)",
+			exp_cp, DPP_CP_LEN);
+	if (os_memcmp_const(cp, exp_cp, DPP_CP_LEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: CSR challengePassword does not match calculated cp");
+		goto fail;
+	}
+
+	ret = 0;
+fail:
+	os_free(cp);
+	X509_REQ_free(req);
+	return ret;
+}
+
+
+struct dpp_reconfig_id * dpp_gen_reconfig_id(const u8 *csign_key,
+					     size_t csign_key_len,
+					     const u8 *pp_key,
+					     size_t pp_key_len)
+{
+	const unsigned char *p;
+	EVP_PKEY *csign = NULL, *ppkey = NULL;
+	struct dpp_reconfig_id *id = NULL;
+	BN_CTX *ctx = NULL;
+	BIGNUM *bn = NULL, *q = NULL;
+	const EC_KEY *eckey;
+	const EC_GROUP *group;
+	EC_POINT *e_id = NULL;
+
+	p = csign_key;
+	csign = d2i_PUBKEY(NULL, &p, csign_key_len);
+	if (!csign)
+		goto fail;
+
+	if (!pp_key)
+		goto fail;
+	p = pp_key;
+	ppkey = d2i_PUBKEY(NULL, &p, pp_key_len);
+	if (!ppkey)
+		goto fail;
+
+	eckey = EVP_PKEY_get0_EC_KEY(csign);
+	if (!eckey)
+		goto fail;
+	group = EC_KEY_get0_group(eckey);
+	if (!group)
+		goto fail;
+
+	e_id = EC_POINT_new(group);
+	ctx = BN_CTX_new();
+	bn = BN_new();
+	q = BN_new();
+	if (!e_id || !ctx || !bn || !q ||
+	    !EC_GROUP_get_order(group, q, ctx) ||
+	    !BN_rand_range(bn, q) ||
+	    !EC_POINT_mul(group, e_id, bn, NULL, NULL, ctx))
+		goto fail;
+
+	dpp_debug_print_point("DPP: Generated random point E-id", group, e_id);
+
+	id = os_zalloc(sizeof(*id));
+	if (!id)
+		goto fail;
+	id->group = group;
+	id->e_id = e_id;
+	e_id = NULL;
+	id->csign = csign;
+	csign = NULL;
+	id->pp_key = ppkey;
+	ppkey = NULL;
+fail:
+	EC_POINT_free(e_id);
+	EVP_PKEY_free(csign);
+	EVP_PKEY_free(ppkey);
+	BN_clear_free(bn);
+	BN_CTX_free(ctx);
+	return id;
+}
+
+
+static EVP_PKEY * dpp_pkey_from_point(const EC_GROUP *group,
+				      const EC_POINT *point)
+{
+	EC_KEY *eckey;
+	EVP_PKEY *pkey = NULL;
+
+	eckey = EC_KEY_new();
+	if (!eckey ||
+	    EC_KEY_set_group(eckey, group) != 1 ||
+	    EC_KEY_set_public_key(eckey, point) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to set EC_KEY: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
+
+	pkey = EVP_PKEY_new();
+	if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) {
+		wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY");
+		EVP_PKEY_free(pkey);
+		pkey = NULL;
+		goto fail;
+	}
+
+fail:
+	EC_KEY_free(eckey);
+	return pkey;
+}
+
+
+int dpp_update_reconfig_id(struct dpp_reconfig_id *id)
+{
+	BN_CTX *ctx = NULL;
+	BIGNUM *bn = NULL, *q = NULL;
+	EC_POINT *e_prime_id = NULL, *a_nonce = NULL;
+	int ret = -1;
+	const EC_KEY *pp;
+	const EC_POINT *pp_point;
+
+	pp = EVP_PKEY_get0_EC_KEY(id->pp_key);
+	if (!pp)
+		goto fail;
+	pp_point = EC_KEY_get0_public_key(pp);
+	e_prime_id = EC_POINT_new(id->group);
+	a_nonce = EC_POINT_new(id->group);
+	ctx = BN_CTX_new();
+	bn = BN_new();
+	q = BN_new();
+	/* Generate random 0 <= a-nonce < q
+	 * A-NONCE = a-nonce * G
+	 * E'-id = E-id + a-nonce * P_pk */
+	if (!pp_point || !e_prime_id || !a_nonce || !ctx || !bn || !q ||
+	    !EC_GROUP_get_order(id->group, q, ctx) ||
+	    !BN_rand_range(bn, q) || /* bn = a-nonce */
+	    !EC_POINT_mul(id->group, a_nonce, bn, NULL, NULL, ctx) ||
+	    !EC_POINT_mul(id->group, e_prime_id, NULL, pp_point, bn, ctx) ||
+	    !EC_POINT_add(id->group, e_prime_id, id->e_id, e_prime_id, ctx))
+		goto fail;
+
+	dpp_debug_print_point("DPP: Generated A-NONCE", id->group, a_nonce);
+	dpp_debug_print_point("DPP: Encrypted E-id to E'-id",
+			      id->group, e_prime_id);
+
+	EVP_PKEY_free(id->a_nonce);
+	EVP_PKEY_free(id->e_prime_id);
+	id->a_nonce = dpp_pkey_from_point(id->group, a_nonce);
+	id->e_prime_id = dpp_pkey_from_point(id->group, e_prime_id);
+	if (!id->a_nonce || !id->e_prime_id)
+		goto fail;
+
+	ret = 0;
+
+fail:
+	EC_POINT_free(e_prime_id);
+	EC_POINT_free(a_nonce);
+	BN_clear_free(bn);
+	BN_CTX_free(ctx);
+	return ret;
+}
+
+
+void dpp_free_reconfig_id(struct dpp_reconfig_id *id)
+{
+	if (id) {
+		EC_POINT_clear_free(id->e_id);
+		EVP_PKEY_free(id->csign);
+		EVP_PKEY_free(id->a_nonce);
+		EVP_PKEY_free(id->e_prime_id);
+		EVP_PKEY_free(id->pp_key);
+		os_free(id);
+	}
+}
+
+
+EC_POINT * dpp_decrypt_e_id(EVP_PKEY *ppkey, EVP_PKEY *a_nonce,
+			    EVP_PKEY *e_prime_id)
+{
+	const EC_KEY *pp_ec, *a_nonce_ec, *e_prime_id_ec;
+	const BIGNUM *pp_bn;
+	const EC_GROUP *group;
+	EC_POINT *e_id = NULL;
+	const EC_POINT *a_nonce_point, *e_prime_id_point;
+	BN_CTX *ctx = NULL;
+
+	if (!ppkey)
+		return NULL;
+
+	/* E-id = E'-id - s_C * A-NONCE */
+	pp_ec = EVP_PKEY_get0_EC_KEY(ppkey);
+	a_nonce_ec = EVP_PKEY_get0_EC_KEY(a_nonce);
+	e_prime_id_ec = EVP_PKEY_get0_EC_KEY(e_prime_id);
+	if (!pp_ec || !a_nonce_ec || !e_prime_id_ec)
+		return NULL;
+	pp_bn = EC_KEY_get0_private_key(pp_ec);
+	group = EC_KEY_get0_group(pp_ec);
+	a_nonce_point = EC_KEY_get0_public_key(a_nonce_ec);
+	e_prime_id_point = EC_KEY_get0_public_key(e_prime_id_ec);
+	ctx = BN_CTX_new();
+	if (!pp_bn || !group || !a_nonce_point || !e_prime_id_point || !ctx)
+		goto fail;
+	e_id = EC_POINT_new(group);
+	if (!e_id ||
+	    !EC_POINT_mul(group, e_id, NULL, a_nonce_point, pp_bn, ctx) ||
+	    !EC_POINT_invert(group, e_id, ctx) ||
+	    !EC_POINT_add(group, e_id, e_prime_id_point, e_id, ctx)) {
+		EC_POINT_clear_free(e_id);
+		goto fail;
+	}
+
+	dpp_debug_print_point("DPP: Decrypted E-id", group, e_id);
+
+fail:
+	BN_CTX_free(ctx);
+	return e_id;
+}
+
 #endif /* CONFIG_DPP2 */
 
 
diff --git a/src/common/dpp_i.h b/src/common/dpp_i.h
index e66eb6c..af12467 100644
--- a/src/common/dpp_i.h
+++ b/src/common/dpp_i.h
@@ -73,6 +73,7 @@
 const struct dpp_curve_params * dpp_get_curve_name(const char *name);
 const struct dpp_curve_params * dpp_get_curve_jwk_crv(const char *name);
 const struct dpp_curve_params * dpp_get_curve_nid(int nid);
+const struct dpp_curve_params * dpp_get_curve_ike_group(u16 group);
 int dpp_bi_pubkey_hash(struct dpp_bootstrap_info *bi,
 		       const u8 *data, size_t data_len);
 struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix);
@@ -132,11 +133,22 @@
 int dpp_reconfig_derive_ke_initiator(struct dpp_authentication *auth,
 				     const u8 *r_proto, u16 r_proto_len,
 				     struct json_token *net_access_key);
+EC_POINT * dpp_decrypt_e_id(EVP_PKEY *ppkey, EVP_PKEY *a_nonce,
+			    EVP_PKEY *e_prime_id);
 char * dpp_sign_connector(struct dpp_configurator *conf,
 			  const struct wpabuf *dppcon);
 int dpp_test_gen_invalid_key(struct wpabuf *msg,
 			     const struct dpp_curve_params *curve);
 
+struct dpp_reconfig_id {
+	const EC_GROUP *group;
+	EC_POINT *e_id; /* E-id */
+	EVP_PKEY *csign;
+	EVP_PKEY *a_nonce; /* A-NONCE */
+	EVP_PKEY *e_prime_id; /* E'-id */
+	EVP_PKEY *pp_key;
+};
+
 /* dpp_tcp.c */
 
 void dpp_controller_conn_status_result_wait_timeout(void *eloop_ctx,
diff --git a/src/common/dpp_reconfig.c b/src/common/dpp_reconfig.c
index 0bb00cc..c4a0273 100644
--- a/src/common/dpp_reconfig.c
+++ b/src/common/dpp_reconfig.c
@@ -34,9 +34,12 @@
 
 
 struct wpabuf * dpp_build_reconfig_announcement(const u8 *csign_key,
-						size_t csign_key_len)
+						size_t csign_key_len,
+						const u8 *net_access_key,
+						size_t net_access_key_len,
+						struct dpp_reconfig_id *id)
 {
-	struct wpabuf *msg;
+	struct wpabuf *msg = NULL;
 	EVP_PKEY *csign = NULL;
 	const unsigned char *p;
 	struct wpabuf *uncomp;
@@ -44,39 +47,86 @@
 	const u8 *addr[1];
 	size_t len[1];
 	int res;
+	size_t attr_len;
+	const struct dpp_curve_params *own_curve;
+	EVP_PKEY *own_key;
+	struct wpabuf *a_nonce = NULL, *e_id = NULL;
 
 	wpa_printf(MSG_DEBUG, "DPP: Build Reconfig Announcement frame");
 
+	own_key = dpp_set_keypair(&own_curve, net_access_key,
+				  net_access_key_len);
+	if (!own_key) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
+		goto fail;
+	}
+
 	p = csign_key;
 	csign = d2i_PUBKEY(NULL, &p, csign_key_len);
 	if (!csign) {
 		wpa_printf(MSG_ERROR,
 			   "DPP: Failed to parse local C-sign-key information");
-		return NULL;
+		goto fail;
 	}
 
 	uncomp = dpp_get_pubkey_point(csign, 1);
 	EVP_PKEY_free(csign);
 	if (!uncomp)
-		return NULL;
+		goto fail;
 	addr[0] = wpabuf_head(uncomp);
 	len[0] = wpabuf_len(uncomp);
 	wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed C-sign key", addr[0], len[0]);
 	res = sha256_vector(1, addr, len, hash);
 	wpabuf_free(uncomp);
 	if (res < 0)
-		return NULL;
+		goto fail;
 	wpa_hexdump(MSG_DEBUG, "DPP: kid = SHA256(uncompressed C-sign key)",
 		    hash, SHA256_MAC_LEN);
 
-	msg = dpp_alloc_msg(DPP_PA_RECONFIG_ANNOUNCEMENT, 4 + SHA256_MAC_LEN);
+	if (dpp_update_reconfig_id(id) < 0) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to generate E'-id");
+		goto fail;
+	}
+
+	a_nonce = dpp_get_pubkey_point(id->a_nonce, 0);
+	e_id = dpp_get_pubkey_point(id->e_prime_id, 0);
+	if (!a_nonce || !e_id)
+		goto fail;
+
+	attr_len = 4 + SHA256_MAC_LEN;
+	attr_len += 4 + 2;
+	attr_len += 4 + wpabuf_len(a_nonce);
+	attr_len += 4 + wpabuf_len(e_id);
+	msg = dpp_alloc_msg(DPP_PA_RECONFIG_ANNOUNCEMENT, attr_len);
 	if (!msg)
-		return NULL;
+		goto fail;
 
 	/* Configurator C-sign key Hash */
 	dpp_build_attr_csign_key_hash(msg, hash);
+
+	/* Finite Cyclic Group attribute */
+	wpa_printf(MSG_DEBUG, "DPP: Finite Cyclic Group: %u",
+		   own_curve->ike_group);
+	wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
+	wpabuf_put_le16(msg, 2);
+	wpabuf_put_le16(msg, own_curve->ike_group);
+
+	/* A-NONCE */
+	wpabuf_put_le16(msg, DPP_ATTR_A_NONCE);
+	wpabuf_put_le16(msg, wpabuf_len(a_nonce));
+	wpabuf_put_buf(msg, a_nonce);
+
+	/* E'-id */
+	wpabuf_put_le16(msg, DPP_ATTR_E_PRIME_ID);
+	wpabuf_put_le16(msg, wpabuf_len(e_id));
+	wpabuf_put_buf(msg, e_id);
+
 	wpa_hexdump_buf(MSG_DEBUG,
 			"DPP: Reconfig Announcement frame attributes", msg);
+fail:
+	wpabuf_free(a_nonce);
+	wpabuf_free(e_id);
+	EVP_PKEY_free(own_key);
 	return msg;
 }
 
@@ -108,10 +158,10 @@
 	wpabuf_put_le16(msg, os_strlen(auth->conf->connector));
 	wpabuf_put_str(msg, auth->conf->connector);
 
-	/* I-nonce */
-	wpabuf_put_le16(msg, DPP_ATTR_I_NONCE);
+	/* C-nonce */
+	wpabuf_put_le16(msg, DPP_ATTR_CONFIGURATOR_NONCE);
 	wpabuf_put_le16(msg, auth->curve->nonce_len);
-	wpabuf_put_data(msg, auth->i_nonce, auth->curve->nonce_len);
+	wpabuf_put_data(msg, auth->c_nonce, auth->curve->nonce_len);
 
 	wpa_hexdump_buf(MSG_DEBUG,
 			"DPP: Reconfig Authentication Request frame attributes",
@@ -121,7 +171,9 @@
 }
 
 
-static int dpp_configurator_build_own_connector(struct dpp_configurator *conf)
+static int
+dpp_configurator_build_own_connector(struct dpp_configurator *conf,
+				     const struct dpp_curve_params *curve)
 {
 	struct wpabuf *dppcon = NULL;
 	int ret = -1;
@@ -132,12 +184,12 @@
 	wpa_printf(MSG_DEBUG,
 		   "DPP: Sign own Configurator Connector for reconfiguration with curve %s",
 		   conf->curve->name);
-	conf->connector_key = dpp_gen_keypair(conf->curve);
+	conf->connector_key = dpp_gen_keypair(curve);
 	if (!conf->connector_key)
 		goto fail;
 
 	/* Connector (JSON dppCon object) */
-	dppcon = wpabuf_alloc(1000 + 2 * conf->curve->prime_len * 4 / 3);
+	dppcon = wpabuf_alloc(1000 + 2 * curve->prime_len * 4 / 3);
 	if (!dppcon)
 		goto fail;
 	json_start_object(dppcon, NULL);
@@ -150,7 +202,7 @@
 	json_end_array(dppcon);
 	json_value_sep(dppcon);
 	if (dpp_build_jwk(dppcon, "netAccessKey", conf->connector_key, NULL,
-			  conf->curve) < 0) {
+			  curve) < 0) {
 		wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK");
 		goto fail;
 	}
@@ -172,9 +224,58 @@
 
 struct dpp_authentication *
 dpp_reconfig_init(struct dpp_global *dpp, void *msg_ctx,
-		  struct dpp_configurator *conf, unsigned int freq)
+		  struct dpp_configurator *conf, unsigned int freq, u16 group,
+		  const u8 *a_nonce_attr, size_t a_nonce_len,
+		  const u8 *e_id_attr, size_t e_id_len)
 {
 	struct dpp_authentication *auth;
+	const struct dpp_curve_params *curve;
+	EVP_PKEY *a_nonce, *e_prime_id;
+	EC_POINT *e_id;
+
+	curve = dpp_get_curve_ike_group(group);
+	if (!curve) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unsupported group %u - cannot reconfigure",
+			   group);
+		return NULL;
+	}
+
+	if (!a_nonce_attr) {
+		wpa_printf(MSG_INFO, "DPP: Missing required A-NONCE attribute");
+		return NULL;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: A-NONCE", a_nonce_attr, a_nonce_len);
+	a_nonce = dpp_set_pubkey_point(conf->csign, a_nonce_attr, a_nonce_len);
+	if (!a_nonce) {
+		wpa_printf(MSG_INFO, "DPP: Invalid A-NONCE");
+		return NULL;
+	}
+	dpp_debug_print_key("A-NONCE", a_nonce);
+
+	if (!e_id_attr) {
+		wpa_printf(MSG_INFO, "DPP: Missing required E'-id attribute");
+		return NULL;
+	}
+	e_prime_id = dpp_set_pubkey_point(conf->csign, e_id_attr, e_id_len);
+	if (!e_prime_id) {
+		wpa_printf(MSG_INFO, "DPP: Invalid E'-id");
+		EVP_PKEY_free(a_nonce);
+		return NULL;
+	}
+	dpp_debug_print_key("E'-id", e_prime_id);
+	e_id = dpp_decrypt_e_id(conf->pp_key, a_nonce, e_prime_id);
+	EVP_PKEY_free(a_nonce);
+	EVP_PKEY_free(e_prime_id);
+	if (!e_id) {
+		wpa_printf(MSG_INFO, "DPP: Could not decrypt E'-id");
+		return NULL;
+	}
+	/* TODO: could use E-id to determine whether reconfiguration with this
+	 * Enrollee has already been started and is waiting for updated
+	 * configuration instead of replying again before such configuration
+	 * becomes available */
+	EC_POINT_clear_free(e_id);
 
 	auth = dpp_alloc_auth(dpp, msg_ctx);
 	if (!auth)
@@ -186,16 +287,16 @@
 	auth->waiting_auth_resp = 1;
 	auth->allowed_roles = DPP_CAPAB_CONFIGURATOR;
 	auth->configurator = 1;
-	auth->curve = conf->curve;
+	auth->curve = curve;
 	auth->transaction_id = 1;
 	if (freq && dpp_prepare_channel_list(auth, freq, NULL, 0) < 0)
 		goto fail;
 
-	if (dpp_configurator_build_own_connector(conf) < 0)
+	if (dpp_configurator_build_own_connector(conf, curve) < 0)
 		goto fail;
 
-	if (random_get_bytes(auth->i_nonce, auth->curve->nonce_len)) {
-		wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce");
+	if (random_get_bytes(auth->c_nonce, auth->curve->nonce_len)) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to generate C-nonce");
 		goto fail;
 	}
 
@@ -224,21 +325,16 @@
 	int res = -1;
 
 	/* Build DPP Reconfig Authentication Response frame attributes */
-	clear_len = 2 * (4 + auth->curve->nonce_len) +
+	clear_len = 4 + auth->curve->nonce_len +
 		4 + wpabuf_len(conn_status);
 	clear = wpabuf_alloc(clear_len);
 	if (!clear)
 		goto fail;
 
-	/* I-nonce (wrapped) */
-	wpabuf_put_le16(clear, DPP_ATTR_I_NONCE);
+	/* C-nonce (wrapped) */
+	wpabuf_put_le16(clear, DPP_ATTR_CONFIGURATOR_NONCE);
 	wpabuf_put_le16(clear, auth->curve->nonce_len);
-	wpabuf_put_data(clear, auth->i_nonce, auth->curve->nonce_len);
-
-	/* R-nonce (wrapped) */
-	wpabuf_put_le16(clear, DPP_ATTR_R_NONCE);
-	wpabuf_put_le16(clear, auth->curve->nonce_len);
-	wpabuf_put_data(clear, auth->r_nonce, auth->curve->nonce_len);
+	wpabuf_put_data(clear, auth->c_nonce, auth->curve->nonce_len);
 
 	/* Connection Status (wrapped) */
 	wpabuf_put_le16(clear, DPP_ATTR_CONN_STATUS);
@@ -251,6 +347,7 @@
 
 	attr_len = 4 + 1 + 4 + 1 +
 		4 + os_strlen(own_connector) +
+		4 + auth->curve->nonce_len +
 		4 + wpabuf_len(pr) +
 		4 + wpabuf_len(clear) + AES_BLOCK_SIZE;
 	msg = dpp_alloc_msg(DPP_PA_RECONFIG_AUTH_RESP, attr_len);
@@ -274,6 +371,11 @@
 	wpabuf_put_le16(msg, os_strlen(own_connector));
 	wpabuf_put_str(msg, own_connector);
 
+	/* E-nonce */
+	wpabuf_put_le16(msg, DPP_ATTR_ENROLLEE_NONCE);
+	wpabuf_put_le16(msg, auth->curve->nonce_len);
+	wpabuf_put_data(msg, auth->e_nonce, auth->curve->nonce_len);
+
 	/* Responder Protocol Key (Pr) */
 	wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
 	wpabuf_put_le16(msg, wpabuf_len(pr));
@@ -291,7 +393,7 @@
 	len[1] = attr_end - attr_start;
 	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
 
-	/* Wrapped Data: {I-nonce, R-nonce, Connection Status}ke */
+	/* Wrapped Data: {C-nonce, E-nonce, Connection Status}ke */
 	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
 	wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
 	wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
@@ -329,8 +431,8 @@
 			 const u8 *attr_start, size_t attr_len)
 {
 	struct dpp_authentication *auth = NULL;
-	const u8 *trans_id, *version, *i_connector, *i_nonce;
-	u16 trans_id_len, version_len, i_connector_len, i_nonce_len;
+	const u8 *trans_id, *version, *i_connector, *c_nonce;
+	u16 trans_id_len, version_len, i_connector_len, c_nonce_len;
 	struct dpp_signed_connector_info info;
 	enum dpp_status_error res;
 	struct json_token *root = NULL, *own_root = NULL, *token;
@@ -364,14 +466,14 @@
 	wpa_hexdump_ascii(MSG_DEBUG, "DPP: I-Connector",
 			  i_connector, i_connector_len);
 
-	i_nonce = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_NONCE,
-			       &i_nonce_len);
-	if (!i_nonce || i_nonce_len > DPP_MAX_NONCE_LEN) {
+	c_nonce = dpp_get_attr(attr_start, attr_len,
+			       DPP_ATTR_CONFIGURATOR_NONCE, &c_nonce_len);
+	if (!c_nonce || c_nonce_len > DPP_MAX_NONCE_LEN) {
 		wpa_printf(MSG_DEBUG,
-			   "DPP: Missing or invalid I-Nonce attribute");
+			   "DPP: Missing or invalid C-nonce attribute");
 		goto fail;
 	}
-	wpa_hexdump(MSG_DEBUG, "DPP: I-Nonce", i_nonce, i_nonce_len);
+	wpa_hexdump(MSG_DEBUG, "DPP: C-nonce", c_nonce, c_nonce_len);
 
 	res = dpp_check_signed_connector(&info, csign_key, csign_key_len,
 					 i_connector, i_connector_len);
@@ -418,26 +520,19 @@
 	wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
 		   auth->peer_version);
 
-	os_memcpy(auth->i_nonce, i_nonce, i_nonce_len);
+	os_memcpy(auth->c_nonce, c_nonce, c_nonce_len);
 
 	if (dpp_reconfig_derive_ke_responder(auth, net_access_key,
 					     net_access_key_len, token) < 0)
 		goto fail;
 
-	if (i_nonce_len != auth->curve->nonce_len) {
+	if (c_nonce_len != auth->curve->nonce_len) {
 		wpa_printf(MSG_DEBUG,
-			   "DPP: Unexpected I-nonce length %u (curve nonce len %zu)",
-			   i_nonce_len, auth->curve->nonce_len);
+			   "DPP: Unexpected C-nonce length %u (curve nonce len %zu)",
+			   c_nonce_len, auth->curve->nonce_len);
 		goto fail;
 	}
 
-	if (random_get_bytes(auth->r_nonce, auth->curve->nonce_len)) {
-		wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce");
-		goto fail;
-	}
-	wpa_hexdump_key(MSG_DEBUG, "DPP: R-nonce",
-			auth->r_nonce, auth->curve->nonce_len);
-
 	/* Build Connection Status object */
 	/* TODO: Get appropriate result value */
 	/* TODO: ssid64 and channelList */
@@ -462,40 +557,19 @@
 }
 
 
-static struct wpabuf *
-dpp_build_reconfig_flags(enum dpp_connector_key connector_key)
-{
-	struct wpabuf *json;
-
-	json = wpabuf_alloc(100);
-	if (!json)
-		return NULL;
-	json_start_object(json, NULL);
-	json_add_int(json, "connectorKey", connector_key);
-	json_end_object(json);
-	wpa_hexdump_ascii(MSG_DEBUG, "DPP: Reconfig-Flags JSON",
-			  wpabuf_head(json), wpabuf_len(json));
-
-	return json;
-}
-
-
 struct wpabuf *
 dpp_reconfig_build_conf(struct dpp_authentication *auth)
 {
-	struct wpabuf *msg = NULL, *clear = NULL, *reconfig_flags;
+	struct wpabuf *msg = NULL, *clear;
 	u8 *attr_start, *attr_end;
 	size_t clear_len, attr_len, len[2];
 	const u8 *addr[2];
 	u8 *wrapped;
-
-	reconfig_flags = dpp_build_reconfig_flags(DPP_CONFIG_REPLACEKEY);
-	if (!reconfig_flags)
-		goto fail;
+	u8 flags;
 
 	/* Build DPP Reconfig Authentication Confirm frame attributes */
 	clear_len = 4 + 1 + 4 + 1 + 2 * (4 + auth->curve->nonce_len) +
-		4 + wpabuf_len(reconfig_flags);
+		4 + 1;
 	clear = wpabuf_alloc(clear_len);
 	if (!clear)
 		goto fail;
@@ -510,27 +584,33 @@
 	wpabuf_put_le16(clear, 1);
 	wpabuf_put_u8(clear, auth->peer_version);
 
-	/* I-nonce (wrapped) */
-	wpabuf_put_le16(clear, DPP_ATTR_I_NONCE);
+	/* C-nonce (wrapped) */
+	wpabuf_put_le16(clear, DPP_ATTR_CONFIGURATOR_NONCE);
 	wpabuf_put_le16(clear, auth->curve->nonce_len);
-	wpabuf_put_data(clear, auth->i_nonce, auth->curve->nonce_len);
+	wpabuf_put_data(clear, auth->c_nonce, auth->curve->nonce_len);
 
-	/* R-nonce (wrapped) */
-	wpabuf_put_le16(clear, DPP_ATTR_R_NONCE);
+	/* E-nonce (wrapped) */
+	wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
 	wpabuf_put_le16(clear, auth->curve->nonce_len);
-	wpabuf_put_data(clear, auth->r_nonce, auth->curve->nonce_len);
+	wpabuf_put_data(clear, auth->e_nonce, auth->curve->nonce_len);
 
 	/* Reconfig-Flags (wrapped) */
+	flags = DPP_CONFIG_REPLACEKEY;
 	wpabuf_put_le16(clear, DPP_ATTR_RECONFIG_FLAGS);
-	wpabuf_put_le16(clear, wpabuf_len(reconfig_flags));
-	wpabuf_put_buf(clear, reconfig_flags);
+	wpabuf_put_le16(clear, 1);
+	wpabuf_put_u8(clear, flags);
 
 	attr_len = 4 + wpabuf_len(clear) + AES_BLOCK_SIZE;
+	attr_len += 4 + 1;
 	msg = dpp_alloc_msg(DPP_PA_RECONFIG_AUTH_CONF, attr_len);
 	if (!msg)
 		goto fail;
 
 	attr_start = wpabuf_put(msg, 0);
+
+	/* DPP Status */
+	dpp_build_attr_status(msg, DPP_STATUS_OK);
+
 	attr_end = wpabuf_put(msg, 0);
 
 	/* OUI, OUI type, Crypto Suite, DPP frame type */
@@ -559,7 +639,6 @@
 			msg);
 
 out:
-	wpabuf_free(reconfig_flags);
 	wpabuf_free(clear);
 	return msg;
 fail:
@@ -574,9 +653,9 @@
 			 const u8 *attr_start, size_t attr_len)
 {
 	const u8 *trans_id, *version, *r_connector, *r_proto, *wrapped_data,
-		*i_nonce, *r_nonce, *conn_status;
+		*c_nonce, *e_nonce, *conn_status;
 	u16 trans_id_len, version_len, r_connector_len, r_proto_len,
-		wrapped_data_len, i_nonce_len, r_nonce_len, conn_status_len;
+		wrapped_data_len, c_nonce_len, e_nonce_len, conn_status_len;
 	struct wpabuf *conf = NULL;
 	char *signed_connector = NULL;
 	struct dpp_signed_connector_info info;
@@ -634,6 +713,15 @@
 	wpa_hexdump_ascii(MSG_DEBUG, "DPP: R-Connector",
 			  r_connector, r_connector_len);
 
+	e_nonce = dpp_get_attr(attr_start, attr_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 E-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", e_nonce, e_nonce_len);
+	os_memcpy(auth->e_nonce, e_nonce, e_nonce_len);
+
 	r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY,
 			       &r_proto_len);
 	if (!r_proto) {
@@ -702,23 +790,14 @@
 		goto fail;
 	}
 
-	i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
-			       &i_nonce_len);
-	if (!i_nonce || i_nonce_len != auth->curve->nonce_len ||
-	    os_memcmp(i_nonce, auth->i_nonce, i_nonce_len) != 0) {
-		dpp_auth_fail(auth, "Missing or invalid I-nonce");
+	c_nonce = dpp_get_attr(unwrapped, unwrapped_len,
+			       DPP_ATTR_CONFIGURATOR_NONCE, &c_nonce_len);
+	if (!c_nonce || c_nonce_len != auth->curve->nonce_len ||
+	    os_memcmp(c_nonce, auth->c_nonce, c_nonce_len) != 0) {
+		dpp_auth_fail(auth, "Missing or invalid C-nonce");
 		goto fail;
 	}
-	wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
-
-	r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
-			       &r_nonce_len);
-	if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
-		dpp_auth_fail(auth, "Missing or invalid R-nonce");
-		goto fail;
-	}
-	wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
-	os_memcpy(auth->r_nonce, r_nonce, r_nonce_len);
+	wpa_hexdump(MSG_DEBUG, "DPP: C-nonce", c_nonce, c_nonce_len);
 
 	conn_status = dpp_get_attr(unwrapped, unwrapped_len,
 				   DPP_ATTR_CONN_STATUS, &conn_status_len);
@@ -758,16 +837,16 @@
 int dpp_reconfig_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
 			      const u8 *attr_start, size_t attr_len)
 {
-	const u8 *trans_id, *version, *wrapped_data, *i_nonce, *r_nonce,
-		*reconfig_flags;
-	u16 trans_id_len, version_len, wrapped_data_len, i_nonce_len,
-		r_nonce_len, reconfig_flags_len;
+	const u8 *trans_id, *version, *wrapped_data, *c_nonce, *e_nonce,
+		*reconfig_flags, *status;
+	u16 trans_id_len, version_len, wrapped_data_len, c_nonce_len,
+		e_nonce_len, reconfig_flags_len, status_len;
 	const u8 *addr[2];
 	size_t len[2];
 	u8 *unwrapped = NULL;
 	size_t unwrapped_len = 0;
-	struct json_token *root = NULL, *token;
 	int res = -1;
+	u8 flags;
 
 	if (!auth->reconfig || auth->configurator)
 		goto fail;
@@ -781,11 +860,26 @@
 	}
 	wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
 		    wrapped_data, wrapped_data_len);
+	attr_len = wrapped_data - 4 - attr_start;
+
+	status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
+			      &status_len);
+	if (!status || status_len < 1) {
+		dpp_auth_fail(auth,
+			      "Missing or invalid required DPP Status attribute");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+	if (status[0] != DPP_STATUS_OK) {
+		dpp_auth_fail(auth,
+			      "Reconfiguration did not complete successfully");
+		goto fail;
+	}
 
 	addr[0] = hdr;
 	len[0] = DPP_HDR_LEN;
 	addr[1] = attr_start;
-	len[1] = 0;
+	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",
@@ -825,55 +919,38 @@
 		goto fail;
 	}
 
-	i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
-			       &i_nonce_len);
-	if (!i_nonce || i_nonce_len != auth->curve->nonce_len ||
-	    os_memcmp(i_nonce, auth->i_nonce, i_nonce_len) != 0) {
-		dpp_auth_fail(auth, "Missing or invalid I-nonce");
+	c_nonce = dpp_get_attr(unwrapped, unwrapped_len,
+			       DPP_ATTR_CONFIGURATOR_NONCE, &c_nonce_len);
+	if (!c_nonce || c_nonce_len != auth->curve->nonce_len ||
+	    os_memcmp(c_nonce, auth->c_nonce, c_nonce_len) != 0) {
+		dpp_auth_fail(auth, "Missing or invalid C-nonce");
 		goto fail;
 	}
-	wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+	wpa_hexdump(MSG_DEBUG, "DPP: C-nonce", c_nonce, c_nonce_len);
 
-	r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
-			       &r_nonce_len);
-	if (!r_nonce || r_nonce_len != auth->curve->nonce_len ||
-	    os_memcmp(r_nonce, auth->r_nonce, r_nonce_len) != 0) {
-		dpp_auth_fail(auth, "Missing or invalid R-nonce");
+	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 ||
+	    os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) {
+		dpp_auth_fail(auth, "Missing or invalid E-nonce");
 		goto fail;
 	}
-	wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
+	wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", e_nonce, e_nonce_len);
 
 	reconfig_flags = dpp_get_attr(unwrapped, unwrapped_len,
 				      DPP_ATTR_RECONFIG_FLAGS,
 				      &reconfig_flags_len);
-	if (!reconfig_flags) {
+	if (!reconfig_flags || reconfig_flags_len < 1) {
 		dpp_auth_fail(auth, "Missing or invalid Reconfig-Flags");
 		goto fail;
 	}
-	wpa_hexdump_ascii(MSG_DEBUG, "DPP: Reconfig-Flags",
-			  reconfig_flags, reconfig_flags_len);
-	root = json_parse((const char *) reconfig_flags, reconfig_flags_len);
-	if (!root) {
-		dpp_auth_fail(auth, "Could not parse Reconfig-Flags");
-		goto fail;
-	}
-	token = json_get_member(root, "connectorKey");
-	if (!token || token->type != JSON_NUMBER) {
-		dpp_auth_fail(auth, "No connectorKey in Reconfig-Flags");
-		goto fail;
-	}
-	if (token->number != DPP_CONFIG_REUSEKEY &&
-	    token->number != DPP_CONFIG_REPLACEKEY) {
-		dpp_auth_fail(auth,
-			      "Unsupported connectorKey value in Reconfig-Flags");
-		goto fail;
-	}
-	auth->reconfig_connector_key = token->number;
+	flags = reconfig_flags[0] & BIT(0);
+	wpa_printf(MSG_DEBUG, "DPP: Reconfig Flags connectorKey=%u", flags);
+	auth->reconfig_connector_key = flags;
 
 	auth->reconfig_success = true;
 	res = 0;
 fail:
-	json_free(root);
 	bin_clear_free(unwrapped, unwrapped_len);
 	return res;
 }
diff --git a/src/common/dpp_tcp.c b/src/common/dpp_tcp.c
index fc53b8a..7e330d6 100644
--- a/src/common/dpp_tcp.c
+++ b/src/common/dpp_tcp.c
@@ -25,6 +25,9 @@
 	struct dpp_relay_controller *relay;
 	struct dpp_global *global;
 	struct dpp_authentication *auth;
+	void *msg_ctx;
+	void *cb_ctx;
+	int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
 	int sock;
 	u8 mac_addr[ETH_ALEN];
 	unsigned int freq;
@@ -38,6 +41,10 @@
 	unsigned int on_tcp_tx_complete_gas_done:1;
 	unsigned int on_tcp_tx_complete_remove:1;
 	unsigned int on_tcp_tx_complete_auth_ok:1;
+	unsigned int gas_comeback_in_progress:1;
+	u8 gas_dialog_token;
+	char *name;
+	enum dpp_netrole netrole;
 };
 
 /* Remote Controller */
@@ -46,6 +53,7 @@
 	struct dpp_global *global;
 	u8 pkhash[SHA256_MAC_LEN];
 	struct hostapd_ip_addr ipaddr;
+	void *msg_ctx;
 	void *cb_ctx;
 	void (*tx)(void *ctx, const u8 *addr, unsigned int freq, const u8 *msg,
 		   size_t len);
@@ -62,12 +70,18 @@
 	int sock;
 	struct dl_list conn; /* struct dpp_connection */
 	char *configurator_params;
+	enum dpp_netrole netrole;
+	void *msg_ctx;
+	void *cb_ctx;
+	int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
 };
 
 static void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx);
 static void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx);
 static void dpp_controller_auth_success(struct dpp_connection *conn,
 					int initiator);
+static void dpp_tcp_build_csr(void *eloop_ctx, void *timeout_ctx);
+static void dpp_tcp_gas_query_comeback(void *eloop_ctx, void *timeout_ctx);
 
 
 static void dpp_connection_free(struct dpp_connection *conn)
@@ -81,9 +95,12 @@
 	}
 	eloop_cancel_timeout(dpp_controller_conn_status_result_wait_timeout,
 			     conn, NULL);
+	eloop_cancel_timeout(dpp_tcp_build_csr, conn, NULL);
+	eloop_cancel_timeout(dpp_tcp_gas_query_comeback, conn, NULL);
 	wpabuf_free(conn->msg);
 	wpabuf_free(conn->msg_out);
 	dpp_auth_deinit(conn->auth);
+	os_free(conn->name);
 	os_free(conn);
 }
 
@@ -110,6 +127,7 @@
 	ctrl->global = dpp;
 	os_memcpy(&ctrl->ipaddr, config->ipaddr, sizeof(*config->ipaddr));
 	os_memcpy(ctrl->pkhash, config->pkhash, SHA256_MAC_LEN);
+	ctrl->msg_ctx = config->msg_ctx;
 	ctrl->cb_ctx = config->cb_ctx;
 	ctrl->tx = config->tx;
 	ctrl->gas_resp_tx = config->gas_resp_tx;
@@ -139,7 +157,12 @@
 static void dpp_controller_gas_done(struct dpp_connection *conn)
 {
 	struct dpp_authentication *auth = conn->auth;
-	void *msg_ctx;
+
+	if (auth->waiting_csr) {
+		wpa_printf(MSG_DEBUG, "DPP: Waiting for CSR");
+		conn->on_tcp_tx_complete_gas_done = 0;
+		return;
+	}
 
 	if (auth->peer_version >= 2 &&
 	    auth->conf_resp_status == DPP_STATUS_OK) {
@@ -148,11 +171,7 @@
 		return;
 	}
 
-	if (conn->ctrl)
-		msg_ctx = conn->ctrl->global->msg_ctx;
-	else
-		msg_ctx = auth->msg_ctx;
-	wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+	wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
 	dpp_connection_remove(conn);
 }
 
@@ -243,9 +262,11 @@
 {
 	struct dpp_authentication *auth = conn->auth;
 	struct wpabuf *buf;
-	int netrole_ap = 0; /* TODO: make this configurable */
+	const char *dpp_name;
 
-	buf = dpp_build_conf_req_helper(auth, "Test", netrole_ap, NULL, NULL);
+	dpp_name = conn->name ? conn->name : "Test";
+	buf = dpp_build_conf_req_helper(auth, dpp_name, conn->netrole, NULL,
+					NULL);
 	if (!buf) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: No configuration request data available");
@@ -266,7 +287,7 @@
 		return;
 
 	wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
-	wpa_msg(conn->global->msg_ctx, MSG_INFO,
+	wpa_msg(conn->msg_ctx, MSG_INFO,
 		DPP_EVENT_AUTH_SUCCESS "init=%d", initiator);
 #ifdef CONFIG_TESTING_OPTIONS
 	if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
@@ -356,6 +377,8 @@
 
 	conn->global = ctrl->global;
 	conn->relay = ctrl;
+	conn->msg_ctx = ctrl->msg_ctx;
+	conn->cb_ctx = ctrl->global->cb_ctx;
 	os_memcpy(conn->mac_addr, src, ETH_ALEN);
 	conn->freq = freq;
 
@@ -608,8 +631,7 @@
 		return 0;
 	}
 
-	conn->auth = dpp_auth_req_rx(conn->ctrl->global,
-				     conn->ctrl->global->msg_ctx,
+	conn->auth = dpp_auth_req_rx(conn->ctrl->global, conn->msg_ctx,
 				     conn->ctrl->allowed_roles,
 				     conn->ctrl->qr_mutual,
 				     peer_bi, own_bi, -1, hdr, buf, len);
@@ -645,7 +667,7 @@
 		if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
 			wpa_printf(MSG_DEBUG,
 				   "DPP: Start wait for full response");
-			return -1;
+			return 0;
 		}
 		wpa_printf(MSG_DEBUG, "DPP: No confirm generated");
 		dpp_connection_remove(conn);
@@ -692,7 +714,7 @@
 
 	wpa_printf(MSG_DEBUG,
 		   "DPP: Timeout while waiting for Connection Status Result");
-	wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
+	wpa_msg(conn->msg_ctx, MSG_INFO,
 		DPP_EVENT_CONN_STATUS_RESULT "timeout");
 	dpp_connection_remove(conn);
 }
@@ -704,7 +726,7 @@
 {
 	struct dpp_authentication *auth = conn->auth;
 	enum dpp_status_error status;
-	void *msg_ctx;
+	void *msg_ctx = conn->msg_ctx;
 
 	if (!conn->ctrl && (!auth || !auth->configurator))
 		return 0;
@@ -716,10 +738,6 @@
 			   "DPP: No DPP Configuration waiting for result - drop");
 		return -1;
 	}
-	if (conn->ctrl)
-		msg_ctx = conn->ctrl->global->msg_ctx;
-	else
-		msg_ctx = auth->msg_ctx;
 
 	status = dpp_conf_result_rx(auth, hdr, buf, len);
 	if (status == DPP_STATUS_OK && auth->send_conn_status) {
@@ -765,8 +783,7 @@
 
 	status = dpp_conn_status_result_rx(auth, hdr, buf, len,
 					   ssid, &ssid_len, &channel_list);
-	wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
-		DPP_EVENT_CONN_STATUS_RESULT
+	wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_CONN_STATUS_RESULT
 		"result=%d ssid=%s channel_list=%s",
 		status, wpa_ssid_txt(ssid, ssid_len),
 		channel_list ? channel_list : "N/A");
@@ -796,7 +813,7 @@
 	r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
 				   &r_bootstrap_len);
 	if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
-		wpa_msg(dpp->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+		wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
 			"Missing or invalid required Responder Bootstrapping Key Hash attribute");
 		return -1;
 	}
@@ -809,7 +826,7 @@
 		return -1;
 	}
 
-	auth = dpp_auth_init(dpp, dpp->msg_ctx, peer_bi, NULL,
+	auth = dpp_auth_init(dpp, conn->msg_ctx, peer_bi, NULL,
 			     DPP_CAPAB_CONFIGURATOR, -1, NULL, 0);
 	if (!auth)
 		return -1;
@@ -828,11 +845,12 @@
 						   const u8 *hdr, const u8 *buf,
 						   size_t len)
 {
-	const u8 *csign_hash;
-	u16 csign_hash_len;
+	const u8 *csign_hash, *fcgroup, *a_nonce, *e_id;
+	u16 csign_hash_len, fcgroup_len, a_nonce_len, e_id_len;
 	struct dpp_configurator *conf;
 	struct dpp_global *dpp = conn->ctrl->global;
 	struct dpp_authentication *auth;
+	u16 group;
 
 	if (conn->auth) {
 		wpa_printf(MSG_DEBUG,
@@ -845,7 +863,7 @@
 	csign_hash = dpp_get_attr(buf, len, DPP_ATTR_C_SIGN_KEY_HASH,
 				  &csign_hash_len);
 	if (!csign_hash || csign_hash_len != SHA256_MAC_LEN) {
-		wpa_msg(dpp->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+		wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
 			"Missing or invalid required Configurator C-sign key Hash attribute");
 		return -1;
 	}
@@ -858,7 +876,21 @@
 		return -1;
 	}
 
-	auth = dpp_reconfig_init(dpp, dpp->msg_ctx, conf, 0);
+	fcgroup = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
+			       &fcgroup_len);
+	if (!fcgroup || fcgroup_len != 2) {
+		wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+			"Missing or invalid required Finite Cyclic Group attribute");
+		return -1;
+	}
+	group = WPA_GET_LE16(fcgroup);
+	wpa_printf(MSG_DEBUG, "DPP: Enrollee finite cyclic group: %u", group);
+
+	a_nonce = dpp_get_attr(buf, len, DPP_ATTR_A_NONCE, &a_nonce_len);
+	e_id = dpp_get_attr(buf, len, DPP_ATTR_E_PRIME_ID, &e_id_len);
+
+	auth = dpp_reconfig_init(dpp, conn->msg_ctx, conf, 0, group,
+				 a_nonce, a_nonce_len, e_id, e_id_len);
 	if (!auth)
 		return -1;
 	if (dpp_set_configurator(auth, conn->ctrl->configurator_params) < 0) {
@@ -965,14 +997,91 @@
 }
 
 
+static int dpp_tcp_send_comeback_delay(struct dpp_connection *conn, u8 action)
+{
+	struct wpabuf *buf;
+	size_t len = 18;
+
+	if (action == WLAN_PA_GAS_COMEBACK_RESP)
+		len++;
+
+	buf = wpabuf_alloc(4 + len);
+	if (!buf)
+		return -1;
+
+	wpabuf_put_be32(buf, len);
+
+	wpabuf_put_u8(buf, action);
+	wpabuf_put_u8(buf, conn->gas_dialog_token);
+	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+	if (action == WLAN_PA_GAS_COMEBACK_RESP)
+		wpabuf_put_u8(buf, 0);
+	wpabuf_put_le16(buf, 500); /* GAS Comeback Delay */
+
+	dpp_write_adv_proto(buf);
+	wpabuf_put_le16(buf, 0); /* Query Response Length */
+
+	/* Send Config Response over TCP */
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", buf);
+	wpabuf_free(conn->msg_out);
+	conn->msg_out_pos = 0;
+	conn->msg_out = buf;
+	dpp_tcp_send(conn);
+	return 0;
+}
+
+
+static int dpp_tcp_send_gas_resp(struct dpp_connection *conn, u8 action,
+				 struct wpabuf *resp)
+{
+	struct wpabuf *buf;
+	size_t len;
+
+	if (!resp)
+		return -1;
+
+	len = 18 + wpabuf_len(resp);
+	if (action == WLAN_PA_GAS_COMEBACK_RESP)
+		len++;
+
+	buf = wpabuf_alloc(4 + len);
+	if (!buf) {
+		wpabuf_free(resp);
+		return -1;
+	}
+
+	wpabuf_put_be32(buf, len);
+
+	wpabuf_put_u8(buf, action);
+	wpabuf_put_u8(buf, conn->gas_dialog_token);
+	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+	if (action == WLAN_PA_GAS_COMEBACK_RESP)
+		wpabuf_put_u8(buf, 0);
+	wpabuf_put_le16(buf, 0); /* GAS Comeback Delay */
+
+	dpp_write_adv_proto(buf);
+	dpp_write_gas_query(buf, resp);
+	wpabuf_free(resp);
+
+	/* Send Config Response over TCP; GAS fragmentation is taken care of by
+	 * the Relay */
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", buf);
+	wpabuf_free(conn->msg_out);
+	conn->msg_out_pos = 0;
+	conn->msg_out = buf;
+	conn->on_tcp_tx_complete_gas_done = 1;
+	dpp_tcp_send(conn);
+	return 0;
+}
+
+
 static int dpp_controller_rx_gas_req(struct dpp_connection *conn, const u8 *msg,
 				     size_t len)
 {
 	const u8 *pos, *end, *next;
-	u8 dialog_token;
 	const u8 *adv_proto;
 	u16 slen;
-	struct wpabuf *resp, *buf;
+	struct wpabuf *resp;
 	struct dpp_authentication *auth = conn->auth;
 
 	if (len < 1 + 2)
@@ -990,7 +1099,7 @@
 	pos = msg;
 	end = msg + len;
 
-	dialog_token = *pos++;
+	conn->gas_dialog_token = *pos++;
 	adv_proto = pos++;
 	slen = *pos++;
 	if (*adv_proto != WLAN_EID_ADV_PROTO ||
@@ -1015,35 +1124,76 @@
 		return -1;
 
 	resp = dpp_conf_req_rx(auth, pos, slen);
-	if (!resp)
+	if (!resp && auth->waiting_cert) {
+		wpa_printf(MSG_DEBUG, "DPP: Certificate not yet ready");
+		conn->gas_comeback_in_progress = 1;
+		return dpp_tcp_send_comeback_delay(conn,
+						   WLAN_PA_GAS_INITIAL_RESP);
+	}
+
+	return dpp_tcp_send_gas_resp(conn, WLAN_PA_GAS_INITIAL_RESP, resp);
+}
+
+
+static int dpp_controller_rx_gas_comeback_req(struct dpp_connection *conn,
+					      const u8 *msg, size_t len)
+{
+	u8 dialog_token;
+	struct dpp_authentication *auth = conn->auth;
+	struct wpabuf *resp;
+
+	if (len < 1)
 		return -1;
 
-	buf = wpabuf_alloc(4 + 18 + wpabuf_len(resp));
-	if (!buf) {
-		wpabuf_free(resp);
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Received DPP Configuration Request over TCP (comeback)");
+
+	if (!auth || (!conn->ctrl && !auth->configurator) ||
+	    (!auth->auth_success && !auth->reconfig_success) ||
+	    !conn->gas_comeback_in_progress) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
 		return -1;
 	}
 
-	wpabuf_put_be32(buf, 18 + wpabuf_len(resp));
+	dialog_token = msg[0];
+	if (dialog_token != conn->gas_dialog_token) {
+		wpa_printf(MSG_DEBUG, "DPP: Dialog token mismatch (%u != %u)",
+			   dialog_token, conn->gas_dialog_token);
+		return -1;
+	}
 
-	wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_RESP);
-	wpabuf_put_u8(buf, dialog_token);
-	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
-	wpabuf_put_le16(buf, 0); /* GAS Comeback Delay */
+	if (!auth->conf_resp_tcp) {
+		wpa_printf(MSG_DEBUG, "DPP: Certificate not yet ready");
+		return dpp_tcp_send_comeback_delay(conn,
+						   WLAN_PA_GAS_COMEBACK_RESP);
+	}
 
-	dpp_write_adv_proto(buf);
-	dpp_write_gas_query(buf, resp);
-	wpabuf_free(resp);
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Configuration response is ready to be sent out");
+	resp = auth->conf_resp_tcp;
+	auth->conf_resp_tcp = NULL;
+	return dpp_tcp_send_gas_resp(conn, WLAN_PA_GAS_COMEBACK_RESP, resp);
+}
 
-	/* Send Config Response over TCP; GAS fragmentation is taken care of by
-	 * the Relay */
-	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", buf);
-	wpabuf_free(conn->msg_out);
-	conn->msg_out_pos = 0;
-	conn->msg_out = buf;
-	conn->on_tcp_tx_complete_gas_done = 1;
-	dpp_tcp_send(conn);
-	return 0;
+
+static void dpp_tcp_build_csr(void *eloop_ctx, void *timeout_ctx)
+{
+	struct dpp_connection *conn = eloop_ctx;
+	struct dpp_authentication *auth = conn->auth;
+
+	if (!auth || !auth->csrattrs)
+		return;
+
+	wpa_printf(MSG_DEBUG, "DPP: Build CSR");
+	wpabuf_free(auth->csr);
+	/* TODO: Additional information needed for CSR based on csrAttrs */
+	auth->csr = dpp_build_csr(auth, conn->name ? conn->name : "Test");
+	if (!auth->csr) {
+		dpp_connection_remove(conn);
+		return;
+	}
+
+	dpp_controller_start_gas_client(conn);
 }
 
 
@@ -1062,14 +1212,18 @@
 	else
 		res = -1;
 	wpabuf_free(resp);
+	if (res == -2) {
+		wpa_printf(MSG_DEBUG, "DPP: CSR needed");
+		eloop_register_timeout(0, 0, dpp_tcp_build_csr, conn, NULL);
+		return 0;
+	}
 	if (res < 0) {
 		wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
 		return -1;
 	}
 
-	if (conn->global->process_conf_obj)
-		res = conn->global->process_conf_obj(conn->global->cb_ctx,
-						     auth);
+	if (conn->process_conf_obj)
+		res = conn->process_conf_obj(conn->cb_ctx, auth);
 	else
 		res = 0;
 
@@ -1092,15 +1246,40 @@
 }
 
 
+static void dpp_tcp_gas_query_comeback(void *eloop_ctx, void *timeout_ctx)
+{
+	struct dpp_connection *conn = eloop_ctx;
+	struct dpp_authentication *auth = conn->auth;
+	struct wpabuf *msg;
+
+	if (!auth)
+		return;
+
+	wpa_printf(MSG_DEBUG, "DPP: Send GAS Comeback Request");
+	msg = wpabuf_alloc(4 + 2);
+	if (!msg)
+		return;
+	wpabuf_put_be32(msg, 2);
+	wpabuf_put_u8(msg, WLAN_PA_GAS_COMEBACK_REQ);
+	wpabuf_put_u8(msg, conn->gas_dialog_token);
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg);
+
+	wpabuf_free(conn->msg_out);
+	conn->msg_out_pos = 0;
+	conn->msg_out = msg;
+	dpp_tcp_send(conn);
+}
+
+
 static int dpp_rx_gas_resp(struct dpp_connection *conn, const u8 *msg,
-			   size_t len)
+			   size_t len, bool comeback)
 {
 	struct wpabuf *buf;
 	u8 dialog_token;
 	const u8 *pos, *end, *next, *adv_proto;
-	u16 status, slen;
+	u16 status, slen, comeback_delay;
 
-	if (len < 5 + 2)
+	if (len < 5 + 2 + (comeback ? 1 : 0))
 		return -1;
 
 	wpa_printf(MSG_DEBUG,
@@ -1116,7 +1295,10 @@
 		return -1;
 	}
 	pos += 2;
-	pos += 2; /* ignore GAS Comeback Delay */
+	if (comeback)
+		pos++; /* ignore Fragment ID */
+	comeback_delay = WPA_GET_LE16(pos);
+	pos += 2;
 
 	adv_proto = pos++;
 	slen = *pos++;
@@ -1141,6 +1323,20 @@
 	if (slen > end - pos)
 		return -1;
 
+	if (comeback_delay) {
+		unsigned int secs, usecs;
+
+		conn->gas_dialog_token = dialog_token;
+		secs = (comeback_delay * 1024) / 1000000;
+		usecs = comeback_delay * 1024 - secs * 1000000;
+		wpa_printf(MSG_DEBUG, "DPP: Comeback delay: %u",
+			   comeback_delay);
+		eloop_cancel_timeout(dpp_tcp_gas_query_comeback, conn, NULL);
+		eloop_register_timeout(secs, usecs, dpp_tcp_gas_query_comeback,
+				       conn, NULL);
+		return 0;
+	}
+
 	buf = wpabuf_alloc(slen);
 	if (!buf)
 		return -1;
@@ -1264,8 +1460,15 @@
 			dpp_connection_remove(conn);
 		break;
 	case WLAN_PA_GAS_INITIAL_RESP:
+	case WLAN_PA_GAS_COMEBACK_RESP:
 		if (dpp_rx_gas_resp(conn, pos + 1,
-				    wpabuf_len(conn->msg) - 1) < 0)
+				    wpabuf_len(conn->msg) - 1,
+				    *pos == WLAN_PA_GAS_COMEBACK_RESP) < 0)
+			dpp_connection_remove(conn);
+		break;
+	case WLAN_PA_GAS_COMEBACK_REQ:
+		if (dpp_controller_rx_gas_comeback_req(
+			    conn, pos + 1, wpabuf_len(conn->msg) - 1) < 0)
 			dpp_connection_remove(conn);
 		break;
 	default:
@@ -1302,7 +1505,11 @@
 
 	conn->global = ctrl->global;
 	conn->ctrl = ctrl;
+	conn->msg_ctx = ctrl->msg_ctx;
+	conn->cb_ctx = ctrl->cb_ctx;
+	conn->process_conf_obj = ctrl->process_conf_obj;
 	conn->sock = fd;
+	conn->netrole = ctrl->netrole;
 
 	if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
 		wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
@@ -1327,7 +1534,10 @@
 
 
 int dpp_tcp_init(struct dpp_global *dpp, struct dpp_authentication *auth,
-		 const struct hostapd_ip_addr *addr, int port)
+		 const struct hostapd_ip_addr *addr, int port, const char *name,
+		 enum dpp_netrole netrole, void *msg_ctx, void *cb_ctx,
+		 int (*process_conf_obj)(void *ctx,
+					 struct dpp_authentication *auth))
 {
 	struct dpp_connection *conn;
 	struct sockaddr_storage saddr;
@@ -1349,6 +1559,11 @@
 		return -1;
 	}
 
+	conn->msg_ctx = msg_ctx;
+	conn->cb_ctx = cb_ctx;
+	conn->process_conf_obj = process_conf_obj;
+	conn->name = os_strdup(name ? name : "Test");
+	conn->netrole = netrole;
 	conn->global = dpp;
 	conn->auth = auth;
 	conn->sock = socket(AF_INET, SOCK_STREAM, 0);
@@ -1418,7 +1633,11 @@
 			os_strdup(config->configurator_params);
 	dl_list_init(&ctrl->conn);
 	ctrl->allowed_roles = config->allowed_roles;
-	ctrl->qr_mutual = 0;
+	ctrl->qr_mutual = config->qr_mutual;
+	ctrl->netrole = config->netrole;
+	ctrl->msg_ctx = config->msg_ctx;
+	ctrl->cb_ctx = config->cb_ctx;
+	ctrl->process_conf_obj = config->process_conf_obj;
 
 	ctrl->sock = socket(AF_INET, SOCK_STREAM, 0);
 	if (ctrl->sock < 0)
@@ -1474,6 +1693,69 @@
 }
 
 
+static bool dpp_tcp_peer_id_match(struct dpp_authentication *auth,
+				  unsigned int id)
+{
+	return auth &&
+		((auth->peer_bi && auth->peer_bi->id == id) ||
+		 (auth->tmp_peer_bi && auth->tmp_peer_bi->id == id));
+}
+
+
+static struct dpp_authentication * dpp_tcp_get_auth(struct dpp_global *dpp,
+						    unsigned int id)
+{
+	struct dpp_connection *conn;
+
+	dl_list_for_each(conn, &dpp->tcp_init, struct dpp_connection, list) {
+		if (dpp_tcp_peer_id_match(conn->auth, id))
+			return conn->auth;
+	}
+
+	return NULL;
+}
+
+
+struct dpp_authentication * dpp_controller_get_auth(struct dpp_global *dpp,
+						    unsigned int id)
+{
+	struct dpp_controller *ctrl = dpp->controller;
+	struct dpp_connection *conn;
+
+	if (!ctrl)
+		return dpp_tcp_get_auth(dpp, id);
+
+	dl_list_for_each(conn, &ctrl->conn, struct dpp_connection, list) {
+		if (dpp_tcp_peer_id_match(conn->auth, id))
+			return conn->auth;
+	}
+
+	return dpp_tcp_get_auth(dpp, id);
+}
+
+
+void dpp_controller_new_qr_code(struct dpp_global *dpp,
+				struct dpp_bootstrap_info *bi)
+{
+	struct dpp_controller *ctrl = dpp->controller;
+	struct dpp_connection *conn;
+
+	if (!ctrl)
+		return;
+
+	dl_list_for_each(conn, &ctrl->conn, struct dpp_connection, list) {
+		struct dpp_authentication *auth = conn->auth;
+
+		if (!auth->response_pending ||
+		    dpp_notify_new_qr_code(auth, bi) != 1)
+			continue;
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Sending out pending authentication response");
+		dpp_tcp_send_msg(conn, conn->auth->resp_msg);
+	}
+}
+
+
 void dpp_tcp_init_flush(struct dpp_global *dpp)
 {
 	struct dpp_connection *conn, *tmp;
diff --git a/src/common/gas_server.c b/src/common/gas_server.c
index ca46758..c000aeb 100644
--- a/src/common/gas_server.c
+++ b/src/common/gas_server.c
@@ -1,6 +1,7 @@
 /*
  * Generic advertisement service (GAS) server
  * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2020, The Linux Foundation
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -23,8 +24,9 @@
 	struct dl_list list;
 	u8 adv_proto_id[MAX_ADV_PROTO_ID_LEN];
 	u8 adv_proto_id_len;
-	struct wpabuf * (*req_cb)(void *ctx, const u8 *sa,
-				  const u8 *query, size_t query_len);
+	struct wpabuf * (*req_cb)(void *ctx, void *resp_ctx, const u8 *sa,
+				  const u8 *query, size_t query_len,
+				  u16 *comeback_delay);
 	void (*status_cb)(void *ctx, struct wpabuf *resp, int ok);
 	void *ctx;
 	struct gas_server *gas;
@@ -39,6 +41,7 @@
 	u8 dst[ETH_ALEN];
 	u8 dialog_token;
 	struct gas_server_handler *handler;
+	u16 comeback_delay;
 };
 
 struct gas_server {
@@ -61,7 +64,8 @@
 		   response, MAC2STR(response->dst), response->dialog_token,
 		   response->freq, response->frag_id,
 		   (unsigned long) response->offset,
-		   (unsigned long) wpabuf_len(response->resp));
+		   (unsigned long) (response->resp ?
+				    wpabuf_len(response->resp) : 0));
 	response->handler->status_cb(response->handler->ctx,
 				     response->resp, 0);
 	response->resp = NULL;
@@ -83,30 +87,29 @@
 
 static void
 gas_server_send_resp(struct gas_server *gas, struct gas_server_handler *handler,
+		     struct gas_server_response *response,
 		     const u8 *da, int freq, u8 dialog_token,
-		     struct wpabuf *query_resp)
+		     struct wpabuf *query_resp, u16 comeback_delay)
 {
 	size_t max_len = (freq > 56160) ? 928 : 1400;
 	size_t hdr_len = 24 + 2 + 5 + 3 + handler->adv_proto_id_len + 2;
 	size_t resp_frag_len;
 	struct wpabuf *resp;
-	u16 comeback_delay;
-	struct gas_server_response *response;
 
-	if (!query_resp)
-		return;
-
-	response = os_zalloc(sizeof(*response));
-	if (!response) {
-		wpabuf_free(query_resp);
+	if (comeback_delay == 0 && !query_resp) {
+		gas_server_free_response(response);
 		return;
 	}
-	wpa_printf(MSG_DEBUG, "DPP: Allocated GAS response @%p", response);
+
 	response->freq = freq;
 	response->handler = handler;
 	os_memcpy(response->dst, da, ETH_ALEN);
 	response->dialog_token = dialog_token;
-	if (hdr_len + wpabuf_len(query_resp) > max_len) {
+	if (comeback_delay) {
+		/* Need more time to prepare the response */
+		resp_frag_len = 0;
+		response->comeback_delay = comeback_delay;
+	} else if (hdr_len + wpabuf_len(query_resp) > max_len) {
 		/* Need to use comeback to initiate fragmentation */
 		comeback_delay = 1;
 		resp_frag_len = 0;
@@ -135,10 +138,12 @@
 
 	/* Query Response Length */
 	wpabuf_put_le16(resp, resp_frag_len);
-	if (!comeback_delay)
+	if (!comeback_delay && query_resp)
 		wpabuf_put_buf(resp, query_resp);
 
-	if (comeback_delay) {
+	if (comeback_delay && !query_resp) {
+		wpa_printf(MSG_DEBUG, "GAS: No response available yet");
+	} else if (comeback_delay) {
 		wpa_printf(MSG_DEBUG,
 			   "GAS: Need to fragment query response");
 	} else {
@@ -165,6 +170,7 @@
 	u16 query_req_len;
 	struct gas_server_handler *handler;
 	struct wpabuf *resp;
+	struct gas_server_response *response;
 
 	wpa_hexdump(MSG_MSGDUMP, "GAS: Received GAS Initial Request frame",
 		    data, len);
@@ -210,8 +216,15 @@
 			    pos, end - pos);
 	}
 
+	response = os_zalloc(sizeof(*response));
+	if (!response)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "DPP: Allocated GAS response @%p", response);
 	dl_list_for_each(handler, &gas->handlers, struct gas_server_handler,
 			 list) {
+		u16 comeback_delay = 0;
+
 		if (adv_proto_len < 1 + handler->adv_proto_id_len ||
 		    os_memcmp(adv_proto + 1, handler->adv_proto_id,
 			      handler->adv_proto_id_len) != 0)
@@ -219,17 +232,22 @@
 
 		wpa_printf(MSG_DEBUG,
 			   "GAS: Calling handler for the requested Advertisement Protocol ID");
-		resp = handler->req_cb(handler->ctx, sa, query_req,
-				       query_req_len);
+		resp = handler->req_cb(handler->ctx, response, sa, query_req,
+				       query_req_len, &comeback_delay);
 		wpa_hexdump_buf(MSG_MSGDUMP, "GAS: Response from the handler",
 				resp);
-		gas_server_send_resp(gas, handler, sa, freq, dialog_token,
-				     resp);
+		if (comeback_delay)
+			wpa_printf(MSG_DEBUG,
+				   "GAS: Handler requested comeback delay: %u TU",
+				   comeback_delay);
+		gas_server_send_resp(gas, handler, response, sa, freq,
+				     dialog_token, resp, comeback_delay);
 		return 0;
 	}
 
 	wpa_printf(MSG_DEBUG,
 		   "GAS: No registered handler for the requested Advertisement Protocol ID");
+	gas_server_free_response(response);
 	return -1;
 }
 
@@ -243,6 +261,31 @@
 	size_t hdr_len = 24 + 2 + 6 + 3 + handler->adv_proto_id_len + 2;
 	size_t remaining, resp_frag_len;
 	struct wpabuf *resp;
+	unsigned int wait_time = 0;
+
+	if (!response->resp) {
+		resp = gas_build_comeback_resp(response->dialog_token,
+					       WLAN_STATUS_SUCCESS, 0, 0,
+					       response->comeback_delay,
+					       handler->adv_proto_id_len);
+		if (!resp) {
+			dl_list_del(&response->list);
+			gas_server_free_response(response);
+			return;
+		}
+
+		/* Advertisement Protocol element */
+		wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO);
+		wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */
+		wpabuf_put_u8(resp, 0x7f);
+		/* Advertisement Protocol ID */
+		wpabuf_put_data(resp, handler->adv_proto_id,
+				handler->adv_proto_id_len);
+
+		/* Query Response Length */
+		wpabuf_put_le16(resp, 0);
+		goto send_resp;
+	}
 
 	remaining = wpabuf_len(response->resp) - response->offset;
 	if (hdr_len + remaining > max_len)
@@ -279,8 +322,11 @@
 
 	response->offset += resp_frag_len;
 
-	gas->tx(gas->ctx, response->freq, response->dst, resp,
-		remaining > resp_frag_len ? 2000 : 0);
+	if (remaining > resp_frag_len)
+		wait_time = 2000;
+
+send_resp:
+	gas->tx(gas->ctx, response->freq, response->dst, resp, wait_time);
 	wpabuf_free(resp);
 }
 
@@ -359,12 +405,19 @@
 static void gas_server_handle_tx_status(struct gas_server_response *response,
 					int ack)
 {
-	if (ack && response->offset < wpabuf_len(response->resp)) {
+	if (ack && response->resp &&
+	    response->offset < wpabuf_len(response->resp)) {
 		wpa_printf(MSG_DEBUG,
 			   "GAS: More fragments remaining - keep pending entry");
 		return;
 	}
 
+	if (ack && !response->resp && response->comeback_delay) {
+		wpa_printf(MSG_DEBUG,
+			   "GAS: Waiting for response - keep pending entry");
+		return;
+	}
+
 	if (!ack)
 		wpa_printf(MSG_DEBUG,
 			   "GAS: No ACK received - drop pending entry");
@@ -415,6 +468,27 @@
 }
 
 
+int gas_server_set_resp(struct gas_server *gas, void *resp_ctx,
+			struct wpabuf *resp)
+{
+	struct gas_server_response *tmp, *response = NULL;
+
+	dl_list_for_each(tmp, &gas->responses, struct gas_server_response,
+			 list) {
+		if (tmp == resp_ctx) {
+			response = tmp;
+			break;
+		}
+	}
+
+	if (!response || response->resp)
+		return -1;
+
+	response->resp = resp;
+	return 0;
+}
+
+
 struct gas_server * gas_server_init(void *ctx,
 				    void (*tx)(void *ctx, int freq,
 					       const u8 *da,
@@ -461,8 +535,9 @@
 int gas_server_register(struct gas_server *gas,
 			const u8 *adv_proto_id, u8 adv_proto_id_len,
 			struct wpabuf *
-			(*req_cb)(void *ctx, const u8 *sa,
-				  const u8 *query, size_t query_len),
+			(*req_cb)(void *ctx, void *resp_ctx, const u8 *sa,
+				  const u8 *query, size_t query_len,
+				  u16 *comeback_delay),
 			void (*status_cb)(void *ctx, struct wpabuf *resp,
 					  int ok),
 			void *ctx)
diff --git a/src/common/gas_server.h b/src/common/gas_server.h
index 299f529..2611dde 100644
--- a/src/common/gas_server.h
+++ b/src/common/gas_server.h
@@ -1,6 +1,7 @@
 /*
  * Generic advertisement service (GAS) server
  * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2020, The Linux Foundation
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -22,8 +23,9 @@
 int gas_server_register(struct gas_server *gas,
 			const u8 *adv_proto_id, u8 adv_proto_id_len,
 			struct wpabuf *
-			(*req_cb)(void *ctx, const u8 *sa,
-				  const u8 *query, size_t query_len),
+			(*req_cb)(void *ctx, void *resp_ctx, const u8 *sa,
+				  const u8 *query, size_t query_len,
+				  u16 *comeback_delay),
 			void (*status_cb)(void *ctx, struct wpabuf *resp,
 					  int ok),
 			void *ctx);
@@ -32,6 +34,8 @@
 		  int freq);
 void gas_server_tx_status(struct gas_server *gas, const u8 *dst, const u8 *data,
 			  size_t data_len, int ack);
+int gas_server_set_resp(struct gas_server *gas, void *resp_ctx,
+			struct wpabuf *resp);
 
 #else /* CONFIG_GAS_SERVER */
 
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index f6c67a3..511e68f 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -415,7 +415,7 @@
 			return -1;
 		}
 
-		if (center_idx_to_bw_6ghz(channel) != 0) {
+		if (center_idx_to_bw_6ghz(channel) < 0) {
 			wpa_printf(MSG_ERROR,
 				   "Invalid control channel for 6 GHz band");
 			return -1;
@@ -540,13 +540,20 @@
 		if (center_segment1 ||
 		    (center_segment0 != 0 &&
 		     5000 + center_segment0 * 5 != data->center_freq1 &&
-		     2407 + center_segment0 * 5 != data->center_freq1))
+		     2407 + center_segment0 * 5 != data->center_freq1)) {
+			wpa_printf(MSG_ERROR,
+				   "20/40 MHz: center segment 0 (=%d) and center freq 1 (=%d) not in sync",
+				   center_segment0, data->center_freq1);
 			return -1;
+		}
 		break;
 	case CHANWIDTH_80P80MHZ:
 		if (center_segment1 == center_segment0 + 4 ||
-		    center_segment1 == center_segment0 - 4)
+		    center_segment1 == center_segment0 - 4) {
+			wpa_printf(MSG_ERROR,
+				   "80+80 MHz: center segment 1 only 20 MHz apart");
 			return -1;
+		}
 		data->center_freq2 = 5000 + center_segment1 * 5;
 		/* fall through */
 	case CHANWIDTH_80MHZ:
@@ -555,8 +562,11 @@
 		     center_segment1) ||
 		    (oper_chwidth == CHANWIDTH_80P80MHZ &&
 		     !center_segment1) ||
-		    !sec_channel_offset)
+		    !sec_channel_offset) {
+			wpa_printf(MSG_ERROR,
+				   "80/80+80 MHz: center segment 1 wrong or no second channel offset");
 			return -1;
+		}
 		if (!center_segment0) {
 			if (channel <= 48)
 				center_segment0 = 42;
@@ -582,16 +592,25 @@
 			    center_segment0 == channel - 2 ||
 			    center_segment0 == channel - 6)
 				data->center_freq1 = 5000 + center_segment0 * 5;
-			else
+			else {
+				wpa_printf(MSG_ERROR,
+					   "Wrong coupling between HT and VHT/HE channel setting");
 				return -1;
+			}
 		}
 		break;
 	case CHANWIDTH_160MHZ:
 		data->bandwidth = 160;
-		if (center_segment1)
+		if (center_segment1) {
+			wpa_printf(MSG_ERROR,
+				   "160 MHz: center segment 1 should not be set");
 			return -1;
-		if (!sec_channel_offset)
+		}
+		if (!sec_channel_offset) {
+			wpa_printf(MSG_ERROR,
+				   "160 MHz: second channel offset not set");
 			return -1;
+		}
 		/*
 		 * Note: HT/VHT config and params are coupled. Check if
 		 * HT40 channel band is in VHT160 channel band configuration.
@@ -605,8 +624,11 @@
 		    center_segment0 == channel - 10 ||
 		    center_segment0 == channel - 14)
 			data->center_freq1 = 5000 + center_segment0 * 5;
-		else
+		else {
+			wpa_printf(MSG_ERROR,
+				   "160 MHz: HT40 channel band is not in 160 MHz band");
 			return -1;
+		}
 		break;
 	}
 
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index 6859787..72d7dda 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -136,6 +136,10 @@
 		case DPP_CC_OUI_TYPE:
 			/* DPP Configurator Connectivity element */
 			break;
+		case SAE_PK_OUI_TYPE:
+			elems->sae_pk = pos + 4;
+			elems->sae_pk_len = elen - 4;
+			break;
 		default:
 			wpa_printf(MSG_MSGDUMP, "Unknown WFA "
 				   "information element ignored "
@@ -769,6 +773,98 @@
 }
 
 
+/* convert floats with one decimal place to value*10 int, i.e.,
+ * "1.5" will return 15
+ */
+static int hostapd_config_read_int10(const char *value)
+{
+	int i, d;
+	char *pos;
+
+	i = atoi(value);
+	pos = os_strchr(value, '.');
+	d = 0;
+	if (pos) {
+		pos++;
+		if (*pos >= '0' && *pos <= '9')
+			d = *pos - '0';
+	}
+
+	return i * 10 + d;
+}
+
+
+static int valid_cw(int cw)
+{
+	return (cw == 1 || cw == 3 || cw == 7 || cw == 15 || cw == 31 ||
+		cw == 63 || cw == 127 || cw == 255 || cw == 511 || cw == 1023 ||
+		cw == 2047 || cw == 4095 || cw == 8191 || cw == 16383 ||
+		cw == 32767);
+}
+
+
+int hostapd_config_tx_queue(struct hostapd_tx_queue_params tx_queue[],
+			    const char *name, const char *val)
+{
+	int num;
+	const char *pos;
+	struct hostapd_tx_queue_params *queue;
+
+	/* skip 'tx_queue_' prefix */
+	pos = name + 9;
+	if (os_strncmp(pos, "data", 4) == 0 &&
+	    pos[4] >= '0' && pos[4] <= '9' && pos[5] == '_') {
+		num = pos[4] - '0';
+		pos += 6;
+	} else if (os_strncmp(pos, "after_beacon_", 13) == 0 ||
+		   os_strncmp(pos, "beacon_", 7) == 0) {
+		wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name);
+		return 0;
+	} else {
+		wpa_printf(MSG_ERROR, "Unknown tx_queue name '%s'", pos);
+		return -1;
+	}
+
+	if (num >= NUM_TX_QUEUES) {
+		/* for backwards compatibility, do not trigger failure */
+		wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name);
+		return 0;
+	}
+
+	queue = &tx_queue[num];
+
+	if (os_strcmp(pos, "aifs") == 0) {
+		queue->aifs = atoi(val);
+		if (queue->aifs < 0 || queue->aifs > 255) {
+			wpa_printf(MSG_ERROR, "Invalid AIFS value %d",
+				   queue->aifs);
+			return -1;
+		}
+	} else if (os_strcmp(pos, "cwmin") == 0) {
+		queue->cwmin = atoi(val);
+		if (!valid_cw(queue->cwmin)) {
+			wpa_printf(MSG_ERROR, "Invalid cwMin value %d",
+				   queue->cwmin);
+			return -1;
+		}
+	} else if (os_strcmp(pos, "cwmax") == 0) {
+		queue->cwmax = atoi(val);
+		if (!valid_cw(queue->cwmax)) {
+			wpa_printf(MSG_ERROR, "Invalid cwMax value %d",
+				   queue->cwmax);
+			return -1;
+		}
+	} else if (os_strcmp(pos, "burst") == 0) {
+		queue->burst = hostapd_config_read_int10(val);
+	} else {
+		wpa_printf(MSG_ERROR, "Unknown queue field '%s'", pos);
+		return -1;
+	}
+
+	return 0;
+}
+
+
 enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel)
 {
 	u8 op_class;
@@ -1683,7 +1779,9 @@
 	S2S(FILS_AUTHENTICATION_FAILURE)
 	S2S(UNKNOWN_AUTHENTICATION_SERVER)
 	S2S(UNKNOWN_PASSWORD_IDENTIFIER)
+	S2S(DENIED_HE_NOT_SUPPORTED)
 	S2S(SAE_HASH_TO_ELEMENT)
+	S2S(SAE_PK)
 	}
 	return "UNKNOWN";
 #undef S2S
@@ -1781,7 +1879,6 @@
 	 */
 	{ HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80, P2P_SUPP },
 	{ HOSTAPD_MODE_IEEE80211A, 129, 50, 114, 16, BW160, P2P_SUPP },
-	{ HOSTAPD_MODE_IEEE80211A, 130, 36, 161, 4, BW80P80, P2P_SUPP },
 	{ HOSTAPD_MODE_IEEE80211A, 131, 1, 233, 4, BW20, P2P_SUPP },
 
 	/*
@@ -1793,6 +1890,12 @@
 	{ HOSTAPD_MODE_IEEE80211AD, 181, 9, 13, 1, BW4320, P2P_SUPP },
 	{ HOSTAPD_MODE_IEEE80211AD, 182, 17, 20, 1, BW6480, P2P_SUPP },
 	{ HOSTAPD_MODE_IEEE80211AD, 183, 25, 27, 1, BW8640, P2P_SUPP },
+
+	/* Keep the operating class 130 as the last entry as a workaround for
+	 * the OneHundredAndThirty Delimiter value used in the Supported
+	 * Operating Classes element to indicate the end of the Operating
+	 * Classes field. */
+	{ HOSTAPD_MODE_IEEE80211A, 130, 36, 161, 4, BW80P80, P2P_SUPP },
 	{ -1, 0, 0, 0, 0, BW20, NO_P2P_SUPP }
 };
 
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 55f7aa6..98a55ec 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -114,6 +114,7 @@
 	const u8 *he_operation;
 	const u8 *short_ssid_list;
 	const u8 *he_6ghz_band_cap;
+	const u8 *sae_pk;
 
 	u8 ssid_len;
 	u8 supp_rates_len;
@@ -166,6 +167,7 @@
 	u8 he_capabilities_len;
 	u8 he_operation_len;
 	u8 short_ssid_list_len;
+	u8 sae_pk_len;
 
 	struct mb_ies_info mb_ies;
 	struct frag_ies_info frag_ies;
@@ -192,6 +194,18 @@
 
 int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
 			  const char *name, const char *val);
+
+struct hostapd_tx_queue_params {
+	int aifs;
+	int cwmin;
+	int cwmax;
+	int burst; /* maximum burst time in 0.1 ms, i.e., 10 = 1 ms */
+};
+
+#define NUM_TX_QUEUES 4
+
+int hostapd_config_tx_queue(struct hostapd_tx_queue_params queue[],
+			    const char *name, const char *val);
 enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel);
 int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan);
 enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 65cc4df..de41d7f 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -204,7 +204,9 @@
 #define WLAN_STATUS_FILS_AUTHENTICATION_FAILURE 112
 #define WLAN_STATUS_UNKNOWN_AUTHENTICATION_SERVER 113
 #define WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER 123
+#define WLAN_STATUS_DENIED_HE_NOT_SUPPORTED 124
 #define WLAN_STATUS_SAE_HASH_TO_ELEMENT 126
+#define WLAN_STATUS_SAE_PK 127
 
 /* Reason codes (IEEE Std 802.11-2016, 9.4.1.7, Table 9-45) */
 #define WLAN_REASON_UNSPECIFIED 1
@@ -476,6 +478,8 @@
 #define WLAN_EID_EXT_HE_6GHZ_BAND_CAP 59
 #define WLAN_EID_EXT_EDMG_CAPABILITIES 61
 #define WLAN_EID_EXT_EDMG_OPERATION 62
+#define WLAN_EID_EXT_MSCS_DESCRIPTOR 88
+#define WLAN_EID_EXT_TCLAS_MASK 89
 #define WLAN_EID_EXT_REJECTED_GROUPS 92
 #define WLAN_EID_EXT_ANTI_CLOGGING_TOKEN 93
 
@@ -560,11 +564,14 @@
 #define WLAN_EXT_CAPAB_SAE_PW_ID 81
 #define WLAN_EXT_CAPAB_SAE_PW_ID_EXCLUSIVELY 82
 #define WLAN_EXT_CAPAB_BEACON_PROTECTION 84
+#define WLAN_EXT_CAPAB_MSCS 85
+#define WLAN_EXT_CAPAB_SAE_PK_EXCLUSIVELY 88
 
 /* Extended RSN Capabilities */
 /* bits 0-3: Field length (n-1) */
 #define WLAN_RSNX_CAPAB_PROTECTED_TWT 4
 #define WLAN_RSNX_CAPAB_SAE_H2E 5
+#define WLAN_RSNX_CAPAB_SAE_PK 6
 
 /* Action frame categories (IEEE Std 802.11-2016, 9.4.1.11, Table 9-76) */
 #define WLAN_ACTION_SPECTRUM_MGMT 0
@@ -1325,6 +1332,8 @@
 #define MULTI_AP_OUI_TYPE 0x1B
 #define DPP_CC_IE_VENDOR_TYPE 0x506f9a1e
 #define DPP_CC_OUI_TYPE 0x1e
+#define SAE_PK_IE_VENDOR_TYPE 0x506f9a1f
+#define SAE_PK_OUI_TYPE 0x1f
 
 #define MULTI_AP_SUB_ELEM_TYPE 0x06
 #define MULTI_AP_TEAR_DOWN BIT(4)
@@ -2178,23 +2187,30 @@
 	 /* Minimum MPDU Start Spacing B0..B2
 	  * Maximum A-MPDU Length Exponent B3..B5
 	  * Maximum MPDU Length B6..B7 */
-	u8 a_mpdu_params; /* B0..B7 */
-	u8 info; /* B8..B15 */
+	le16 capab;
 } STRUCT_PACKED;
 
-#define HE_6GHZ_BAND_CAP_MIN_MPDU_START_SPACE_MASK		0x7
-#define HE_6GHZ_BAND_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK	0x7
-#define HE_6GHZ_BAND_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT	3
-#define HE_6GHZ_BAND_CAP_MAX_MPDU_LENGTH_MASK			0x3
-#define HE_6GHZ_BAND_CAP_MAX_MPDU_LENGTH_SHIFT			6
-
-#define HE_6GHZ_BAND_CAP_SMPS_MASK			  (BIT(1) | BIT(2))
-#define HE_6GHZ_BAND_CAP_SMPS_STATIC			  0
-#define HE_6GHZ_BAND_CAP_SMPS_DYNAMIC			  BIT(1)
-#define HE_6GHZ_BAND_CAP_SMPS_DISABLED			  (BIT(1) | BIT(2))
-#define HE_6GHZ_BAND_CAP_RD_RESPONDER			  BIT(3)
-#define HE_6GHZ_BAND_CAP_RX_ANTENNA_PATTERN		  BIT(4)
-#define HE_6GHZ_BAND_CAP_TX_ANTENNA_PATTERN		  BIT(5)
+#define HE_6GHZ_BAND_CAP_MIN_MPDU_START              (BIT(0) | BIT(1) | BIT(2))
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_16K       BIT(3)
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_32K       BIT(4)
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_64K       (BIT(3) | BIT(4))
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_128K      BIT(5)
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_256K      (BIT(3) | BIT(5))
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_512K      (BIT(4) | BIT(5))
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_1024K     (BIT(3) | BIT(4) | BIT(5))
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_MASK      (BIT(3) | BIT(4) | BIT(5))
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_SHIFT     3
+#define HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_7991           BIT(6)
+#define HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_11454          BIT(7)
+#define HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_MASK           (BIT(6) | BIT(7))
+#define HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_SHIFT	     6
+#define HE_6GHZ_BAND_CAP_SMPS_MASK                   (BIT(9) | BIT(10))
+#define HE_6GHZ_BAND_CAP_SMPS_STATIC                 0
+#define HE_6GHZ_BAND_CAP_SMPS_DYNAMIC                BIT(9)
+#define HE_6GHZ_BAND_CAP_SMPS_DISABLED               (BIT(9) | BIT(10))
+#define HE_6GHZ_BAND_CAP_RD_RESPONDER                BIT(11)
+#define HE_6GHZ_BAND_CAP_RX_ANTPAT_CONS              BIT(12)
+#define HE_6GHZ_BAND_CAP_TX_ANTPAT_CONS              BIT(13)
 
 /*
  * IEEE P802.11ax/D4.0, 9.4.2.246 Spatial Reuse Parameter Set element
@@ -2355,4 +2371,25 @@
 /* DPP Public Action frame identifiers - OUI_WFA */
 #define DPP_OUI_TYPE 0x1A
 
+/* Robust AV streaming Action field values */
+enum robust_av_streaming_action {
+	ROBUST_AV_SCS_REQ = 0,
+	ROBUST_AV_SCS_RESP = 1,
+	ROBUST_AV_GROUP_MEMBERSHIP_REQ = 2,
+	ROBUST_AV_GROUP_MEMBERSHIP_RESP = 3,
+	ROBUST_AV_MSCS_REQ = 4,
+	ROBUST_AV_MSCS_RESP = 5,
+};
+
+enum scs_request_type {
+	SCS_REQ_ADD = 0,
+	SCS_REQ_REMOVE = 1,
+	SCS_REQ_CHANGE = 2,
+};
+
+/* Optional subelement IDs for MSCS Descriptor element */
+enum mscs_description_subelem {
+	MCSC_SUBELEM_STATUS = 1,
+};
+
 #endif /* IEEE802_11_DEFS_H */
diff --git a/src/common/ocv.c b/src/common/ocv.c
index 06badfb..4bc2749 100644
--- a/src/common/ocv.c
+++ b/src/common/ocv.c
@@ -95,23 +95,24 @@
 }
 
 
-int ocv_verify_tx_params(const u8 *oci_ie, size_t oci_ie_len,
-			 struct wpa_channel_info *ci, int tx_chanwidth,
-			 int tx_seg1_idx)
+enum oci_verify_result
+ocv_verify_tx_params(const u8 *oci_ie, size_t oci_ie_len,
+		     struct wpa_channel_info *ci, int tx_chanwidth,
+		     int tx_seg1_idx)
 {
 	struct oci_info oci;
 
 	if (!oci_ie) {
 		os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
-			    "OCV failed: did not receive mandatory OCI");
-		return -1;
+			    "did not receive mandatory OCI");
+		return OCI_NOT_FOUND;
 	}
 
 	if (oci_ie_len != 3) {
 		os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
-			    "OCV failed: received OCI of unexpected length (%d)",
+			    "received OCI of unexpected length (%d)",
 			    (int) oci_ie_len);
-		return -1;
+		return OCI_INVALID_LENGTH;
 	}
 
 	os_memset(&oci, 0, sizeof(oci));
@@ -120,25 +121,25 @@
 	oci.seg1_idx = oci_ie[2];
 	if (ocv_derive_all_parameters(&oci) != 0) {
 		os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
-			    "OCV failed: unable to interpret received OCI");
-		return -1;
+			    "unable to interpret received OCI");
+		return OCI_PARSE_ERROR;
 	}
 
 	/* Primary frequency used to send frames to STA must match the STA's */
 	if ((int) ci->frequency != oci.freq) {
 		os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
-			    "OCV failed: primary channel mismatch in received OCI (we use %d but receiver is using %d)",
+			    "primary channel mismatch in received OCI (we use %d but receiver is using %d)",
 			    ci->frequency, oci.freq);
-		return -1;
+		return OCI_PRIMARY_FREQ_MISMATCH;
 	}
 
 	/* We shouldn't transmit with a higher bandwidth than the STA supports
 	 */
 	if (tx_chanwidth > oci.chanwidth) {
 		os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
-			    "OCV failed: channel bandwidth mismatch in received OCI (we use %d but receiver only supports %d)",
+			    "channel bandwidth mismatch in received OCI (we use %d but receiver only supports %d)",
 			    tx_chanwidth, oci.chanwidth);
-		return -1;
+		return OCI_CHANNEL_WIDTH_MISMATCH;
 	}
 
 	/*
@@ -150,9 +151,9 @@
 	if (tx_chanwidth == 40 && ci->frequency < 2500 &&
 	    ci->sec_channel != oci.sec_channel) {
 		os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
-			    "OCV failed: secondary channel mismatch in received OCI (we use %d but receiver is using %d)",
+			    "secondary channel mismatch in received OCI (we use %d but receiver is using %d)",
 			    ci->sec_channel, oci.sec_channel);
-		return -1;
+		return OCI_SECONDARY_FREQ_MISMATCH;
 	}
 
 	/*
@@ -163,10 +164,10 @@
 	     ci->chanwidth == CHAN_WIDTH_80P80) &&
 	    tx_seg1_idx != oci.seg1_idx) {
 		os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
-			    "OCV failed: frequency segment 1 mismatch in received OCI (we use %d but receiver is using %d)",
+			    "frequency segment 1 mismatch in received OCI (we use %d but receiver is using %d)",
 			    tx_seg1_idx, oci.seg1_idx);
-		return -1;
+		return OCI_SEG_1_INDEX_MISMATCH;
 	}
 
-	return 0;
+	return OCI_SUCCESS;
 }
diff --git a/src/common/ocv.h b/src/common/ocv.h
index 6379d9d..7fa4522 100644
--- a/src/common/ocv.h
+++ b/src/common/ocv.h
@@ -27,14 +27,21 @@
 #define OCV_OCI_EXTENDED_LEN	(3 + OCV_OCI_LEN)
 #define OCV_OCI_KDE_LEN		(2 + RSN_SELECTOR_LEN + OCV_OCI_LEN)
 
+enum oci_verify_result {
+	OCI_SUCCESS, OCI_NOT_FOUND, OCI_INVALID_LENGTH, OCI_PARSE_ERROR,
+	OCI_PRIMARY_FREQ_MISMATCH, OCI_CHANNEL_WIDTH_MISMATCH,
+	OCI_SECONDARY_FREQ_MISMATCH, OCI_SEG_1_INDEX_MISMATCH
+};
+
 extern char ocv_errorstr[256];
 
 int ocv_derive_all_parameters(struct oci_info *oci);
 int ocv_insert_oci(struct wpa_channel_info *ci, u8 **argpos);
 int ocv_insert_oci_kde(struct wpa_channel_info *ci, u8 **argpos);
 int ocv_insert_extended_oci(struct wpa_channel_info *ci, u8 *pos);
-int ocv_verify_tx_params(const u8 *oci_ie, size_t oci_ie_len,
-			 struct wpa_channel_info *ci, int tx_chanwidth,
-			 int tx_seg1_idx);
+enum oci_verify_result
+ocv_verify_tx_params(const u8 *oci_ie, size_t oci_ie_len,
+		     struct wpa_channel_info *ci, int tx_chanwidth,
+		     int tx_seg1_idx);
 
 #endif /* OCV_H */
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index e599b8d..eb6a2c1 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -1,7 +1,7 @@
 /*
  * Qualcomm Atheros OUI and vendor specific assignments
  * Copyright (c) 2014-2017, Qualcomm Atheros, Inc.
- * Copyright (c) 2018-2019, The Linux Foundation
+ * Copyright (c) 2018-2020, The Linux Foundation
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -174,6 +174,22 @@
  *	to notify the connected station's status. The attributes for this
  *	command are defined in enum qca_wlan_vendor_attr_link_properties.
  *
+ * @QCA_NL80211_VENDOR_SUBCMD_SETBAND: Command to configure the enabled band(s)
+ *	to the driver. This command sets the band(s) through either the
+ *	attribute QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE or
+ *	QCA_WLAN_VENDOR_ATTR_SETBAND_MASK (or both).
+ *	QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE refers enum qca_set_band as unsigned
+ *	integer values and QCA_WLAN_VENDOR_ATTR_SETBAND_MASK refers it as 32
+ *	bit unsigned bitmask values. The allowed values for
+ *	QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE are limited to QCA_SETBAND_AUTO,
+ *	QCA_SETBAND_5G, and QCA_SETBAND_2G. Other values/bitmasks are valid for
+ *	QCA_WLAN_VENDOR_ATTR_SETBAND_MASK. The attribute
+ *	QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE is deprecated and the recommendation
+ *	is to use the QCA_WLAN_VENDOR_ATTR_SETBAND_MASK. If the	both attributes
+ *	are included for backwards compatibility, the configurations through
+ *	QCA_WLAN_VENDOR_ATTR_SETBAND_MASK will take the precedence with drivers
+ *	that support both attributes.
+ *
  * @QCA_NL80211_VENDOR_SUBCMD_ACS_POLICY: This command is used to configure
  *	DFS policy and channel hint for ACS operation. This command uses the
  *	attributes defined in enum qca_wlan_vendor_attr_acs_config and
@@ -646,6 +662,44 @@
  *	code immediately prior to triggering cfg80211_disconnected(). The
  *	attributes used with this event are defined in enum
  *	qca_wlan_vendor_attr_driver_disconnect_reason.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_CONFIG_TSPEC: This vendor subcommand is used to
+ *	add/delete TSPEC for each AC. One command is for one specific AC only.
+ *	This command can only be used in STA mode and the STA must be
+ *	associated with an AP when the command is issued. Uses attributes
+ *	defined in enum qca_wlan_vendor_attr_config_tspec.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT: Vendor subcommand to configure TWT.
+ *	Uses attributes defined in enum qca_wlan_vendor_attr_config_twt.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GETBAND: Command to get the enabled band(s) from
+ *	the driver. The band configurations obtained are referred through
+ *	QCA_WLAN_VENDOR_ATTR_SETBAND_MASK.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_MEDIUM_ASSESS: Vendor subcommand/event for medium
+ *	assessment.
+ *	Uses attributes defined in enum qca_wlan_vendor_attr_medium_assess.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_UPDATE_SSID: This acts as a vendor event and is
+ *	used to update SSID information in hostapd when it is updated in the
+ *	driver. Uses the attribute NL80211_ATTR_SSID.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_WIFI_FW_STATS: This vendor subcommand is used by
+ *	the driver to send opaque data from the firmware to userspace. The
+ *	driver sends an event to userspace whenever such data is received from
+ *	the firmware.
+ *
+ *	QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA is used as the attribute to
+ *	send this opaque data for this event.
+ *
+ *	The format of the opaque data is specific to the particular firmware
+ *	version and there is no guarantee of the format remaining same.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_MBSSID_TX_VDEV_STATUS: This acts as an event.
+ *	The host driver selects Tx VDEV, and notifies user. The attributes
+ *	used with this event are defined in enum
+ *	qca_wlan_vendor_attr_mbssid_tx_vdev_status.
+ *
  */
 enum qca_nl80211_vendor_subcmds {
 	QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -825,6 +879,13 @@
 	QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS_EVENT = 187,
 	QCA_NL80211_VENDOR_SUBCMD_UPDATE_STA_INFO = 188,
 	QCA_NL80211_VENDOR_SUBCMD_DRIVER_DISCONNECT_REASON = 189,
+	QCA_NL80211_VENDOR_SUBCMD_CONFIG_TSPEC = 190,
+	QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT = 191,
+	QCA_NL80211_VENDOR_SUBCMD_GETBAND = 192,
+	QCA_NL80211_VENDOR_SUBCMD_MEDIUM_ASSESS = 193,
+	QCA_NL80211_VENDOR_SUBCMD_UPDATE_SSID = 194,
+	QCA_NL80211_VENDOR_SUBCMD_WIFI_FW_STATS = 195,
+	QCA_NL80211_VENDOR_SUBCMD_MBSSID_TX_VDEV_STATUS = 196,
 };
 
 enum qca_wlan_vendor_attr {
@@ -855,7 +916,11 @@
 	QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND = 10,
 	/* Unsigned 32-bit value */
 	QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND = 11,
-	/* Unsigned 32-bit value from enum qca_set_band. */
+	/* Unsigned 32-bit value from enum qca_set_band. The allowed values for
+	 * this attribute are limited to QCA_SETBAND_AUTO, QCA_SETBAND_5G, and
+	 * QCA_SETBAND_2G. This attribute is deprecated. Recommendation is to
+	 * use QCA_WLAN_VENDOR_ATTR_SETBAND_MASK instead.
+	 */
 	QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE = 12,
 	/* Dummy (NOP) attribute for 64 bit padding */
 	QCA_WLAN_VENDOR_ATTR_PAD = 13,
@@ -1018,6 +1083,15 @@
 	 */
 	QCA_WLAN_VENDOR_ATTR_FW_STATE = 42,
 
+	/* Unsigned 32-bitmask value from enum qca_set_band. Substitutes the
+	 * attribute QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE for which only a subset
+	 * of single values from enum qca_set_band are valid. This attribute
+	 * uses bitmask combinations to define the respective allowed band
+	 * combinations and this attributes takes precedence over
+	 * QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE if both attributes are included.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SETBAND_MASK = 43,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_MAX	= QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1,
@@ -1565,9 +1639,10 @@
 };
 
 enum qca_set_band {
-	QCA_SETBAND_AUTO,
-	QCA_SETBAND_5G,
-	QCA_SETBAND_2G,
+	QCA_SETBAND_AUTO = 0,
+	QCA_SETBAND_5G = BIT(0),
+	QCA_SETBAND_2G = BIT(1),
+	QCA_SETBAND_6G = BIT(2),
 };
 
 /**
@@ -1695,6 +1770,8 @@
  *	(not including the Element ID Extension field). Please note that the
  *	draft is still work in progress and this element payload is subject to
  *	change.
+ *
+ *  @QCA_VENDOR_ELEM_ALLPLAY: Allplay element
  */
 enum qca_vendor_element_id {
 	QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST = 0,
@@ -1703,6 +1780,7 @@
 	QCA_VENDOR_ELEM_RAPS = 3,
 	QCA_VENDOR_ELEM_MU_EDCA_PARAMS = 4,
 	QCA_VENDOR_ELEM_BSS_COLOR_CHANGE = 5,
+	QCA_VENDOR_ELEM_ALLPLAY = 6,
 };
 
 /**
@@ -2120,6 +2198,141 @@
 	 */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_ROAM_REASON = 61,
 
+	/* 32-bit unsigned value to configure different PHY modes to the
+	 * driver/firmware. The possible values are defined in
+	 * enum qca_wlan_vendor_phy_mode. The configuration will be reset to
+	 * default value, i.e., QCA_WLAN_VENDOR_PHY_MODE_AUTO upon restarting
+	 * the driver.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_PHY_MODE = 62,
+
+	/* 8-bit unsigned value to configure the maximum supported channel width
+	 * for STA mode. If this value is configured when STA is in connected
+	 * state, it should not exceed the negotiated channel width. If it is
+	 * configured when STA is in disconnected state, the configured value
+	 * will take effect for the next immediate connection.
+	 * Possible values are:
+	 *   NL80211_CHAN_WIDTH_20
+	 *   NL80211_CHAN_WIDTH_40
+	 *   NL80211_CHAN_WIDTH_80
+	 *   NL80211_CHAN_WIDTH_80P80
+	 *   NL80211_CHAN_WIDTH_160
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_WIDTH = 63,
+
+	/* 8-bit unsigned value to enable/disable dynamic bandwidth adjustment.
+	 * This attribute is only applicable for STA mode. When dynamic
+	 * bandwidth adjustment is disabled, STA will use static channel width
+	 * the value of which is negotiated during connection.
+	 * 1-enable (default), 0-disable
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_DYNAMIC_BW = 64,
+
+	/* 8-bit unsigned value to configure the maximum number of subframes of
+	 * TX MSDU for aggregation. Possible values are 0-31. When set to 0,
+	 * it is decided by the hardware.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MSDU_AGGREGATION = 65,
+
+	/* 8-bit unsigned value to configure the maximum number of subframes of
+	 * RX MSDU for aggregation. Possible values are 0-31. When set to 0,
+	 * it is decided by the hardware.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MSDU_AGGREGATION = 66,
+
+	/* 8-bit unsigned value. This attribute is used to dynamically
+	 * enable/disable the LDPC capability of the device. When configured in
+	 * the disconnected state, the updated configuration will be considered
+	 * for the immediately following connection attempt. If this
+	 * configuration is modified while the device is in the connected state,
+	 * the LDPC TX will be updated with this configuration immediately,
+	 * while the LDPC RX configuration update will take place starting from
+	 * the subsequent association attempt.
+	 * 1-Enable, 0-Disable.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_LDPC = 67,
+
+	/* 8-bit unsigned value. This attribute is used to dynamically
+	 * enable/disable the TX STBC capability of the device. When configured
+	 * in the disconnected state, the updated configuration will be
+	 * considered for the immediately following connection attempt. If the
+	 * connection is formed with TX STBC enabled and if this configuration
+	 * is disabled during that association, the TX will be impacted
+	 * immediately. Further connection attempts will disable TX STBC.
+	 * However, enabling the TX STBC for a connected session with disabled
+	 * capability is not allowed and will fail.
+	 * 1-Enable, 0-Disable.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TX_STBC = 68,
+
+	/* 8-bit unsigned value. This attribute is used to dynamically
+	 * enable/disable the RX STBC capability of the device. When configured
+	 * in the disconnected state, the updated configuration will be
+	 * considered for the immediately following connection attempt. If the
+	 * configuration is modified in the connected state, there will be no
+	 * impact for the current association, but further connection attempts
+	 * will use the updated configuration.
+	 * 1-Enable, 0-Disable.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_RX_STBC = 69,
+
+	/* 8-bit unsigned value. This attribute is used to dynamically configure
+	 * the number of spatial streams. When configured in the disconnected
+	 * state, the updated configuration will be considered for the
+	 * immediately following connection attempt. If the NSS is updated after
+	 * the connection, the updated NSS value is notified to the peer using
+	 * the Operating Mode Notification/Spatial Multiplexing Power Save
+	 * frame. The updated NSS value after the connection shall not be
+	 * greater than the one negotiated during the connection. Any such
+	 * higher value configuration shall be returned with a failure.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_NSS = 70,
+	/* 8-bit unsigned value to trigger Optimized Power Management:
+	 * 1-Enable, 0-Disable
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT = 71,
+
+	/* 8-bit unsigned value. This attribute takes the QoS/access category
+	 * value represented by the enum qca_wlan_ac_type and expects the driver
+	 * to upgrade the UDP frames to this access category. The value of
+	 * QCA_WLAN_AC_ALL is invalid for this attribute. This will override the
+	 * DSCP value configured in the frame with the intention to only upgrade
+	 * the access category. That said, it is not intended to downgrade the
+	 * access category for the frames.
+	 * Set the value to QCA_WLAN_AC_BK if the QoS upgrade needs to be
+	 * disabled, as BK is of the lowest priority and an upgrade to it does
+	 * not result in any changes for the frames.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_UDP_QOS_UPGRADE = 72,
+
+	/* 8-bit unsigned value. This attribute is used to dynamically configure
+	 * the number of chains to be used for transmitting data. This
+	 * configuration is allowed only when in connected state and will be
+	 * effective until disconnected. The driver rejects this configuration
+	 * if the number of spatial streams being used in the current connection
+	 * cannot be supported by this configuration.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_TX_CHAINS = 73,
+	/* 8-bit unsigned value. This attribute is used to dynamically configure
+	 * the number of chains to be used for receiving data. This
+	 * configuration is allowed only when in connected state and will be
+	 * effective until disconnected. The driver rejects this configuration
+	 * if the number of spatial streams being used in the current connection
+	 * cannot be supported by this configuration.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_RX_CHAINS = 74,
+
+	/* 8-bit unsigned value to configure ANI setting type.
+	 * See &enum qca_wlan_ani_setting for possible values.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANI_SETTING = 75,
+	/* 32-bit signed value to configure ANI level. This is used when
+	 * ANI settings type is &QCA_WLAN_ANI_SETTING_FIXED.
+	 * The set and get of ANI level with &QCA_WLAN_ANI_SETTING_AUTO
+	 * is invalid, the driver will return a failure.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANI_LEVEL = 76,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_CONFIG_MAX =
@@ -2135,6 +2348,16 @@
 	QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_REPORT_FAIL
 
 /**
+ * enum qca_wlan_ani_setting - ANI setting type
+ * @QCA_WLAN_ANI_SETTING_AUTO: Automatically determine ANI level
+ * @QCA_WLAN_ANI_SETTING_FIXED: Fix ANI level to the dBm parameter
+ */
+enum qca_wlan_ani_setting {
+	QCA_WLAN_ANI_SETTING_AUTO = 0,
+	QCA_WLAN_ANI_SETTING_FIXED = 1,
+};
+
+/**
  * enum qca_wlan_vendor_attr_sap_config - Parameters for AP configuration
  *
  * @QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL: Optional (u8)
@@ -2189,19 +2412,54 @@
 
 /**
  * enum qca_wlan_gpio_attr - Parameters for GPIO configuration
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND: Required (u32)
+ * value to specify the GPIO command. Please refer to enum qca_gpio_cmd_type
+ * for the available values.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PINNUM: Required (u32)
+ * value to specify the GPIO number.
+ * This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is
+ * %QCA_WLAN_VENDOR_GPIO_CONFIG or %QCA_WLAN_VENDOR_GPIO_OUTPUT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_VALUE: Required (u32)
+ * value to specify the GPIO output level. Please refer to enum qca_gpio_value
+ * for the available values.
+ * This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is
+ * %QCA_WLAN_VENDOR_GPIO_OUTPUT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PULL_TYPE: Required (u32)
+ * value to specify the GPIO pull type. Please refer to enum qca_gpio_pull_type
+ * for the available values.
+ * This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is
+ * %QCA_WLAN_VENDOR_GPIO_CONFIG.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTR_MODE: Required (u32)
+ * value to specify the GPIO interrupt mode. Please refer to enum
+ * qca_gpio_interrupt_mode for the available values.
+ * This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is
+ * %QCA_WLAN_VENDOR_GPIO_CONFIG.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_DIR: Required (u32)
+ * value to specify the GPIO direction. Please refer to enum qca_gpio_direction
+ * for the available values.
+ * This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is
+ * %QCA_WLAN_VENDOR_GPIO_CONFIG.
  */
 enum qca_wlan_gpio_attr {
 	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INVALID = 0,
 	/* Unsigned 32-bit attribute for GPIO command */
-	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND,
+	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND = 1,
 	/* Unsigned 32-bit attribute for GPIO PIN number to configure */
-	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PINNUM,
+	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PINNUM = 2,
 	/* Unsigned 32-bit attribute for GPIO value to configure */
-	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_VALUE,
+	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_VALUE = 3,
 	/* Unsigned 32-bit attribute for GPIO pull type */
-	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PULL_TYPE,
+	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PULL_TYPE = 4,
 	/* Unsigned 32-bit attribute for GPIO interrupt mode */
-	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTR_MODE,
+	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTR_MODE = 5,
+	/* Unsigned 32-bit attribute for GPIO direction to configure */
+	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_DIR = 6,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_LAST,
@@ -2210,6 +2468,73 @@
 };
 
 /**
+ * enum gpio_cmd_type - GPIO configuration command type
+ * @QCA_WLAN_VENDOR_GPIO_CONFIG: Set GPIO configuration info
+ * @QCA_WLAN_VENDOR_GPIO_OUTPUT: Set GPIO output level
+ */
+enum qca_gpio_cmd_type {
+	QCA_WLAN_VENDOR_GPIO_CONFIG = 0,
+	QCA_WLAN_VENDOR_GPIO_OUTPUT = 1,
+};
+
+/**
+ * enum qca_gpio_pull_type - GPIO pull type
+ * @QCA_WLAN_GPIO_PULL_NONE: Set GPIO pull type to none
+ * @QCA_WLAN_GPIO_PULL_UP: Set GPIO pull up
+ * @QCA_WLAN_GPIO_PULL_DOWN: Set GPIO pull down
+ */
+enum qca_gpio_pull_type {
+	QCA_WLAN_GPIO_PULL_NONE = 0,
+	QCA_WLAN_GPIO_PULL_UP = 1,
+	QCA_WLAN_GPIO_PULL_DOWN = 2,
+	QCA_WLAN_GPIO_PULL_MAX,
+};
+
+/**
+ * enum qca_gpio_direction - GPIO direction
+ * @QCA_WLAN_GPIO_INPUT: Set GPIO as input mode
+ * @QCA_WLAN_GPIO_OUTPUT: Set GPIO as output mode
+ * @QCA_WLAN_GPIO_VALUE_MAX: Invalid value
+ */
+enum qca_gpio_direction {
+	QCA_WLAN_GPIO_INPUT = 0,
+	QCA_WLAN_GPIO_OUTPUT = 1,
+	QCA_WLAN_GPIO_DIR_MAX,
+};
+
+/**
+ * enum qca_gpio_value - GPIO Value
+ * @QCA_WLAN_GPIO_LEVEL_LOW: set gpio output level to low
+ * @QCA_WLAN_GPIO_LEVEL_HIGH: set gpio output level to high
+ * @QCA_WLAN_GPIO_LEVEL_MAX: Invalid value
+ */
+enum qca_gpio_value {
+	QCA_WLAN_GPIO_LEVEL_LOW = 0,
+	QCA_WLAN_GPIO_LEVEL_HIGH = 1,
+	QCA_WLAN_GPIO_LEVEL_MAX,
+};
+
+/**
+ * enum gpio_interrupt_mode - GPIO interrupt mode
+ * @QCA_WLAN_GPIO_INTMODE_DISABLE: Disable interrupt trigger
+ * @QCA_WLAN_GPIO_INTMODE_RISING_EDGE: Interrupt with GPIO rising edge trigger
+ * @QCA_WLAN_GPIO_INTMODE_FALLING_EDGE: Interrupt with GPIO falling edge trigger
+ * @QCA_WLAN_GPIO_INTMODE_BOTH_EDGE: Interrupt with GPIO both edge trigger
+ * @QCA_WLAN_GPIO_INTMODE_LEVEL_LOW: Interrupt with GPIO level low trigger
+ * @QCA_WLAN_GPIO_INTMODE_LEVEL_HIGH: Interrupt with GPIO level high trigger
+ * @QCA_WLAN_GPIO_INTMODE_MAX: Invalid value
+ */
+enum qca_gpio_interrupt_mode {
+	QCA_WLAN_GPIO_INTMODE_DISABLE = 0,
+	QCA_WLAN_GPIO_INTMODE_RISING_EDGE = 1,
+	QCA_WLAN_GPIO_INTMODE_FALLING_EDGE = 2,
+	QCA_WLAN_GPIO_INTMODE_BOTH_EDGE = 3,
+	QCA_WLAN_GPIO_INTMODE_LEVEL_LOW = 4,
+	QCA_WLAN_GPIO_INTMODE_LEVEL_HIGH = 5,
+	QCA_WLAN_GPIO_INTMODE_MAX,
+};
+
+/**
  * qca_wlan_set_qdepth_thresh_attr - Parameters for setting
  * MSDUQ depth threshold per peer per tid in the target
  *
@@ -3297,7 +3622,7 @@
 	QCA_WLAN_VENDOR_ATTR_LL_STATS_NUM_RADIOS = 66,
 
 	/* Signifies the nested list of channel attributes
-	 * QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_*
+	 * QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_*
 	 */
 	QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO = 67,
 
@@ -3359,6 +3684,17 @@
 	 */
 	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_PENDING_MSDU = 83,
 
+	/* u32 value representing total time in milliseconds for which the radio
+	 * is transmitting on this channel. This attribute will be nested
+	 * within QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_TX_TIME = 84,
+	/* u32 value representing total time in milliseconds for which the radio
+	 * is receiving all 802.11 frames intended for this device on this
+	 * channel. This attribute will be nested within
+	 * QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_RX_TIME = 85,
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX =
@@ -3706,6 +4042,30 @@
 	QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST_SCHEME_AFTER_LAST - 1,
 };
 
+/**
+ * enum qca_roam_scan_scheme: Scan scheme
+ *
+ * @QCA_ROAM_SCAN_SCHEME_NO_SCAN: No frequencies specified to scan.
+ *     Indicates the driver to not scan on a Roam Trigger scenario, but
+ *     disconnect. E.g., on a BTM request from the AP the driver/firmware shall
+ *     disconnect from the current connected AP by notifying a failure
+ *     code in the BTM response.
+ *
+ * @QCA_ROAM_SCAN_SCHEME_PARTIAL_SCAN: Indicates the driver/firmware to
+ *     trigger partial frequency scans. These frequencies are the ones learned
+ *     or maintained by the driver based on the probability of finding the
+ *     BSSIDs in the ESS for which the roaming is triggered.
+ *
+ * @QCA_ROAM_SCAN_SCHEME_FULL_SCAN: Indicates the driver/firmware to
+ *     trigger the scan on all the valid frequencies to find better
+ *     candidates to roam.
+ */
+enum qca_roam_scan_scheme {
+	QCA_ROAM_SCAN_SCHEME_NO_SCAN = 0,
+	QCA_ROAM_SCAN_SCHEME_PARTIAL_SCAN = 1,
+	QCA_ROAM_SCAN_SCHEME_FULL_SCAN = 2,
+};
+
 /*
  * enum qca_vendor_roam_triggers: Bitmap of roaming triggers
  *
@@ -3726,6 +4086,18 @@
  *	when BTM Request frame is received from the connected AP.
  * @QCA_ROAM_TRIGGER_REASON_BSS_LOAD: Set if the roam has to be triggered
  *	when the channel utilization is goes above the configured threshold.
+ * @QCA_ROAM_TRIGGER_REASON_USER_TRIGGER: Set if the roam has to be triggered
+ *	based on the request from the user (space).
+ * @QCA_ROAM_TRIGGER_REASON_DEAUTH: Set if the roam has to be triggered when
+ *	device receives Deauthentication/Disassociation frame from connected AP.
+ * @QCA_ROAM_TRIGGER_REASON_IDLE: Set if the roam has to be triggered when the
+ *	device is in idle state (no TX/RX) and suspend mode, if the current RSSI
+ *	is determined to be a poor one.
+ * @QCA_ROAM_TRIGGER_REASON_TX_FAILURES: Set if the roam has to be triggered
+ *	based on continuous TX Data frame failures to the connected AP.
+ * @QCA_ROAM_TRIGGER_REASON_EXTERNAL_SCAN: Set if the roam has to be triggered
+ *	based on the scan results obtained from an external scan (not triggered
+ *	to aim roaming).
  *
  * Set the corresponding roam trigger reason bit to consider it for roam
  * trigger.
@@ -3741,6 +4113,11 @@
 	QCA_ROAM_TRIGGER_REASON_DENSE		= 1 << 5,
 	QCA_ROAM_TRIGGER_REASON_BTM		= 1 << 6,
 	QCA_ROAM_TRIGGER_REASON_BSS_LOAD	= 1 << 7,
+	QCA_ROAM_TRIGGER_REASON_USER_TRIGGER	= 1 << 8,
+	QCA_ROAM_TRIGGER_REASON_DEAUTH          = 1 << 9,
+	QCA_ROAM_TRIGGER_REASON_IDLE		= 1 << 10,
+	QCA_ROAM_TRIGGER_REASON_TX_FAILURES	= 1 << 11,
+	QCA_ROAM_TRIGGER_REASON_EXTERNAL_SCAN	= 1 << 12,
 };
 
 /**
@@ -3897,6 +4274,40 @@
  *
  *	Clears the selection criteria configured in the driver when specified
  *	with clear command.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME: Unsigned 32-bit value.
+ *	Represents value of the scan frequency scheme from enum
+ *	qca_roam_scan_scheme.
+ *	It's an optional attribute. If this attribute is not configured, the
+ *	driver shall proceed with default behavior.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_CONNECTED_RSSI_THRESHOLD: Signed 32-bit value in dBm,
+ *	signifying the RSSI threshold of the current connected AP, indicating
+ *	the driver to trigger roam only when the current connected AP's RSSI
+ *	is less than this threshold.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD: Signed 32-bit value in dBm,
+ *	signifying the RSSI threshold of the candidate AP, indicating
+ *	the driver to trigger roam only to the candidate AP with RSSI
+ *	better than this threshold.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_USER_REASON: Unsigned 32-bit value. Represents the
+ *	user defined reason code to be sent to the AP in response to AP's
+ *	request to trigger the roam if the roaming cannot be triggered.
+ *	Applies to all the scenarios of AP assisted roaming (e.g., BTM).
+ *
+ * @QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME_TRIGGERS: Unsigned 32-bit value.
+ *	Carries a bitmap of the roam triggers specified in
+ *	enum qca_vendor_roam_triggers.
+ *	Represents the roam triggers for which the specific scan scheme from
+ *	enum qca_roam_scan_scheme has to be applied.
+ *	It's an optional attribute. If this attribute is not configured, but
+ *	QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME is specified, the scan scheme
+ *	specified through QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME is applicable for
+ *	all the roams.
+ *	If both QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME and
+ *	QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME_TRIGGERS are not specified, the
+ *	driver shall proceed with the default behavior.
  */
 enum qca_vendor_attr_roam_control {
 	QCA_ATTR_ROAM_CONTROL_ENABLE = 1,
@@ -3907,6 +4318,11 @@
 	QCA_ATTR_ROAM_CONTROL_FULL_SCAN_PERIOD = 6,
 	QCA_ATTR_ROAM_CONTROL_TRIGGERS = 7,
 	QCA_ATTR_ROAM_CONTROL_SELECTION_CRITERIA = 8,
+	QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME = 9,
+	QCA_ATTR_ROAM_CONTROL_CONNECTED_RSSI_THRESHOLD = 10,
+	QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD = 11,
+	QCA_ATTR_ROAM_CONTROL_USER_REASON = 12,
+	QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME_TRIGGERS = 13,
 
 	/* keep last */
 	QCA_ATTR_ROAM_CONTROL_AFTER_LAST,
@@ -3926,7 +4342,7 @@
  *	Represents the Request ID for the specific set of commands.
  *	This also helps to map specific set of commands to the respective
  *	ID / client. e.g., helps to identify the user entity configuring the
- *	Blacklist BSSID and accordingly clear the respective ones with the
+ *	ignored BSSIDs and accordingly clear the respective ones with the
  *	matching ID.
  *
  * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS: Unsigned
@@ -3993,17 +4409,18 @@
  *	the BSSID for the purpose of comparing it with other roam candidate.
  *
  * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS: Nested attribute,
- *	represents the BSSIDs to get blacklisted for roaming.
+ *	represents the BSSIDs to get ignored for roaming.
  *
  * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID: Unsigned
- *	32-bit value, represents the number of blacklisted BSSIDs.
+ *	32-bit value, represents the number of ignored BSSIDs.
  *
  * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID: 6-byte MAC
- *	address representing the Blacklisted BSSID.
+ *	address representing the ignored BSSID.
  *
  * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_HINT: Flag attribute,
- *	indicates this BSSID blacklist as a hint to the driver. The driver can
- *	select this BSSID in the worst case (when no other BSSIDs are better).
+ *	indicates this request to ignore the BSSID as a hint to the driver. The
+ *	driver can select this BSSID in the worst case (when no other BSSIDs are
+ *	better).
  *
  * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_CONTROL: Nested attribute to
  *	set/get/clear the roam control config as
@@ -4038,11 +4455,11 @@
 	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_BSSID = 16,
 	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_RSSI_MODIFIER = 17,
 
-	/* Attribute for set_blacklist bssid params */
+	/* Attribute for setting ignored BSSID parameters */
 	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 */
+	/* Flag attribute indicates this entry as a hint */
 	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_HINT = 21,
 
 	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_CONTROL = 22,
@@ -4078,9 +4495,9 @@
  *	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS to set the BSSID
  *	preference.
  *
- * @QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BLACKLIST_BSSID: Sets the Blacklist
- *	BSSIDs. Refers QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS to
- *	set the same.
+ * @QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BLACKLIST_BSSID: Sets the list of BSSIDs
+ *	to ignore in roaming decision. Uses
+ *	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS to set the list.
  *
  * @QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_SET: Command to set the
  *	roam control config to the driver with the attribute
@@ -4742,9 +5159,9 @@
 	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DISALLOW_ADHOC  = 1 << 6,
 	/* Station only channel */
 	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DISALLOW_HOSTAP = 1 << 7,
-	/* DFS radar history for slave device (STA mode) */
+	/* DFS radar history for client device (STA mode) */
 	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_HISTORY_RADAR   = 1 << 8,
-	/* DFS CAC valid for slave device (STA mode) */
+	/* DFS CAC valid for client device (STA mode) */
 	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_CAC_VALID       = 1 << 9,
 };
 
@@ -6471,8 +6888,9 @@
 
 /**
  * enum qca_wlan_vendor_thermal_level - Defines various thermal levels
- * configured by userspace to the driver/firmware. The values will be
- * encapsulated in QCA_WLAN_VENDOR_ATTR_THERMAL_LEVEL attribute.
+ * configured by userspace to the driver/firmware.
+ * The values can be encapsulated in QCA_WLAN_VENDOR_ATTR_THERMAL_LEVEL or
+ * QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_LEVEL attribute.
  * The driver/firmware takes actions requested by userspace such as throttling
  * wifi TX etc. in order to mitigate high temperature.
  *
@@ -6506,8 +6924,9 @@
 	 */
 	QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_VALUE = 1,
 	/* Userspace uses this attribute to configure thermal level to the
-	 * driver/firmware. Used in request, u32 attribute, possible values
-	 * are defined in enum qca_wlan_vendor_thermal_level.
+	 * driver/firmware, or get thermal level from the driver/firmware.
+	 * Used in request or response, u32 attribute,
+	 * possible values are defined in enum qca_wlan_vendor_thermal_level.
 	 */
 	QCA_WLAN_VENDOR_ATTR_THERMAL_LEVEL = 2,
 	/* Userspace uses this attribute to configure the time in which the
@@ -6545,6 +6964,9 @@
  * resume action.
  * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SET_LEVEL: Configure thermal level to
  * the driver/firmware.
+ * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_LEVEL: Request to get the current
+ * thermal level from the driver/firmware. The driver should respond with a
+ * thermal level defined in enum qca_wlan_vendor_thermal_level.
  */
 enum qca_wlan_vendor_attr_thermal_cmd_type {
 	QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_PARAMS,
@@ -6552,6 +6974,7 @@
 	QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SUSPEND,
 	QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_RESUME,
 	QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SET_LEVEL,
+	QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_LEVEL,
 };
 
 /**
@@ -6634,6 +7057,11 @@
 	 * NLA_FLAG attribute.
 	 */
 	QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_RESUME_COMPLETE,
+	/* Thermal level from the driver.
+	 * u32 attribute. Possible values are defined in
+	 * enum qca_wlan_vendor_thermal_level.
+	 */
+	QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_LEVEL = 3,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_AFTER_LAST,
@@ -6847,6 +7275,72 @@
 	QCA_WLAN_VENDOR_ATTR_HE_OMI_AFTER_LAST - 1,
 };
 
+ /**
+  * enum qca_wlan_vendor_phy_mode - Different PHY modes
+  * These values are used with %QCA_WLAN_VENDOR_ATTR_CONFIG_PHY_MODE.
+  *
+  * @QCA_WLAN_VENDOR_PHY_MODE_AUTO: autoselect
+  * @QCA_WLAN_VENDOR_PHY_MODE_2G_AUTO: 2.4 GHz 802.11b/g/n/ax autoselect
+  * @QCA_WLAN_VENDOR_PHY_MODE_5G_AUTO: 5 GHz 802.11a/n/ac/ax autoselect
+  * @QCA_WLAN_VENDOR_PHY_MODE_11A: 5 GHz, OFDM
+  * @QCA_WLAN_VENDOR_PHY_MODE_11B: 2.4 GHz, CCK
+  * @QCA_WLAN_VENDOR_PHY_MODE_11G: 2.4 GHz, OFDM
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AGN: Support 802.11n in both 2.4 GHz and 5 GHz
+  * @QCA_WLAN_VENDOR_PHY_MODE_11NG_HT20: 2.4 GHz, HT20
+  * @QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40PLUS: 2.4 GHz, HT40 (ext ch +1)
+  * @QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40MINUS: 2.4 GHz, HT40 (ext ch -1)
+  * @QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40: 2.4 GHz, Auto HT40
+  * @QCA_WLAN_VENDOR_PHY_MODE_11NA_HT20: 5 GHz, HT20
+  * @QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40PLUS: 5 GHz, HT40 (ext ch +1)
+  * @QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40MINUS: 5 GHz, HT40 (ext ch -1)
+  * @QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40: 5 GHz, Auto HT40
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT20: 5 GHz, VHT20
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40PLUS: 5 GHz, VHT40 (Ext ch +1)
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40MINUS: 5 GHz VHT40 (Ext ch -1)
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40: 5 GHz, VHT40
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT80: 5 GHz, VHT80
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT80P80: 5 GHz, VHT80+80
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT160: 5 GHz, VHT160
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE20: HE20
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40: HE40
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40PLUS: HE40 (ext ch +1)
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40MINUS: HE40 (ext ch -1)
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80: HE80
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80P80: HE 80P80
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE160: HE160
+  */
+enum qca_wlan_vendor_phy_mode {
+	QCA_WLAN_VENDOR_PHY_MODE_AUTO = 0,
+	QCA_WLAN_VENDOR_PHY_MODE_2G_AUTO = 1,
+	QCA_WLAN_VENDOR_PHY_MODE_5G_AUTO = 2,
+	QCA_WLAN_VENDOR_PHY_MODE_11A = 3,
+	QCA_WLAN_VENDOR_PHY_MODE_11B = 4,
+	QCA_WLAN_VENDOR_PHY_MODE_11G = 5,
+	QCA_WLAN_VENDOR_PHY_MODE_11AGN = 6,
+	QCA_WLAN_VENDOR_PHY_MODE_11NG_HT20 = 7,
+	QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40PLUS = 8,
+	QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40MINUS = 9,
+	QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40 = 10,
+	QCA_WLAN_VENDOR_PHY_MODE_11NA_HT20 = 11,
+	QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40PLUS = 12,
+	QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40MINUS = 13,
+	QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40 = 14,
+	QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT20 = 15,
+	QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40PLUS = 16,
+	QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40MINUS = 17,
+	QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40 = 18,
+	QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT80 = 19,
+	QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT80P80 = 20,
+	QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT160 = 21,
+	QCA_WLAN_VENDOR_PHY_MODE_11AX_HE20 = 22,
+	QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40 = 23,
+	QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40PLUS = 24,
+	QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40MINUS = 25,
+	QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80 = 26,
+	QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80P80 = 27,
+	QCA_WLAN_VENDOR_PHY_MODE_11AX_HE160 = 28,
+};
+
 /* Attributes for data used by
  * QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION
  */
@@ -7130,6 +7624,67 @@
 	 */
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TWT_REQ_SUPPORT = 37,
 
+	/* 8-bit unsigned value to configure protection for Management
+	 * frames when PMF is enabled for the association.
+	 * This attribute is used to configure the testbed device.
+	 * 0-use the correct key, 1-use an incorrect key, 2-disable protection.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_PMF_PROTECTION = 38,
+
+	/* Flag attribute to inject Disassociation frame to the connected AP.
+	 * This attribute is used to configure the testbed device.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_DISASSOC_TX = 39,
+
+	/* 8-bit unsigned value to configure an override for the RSNXE Used
+	 * subfield in the MIC control field of the FTE in FT Reassociation
+	 * Request frame.
+	 * 0 - Default behavior, 1 - override with 1, 2 - override with 0.
+	 * This attribute is used to configure the testbed device.
+	 * This attribute can be configured only when STA is in associated state
+	 * and the configuration is valid until the disconnection.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_FT_REASSOCREQ_RSNXE_USED = 40,
+
+	/* 8-bit unsigned value to configure the driver to ignore CSA (Channel
+	 * Switch Announcement) when STA is in connected state.
+	 * 0 - Default behavior, 1 - Ignore CSA.
+	 * This attribute is used to configure the testbed device.
+	 * This attribute can be configured only when STA is in associated state
+	 * and the configuration is valid until the disconnection.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_IGNORE_CSA = 41,
+
+	/* Nested attribute values required to configure OCI (Operating Channel
+	 * Information). Attributes defined in enum
+	 * qca_wlan_vendor_attr_oci_override are nested within this attribute.
+	 * This attribute is used to configure the testbed device.
+	 * This attribute can be configured only when STA is in associated state
+	 * and the configuration is valid until the disconnection.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OCI_OVERRIDE = 42,
+
+	/* 8-bit unsigned value to configure the driver/firmware to ignore SA
+	 * Query timeout. If this configuration is enabled STA shall not send
+	 * Deauthentication frmae when SA Query times out (mainly, after a
+	 * channel switch when OCV is enabled).
+	 * 0 - Default behavior, 1 - Ignore SA Query timeout.
+	 * This attribute is used to configure the testbed device.
+	 * This attribute can be configured only when STA is in associated state
+	 * and the configuration is valid until the disconnection.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_IGNORE_SA_QUERY_TIMEOUT = 43,
+
+	/* 8-bit unsigned value to configure the driver/firmware to start or
+	 * stop transmitting FILS discovery frames.
+	 * 0 - Stop transmitting FILS discovery frames
+	 * 1 - Start transmitting FILS discovery frames
+	 * This attribute is used to configure the testbed device.
+	 * This attribute can be configured only in AP mode and the
+	 * configuration is valid until AP restart.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_FILS_DISCOVERY_FRAMES_TX = 44,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX =
@@ -7137,6 +7692,63 @@
 };
 
 /**
+ * enum qca_wlan_twt_operation - Operation of the config TWT request
+ * Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_OPERATION.
+ *
+ * @QCA_WLAN_TWT_SET: Setup a TWT session. Required parameters are configured
+ * through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum
+ * qca_wlan_vendor_attr_twt_setup.
+ *
+ * @QCA_WLAN_TWT_GET: Get the configured TWT parameters. Required parameters are
+ * obtained through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum
+ * qca_wlan_vendor_attr_twt_setup.
+ *
+ * @QCA_WLAN_TWT_TERMINATE: Terminate the TWT session. Required parameters are
+ * obtained through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum
+ * qca_wlan_vendor_attr_twt_setup. Valid only after the TWT session is setup.
+ *
+ * @QCA_WLAN_TWT_SUSPEND: Suspend the TWT session. Required parameters are
+ * obtained through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum
+ * qca_wlan_vendor_attr_twt_setup. Valid only after the TWT session is setup.
+ *
+ * @QCA_WLAN_TWT_RESUME: Resume the TWT session. Required parameters are
+ * configured through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum
+ * qca_wlan_vendor_attr_twt_resume. Valid only after the TWT session is setup.
+ */
+enum qca_wlan_twt_operation {
+	QCA_WLAN_TWT_SET = 0,
+	QCA_WLAN_TWT_GET = 1,
+	QCA_WLAN_TWT_TERMINATE = 2,
+	QCA_WLAN_TWT_SUSPEND = 3,
+	QCA_WLAN_TWT_RESUME = 4,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_config_twt: Defines attributes used by
+ * %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_OPERATION: u8 attribute. Specify the TWT
+ * operation of this request. Possible values are defined in enum
+ * qca_wlan_twt_operation. The parameters for the respective operation is
+ * specified through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS: Nested attribute representing the
+ * parameters configured for TWT. These parameters are represented by
+ * enum qca_wlan_vendor_attr_twt_setup or enum qca_wlan_vendor_attr_twt_resume
+ * based on the operation.
+ */
+enum qca_wlan_vendor_attr_config_twt {
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_OPERATION = 1,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS = 2,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_MAX =
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_AFTER_LAST - 1,
+};
+
+/**
  * enum qca_wlan_vendor_attr_bss_filter - Used by the vendor command
  * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER.
  * The user can add/delete the filter by specifying the BSSID/STA MAC address in
@@ -7287,7 +7899,8 @@
  * enum qca_wlan_vendor_attr_twt_setup: Represents attributes for
  * TWT (Target Wake Time) setup request. These attributes are sent as part of
  * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP and
- * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION.
+ * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION. Also used by
+ * attributes through %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT.
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST: Flag attribute.
  * Disable (flag attribute not present) - Individual TWT
@@ -7297,10 +7910,13 @@
  * STA and AP.
  * Broadcast means the session is across multiple STAs and an AP. The
  * configuration parameters are announced in Beacon frames by the AP.
+ * This is used in
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Response
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_REQ_TYPE: Required (u8).
  * Unsigned 8-bit qca_wlan_vendor_twt_setup_req_type to
- * specify the TWT request type
+ * specify the TWT request type. This is used in TWT SET operation.
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TRIGGER: Flag attribute
  * Enable (flag attribute present) - TWT with trigger support.
@@ -7308,40 +7924,113 @@
  * Trigger means the AP will send the trigger frame to allow STA to send data.
  * Without trigger, the STA will wait for the MU EDCA timer before
  * transmitting the data.
+ * This is used in
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Response
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_TYPE: Required (u8)
  * 0 - Announced TWT - In this mode, STA may skip few service periods to
  * save more power. If STA wants to wake up, it will send a PS-POLL/QoS
  * NULL frame to AP.
  * 1 - Unannounced TWT - The STA will wakeup during every SP.
+ * This is a required parameter for
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Response
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_ID: Optional (u8)
  * Flow ID is the unique identifier for each TWT session.
- * Currently this is not required and dialog ID will be set to zero.
+ * If not provided then dialog ID will be set to zero.
+ * This is an optional parameter for
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Request and Response
+ * 3. TWT TERMINATE Request and Response
+ * 4. TWT SUSPEND Request and Response
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_EXP: Required (u8)
  * This attribute (exp) is used along with the mantissa to derive the
  * wake interval using the following formula:
  * pow(2,exp) = wake_intvl_us/wake_intvl_mantis
  * Wake interval is the interval between 2 successive SP.
+ * This is a required parameter for
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Response
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PROTECTION: Flag attribute
  * Enable (flag attribute present) - Protection required.
  * Disable (flag attribute not present) - Protection not required.
  * If protection is enabled, then the AP will use protection
  * mechanism using RTS/CTS to self to reserve the airtime.
+ * This is used in
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Response
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME: Optional (u32)
  * This attribute is used as the SP offset which is the offset from
  * TSF after which the wake happens. The units are in microseconds. If
  * this attribute is not provided, then the value will be set to zero.
+ * This is an optional parameter for
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Response
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION: Required (u32)
- * This is the duration of the service period. The units are in TU.
+ * This is the duration of the service period. This is specified as
+ * multiples of 256 microseconds. Valid values are 0x1 to 0xFF.
+ * This is a required parameter for
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Response
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA: Required (u32)
  * This attribute is used to configure wake interval mantissa.
  * The units are in TU.
+ * This is a required parameter for
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Response
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_STATUS: Required (u8)
+ * This field is applicable for TWT response only.
+ * This contains status values in enum qca_wlan_vendor_twt_status
+ * and is passed to the userspace. This is used in TWT SET operation.
+ * This is a required parameter for
+ * 1. TWT SET Response
+ * 2. TWT TERMINATE Response
+ * 3. TWT SUSPEND Response
+ * 4. TWT RESUME Response
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RESP_TYPE: Required (u8)
+ * This field is applicable for TWT response only.
+ * This field contains response type from the TWT responder and is
+ * passed to the userspace. The values for this field are defined in
+ * enum qca_wlan_vendor_twt_setup_resp_type. This is used in TWT SET
+ * response.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME_TSF: Required (u64)
+ * This field is applicable for TWT response only.
+ * This field contains absolute TSF value of the wake time received
+ * from the TWT responder and is passed to the userspace.
+ * This is a required parameter for
+ * 1. TWT SET Response
+ * 2. TWT GET Response
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TWT_INFO_ENABLED: Flag attribute.
+ * Enable (flag attribute present) - Indicates that the TWT responder
+ * supports reception of TWT information frame from the TWT requestor.
+ * Disable (flag attribute not present) - Indicates that the responder
+ * doesn't support reception of TWT information frame from requestor.
+ * This is used in
+ * 1. TWT SET Response
+ * 2. TWT GET Response
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAC_ADDR: 6-byte MAC address
+ * Represents the MAC address of the peer for which the TWT session
+ * is being configured. This is used in AP mode to represent the respective
+ * client. In AP mode, this is an optional parameter for response and is
+ * a required parameter for
+ * 1. TWT SET Request
+ * 2. TWT GET Request
+ * 3. TWT TERMINATE Request
+ * 4. TWT SUSPEND Request
+ * In STA mode, this is an optional parameter in request and response for
+ * the above four TWT operations.
  */
 enum qca_wlan_vendor_attr_twt_setup {
 	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_INVALID = 0,
@@ -7356,6 +8045,14 @@
 	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION = 9,
 	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA = 10,
 
+	/* TWT Response only attributes */
+	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_STATUS = 11,
+	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RESP_TYPE = 12,
+	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME_TSF = 13,
+	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TWT_INFO_ENABLED = 14,
+
+	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAC_ADDR = 15,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX =
@@ -7363,24 +8060,83 @@
 };
 
 /**
- * enum qca_wlan_vendor_attr_twt_resume: Represents attributes for
+ * enum qca_wlan_vendor_twt_status - Represents the status of the requested
+ * TWT operation
+ *
+ * @QCA_WLAN_VENDOR_TWT_STATUS_OK: TWT request successfully completed
+ * @QCA_WLAN_VENDOR_TWT_STATUS_TWT_NOT_ENABLED: TWT not enabled
+ * @QCA_WLAN_VENDOR_TWT_STATUS_USED_DIALOG_ID: TWT dialog ID is already used
+ * @QCA_WLAN_VENDOR_TWT_STATUS_SESSION_BUSY: TWT session is busy
+ * @QCA_WLAN_VENDOR_TWT_STATUS_SESSION_NOT_EXIST: TWT session does not exist
+ * @QCA_WLAN_VENDOR_TWT_STATUS_NOT_SUSPENDED: TWT session not in suspend state
+ * @QCA_WLAN_VENDOR_TWT_STATUS_INVALID_PARAM: Invalid parameters
+ * @QCA_WLAN_VENDOR_TWT_STATUS_NOT_READY: FW not ready
+ * @QCA_WLAN_VENDOR_TWT_STATUS_NO_RESOURCE: FW resource exhausted
+ * @QCA_WLAN_VENDOR_TWT_STATUS_NO_ACK: Peer AP/STA did not ACK the
+ * request/response frame
+ * @QCA_WLAN_VENDOR_TWT_STATUS_NO_RESPONSE: Peer AP did not send the response
+ * frame
+ * @QCA_WLAN_VENDOR_TWT_STATUS_DENIED: AP did not accept the request
+ * @QCA_WLAN_VENDOR_TWT_STATUS_UNKNOWN_ERROR: Adding TWT dialog failed due to an
+ * unknown reason
+ * @QCA_WLAN_VENDOR_TWT_STATUS_ALREADY_SUSPENDED: TWT session already in
+ * suspend state
+ */
+enum qca_wlan_vendor_twt_status {
+	QCA_WLAN_VENDOR_TWT_STATUS_OK = 0,
+	QCA_WLAN_VENDOR_TWT_STATUS_TWT_NOT_ENABLED = 1,
+	QCA_WLAN_VENDOR_TWT_STATUS_USED_DIALOG_ID = 2,
+	QCA_WLAN_VENDOR_TWT_STATUS_SESSION_BUSY = 3,
+	QCA_WLAN_VENDOR_TWT_STATUS_SESSION_NOT_EXIST = 4,
+	QCA_WLAN_VENDOR_TWT_STATUS_NOT_SUSPENDED = 5,
+	QCA_WLAN_VENDOR_TWT_STATUS_INVALID_PARAM = 6,
+	QCA_WLAN_VENDOR_TWT_STATUS_NOT_READY = 7,
+	QCA_WLAN_VENDOR_TWT_STATUS_NO_RESOURCE = 8,
+	QCA_WLAN_VENDOR_TWT_STATUS_NO_ACK = 9,
+	QCA_WLAN_VENDOR_TWT_STATUS_NO_RESPONSE = 10,
+	QCA_WLAN_VENDOR_TWT_STATUS_DENIED = 11,
+	QCA_WLAN_VENDOR_TWT_STATUS_UNKNOWN_ERROR = 12,
+	QCA_WLAN_VENDOR_TWT_STATUS_ALREADY_SUSPENDED = 13,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_twt_resume - Represents attributes for
  * TWT (Target Wake Time) resume request. These attributes are sent as part of
  * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_RESUME and
- * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION.
+ * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION. Also used by
+ * attributes through %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT.
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT: Optional (u8)
- * This attribute is used as the SP offset which is the offset from
- * TSF after which the wake happens. The units are in microseconds.
- * If this attribute is not provided, then the value will be set to
- * zero.
+ * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT2_TWT: Optional (u32)
+ * These attributes are used as the SP offset which is the offset from TSF after
+ * which the wake happens. The units are in microseconds. Please note that
+ * _NEXT_TWT is limited to u8 whereas _NEXT2_TWT takes the u32 data.
+ * _NEXT2_TWT takes the precedence over _NEXT_TWT and thus the recommendation
+ * is to use _NEXT2_TWT. If neither of these attributes is provided, the value
+ * will be set to zero.
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT_SIZE: Required (u32)
  * This attribute represents the next TWT subfield size.
+ * Value 0 represents 0 bits, 1 represents 32 bits, 2 for 48 bits,
+ * and 4 for 64 bits.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_FLOW_ID: Required (u8).
+ * Flow ID is the unique identifier for each TWT session. This attribute
+ * represents the respective TWT session to resume.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_MAC_ADDR: 6-byte MAC address
+ * Represents the MAC address of the peer to which TWT Resume is
+ * being sent. This is used in AP mode to represent the respective
+ * client and is a required parameter. In STA mode, this is an optional
+ * parameter
  */
 enum qca_wlan_vendor_attr_twt_resume {
 	QCA_WLAN_VENDOR_ATTR_TWT_RESUME_INVALID = 0,
 	QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT = 1,
 	QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT_SIZE = 2,
+	QCA_WLAN_VENDOR_ATTR_TWT_RESUME_FLOW_ID = 3,
+	QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT2_TWT = 4,
+	QCA_WLAN_VENDOR_ATTR_TWT_RESUME_MAC_ADDR = 5,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_TWT_RESUME_AFTER_LAST,
@@ -7389,6 +8145,28 @@
 };
 
 /**
+ * enum qca_wlan_vendor_twt_setup_resp_type - Represents the response type by
+ * the TWT responder
+ *
+ * @QCA_WLAN_VENDOR_TWT_RESP_ALTERNATE: TWT responder suggests TWT
+ * parameters that are different from TWT requesting STA suggested
+ * or demanded TWT parameters
+ * @QCA_WLAN_VENDOR_TWT_RESP_DICTATE: TWT responder demands TWT
+ * parameters that are different from TWT requesting STA TWT suggested
+ * or demanded parameters
+ * @QCA_WLAN_VENDOR_TWT_RESP_REJECT: TWT responder rejects TWT
+ * setup
+ * @QCA_WLAN_VENDOR_TWT_RESP_ACCEPT: TWT responder accepts the TWT
+ * setup.
+ */
+enum qca_wlan_vendor_twt_setup_resp_type {
+	QCA_WLAN_VENDOR_TWT_RESP_ALTERNATE = 1,
+	QCA_WLAN_VENDOR_TWT_RESP_DICTATE = 2,
+	QCA_WLAN_VENDOR_TWT_RESP_REJECT = 3,
+	QCA_WLAN_VENDOR_TWT_RESP_ACCEPT = 4,
+};
+
+/**
  * enum qca_wlan_vendor_twt_setup_req_type - Required (u8)
  * Represents the setup type being requested for TWT.
  * @QCA_WLAN_VENDOR_TWT_SETUP_REQUEST: STA is not specifying all the TWT
@@ -8321,10 +9099,14 @@
  * enum qca_wlan_vendor_attr_oem_data_params - Used by the vendor command/event
  * QCA_NL80211_VENDOR_SUBCMD_OEM_DATA.
  *
- * @QCA_WLAN_VENDOR_ATTR_OEM_DATA_CMD_DATA: The binary blob for the vendor
- * command/event QCA_NL80211_VENDOR_SUBCMD_OEM_DATA are carried through this
- * attribute.
- * NLA_BINARY attribute, the max size is 1024 bytes.
+ * @QCA_WLAN_VENDOR_ATTR_OEM_DATA_CMD_DATA: This NLA_BINARY attribute is
+ * used to set/query the data to/from the firmware. On query, the same
+ * attribute is used to carry the respective data in the reply sent by the
+ * driver to userspace. The request to set/query the data and the format of the
+ * respective data from the firmware are embedded in the attribute. The
+ * maximum size of the attribute payload is 1024 bytes.
+ * Userspace has to set the QCA_WLAN_VENDOR_ATTR_OEM_DATA_RESPONSE_EXPECTED
+ * attribute when the data is queried from the firmware.
  *
  * @QCA_WLAN_VENDOR_ATTR_OEM_DEVICE_INFO: The binary blob will be routed
  * based on this field. This optional attribute is included to specify whether
@@ -8333,11 +9115,16 @@
  * command/event.
  * This u8 attribute is used to carry information for the device type using
  * values defined by enum qca_vendor_oem_device_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OEM_DATA_RESPONSE_EXPECTED: This NLA_FLAG attribute
+ * is set when the userspace queries data from the firmware. This attribute
+ * should not be set when userspace sets the OEM data to the firmware.
  */
 enum qca_wlan_vendor_attr_oem_data_params {
 	QCA_WLAN_VENDOR_ATTR_OEM_DATA_INVALID = 0,
 	QCA_WLAN_VENDOR_ATTR_OEM_DATA_CMD_DATA = 1,
 	QCA_WLAN_VENDOR_ATTR_OEM_DEVICE_INFO = 2,
+	QCA_WLAN_VENDOR_ATTR_OEM_DATA_RESPONSE_EXPECTED = 3,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_OEM_DATA_PARAMS_AFTER_LAST,
@@ -8671,6 +9458,36 @@
  * disconnect reason for the last disconnection if the disconnection is
  * triggered from the host driver. The values are referred from
  * enum qca_disconnect_reason_codes.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BIP_MIC_ERROR_COUNT: u32, used in STA mode
+ * only. This represents the number of group addressed robust management frames
+ * received from this station with an invalid MIC or a missing MME when PMF is
+ * enabled.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BIP_REPLAY_COUNT: u32, used in STA mode
+ * only. This represents the number of group addressed robust management frames
+ * received from this station with the packet number less than or equal to the
+ * last received packet number when PMF is enabled.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_MIC_ERROR_COUNT: u32, used in STA
+ * mode only. This represents the number of Beacon frames received from this
+ * station with an invalid MIC or a missing MME when beacon protection is
+ * enabled.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_REPLAY_COUNT: u32, used in STA mode
+ * only. This represents number of Beacon frames received from this station with
+ * the packet number less than or equal to the last received packet number when
+ * beacon protection is enabled.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE: u32, used in
+ * STA mode only. The driver uses this attribute to populate the connection
+ * failure reason codes and the values are defined in
+ * enum qca_sta_connect_fail_reason_codes. Userspace applications can send
+ * QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO vendor command after receiving
+ * a connection failure indication from the driver. The driver shall not
+ * include this attribute in response to the
+ * QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO command if there is no connection
+ * failure observed in the last attempted connection.
  */
 enum qca_wlan_vendor_attr_get_sta_info {
 	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_INVALID = 0,
@@ -8712,6 +9529,11 @@
 	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_IES = 36,
 	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PROBE_RESP_IES = 37,
 	QCA_WLAN_VENDOR_ATTR_GET_STA_DRIVER_DISCONNECT_REASON = 38,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BIP_MIC_ERROR_COUNT = 39,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BIP_REPLAY_COUNT = 40,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_MIC_ERROR_COUNT = 41,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_REPLAY_COUNT = 42,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE = 43,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_AFTER_LAST,
@@ -8841,4 +9663,352 @@
 	QCA_WLAN_VENDOR_ATTR_DRIVER_DISCONNECT_REASON_AFTER_LAST - 1,
 };
 
+/**
+ * enum qca_wlan_tspec_operation - Operation of the config TSPEC request
+ *
+ * Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_OPERATION.
+ */
+enum qca_wlan_tspec_operation {
+	QCA_WLAN_TSPEC_ADD = 0,
+	QCA_WLAN_TSPEC_DEL = 1,
+	QCA_WLAN_TSPEC_GET = 2,
+};
+
+/**
+ * enum qca_wlan_tspec_direction - Direction in TSPEC
+ * As what is defined in IEEE Std 802.11-2016, Table 9-139.
+ *
+ * Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_DIRECTION.
+ */
+enum qca_wlan_tspec_direction {
+	QCA_WLAN_TSPEC_DIRECTION_UPLINK = 0,
+	QCA_WLAN_TSPEC_DIRECTION_DOWNLINK = 1,
+	QCA_WLAN_TSPEC_DIRECTION_DIRECT = 2,
+	QCA_WLAN_TSPEC_DIRECTION_BOTH = 3,
+};
+
+/**
+ * enum qca_wlan_tspec_ack_policy - MAC acknowledgement policy in TSPEC
+ * As what is defined in IEEE Std 802.11-2016, Table 9-141.
+ *
+ * Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_ACK_POLICY.
+ */
+enum qca_wlan_tspec_ack_policy {
+	QCA_WLAN_TSPEC_NORMAL_ACK = 0,
+	QCA_WLAN_TSPEC_NO_ACK = 1,
+	/* Reserved */
+	QCA_WLAN_TSPEC_BLOCK_ACK = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_config_tspec - Defines attributes
+ * used by %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TSPEC vendor command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_OPERATION:
+ * u8 attribute. Specify the TSPEC operation of this request. Possible values
+ * are defined in enum qca_wlan_tspec_operation.
+ * Mandatory attribute for all kinds of config TSPEC requests.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_TSID:
+ * u8 attribute. TS ID. Possible values are 0-7.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD, QCA_WLAN_TSPEC_DEL,
+ * QCA_WLAN_TSPEC_GET. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_DIRECTION:
+ * u8 attribute. Direction of data carried by the TS. Possible values are
+ * defined in enum qca_wlan_tspec_direction.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_APSD:
+ * Flag attribute. Indicate whether APSD is enabled for the traffic associated
+ * with the TS. set - enabled, not set - disabled.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_USER_PRIORITY:
+ * u8 attribute. User priority to be used for the transport of MSDUs/A-MSDUs
+ * belonging to this TS. Possible values are 0-7.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_ACK_POLICY:
+ * u8 attribute. Indicate whether MAC acknowledgements are required for
+ * MPDUs/A-MSDUs belonging to this TS and the form of those acknowledgements.
+ * Possible values are defined in enum qca_wlan_tspec_ack_policy.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_NOMINAL_MSDU_SIZE:
+ * u16 attribute. Specify the nominal size in bytes of MSDUs/A-MSDUs
+ * belonging to this TS.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAXIMUM_MSDU_SIZE:
+ * u16 attribute. Specify the maximum size in bytes of MSDUs/A-MSDUs
+ * belonging to this TS.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MIN_SERVICE_INTERVAL:
+ * u32 attribute. Specify the minimum interval in microseconds between the
+ * start of two successive SPs.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX_SERVICE_INTERVAL:
+ * u32 attribute. Specify the maximum interval in microseconds between the
+ * start of two successive SPs.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_INACTIVITY_INTERVAL:
+ * u32 attribute. Specify the minimum interval in microseconds that can elapse
+ * without arrival or transfer of an MPDU belonging to the TS before this TS
+ * is deleted by the MAC entity at the HC.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_SUSPENSION_INTERVAL:
+ * u32 attribute. Specify the minimum interval in microseconds that can elapse
+ * without arrival or transfer of an MSDU belonging to the TS before the
+ * generation of successive QoS(+)CF-Poll is stopped for this TS. A value of
+ * 0xFFFFFFFF disables the suspension interval. The value of the suspension
+ * interval is always less than or equal to the inactivity interval.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MINIMUM_DATA_RATE:
+ * u32 attribute. Indicate the lowest data rate in bps specified at the MAC
+ * SAP for transport of MSDUs or A-MSDUs belonging to this TS within the
+ * bounds of this TSPEC.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MEAN_DATA_RATE:
+ * u32 attribute. Indicate the average data rate in bps specified at the MAC
+ * SAP for transport of MSDUs or A-MSDUs belonging to this TS within the
+ * bounds of this TSPEC.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_PEAK_DATA_RATE:
+ * u32 attribute. Indicate the maximum allowable data rate in bps specified at
+ * the MAC SAP for transport of MSDUs or A-MSDUs belonging to this TS within
+ * the bounds of this TSPEC.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_BURST_SIZE:
+ * u32 attribute. Specify the maximum burst size in bytes of the MSDUs/A-MSDUs
+ * belonging to this TS that arrive at the MAC SAP at the peak data rate. A
+ * value of 0 indicates that there are no bursts.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MINIMUM_PHY_RATE:
+ * u32 attribute. Indicate the minimum PHY rate in bps for transport of
+ * MSDUs/A-MSDUs belonging to this TS within the bounds of this TSPEC.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_SURPLUS_BANDWIDTH_ALLOWANCE:
+ * u16 attribute. Specify the excess allocation of time (and bandwidth) over
+ * and above the stated application rates required to transport an MSDU/A-MSDU
+ * belonging to the TS in this TSPEC.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ */
+enum qca_wlan_vendor_attr_config_tspec {
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_OPERATION = 1,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_TSID = 2,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_DIRECTION = 3,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_APSD = 4,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_USER_PRIORITY = 5,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_ACK_POLICY = 6,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_NOMINAL_MSDU_SIZE = 7,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAXIMUM_MSDU_SIZE = 8,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MIN_SERVICE_INTERVAL = 9,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX_SERVICE_INTERVAL = 10,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_INACTIVITY_INTERVAL = 11,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_SUSPENSION_INTERVAL = 12,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MINIMUM_DATA_RATE = 13,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MEAN_DATA_RATE = 14,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_PEAK_DATA_RATE = 15,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_BURST_SIZE = 16,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MINIMUM_PHY_RATE = 17,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_SURPLUS_BANDWIDTH_ALLOWANCE = 18,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX =
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_oci_override_frame_type - OCI override frame type
+ * @QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_SA_QUERY_REQ: SA Query Request frame
+ * @QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_SA_QUERY_RESP: SA Query Response frame
+ * @QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_FT_REASSOC_REQ: FT Reassociation Request
+ * frame
+ * @QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_FILS_REASSOC_REQ: FILS Reassociation
+ * Request frame.
+ */
+enum qca_wlan_vendor_oci_override_frame_type {
+	QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_SA_QUERY_REQ = 1,
+	QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_SA_QUERY_RESP = 2,
+	QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_FT_REASSOC_REQ = 3,
+	QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_FILS_REASSOC_REQ = 4,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_oci_override: Represents attributes for
+ * OCI override request. These attributes are used inside nested attribute
+ * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OCI_OVERRIDE in QCA vendor command
+ * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FRAME_TYPE: Required attribute, u8.
+ * Values from enum qca_wlan_vendor_oci_override_frame_type used in this
+ * attribute to specify the frame type in which the OCI is to be overridden.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FREQUENCY: Required (u32)
+ * OCI frequency (in MHz) to override in the specified frame type.
+ */
+enum qca_wlan_vendor_attr_oci_override {
+	QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FRAME_TYPE = 1,
+	QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FREQUENCY = 2,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_MAX =
+	QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_medium_assess_type - Type of medium assess request
+ *
+ * Values for %QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TYPE.
+ */
+enum qca_wlan_medium_assess_type {
+	QCA_WLAN_MEDIUM_ASSESS_CCA = 0,
+	QCA_WLAN_MEDIUM_ASSESS_CONGESTION_REPORT = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_medium_assess - Attributes used by
+ * %QCA_NL80211_VENDOR_SUBCMD_MEDIUM_ASSESS vendor command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TYPE:
+ * u8 attribute. Mandatory in all kinds of medium assess requests/responses.
+ * Specify the type of medium assess request and indicate its type in response.
+ * Possible values are defined in enum qca_wlan_medium_assess_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_PERIOD:
+ * u32 attribute. Mandatory in CCA request.
+ * Specify the assessment period in terms of seconds. Assessment result will be
+ * sent as the response to the CCA request after the assessment period.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TOTAL_CYCLE_COUNT:
+ * u32 attribute. Mandatory in response to CCA request.
+ * Total timer tick count of the assessment cycle.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_IDLE_COUNT:
+ * u32 attribute. Mandatory in response to CCA request.
+ * Timer tick count of idle time in the assessment cycle.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_IBSS_RX_COUNT:
+ * u32 attribute. Mandatory in response to CCA request.
+ * Timer tick count of Intra BSS traffic RX time in the assessment cycle.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_OBSS_RX_COUNT:
+ * u32 attribute. Mandatory in response to CCA request.
+ * Timer tick count of Overlapping BSS traffic RX time in the assessment cycle.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MAX_IBSS_RSSI:
+ * s32 attribute. Mandatory in response to CCA request.
+ * Maximum RSSI of Intra BSS traffic in the assessment cycle.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MIN_IBSS_RSSI:
+ * s32 attribute. Mandatory in response to CCA request.
+ * Minimum RSSI of Intra BSS traffic in the assessment cycle.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_ENABLE:
+ * u8 attribute. Mandatory in congestion report request.
+ * 1-enable 0-disable.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_THRESHOLD:
+ * u8 attribute. Mandatory in congestion report enable request and will be
+ * ignored if present in congestion report disable request. Possible values are
+ * 0-100. A vendor event QCA_NL80211_VENDOR_SUBCMD_MEDIUM_ASSESS with the type
+ * QCA_WLAN_MEDIUM_ASSESS_CONGESTION_REPORT will be sent to userspace if
+ * congestion percentage reaches the configured threshold.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_INTERVAL:
+ * u8 attribute. Optional in congestion report enable request and will be
+ * ignored if present in congestion report disable request.
+ * Specify the interval of congestion report event in terms of seconds. Possible
+ * values are 1-255. Default value 1 will be used if this attribute is omitted
+ * or using invalid values.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_PERCENTAGE:
+ * u8 attribute. Mandatory in congestion report event.
+ * Indicate the actual congestion percentage. Possible values are 0-100.
+ */
+enum qca_wlan_vendor_attr_medium_assess {
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TYPE = 1,
+
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_PERIOD = 2,
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TOTAL_CYCLE_COUNT = 3,
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_IDLE_COUNT = 4,
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_IBSS_RX_COUNT = 5,
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_OBSS_RX_COUNT = 6,
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MAX_IBSS_RSSI = 7,
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MIN_IBSS_RSSI = 8,
+
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_ENABLE = 9,
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_THRESHOLD = 10,
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_INTERVAL = 11,
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_PERCENTAGE = 12,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MAX =
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_mbssid_tx_vdev_status - Defines attributes
+ * used by QCA_NL80211_VENDOR_SUBCMD_MBSSID_TX_VDEV_STATUS vendor command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_VAL:
+ * u8 attribute. Notify the TX VDEV status. Possible values 0, 1
+ * belonging to MBSSID/EMA_AP configuration. 0 means Non-Tx VDEV,
+ * 1 means Tx VDEV. Mandatory attribute for all MBSSID VDEV status events.
+ */
+enum qca_wlan_vendor_attr_mbssid_tx_vdev_status {
+	QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_VAL = 1,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_MAX =
+	QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_sta_connect_fail_reason_codes - Defines values carried
+ * by QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE vendor
+ * attribute.
+ * @QCA_STA_CONNECT_FAIL_REASON_NO_BSS_FOUND: No Probe Response frame received
+ *	for unicast Probe Request frame.
+ * @QCA_STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL: STA failed to send auth request.
+ * @QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED: AP didn't send ACK for
+ *	auth request.
+ * @QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED: Auth response is not
+ *	received from AP.
+ * @QCA_STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL: STA failed to send
+ *	Association Request frame.
+ * @QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED: AP didn't send ACK for
+ *	Association Request frame.
+ * @QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED: Association Response
+ *	frame is not received from AP.
+ */
+enum qca_sta_connect_fail_reason_codes {
+	QCA_STA_CONNECT_FAIL_REASON_NO_BSS_FOUND = 1,
+	QCA_STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL = 2,
+	QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED = 3,
+	QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED = 4,
+	QCA_STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL = 5,
+	QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED = 6,
+	QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED = 7,
+};
+
 #endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index 1b4ec6d..057e1ce 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -713,6 +713,8 @@
 		goto fail;
 	const_time_select_bin(m_is_zero, bin1, bin2, prime_len, bin);
 	x1 = crypto_bignum_init_set(bin, prime_len);
+	if (!x1)
+		goto fail;
 	debug_print_bignum("SSWU: x1 = CSEL(l, x1a, x1b)", x1, prime_len);
 
 	/* gx1 = x1^3 + a * x1 + b */
@@ -753,6 +755,8 @@
 		goto fail;
 	const_time_select_bin(is_qr, bin1, bin2, prime_len, bin);
 	v = crypto_bignum_init_set(bin, prime_len);
+	if (!v)
+		goto fail;
 	debug_print_bignum("SSWU: v = CSEL(l, gx1, gx2)", v, prime_len);
 
 	/* x = CSEL(l, x1, x2) */
@@ -1052,10 +1056,17 @@
 
 	wpa_printf(MSG_DEBUG, "SAE: Derive PT - group %d", group);
 
+	if (ssid_len > 32)
+		return NULL;
+
 	pt = os_zalloc(sizeof(*pt));
 	if (!pt)
 		return NULL;
 
+#ifdef CONFIG_SAE_PK
+	os_memcpy(pt->ssid, ssid, ssid_len);
+	pt->ssid_len = ssid_len;
+#endif /* CONFIG_SAE_PK */
 	pt->group = group;
 	pt->ec = crypto_ec_init(group);
 	if (pt->ec) {
@@ -1354,14 +1365,15 @@
 						identifier) < 0))
 		return -1;
 
-	sae->tmp->h2e = 0;
+	sae->h2e = 0;
+	sae->pk = 0;
 	return sae_derive_commit(sae);
 }
 
 
 int sae_prepare_commit_pt(struct sae_data *sae, const struct sae_pt *pt,
 			  const u8 *addr1, const u8 *addr2,
-			  int *rejected_groups)
+			  int *rejected_groups, const struct sae_pk *pk)
 {
 	if (!sae->tmp)
 		return -1;
@@ -1377,6 +1389,11 @@
 		return -1;
 	}
 
+#ifdef CONFIG_SAE_PK
+	os_memcpy(sae->tmp->ssid, pt->ssid, pt->ssid_len);
+	sae->tmp->ssid_len = pt->ssid_len;
+	sae->tmp->ap_pk = pk;
+#endif /* CONFIG_SAE_PK */
 	sae->tmp->own_addr_higher = os_memcmp(addr1, addr2, ETH_ALEN) > 0;
 	wpabuf_free(sae->tmp->own_rejected_groups);
 	sae->tmp->own_rejected_groups = NULL;
@@ -1409,7 +1426,7 @@
 			return -1;
 	}
 
-	sae->tmp->h2e = 1;
+	sae->h2e = 1;
 	return sae_derive_commit(sae);
 }
 
@@ -1515,7 +1532,7 @@
 	const u8 *salt;
 	struct wpabuf *rejected_groups = NULL;
 	u8 keyseed[SAE_MAX_HASH_LEN];
-	u8 keys[SAE_MAX_HASH_LEN + SAE_PMK_LEN];
+	u8 keys[2 * SAE_MAX_HASH_LEN + SAE_PMK_LEN];
 	struct crypto_bignum *tmp;
 	int ret = -1;
 	size_t hash_len, salt_len, prime_len = sae->tmp->prime_len;
@@ -1530,15 +1547,18 @@
 	 * KCK || PMK = KDF-Hash-Length(keyseed, "SAE KCK and PMK",
 	 *                      (commit-scalar + peer-commit-scalar) modulo r)
 	 * PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128)
+	 *
+	 * When SAE-PK is used,
+	 * KCK || PMK || KEK = KDF-Hash-Length(keyseed, "SAE-PK keys", context)
 	 */
-	if (!sae->tmp->h2e)
+	if (!sae->h2e)
 		hash_len = SHA256_MAC_LEN;
 	else if (sae->tmp->dh)
 		hash_len = sae_ffc_prime_len_2_hash_len(prime_len);
 	else
 		hash_len = sae_ecc_prime_len_2_hash_len(prime_len);
-	if (sae->tmp->h2e && (sae->tmp->own_rejected_groups ||
-			      sae->tmp->peer_rejected_groups)) {
+	if (sae->h2e && (sae->tmp->own_rejected_groups ||
+			 sae->tmp->peer_rejected_groups)) {
 		struct wpabuf *own, *peer;
 
 		own = sae->tmp->own_rejected_groups;
@@ -1589,15 +1609,32 @@
 	 * octets). */
 	crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->order_len);
 	wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN);
-	if (sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK",
+	if (!sae->pk &&
+	    sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK",
 			 val, sae->tmp->order_len,
 			 keys, hash_len + SAE_PMK_LEN) < 0)
 		goto fail;
+#ifdef CONFIG_SAE_PK
+	if (sae->pk &&
+	    sae_kdf_hash(hash_len, keyseed, "SAE-PK keys",
+			 val, sae->tmp->order_len,
+			 keys, 2 * hash_len + SAE_PMK_LEN) < 0)
+		goto fail;
+#endif /* CONFIG_SAE_PK */
 	forced_memzero(keyseed, sizeof(keyseed));
 	os_memcpy(sae->tmp->kck, keys, hash_len);
 	sae->tmp->kck_len = hash_len;
 	os_memcpy(sae->pmk, keys + hash_len, SAE_PMK_LEN);
 	os_memcpy(sae->pmkid, val, SAE_PMKID_LEN);
+#ifdef CONFIG_SAE_PK
+	if (sae->pk) {
+		os_memcpy(sae->tmp->kek, keys + hash_len + SAE_PMK_LEN,
+			  hash_len);
+		sae->tmp->kek_len = hash_len;
+		wpa_hexdump_key(MSG_DEBUG, "SAE: KEK for SAE-PK",
+				sae->tmp->kek, sae->tmp->kek_len);
+	}
+#endif /* CONFIG_SAE_PK */
 	forced_memzero(keys, sizeof(keys));
 	wpa_hexdump_key(MSG_DEBUG, "SAE: KCK",
 			sae->tmp->kck, sae->tmp->kck_len);
@@ -1632,7 +1669,7 @@
 		return -1;
 
 	wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */
-	if (!sae->tmp->h2e && token) {
+	if (!sae->h2e && token) {
 		wpabuf_put_buf(buf, token);
 		wpa_hexdump(MSG_DEBUG, "SAE: Anti-clogging token",
 			    wpabuf_head(token), wpabuf_len(token));
@@ -1673,7 +1710,7 @@
 			   identifier);
 	}
 
-	if (sae->tmp->h2e && sae->tmp->own_rejected_groups) {
+	if (sae->h2e && sae->tmp->own_rejected_groups) {
 		wpa_hexdump_buf(MSG_DEBUG, "SAE: own Rejected Groups",
 				sae->tmp->own_rejected_groups);
 		wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
@@ -1683,7 +1720,7 @@
 		wpabuf_put_buf(buf, sae->tmp->own_rejected_groups);
 	}
 
-	if (sae->tmp->h2e && token) {
+	if (sae->h2e && token) {
 		wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
 		wpabuf_put_u8(buf, 1 + wpabuf_len(token));
 		wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN);
@@ -2189,13 +2226,14 @@
 }
 
 
-void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf)
+int sae_write_confirm(struct sae_data *sae, struct wpabuf *buf)
 {
 	const u8 *sc;
 	size_t hash_len;
+	int res;
 
 	if (sae->tmp == NULL)
-		return;
+		return -1;
 
 	hash_len = sae->tmp->kck_len;
 
@@ -2206,17 +2244,26 @@
 		sae->send_confirm++;
 
 	if (sae->tmp->ec)
-		sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar,
-				   sae->tmp->own_commit_element_ecc,
-				   sae->peer_commit_scalar,
-				   sae->tmp->peer_commit_element_ecc,
-				   wpabuf_put(buf, hash_len));
+		res = sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar,
+					 sae->tmp->own_commit_element_ecc,
+					 sae->peer_commit_scalar,
+					 sae->tmp->peer_commit_element_ecc,
+					 wpabuf_put(buf, hash_len));
 	else
-		sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar,
-				   sae->tmp->own_commit_element_ffc,
-				   sae->peer_commit_scalar,
-				   sae->tmp->peer_commit_element_ffc,
-				   wpabuf_put(buf, hash_len));
+		res = sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar,
+					 sae->tmp->own_commit_element_ffc,
+					 sae->peer_commit_scalar,
+					 sae->tmp->peer_commit_element_ffc,
+					 wpabuf_put(buf, hash_len));
+	if (res)
+		return res;
+
+#ifdef CONFIG_SAE_PK
+	if (sae_write_confirm_pk(sae, buf) < 0)
+		return -1;
+#endif /* CONFIG_SAE_PK */
+
+	return 0;
 }
 
 
@@ -2270,6 +2317,12 @@
 		return -1;
 	}
 
+#ifdef CONFIG_SAE_PK
+	if (sae_check_confirm_pk(sae, data + 2 + hash_len,
+				 len - 2 - hash_len) < 0)
+		return -1;
+#endif /* CONFIG_SAE_PK */
+
 	return 0;
 }
 
diff --git a/src/common/sae.h b/src/common/sae.h
index 7966d70..2243c0f 100644
--- a/src/common/sae.h
+++ b/src/common/sae.h
@@ -16,11 +16,27 @@
 #define SAE_MAX_ECC_PRIME_LEN 66
 #define SAE_MAX_HASH_LEN 64
 #define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN + 255)
+#ifdef CONFIG_SAE_PK
+#define SAE_CONFIRM_MAX_LEN ((2 + SAE_MAX_HASH_LEN) + 1500)
+#else /* CONFIG_SAE_PK */
 #define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_HASH_LEN)
+#endif /* CONFIG_SAE_PK */
+#define SAE_PK_M_LEN 16
 
 /* Special value returned by sae_parse_commit() */
 #define SAE_SILENTLY_DISCARD 65535
 
+struct sae_pk {
+	struct wpabuf *m;
+	struct crypto_ec_key *key;
+	int group;
+	struct wpabuf *pubkey; /* DER encoded subjectPublicKey */
+#ifdef CONFIG_TESTING_OPTIONS
+	struct crypto_ec_key *sign_key_override;
+#endif /* CONFIG_TESTING_OPTIONS */
+};
+
+
 struct sae_temporary_data {
 	u8 kck[SAE_MAX_HASH_LEN];
 	size_t kck_len;
@@ -46,8 +62,25 @@
 	u8 bssid[ETH_ALEN];
 	struct wpabuf *own_rejected_groups;
 	struct wpabuf *peer_rejected_groups;
-	unsigned int h2e:1;
 	unsigned int own_addr_higher:1;
+
+#ifdef CONFIG_SAE_PK
+	u8 kek[SAE_MAX_HASH_LEN];
+	size_t kek_len;
+	const struct sae_pk *ap_pk;
+	u8 own_addr[ETH_ALEN];
+	u8 peer_addr[ETH_ALEN];
+	u8 fingerprint[SAE_MAX_HASH_LEN];
+	size_t fingerprint_bytes;
+	size_t fingerprint_bits;
+	size_t lambda;
+	unsigned int sec;
+	u8 ssid[32];
+	size_t ssid_len;
+#ifdef CONFIG_TESTING_OPTIONS
+	bool omit_pk_elem;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_SAE_PK */
 };
 
 struct sae_pt {
@@ -58,6 +91,10 @@
 
 	const struct dh_group *dh;
 	struct crypto_bignum *ffc_pt;
+#ifdef CONFIG_SAE_PK
+	u8 ssid[32];
+	size_t ssid_len;
+#endif /* CONFIG_SAE_PK */
 };
 
 enum sae_state {
@@ -74,6 +111,8 @@
 	int group;
 	unsigned int sync; /* protocol instance variable: Sync */
 	u16 rc; /* protocol instance variable: Rc (received send-confirm) */
+	unsigned int h2e:1;
+	unsigned int pk:1;
 	struct sae_temporary_data *tmp;
 };
 
@@ -86,17 +125,19 @@
 		       const char *identifier, struct sae_data *sae);
 int sae_prepare_commit_pt(struct sae_data *sae, const struct sae_pt *pt,
 			  const u8 *addr1, const u8 *addr2,
-			  int *rejected_groups);
+			  int *rejected_groups, const struct sae_pk *pk);
 int sae_process_commit(struct sae_data *sae);
 int sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
 		     const struct wpabuf *token, const char *identifier);
 u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
 		     const u8 **token, size_t *token_len, int *allowed_groups,
 		     int h2e);
-void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf);
+int sae_write_confirm(struct sae_data *sae, struct wpabuf *buf);
 int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len);
 u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group);
 const char * sae_state_txt(enum sae_state state);
+size_t sae_ecc_prime_len_2_hash_len(size_t prime_len);
+size_t sae_ffc_prime_len_2_hash_len(size_t prime_len);
 struct sae_pt * sae_derive_pt(int *groups, const u8 *ssid, size_t ssid_len,
 			      const u8 *password, size_t password_len,
 			      const char *identifier);
@@ -108,4 +149,24 @@
 			   const u8 *addr1, const u8 *addr2);
 void sae_deinit_pt(struct sae_pt *pt);
 
+/* sae_pk.c */
+#ifdef CONFIG_SAE_PK
+bool sae_pk_valid_password(const char *pw);
+#else /* CONFIG_SAE_PK */
+static inline bool sae_pk_valid_password(const char *pw)
+{
+	return false;
+}
+#endif /* CONFIG_SAE_PK */
+char * sae_pk_base32_encode(const u8 *src, size_t len_bits);
+u8 * sae_pk_base32_decode(const char *src, size_t len, size_t *out_len);
+int sae_pk_set_password(struct sae_data *sae, const char *password);
+void sae_deinit_pk(struct sae_pk *pk);
+struct sae_pk * sae_parse_pk(const char *val);
+int sae_write_confirm_pk(struct sae_data *sae, struct wpabuf *buf);
+int sae_check_confirm_pk(struct sae_data *sae, const u8 *ies, size_t ies_len);
+int sae_hash(size_t hash_len, const u8 *data, size_t len, u8 *hash);
+u32 sae_pk_get_be19(const u8 *buf);
+void sae_pk_buf_shift_left_19(u8 *buf, size_t len);
+
 #endif /* SAE_H */
diff --git a/src/common/sae_pk.c b/src/common/sae_pk.c
new file mode 100644
index 0000000..df79e5f
--- /dev/null
+++ b/src/common/sae_pk.c
@@ -0,0 +1,884 @@
+/*
+ * SAE-PK
+ * Copyright (c) 2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <stdint.h>
+
+#include "utils/common.h"
+#include "utils/base64.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/crypto.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "sae.h"
+
+
+/* RFC 4648 base 32 alphabet with lowercase characters */
+static const char *sae_pk_base32_table = "abcdefghijklmnopqrstuvwxyz234567";
+
+
+static const u8 d_mult_table[] = {
+	 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
+	16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+	 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,  0,
+	17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16,
+	 2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,  0,  1,
+	18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17,
+	 3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,  0,  1,  2,
+	19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18,
+	 4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,  0,  1,  2,  3,
+	20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19,
+	 5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,  0,  1,  2,  3,  4,
+	21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20,
+	 6,  7,  8,  9, 10, 11, 12, 13, 14, 15,  0,  1,  2,  3,  4,  5,
+	22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21,
+	 7,  8,  9, 10, 11, 12, 13, 14, 15,  0,  1,  2,  3,  4,  5,  6,
+	23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22,
+	 8,  9, 10, 11, 12, 13, 14, 15,  0,  1,  2,  3,  4,  5,  6,  7,
+	24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23,
+	 9, 10, 11, 12, 13, 14, 15,  0,  1,  2,  3,  4,  5,  6,  7,  8,
+	25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+	10, 11, 12, 13, 14, 15,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
+	26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+	11, 12, 13, 14, 15,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10,
+	27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
+	12, 13, 14, 15,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,
+	28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+	13, 14, 15,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12,
+	29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
+	14, 15,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13,
+	30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+	15,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+	31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+	16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17,
+	 0, 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,
+	17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18,
+	 1,  0, 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,
+	18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19,
+	 2,  1,  0, 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,
+	19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20,
+	 3,  2,  1,  0, 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,
+	20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21,
+	 4,  3,  2,  1,  0, 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,
+	21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22,
+	 5,  4,  3,  2,  1,  0, 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,
+	22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23,
+	 6,  5,  4,  3,  2,  1,  0, 15, 14, 13, 12, 11, 10,  9,  8,  7,
+	23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24,
+	 7,  6,  5,  4,  3,  2,  1,  0, 15, 14, 13, 12, 11, 10,  9,  8,
+	24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25,
+	 8,  7,  6,  5,  4,  3,  2,  1,  0, 15, 14, 13, 12, 11, 10,  9,
+	25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26,
+	 9,  8,  7,  6,  5,  4,  3,  2,  1,  0, 15, 14, 13, 12, 11, 10,
+	26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27,
+	10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0, 15, 14, 13, 12, 11,
+	27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28,
+	11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0, 15, 14, 13, 12,
+	28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29,
+	12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0, 15, 14, 13,
+	29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30,
+	13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0, 15, 14,
+	30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31,
+	14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0, 15,
+	31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16,
+	15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0
+};
+
+static const u8 d_perm_table[] = {
+	 7,  2,  1, 30, 16, 20, 27, 11, 31,  6,  8, 13, 29,  5, 10, 21,
+	22,  3, 24,  0, 23, 25, 12,  9, 28, 14,  4, 15, 17, 18, 19, 26
+};
+
+
+static u8 d_permute(u8 val, unsigned int iter)
+{
+	if (iter == 0)
+		return val;
+	return d_permute(d_perm_table[val], iter - 1);
+}
+
+
+static u8 d_invert(u8 val)
+{
+	if (val > 0 && val < 16)
+		return 16 - val;
+	return val;
+}
+
+
+static char d_check_char(const char *str, size_t len)
+{
+	size_t i;
+	u8 val = 0;
+	u8 dtable[256];
+	unsigned int iter = 1;
+	int j;
+
+	os_memset(dtable, 0x80, 256);
+	for (i = 0; sae_pk_base32_table[i]; i++)
+		dtable[(u8) sae_pk_base32_table[i]] = i;
+
+	for (j = len - 1; j >= 0; j--) {
+		u8 c, p;
+
+		c = dtable[(u8) str[j]];
+		if (c == 0x80)
+			continue;
+		p = d_permute(c, iter);
+		iter++;
+		val = d_mult_table[val * 32 + p];
+	}
+
+	return sae_pk_base32_table[d_invert(val)];
+}
+
+
+bool sae_pk_valid_password(const char *pw)
+{
+	int pos;
+	size_t i, pw_len = os_strlen(pw);
+	u8 sec_1b;
+	u8 dtable[256];
+
+	os_memset(dtable, 0x80, 256);
+	for (i = 0; sae_pk_base32_table[i]; i++)
+		dtable[(u8) sae_pk_base32_table[i]] = i;
+
+	/* SAE-PK password has at least three four character components
+	 * separated by hyphens. */
+	if (pw_len < 14 || pw_len % 5 != 4) {
+		wpa_printf(MSG_DEBUG, "SAE-PK: Not a valid password (length)");
+		return false;
+	}
+
+	for (pos = 0; pw[pos]; pos++) {
+		if (pos && pos % 5 == 4) {
+			if (pw[pos] != '-') {
+				wpa_printf(MSG_DEBUG,
+					   "SAE-PK: Not a valid password (separator)");
+				return false;
+			}
+			continue;
+		}
+		if (dtable[(u8) pw[pos]] == 0x80) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE-PK: Not a valid password (character)");
+			return false;
+		}
+	}
+
+	/* Verify that the checksum character is valid */
+	if (pw[pw_len - 1] != d_check_char(pw, pw_len - 1)) {
+		wpa_printf(MSG_DEBUG,
+			   "SAE-PK: Not a valid password (checksum)");
+		return false;
+	}
+
+	/* Verify that Sec_1b bits match */
+	sec_1b = dtable[(u8) pw[0]] & BIT(4);
+	for (i = 5; i < pw_len; i += 5) {
+		if (sec_1b != (dtable[(u8) pw[i]] & BIT(4))) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE-PK: Not a valid password (Sec_1b)");
+			return false;
+		}
+	}
+	return true;
+}
+
+
+static char * add_char(const char *start, char *pos, u8 idx, size_t *bits)
+{
+	if (*bits == 0)
+		return pos;
+	if (*bits > 5)
+		*bits -= 5;
+	else
+		*bits = 0;
+
+	if ((pos - start) % 5 == 4)
+		*pos++ = '-';
+	*pos++ = sae_pk_base32_table[idx];
+	return pos;
+}
+
+
+/* Base32 encode a password and add hyper separators and checksum */
+char * sae_pk_base32_encode(const u8 *src, size_t len_bits)
+{
+	char *out, *pos;
+	size_t olen, extra_pad, i;
+	u64 block = 0;
+	u8 val;
+	size_t len = (len_bits + 7) / 8;
+	size_t left = len_bits;
+	int j;
+
+	if (len == 0 || len >= SIZE_MAX / 8)
+		return NULL;
+	olen = len * 8 / 5 + 1;
+	olen += olen / 4; /* hyphen separators */
+	pos = out = os_zalloc(olen + 2); /* include room for ChkSum and nul */
+	if (!out)
+		return NULL;
+
+	extra_pad = (5 - len % 5) % 5;
+	for (i = 0; i < len + extra_pad; i++) {
+		val = i < len ? src[i] : 0;
+		block <<= 8;
+		block |= val;
+		if (i % 5 == 4) {
+			for (j = 7; j >= 0; j--)
+				pos = add_char(out, pos,
+					       (block >> j * 5) & 0x1f, &left);
+			block = 0;
+		}
+	}
+
+	*pos = d_check_char(out, os_strlen(out));
+
+	return out;
+}
+
+
+u8 * sae_pk_base32_decode(const char *src, size_t len, size_t *out_len)
+{
+	u8 dtable[256], *out, *pos, tmp;
+	u64 block = 0;
+	size_t i, count, olen;
+	int pad = 0;
+	size_t extra_pad;
+
+	os_memset(dtable, 0x80, 256);
+	for (i = 0; sae_pk_base32_table[i]; i++)
+		dtable[(u8) sae_pk_base32_table[i]] = i;
+	dtable['='] = 0;
+
+	count = 0;
+	for (i = 0; i < len; i++) {
+		if (dtable[(u8) src[i]] != 0x80)
+			count++;
+	}
+
+	if (count == 0)
+		return NULL;
+	extra_pad = (8 - count % 8) % 8;
+
+	olen = (count + extra_pad) / 8 * 5;
+	pos = out = os_malloc(olen);
+	if (!out)
+		return NULL;
+
+	count = 0;
+	for (i = 0; i < len + extra_pad; i++) {
+		u8 val;
+
+		if (i >= len)
+			val = '=';
+		else
+			val = src[i];
+		tmp = dtable[val];
+		if (tmp == 0x80)
+			continue;
+
+		if (val == '=')
+			pad++;
+		block <<= 5;
+		block |= tmp;
+		count++;
+		if (count == 8) {
+			*pos++ = (block >> 32) & 0xff;
+			*pos++ = (block >> 24) & 0xff;
+			*pos++ = (block >> 16) & 0xff;
+			*pos++ = (block >> 8) & 0xff;
+			*pos++ = block & 0xff;
+			count = 0;
+			block = 0;
+			if (pad) {
+				/* Leave in all the available bits with zero
+				 * padding to full octets from right. */
+				pos -= pad * 5 / 8;
+				break;
+			}
+		}
+	}
+
+	*out_len = pos - out;
+	return out;
+}
+
+
+u32 sae_pk_get_be19(const u8 *buf)
+{
+	return (buf[0] << 11) | (buf[1] << 3) | (buf[2] >> 5);
+}
+
+
+/* shift left by two octets and three bits; fill in zeros from right;
+ * len must be at least three */
+void sae_pk_buf_shift_left_19(u8 *buf, size_t len)
+{
+	u8 *dst, *src, *end;
+
+	dst = buf;
+	src = buf + 2;
+	end = buf + len;
+
+	while (src + 1 < end) {
+		*dst++ = (src[0] << 3) | (src[1] >> 5);
+		src++;
+	}
+	*dst++ = *src << 3;
+	*dst++ = 0;
+	*dst++ = 0;
+}
+
+
+static void sae_pk_buf_shift_left_1(u8 *buf, size_t len)
+{
+	u8 *dst, *src, *end;
+
+	dst = buf;
+	src = buf;
+	end = buf + len;
+
+	while (src + 1 < end) {
+		*dst++ = (src[0] << 1) | (src[1] >> 7);
+		src++;
+	}
+	*dst++ = *src << 1;
+}
+
+
+int sae_pk_set_password(struct sae_data *sae, const char *password)
+{
+	struct sae_temporary_data *tmp = sae->tmp;
+	size_t len, pw_len;
+	u8 *pw, *pos;
+	int bits;
+	u32 val = 0, val19;
+	unsigned int val_bits = 0;
+
+	if (!tmp)
+		return -1;
+
+	os_memset(tmp->fingerprint, 0, sizeof(tmp->fingerprint));
+	tmp->fingerprint_bytes = tmp->fingerprint_bits = 0;
+
+	len = os_strlen(password);
+	if (len < 1 || !sae_pk_valid_password(password))
+		return -1;
+
+	pw = sae_pk_base32_decode(password, len, &pw_len);
+	if (!pw)
+		return -1;
+
+	tmp->sec = (pw[0] & BIT(7)) ? 3 : 5;
+	tmp->lambda = len - len / 5;
+	tmp->fingerprint_bits = 8 * tmp->sec + 19 * tmp->lambda / 4 - 5;
+	wpa_printf(MSG_DEBUG, "SAE-PK: Sec=%u Lambda=%zu fingerprint_bits=%zu",
+		   tmp->sec, tmp->lambda, tmp->fingerprint_bits);
+
+	/* Construct Fingerprint from PasswordBase by prefixing with Sec zero
+	 * octets and skipping the Sec_1b bits */
+	pos = &tmp->fingerprint[tmp->sec];
+	bits = tmp->fingerprint_bits - 8 * tmp->sec;
+	wpa_hexdump_key(MSG_DEBUG, "SAE-PK: PasswordBase", pw, pw_len);
+	while (bits > 0) {
+		if (val_bits < 8) {
+			sae_pk_buf_shift_left_1(pw, pw_len); /* Sec_1b */
+			val19 = sae_pk_get_be19(pw);
+			sae_pk_buf_shift_left_19(pw, pw_len);
+			val = (val << 19) | val19;
+			val_bits += 19;
+		}
+		if (val_bits >= 8) {
+			if (bits < 8)
+				break;
+			*pos++ = (val >> (val_bits - 8)) & 0xff;
+			val_bits -= 8;
+			bits -= 8;
+		}
+	}
+	if (bits > 0) {
+		val >>= val_bits - bits;
+		*pos++ = val << (8 - bits);
+	}
+	tmp->fingerprint_bytes = pos - tmp->fingerprint;
+	wpa_hexdump_key(MSG_DEBUG, "SAE-PK: Fingerprint",
+			tmp->fingerprint, tmp->fingerprint_bytes);
+	bin_clear_free(pw, pw_len);
+	return 0;
+}
+
+
+static size_t sae_group_2_hash_len(int group)
+{
+	switch (group) {
+	case 19:
+		return 32;
+	case 20:
+		return 48;
+	case 21:
+		return 64;
+	}
+
+	return 0;
+}
+
+
+void sae_deinit_pk(struct sae_pk *pk)
+{
+	if (pk) {
+		wpabuf_free(pk->m);
+		crypto_ec_key_deinit(pk->key);
+#ifdef CONFIG_TESTING_OPTIONS
+		crypto_ec_key_deinit(pk->sign_key_override);
+#endif /* CONFIG_TESTING_OPTIONS */
+		wpabuf_free(pk->pubkey);
+		os_free(pk);
+	}
+}
+
+
+struct sae_pk * sae_parse_pk(const char *val)
+{
+	struct sae_pk *pk;
+	const char *pos;
+#ifdef CONFIG_TESTING_OPTIONS
+	const char *pos2;
+#endif /* CONFIG_TESTING_OPTIONS */
+	size_t len;
+	unsigned char *der;
+	size_t der_len, b_len;
+
+	/* <m-as-hexdump>:<base64-encoded-DER-encoded-key> */
+
+	pos = os_strchr(val, ':');
+	if (!pos || (pos - val) & 0x01)
+		return NULL;
+	len = (pos - val) / 2;
+	if (len != SAE_PK_M_LEN) {
+		wpa_printf(MSG_INFO, "SAE: Unexpected Modifier M length %zu",
+			   len);
+		return NULL;
+	}
+
+	pk = os_zalloc(sizeof(*pk));
+	if (!pk)
+		return NULL;
+	pk->m = wpabuf_alloc(len);
+	if (!pk->m || hexstr2bin(val, wpabuf_put(pk->m, len), len)) {
+		wpa_printf(MSG_INFO, "SAE: Failed to parse m");
+		goto fail;
+	}
+
+	pos++;
+	b_len = os_strlen(pos);
+#ifdef CONFIG_TESTING_OPTIONS
+	pos2 = os_strchr(pos, ':');
+	if (pos2) {
+		b_len = pos2 - pos;
+		pos2++;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	der = base64_decode(pos, b_len, &der_len);
+	if (!der) {
+		wpa_printf(MSG_INFO, "SAE: Failed to base64 decode PK key");
+		goto fail;
+	}
+
+	pk->key = crypto_ec_key_parse_priv(der, der_len);
+	bin_clear_free(der, der_len);
+	if (!pk->key)
+		goto fail;
+	pk->group = crypto_ec_key_group(pk->key);
+	pk->pubkey = crypto_ec_key_get_subject_public_key(pk->key);
+	if (!pk->pubkey)
+		goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (pos2) {
+		der = base64_decode(pos2, os_strlen(pos2), &der_len);
+		if (!der) {
+			wpa_printf(MSG_INFO,
+				   "SAE: Failed to base64 decode PK key");
+			goto fail;
+		}
+
+		pk->sign_key_override = crypto_ec_key_parse_priv(der, der_len);
+		bin_clear_free(der, der_len);
+		if (!pk->sign_key_override)
+			goto fail;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	return pk;
+fail:
+	sae_deinit_pk(pk);
+	return NULL;
+}
+
+
+int sae_hash(size_t hash_len, const u8 *data, size_t len, u8 *hash)
+{
+	if (hash_len == 32)
+		return sha256_vector(1, &data, &len, hash);
+#ifdef CONFIG_SHA384
+	if (hash_len == 48)
+		return sha384_vector(1, &data, &len, hash);
+#endif /* CONFIG_SHA384 */
+#ifdef CONFIG_SHA512
+	if (hash_len == 64)
+		return sha512_vector(1, &data, &len, hash);
+#endif /* CONFIG_SHA512 */
+	return -1;
+}
+
+
+static int sae_pk_hash_sig_data(struct sae_data *sae, size_t hash_len,
+				bool ap, const u8 *m, size_t m_len,
+				const u8 *pubkey, size_t pubkey_len, u8 *hash)
+{
+	struct sae_temporary_data *tmp = sae->tmp;
+	struct wpabuf *sig_data;
+	u8 *pos;
+	int ret = -1;
+
+	/* Signed data for KeyAuth: eleAP || eleSTA || scaAP || scaSTA ||
+	 * M || K_AP || AP-BSSID || STA-MAC */
+	sig_data = wpabuf_alloc(tmp->prime_len * 6 + m_len + pubkey_len +
+				2 * ETH_ALEN);
+	if (!sig_data)
+		goto fail;
+	pos = wpabuf_put(sig_data, 2 * tmp->prime_len);
+	if (crypto_ec_point_to_bin(tmp->ec, ap ? tmp->own_commit_element_ecc :
+				   tmp->peer_commit_element_ecc,
+				   pos, pos + tmp->prime_len) < 0)
+		goto fail;
+	pos = wpabuf_put(sig_data, 2 * tmp->prime_len);
+	if (crypto_ec_point_to_bin(tmp->ec, ap ? tmp->peer_commit_element_ecc :
+				   tmp->own_commit_element_ecc,
+				   pos, pos + tmp->prime_len) < 0)
+		goto fail;
+	if (crypto_bignum_to_bin(ap ? tmp->own_commit_scalar :
+				 sae->peer_commit_scalar,
+				 wpabuf_put(sig_data, tmp->prime_len),
+				 tmp->prime_len, tmp->prime_len) < 0 ||
+	    crypto_bignum_to_bin(ap ? sae->peer_commit_scalar :
+				 tmp->own_commit_scalar,
+				 wpabuf_put(sig_data, tmp->prime_len),
+				 tmp->prime_len, tmp->prime_len) < 0)
+		goto fail;
+	wpabuf_put_data(sig_data, m, m_len);
+	wpabuf_put_data(sig_data, pubkey, pubkey_len);
+	wpabuf_put_data(sig_data, ap ? tmp->own_addr : tmp->peer_addr,
+			ETH_ALEN);
+	wpabuf_put_data(sig_data, ap ? tmp->peer_addr : tmp->own_addr,
+			ETH_ALEN);
+	wpa_hexdump_buf_key(MSG_DEBUG, "SAE-PK: Data to be signed for KeyAuth",
+			    sig_data);
+	if (sae_hash(hash_len, wpabuf_head(sig_data), wpabuf_len(sig_data),
+		     hash) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "SAE-PK: hash(data to be signed)",
+		    hash, hash_len);
+	ret = 0;
+fail:
+	wpabuf_free(sig_data);
+	return ret;
+}
+
+
+int sae_write_confirm_pk(struct sae_data *sae, struct wpabuf *buf)
+{
+	struct sae_temporary_data *tmp = sae->tmp;
+	struct wpabuf *sig = NULL;
+	size_t need;
+	int ret = -1;
+	u8 *encr_mod;
+	size_t encr_mod_len;
+	const struct sae_pk *pk;
+	u8 hash[SAE_MAX_HASH_LEN];
+	size_t hash_len;
+	struct crypto_ec_key *key;
+
+	if (!tmp)
+		return -1;
+
+	pk = tmp->ap_pk;
+	if (!sae->pk || !pk)
+		return 0;
+
+	key = pk->key;
+#ifdef CONFIG_TESTING_OPTIONS
+	if (tmp->omit_pk_elem)
+		return 0;
+	if (pk->sign_key_override) {
+		wpa_printf(MSG_INFO, "TESTING: Override SAE-PK signing key");
+		key = pk->sign_key_override;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (tmp->kek_len != 32 && tmp->kek_len != 48 && tmp->kek_len != 64) {
+		wpa_printf(MSG_INFO,
+			   "SAE-PK: No KEK available for writing confirm");
+		return -1;
+	}
+
+	if (!tmp->ec) {
+		/* Only ECC groups are supported for SAE-PK in the current
+		 * implementation. */
+		wpa_printf(MSG_INFO,
+			   "SAE-PK: SAE commit did not use an ECC group");
+		return -1;
+	}
+
+	hash_len = sae_group_2_hash_len(pk->group);
+	if (sae_pk_hash_sig_data(sae, hash_len, true, wpabuf_head(pk->m),
+				 wpabuf_len(pk->m), wpabuf_head(pk->pubkey),
+				 wpabuf_len(pk->pubkey), hash) < 0)
+		goto fail;
+	sig = crypto_ec_key_sign(key, hash, hash_len);
+	if (!sig)
+		goto fail;
+	wpa_hexdump_buf(MSG_DEBUG, "SAE-PK: KeyAuth = Sig_AP()", sig);
+
+	/* TODO: fragmentation if any of the elements needs it for a group
+	 * using sufficiently large primes (none of the currently supported
+	 * ones do) */
+
+	encr_mod_len = wpabuf_len(pk->m) + AES_BLOCK_SIZE;
+	need = 4 + wpabuf_len(pk->pubkey) + 3 + wpabuf_len(sig) +
+		6 + encr_mod_len;
+	if (wpabuf_tailroom(buf) < need) {
+		wpa_printf(MSG_INFO,
+			   "SAE-PK: No room in message buffer for SAE-PK elements (%zu < %zu)",
+			   wpabuf_tailroom(buf), need);
+		goto fail;
+	}
+
+	/* FILS Public Key element */
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+	wpabuf_put_u8(buf, 2 + wpabuf_len(pk->pubkey));
+	wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_PUBLIC_KEY);
+	wpabuf_put_u8(buf, 2); /* Key Type: ECDSA public key */
+	wpabuf_put_buf(buf, pk->pubkey);
+
+	/* FILS Key Confirmation element (KeyAuth) */
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+	wpabuf_put_u8(buf, 1 + wpabuf_len(sig));
+	wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_KEY_CONFIRM);
+	/* KeyAuth = Sig_AP(eleAP || eleSTA || scaAP || scaSTA || M || K_AP ||
+	 *                  AP-BSSID || STA-MAC) */
+	wpabuf_put_buf(buf, sig);
+
+	/* SAE-PK element */
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+	wpabuf_put_u8(buf, 4 + encr_mod_len);
+	wpabuf_put_be32(buf, SAE_PK_IE_VENDOR_TYPE);
+	/* EncryptedModifier = AES-SIV-Q(M); no AAD */
+	encr_mod = wpabuf_put(buf, encr_mod_len);
+	if (aes_siv_encrypt(tmp->kek, tmp->kek_len,
+			    wpabuf_head(pk->m), wpabuf_len(pk->m),
+			    0, NULL, NULL, encr_mod) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "SAE-PK: EncryptedModifier",
+		    encr_mod, encr_mod_len);
+
+	ret = 0;
+fail:
+	wpabuf_free(sig);
+	return ret;
+
+}
+
+
+static bool sae_pk_valid_fingerprint(struct sae_data *sae,
+				     const u8 *m, size_t m_len,
+				     const u8 *k_ap, size_t k_ap_len, int group)
+{
+	struct sae_temporary_data *tmp = sae->tmp;
+	u8 *hash_data, *pos;
+	size_t hash_len, hash_data_len;
+	u8 hash[SAE_MAX_HASH_LEN];
+	int res;
+
+	if (!tmp->fingerprint_bytes) {
+		wpa_printf(MSG_DEBUG,
+			   "SAE-PK: No PW available for K_AP fingerprint check");
+		return false;
+	}
+
+	/* Fingerprint = L(Hash(SSID || M || K_AP), 0, 8*Sec + 19*Lambda/4 - 5)
+	 */
+
+	hash_len = sae_group_2_hash_len(group);
+	hash_data_len = tmp->ssid_len + m_len + k_ap_len;
+	hash_data = os_malloc(hash_data_len);
+	if (!hash_data)
+		return false;
+	pos = hash_data;
+	os_memcpy(pos, tmp->ssid, tmp->ssid_len);
+	pos += tmp->ssid_len;
+	os_memcpy(pos, m, m_len);
+	pos += m_len;
+	os_memcpy(pos, k_ap, k_ap_len);
+
+	wpa_hexdump_key(MSG_DEBUG, "SAE-PK: SSID || M || K_AP",
+			hash_data, hash_data_len);
+	res = sae_hash(hash_len, hash_data, hash_data_len, hash);
+	bin_clear_free(hash_data, hash_data_len);
+	if (res < 0)
+		return false;
+	wpa_hexdump(MSG_DEBUG, "SAE-PK: Hash(SSID || M || K_AP)",
+		    hash, hash_len);
+
+	if (tmp->fingerprint_bits > hash_len * 8) {
+		wpa_printf(MSG_INFO,
+			   "SAE-PK: Not enough hash output bits for the fingerprint");
+		return false;
+	}
+	if (tmp->fingerprint_bits % 8) {
+		size_t extra;
+
+		/* Zero out the extra bits in the last octet */
+		extra = 8 - tmp->fingerprint_bits % 8;
+		pos = &hash[tmp->fingerprint_bits / 8];
+		*pos = (*pos >> extra) << extra;
+	}
+	wpa_hexdump(MSG_DEBUG, "SAE-PK: Fingerprint", hash,
+		    tmp->fingerprint_bytes);
+	res = os_memcmp_const(hash, tmp->fingerprint, tmp->fingerprint_bytes);
+	if (res) {
+		wpa_printf(MSG_DEBUG, "SAE-PK: K_AP fingerprint mismatch");
+	wpa_hexdump(MSG_DEBUG, "SAE-PK: Expected fingerprint",
+		    tmp->fingerprint, tmp->fingerprint_bytes);
+		return false;
+	}
+
+	wpa_printf(MSG_DEBUG, "SAE-PK: Valid K_AP fingerprint");
+	return true;
+}
+
+
+int sae_check_confirm_pk(struct sae_data *sae, const u8 *ies, size_t ies_len)
+{
+	struct sae_temporary_data *tmp = sae->tmp;
+	const u8 *k_ap;
+	u8 m[SAE_PK_M_LEN];
+	size_t k_ap_len;
+	struct crypto_ec_key *key;
+	int res;
+	u8 hash[SAE_MAX_HASH_LEN];
+	size_t hash_len;
+	int group;
+	struct ieee802_11_elems elems;
+
+	if (!tmp)
+		return -1;
+	if (!sae->pk || tmp->ap_pk)
+		return 0;
+
+	if (tmp->kek_len != 32 && tmp->kek_len != 48 && tmp->kek_len != 64) {
+		wpa_printf(MSG_INFO,
+			   "SAE-PK: No KEK available for checking confirm");
+		return -1;
+	}
+
+	if (!tmp->ec) {
+		/* Only ECC groups are supported for SAE-PK in the current
+		 * implementation. */
+		wpa_printf(MSG_INFO,
+			   "SAE-PK: SAE commit did not use an ECC group");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "SAE-PK: Received confirm IEs", ies, ies_len);
+	if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+		wpa_printf(MSG_INFO, "SAE-PK: Failed to parse confirm IEs");
+		return -1;
+	}
+	if (!elems.fils_pk || !elems.fils_key_confirm || !elems.sae_pk) {
+		wpa_printf(MSG_INFO,
+			   "SAE-PK: Not all mandatory IEs included in confirm");
+		return -1;
+	}
+
+	/* TODO: Fragment reassembly */
+
+	if (elems.sae_pk_len < SAE_PK_M_LEN + AES_BLOCK_SIZE) {
+		wpa_printf(MSG_INFO,
+			   "SAE-PK: No room for EncryptedModifier in SAE-PK element");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "SAE-PK: EncryptedModifier",
+		    elems.sae_pk, SAE_PK_M_LEN + AES_BLOCK_SIZE);
+
+	if (aes_siv_decrypt(tmp->kek, tmp->kek_len,
+			    elems.sae_pk, SAE_PK_M_LEN + AES_BLOCK_SIZE,
+			    0, NULL, NULL, m) < 0) {
+		wpa_printf(MSG_INFO,
+			   "SAE-PK: Failed to decrypt EncryptedModifier");
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "SAE-PK: Modifier M", m, SAE_PK_M_LEN);
+
+	if (elems.fils_pk[0] != 2) {
+		wpa_printf(MSG_INFO, "SAE-PK: Unsupported public key type %u",
+			   elems.fils_pk[0]);
+		return -1;
+	}
+	k_ap_len = elems.fils_pk_len - 1;
+	k_ap = elems.fils_pk + 1;
+	wpa_hexdump(MSG_DEBUG, "SAE-PK: Received K_AP", k_ap, k_ap_len);
+	/* TODO: Check against the public key, if one is stored in the network
+	 * profile */
+
+	key = crypto_ec_key_parse_pub(k_ap, k_ap_len);
+	if (!key) {
+		wpa_printf(MSG_INFO, "SAE-PK: Failed to parse K_AP");
+		return -1;
+	}
+
+	group = crypto_ec_key_group(key);
+	if (!sae_pk_valid_fingerprint(sae, m, SAE_PK_M_LEN, k_ap, k_ap_len,
+				      group)) {
+		crypto_ec_key_deinit(key);
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "SAE-PK: Received KeyAuth",
+		    elems.fils_key_confirm, elems.fils_key_confirm_len);
+
+	hash_len = sae_group_2_hash_len(group);
+	if (sae_pk_hash_sig_data(sae, hash_len, false, m, SAE_PK_M_LEN,
+				 k_ap, k_ap_len, hash) < 0) {
+		crypto_ec_key_deinit(key);
+		return -1;
+	}
+
+	res = crypto_ec_key_verify_signature(key, hash, hash_len,
+					     elems.fils_key_confirm,
+					     elems.fils_key_confirm_len);
+	crypto_ec_key_deinit(key);
+
+	if (res != 1) {
+		wpa_printf(MSG_INFO,
+			   "SAE-PK: Invalid or incorrect signature in KeyAuth");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "SAE-PK: Valid KeyAuth signature received");
+
+	/* TODO: Store validated public key into network profile */
+
+	return 0;
+}
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index 1e7498a..82a5a17 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -1633,7 +1633,8 @@
 	if (!use_sha384 && sha256_vector(2, addr, len, hash) < 0)
 		return -1;
 	os_memcpy(pmk_r0_name, hash, WPA_PMK_NAME_LEN);
-	os_memset(r0_key_data, 0, sizeof(r0_key_data));
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN);
+	forced_memzero(r0_key_data, sizeof(r0_key_data));
 	return 0;
 }
 
@@ -1670,6 +1671,7 @@
 	if (!use_sha384 && sha256_vector(4, addr, len, hash) < 0)
 		return -1;
 	os_memcpy(pmk_r1_name, hash, WPA_PMK_NAME_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
 	return 0;
 }
 
@@ -1839,7 +1841,7 @@
 	wpa_hexdump_key(MSG_DEBUG, "FT: TK", ptk->tk, ptk->tk_len);
 	wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
 
-	os_memset(tmp, 0, sizeof(tmp));
+	forced_memzero(tmp, sizeof(tmp));
 
 	return 0;
 }
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 354de28..acc2d6c 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -97,6 +97,10 @@
 	"CTRL-EVENT-SAE-UNKNOWN-PASSWORD-IDENTIFIER "
 /** Unprotected Beacon frame dropped */
 #define WPA_EVENT_UNPROT_BEACON "CTRL-EVENT-UNPROT-BEACON "
+/** Decision made to do a within-ESS roam */
+#define WPA_EVENT_DO_ROAM "CTRL-EVENT-DO-ROAM "
+/** Decision made to skip a within-ESS roam */
+#define WPA_EVENT_SKIP_ROAM "CTRL-EVENT-SKIP-ROAM "
 
 /** IP subnet status change notification
  *
@@ -122,6 +126,8 @@
 #define WPA_EVENT_FREQ_CONFLICT "CTRL-EVENT-FREQ-CONFLICT "
 /** Frequency ranges that the driver recommends to avoid */
 #define WPA_EVENT_AVOID_FREQ "CTRL-EVENT-AVOID-FREQ "
+/** Result of MSCS setup */
+#define WPA_EVENT_MSCS_RESULT "CTRL-EVENT-MSCS-RESULT "
 /** WPS overlap detected in PBC mode */
 #define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED "
 /** Available WPS AP with active PBC found in scan results */
@@ -178,7 +184,11 @@
 #define DPP_EVENT_CONFOBJ_PSK "DPP-CONFOBJ-PSK "
 #define DPP_EVENT_CONNECTOR "DPP-CONNECTOR "
 #define DPP_EVENT_C_SIGN_KEY "DPP-C-SIGN-KEY "
+#define DPP_EVENT_PP_KEY "DPP-PP-KEY "
 #define DPP_EVENT_NET_ACCESS_KEY "DPP-NET-ACCESS-KEY "
+#define DPP_EVENT_SERVER_NAME "DPP-SERVER-NAME "
+#define DPP_EVENT_CERTBAG "DPP-CERTBAG "
+#define DPP_EVENT_CACERT "DPP-CACERT "
 #define DPP_EVENT_MISSING_CONNECTOR "DPP-MISSING-CONNECTOR "
 #define DPP_EVENT_NETWORK_ID "DPP-NETWORK-ID "
 #define DPP_EVENT_CONFIGURATOR_ID "DPP-CONFIGURATOR-ID "
@@ -192,6 +202,8 @@
 #define DPP_EVENT_CHIRP_STOPPED "DPP-CHIRP-STOPPED "
 #define DPP_EVENT_MUD_URL "DPP-MUD-URL "
 #define DPP_EVENT_BAND_SUPPORT "DPP-BAND-SUPPORT "
+#define DPP_EVENT_CSR "DPP-CSR "
+#define DPP_EVENT_CHIRP_RX "DPP-CHIRP-RX "
 
 /* MESH events */
 #define MESH_GROUP_STARTED "MESH-GROUP-STARTED "
@@ -388,6 +400,10 @@
 /* Transition mode disabled indication - followed by bitmap */
 #define TRANSITION_DISABLE "TRANSITION-DISABLE "
 
+/* OCV validation failure; parameters: addr=<src addr>
+ * frame=<saqueryreq/saqueryresp> error=<error string> */
+#define OCV_FAILURE "OCV-FAILURE "
+
 #ifndef BIT
 #define BIT(x) (1U << (x))
 #endif