Cumulative patch from commit 3d6953288b592704484864f41791f8c67ff9aa5a

3d6953288 Extend RESEND_* test commands to allow forcing plaintext TX
4be5bc98a DPP: Update AES-SIV AD for PKEX frames
dc4d271c6 DPP: Update AES-SIV AD for DPP Authentication frames
6338c99ef FILS: Send updated connection parameters to drivers if needed
d2ba0d719 Move assoc param setting into a helper function
084131c85 FILS: Allow eap_peer_get_erp_info() to be called without config
b0a21e228 FILS: Update replay counter from roam info
693eafb15 nl80211: Update FILS roam info from vendor roam event
3c67e977d nl80211: Add support to send updated connection parameters
cddfda789 Add attributes to support roam+auth vendor event for FILS
c0fe5f125 Clear BSSID information in supplicant state machine on disconnection
006fb845b nl80211: Use NL80211_BSS_LAST_SEEN_BOOTTIME if available
a6ea66530 Additional consistentcy checks for PTK component lengths
6f234c1e2 Optional AP side workaround for key reinstallation attacks
daa409608 Allow last (Re)Association Request frame to be replayed for testing
751f5b293 Allow EAPOL-Key Request to be sent through control interface
bb06748f4 Make last received ANonce available through control interface
143b81bad Allow arbitrary key configuration for testing
d8afdb210 Allow EAPOL-Key messages 1/4 and 3/4 to be retransmitted for testing
6bc2f00f4 Allow group key handshake message 1/2 to be retransmitted for testing
16579769f Add testing functionality for resetting PN/IPN for configured keys
b488a1294 Clear PMK length and check for this when deriving PTK
00583ef11 Add debug prints on PMK configuration in WPA supplicant
a00e946c1 WPA: Extra defense against PTK reinstalls in 4-way handshake
a0bf1b68c Remove all PeerKey functionality
e76085117 FILS: Do not allow multiple (Re)Association Response frames
2f1357fb6 FILS: Accept another (Re)Association Request frame during an association
df9490620 Add MGMT_TX_STATUS_PROCESS command for testing purposes
c53eb9461 OWE: Remove forgotten developer debug prints
a34ca59e4 SAE: Allow SAE password to be configured separately (STA)
2377c1cae SAE: Allow SAE password to be configured separately (AP)
c5aeb4343 P2P: Do not mark DFS channel as invalid if DFS is offloaded to driver
58efbcbcd DPP: Fix static analyzer warnings in key generation and JWK construction
f51609022 P2P: Prefer 5/60 GHz band over 2.4 GHz during GO configuration
91cc34bf3 OWE: Allow set of enabled DH groups to be limited on AP
265bda344 OWE: Allow DH Parameters element to be overridden for testing purposes
8c19ea3f2 DPP: Add the crypto suite field to the frames
c77e2ff09 DPP: Remove C-sign-key expiry
6254045a5 DPP: Explicitly delete the PKEX secret element K upon generation of z
0e6709a4e DPP: Rename PKEX secret element from Z to K
657317179 DPP: Verify that PKEX Qi is not the point-at-infinity
a89138818 OWE: Transition mode information based on BSS ifname
109704657 OWE: Support station SME-in-driver case
5a78c3619 OWE: PMKSA caching in station mode
d90f10fa4 OWE: PMKSA caching in AP mode
8b5579e17 DPP: Fix EAPOL-Key Key MIC calculation
ec9f48377 OWE: Support DH groups 20 (NIST P-384) and 21 (NIST P-521) in station
7a12edd16 OWE: Support DH groups 20 (NIST P-384) and 21 (NIST P-521) in AP mode
6c4726189 OWE: Extend shared helper functions to support other DH curves
d8c8d8575 OWE: Include RSNE in (Re)Association Response frame
e8b964901 OWE: Transition mode support on station side
675112df1 OWE: Set PMK length properly on supplicant side
ea079153f OWE: Add AP support for transition mode
4a3746341 hostapd: Update HE capabilities and HE operation definition
63bc0ab0e P2P: Allow GO to advertise Interworking element
3567641eb Add TX/RX rate info and signal strength into STA output
fa4b605a0 WPS: Do not increment wildcard_uuid when pin is locked
e37cea308 OCE: Update default scan IEs when OCE is enabled/disabled
ee522d27c Vendor flags for 11ax channel property flags for use with external ACS
61a56c148 Add group_mgmt network parameter for PMF cipher selection
0ad5893a2 PAE: Validate input before pointer
fd35ed5bb AP: Remove unneeded check for 'added_unassociated'
d55b17460 FILS: Vendor attribute to disable driver FILS features
a7297ae5c Fix hostapd debug messages on wpa_pairwise and rsn_pairwise parsing
29c940e7a TDLS: Update the comments related to TPK derivation
3de1566db FILS: Check req_ies for NULL pointer in hostapd_notif_assoc()
1c9663cf6 OpenSSL: Force RSA 3072-bit DH prime size limit for Suite B
2ed70c758 OpenSSL: Add option to disable ECDHE with Suite B RSA
4eb8cfe06 OpenSSL: Force RSA 3072-bit key size limit for Suite B
6418400db Add hostapd tls_flags parameter
60ed2f24e Suite B: Add tls_suiteb=1 parameter for RSA 3k key case
5030d7d9f DPP: Allow raw hex PSK to be used for legacy configuration
039ab15fd DPP: Add DPP-CONFOBJ-PASS/PSK events for hostapd legacy configuration
9824de57a Fix EAPOL-Key version check for a corner case with Suite B AKM
3c7863f81 wpa_supplicant: Support dynamic update of wowlan_triggers
4cada9dcc FILS: Add DHss into FILS-Key-Data derivation when using FILS SK+PFS
41b819148 FILS: Update PMKID derivation rules for ERP key hierarchy establishment
303113398 mesh: Move writing of mesh_rssi_threshold inside CONFIG_MESH
2efc67207 Fix RSN pre-authentication regression with pre-connection scan results
73b3de01c macsec_linux: Exit early when missing macsec kernel module
7612e65b9 mka: Add error handling for secy_init_macsec() calls
2c66c7d11 wpa_supplicant: Check length when building ext_capability in assoc_cb
fdbfb63e4 nl80211: Fix bridge name print while removing interface from bridge
333517ac1 crypto: Fix undefined behavior in random number generator
84fccc724 Send Client-Error when AT_KDF attributes from the server are incorrect
446600c35 Add AT_KDF attributes to Synchronization-Failure in EAP-AKA'
155bf1108 PMKSA: Fix use-after-free in pmksa_cache_clone_entry()
cdf250149 dbus: Add new interface property to get mesh group
190f6f117 dbus: Add new interface property to get connected mesh peers
a9de99b1c dbus: Add MeshPeerDisconnected signal
9b0701fbf dbus: Add MeshPeerConnected signal
a39b040b4 dbus: Add MeshGroupRemoved signal
89e9cd25d dbus: Add MeshGroupStarted signal
49e6a5553 FILS: Add a space before MAC address to a HLP debug message
359166ed2 Remove the completely unused FT parameters in driver association data
3db2a82df Add SHA-384 routines to libcrypto.a
a0f19e9c7 SAE: Allow commit fields to be overridden for testing purposes (STA)
3648d8a18 SAE: Allow commit fields to be overridden for testing purposes
e75335384 SAE: Add testing code for reflection attack
e61fea6b4 SAE: Fix PMKSA caching behavior in AP mode
a6f238f21 DPP: Add base64 dependency in makefiles
c2d4f2eb5 DPP: Derive PMKID using SHA256() for all curves
64a0a75b5 nl80211: Fix auth_alg selection with FILS in the connect command
7475e80f1 FILS: Fix wpa_supplicant AP build without CONFIG_IEEE80211W
85fd8263a DPP: Use Transaction ID in Peer Discovery Request/Response frames
a28675da2 hs20-osu-client: Fix build with new OpenSSL and BoringSSL
cf39475b4 Introduce QCA_NL80211_VENDOR_SUBCMD_HANG
17385fba2 tests: JSON module tests for additional array parsing
d4488b9da JSON: Fix parsing of arrays of numbers, strings, literals
a4bf00787 DPP: Remove devices object from the connector
e77d13ef9 QCA vendor attribute to configure beacon miss penalize count for BTC
7bd88aaf3 QCA vendor attribute to configure beacon miss count
505554bbf QCA vendor attribute to enable/disable scan
ae048257c WPS: Interpret zero length ap_pin hostapd.conf parameter as "unset"
2bdbace63 Remove some obsolete information from hostapd README file
b0fc2ef3a hw_features: Fix check of supported 802.11ac channel width
b5bf84ba3 WNM: Differentiate between WNM for station and for AP in build
922dcf1b4 RRM: Remove duplicate frequencies from beacon report scan request
705e2909c RRM: Send response when Beacon report request is not supported/refused
3756acfd4 RRM: Send Radio Measurement response when beacon report scan fails
b3c148e9f RRM: Send reject/refuse response only to unicast measurement request
51143af7e wpa_cli: Fix global control interface for STA-FIRST/STA-NEXT
809c67502 DPP: Fix build with OpenSSL 1.1.0
89971d8b1 OpenSSL: Clear default_passwd_cb more thoroughly
f665c93e1 OpenSSL: Fix private key password handling with OpenSSL >= 1.1.0f
2b9891bd6 OpenSSL: Add build option to select default ciphers
65833d71a OCE: Add hostapd mode OCE capability indication if enabled
332aadb8a STA: Add OCE capability indication attribute
fb718f94d nl80211: Check if driver supports OCE specific features
46b15e470 Add vendor flags for OCE feature support indication
b377ec258 FILS: Fix issuing FILS connect to a non-FILS AP in driver-FILS case
9f44f7f3b Introduce a vendor attribute to represent the PNO/EPNO Request ID
881a92e8b FILS: Fix compilation with CONFIG_NO_WPA
1f2ae8cff EAP-TTLS: Fix a memory leak on error paths
83e003a91 EAP-TTLS: Fix possible memory leak in eap_ttls_phase2_request_mschap()
422570eec MBO: Fix possible memory leak in anqp_send_req()
96e595a9f EAP-LEAP: Fix possible memory leak in eap_leap_process_request()
23eead4d7 RRM: Filter scan results by parent TSF only if driver supports it
3f8e3a548 ap: Fix invalid HT40 channel pair fallback
6d3e24d3e ap: Fix return value in hostapd_drv_switch_channel()
d02e4c8ac P2P: Clear get_pref_freq_list_override on P2P Device
bfbc41eac DPP: Fix compilation without openssl
cc6088463 P2P: Allow auto GO on DFS channels if driver supports this
fe3e0bac1 FILS: Advertize FILS capability based on driver capability
5579c11c3 Fix a typo in vendor attribute documentation
8b5ddda5f FILS: Add HLP support with driver-based AP SME
31ec556ce FILS: Fix the IP header protocol field in HLP DHCP response
b3e567c89 FILS: ERP-based PMKSA cache addition on AP
bfe448331 FILS: Fix a frame name in a debug print
6d49aeb76 MBO: Whitespace cleanup
f2cdb41b8 OCE: Define OCE attributes and other related macros
267fc0dd3 Add wpa_supplicant ctrl iface support to scan for a specific BSSID
f522bb237 DPP: Add DPP_CONFIGURATOR_SIGN to generate own connector
dc7fc09cc DPP: Add control interface commands into hostapd_cli
a86fb43ca DPP: DPP_BOOTSTRAP_INFO for hostapd
484788b87 DPP: Share bootstrap type to string helper function
623f95685 DPP: Allow wpa_cli DPP_CONFIGURATOR_ADD without arguments
888502325 Add new key_mgmt values for wpa_supplicant STATUS command
3a5954ef9 Add mgmt_group_cipher to wpa_supplicant STATUS command
90f837b0b Update default wpa_group_rekey to once-per-day when using CCMP/GCMP
787615b38 DPP: Set PMKSA expiration based on peer connector
6b140f0fa DPP: Update hostapd configurator parameters to match wpa_supplicant
2605405aa DPP: Configurator in hostapd
efeada91a DPP: PKEX in hostapd
6095b4790 DPP: Check JWS protected header alg against C-sign-key curve
b65b22d60 DPP: Configurator parameters in responder role
31f03cb00 DPP: Update JWS algorithm strings for Brainpool curves
e0d3d3fce DPP: Rename Brainpool curve names for JSON
500ed7f00 DPP: PKEX bootstrapping
b9d47b484 DPP: Add helper functions for running hash operations
0c7cf1f50 DPP: Increase hostapd_cli buffer limits
44d6b272c DPP: Fix configuration item list
43fbb8db5 DPP: More debug for own connector configuration errors
f1f4fa797 DPP: Fix JWK debug prints
b04854cef nl80211/MBO: Set temporary disallowed BSSID list to driver
2a71673e2 ERP: Derive ERP key only after successful EAP authentication
528b65578 Add Set Wi-Fi Configuration vendor attribute to configure LRO
944f359e1 Introduce a vendor command to specify the active Type Of Service
b6ea76425 nl80211: Make KCK attribute optional in rekey data
35f064212 DPP: Allow passphrase to be set for Configurator
68cb6dcec DPP: Allow SSID to be set for Configurator
186f20489 JSON: Fix \u escaping
a82349347 DPP: Add an example python script for QR Code operations
6a7182a9c DPP: Add DPP_BOOTSTRAP_INFO command
8528994e2 DPP: Automatic network profile creation
da143f7fb FILS: Fix EVENT_ASSOC processing checks for driver-SME
3c0daa13d Make wpa_config_read_blob() easier for static analyzers
a0d5c56f8 DPP: Network Introduction protocol for wpa_supplicant
4ff89c2eb DPP: Network Introduction protocol for hostapd
650a70a72 DPP: Network Introduction protocol
56c754958 DPP: AP parameters for DPP AKM
b979caae5 DPP: Network profile parameters for DPP AKM
0c52953b0 DPP: Allow PMKSA cache entries to be added through hostapd ctrl_iface
567da5bbd DPP: Add new AKM
9c2b8204e DPP: Integration for hostapd
9beb2892d DPP: Add wpa_cli commands for DPP operations
461d39af4 DPP: Configuration exchange
30d27b048 DPP: Authentication exchange
be27e185b DPP: Bootstrap information management
d4d76d983 Fix offchannel TX done handling for sequence of TX frames
00b02149e nl80211: Register to receive DPP Public Action frames
4e19eb88a tests: Module tests for JSON parser
005be3daa Add JavaScript Object Notation (JSON) parser (RFC7159)
5b52e1adc tests: Update base64 OOM test cases to match implementation changes
0ffdc8b19 Add base64url encoding/decoding per RFC 4648
77f273c82 Extend SHA-384 and SHA-512 support to match SHA-256
2c9d92497 P2P: Debug print P2P_FIND rejection reason
618aa2290 P2P: Fix p2p_in_provisioning clearing in failure case
b5db6e5dc eap_proxy: Support multiple SIMs in get_imsi()
002087651 eap_proxy: Build realm from IMSI for proxy based EAP methods
5e0c20ff3 nl80211: Do not notify interface as re-enabled if initialization fails
8696e6170 eap_proxy: Add support for deriving ERP information
115d5e222 hostapd: Fix handling a 20/40 BSS Coexistence Management frame
2d18ab408 Add a config parameter to exclude DFS channels from ACS
3c2bd55f0 P2P: wpas_p2p_select_go_freq() to check for supported pref_freq
8d968351a Interworking: Add NULL checking for EAP name in phase2/autheap parameter
9ddba3a30 Rename vendor attribute DISABLE_OFFCHANNEL to RESTRICT_OFFCHANNEL
d506c35ef Set Wi-Fi Configuration attribute to restrict offchannel operations
4aa329298 ERP: Do not generate ERP keys when domain name is not specified
34ee12c55 Do not flush PMKSA on bssid_hint change
290834df6 nl80211: Fix race condition in detecting MAC change
04f667fcd DFS: Allow switch to DFS channel after radar detection in ETSI
aa56e36d6 driver: Make DFS domain information available to core
e8e430fe7 Vendor attributes to retain connection on a roam request failure
43a356b26 Provide option to configure BSSID hint for a network
33117656e Define a QCA vendor attribute to update the listen interval
85cff4b0d OpenSSL: Try SHA256 hash for OCSP certificate matching
d264c2e39 HTTP (curl): Try SHA256 hash for OCSP certificate matching
95818ec17 Fix compiler warning with CONFIG_IEEE80211R no-CONFIG_FILS build
613639454 Make CONFIG_MACSEC depend on IEEE8021X_EAPOL
31a856a12 mesh: Make NL80211_MESHCONF_RSSI_THRESHOLD configurable
1f3c49d41 Fix 160 MHz opclass channel to frequency conversion
9f4947466 dbus: Add method to disable channel switching with a TDLS peer
2a57b4b82 dbus: Add method to enable channel switching with a TDLS peer
193950541 dbus: Add AbortScan method to abort ongoing scan
8fed47e01 FILS: Derive FT key hierarchy on authenticator side for FILS+FT
7d440a3bc FILS: Derive FT key hierarchy on supplicant side for FILS+FT
215eaa748 FILS: Implement FILS-FT derivation
80ddf5d99 FILS: Fix Key-Auth derivation for SK+PFS for authenticator side
e6b623133 FILS: Fix Key-Auth derivation for SK+PFS for supplicant side
4d0a61c50 FILS: Debug print inputs to Key-Auth derivation
be1ece46f wpa_supplicant: Add GET_CAPABILITY for P2P redirection
853cfa873 Detect endianness when building for RTEMS
35bb8a9a5 Android: Define CONFIG_TESTING_OPTIONS if enabled in config
178553b70 MBO: Add support to set ignore assoc disallow to driver
3a46cf93d FT: Add support for wildcard R0KH/R1KH
eefe86301 FT RRB: Add msg replay and msg delay protection
245fc96e5 FT: New RRB message format
50bd8e0a9 FT: Replace inter-AP protocol with use of OUI Extended Ethertype
f2a04874c MBO: Fix possible NULL pointer dereference on candidate handling
01dd2b105 ERP: Silence static analyzer warning
d912953e3 atheros: Get rid of static analyzer warnings on 0-length memcpy
470f08b4f Enable CONFIG_WNM=y automatically for CONFIG_MBO=y builds
f54114825 Fix GAS server ifdef block use
8b49b530b Fix CONFIG_INTERWORKING=y build without CONFIG_HS20=y
0661163ef Do not blacklist the current AP on DISABLE_NETWORK
ec27b04e6 hostapd: Select a valid secondary channel if both enabled
da6a28ba6 FILS: Specify if FILS HLP was sent in connect
a38090b16 FILS: Add HLP to Connect IEs
1e6780bda Allocate dynamic memory for connect IEs
9f894823f PAE: Silence static analyzer warning about NULL pointer dereference
9a72bfe9a Add control interface command to enable/disable roaming
fa61bff6a FILS: Handle authentication/association in partial driver AP SME
5cee22ca4 FILS: Make handle_auth_fils() re-usable for driver-based AP SME
5e5f8c816 FILS: Move authentication response handling into a helper function
b8a3453ac FILS: Pass only IE area to handle_auth_fils()
9392859d7 FILS: Move AssocResp construction to a helper function
bd5993532 FILS: Move Key Confirm element validation to a helper function
087631b98 FILS: Move Session element validation to a helper function
cc20edc9f FILS: Add FILS auth_alg to driver-based AP SME association handling
957bff83c FILS: Add driver-AP SME callback to set TK after association
8acbf85fa FILS: Add FILS AEAD parameters for sta_auth() calls
f46c154c5 atheros: Add FILS AAD parameters in sta_auth() handler
6b128fb2a driver: Move sta_auth() arguments to a struct
d7cff1d87 atheros: Enable raw management frame receive for FILS builds
2b7a8ec47 atheros: Read driver FILS capability
d5444aac4 FILS: Add FILS Indication element into Beacon/Probe Response template
8befe8a99 Define a QCA attribute to specify the PCL policy for external ACS
183d3924c WPS: Add option for using random UUID
b44d1efd2 FILS: Fix key info in GTK rekey EAPOL-Key msg 2/2
04243740c FILS: Fix GTK rekey by accepting EAPOL-Key msg 1/2 with FILS AKM
bbe7969d6 FILS: Update cache identifier on association
f705f41b7 FILS: Update PMKSA cache with FILS shared key offload
01ef320f1 FILS: Update ERP next sequence number with driver offload
5538fc930 FILS: Track completion with FILS shared key authentication offload
8b0a6dba8 FILS: Connect request for offloaded FILS shared key authentication
79f3121bb FILS: Set cache identifier in current PMKSA entry for driver-SME case
15def72fa ERP: External control of ERP key information
42e69bda2 FILS: Add support for Cache Identifier in add/remove PMKSA
061a3d3d5 nl80211: Add support for FILS Cache Identifier in add/remove_pmkid()
6fbb54140 driver: Move add_pmkid() and remove_pmkid() arguments into a struct
ad295f3b8 nl80211: Add support for FILS shared key offload
199eb3a4e FILS: Add support to write FILS key_mgmt values in network blocks
16217e13d QCA vendor commands and attributes for spectral scan
5db997e34 FILS: Add FTE into FILS Authentication frame from AP when using FILS+FT
af3e362fa FILS: Add MDE into Authentication frame for FILS+FT
5aa08153a FT: Add selection of FT+FILS AKMs
c10e0ccc9 Hide *PMKSA_ADD parameters from debug log
2971da270 P2P: Do not use wait_time for SD Response TX for last fragmentation
c5fee1604 FT: Schedule wpa_ft_rrb_rx() through eloop in intra-process communication
469677367 Sync with mac80211-next.git include/uapi/linux/nl80211.h
775e986d5 hostapd: Fix crash on consecutive channel switch failures
3d5f0e916 wpa_supplicant: Avoid associating to temp disabled SSID in ap_scan=2
fbba28f8c P2P: Suppress warning on non-P2P config
127595887 QCA vendor command: Add TA max duration attribute for OCB configure
2a9ec7c69 Define attributes for QCA vendor OCB commands
6a4363f5f MBO: Fix reject reason codes
0119d4424 FILS: Fix wpa_supplicant compilation errors
4cc6574d0 FILS: Fix fils_cache_id check
a5269dc20 wpa_helpers: Ignore link-local IPv4 address while waiting for DHCP
e2f00bb5f xml: Add Value node in TNDS node conversion for empty value case
a34317b52 GAS: Handle no-ACK TX status for GAS request frames
5db86df6a macsec_linux: Fix NULL pointer dereference on error cases
e50df5d2a mka: Fix use-after-free when transmit secure channels are deleted
529d6ed72 mka: Fix use-after-free when receive secure channels are deleted
6c2056abe QCA vendor attributes to extend antenna diversity functionality
b4ae5f04d Add vendor attribute to config propagation delay's absolute value
7f5f4e46a Fix QCA_ATTR_NUD_STATS_IS_DAD value
2c0ac6d61 P2P: Run full P2P_FIND scan after pending scan completes
d3bb082a7 P2P: Continue scanning specified channel with P2P_FIND freq argument
31e130f82 FILS: Add FILS-SK-PFS capability into "GET_CAPABILITY fils" command
649835167 FILS: Check FILS Indication element against local network profile
76e20f4fa FILS: Add FILS SK auth PFS support in STA mode
1764559ee FILS: Add FILS SK auth PFS support in AP mode
cad291d67 FILS: Define authentication algorithm for FILS SK auth with PFS
611523849 OWE: Add CONFIG_OWE=y build option
07a5fe823 OWE: Use AKM 00-0F-AC:11 style parameters for EAPOL-Key frames
ef9627cbc Print the algorithms used for EAPOL-Key professing in log
ef2383859 Return success/failure result from sha384_prf()
0a6147991 OWE: Process Diffie-Hellman Parameter element in STA mode
09368515d OWE: Process Diffie-Hellman Parameter element in AP mode
f9561868e OWE: Add driver capability flag for OWE AKM
a1ea1b452 OWE: Define and parse OWE AKM selector
9c7aac738 OWE: Define and parse Diffie-Hellman Parameter element
e73244c24 tests: Extract-and-Expand HKDF (RFC 5869)
4ec833daf Extend hmac_sha256_kdf() to support HKDF-Expand() as defined in RFC 5869
ae1ec1aaf OpenSSL: Add wrapper functions for ECDH
b07ff9cb0 wpa_supplicant: Allow disabling HT in AP mode without HT overrides
2124a615e wpa_supplicant: Allow explicit wide channel configuration for AP mode
57ee04dc7 wpa_cli: Execute action file in case of WPS_EVENT_TIMEOUT
6252b981d wpa_cli: Execute action file in case of WPS_EVENT_ACTIVE
acdf50219 WPS: Notify about WPS PBC event in Enrollee mode
15e5ee0b7 wpa_supplicant: events: Don't bounce timeout reason through a buffer
e97d15b73 wpa_cli: Update wnm_bss_query auto complete message
15ab61eda WNM: Add option to configure candidates for BTM query candidate list
e044a9d1e common: Add candidate list parsing helper function
13bf18eda WNM: Use a dynamically allocated buffer for BTM query and response
34f285190 MBO: Parse MBO ANQP-element on STA
941caed98 MBO: Add MBO ANQP-element processing on AP
8ecf2231f ANQP: Extend ANQP_GET command to request without IEEE 802.11 elements
2316cb358 MBO: Add option to add MBO query list to ANQP query
7cbb5f1a4 DFS: Handle pre-CAC expired event
62c8c7f72 nl80211: Handle pre-CAC expired event from the driver
caaaee072 Sync with mac80211-next.git include/uapi/linux/nl80211.h
3dcd735c1 DFS: Handle CAC completion event from other radio
d0330d57f nl80211: Add option to delay start of schedule scan plans
b696f791a RRM: Fix wpas_rrm_send_msr_report() loop handling
891aa65b8 RRM: Use dynamically allocated buffer for beacon report
a1f11e34c Use os_memdup()
dbdda355d Introduce os_memdup()
af8bc24da MBO: Add support for transition reject reason code
3ab484928 nl80211: Driver command for checking BTM accept/reject
23cddd751 wpa_supplicant: Fix non_pref_chan example
b9fd3c244 tests: Add TEST_FAIL() to radius_msg_add_attr()
de01f254a RADIUS server: Fix error paths in new session creation
4c803dfcd ACS: Fix memory leak if interface is disabled during scan
29be2c090 ACS: Simplify code paths
fa07d2d46 tests: Add TEST_FAIL() checks in l2_packet
d4359923e Fix DHCP/NDISC snoop deinit followed by failing re-init
160dca078 Add QCA vendor command/attr for BRP antenna limit control
57d3c5913 Clear scan_res_handler on no-retry failure
e9518ae74 WFD: Add WFD R2 Subelements
21ac78279 QCA nl80211 vendor attribute for specific sub-20 MHz channel width
ff936bc75 Make the third octet of Country String configurable
511831983 trace: Look up start to cope with ASLR
206516e8c af_alg: Crypto wrappers for Linux kernel crypto (AF_ALG)
b41d3e0a7 crypto: Process des_encrypt() error returns in callers
5f0e165e8 crypto: Add return value to DES and AES encrypt/decrypt
dca4b503f MBO: Fix minimum length check on non_pref_chan configuration
5b9f46df0 hostapd: Get channel number from frequency based on other modes as well
4c8836f13 FILS: Fix fils_hlp.c build with older netinet/udp.h definitions
c4bb39707 Fix AES-SIV build dependencies
694a3a0d6 mesh: Fix CONFIG_MESH=y build without CONFIG_IEEE80211W=y
88a447556 Fix SELECT_NETWORK freq parameter
d02989f2e D-Bus: Notify mesh capability if driver supports it
57a2aacab Add option to disable broadcast deauth in hostapd on AP start/stop
21ed24f5a hostapd: Fix potential mesh-related change from impacting non-mesh cases
b7286c1b5 FILS: External management of PMKSA cache entry with Cache Identifier
869af3072 FILS: Use FILS Cache Identifier to extend PMKSA applicability
6aea02e57 SME: Clear portValid on starting authentication to fix FILS
ba9774bd7 FILS: Fix BSSID in reassociation case
7eace3787 FILS: Find PMKSA cache entries on AP based on FILS Cache Identifier

Bug: 68042382
Test: Device boots up and connects to wifi networks.
Test: Regression tests.

Change-Id: I6710d39e00c489288f8afe855868ad28aeba0100
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index d9c4b97..04060ab 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -155,6 +155,7 @@
 
 OBJS += src/eapol_auth/eapol_auth_sm.c
 
+
 ifndef CONFIG_NO_DUMP_STATE
 # define HOSTAPD_DUMP_STATE to include support for dumping internal state
 # through control interface commands (undefine it, if you want to save in
@@ -217,11 +218,6 @@
 CONFIG_L2_PACKET=y
 endif
 
-ifdef CONFIG_PEERKEY
-L_CFLAGS += -DCONFIG_PEERKEY
-OBJS += src/ap/peerkey_auth.c
-endif
-
 ifdef CONFIG_HS20
 NEED_AES_OMAC1=y
 CONFIG_PROXYARP=y
@@ -254,6 +250,15 @@
 NEED_SHA256=y
 NEED_AES_OMAC1=y
 NEED_AES_UNWRAP=y
+NEED_AES_SIV=y
+NEED_ETH_P_OUI=y
+NEED_SHA256=y
+NEED_HMAC_SHA256_KDF=y
+endif
+
+ifdef NEED_ETH_P_OUI
+L_CFLAGS += -DCONFIG_ETH_P_OUI
+OBJS += src/ap/eth_p_oui.c
 endif
 
 ifdef CONFIG_SAE
@@ -263,15 +268,30 @@
 NEED_DH_GROUPS=y
 endif
 
+ifdef CONFIG_OWE
+L_CFLAGS += -DCONFIG_OWE
+NEED_ECC=y
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_HMAC_SHA512_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+NEED_SHA512=y
+endif
+
 ifdef CONFIG_FILS
 L_CFLAGS += -DCONFIG_FILS
 OBJS += src/ap/fils_hlp.c
 NEED_SHA384=y
 NEED_AES_SIV=y
+ifdef CONFIG_FILS_SK_PFS
+L_CFLAGS += -DCONFIG_FILS_SK_PFS
+NEED_ECC=y
+endif
 endif
 
 ifdef CONFIG_WNM
-L_CFLAGS += -DCONFIG_WNM
+L_CFLAGS += -DCONFIG_WNM -DCONFIG_WNM_AP
 OBJS += src/ap/wnm_ap.c
 endif
 
@@ -515,6 +535,23 @@
 
 endif
 
+ifdef CONFIG_DPP
+L_CFLAGS += -DCONFIG_DPP
+OBJS += src/common/dpp.c
+OBJS += src/ap/dpp_hostapd.c
+OBJS += src/ap/gas_query_ap.c
+NEED_AES_SIV=y
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_HMAC_SHA512_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+NEED_SHA512=y
+NEED_JSON=y
+NEED_GAS=y
+NEED_BASE64=y
+endif
+
 ifdef CONFIG_EAP_IKEV2
 L_CFLAGS += -DEAP_SERVER_IKEV2
 OBJS += src/eap_server/eap_server_ikev2.c src/eap_server/ikev2.c
@@ -597,6 +634,10 @@
 NEED_TLS_PRF_SHA256=y
 LIBS += -lcrypto
 LIBS_h += -lcrypto
+ifndef CONFIG_TLS_DEFAULT_CIPHERS
+CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW"
+endif
+L_CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
 endif
 
 ifeq ($(CONFIG_TLS), gnutls)
@@ -731,6 +772,12 @@
 ifdef NEED_AES_EAX
 AESOBJS += src/crypto/aes-eax.c
 NEED_AES_CTR=y
+NEED_AES_OMAC1=y
+endif
+ifdef NEED_AES_SIV
+AESOBJS += src/crypto/aes-siv.c
+NEED_AES_CTR=y
+NEED_AES_OMAC1=y
 endif
 ifdef NEED_AES_CTR
 AESOBJS += src/crypto/aes-ctr.c
@@ -753,9 +800,6 @@
 AESOBJS += src/crypto/aes-cbc.c
 endif
 endif
-ifdef NEED_AES_SIV
-AESOBJS += src/crypto/aes-siv.c
-endif
 ifdef NEED_AES_DEC
 ifdef CONFIG_INTERNAL_AES
 AESOBJS += src/crypto/aes-internal-dec.c
@@ -839,6 +883,15 @@
 ifdef NEED_TLS_PRF_SHA256
 OBJS += src/crypto/sha256-tlsprf.c
 endif
+ifdef NEED_HMAC_SHA256_KDF
+OBJS += src/crypto/sha256-kdf.c
+endif
+ifdef NEED_HMAC_SHA384_KDF
+OBJS += src/crypto/sha384-kdf.c
+endif
+ifdef NEED_HMAC_SHA512_KDF
+OBJS += src/crypto/sha512-kdf.c
+endif
 endif
 ifdef NEED_SHA384
 L_CFLAGS += -DCONFIG_SHA384
@@ -847,6 +900,15 @@
 endif
 OBJS += src/crypto/sha384-prf.c
 endif
+ifdef NEED_SHA512
+L_CFLAGS += -DCONFIG_SHA512
+ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
+OBJS += src/crypto/sha512.c
+endif
+endif
+OBJS += src/crypto/sha512-prf.c
+endif
 
 ifdef CONFIG_INTERNAL_SHA384
 L_CFLAGS += -DCONFIG_INTERNAL_SHA384
@@ -903,6 +965,11 @@
 OBJS += src/utils/base64.c
 endif
 
+ifdef NEED_JSON
+OBJS += src/utils/json.c
+L_CFLAGS += -DCONFIG_JSON
+endif
+
 ifdef NEED_AP_MLME
 OBJS += src/ap/wmm.c
 OBJS += src/ap/ap_list.c
@@ -936,6 +1003,10 @@
 
 ifdef CONFIG_INTERWORKING
 L_CFLAGS += -DCONFIG_INTERWORKING
+NEED_GAS=y
+endif
+
+ifdef NEED_GAS
 OBJS += src/common/gas.c
 OBJS += src/ap/gas_serv.c
 endif
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 3160d0d..eb35672 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -258,11 +258,6 @@
 CONFIG_L2_PACKET=y
 endif
 
-ifdef CONFIG_PEERKEY
-CFLAGS += -DCONFIG_PEERKEY
-OBJS += ../src/ap/peerkey_auth.o
-endif
-
 ifdef CONFIG_HS20
 NEED_AES_OMAC1=y
 CONFIG_PROXYARP=y
@@ -295,6 +290,15 @@
 NEED_SHA256=y
 NEED_AES_OMAC1=y
 NEED_AES_UNWRAP=y
+NEED_AES_SIV=y
+NEED_ETH_P_OUI=y
+NEED_SHA256=y
+NEED_HMAC_SHA256_KDF=y
+endif
+
+ifdef NEED_ETH_P_OUI
+CFLAGS += -DCONFIG_ETH_P_OUI
+OBJS += ../src/ap/eth_p_oui.o
 endif
 
 ifdef CONFIG_SAE
@@ -305,15 +309,30 @@
 NEED_AP_MLME=y
 endif
 
+ifdef CONFIG_OWE
+CFLAGS += -DCONFIG_OWE
+NEED_ECC=y
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_HMAC_SHA512_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+NEED_SHA512=y
+endif
+
 ifdef CONFIG_FILS
 CFLAGS += -DCONFIG_FILS
 OBJS += ../src/ap/fils_hlp.o
 NEED_SHA384=y
 NEED_AES_SIV=y
+ifdef CONFIG_FILS_SK_PFS
+CFLAGS += -DCONFIG_FILS_SK_PFS
+NEED_ECC=y
+endif
 endif
 
 ifdef CONFIG_WNM
-CFLAGS += -DCONFIG_WNM
+CFLAGS += -DCONFIG_WNM -DCONFIG_WNM_AP
 OBJS += ../src/ap/wnm_ap.o
 endif
 
@@ -547,6 +566,23 @@
 
 endif
 
+ifdef CONFIG_DPP
+CFLAGS += -DCONFIG_DPP
+OBJS += ../src/common/dpp.o
+OBJS += ../src/ap/dpp_hostapd.o
+OBJS += ../src/ap/gas_query_ap.o
+NEED_AES_SIV=y
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_HMAC_SHA512_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+NEED_SHA512=y
+NEED_JSON=y
+NEED_GAS=y
+NEED_BASE64=y
+endif
+
 ifdef CONFIG_EAP_IKEV2
 CFLAGS += -DEAP_SERVER_IKEV2
 OBJS += ../src/eap_server/eap_server_ikev2.o ../src/eap_server/ikev2.o
@@ -633,6 +669,10 @@
 LIBS += -ldl
 LIBS_h += -ldl
 endif
+ifndef CONFIG_TLS_DEFAULT_CIPHERS
+CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW"
+endif
+CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
 endif
 
 ifeq ($(CONFIG_TLS), gnutls)
@@ -732,6 +772,47 @@
 endif
 endif
 
+ifeq ($(CONFIG_TLS), linux)
+OBJS += ../src/crypto/crypto_linux.o
+ifdef TLS_FUNCS
+OBJS += ../src/crypto/crypto_internal-rsa.o
+OBJS += ../src/crypto/tls_internal.o
+OBJS += ../src/tls/tlsv1_common.o
+OBJS += ../src/tls/tlsv1_record.o
+OBJS += ../src/tls/tlsv1_cred.o
+OBJS += ../src/tls/tlsv1_server.o
+OBJS += ../src/tls/tlsv1_server_write.o
+OBJS += ../src/tls/tlsv1_server_read.o
+OBJS += ../src/tls/asn1.o
+OBJS += ../src/tls/rsa.o
+OBJS += ../src/tls/x509v3.o
+OBJS += ../src/tls/pkcs1.o
+OBJS += ../src/tls/pkcs5.o
+OBJS += ../src/tls/pkcs8.o
+NEED_SHA256=y
+NEED_BASE64=y
+NEED_TLS_PRF=y
+ifdef CONFIG_TLSV12
+NEED_TLS_PRF_SHA256=y
+endif
+NEED_MODEXP=y
+NEED_CIPHER=y
+CFLAGS += -DCONFIG_TLS_INTERNAL
+CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
+endif
+ifdef NEED_MODEXP
+OBJS += ../src/crypto/crypto_internal-modexp.o
+OBJS += ../src/tls/bignum.o
+CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+CFLAGS += -DLTM_FAST
+endif
+CONFIG_INTERNAL_DH_GROUP5=y
+ifdef NEED_FIPS186_2_PRF
+OBJS += ../src/crypto/fips_prf_internal.o
+OBJS += ../src/crypto/sha1-internal.o
+endif
+endif
+
 ifeq ($(CONFIG_TLS), none)
 ifdef TLS_FUNCS
 OBJS += ../src/crypto/tls_none.o
@@ -767,6 +848,12 @@
 ifdef NEED_AES_EAX
 AESOBJS += ../src/crypto/aes-eax.o
 NEED_AES_CTR=y
+NEED_AES_OMAC1=y
+endif
+ifdef NEED_AES_SIV
+AESOBJS += ../src/crypto/aes-siv.o
+NEED_AES_CTR=y
+NEED_AES_OMAC1=y
 endif
 ifdef NEED_AES_CTR
 AESOBJS += ../src/crypto/aes-ctr.o
@@ -775,22 +862,25 @@
 AESOBJS += ../src/crypto/aes-encblock.o
 endif
 ifdef NEED_AES_OMAC1
+ifneq ($(CONFIG_TLS), linux)
 AESOBJS += ../src/crypto/aes-omac1.o
 endif
+endif
 ifdef NEED_AES_UNWRAP
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 NEED_AES_DEC=y
 AESOBJS += ../src/crypto/aes-unwrap.o
 endif
 endif
+endif
 ifdef NEED_AES_CBC
 NEED_AES_DEC=y
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 AESOBJS += ../src/crypto/aes-cbc.o
 endif
 endif
-ifdef NEED_AES_SIV
-AESOBJS += ../src/crypto/aes-siv.o
 endif
 ifdef NEED_AES_DEC
 ifdef CONFIG_INTERNAL_AES
@@ -803,8 +893,10 @@
 
 ifdef NEED_SHA1
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 SHA1OBJS += ../src/crypto/sha1.o
 endif
+endif
 SHA1OBJS += ../src/crypto/sha1-prf.o
 ifdef CONFIG_INTERNAL_SHA1
 SHA1OBJS += ../src/crypto/sha1-internal.o
@@ -828,8 +920,10 @@
 endif
 
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 OBJS += ../src/crypto/md5.o
 endif
+endif
 
 ifdef NEED_MD5
 ifdef CONFIG_INTERNAL_MD5
@@ -865,8 +959,10 @@
 ifdef NEED_SHA256
 CFLAGS += -DCONFIG_SHA256
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 OBJS += ../src/crypto/sha256.o
 endif
+endif
 OBJS += ../src/crypto/sha256-prf.o
 ifdef CONFIG_INTERNAL_SHA256
 OBJS += ../src/crypto/sha256-internal.o
@@ -877,14 +973,31 @@
 ifdef NEED_HMAC_SHA256_KDF
 OBJS += ../src/crypto/sha256-kdf.o
 endif
+ifdef NEED_HMAC_SHA384_KDF
+OBJS += ../src/crypto/sha384-kdf.o
+endif
+ifdef NEED_HMAC_SHA512_KDF
+OBJS += ../src/crypto/sha512-kdf.o
+endif
 endif
 ifdef NEED_SHA384
 CFLAGS += -DCONFIG_SHA384
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 OBJS += ../src/crypto/sha384.o
 endif
+endif
 OBJS += ../src/crypto/sha384-prf.o
 endif
+ifdef NEED_SHA512
+CFLAGS += -DCONFIG_SHA512
+ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
+OBJS += ../src/crypto/sha512.o
+endif
+endif
+OBJS += ../src/crypto/sha512-prf.o
+endif
 
 ifdef CONFIG_INTERNAL_SHA384
 CFLAGS += -DCONFIG_INTERNAL_SHA384
@@ -920,9 +1033,11 @@
 HOBJS += ../src/utils/eloop.o
 HOBJS += $(SHA1OBJS)
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 HOBJS += ../src/crypto/md5.o
 endif
 endif
+endif
 
 ifdef CONFIG_RADIUS_SERVER
 CFLAGS += -DRADIUS_SERVER
@@ -941,6 +1056,11 @@
 OBJS += ../src/utils/base64.o
 endif
 
+ifdef NEED_JSON
+OBJS += ../src/utils/json.o
+CFLAGS += -DCONFIG_JSON
+endif
+
 ifdef NEED_AP_MLME
 OBJS += ../src/ap/wmm.o
 OBJS += ../src/ap/ap_list.o
@@ -970,6 +1090,10 @@
 
 ifdef CONFIG_INTERWORKING
 CFLAGS += -DCONFIG_INTERWORKING
+NEED_GAS=y
+endif
+
+ifdef NEED_GAS
 OBJS += ../src/common/gas.o
 OBJS += ../src/ap/gas_serv.o
 endif
@@ -1121,6 +1245,9 @@
 HOBJS += ../src/crypto/aes-internal.o
 HOBJS += ../src/crypto/aes-internal-enc.o
 endif
+ifeq ($(CONFIG_TLS), linux)
+HOBJS += ../src/crypto/crypto_linux.o
+endif
 
 nt_password_hash: $(NOBJS)
 	$(Q)$(CC) $(LDFLAGS) -o nt_password_hash $(NOBJS) $(LIBS_n)
diff --git a/hostapd/README b/hostapd/README
index cb37c8e..298391b 100644
--- a/hostapd/README
+++ b/hostapd/README
@@ -70,7 +70,7 @@
 Current hardware/software requirements:
 - drivers:
 	Host AP driver for Prism2/2.5/3.
-	(http://hostap.epitest.fi/)
+	(http://w1.fi/hostap-driver.html)
 	Please note that station firmware version needs to be 1.7.0 or newer
 	to work in WPA mode.
 
@@ -81,8 +81,7 @@
 	Any wired Ethernet driver for wired IEEE 802.1X authentication
 	(experimental code)
 
-	FreeBSD -current (with some kernel mods that have not yet been
-	committed when hostapd v0.3.0 was released)
+	FreeBSD -current
 	BSD net80211 layer (e.g., Atheros driver)
 
 
@@ -186,23 +185,13 @@
 the Authentication Server. Other than this, the functionality is similar
 to the case with the co-located Authentication Server.
 
-Authentication Server and Supplicant
-------------------------------------
+Authentication Server
+---------------------
 
 Any RADIUS server supporting EAP should be usable as an IEEE 802.1X
 Authentication Server with hostapd Authenticator. FreeRADIUS
 (http://www.freeradius.org/) has been successfully tested with hostapd
-Authenticator and both Xsupplicant (http://www.open1x.org) and Windows
-XP Supplicants. EAP/TLS was used with Xsupplicant and
-EAP/MD5-Challenge with Windows XP.
-
-http://www.missl.cs.umd.edu/wireless/eaptls/ has useful information
-about using EAP/TLS with FreeRADIUS and Xsupplicant (just replace
-Cisco access point with Host AP driver, hostapd daemon, and a Prism2
-card ;-). http://www.freeradius.org/doc/EAP-MD5.html has information
-about using EAP/MD5 with FreeRADIUS, including instructions for WinXP
-configuration. http://www.denobula.com/EAPTLS.pdf has a HOWTO on
-EAP/TLS use with WinXP Supplicant.
+Authenticator.
 
 Automatic WEP key configuration
 -------------------------------
@@ -243,16 +232,15 @@
 of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked
 to address the flaws of the base standard and has in practice
 completed its work in May 2004. The IEEE 802.11i amendment to the IEEE
-802.11 standard was approved in June 2004 and this amendment is likely
-to be published in July 2004.
+802.11 standard was approved in June 2004 and this amendment was
+published in July 2004.
 
 Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version of the
 IEEE 802.11i work (draft 3.0) to define a subset of the security
 enhancements that can be implemented with existing wlan hardware. This
 is called Wi-Fi Protected Access<TM> (WPA). This has now become a
 mandatory component of interoperability testing and certification done
-by Wi-Fi Alliance. Wi-Fi provides information about WPA at its web
-site (http://www.wi-fi.org/OpenSection/protected_access.asp).
+by Wi-Fi Alliance.
 
 IEEE 802.11 standard defined wired equivalent privacy (WEP) algorithm
 for protecting wireless networks. WEP uses RC4 with 40-bit keys,
diff --git a/hostapd/android.config b/hostapd/android.config
index 1715cc8..b293828 100644
--- a/hostapd/android.config
+++ b/hostapd/android.config
@@ -44,9 +44,6 @@
 # WPA2/IEEE 802.11i RSN pre-authentication
 #CONFIG_RSN_PREAUTH=y
 
-# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
-#CONFIG_PEERKEY=y
-
 # IEEE 802.11w (management frame protection)
 # This version is an experimental implementation based on IEEE 802.11w/D1.0
 # draft and is subject to change since the standard has not yet been finalized.
@@ -202,3 +199,7 @@
 
 # Include internal line edit mode in hostapd_cli.
 CONFIG_WPA_CLI_EDIT=y
+
+# Opportunistic Wireless Encryption (OWE)
+# Experimental implementation of draft-harkins-owe-07.txt
+#CONFIG_OWE=y
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 9e95440..e2a470c 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -14,6 +14,8 @@
 #include "utils/common.h"
 #include "utils/uuid.h"
 #include "common/ieee802_11_defs.h"
+#include "crypto/sha256.h"
+#include "crypto/tls.h"
 #include "drivers/driver.h"
 #include "eap_server/eap.h"
 #include "radius/radius_client.h"
@@ -307,13 +309,12 @@
 				goto failed;
 			}
 
-			user->identity = os_malloc(pos - start);
+			user->identity = os_memdup(start, pos - start);
 			if (user->identity == NULL) {
 				wpa_printf(MSG_ERROR, "Failed to allocate "
 					   "memory for EAP identity");
 				goto failed;
 			}
-			os_memcpy(user->identity, start, pos - start);
 			user->identity_len = pos - start;
 
 			if (pos[0] == '"' && pos[1] == '*') {
@@ -431,13 +432,12 @@
 				goto failed;
 			}
 
-			user->password = os_malloc(pos - start);
+			user->password = os_memdup(start, pos - start);
 			if (user->password == NULL) {
 				wpa_printf(MSG_ERROR, "Failed to allocate "
 					   "memory for EAP password");
 				goto failed;
 			}
-			os_memcpy(user->password, start, pos - start);
 			user->password_len = pos - start;
 
 			pos++;
@@ -712,6 +712,14 @@
 			val |= WPA_KEY_MGMT_FT_FILS_SHA384;
 #endif /* CONFIG_IEEE80211R_AP */
 #endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+		else if (os_strcmp(start, "OWE") == 0)
+			val |= WPA_KEY_MGMT_OWE;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+		else if (os_strcmp(start, "DPP") == 0)
+			val |= WPA_KEY_MGMT_DPP;
+#endif /* CONFIG_DPP */
 		else {
 			wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
 				   line, start);
@@ -782,10 +790,9 @@
 		if (len < 2 || val[len - 1] != '"')
 			return -1;
 		len -= 2;
-		wep->key[keyidx] = os_malloc(len);
+		wep->key[keyidx] = os_memdup(val + 1, len);
 		if (wep->key[keyidx] == NULL)
 			return -1;
-		os_memcpy(wep->key[keyidx], val + 1, len);
 		wep->len[keyidx] = len;
 	} else {
 		if (len & 1)
@@ -999,6 +1006,26 @@
 
 
 #ifdef CONFIG_IEEE80211R_AP
+
+static int rkh_derive_key(const char *pos, u8 *key, size_t key_len)
+{
+	u8 oldkey[16];
+	int ret;
+
+	if (!hexstr2bin(pos, key, key_len))
+		return 0;
+
+	/* Try to use old short key for backwards compatibility */
+	if (hexstr2bin(pos, oldkey, sizeof(oldkey)))
+		return -1;
+
+	ret = hmac_sha256_kdf(oldkey, sizeof(oldkey), "FT OLDKEY", NULL, 0,
+			      key, key_len);
+	os_memset(oldkey, 0, sizeof(oldkey));
+	return ret;
+}
+
+
 static int add_r0kh(struct hostapd_bss_config *bss, char *value)
 {
 	struct ft_remote_r0kh *r0kh;
@@ -1032,7 +1059,7 @@
 	os_memcpy(r0kh->id, pos, r0kh->id_len);
 
 	pos = next;
-	if (hexstr2bin(pos, r0kh->key, sizeof(r0kh->key))) {
+	if (rkh_derive_key(pos, r0kh->key, sizeof(r0kh->key)) < 0) {
 		wpa_printf(MSG_ERROR, "Invalid R0KH key: '%s'", pos);
 		os_free(r0kh);
 		return -1;
@@ -1077,7 +1104,7 @@
 	}
 
 	pos = next;
-	if (hexstr2bin(pos, r1kh->key, sizeof(r1kh->key))) {
+	if (rkh_derive_key(pos, r1kh->key, sizeof(r1kh->key)) < 0) {
 		wpa_printf(MSG_ERROR, "Invalid R1KH key: '%s'", pos);
 		os_free(r1kh);
 		return -1;
@@ -1105,6 +1132,10 @@
 		conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
 		conf->secondary_channel = 1;
 	}
+	if (os_strstr(capab, "[HT40+]") && os_strstr(capab, "[HT40-]")) {
+		conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+		conf->ht40_plus_minus_allowed = 1;
+	}
 	if (os_strstr(capab, "[SMPS-STATIC]")) {
 		conf->ht_capab &= ~HT_CAP_INFO_SMPS_MASK;
 		conf->ht_capab |= HT_CAP_INFO_SMPS_STATIC;
@@ -2029,6 +2060,31 @@
 #endif /* CONFIG_FILS */
 
 
+#ifdef EAP_SERVER
+static unsigned int parse_tls_flags(const char *val)
+{
+	unsigned int flags = 0;
+
+	if (os_strstr(val, "[ALLOW-SIGN-RSA-MD5]"))
+		flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5;
+	if (os_strstr(val, "[DISABLE-TIME-CHECKS]"))
+		flags |= TLS_CONN_DISABLE_TIME_CHECKS;
+	if (os_strstr(val, "[DISABLE-TLSv1.0]"))
+		flags |= TLS_CONN_DISABLE_TLSv1_0;
+	if (os_strstr(val, "[DISABLE-TLSv1.1]"))
+		flags |= TLS_CONN_DISABLE_TLSv1_1;
+	if (os_strstr(val, "[DISABLE-TLSv1.2]"))
+		flags |= TLS_CONN_DISABLE_TLSv1_2;
+	if (os_strstr(val, "[SUITEB]"))
+		flags |= TLS_CONN_SUITEB;
+	if (os_strstr(val, "[SUITEB-NO-ECDH]"))
+		flags |= TLS_CONN_SUITEB_NO_ECDH | TLS_CONN_SUITEB;
+
+	return flags;
+}
+#endif /* EAP_SERVER */
+
+
 static int hostapd_config_fill(struct hostapd_config *conf,
 			       struct hostapd_bss_config *bss,
 			       const char *buf, char *pos, int line)
@@ -2138,8 +2194,8 @@
 		bss->skip_inactivity_poll = atoi(pos);
 	} else if (os_strcmp(buf, "country_code") == 0) {
 		os_memcpy(conf->country, pos, 2);
-		/* FIX: make this configurable */
-		conf->country[2] = ' ';
+	} else if (os_strcmp(buf, "country3") == 0) {
+		conf->country[2] = strtol(pos, NULL, 16);
 	} else if (os_strcmp(buf, "ieee80211d") == 0) {
 		conf->ieee80211d = atoi(pos);
 	} else if (os_strcmp(buf, "ieee80211h") == 0) {
@@ -2182,6 +2238,8 @@
 		bss->check_crl = atoi(pos);
 	} else if (os_strcmp(buf, "tls_session_lifetime") == 0) {
 		bss->tls_session_lifetime = atoi(pos);
+	} else if (os_strcmp(buf, "tls_flags") == 0) {
+		bss->tls_flags = parse_tls_flags(pos);
 	} else if (os_strcmp(buf, "ocsp_stapling_response") == 0) {
 		os_free(bss->ocsp_stapling_response);
 		bss->ocsp_stapling_response = os_strdup(pos);
@@ -2483,6 +2541,7 @@
 		bss->wpa = atoi(pos);
 	} else if (os_strcmp(buf, "wpa_group_rekey") == 0) {
 		bss->wpa_group_rekey = atoi(pos);
+		bss->wpa_group_rekey_set = 1;
 	} else if (os_strcmp(buf, "wpa_strict_rekey") == 0) {
 		bss->wpa_strict_rekey = atoi(pos);
 	} else if (os_strcmp(buf, "wpa_gmk_rekey") == 0) {
@@ -2511,6 +2570,8 @@
 			return 1;
 		}
 		bss->wpa_pairwise_update_count = (u32) val;
+	} else if (os_strcmp(buf, "wpa_disable_eapol_key_retries") == 0) {
+		bss->wpa_disable_eapol_key_retries = atoi(pos);
 	} else if (os_strcmp(buf, "wpa_passphrase") == 0) {
 		int len = os_strlen(pos);
 		if (len < 8 || len > 63) {
@@ -2569,7 +2630,7 @@
 		if (bss->wpa_pairwise &
 		    (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) {
 			wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'",
-				   bss->wpa_pairwise, pos);
+				   line, pos);
 			return 1;
 		}
 	} else if (os_strcmp(buf, "rsn_pairwise") == 0) {
@@ -2579,7 +2640,7 @@
 		if (bss->rsn_pairwise &
 		    (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) {
 			wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'",
-				   bss->rsn_pairwise, pos);
+				   line, pos);
 			return 1;
 		}
 #ifdef CONFIG_RSN_PREAUTH
@@ -2589,10 +2650,9 @@
 		os_free(bss->rsn_preauth_interfaces);
 		bss->rsn_preauth_interfaces = os_strdup(pos);
 #endif /* CONFIG_RSN_PREAUTH */
-#ifdef CONFIG_PEERKEY
 	} else if (os_strcmp(buf, "peerkey") == 0) {
-		bss->peerkey = atoi(pos);
-#endif /* CONFIG_PEERKEY */
+		wpa_printf(MSG_INFO,
+			   "Line %d: Obsolete peerkey parameter ignored", line);
 #ifdef CONFIG_IEEE80211R_AP
 	} else if (os_strcmp(buf, "mobility_domain") == 0) {
 		if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN ||
@@ -2615,6 +2675,14 @@
 		bss->r0_key_lifetime = atoi(pos);
 	} else if (os_strcmp(buf, "reassociation_deadline") == 0) {
 		bss->reassociation_deadline = atoi(pos);
+	} else if (os_strcmp(buf, "rkh_pos_timeout") == 0) {
+		bss->rkh_pos_timeout = atoi(pos);
+	} else if (os_strcmp(buf, "rkh_neg_timeout") == 0) {
+		bss->rkh_neg_timeout = atoi(pos);
+	} else if (os_strcmp(buf, "rkh_pull_timeout") == 0) {
+		bss->rkh_pull_timeout = atoi(pos);
+	} else if (os_strcmp(buf, "rkh_pull_retries") == 0) {
+		bss->rkh_pull_retries = atoi(pos);
 	} else if (os_strcmp(buf, "r0kh") == 0) {
 		if (add_r0kh(bss, pos) < 0) {
 			wpa_printf(MSG_DEBUG, "Line %d: Invalid r0kh '%s'",
@@ -2711,6 +2779,8 @@
 				   line, pos);
 			return 1;
 		}
+	} else if (os_strcmp(buf, "acs_exclude_dfs") == 0) {
+		conf->acs_exclude_dfs = atoi(pos);
 	} else if (os_strcmp(buf, "channel") == 0) {
 		if (os_strcmp(pos, "acs_survey") == 0) {
 #ifndef CONFIG_ACS
@@ -3106,7 +3176,10 @@
 		}
 	} else if (os_strcmp(buf, "ap_pin") == 0) {
 		os_free(bss->ap_pin);
-		bss->ap_pin = os_strdup(pos);
+		if (*pos == '\0')
+			bss->ap_pin = NULL;
+		else
+			bss->ap_pin = os_strdup(pos);
 	} else if (os_strcmp(buf, "skip_cred_build") == 0) {
 		bss->skip_cred_build = atoi(pos);
 	} else if (os_strcmp(buf, "extra_cred") == 0) {
@@ -3217,12 +3290,12 @@
 		bss->time_zone = os_strdup(pos);
 		if (bss->time_zone == NULL)
 			return 1;
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
 	} else if (os_strcmp(buf, "wnm_sleep_mode") == 0) {
 		bss->wnm_sleep_mode = atoi(pos);
 	} else if (os_strcmp(buf, "bss_transition") == 0) {
 		bss->bss_transition = atoi(pos);
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
 #ifdef CONFIG_INTERWORKING
 	} else if (os_strcmp(buf, "interworking") == 0) {
 		bss->interworking = atoi(pos);
@@ -3445,6 +3518,10 @@
 #ifdef CONFIG_MBO
 	} else if (os_strcmp(buf, "mbo") == 0) {
 		bss->mbo_enabled = atoi(pos);
+	} else if (os_strcmp(buf, "mbo_cell_data_conn_pref") == 0) {
+		bss->mbo_cell_data_conn_pref = atoi(pos);
+	} else if (os_strcmp(buf, "oce") == 0) {
+		bss->oce = atoi(pos);
 #endif /* CONFIG_MBO */
 #ifdef CONFIG_TESTING_OPTIONS
 #define PARSE_TEST_PROBABILITY(_val)				\
@@ -3513,6 +3590,14 @@
 
 		wpabuf_free(bss->own_ie_override);
 		bss->own_ie_override = tmp;
+	} else if (os_strcmp(buf, "sae_reflection_attack") == 0) {
+		bss->sae_reflection_attack = atoi(pos);
+	} else if (os_strcmp(buf, "sae_commit_override") == 0) {
+		wpabuf_free(bss->sae_commit_override);
+		bss->sae_commit_override = wpabuf_parse_bin(pos);
+	} else if (os_strcmp(buf, "sae_password") == 0) {
+		os_free(bss->sae_password);
+		bss->sae_password = os_strdup(pos);
 #endif /* CONFIG_TESTING_OPTIONS */
 	} else if (os_strcmp(buf, "vendor_elements") == 0) {
 		if (parse_wpabuf_hex(line, buf, &bss->vendor_elements, pos))
@@ -3655,6 +3740,8 @@
 	} else if (os_strcmp(buf, "fils_realm") == 0) {
 		if (parse_fils_realm(bss, pos) < 0)
 			return 1;
+	} else if (os_strcmp(buf, "fils_dh_group") == 0) {
+		bss->fils_dh_group = atoi(pos);
 	} else if (os_strcmp(buf, "dhcp_server") == 0) {
 		if (hostapd_parse_ip_addr(pos, &bss->dhcp_server)) {
 			wpa_printf(MSG_ERROR,
@@ -3673,6 +3760,53 @@
 #endif /* CONFIG_FILS */
 	} else if (os_strcmp(buf, "multicast_to_unicast") == 0) {
 		bss->multicast_to_unicast = atoi(pos);
+	} else if (os_strcmp(buf, "broadcast_deauth") == 0) {
+		bss->broadcast_deauth = atoi(pos);
+#ifdef CONFIG_DPP
+	} else if (os_strcmp(buf, "dpp_connector") == 0) {
+		os_free(bss->dpp_connector);
+		bss->dpp_connector = os_strdup(pos);
+	} else if (os_strcmp(buf, "dpp_netaccesskey") == 0) {
+		if (parse_wpabuf_hex(line, buf, &bss->dpp_netaccesskey, pos))
+			return 1;
+	} else if (os_strcmp(buf, "dpp_netaccesskey_expiry") == 0) {
+		bss->dpp_netaccesskey_expiry = strtol(pos, NULL, 0);
+	} else if (os_strcmp(buf, "dpp_csign") == 0) {
+		if (parse_wpabuf_hex(line, buf, &bss->dpp_csign, pos))
+			return 1;
+#endif /* CONFIG_DPP */
+#ifdef CONFIG_OWE
+	} else if (os_strcmp(buf, "owe_transition_bssid") == 0) {
+		if (hwaddr_aton(pos, bss->owe_transition_bssid)) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid owe_transition_bssid",
+				   line);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "owe_transition_ssid") == 0) {
+		size_t slen;
+		char *str = wpa_config_parse_string(pos, &slen);
+
+		if (!str || slen < 1 || slen > SSID_MAX_LEN) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'",
+				   line, pos);
+			os_free(str);
+			return 1;
+		}
+		os_memcpy(bss->owe_transition_ssid, str, slen);
+		bss->owe_transition_ssid_len = slen;
+		os_free(str);
+	} else if (os_strcmp(buf, "owe_transition_ifname") == 0) {
+		os_strlcpy(bss->owe_transition_ifname, pos,
+			   sizeof(bss->owe_transition_ifname));
+	} else if (os_strcmp(buf, "owe_groups") == 0) {
+		if (hostapd_parse_intlist(&bss->owe_groups, pos)) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid owe_groups value '%s'",
+				   line, pos);
+			return 1;
+		}
+#endif /* CONFIG_OWE */
 	} else {
 		wpa_printf(MSG_ERROR,
 			   "Line %d: unknown configuration item '%s'",
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index 62feaa4..b3ef8d3 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -50,6 +50,7 @@
 #include "ap/beacon.h"
 #include "ap/neighbor_db.h"
 #include "ap/rrm.h"
+#include "ap/dpp_hostapd.h"
 #include "wps/wps_defs.h"
 #include "wps/wps.h"
 #include "fst/fst_ctrl_iface.h"
@@ -763,7 +764,7 @@
 #endif /* CONFIG_INTERWORKING */
 
 
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
 
 static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd,
 						const char *cmd)
@@ -838,7 +839,7 @@
 	char *url = NULL;
 	int ret;
 	u8 nei_rep[1000];
-	u8 *nei_pos = nei_rep;
+	int nei_len;
 	u8 mbo[10];
 	size_t mbo_len = 0;
 
@@ -888,99 +889,10 @@
 		WPA_PUT_LE16(&bss_term_dur[10], atoi(end));
 	}
 
-
-	/*
-	 * BSS Transition Candidate List Entries - Neighbor Report elements
-	 * neighbor=<BSSID>,<BSSID Information>,<Operating Class>,
-	 * <Channel Number>,<PHY Type>[,<hexdump of Optional Subelements>]
-	 */
-	pos = cmd;
-	while (pos) {
-		u8 *nei_start;
-		long int val;
-		char *endptr, *tmp;
-
-		pos = os_strstr(pos, " neighbor=");
-		if (!pos)
-			break;
-		if (nei_pos + 15 > nei_rep + sizeof(nei_rep)) {
-			wpa_printf(MSG_DEBUG,
-				   "Not enough room for additional neighbor");
-			return -1;
-		}
-		pos += 10;
-
-		nei_start = nei_pos;
-		*nei_pos++ = WLAN_EID_NEIGHBOR_REPORT;
-		nei_pos++; /* length to be filled in */
-
-		if (hwaddr_aton(pos, nei_pos)) {
-			wpa_printf(MSG_DEBUG, "Invalid BSSID");
-			return -1;
-		}
-		nei_pos += ETH_ALEN;
-		pos += 17;
-		if (*pos != ',') {
-			wpa_printf(MSG_DEBUG, "Missing BSSID Information");
-			return -1;
-		}
-		pos++;
-
-		val = strtol(pos, &endptr, 0);
-		WPA_PUT_LE32(nei_pos, val);
-		nei_pos += 4;
-		if (*endptr != ',') {
-			wpa_printf(MSG_DEBUG, "Missing Operating Class");
-			return -1;
-		}
-		pos = endptr + 1;
-
-		*nei_pos++ = atoi(pos); /* Operating Class */
-		pos = os_strchr(pos, ',');
-		if (pos == NULL) {
-			wpa_printf(MSG_DEBUG, "Missing Channel Number");
-			return -1;
-		}
-		pos++;
-
-		*nei_pos++ = atoi(pos); /* Channel Number */
-		pos = os_strchr(pos, ',');
-		if (pos == NULL) {
-			wpa_printf(MSG_DEBUG, "Missing PHY Type");
-			return -1;
-		}
-		pos++;
-
-		*nei_pos++ = atoi(pos); /* PHY Type */
-		end = os_strchr(pos, ' ');
-		tmp = os_strchr(pos, ',');
-		if (tmp && (!end || tmp < end)) {
-			/* Optional Subelements (hexdump) */
-			size_t len;
-
-			pos = tmp + 1;
-			end = os_strchr(pos, ' ');
-			if (end)
-				len = end - pos;
-			else
-				len = os_strlen(pos);
-			if (nei_pos + len / 2 > nei_rep + sizeof(nei_rep)) {
-				wpa_printf(MSG_DEBUG,
-					   "Not enough room for neighbor subelements");
-				return -1;
-			}
-			if (len & 0x01 ||
-			    hexstr2bin(pos, nei_pos, len / 2) < 0) {
-				wpa_printf(MSG_DEBUG,
-					   "Invalid neighbor subelement info");
-				return -1;
-			}
-			nei_pos += len / 2;
-			pos = end;
-		}
-
-		nei_start[1] = nei_pos - nei_start - 2;
-	}
+	nei_len = ieee802_11_parse_candidate_list(cmd, nei_rep,
+						  sizeof(nei_rep));
+	if (nei_len < 0)
+		return -1;
 
 	pos = os_strstr(cmd, " url=");
 	if (pos) {
@@ -1067,9 +979,8 @@
 
 	ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer,
 				  valid_int, bss_term_dur, url,
-				  nei_pos > nei_rep ? nei_rep : NULL,
-				  nei_pos - nei_rep, mbo_len ? mbo : NULL,
-				  mbo_len);
+				  nei_len ? nei_rep : NULL, nei_len,
+				  mbo_len ? mbo : NULL, mbo_len);
 #ifdef CONFIG_MBO
 fail:
 #endif /* CONFIG_MBO */
@@ -1077,7 +988,7 @@
 	return ret;
 }
 
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
 
 
 static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd,
@@ -1190,6 +1101,24 @@
 	}
 #endif /* CONFIG_FILS */
 
+#ifdef CONFIG_OWE
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) {
+		ret = os_snprintf(pos, end - pos, "OWE ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_OWE */
+
+#ifdef CONFIG_DPP
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) {
+		ret = os_snprintf(pos, end - pos, "DPP ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_DPP */
+
 	if (pos > buf && *(pos - 1) == ' ') {
 		*(pos - 1) = '\0';
 		pos--;
@@ -1359,6 +1288,20 @@
 		hapd->ext_mgmt_frame_handling = atoi(value);
 	} else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) {
 		hapd->ext_eapol_frame_io = atoi(value);
+#ifdef CONFIG_DPP
+	} else if (os_strcasecmp(cmd, "dpp_config_obj_override") == 0) {
+		os_free(hapd->dpp_config_obj_override);
+		hapd->dpp_config_obj_override = os_strdup(value);
+	} else if (os_strcasecmp(cmd, "dpp_discovery_override") == 0) {
+		os_free(hapd->dpp_discovery_override);
+		hapd->dpp_discovery_override = os_strdup(value);
+	} else if (os_strcasecmp(cmd, "dpp_groups_override") == 0) {
+		os_free(hapd->dpp_groups_override);
+		hapd->dpp_groups_override = os_strdup(value);
+	} else if (os_strcasecmp(cmd,
+				 "dpp_ignore_netaccesskey_mismatch") == 0) {
+		hapd->dpp_ignore_netaccesskey_mismatch = atoi(value);
+#endif /* CONFIG_DPP */
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifdef CONFIG_MBO
 	} else if (os_strcasecmp(cmd, "mbo_assoc_disallow") == 0) {
@@ -1379,6 +1322,11 @@
 		 * disallowing station logic.
 		 */
 #endif /* CONFIG_MBO */
+#ifdef CONFIG_DPP
+	} else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) {
+		os_free(hapd->dpp_configurator_params);
+		hapd->dpp_configurator_params = os_strdup(value);
+#endif /* CONFIG_DPP */
 	} else {
 		struct sta_info *sta;
 		struct vlan_description vlan_id;
@@ -1561,6 +1509,67 @@
 }
 
 
+static int hostapd_ctrl_iface_mgmt_tx_status_process(struct hostapd_data *hapd,
+						     char *cmd)
+{
+	char *pos, *param;
+	size_t len;
+	u8 *buf;
+	int stype = 0, ok = 0;
+	union wpa_event_data event;
+
+	if (!hapd->ext_mgmt_frame_handling)
+		return -1;
+
+	/* stype=<val> ok=<0/1> buf=<frame hexdump> */
+
+	wpa_printf(MSG_DEBUG, "External MGMT TX status process: %s", cmd);
+
+	pos = cmd;
+	param = os_strstr(pos, "stype=");
+	if (param) {
+		param += 6;
+		stype = atoi(param);
+	}
+
+	param = os_strstr(pos, " ok=");
+	if (param) {
+		param += 4;
+		ok = atoi(param);
+	}
+
+	param = os_strstr(pos, " buf=");
+	if (!param)
+		return -1;
+	param += 5;
+
+	len = os_strlen(param);
+	if (len & 1)
+		return -1;
+	len /= 2;
+
+	buf = os_malloc(len);
+	if (!buf || hexstr2bin(param, buf, len) < 0) {
+		os_free(buf);
+		return -1;
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	event.tx_status.type = WLAN_FC_TYPE_MGMT;
+	event.tx_status.data = buf;
+	event.tx_status.data_len = len;
+	event.tx_status.stype = stype;
+	event.tx_status.ack = ok;
+	hapd->ext_mgmt_frame_handling = 0;
+	wpa_supplicant_event(hapd, EVENT_TX_STATUS, &event);
+	hapd->ext_mgmt_frame_handling = 1;
+
+	os_free(buf);
+
+	return 0;
+}
+
+
 static int hostapd_ctrl_iface_mgmt_rx_process(struct hostapd_data *hapd,
 					      char *cmd)
 {
@@ -1940,6 +1949,245 @@
 #endif /* WPA_TRACE_BFD */
 }
 
+
+static int hostapd_ctrl_reset_pn(struct hostapd_data *hapd, const char *cmd)
+{
+	struct sta_info *sta;
+	u8 addr[ETH_ALEN];
+	u8 zero[WPA_TK_MAX_LEN];
+
+	os_memset(zero, 0, sizeof(zero));
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+#ifdef CONFIG_IEEE80211W
+	if (is_broadcast_ether_addr(addr) && os_strstr(cmd, "IGTK")) {
+		if (hapd->last_igtk_alg == WPA_ALG_NONE)
+			return -1;
+
+		wpa_printf(MSG_INFO, "TESTING: Reset IPN for IGTK");
+
+		/* First, use a zero key to avoid any possible duplicate key
+		 * avoidance in the driver. */
+		if (hostapd_drv_set_key(hapd->conf->iface, hapd,
+					hapd->last_igtk_alg,
+					broadcast_ether_addr,
+					hapd->last_igtk_key_idx, 1, NULL, 0,
+					zero, hapd->last_igtk_len) < 0)
+			return -1;
+
+		/* Set the previously configured key to reset its TSC */
+		return hostapd_drv_set_key(hapd->conf->iface, hapd,
+					   hapd->last_igtk_alg,
+					   broadcast_ether_addr,
+					   hapd->last_igtk_key_idx, 1, NULL, 0,
+					   hapd->last_igtk,
+					   hapd->last_igtk_len);
+	}
+#endif /* CONFIG_IEEE80211W */
+
+	if (is_broadcast_ether_addr(addr)) {
+		if (hapd->last_gtk_alg == WPA_ALG_NONE)
+			return -1;
+
+		wpa_printf(MSG_INFO, "TESTING: Reset PN for GTK");
+
+		/* First, use a zero key to avoid any possible duplicate key
+		 * avoidance in the driver. */
+		if (hostapd_drv_set_key(hapd->conf->iface, hapd,
+					hapd->last_gtk_alg,
+					broadcast_ether_addr,
+					hapd->last_gtk_key_idx, 1, NULL, 0,
+					zero, hapd->last_gtk_len) < 0)
+			return -1;
+
+		/* Set the previously configured key to reset its TSC */
+		return hostapd_drv_set_key(hapd->conf->iface, hapd,
+					   hapd->last_gtk_alg,
+					   broadcast_ether_addr,
+					   hapd->last_gtk_key_idx, 1, NULL, 0,
+					   hapd->last_gtk, hapd->last_gtk_len);
+	}
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta)
+		return -1;
+
+	if (sta->last_tk_alg == WPA_ALG_NONE)
+		return -1;
+
+	wpa_printf(MSG_INFO, "TESTING: Reset PN for " MACSTR,
+		   MAC2STR(sta->addr));
+
+	/* First, use a zero key to avoid any possible duplicate key avoidance
+	 * in the driver. */
+	if (hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg,
+				sta->addr, sta->last_tk_key_idx, 1, NULL, 0,
+				zero, sta->last_tk_len) < 0)
+		return -1;
+
+	/* Set the previously configured key to reset its TSC/RSC */
+	return hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg,
+				   sta->addr, sta->last_tk_key_idx, 1, NULL, 0,
+				   sta->last_tk, sta->last_tk_len);
+}
+
+
+static int hostapd_ctrl_set_key(struct hostapd_data *hapd, const char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	const char *pos = cmd;
+	enum wpa_alg alg;
+	int idx, set_tx;
+	u8 seq[6], key[WPA_TK_MAX_LEN];
+	size_t key_len;
+
+	/* parameters: alg addr idx set_tx seq key */
+
+	alg = atoi(pos);
+	pos = os_strchr(pos, ' ');
+	if (!pos)
+		return -1;
+	pos++;
+	if (hwaddr_aton(pos, addr))
+		return -1;
+	pos += 17;
+	if (*pos != ' ')
+		return -1;
+	pos++;
+	idx = atoi(pos);
+	pos = os_strchr(pos, ' ');
+	if (!pos)
+		return -1;
+	pos++;
+	set_tx = atoi(pos);
+	pos = os_strchr(pos, ' ');
+	if (!pos)
+		return -1;
+	pos++;
+	if (hexstr2bin(pos, seq, sizeof(6)) < 0)
+		return -1;
+	pos += 2 * 6;
+	if (*pos != ' ')
+		return -1;
+	pos++;
+	key_len = os_strlen(pos) / 2;
+	if (hexstr2bin(pos, key, key_len) < 0)
+		return -1;
+
+	wpa_printf(MSG_INFO, "TESTING: Set key");
+	return hostapd_drv_set_key(hapd->conf->iface, hapd, alg, addr, idx,
+				   set_tx, seq, 6, key, key_len);
+}
+
+
+static void restore_tk(void *ctx1, void *ctx2)
+{
+	struct hostapd_data *hapd = ctx1;
+	struct sta_info *sta = ctx2;
+
+	wpa_printf(MSG_INFO, "TESTING: Restore TK for " MACSTR,
+		   MAC2STR(sta->addr));
+	/* This does not really restore the TSC properly, so this will result
+	 * in replay protection issues for now since there is no clean way of
+	 * preventing encryption of a single EAPOL frame. */
+	hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg,
+			    sta->addr, sta->last_tk_key_idx, 1, NULL, 0,
+			    sta->last_tk, sta->last_tk_len);
+}
+
+
+static int hostapd_ctrl_resend_m1(struct hostapd_data *hapd, const char *cmd)
+{
+	struct sta_info *sta;
+	u8 addr[ETH_ALEN];
+	int plain = os_strstr(cmd, "plaintext") != NULL;
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta || !sta->wpa_sm)
+		return -1;
+
+	if (plain && sta->last_tk_alg == WPA_ALG_NONE)
+		plain = 0; /* no need for special processing */
+	if (plain) {
+		wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR,
+			   MAC2STR(sta->addr));
+		hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE,
+				    sta->addr, sta->last_tk_key_idx, 0, NULL, 0,
+				    NULL, 0);
+	}
+
+	wpa_printf(MSG_INFO, "TESTING: Send M1 to " MACSTR, MAC2STR(sta->addr));
+	return wpa_auth_resend_m1(sta->wpa_sm,
+				  os_strstr(cmd, "change-anonce") != NULL,
+				  plain ? restore_tk : NULL, hapd, sta);
+}
+
+
+static int hostapd_ctrl_resend_m3(struct hostapd_data *hapd, const char *cmd)
+{
+	struct sta_info *sta;
+	u8 addr[ETH_ALEN];
+	int plain = os_strstr(cmd, "plaintext") != NULL;
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta || !sta->wpa_sm)
+		return -1;
+
+	if (plain && sta->last_tk_alg == WPA_ALG_NONE)
+		plain = 0; /* no need for special processing */
+	if (plain) {
+		wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR,
+			   MAC2STR(sta->addr));
+		hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE,
+				    sta->addr, sta->last_tk_key_idx, 0, NULL, 0,
+				    NULL, 0);
+	}
+
+	wpa_printf(MSG_INFO, "TESTING: Send M3 to " MACSTR, MAC2STR(sta->addr));
+	return wpa_auth_resend_m3(sta->wpa_sm,
+				  plain ? restore_tk : NULL, hapd, sta);
+}
+
+
+static int hostapd_ctrl_resend_group_m1(struct hostapd_data *hapd,
+					const char *cmd)
+{
+	struct sta_info *sta;
+	u8 addr[ETH_ALEN];
+	int plain = os_strstr(cmd, "plaintext") != NULL;
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta || !sta->wpa_sm)
+		return -1;
+
+	if (plain && sta->last_tk_alg == WPA_ALG_NONE)
+		plain = 0; /* no need for special processing */
+	if (plain) {
+		wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR,
+			   MAC2STR(sta->addr));
+		hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE,
+				    sta->addr, sta->last_tk_key_idx, 0, NULL, 0,
+				    NULL, 0);
+	}
+
+	wpa_printf(MSG_INFO,
+		   "TESTING: Send group M1 for the same GTK and zero RSC to "
+		   MACSTR, MAC2STR(sta->addr));
+	return wpa_auth_resend_group_m1(sta->wpa_sm,
+					plain ? restore_tk : NULL, hapd, sta);
+}
+
 #endif /* CONFIG_TESTING_OPTIONS */
 
 
@@ -2587,7 +2835,7 @@
 		if (hostapd_ctrl_iface_hs20_deauth_req(hapd, buf + 16))
 			reply_len = -1;
 #endif /* CONFIG_HS20 */
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
 	} else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) {
 		if (hostapd_ctrl_iface_disassoc_imminent(hapd, buf + 18))
 			reply_len = -1;
@@ -2597,7 +2845,7 @@
 	} else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) {
 		if (hostapd_ctrl_iface_bss_tm_req(hapd, buf + 11))
 			reply_len = -1;
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
 	} else if (os_strcmp(buf, "GET_CONFIG") == 0) {
 		reply_len = hostapd_ctrl_iface_get_config(hapd, reply,
 							  reply_size);
@@ -2626,6 +2874,10 @@
 	} else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) {
 		if (hostapd_ctrl_iface_mgmt_tx(hapd, buf + 8))
 			reply_len = -1;
+	} else if (os_strncmp(buf, "MGMT_TX_STATUS_PROCESS ", 23) == 0) {
+		if (hostapd_ctrl_iface_mgmt_tx_status_process(hapd,
+							      buf + 23) < 0)
+			reply_len = -1;
 	} else if (os_strncmp(buf, "MGMT_RX_PROCESS ", 16) == 0) {
 		if (hostapd_ctrl_iface_mgmt_rx_process(hapd, buf + 16) < 0)
 			reply_len = -1;
@@ -2652,6 +2904,21 @@
 			reply_len = -1;
 	} else if (os_strcmp(buf, "GET_FAIL") == 0) {
 		reply_len = hostapd_ctrl_get_fail(hapd, reply, reply_size);
+	} else if (os_strncmp(buf, "RESET_PN ", 9) == 0) {
+		if (hostapd_ctrl_reset_pn(hapd, buf + 9) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "SET_KEY ", 8) == 0) {
+		if (hostapd_ctrl_set_key(hapd, buf + 8) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "RESEND_M1 ", 10) == 0) {
+		if (hostapd_ctrl_resend_m1(hapd, buf + 10) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "RESEND_M3 ", 10) == 0) {
+		if (hostapd_ctrl_resend_m3(hapd, buf + 10) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "RESEND_GROUP_M1 ", 16) == 0) {
+		if (hostapd_ctrl_resend_group_m1(hapd, buf + 16) < 0)
+			reply_len = -1;
 #endif /* CONFIG_TESTING_OPTIONS */
 	} else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
 		if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12))
@@ -2683,6 +2950,9 @@
 							  reply_size);
 	} else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) {
 		hostapd_ctrl_iface_pmksa_flush(hapd);
+	} else if (os_strncmp(buf, "PMKSA_ADD ", 10) == 0) {
+		if (hostapd_ctrl_iface_pmksa_add(hapd, buf + 10) < 0)
+			reply_len = -1;
 	} else if (os_strncmp(buf, "SET_NEIGHBOR ", 13) == 0) {
 		if (hostapd_ctrl_iface_set_neighbor(hapd, buf + 13))
 			reply_len = -1;
@@ -2703,6 +2973,70 @@
 						      reply_size);
 	} else if (os_strcmp(buf, "TERMINATE") == 0) {
 		eloop_terminate();
+#ifdef CONFIG_DPP
+	} else if (os_strncmp(buf, "DPP_QR_CODE ", 12) == 0) {
+		res = hostapd_dpp_qr_code(hapd, buf + 12);
+		if (res < 0) {
+			reply_len = -1;
+		} else {
+			reply_len = os_snprintf(reply, reply_size, "%d", res);
+			if (os_snprintf_error(reply_size, reply_len))
+				reply_len = -1;
+		}
+	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_GEN ", 18) == 0) {
+		res = hostapd_dpp_bootstrap_gen(hapd, buf + 18);
+		if (res < 0) {
+			reply_len = -1;
+		} else {
+			reply_len = os_snprintf(reply, reply_size, "%d", res);
+			if (os_snprintf_error(reply_size, reply_len))
+				reply_len = -1;
+		}
+	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_REMOVE ", 21) == 0) {
+		if (hostapd_dpp_bootstrap_remove(hapd, buf + 21) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_GET_URI ", 22) == 0) {
+		const char *uri;
+
+		uri = hostapd_dpp_bootstrap_get_uri(hapd, atoi(buf + 22));
+		if (!uri) {
+			reply_len = -1;
+		} else {
+			reply_len = os_snprintf(reply, reply_size, "%s", uri);
+			if (os_snprintf_error(reply_size, reply_len))
+				reply_len = -1;
+		}
+	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_INFO ", 19) == 0) {
+		reply_len = hostapd_dpp_bootstrap_info(hapd, atoi(buf + 19),
+						       reply, reply_size);
+	} else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) {
+		if (hostapd_dpp_auth_init(hapd, buf + 13) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_ADD", 20) == 0) {
+		res = hostapd_dpp_configurator_add(hapd, buf + 20);
+		if (res < 0) {
+			reply_len = -1;
+		} else {
+			reply_len = os_snprintf(reply, reply_size, "%d", res);
+			if (os_snprintf_error(reply_size, reply_len))
+				reply_len = -1;
+		}
+	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_REMOVE ", 24) == 0) {
+		if (hostapd_dpp_configurator_remove(hapd, buf + 24) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) {
+		res = hostapd_dpp_pkex_add(hapd, buf + 12);
+		if (res < 0) {
+			reply_len = -1;
+		} else {
+			reply_len = os_snprintf(reply, reply_size, "%d", res);
+			if (os_snprintf_error(reply_size, reply_len))
+				reply_len = -1;
+		}
+	} else if (os_strncmp(buf, "DPP_PKEX_REMOVE ", 16) == 0) {
+		if (hostapd_dpp_pkex_remove(hapd, buf + 16) < 0)
+			reply_len = -1;
+#endif /* CONFIG_DPP */
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
diff --git a/hostapd/defconfig b/hostapd/defconfig
index 9ade580..c67c662 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -50,9 +50,6 @@
 # WPA2/IEEE 802.11i RSN pre-authentication
 CONFIG_RSN_PREAUTH=y
 
-# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
-CONFIG_PEERKEY=y
-
 # IEEE 802.11w (management frame protection)
 CONFIG_IEEE80211W=y
 
@@ -265,6 +262,7 @@
 # openssl = OpenSSL (default)
 # gnutls = GnuTLS
 # internal = Internal TLSv1 implementation (experimental)
+# linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
 # none = Empty template
 #CONFIG_TLS=openssl
 
@@ -277,6 +275,10 @@
 # can be enabled to enable use of stronger crypto algorithms.
 #CONFIG_TLSV12=y
 
+# Select which ciphers to use by default with OpenSSL if the user does not
+# specify them.
+#CONFIG_TLS_DEFAULT_CIPHERS="DEFAULT:!EXP:!LOW"
+
 # If CONFIG_TLS=internal is used, additional library and include paths are
 # needed for LibTomMath. Alternatively, an integrated, minimal version of
 # LibTomMath can be used. See beginning of libtommath.c for details on benefits
@@ -357,7 +359,17 @@
 # Note: This is an experimental and not yet complete implementation. This
 # should not be enabled for production use.
 #CONFIG_FILS=y
+# FILS shared key authentication with PFS
+#CONFIG_FILS_SK_PFS=y
 
 # Include internal line edit mode in hostapd_cli. This can be used to provide
 # limited command line editing and history support.
 #CONFIG_WPA_CLI_EDIT=y
+
+# Opportunistic Wireless Encryption (OWE)
+# Experimental implementation of draft-harkins-owe-07.txt
+#CONFIG_OWE=y
+
+# Override default value for the wpa_disable_eapol_key_retries configuration
+# parameter. See that parameter in hostapd.conf for more details.
+#CFLAGS += -DDEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES=1
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index c4eebff..f558855 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -98,8 +98,25 @@
 # Country code (ISO/IEC 3166-1). Used to set regulatory domain.
 # Set as needed to indicate country in which device is operating.
 # This can limit available channels and transmit power.
+# These two octets are used as the first two octets of the Country String
+# (dot11CountryString)
 #country_code=US
 
+# The third octet of the Country String (dot11CountryString)
+# This parameter is used to set the third octet of the country string.
+#
+# All environments of the current frequency band and country (default)
+#country3=0x20
+# Outdoor environment only
+#country3=0x4f
+# Indoor environment only
+#country3=0x49
+# Noncountry entity (country_code=XX)
+#country3=0x58
+# IEEE 802.11 standard Annex E table indication: 0x01 .. 0x1f
+# Annex E, Table E-4 (Global operating classes)
+#country3=0x04
+
 # Enable IEEE 802.11d. This advertises the country_code and the set of allowed
 # channels and transmit power levels based on the regulatory limits. The
 # country_code setting must be configured with the correct country for
@@ -182,6 +199,11 @@
 #chanlist=100 104 108 112 116
 #chanlist=1 6 11-13
 
+# Exclude DFS channels from ACS
+# This option can be used to exclude all DFS channels from the ACS channel list
+# in cases where the driver supports DFS channels.
+#acs_exclude_dfs=1
+
 # Beacon interval in kus (1.024 ms) (default: 100; range 15..65535)
 beacon_int=100
 
@@ -511,6 +533,10 @@
 #
 #multicast_to_unicast=0
 
+# Send broadcast Deauthentication frame on AP start/stop
+# Default: 1 (enabled)
+#broadcast_deauth=1
+
 ##### IEEE 802.11n related configuration ######################################
 
 # ieee80211n: Whether IEEE 802.11n (HT) is enabled
@@ -905,7 +931,8 @@
 # OpenSSL cipher string
 #
 # This is an OpenSSL specific configuration option for configuring the default
-# ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the default.
+# ciphers. If not set, the value configured at build time ("DEFAULT:!EXP:!LOW"
+# by default) is used.
 # See https://www.openssl.org/docs/apps/ciphers.html for OpenSSL documentation
 # on cipher suite configuration. This is applicable only if hostapd is built to
 # use OpenSSL.
@@ -1256,7 +1283,10 @@
 
 # Time interval for rekeying GTK (broadcast/multicast encryption keys) in
 # seconds. (dot11RSNAConfigGroupRekeyTime)
-#wpa_group_rekey=600
+# This defaults to 86400 seconds (once per day) when using CCMP/GCMP as the
+# group cipher and 600 seconds (once per 10 minutes) when using TKIP as the
+# group cipher.
+#wpa_group_rekey=86400
 
 # Rekey GTK when any STA that possesses the current GTK is leaving the BSS.
 # (dot11RSNAConfigGroupRekeyStrict)
@@ -1285,6 +1315,30 @@
 # Range 1..4294967295; default: 4
 #wpa_pairwise_update_count=4
 
+# Workaround for key reinstallation attacks
+#
+# This parameter can be used to disable retransmission of EAPOL-Key frames that
+# are used to install keys (EAPOL-Key message 3/4 and group message 1/2). This
+# is similar to setting wpa_group_update_count=1 and
+# wpa_pairwise_update_count=1, but with no impact to message 1/4 and with
+# extended timeout on the response to avoid causing issues with stations that
+# may use aggressive power saving have very long time in replying to the
+# EAPOL-Key messages.
+#
+# This option can be used to work around key reinstallation attacks on the
+# station (supplicant) side in cases those station devices cannot be updated
+# for some reason. By removing the retransmissions the attacker cannot cause
+# key reinstallation with a delayed frame transmission. This is related to the
+# station side vulnerabilities CVE-2017-13077, CVE-2017-13078, CVE-2017-13079,
+# CVE-2017-13080, and CVE-2017-13081.
+#
+# This workaround might cause interoperability issues and reduced robustness of
+# key negotiation especially in environments with heavy traffic load due to the
+# number of attempts to perform the key exchange is reduced significantly. As
+# such, this workaround is disabled by default (unless overridden in build
+# configuration). To enable this, set the parameter to 1.
+#wpa_disable_eapol_key_retries=1
+
 # Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up
 # roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN
 # authentication and key handshake before actually associating with a new AP.
@@ -1300,12 +1354,6 @@
 # one.
 #rsn_preauth_interfaces=eth0
 
-# peerkey: Whether PeerKey negotiation for direct links (IEEE 802.11e) is
-# allowed. This is only used with RSN/WPA2.
-# 0 = disabled (default)
-# 1 = enabled
-#peerkey=1
-
 # ieee80211w: Whether management frame protection (MFP) is enabled
 # 0 = disabled (default)
 # 1 = optional
@@ -1348,6 +1396,15 @@
 # 1 = enabled
 #okc=1
 
+# SAE password
+# This parameter can be used to set a password for SAE. By default, the
+# wpa_passphrase value is used if this separate parameter is not used, but
+# wpa_passphrase follows the WPA-PSK constraints (8..63 characters) even though
+# SAE passwords do not have such constraints. If the BSS enabled both SAE and
+# WPA-PSK and both values are set, SAE uses the sae_password value and WPA-PSK
+# uses the wpa_passphrase value.
+#sae_password=secret
+
 # SAE threshold for anti-clogging mechanism (dot11RSNASAEAntiCloggingThreshold)
 # This parameter defines how many open SAE instances can be in progress at the
 # same time before the anti-clogging mechanism is taken into use.
@@ -1372,6 +1429,29 @@
 #fils_realm=example.com
 #fils_realm=example.org
 
+# FILS DH Group for PFS
+# 0 = PFS disabled with FILS shared key authentication (default)
+# 1-65535 DH Group to use for FILS PFS
+#fils_dh_group=0
+
+# OWE DH groups
+# OWE implementations are required to support group 19 (NIST P-256). All groups
+# that are supported by the implementation (e.g., groups 19, 20, and 21 when
+# using OpenSSL) are enabled by default. This configuration parameter can be
+# used to specify a limited set of allowed groups. The group values are listed
+# in the IANA registry:
+# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-10
+#owe_groups=19 20 21
+
+# OWE transition mode configuration
+# Pointer to the matching open/OWE BSS
+#owe_transition_bssid=<bssid>
+# SSID in same format as ssid2 described above.
+#owe_transition_ssid=<SSID>
+# Alternatively, OWE transition mode BSSID/SSID can be configured with a
+# reference to a BSS operated by this hostapd process.
+#owe_transition_ifname=<ifname>
+
 # DHCP server for FILS HLP
 # If configured, hostapd will act as a DHCP relay for all FILS HLP requests
 # that include a DHCPDISCOVER message and send them to the specific DHCP
@@ -1426,22 +1506,52 @@
 #reassociation_deadline=1000
 
 # List of R0KHs in the same Mobility Domain
-# format: <MAC address> <NAS Identifier> <128-bit key as hex string>
+# format: <MAC address> <NAS Identifier> <256-bit key as hex string>
 # This list is used to map R0KH-ID (NAS Identifier) to a destination MAC
 # address when requesting PMK-R1 key from the R0KH that the STA used during the
 # Initial Mobility Domain Association.
-#r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f
-#r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff
+#r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
+#r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff
 # And so on.. One line per R0KH.
+# Wildcard entry:
+# Upon receiving a response from R0KH, it will be added to this list, so
+# subsequent requests won't be broadcast. If R0KH does not reply, it will be
+# blacklisted.
+#r0kh=ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff
 
 # List of R1KHs in the same Mobility Domain
-# format: <MAC address> <R1KH-ID> <128-bit key as hex string>
+# format: <MAC address> <R1KH-ID> <256-bit key as hex string>
 # This list is used to map R1KH-ID to a destination MAC address when sending
 # PMK-R1 key from the R0KH. This is also the list of authorized R1KHs in the MD
 # that can request PMK-R1 keys.
-#r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f
-#r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff
+#r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
+#r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff
 # And so on.. One line per R1KH.
+# Wildcard entry:
+# Upon receiving a request from an R1KH not yet known, it will be added to this
+# list and thus will receive push notifications.
+#r1kh=00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff
+
+# Timeout (seconds) for newly discovered R0KH/R1KH (see wildcard entries above)
+# Special values: 0 -> do not expire
+# Warning: do not cache implies no sequence number validation with wildcards
+#rkh_pos_timeout=86400 (default = 1 day)
+
+# Timeout (milliseconds) for requesting PMK-R1 from R0KH using PULL request
+# and number of retries.
+#rkh_pull_timeout=1000 (default = 1 second)
+#rkh_pull_retries=4 (default)
+
+# Timeout (seconds) for non replying R0KH (see wildcard entries above)
+# Special values: 0 -> do not cache
+# default: 60 seconds
+#rkh_neg_timeout=60
+
+# Note: The R0KH/R1KH keys used to be 128-bit in length before the message
+# format was changed. That shorter key length is still supported for backwards
+# compatibility of the configuration files. If such a shorter key is used, a
+# 256-bit key is derived from it. For new deployments, configuring the 256-bit
+# key is recommended.
 
 # Whether PMK-R1 push is enabled at R0KH
 # 0 = do not push PMK-R1 to all configured R1KHs (default)
@@ -2014,6 +2124,28 @@
 #
 #osu_server_uri=...
 
+##### Multiband Operation (MBO) ###############################################
+#
+# MBO enabled
+# 0 = disabled (default)
+# 1 = enabled
+#mbo=1
+#
+# Cellular data connection preference
+# 0 = Excluded - AP does not want STA to use the cellular data connection
+# 1 = AP prefers the STA not to use cellular data connection
+# 255 = AP prefers the STA to use cellular data connection
+#mbo_cell_data_conn_pref=1
+
+##### Optimized Connectivity Experience (OCE) #################################
+#
+# Enable OCE specific features (bitmap)
+# BIT(0) - Reserved
+# Set BIT(1) (= 2) to enable OCE in STA-CFON mode
+# Set BIT(2) (= 4) to enable OCE in AP mode
+# Default is 0 = OCE disabled
+#oce=0
+
 ##### Fast Session Transfer (FST) support #####################################
 #
 # The options in this section are only available when the build configuration
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index f8d1eda..5b0882a 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -1024,7 +1024,7 @@
 
 static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	char cmd[256];
+	char cmd[2048];
 	int res;
 
 	if (argc != 2) {
@@ -1364,6 +1364,80 @@
 }
 
 
+#ifdef CONFIG_DPP
+
+static int hostapd_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	return hostapd_cli_cmd(ctrl, "DPP_QR_CODE", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_bootstrap_gen(struct wpa_ctrl *ctrl, int argc,
+					     char *argv[])
+{
+	return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_GEN", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_bootstrap_remove(struct wpa_ctrl *ctrl, int argc,
+						char *argv[])
+{
+	return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_REMOVE", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_bootstrap_get_uri(struct wpa_ctrl *ctrl,
+						 int argc, char *argv[])
+{
+	return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_GET_URI", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_bootstrap_info(struct wpa_ctrl *ctrl, int argc,
+					      char *argv[])
+{
+	return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_INFO", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_auth_init(struct wpa_ctrl *ctrl, int argc,
+					 char *argv[])
+{
+	return hostapd_cli_cmd(ctrl, "DPP_AUTH_INIT", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_configurator_add(struct wpa_ctrl *ctrl, int argc,
+						char *argv[])
+{
+	return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_ADD", 0, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_configurator_remove(struct wpa_ctrl *ctrl,
+						   int argc, char *argv[])
+{
+	return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_REMOVE", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_pkex_add(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	return hostapd_cli_cmd(ctrl, "DPP_PKEX_ADD", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_pkex_remove(struct wpa_ctrl *ctrl, int argc,
+					   char *argv[])
+{
+	return hostapd_cli_cmd(ctrl, "DPP_PKEX_REMOVE", 1, argc, argv);
+}
+
+#endif /* CONFIG_DPP */
+
+
 struct hostapd_cli_cmd {
 	const char *cmd;
 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
@@ -1499,6 +1573,29 @@
 	  " = send FTM range request"},
 	{ "driver_flags", hostapd_cli_cmd_driver_flags, NULL,
 	  " = show supported driver flags"},
+#ifdef CONFIG_DPP
+	{ "dpp_qr_code", hostapd_cli_cmd_dpp_qr_code, NULL,
+	  "report a scanned DPP URI from a QR Code" },
+	{ "dpp_bootstrap_gen", hostapd_cli_cmd_dpp_bootstrap_gen, NULL,
+	  "type=<qrcode> [chan=..] [mac=..] [info=..] [curve=..] [key=..] = generate DPP bootstrap information" },
+	{ "dpp_bootstrap_remove", hostapd_cli_cmd_dpp_bootstrap_remove, NULL,
+	  "*|<id> = remove DPP bootstrap information" },
+	{ "dpp_bootstrap_get_uri", hostapd_cli_cmd_dpp_bootstrap_get_uri, NULL,
+	  "<id> = get DPP bootstrap URI" },
+	{ "dpp_bootstrap_info", hostapd_cli_cmd_dpp_bootstrap_info, NULL,
+	  "<id> = show DPP bootstrap information" },
+	{ "dpp_auth_init", hostapd_cli_cmd_dpp_auth_init, NULL,
+	  "peer=<id> [own=<id>] = initiate DPP bootstrapping" },
+	{ "dpp_configurator_add", hostapd_cli_cmd_dpp_configurator_add, NULL,
+	  "[curve=..] [key=..] = add DPP configurator" },
+	{ "dpp_configurator_remove", hostapd_cli_cmd_dpp_configurator_remove,
+	  NULL,
+	  "*|<id> = remove DPP configurator" },
+	{ "dpp_pkex_add", hostapd_cli_cmd_dpp_pkex_add, NULL,
+	  "add PKEX code" },
+	{ "dpp_pkex_remove", hostapd_cli_cmd_dpp_pkex_remove, NULL,
+	  "*|<id> = remove DPP pkex information" },
+#endif /* CONFIG_DPP */
 	{ NULL, NULL, NULL, NULL }
 };
 
@@ -1610,7 +1707,7 @@
 	if (ctrl_conn == NULL)
 		return;
 	while (wpa_ctrl_pending(ctrl)) {
-		char buf[256];
+		char buf[4096];
 		size_t len = sizeof(buf) - 1;
 		if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
 			buf[len] = '\0';
diff --git a/hostapd/main.c b/hostapd/main.c
index 593267c..ce94d05 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -666,6 +666,9 @@
 	interfaces.global_iface_name = NULL;
 	interfaces.global_ctrl_sock = -1;
 	dl_list_init(&interfaces.global_ctrl_dst);
+#ifdef CONFIG_ETH_P_OUI
+	dl_list_init(&interfaces.eth_p_oui);
+#endif /* CONFIG_ETH_P_OUI */
 
 	for (;;) {
 		c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:");
@@ -866,8 +869,27 @@
 	 */
 	interfaces.terminate_on_error = interfaces.count;
 	for (i = 0; i < interfaces.count; i++) {
-		if (hostapd_driver_init(interfaces.iface[i]) ||
-		    hostapd_setup_interface(interfaces.iface[i]))
+		if (hostapd_driver_init(interfaces.iface[i]))
+			goto out;
+#ifdef CONFIG_MBO
+		for (j = 0; j < interfaces.iface[i]->num_bss; j++) {
+			struct hostapd_data *hapd = interfaces.iface[i]->bss[j];
+
+			if (hapd && (hapd->conf->oce & OCE_STA_CFON) &&
+			    (interfaces.iface[i]->drv_flags &
+			     WPA_DRIVER_FLAGS_OCE_STA_CFON))
+				hapd->enable_oce = OCE_STA_CFON;
+
+			if (hapd && (hapd->conf->oce & OCE_AP) &&
+			    (interfaces.iface[i]->drv_flags &
+			     WPA_DRIVER_FLAGS_OCE_STA_CFON)) {
+				/* TODO: Need to add OCE-AP support */
+				wpa_printf(MSG_ERROR,
+					   "OCE-AP feature is not yet supported");
+			}
+		}
+#endif /* CONFIG_MBO */
+		if (hostapd_setup_interface(interfaces.iface[i]))
 			goto out;
 	}
 
diff --git a/src/ap/Makefile b/src/ap/Makefile
index 1c65bd6..b8c167c 100644
--- a/src/ap/Makefile
+++ b/src/ap/Makefile
@@ -48,7 +48,6 @@
 	neighbor_db.o \
 	ndisc_snoop.o \
 	p2p_hostapd.o \
-	peerkey_auth.o \
 	pmksa_cache_auth.o \
 	preauth_auth.o \
 	rrm.o \
diff --git a/src/ap/acs.c b/src/ap/acs.c
index 5e83805..aa59058 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -260,7 +260,7 @@
 }
 
 
-static void acs_cleanup(struct hostapd_iface *iface)
+void acs_cleanup(struct hostapd_iface *iface)
 {
 	int i;
 	struct hostapd_channel_data *chan;
@@ -331,10 +331,8 @@
 	long double int_factor = 0;
 	unsigned count = 0;
 
-	if (dl_list_empty(&chan->survey_list))
-		return;
-
-	if (chan->flag & HOSTAPD_CHAN_DISABLED)
+	if (dl_list_empty(&chan->survey_list) ||
+	    (chan->flag & HOSTAPD_CHAN_DISABLED))
 		return;
 
 	chan->interference_factor = 0;
@@ -359,9 +357,8 @@
 			   (unsigned long) survey->channel_time_rx);
 	}
 
-	if (!count)
-		return;
-	chan->interference_factor /= count;
+	if (count)
+		chan->interference_factor /= count;
 }
 
 
@@ -450,13 +447,9 @@
 
 	for (i = 0; i < iface->current_mode->num_channels; i++) {
 		chan = &iface->current_mode->channels[i];
-		if (chan->flag & HOSTAPD_CHAN_DISABLED)
-			continue;
-
-		if (!acs_survey_list_is_sufficient(chan))
-			continue;
-
-		valid++;
+		if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+		    acs_survey_list_is_sufficient(chan))
+			valid++;
 	}
 
 	/* We need at least survey data for one channel */
@@ -466,13 +459,9 @@
 
 static int acs_usable_chan(struct hostapd_channel_data *chan)
 {
-	if (dl_list_empty(&chan->survey_list))
-		return 0;
-	if (chan->flag & HOSTAPD_CHAN_DISABLED)
-		return 0;
-	if (!acs_survey_list_is_sufficient(chan))
-		return 0;
-	return 1;
+	return !dl_list_empty(&chan->survey_list) &&
+		!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+		acs_survey_list_is_sufficient(chan);
 }
 
 
@@ -788,10 +777,7 @@
 
 static int acs_study_options(struct hostapd_iface *iface)
 {
-	int err;
-
-	err = acs_study_survey_based(iface);
-	if (err == 0)
+	if (acs_study_survey_based(iface) == 0)
 		return 0;
 
 	/* TODO: If no surveys are available/sufficient this is a good
@@ -920,14 +906,11 @@
 
 enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
 {
-	int err;
-
 	wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit");
 
 	if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) {
 		wpa_printf(MSG_INFO, "ACS: Offloading to driver");
-		err = hostapd_drv_do_acs(iface->bss[0]);
-		if (err)
+		if (hostapd_drv_do_acs(iface->bss[0]))
 			return HOSTAPD_CHAN_INVALID;
 		return HOSTAPD_CHAN_ACS;
 	}
@@ -937,8 +920,7 @@
 
 	acs_cleanup(iface);
 
-	err = acs_request_scan(iface);
-	if (err < 0)
+	if (acs_request_scan(iface) < 0)
 		return HOSTAPD_CHAN_INVALID;
 
 	hostapd_set_state(iface, HAPD_IFACE_ACS);
diff --git a/src/ap/acs.h b/src/ap/acs.h
index fc85259..ec84f0e 100644
--- a/src/ap/acs.h
+++ b/src/ap/acs.h
@@ -13,6 +13,7 @@
 #ifdef CONFIG_ACS
 
 enum hostapd_chan_status acs_init(struct hostapd_iface *iface);
+void acs_cleanup(struct hostapd_iface *iface);
 
 #else /* CONFIG_ACS */
 
@@ -22,6 +23,10 @@
 	return HOSTAPD_CHAN_INVALID;
 }
 
+static inline void acs_cleanup(struct hostapd_iface *iface)
+{
+}
+
 #endif /* CONFIG_ACS */
 
 #endif /* ACS_H */
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 9abcab7..07310f9 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -37,6 +37,10 @@
 }
 
 
+#ifndef DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES
+#define DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES 0
+#endif /* DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES */
+
 void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
 {
 	dl_list_init(&bss->anqp_elem);
@@ -58,6 +62,8 @@
 	bss->wpa_gmk_rekey = 86400;
 	bss->wpa_group_update_count = 4;
 	bss->wpa_pairwise_update_count = 4;
+	bss->wpa_disable_eapol_key_retries =
+		DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES;
 	bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
 	bss->wpa_pairwise = WPA_CIPHER_TKIP;
 	bss->wpa_group = WPA_CIPHER_TKIP;
@@ -93,6 +99,10 @@
 
 #ifdef CONFIG_IEEE80211R_AP
 	bss->ft_over_ds = 1;
+	bss->rkh_pos_timeout = 86400;
+	bss->rkh_neg_timeout = 60;
+	bss->rkh_pull_timeout = 1000;
+	bss->rkh_pull_retries = 4;
 #endif /* CONFIG_IEEE80211R_AP */
 
 	bss->radius_das_time_window = 300;
@@ -107,6 +117,12 @@
 	bss->dhcp_server_port = DHCP_SERVER_PORT;
 	bss->dhcp_relay_port = DHCP_SERVER_PORT;
 #endif /* CONFIG_FILS */
+
+	bss->broadcast_deauth = 1;
+
+#ifdef CONFIG_MBO
+	bss->mbo_cell_data_conn_pref = -1;
+#endif /* CONFIG_MBO */
 }
 
 
@@ -204,6 +220,11 @@
 	conf->acs_num_scans = 5;
 #endif /* CONFIG_ACS */
 
+	/* The third octet of the country string uses an ASCII space character
+	 * by default to indicate that the regulations encompass all
+	 * environments for the current frequency band in the country. */
+	conf->country[2] = ' ';
+
 	return conf;
 }
 
@@ -595,6 +616,9 @@
 	wpabuf_free(conf->assocresp_elements);
 
 	os_free(conf->sae_groups);
+#ifdef CONFIG_OWE
+	os_free(conf->owe_groups);
+#endif /* CONFIG_OWE */
 
 	os_free(conf->wowlan_triggers);
 
@@ -602,6 +626,7 @@
 
 #ifdef CONFIG_TESTING_OPTIONS
 	wpabuf_free(conf->own_ie_override);
+	wpabuf_free(conf->sae_commit_override);
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	os_free(conf->no_probe_resp_if_seen_on);
@@ -609,6 +634,14 @@
 
 	hostapd_config_free_fils_realms(conf);
 
+#ifdef CONFIG_DPP
+	os_free(conf->dpp_connector);
+	wpabuf_free(conf->dpp_netaccesskey);
+	wpabuf_free(conf->dpp_csign);
+#endif /* CONFIG_DPP */
+
+	os_free(conf->sae_password);
+
 	os_free(conf);
 }
 
@@ -1015,6 +1048,9 @@
 		bss->rsn_pairwise = bss->wpa_pairwise;
 	bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise,
 						    bss->rsn_pairwise);
+	if (!bss->wpa_group_rekey_set)
+		bss->wpa_group_rekey = bss->wpa_group == WPA_CIPHER_TKIP ?
+			600 : 86400;
 
 	if (full_config) {
 		bss->radius->auth_server = bss->radius->auth_servers;
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index fdd5a1a..89bf289 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -327,21 +327,26 @@
 	int wpa_pairwise;
 	int wpa_group;
 	int wpa_group_rekey;
+	int wpa_group_rekey_set;
 	int wpa_strict_rekey;
 	int wpa_gmk_rekey;
 	int wpa_ptk_rekey;
 	u32 wpa_group_update_count;
 	u32 wpa_pairwise_update_count;
+	int wpa_disable_eapol_key_retries;
 	int rsn_pairwise;
 	int rsn_preauth;
 	char *rsn_preauth_interfaces;
-	int peerkey;
 
 #ifdef CONFIG_IEEE80211R_AP
 	/* IEEE 802.11r - Fast BSS Transition */
 	u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
 	u8 r1_key_holder[FT_R1KH_ID_LEN];
 	u32 r0_key_lifetime;
+	int rkh_pos_timeout;
+	int rkh_neg_timeout;
+	int rkh_pull_timeout; /* ms */
+	int rkh_pull_retries;
 	u32 reassociation_deadline;
 	struct ft_remote_r0kh *r0kh_list;
 	struct ft_remote_r1kh *r1kh_list;
@@ -362,6 +367,7 @@
 	char *private_key_passwd;
 	int check_crl;
 	unsigned int tls_session_lifetime;
+	unsigned int tls_flags;
 	char *ocsp_stapling_response;
 	char *ocsp_stapling_response_multi;
 	char *dh_file;
@@ -576,6 +582,7 @@
 
 	unsigned int sae_anti_clogging_threshold;
 	int *sae_groups;
+	char *sae_password;
 
 	char *wowlan_triggers; /* Wake-on-WLAN triggers */
 
@@ -583,6 +590,8 @@
 	u8 bss_load_test[5];
 	u8 bss_load_test_set;
 	struct wpabuf *own_ie_override;
+	int sae_reflection_attack;
+	struct wpabuf *sae_commit_override;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 #define MESH_ENABLED BIT(0)
@@ -600,6 +609,14 @@
 
 #ifdef CONFIG_MBO
 	int mbo_enabled;
+	/**
+	 * oce - Enable OCE in AP and/or STA-CFON mode
+	 *  - BIT(0) is Reserved
+	 *  - Set BIT(1) to enable OCE in STA-CFON mode
+	 *  - Set BIT(2) to enable OCE in AP mode
+	 */
+	unsigned int oce;
+	int mbo_cell_data_conn_pref;
 #endif /* CONFIG_MBO */
 
 	int ftm_responder;
@@ -609,6 +626,7 @@
 	u8 fils_cache_id[FILS_CACHE_ID_LEN];
 	int fils_cache_id_set;
 	struct dl_list fils_realms; /* list of struct fils_realm */
+	int fils_dh_group;
 	struct hostapd_ip_addr dhcp_server;
 	int dhcp_rapid_commit_proxy;
 	unsigned int fils_hlp_wait_time;
@@ -617,6 +635,23 @@
 #endif /* CONFIG_FILS */
 
 	int multicast_to_unicast;
+
+	int broadcast_deauth;
+
+#ifdef CONFIG_DPP
+	char *dpp_connector;
+	struct wpabuf *dpp_netaccesskey;
+	unsigned int dpp_netaccesskey_expiry;
+	struct wpabuf *dpp_csign;
+#endif /* CONFIG_DPP */
+
+#ifdef CONFIG_OWE
+	macaddr owe_transition_bssid;
+	u8 owe_transition_ssid[SSID_MAX_LEN];
+	size_t owe_transition_ssid_len;
+	char owe_transition_ifname[IFNAMSIZ + 1];
+	int *owe_groups;
+#endif /* CONFIG_OWE */
 };
 
 /**
@@ -652,6 +687,7 @@
 	u8 channel;
 	u8 acs;
 	struct wpa_freq_range_list acs_ch_list;
+	int acs_exclude_dfs;
 	enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */
 	enum {
 		LONG_PREAMBLE = 0,
@@ -677,6 +713,9 @@
 			  * ' ' (ascii 32): all environments
 			  * 'O': Outdoor environemnt only
 			  * 'I': Indoor environment only
+			  * 'X': Used with noncountry entity ("XXX")
+			  * 0x00..0x31: identifying IEEE 802.11 standard
+			  *	Annex E table (0x04 = global table)
 			  */
 
 	int ieee80211d;
@@ -717,6 +756,7 @@
 	u8 vht_oper_chwidth;
 	u8 vht_oper_centr_freq_seg0_idx;
 	u8 vht_oper_centr_freq_seg1_idx;
+	u8 ht40_plus_minus_allowed;
 
 	/* Use driver-generated interface addresses when adding multiple BSSs */
 	u8 use_driver_iface_addr;
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index f139465..8f4d839 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -19,6 +19,7 @@
 #include "ap_config.h"
 #include "p2p_hostapd.h"
 #include "hs20.h"
+#include "wpa_auth.h"
 #include "ap_drv_ops.h"
 
 
@@ -99,6 +100,13 @@
 		goto fail;
 #endif /* CONFIG_FST */
 
+#ifdef CONFIG_FILS
+	pos = hostapd_eid_fils_indic(hapd, buf, 0);
+	if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
+	    add_buf_data(&proberesp, buf, pos - buf) < 0)
+		goto fail;
+#endif /* CONFIG_FILS */
+
 	if (add_buf(&beacon, hapd->wps_beacon_ie) < 0 ||
 	    add_buf(&proberesp, hapd->wps_probe_resp_ie) < 0)
 		goto fail;
@@ -168,7 +176,7 @@
 #endif /* CONFIG_HS20 */
 
 #ifdef CONFIG_MBO
-	if (hapd->conf->mbo_enabled) {
+	if (hapd->conf->mbo_enabled || hapd->enable_oce) {
 		pos = hostapd_eid_mbo(hapd, buf, sizeof(buf));
 		if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
 		    add_buf_data(&proberesp, buf, pos - buf) < 0 ||
@@ -340,10 +348,44 @@
 int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
 		     u16 seq, u16 status, const u8 *ie, size_t len)
 {
+	struct wpa_driver_sta_auth_params params;
+#ifdef CONFIG_FILS
+	struct sta_info *sta;
+#endif /* CONFIG_FILS */
+
 	if (hapd->driver == NULL || hapd->driver->sta_auth == NULL)
 		return 0;
-	return hapd->driver->sta_auth(hapd->drv_priv, hapd->own_addr, addr,
-				      seq, status, ie, len);
+
+	os_memset(&params, 0, sizeof(params));
+
+#ifdef CONFIG_FILS
+	sta = ap_get_sta(hapd, addr);
+	if (!sta) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR
+			   " not found for sta_auth processing",
+			   MAC2STR(addr));
+		return 0;
+	}
+
+	if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	    sta->auth_alg == WLAN_AUTH_FILS_PK) {
+		params.fils_auth = 1;
+		wpa_auth_get_fils_aead_params(sta->wpa_sm, params.fils_anonce,
+					      params.fils_snonce,
+					      params.fils_kek,
+					      &params.fils_kek_len);
+	}
+#endif /* CONFIG_FILS */
+
+	params.own_addr = hapd->own_addr;
+	params.addr = addr;
+	params.seq = seq;
+	params.status = status;
+	params.ie = ie;
+	params.len = len;
+
+	return hapd->driver->sta_auth(hapd->drv_priv, &params);
 }
 
 
@@ -554,13 +596,13 @@
 
 struct hostapd_hw_modes *
 hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
-			    u16 *flags)
+			    u16 *flags, u8 *dfs_domain)
 {
 	if (hapd->driver == NULL ||
 	    hapd->driver->get_hw_feature_data == NULL)
 		return NULL;
 	return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes,
-						 flags);
+						 flags, dfs_domain);
 }
 
 
@@ -774,7 +816,9 @@
 		if ((acs_ch_list_all ||
 		     freq_range_list_includes(&hapd->iface->conf->acs_ch_list,
 					      chan->chan)) &&
-		    !(chan->flag & HOSTAPD_CHAN_DISABLED))
+		    !(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+		    !(hapd->iface->conf->acs_exclude_dfs &&
+		      (chan->flag & HOSTAPD_CHAN_RADAR)))
 			int_array_add_unique(freq_list, chan->freq);
 	}
 }
@@ -829,6 +873,9 @@
 				    &hapd->iface->conf->acs_ch_list,
 				    chan->chan))
 				continue;
+			if (hapd->iface->conf->acs_exclude_dfs &&
+			    (chan->flag & HOSTAPD_CHAN_RADAR))
+				continue;
 			if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) {
 				channels[num_channels++] = chan->chan;
 				int_array_add_unique(&freq_list, chan->freq);
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 0bb7954..bf8169d 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -72,7 +72,7 @@
 				int cw_min, int cw_max, int burst_time);
 struct hostapd_hw_modes *
 hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
-			    u16 *flags);
+			    u16 *flags, u8 *dfs_domain);
 int hostapd_driver_commit(struct hostapd_data *hapd);
 int hostapd_drv_none(struct hostapd_data *hapd);
 int hostapd_driver_scan(struct hostapd_data *hapd,
@@ -274,8 +274,9 @@
 static inline int hostapd_drv_switch_channel(struct hostapd_data *hapd,
 					     struct csa_settings *settings)
 {
-	if (hapd->driver == NULL || hapd->driver->switch_channel == NULL)
-		return -ENOTSUP;
+	if (hapd->driver == NULL || hapd->driver->switch_channel == NULL ||
+	    hapd->drv_priv == NULL)
+		return -1;
 
 	return hapd->driver->switch_channel(hapd->drv_priv, settings);
 }
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index cdb49cd..a20f49f 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -71,11 +71,10 @@
 	}
 
 	if (eap_user->password) {
-		user->password = os_malloc(eap_user->password_len);
+		user->password = os_memdup(eap_user->password,
+					   eap_user->password_len);
 		if (user->password == NULL)
 			goto out;
-		os_memcpy(user->password, eap_user->password,
-			  eap_user->password_len);
 		user->password_len = eap_user->password_len;
 		user->password_hash = eap_user->password_hash;
 	}
@@ -133,6 +132,7 @@
 	srv.erp = conf->eap_server_erp;
 	srv.erp_domain = conf->erp_domain;
 	srv.tls_session_lifetime = conf->tls_session_lifetime;
+	srv.tls_flags = conf->tls_flags;
 
 	hapd->radius_srv = radius_server_init(&srv);
 	if (hapd->radius_srv == NULL) {
@@ -157,6 +157,7 @@
 
 		os_memset(&conf, 0, sizeof(conf));
 		conf.tls_session_lifetime = hapd->conf->tls_session_lifetime;
+		conf.tls_flags = hapd->conf->tls_flags;
 		hapd->ssl_ctx = tls_init(&conf);
 		if (hapd->ssl_ctx == NULL) {
 			wpa_printf(MSG_ERROR, "Failed to initialize TLS");
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index c6bbda3..3ea28a7 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -363,6 +363,67 @@
 }
 
 
+#ifdef CONFIG_OWE
+static int hostapd_eid_owe_trans_enabled(struct hostapd_data *hapd)
+{
+	return hapd->conf->owe_transition_ssid_len > 0 &&
+		!is_zero_ether_addr(hapd->conf->owe_transition_bssid);
+}
+#endif /* CONFIG_OWE */
+
+
+static size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_OWE
+	if (!hostapd_eid_owe_trans_enabled(hapd))
+		return 0;
+	return 6 + ETH_ALEN + 1 + hapd->conf->owe_transition_ssid_len;
+#else /* CONFIG_OWE */
+	return 0;
+#endif /* CONFIG_OWE */
+}
+
+
+static u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid,
+				  size_t len)
+{
+#ifdef CONFIG_OWE
+	u8 *pos = eid;
+	size_t elen;
+
+	if (hapd->conf->owe_transition_ifname[0] &&
+	    !hostapd_eid_owe_trans_enabled(hapd))
+		hostapd_owe_trans_get_info(hapd);
+
+	if (!hostapd_eid_owe_trans_enabled(hapd))
+		return pos;
+
+	elen = hostapd_eid_owe_trans_len(hapd);
+	if (len < elen) {
+		wpa_printf(MSG_DEBUG,
+			   "OWE: Not enough room in the buffer for OWE IE");
+		return pos;
+	}
+
+	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+	*pos++ = elen - 2;
+	WPA_PUT_BE24(pos, OUI_WFA);
+	pos += 3;
+	*pos++ = OWE_OUI_TYPE;
+	os_memcpy(pos, hapd->conf->owe_transition_bssid, ETH_ALEN);
+	pos += ETH_ALEN;
+	*pos++ = hapd->conf->owe_transition_ssid_len;
+	os_memcpy(pos, hapd->conf->owe_transition_ssid,
+		  hapd->conf->owe_transition_ssid_len);
+	pos += hapd->conf->owe_transition_ssid_len;
+
+	return pos;
+#else /* CONFIG_OWE */
+	return eid;
+#endif /* CONFIG_OWE */
+}
+
+
 static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
 				   const struct ieee80211_mgmt *req,
 				   int is_p2p, size_t *resp_len)
@@ -394,12 +455,13 @@
 
 #ifdef CONFIG_IEEE80211AX
 	if (hapd->iconf->ieee80211ax) {
-		buflen += 4 + sizeof (struct ieee80211_he_capabilities) +
-			4 + sizeof (struct ieee80211_he_operation);
+		buflen += 3 + sizeof(struct ieee80211_he_capabilities) +
+			3 + sizeof(struct ieee80211_he_operation);
 	}
-#endif
+#endif /* CONFIG_IEEE80211AX */
 
 	buflen += hostapd_mbo_ie_len(hapd);
+	buflen += hostapd_eid_owe_trans_len(hapd);
 
 	resp = os_zalloc(buflen);
 	if (resp == NULL)
@@ -502,18 +564,18 @@
 
 	pos = hostapd_eid_fils_indic(hapd, pos, 0);
 
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax) {
+		pos = hostapd_eid_he_capab(hapd, pos);
+		pos = hostapd_eid_he_operation(hapd, pos);
+	}
+#endif /* CONFIG_IEEE80211AX */
+
 #ifdef CONFIG_IEEE80211AC
 	if (hapd->conf->vendor_vht)
 		pos = hostapd_eid_vendor_vht(hapd, pos);
 #endif /* CONFIG_IEEE80211AC */
 
-#ifdef CONFIG_IEEE80211AX
-	if (hapd->iconf->ieee80211ax) {
-		pos = hostapd_eid_vendor_he_capab(hapd, pos);
-		pos = hostapd_eid_vendor_he_operation(hapd, pos);
-	}
-#endif /* CONFIG_IEEE80211AX */
-
 	/* Wi-Fi Alliance WMM */
 	pos = hostapd_eid_wmm(hapd, pos);
 
@@ -545,6 +607,7 @@
 #endif /* CONFIG_HS20 */
 
 	pos = hostapd_eid_mbo(hapd, pos, (u8 *) resp + buflen - pos);
+	pos = hostapd_eid_owe_trans(hapd, pos, (u8 *) resp + buflen - pos);
 
 	if (hapd->conf->vendor_elements) {
 		os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements),
@@ -1056,12 +1119,13 @@
 
 #ifdef CONFIG_IEEE80211AX
 	if (hapd->iconf->ieee80211ax) {
-		tail_len += 4 + sizeof (struct ieee80211_he_capabilities) +
-			4 + sizeof (struct ieee80211_he_operation);
+		tail_len += 3 + sizeof(struct ieee80211_he_capabilities) +
+			3 + sizeof(struct ieee80211_he_operation);
 	}
-#endif
+#endif /* CONFIG_IEEE80211AX */
 
 	tail_len += hostapd_mbo_ie_len(hapd);
+	tail_len += hostapd_eid_owe_trans_len(hapd);
 
 	tailpos = tail = os_malloc(tail_len);
 	if (head == NULL || tail == NULL) {
@@ -1187,18 +1251,18 @@
 
 	tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
 
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax) {
+		tailpos = hostapd_eid_he_capab(hapd, tailpos);
+		tailpos = hostapd_eid_he_operation(hapd, tailpos);
+	}
+#endif /* CONFIG_IEEE80211AX */
+
 #ifdef CONFIG_IEEE80211AC
 	if (hapd->conf->vendor_vht)
 		tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
 #endif /* CONFIG_IEEE80211AC */
 
-#ifdef CONFIG_IEEE80211AX
-	if (hapd->iconf->ieee80211ax) {
-		tailpos = hostapd_eid_vendor_he_capab(hapd, tailpos);
-		tailpos = hostapd_eid_vendor_he_operation(hapd, tailpos);
-	}
-#endif /* CONFIG_IEEE80211AX */
-
 	/* Wi-Fi Alliance WMM */
 	tailpos = hostapd_eid_wmm(hapd, tailpos);
 
@@ -1229,6 +1293,8 @@
 #endif /* CONFIG_HS20 */
 
 	tailpos = hostapd_eid_mbo(hapd, tailpos, tail + tail_len - tailpos);
+	tailpos = hostapd_eid_owe_trans(hapd, tailpos,
+					tail + tail_len - tailpos);
 
 	if (hapd->conf->vendor_elements) {
 		os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements),
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index a760e3a..1a2b4e5 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -32,17 +32,86 @@
 {
 	struct hostap_sta_driver_data data;
 	int ret;
+	int len = 0;
 
 	if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0)
 		return 0;
 
 	ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n"
-			  "rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n",
+			  "rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n"
+			  "signal=%d\n",
 			  data.rx_packets, data.tx_packets,
-			  data.rx_bytes, data.tx_bytes, data.inactive_msec);
+			  data.rx_bytes, data.tx_bytes, data.inactive_msec,
+			  data.signal);
 	if (os_snprintf_error(buflen, ret))
 		return 0;
-	return ret;
+	len += ret;
+
+	ret = os_snprintf(buf + len, buflen - len, "rx_rate_info=%lu",
+			  data.current_rx_rate);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+	if (data.flags & STA_DRV_DATA_RX_MCS) {
+		ret = os_snprintf(buf + len, buflen - len, " mcs %u",
+				  data.rx_mcs);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+	if (data.flags & STA_DRV_DATA_RX_VHT_MCS) {
+		ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u",
+				  data.rx_vhtmcs);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+	if (data.flags & STA_DRV_DATA_RX_VHT_NSS) {
+		ret = os_snprintf(buf + len, buflen - len, " vhtnss %u",
+				  data.rx_vht_nss);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+	if (data.flags & STA_DRV_DATA_RX_SHORT_GI) {
+		ret = os_snprintf(buf + len, buflen - len, " shortGI");
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+	ret = os_snprintf(buf + len, buflen - len, "\n");
+	if (!os_snprintf_error(buflen - len, ret))
+		len += ret;
+
+	ret = os_snprintf(buf + len, buflen - len, "tx_rate_info=%lu",
+			  data.current_tx_rate);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+	if (data.flags & STA_DRV_DATA_TX_MCS) {
+		ret = os_snprintf(buf + len, buflen - len, " mcs %u",
+				  data.tx_mcs);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+	if (data.flags & STA_DRV_DATA_TX_VHT_MCS) {
+		ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u",
+				  data.tx_vhtmcs);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+	if (data.flags & STA_DRV_DATA_TX_VHT_NSS) {
+		ret = os_snprintf(buf + len, buflen - len, " vhtnss %u",
+				  data.tx_vht_nss);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+	if (data.flags & STA_DRV_DATA_TX_SHORT_GI) {
+		ret = os_snprintf(buf + len, buflen - len, " shortGI");
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+	ret = os_snprintf(buf + len, buflen - len, "\n");
+	if (!os_snprintf_error(buflen - len, ret))
+		len += ret;
+
+	return len;
 }
 
 
@@ -641,6 +710,54 @@
 }
 
 
+int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd)
+{
+	u8 spa[ETH_ALEN];
+	u8 pmkid[PMKID_LEN];
+	u8 pmk[PMK_LEN_MAX];
+	size_t pmk_len;
+	char *pos, *pos2;
+	int akmp = 0, expiration = 0;
+
+	/*
+	 * Entry format:
+	 * <STA addr> <PMKID> <PMK> <expiration in seconds> <akmp>
+	 */
+
+	if (hwaddr_aton(cmd, spa))
+		return -1;
+
+	pos = os_strchr(cmd, ' ');
+	if (!pos)
+		return -1;
+	pos++;
+
+	if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0)
+		return -1;
+
+	pos = os_strchr(pos, ' ');
+	if (!pos)
+		return -1;
+	pos++;
+
+	pos2 = os_strchr(pos, ' ');
+	if (!pos2)
+		return -1;
+	pmk_len = (pos2 - pos) / 2;
+	if (pmk_len < PMK_LEN || pmk_len > PMK_LEN_MAX ||
+	    hexstr2bin(pos, pmk, pmk_len) < 0)
+		return -1;
+
+	pos = pos2 + 1;
+
+	if (sscanf(pos, "%d %d", &expiration, &akmp) != 2)
+		return -1;
+
+	return wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len,
+				   pmkid, expiration, akmp);
+}
+
+
 #ifdef CONFIG_PMKSA_CACHE_EXTERNAL
 #ifdef CONFIG_MESH
 
diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h
index 3b61cac..d1dcebf 100644
--- a/src/ap/ctrl_iface_ap.h
+++ b/src/ap/ctrl_iface_ap.h
@@ -32,6 +32,7 @@
 int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf,
 				  size_t len);
 void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd);
+int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd);
 int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd,
 				       const u8 *addr, char *buf, size_t len);
 void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd);
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 47adba7..5a0d781 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -1,7 +1,7 @@
 /*
  * DFS - Dynamic Frequency Selection
  * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2013-2015, Qualcomm Atheros, Inc.
+ * Copyright (c) 2013-2017, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -747,6 +747,23 @@
 }
 
 
+static int hostapd_config_dfs_chan_available(struct hostapd_iface *iface)
+{
+	int n_chans, n_chans1, start_chan_idx, start_chan_idx1;
+
+	/* Get the start (first) channel for current configuration */
+	start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1);
+	if (start_chan_idx < 0)
+		return 0;
+
+	/* Get the number of used channels, depending on width */
+	n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+	/* Check if all channels are DFS available */
+	return dfs_check_chans_available(iface, start_chan_idx, n_chans);
+}
+
+
 int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
 			     int ht_enabled, int chan_offset, int chan_width,
 			     int cf1, int cf2)
@@ -767,8 +784,21 @@
 			set_dfs_state(iface, freq, ht_enabled, chan_offset,
 				      chan_width, cf1, cf2,
 				      HOSTAPD_CHAN_DFS_AVAILABLE);
-			iface->cac_started = 0;
-			hostapd_setup_interface_complete(iface, 0);
+			/*
+			 * Just mark the channel available when CAC completion
+			 * event is received in enabled state. CAC result could
+			 * have been propagated from another radio having the
+			 * same regulatory configuration. When CAC completion is
+			 * received during non-HAPD_IFACE_ENABLED state, make
+			 * sure the configured channel is available because this
+			 * CAC completion event could have been propagated from
+			 * another radio.
+			 */
+			if (iface->state != HAPD_IFACE_ENABLED &&
+			    hostapd_config_dfs_chan_available(iface)) {
+				hostapd_setup_interface_complete(iface, 0);
+				iface->cac_started = 0;
+			}
 		}
 	}
 
@@ -776,6 +806,25 @@
 }
 
 
+int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
+				int ht_enabled, int chan_offset, int chan_width,
+				int cf1, int cf2)
+{
+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_PRE_CAC_EXPIRED
+		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
+		freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+
+	/* Proceed only if DFS is not offloaded to the driver */
+	if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+		return 0;
+
+	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
+		      cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
+
+	return 0;
+}
+
+
 static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
 {
 	struct hostapd_channel_data *channel;
@@ -840,6 +889,13 @@
 	if (iface->cac_started)
 		return hostapd_dfs_start_channel_switch_cac(iface);
 
+	/*
+	 * Allow selection of DFS channel in ETSI to comply with
+	 * uniform spreading.
+	 */
+	if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
+		skip_radar = 0;
+
 	/* Perform channel switch/CSA */
 	channel = dfs_get_valid_channel(iface, &secondary_channel,
 					&vht_oper_centr_freq_seg0_idx,
diff --git a/src/ap/dfs.h b/src/ap/dfs.h
index be8c0e6..f0fa6f6 100644
--- a/src/ap/dfs.h
+++ b/src/ap/dfs.h
@@ -1,7 +1,7 @@
 /*
  * DFS - Dynamic Frequency Selection
  * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ * Copyright (c) 2013-2017, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -14,6 +14,9 @@
 int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
 			     int ht_enabled, int chan_offset, int chan_width,
 			     int cf1, int cf2);
+int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
+				int ht_enabled, int chan_offset, int chan_width,
+				int cf1, int cf2);
 int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
 			       int ht_enabled,
 			       int chan_offset, int chan_width,
diff --git a/src/ap/dhcp_snoop.c b/src/ap/dhcp_snoop.c
index b9a36d7..6d8c2f4 100644
--- a/src/ap/dhcp_snoop.c
+++ b/src/ap/dhcp_snoop.c
@@ -154,4 +154,5 @@
 void dhcp_snoop_deinit(struct hostapd_data *hapd)
 {
 	l2_packet_deinit(hapd->sock_dhcp);
+	hapd->sock_dhcp = NULL;
 }
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
new file mode 100644
index 0000000..aae2910
--- /dev/null
+++ b/src/ap/dpp_hostapd.c
@@ -0,0 +1,1483 @@
+/*
+ * hostapd / DPP integration
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/dpp.h"
+#include "common/gas.h"
+#include "common/wpa_ctrl.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "gas_query_ap.h"
+#include "wpa_auth.h"
+#include "dpp_hostapd.h"
+
+
+static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator);
+
+static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+
+static struct dpp_configurator *
+hostapd_dpp_configurator_get_id(struct hostapd_data *hapd, unsigned int id)
+{
+	struct dpp_configurator *conf;
+
+	dl_list_for_each(conf, &hapd->dpp_configurator,
+			 struct dpp_configurator, list) {
+		if (conf->id == id)
+			return conf;
+	}
+	return NULL;
+}
+
+
+static unsigned int hapd_dpp_next_id(struct hostapd_data *hapd)
+{
+	struct dpp_bootstrap_info *bi;
+	unsigned int max_id = 0;
+
+	dl_list_for_each(bi, &hapd->dpp_bootstrap, struct dpp_bootstrap_info,
+			 list) {
+		if (bi->id > max_id)
+			max_id = bi->id;
+	}
+	return max_id + 1;
+}
+
+
+/**
+ * hostapd_dpp_qr_code - Parse and add DPP bootstrapping info from a QR Code
+ * @hapd: Pointer to hostapd_data
+ * @cmd: DPP URI read from a QR Code
+ * Returns: Identifier of the stored info or -1 on failure
+ */
+int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd)
+{
+	struct dpp_bootstrap_info *bi;
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
+	bi = dpp_parse_qr_code(cmd);
+	if (!bi)
+		return -1;
+
+	bi->id = hapd_dpp_next_id(hapd);
+	dl_list_add(&hapd->dpp_bootstrap, &bi->list);
+
+	if (auth && auth->response_pending &&
+	    dpp_notify_new_qr_code(auth, bi) == 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Sending out pending authentication response");
+		hostapd_drv_send_action(hapd, auth->curr_freq, 0,
+					auth->peer_mac_addr,
+					wpabuf_head(hapd->dpp_auth->resp_msg),
+					wpabuf_len(hapd->dpp_auth->resp_msg));
+	}
+
+	return bi->id;
+}
+
+
+static char * get_param(const char *cmd, const char *param)
+{
+	const char *pos, *end;
+	char *val;
+	size_t len;
+
+	pos = os_strstr(cmd, param);
+	if (!pos)
+		return NULL;
+
+	pos += os_strlen(param);
+	end = os_strchr(pos, ' ');
+	if (end)
+		len = end - pos;
+	else
+		len = os_strlen(pos);
+	val = os_malloc(len + 1);
+	if (!val)
+		return NULL;
+	os_memcpy(val, pos, len);
+	val[len] = '\0';
+	return val;
+}
+
+
+int hostapd_dpp_bootstrap_gen(struct hostapd_data *hapd, const char *cmd)
+{
+	char *chan = NULL, *mac = NULL, *info = NULL, *pk = NULL, *curve = NULL;
+	char *key = NULL;
+	u8 *privkey = NULL;
+	size_t privkey_len = 0;
+	size_t len;
+	int ret = -1;
+	struct dpp_bootstrap_info *bi;
+
+	bi = os_zalloc(sizeof(*bi));
+	if (!bi)
+		goto fail;
+
+	if (os_strstr(cmd, "type=qrcode"))
+		bi->type = DPP_BOOTSTRAP_QR_CODE;
+	else if (os_strstr(cmd, "type=pkex"))
+		bi->type = DPP_BOOTSTRAP_PKEX;
+	else
+		goto fail;
+
+	chan = get_param(cmd, " chan=");
+	mac = get_param(cmd, " mac=");
+	info = get_param(cmd, " info=");
+	curve = get_param(cmd, " curve=");
+	key = get_param(cmd, " key=");
+
+	if (key) {
+		privkey_len = os_strlen(key) / 2;
+		privkey = os_malloc(privkey_len);
+		if (!privkey ||
+		    hexstr2bin(key, privkey, privkey_len) < 0)
+			goto fail;
+	}
+
+	pk = dpp_keygen(bi, curve, privkey, privkey_len);
+	if (!pk)
+		goto fail;
+
+	len = 4; /* "DPP:" */
+	if (chan) {
+		if (dpp_parse_uri_chan_list(bi, chan) < 0)
+			goto fail;
+		len += 3 + os_strlen(chan); /* C:...; */
+	}
+	if (mac) {
+		if (dpp_parse_uri_mac(bi, mac) < 0)
+			goto fail;
+		len += 3 + os_strlen(mac); /* M:...; */
+	}
+	if (info) {
+		if (dpp_parse_uri_info(bi, info) < 0)
+			goto fail;
+		len += 3 + os_strlen(info); /* I:...; */
+	}
+	len += 4 + os_strlen(pk);
+	bi->uri = os_malloc(len + 1);
+	if (!bi->uri)
+		goto fail;
+	os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%sK:%s;;",
+		    chan ? "C:" : "", chan ? chan : "", chan ? ";" : "",
+		    mac ? "M:" : "", mac ? mac : "", mac ? ";" : "",
+		    info ? "I:" : "", info ? info : "", info ? ";" : "",
+		    pk);
+	bi->id = hapd_dpp_next_id(hapd);
+	dl_list_add(&hapd->dpp_bootstrap, &bi->list);
+	ret = bi->id;
+	bi = NULL;
+fail:
+	os_free(curve);
+	os_free(pk);
+	os_free(chan);
+	os_free(mac);
+	os_free(info);
+	str_clear_free(key);
+	bin_clear_free(privkey, privkey_len);
+	dpp_bootstrap_info_free(bi);
+	return ret;
+}
+
+
+static struct dpp_bootstrap_info *
+dpp_bootstrap_get_id(struct hostapd_data *hapd, unsigned int id)
+{
+	struct dpp_bootstrap_info *bi;
+
+	dl_list_for_each(bi, &hapd->dpp_bootstrap, struct dpp_bootstrap_info,
+			 list) {
+		if (bi->id == id)
+			return bi;
+	}
+	return NULL;
+}
+
+
+static int dpp_bootstrap_del(struct hostapd_data *hapd, unsigned int id)
+{
+	struct dpp_bootstrap_info *bi, *tmp;
+	int found = 0;
+
+	dl_list_for_each_safe(bi, tmp, &hapd->dpp_bootstrap,
+			      struct dpp_bootstrap_info, list) {
+		if (id && bi->id != id)
+			continue;
+		found = 1;
+		dl_list_del(&bi->list);
+		dpp_bootstrap_info_free(bi);
+	}
+
+	if (id == 0)
+		return 0; /* flush succeeds regardless of entries found */
+	return found ? 0 : -1;
+}
+
+
+int hostapd_dpp_bootstrap_remove(struct hostapd_data *hapd, const char *id)
+{
+	unsigned int id_val;
+
+	if (os_strcmp(id, "*") == 0) {
+		id_val = 0;
+	} else {
+		id_val = atoi(id);
+		if (id_val == 0)
+			return -1;
+	}
+
+	return dpp_bootstrap_del(hapd, id_val);
+}
+
+
+const char * hostapd_dpp_bootstrap_get_uri(struct hostapd_data *hapd,
+					   unsigned int id)
+{
+	struct dpp_bootstrap_info *bi;
+
+	bi = dpp_bootstrap_get_id(hapd, id);
+	if (!bi)
+		return NULL;
+	return bi->uri;
+}
+
+
+int hostapd_dpp_bootstrap_info(struct hostapd_data *hapd, int id,
+			       char *reply, int reply_size)
+{
+	struct dpp_bootstrap_info *bi;
+
+	bi = dpp_bootstrap_get_id(hapd, id);
+	if (!bi)
+		return -1;
+	return os_snprintf(reply, reply_size, "type=%s\n"
+			   "mac_addr=" MACSTR "\n"
+			   "info=%s\n"
+			   "num_freq=%u\n"
+			   "curve=%s\n",
+			   dpp_bootstrap_type_txt(bi->type),
+			   MAC2STR(bi->mac_addr),
+			   bi->info ? bi->info : "",
+			   bi->num_freq,
+			   bi->curve->name);
+}
+
+
+void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
+			   const u8 *data, size_t data_len, int ok)
+{
+	wpa_printf(MSG_DEBUG, "DPP: TX status: dst=" MACSTR " ok=%d",
+		   MAC2STR(dst), ok);
+
+	if (!hapd->dpp_auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore TX status since there is no ongoing authentication exchange");
+		return;
+	}
+
+	if (hapd->dpp_auth->remove_on_tx_status) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Terminate authentication exchange due to an earlier error");
+		dpp_auth_deinit(hapd->dpp_auth);
+		hapd->dpp_auth = NULL;
+		return;
+	}
+
+	if (hapd->dpp_auth_ok_on_ack)
+		hostapd_dpp_auth_success(hapd, 1);
+}
+
+
+static void hostapd_dpp_set_testing_options(struct hostapd_data *hapd,
+					    struct dpp_authentication *auth)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->dpp_config_obj_override)
+		auth->config_obj_override =
+			os_strdup(hapd->dpp_config_obj_override);
+	if (hapd->dpp_discovery_override)
+		auth->discovery_override =
+			os_strdup(hapd->dpp_discovery_override);
+	if (hapd->dpp_groups_override)
+		auth->groups_override = os_strdup(hapd->dpp_groups_override);
+	auth->ignore_netaccesskey_mismatch =
+		hapd->dpp_ignore_netaccesskey_mismatch;
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
+static void hostapd_dpp_set_configurator(struct hostapd_data *hapd,
+					 struct dpp_authentication *auth,
+					 const char *cmd)
+{
+	const char *pos, *end;
+	struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL;
+	struct dpp_configurator *conf = NULL;
+	u8 ssid[32] = { "test" };
+	size_t ssid_len = 4;
+	char pass[64] = { };
+	size_t pass_len = 0;
+	u8 psk[PMK_LEN];
+	int psk_set = 0;
+
+	if (!cmd)
+		return;
+
+	wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd);
+	pos = os_strstr(cmd, " ssid=");
+	if (pos) {
+		pos += 6;
+		end = os_strchr(pos, ' ');
+		ssid_len = end ? (size_t) (end - pos) : os_strlen(pos);
+		ssid_len /= 2;
+		if (ssid_len > sizeof(ssid) ||
+		    hexstr2bin(pos, ssid, ssid_len) < 0)
+			goto fail;
+	}
+
+	pos = os_strstr(cmd, " pass=");
+	if (pos) {
+		pos += 6;
+		end = os_strchr(pos, ' ');
+		pass_len = end ? (size_t) (end - pos) : os_strlen(pos);
+		pass_len /= 2;
+		if (pass_len > sizeof(pass) - 1 || pass_len < 8 ||
+		    hexstr2bin(pos, (u8 *) pass, pass_len) < 0)
+			goto fail;
+	}
+
+	pos = os_strstr(cmd, " psk=");
+	if (pos) {
+		pos += 5;
+		if (hexstr2bin(pos, psk, PMK_LEN) < 0)
+			goto fail;
+		psk_set = 1;
+	}
+
+	if (os_strstr(cmd, " conf=sta-")) {
+		conf_sta = os_zalloc(sizeof(struct dpp_configuration));
+		if (!conf_sta)
+			goto fail;
+		os_memcpy(conf_sta->ssid, ssid, ssid_len);
+		conf_sta->ssid_len = ssid_len;
+		if (os_strstr(cmd, " conf=sta-psk")) {
+			conf_sta->dpp = 0;
+			if (psk_set) {
+				os_memcpy(conf_sta->psk, psk, PMK_LEN);
+			} else {
+				conf_sta->passphrase = os_strdup(pass);
+				if (!conf_sta->passphrase)
+					goto fail;
+			}
+		} else if (os_strstr(cmd, " conf=sta-dpp")) {
+			conf_sta->dpp = 1;
+		} else {
+			goto fail;
+		}
+	}
+
+	if (os_strstr(cmd, " conf=ap-")) {
+		conf_ap = os_zalloc(sizeof(struct dpp_configuration));
+		if (!conf_ap)
+			goto fail;
+		os_memcpy(conf_ap->ssid, ssid, ssid_len);
+		conf_ap->ssid_len = ssid_len;
+		if (os_strstr(cmd, " conf=ap-psk")) {
+			conf_ap->dpp = 0;
+			if (psk_set) {
+				os_memcpy(conf_ap->psk, psk, PMK_LEN);
+			} else {
+				conf_ap->passphrase = os_strdup(pass);
+				if (!conf_ap->passphrase)
+					goto fail;
+			}
+		} else if (os_strstr(cmd, " conf=ap-dpp")) {
+			conf_ap->dpp = 1;
+		} else {
+			goto fail;
+		}
+	}
+
+	pos = os_strstr(cmd, " expiry=");
+	if (pos) {
+		long int val;
+
+		pos += 8;
+		val = strtol(pos, NULL, 0);
+		if (val <= 0)
+			goto fail;
+		if (conf_sta)
+			conf_sta->netaccesskey_expiry = val;
+		if (conf_ap)
+			conf_ap->netaccesskey_expiry = val;
+	}
+
+	pos = os_strstr(cmd, " configurator=");
+	if (pos) {
+		auth->configurator = 1;
+		pos += 14;
+		conf = hostapd_dpp_configurator_get_id(hapd, atoi(pos));
+		if (!conf) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Could not find the specified configurator");
+			goto fail;
+		}
+	}
+	auth->conf_sta = conf_sta;
+	auth->conf_ap = conf_ap;
+	auth->conf = conf;
+	return;
+
+fail:
+	wpa_printf(MSG_DEBUG, "DPP: Failed to set configurator parameters");
+	dpp_configuration_free(conf_sta);
+	dpp_configuration_free(conf_ap);
+}
+
+
+int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd)
+{
+	const char *pos;
+	struct dpp_bootstrap_info *peer_bi, *own_bi = NULL;
+	const u8 *dst;
+	int res;
+	int configurator = 1;
+	struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL;
+
+	pos = os_strstr(cmd, " peer=");
+	if (!pos)
+		return -1;
+	pos += 6;
+	peer_bi = dpp_bootstrap_get_id(hapd, atoi(pos));
+	if (!peer_bi) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Could not find bootstrapping info for the identified peer");
+		return -1;
+	}
+
+	pos = os_strstr(cmd, " own=");
+	if (pos) {
+		pos += 5;
+		own_bi = dpp_bootstrap_get_id(hapd, atoi(pos));
+		if (!own_bi) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Could not find bootstrapping info for the identified local entry");
+			return -1;
+		}
+
+		if (peer_bi->curve != own_bi->curve) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Mismatching curves in bootstrapping info (peer=%s own=%s)",
+				   peer_bi->curve->name, own_bi->curve->name);
+			return -1;
+		}
+	}
+
+	pos = os_strstr(cmd, " role=");
+	if (pos) {
+		pos += 6;
+		if (os_strncmp(pos, "configurator", 12) == 0)
+			configurator = 1;
+		else if (os_strncmp(pos, "enrollee", 8) == 0)
+			configurator = 0;
+		else
+			goto fail;
+	}
+
+	if (hapd->dpp_auth)
+		dpp_auth_deinit(hapd->dpp_auth);
+	hapd->dpp_auth = dpp_auth_init(hapd, peer_bi, own_bi, configurator);
+	if (!hapd->dpp_auth)
+		goto fail;
+	hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth);
+	hostapd_dpp_set_configurator(hapd, hapd->dpp_auth, cmd);
+
+	/* TODO: Support iteration over all frequencies and filtering of
+	 * frequencies based on locally enabled channels that allow initiation
+	 * of transmission. */
+	if (peer_bi->num_freq > 0)
+		hapd->dpp_auth->curr_freq = peer_bi->freq[0];
+	else
+		hapd->dpp_auth->curr_freq = 2412;
+
+	if (is_zero_ether_addr(peer_bi->mac_addr)) {
+		dst = broadcast;
+	} else {
+		dst = peer_bi->mac_addr;
+		os_memcpy(hapd->dpp_auth->peer_mac_addr, peer_bi->mac_addr,
+			  ETH_ALEN);
+	}
+	hapd->dpp_auth_ok_on_ack = 0;
+
+	res = hostapd_drv_send_action(hapd, hapd->dpp_auth->curr_freq, 0,
+				      dst, wpabuf_head(hapd->dpp_auth->req_msg),
+				      wpabuf_len(hapd->dpp_auth->req_msg));
+
+	return res;
+fail:
+	dpp_configuration_free(conf_sta);
+	dpp_configuration_free(conf_ap);
+	return -1;
+}
+
+
+static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src,
+				    const u8 *hdr, const u8 *buf, size_t len,
+				    unsigned int freq)
+{
+	const u8 *r_bootstrap, *i_bootstrap, *wrapped_data;
+	u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len;
+	struct dpp_bootstrap_info *bi, *own_bi = NULL, *peer_bi = NULL;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR,
+		   MAC2STR(src));
+
+	wrapped_data = dpp_get_attr(buf, len, DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing required Wrapped data attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped data",
+		    wrapped_data, wrapped_data_len);
+
+	r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+				   &r_bootstrap_len);
+	if (!r_bootstrap || r_bootstrap > wrapped_data ||
+	    r_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+		    r_bootstrap, r_bootstrap_len);
+
+	i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+				   &i_bootstrap_len);
+	if (!i_bootstrap || i_bootstrap > wrapped_data ||
+	    i_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Initiator Bootstrapping Key Hash attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash",
+		    i_bootstrap, i_bootstrap_len);
+
+	/* Try to find own and peer bootstrapping key matches based on the
+	 * received hash values */
+	dl_list_for_each(bi, &hapd->dpp_bootstrap, struct dpp_bootstrap_info,
+			 list) {
+		if (!own_bi && bi->own &&
+		    os_memcmp(bi->pubkey_hash, r_bootstrap,
+			      SHA256_MAC_LEN) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Found matching own bootstrapping information");
+			own_bi = bi;
+		}
+
+		if (!peer_bi && !bi->own &&
+		    os_memcmp(bi->pubkey_hash, i_bootstrap,
+			      SHA256_MAC_LEN) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Found matching peer bootstrapping information");
+			peer_bi = bi;
+		}
+
+		if (own_bi && peer_bi)
+			break;
+	}
+
+	if (!own_bi) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No matching own bootstrapping key found - ignore message");
+		return;
+	}
+
+	if (hapd->dpp_auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Already in DPP authentication exchange - ignore new one");
+		return;
+	}
+
+	hapd->dpp_auth_ok_on_ack = 0;
+	hapd->dpp_auth = dpp_auth_req_rx(hapd->msg_ctx, hapd->dpp_allowed_roles,
+					 hapd->dpp_qr_mutual,
+					 peer_bi, own_bi, freq, hdr, buf,
+					 wrapped_data, wrapped_data_len);
+	if (!hapd->dpp_auth) {
+		wpa_printf(MSG_DEBUG, "DPP: No response generated");
+		return;
+	}
+	hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth);
+	hostapd_dpp_set_configurator(hapd, hapd->dpp_auth,
+				     hapd->dpp_configurator_params);
+	os_memcpy(hapd->dpp_auth->peer_mac_addr, src, ETH_ALEN);
+
+	hostapd_drv_send_action(hapd, hapd->dpp_auth->curr_freq, 0,
+				src, wpabuf_head(hapd->dpp_auth->resp_msg),
+				wpabuf_len(hapd->dpp_auth->resp_msg));
+}
+
+
+static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
+				    enum gas_query_ap_result result,
+				    const struct wpabuf *adv_proto,
+				    const struct wpabuf *resp, u16 status_code)
+{
+	struct hostapd_data *hapd = ctx;
+	const u8 *pos;
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
+	if (!auth || !auth->auth_success) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+		return;
+	}
+	if (!resp || status_code != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "DPP: GAS query did not succeed");
+		goto fail;
+	}
+
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response adv_proto",
+			adv_proto);
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response (GAS response)",
+			resp);
+
+	if (wpabuf_len(adv_proto) != 10 ||
+	    !(pos = wpabuf_head(adv_proto)) ||
+	    pos[0] != WLAN_EID_ADV_PROTO ||
+	    pos[1] != 8 ||
+	    pos[3] != WLAN_EID_VENDOR_SPECIFIC ||
+	    pos[4] != 5 ||
+	    WPA_GET_BE24(&pos[5]) != OUI_WFA ||
+	    pos[8] != 0x1a ||
+	    pos[9] != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Not a DPP Advertisement Protocol ID");
+		goto fail;
+	}
+
+	if (dpp_conf_resp_rx(auth, resp) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
+		goto fail;
+	}
+
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_RECEIVED);
+	if (auth->ssid_len)
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_SSID "%s",
+			wpa_ssid_txt(auth->ssid, auth->ssid_len));
+	if (auth->connector) {
+		/* TODO: Save the Connector and consider using a command
+		 * to fetch the value instead of sending an event with
+		 * it. The Connector could end up being larger than what
+		 * most clients are ready to receive as an event
+		 * message. */
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONNECTOR "%s",
+			auth->connector);
+	} else if (auth->passphrase[0]) {
+		char hex[64 * 2 + 1];
+
+		wpa_snprintf_hex(hex, sizeof(hex),
+				 (const u8 *) auth->passphrase,
+				 os_strlen(auth->passphrase));
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PASS "%s",
+			hex);
+	} else if (auth->psk_set) {
+		char hex[PMK_LEN * 2 + 1];
+
+		wpa_snprintf_hex(hex, sizeof(hex), auth->psk, PMK_LEN);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PSK "%s",
+			hex);
+	}
+	if (auth->c_sign_key) {
+		char *hex;
+		size_t hexlen;
+
+		hexlen = 2 * wpabuf_len(auth->c_sign_key) + 1;
+		hex = os_malloc(hexlen);
+		if (hex) {
+			wpa_snprintf_hex(hex, hexlen,
+					 wpabuf_head(auth->c_sign_key),
+					 wpabuf_len(auth->c_sign_key));
+			wpa_msg(hapd->msg_ctx, MSG_INFO,
+				DPP_EVENT_C_SIGN_KEY "%s", hex);
+			os_free(hex);
+		}
+	}
+	if (auth->net_access_key) {
+		char *hex;
+		size_t hexlen;
+
+		hexlen = 2 * wpabuf_len(auth->net_access_key) + 1;
+		hex = os_malloc(hexlen);
+		if (hex) {
+			wpa_snprintf_hex(hex, hexlen,
+					 wpabuf_head(auth->net_access_key),
+					 wpabuf_len(auth->net_access_key));
+			if (auth->net_access_key_expiry)
+				wpa_msg(hapd->msg_ctx, MSG_INFO,
+					DPP_EVENT_NET_ACCESS_KEY "%s %lu", hex,
+					(unsigned long)
+					auth->net_access_key_expiry);
+			else
+				wpa_msg(hapd->msg_ctx, MSG_INFO,
+					DPP_EVENT_NET_ACCESS_KEY "%s", hex);
+			os_free(hex);
+		}
+	}
+	dpp_auth_deinit(hapd->dpp_auth);
+	hapd->dpp_auth = NULL;
+	return;
+
+fail:
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+	dpp_auth_deinit(hapd->dpp_auth);
+	hapd->dpp_auth = NULL;
+}
+
+
+static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd)
+{
+	struct dpp_authentication *auth = hapd->dpp_auth;
+	struct wpabuf *buf, *conf_req;
+	char json[100];
+	int res;
+	int netrole_ap = 1;
+
+	os_snprintf(json, sizeof(json),
+		    "{\"name\":\"Test\","
+		    "\"wi-fi_tech\":\"infra\","
+		    "\"netRole\":\"%s\"}",
+		    netrole_ap ? "ap" : "sta");
+	wpa_printf(MSG_DEBUG, "DPP: GAS Config Attributes: %s", json);
+
+	conf_req = dpp_build_conf_req(auth, json);
+	if (!conf_req) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No configuration request data available");
+		return;
+	}
+
+	buf = gas_build_initial_req(0, 10 + 2 + wpabuf_len(conf_req));
+	if (!buf) {
+		wpabuf_free(conf_req);
+		return;
+	}
+
+	/* Advertisement Protocol IE */
+	wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
+	wpabuf_put_u8(buf, 8); /* Length */
+	wpabuf_put_u8(buf, 0x7f);
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+	wpabuf_put_u8(buf, 5);
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, DPP_OUI_TYPE);
+	wpabuf_put_u8(buf, 0x01);
+
+	/* GAS Query */
+	wpabuf_put_le16(buf, wpabuf_len(conf_req));
+	wpabuf_put_buf(buf, conf_req);
+	wpabuf_free(conf_req);
+
+	wpa_printf(MSG_DEBUG, "DPP: GAS request to " MACSTR " (freq %u MHz)",
+		   MAC2STR(auth->peer_mac_addr), auth->curr_freq);
+
+	res = gas_query_ap_req(hapd->gas, auth->peer_mac_addr, auth->curr_freq,
+			       buf, hostapd_dpp_gas_resp_cb, hapd);
+	if (res < 0) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+			"GAS: Failed to send Query Request");
+		wpabuf_free(buf);
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: GAS query started with dialog token %u", res);
+	}
+}
+
+
+static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator)
+{
+	wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=%d",
+		initiator);
+
+	if (!hapd->dpp_auth->configurator)
+		hostapd_dpp_start_gas_client(hapd);
+}
+
+
+static void hostapd_dpp_rx_auth_resp(struct hostapd_data *hapd, const u8 *src,
+				     const u8 *hdr, const u8 *buf, size_t len)
+{
+	struct dpp_authentication *auth = hapd->dpp_auth;
+	struct wpabuf *msg;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication Response from " MACSTR,
+		   MAC2STR(src));
+
+	if (!auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Authentication in progress - drop");
+		return;
+	}
+
+	if (!is_zero_ether_addr(auth->peer_mac_addr) &&
+	    os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+			   MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+		return;
+	}
+
+	msg = dpp_auth_resp_rx(auth, hdr, buf, len);
+	if (!msg) {
+		if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
+			wpa_printf(MSG_DEBUG, "DPP: Wait for full response");
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "DPP: No confirm generated");
+		return;
+	}
+	os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
+
+	hostapd_drv_send_action(hapd, auth->curr_freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+	wpabuf_free(msg);
+	hapd->dpp_auth_ok_on_ack = 1;
+}
+
+
+static void hostapd_dpp_rx_auth_conf(struct hostapd_data *hapd, const u8 *src,
+				     const u8 *hdr, const u8 *buf, size_t len)
+{
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation from " MACSTR,
+		   MAC2STR(src));
+
+	if (!auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Authentication in progress - drop");
+		return;
+	}
+
+	if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+			   MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+		return;
+	}
+
+	if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
+		return;
+	}
+
+	hostapd_dpp_auth_success(hapd, 0);
+}
+
+
+static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd,
+					 const u8 *src,
+					 const u8 *buf, size_t len,
+					 unsigned int freq)
+{
+	const u8 *connector, *trans_id;
+	u16 connector_len, trans_id_len;
+	struct os_time now;
+	struct dpp_introduction intro;
+	os_time_t expire;
+	int expiration;
+	struct wpabuf *msg;
+
+	wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Request from " MACSTR,
+		   MAC2STR(src));
+	if (!hapd->wpa_auth ||
+	    !(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) ||
+	    !(hapd->conf->wpa & WPA_PROTO_RSN)) {
+		wpa_printf(MSG_DEBUG, "DPP: DPP AKM not in use");
+		return;
+	}
+
+	if (!hapd->conf->dpp_connector || !hapd->conf->dpp_netaccesskey ||
+	    !hapd->conf->dpp_csign) {
+		wpa_printf(MSG_DEBUG, "DPP: No own Connector/keys set");
+		return;
+	}
+
+	os_get_time(&now);
+
+	if (hapd->conf->dpp_netaccesskey_expiry &&
+	    hapd->conf->dpp_netaccesskey_expiry < now.sec) {
+		wpa_printf(MSG_INFO, "DPP: Own netAccessKey expired");
+		return;
+	}
+
+	trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
+			       &trans_id_len);
+	if (!trans_id || trans_id_len != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include Transaction ID");
+		return;
+	}
+
+	connector = dpp_get_attr(buf, len, DPP_ATTR_CONNECTOR, &connector_len);
+	if (!connector) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include its Connector");
+		return;
+	}
+
+	if (dpp_peer_intro(&intro, hapd->conf->dpp_connector,
+			   wpabuf_head(hapd->conf->dpp_netaccesskey),
+			   wpabuf_len(hapd->conf->dpp_netaccesskey),
+			   wpabuf_head(hapd->conf->dpp_csign),
+			   wpabuf_len(hapd->conf->dpp_csign),
+			   connector, connector_len, &expire) < 0) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Network Introduction protocol resulted in failure");
+		return;
+	}
+
+	if (!expire || hapd->conf->dpp_netaccesskey_expiry < expire)
+		expire = hapd->conf->dpp_netaccesskey_expiry;
+	if (expire)
+		expiration = expire - now.sec;
+	else
+		expiration = 0;
+
+	if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+				intro.pmkid, expiration,
+				WPA_KEY_MGMT_DPP) < 0) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
+		return;
+	}
+
+	msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_RESP,
+			    5 + 4 + os_strlen(hapd->conf->dpp_connector));
+	if (!msg)
+		return;
+
+	/* Transaction ID */
+	wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, trans_id[0]);
+
+	/* DPP Connector */
+	wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+	wpabuf_put_le16(msg, os_strlen(hapd->conf->dpp_connector));
+	wpabuf_put_str(msg, hapd->conf->dpp_connector);
+
+	wpa_printf(MSG_DEBUG, "DPP: Send Peer Discovery Response to " MACSTR,
+		   MAC2STR(src));
+	hostapd_drv_send_action(hapd, freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+	wpabuf_free(msg);
+}
+
+
+static void
+hostapd_dpp_rx_pkex_exchange_req(struct hostapd_data *hapd, const u8 *src,
+				 const u8 *buf, size_t len,
+				 unsigned int freq)
+{
+	struct wpabuf *msg;
+
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request from " MACSTR,
+		   MAC2STR(src));
+
+	/* TODO: Support multiple PKEX codes by iterating over all the enabled
+	 * values here */
+
+	if (!hapd->dpp_pkex_code || !hapd->dpp_pkex_bi) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No PKEX code configured - ignore request");
+		return;
+	}
+
+	if (hapd->dpp_pkex) {
+		/* TODO: Support parallel operations */
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Already in PKEX session - ignore new request");
+		return;
+	}
+
+	hapd->dpp_pkex = dpp_pkex_rx_exchange_req(hapd->dpp_pkex_bi,
+						  hapd->own_addr, src,
+						  hapd->dpp_pkex_identifier,
+						  hapd->dpp_pkex_code,
+						  buf, len);
+	if (!hapd->dpp_pkex) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to process the request - ignore it");
+		return;
+	}
+
+	msg = hapd->dpp_pkex->exchange_resp;
+	hostapd_drv_send_action(hapd, freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+}
+
+
+static void
+hostapd_dpp_rx_pkex_exchange_resp(struct hostapd_data *hapd, const u8 *src,
+				  const u8 *buf, size_t len, unsigned int freq)
+{
+	struct wpabuf *msg;
+
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response from " MACSTR,
+		   MAC2STR(src));
+
+	/* TODO: Support multiple PKEX codes by iterating over all the enabled
+	 * values here */
+
+	if (!hapd->dpp_pkex || !hapd->dpp_pkex->initiator ||
+	    hapd->dpp_pkex->exchange_done) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
+		return;
+	}
+
+	os_memcpy(hapd->dpp_pkex->peer_mac, src, ETH_ALEN);
+	msg = dpp_pkex_rx_exchange_resp(hapd->dpp_pkex, buf, len);
+	if (!msg) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Request to " MACSTR,
+		   MAC2STR(src));
+
+	hostapd_drv_send_action(hapd, freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+	wpabuf_free(msg);
+}
+
+
+static void
+hostapd_dpp_rx_pkex_commit_reveal_req(struct hostapd_data *hapd, const u8 *src,
+				      const u8 *hdr, const u8 *buf, size_t len,
+				      unsigned int freq)
+{
+	struct wpabuf *msg;
+	struct dpp_pkex *pkex = hapd->dpp_pkex;
+	struct dpp_bootstrap_info *bi;
+
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Request from " MACSTR,
+		   MAC2STR(src));
+
+	if (!pkex || pkex->initiator || !pkex->exchange_done) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
+		return;
+	}
+
+	msg = dpp_pkex_rx_commit_reveal_req(pkex, hdr, buf, len);
+	if (!msg) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to process the request");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Response to "
+		   MACSTR, MAC2STR(src));
+
+	hostapd_drv_send_action(hapd, freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+	wpabuf_free(msg);
+
+	bi = os_zalloc(sizeof(*bi));
+	if (!bi)
+		return;
+	bi->id = hapd_dpp_next_id(hapd);
+	bi->type = DPP_BOOTSTRAP_PKEX;
+	os_memcpy(bi->mac_addr, src, ETH_ALEN);
+	bi->num_freq = 1;
+	bi->freq[0] = freq;
+	bi->curve = pkex->own_bi->curve;
+	bi->pubkey = pkex->peer_bootstrap_key;
+	pkex->peer_bootstrap_key = NULL;
+	dpp_pkex_free(pkex);
+	hapd->dpp_pkex = NULL;
+	if (dpp_bootstrap_key_hash(bi) < 0) {
+		dpp_bootstrap_info_free(bi);
+		return;
+	}
+	dl_list_add(&hapd->dpp_bootstrap, &bi->list);
+}
+
+
+static void
+hostapd_dpp_rx_pkex_commit_reveal_resp(struct hostapd_data *hapd, const u8 *src,
+				       const u8 *hdr, const u8 *buf, size_t len,
+				       unsigned int freq)
+{
+	int res;
+	struct dpp_bootstrap_info *bi, *own_bi;
+	struct dpp_pkex *pkex = hapd->dpp_pkex;
+	char cmd[500];
+
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Response from " MACSTR,
+		   MAC2STR(src));
+
+	if (!pkex || !pkex->initiator || !pkex->exchange_done) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
+		return;
+	}
+
+	res = dpp_pkex_rx_commit_reveal_resp(pkex, hdr, buf, len);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
+		return;
+	}
+
+	own_bi = pkex->own_bi;
+
+	bi = os_zalloc(sizeof(*bi));
+	if (!bi)
+		return;
+	bi->id = hapd_dpp_next_id(hapd);
+	bi->type = DPP_BOOTSTRAP_PKEX;
+	os_memcpy(bi->mac_addr, src, ETH_ALEN);
+	bi->num_freq = 1;
+	bi->freq[0] = freq;
+	bi->curve = own_bi->curve;
+	bi->pubkey = pkex->peer_bootstrap_key;
+	pkex->peer_bootstrap_key = NULL;
+	dpp_pkex_free(pkex);
+	hapd->dpp_pkex = NULL;
+	if (dpp_bootstrap_key_hash(bi) < 0) {
+		dpp_bootstrap_info_free(bi);
+		return;
+	}
+	dl_list_add(&hapd->dpp_bootstrap, &bi->list);
+
+	os_snprintf(cmd, sizeof(cmd), " peer=%u %s",
+		    bi->id,
+		    hapd->dpp_pkex_auth_cmd ? hapd->dpp_pkex_auth_cmd : "");
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Start authentication after PKEX with parameters: %s",
+		   cmd);
+	if (hostapd_dpp_auth_init(hapd, cmd) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Authentication initialization failed");
+		return;
+	}
+}
+
+
+void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
+			   const u8 *buf, size_t len, unsigned int freq)
+{
+	u8 crypto_suite;
+	enum dpp_public_action_frame_type type;
+	const u8 *hdr;
+
+	if (len < DPP_HDR_LEN)
+		return;
+	if (WPA_GET_BE24(buf) != OUI_WFA || buf[3] != DPP_OUI_TYPE)
+		return;
+	hdr = buf;
+	buf += 4;
+	len -= 4;
+	crypto_suite = *buf++;
+	type = *buf++;
+	len -= 2;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Received DPP Public Action frame crypto suite %u type %d from "
+		   MACSTR " freq=%u",
+		   crypto_suite, type, MAC2STR(src), freq);
+	if (crypto_suite != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: Unsupported crypto suite %u",
+			   crypto_suite);
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes", buf, len);
+	if (dpp_check_attrs(buf, len) < 0)
+		return;
+
+	switch (type) {
+	case DPP_PA_AUTHENTICATION_REQ:
+		hostapd_dpp_rx_auth_req(hapd, src, hdr, buf, len, freq);
+		break;
+	case DPP_PA_AUTHENTICATION_RESP:
+		hostapd_dpp_rx_auth_resp(hapd, src, hdr, buf, len);
+		break;
+	case DPP_PA_AUTHENTICATION_CONF:
+		hostapd_dpp_rx_auth_conf(hapd, src, hdr, buf, len);
+		break;
+	case DPP_PA_PEER_DISCOVERY_REQ:
+		hostapd_dpp_rx_peer_disc_req(hapd, src, buf, len, freq);
+		break;
+	case DPP_PA_PKEX_EXCHANGE_REQ:
+		hostapd_dpp_rx_pkex_exchange_req(hapd, src, buf, len, freq);
+		break;
+	case DPP_PA_PKEX_EXCHANGE_RESP:
+		hostapd_dpp_rx_pkex_exchange_resp(hapd, src, buf, len, freq);
+		break;
+	case DPP_PA_PKEX_COMMIT_REVEAL_REQ:
+		hostapd_dpp_rx_pkex_commit_reveal_req(hapd, src, hdr, buf, len,
+						      freq);
+		break;
+	case DPP_PA_PKEX_COMMIT_REVEAL_RESP:
+		hostapd_dpp_rx_pkex_commit_reveal_resp(hapd, src, hdr, buf, len,
+						       freq);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignored unsupported frame subtype %d", type);
+		break;
+	}
+}
+
+
+struct wpabuf *
+hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
+			    const u8 *query, size_t query_len)
+{
+	struct dpp_authentication *auth = hapd->dpp_auth;
+	struct wpabuf *resp;
+
+	wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa));
+	if (!auth || !auth->auth_success ||
+	    os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+		return NULL;
+	}
+	wpa_hexdump(MSG_DEBUG,
+		    "DPP: Received Configuration Request (GAS Query Request)",
+		    query, query_len);
+	resp = dpp_conf_req_rx(auth, query, query_len);
+	if (!resp)
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+	return resp;
+}
+
+
+static unsigned int hostapd_dpp_next_configurator_id(struct hostapd_data *hapd)
+{
+	struct dpp_configurator *conf;
+	unsigned int max_id = 0;
+
+	dl_list_for_each(conf, &hapd->dpp_configurator,
+			 struct dpp_configurator, list) {
+		if (conf->id > max_id)
+			max_id = conf->id;
+	}
+	return max_id + 1;
+}
+
+
+int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd)
+{
+	char *curve = NULL;
+	char *key = NULL;
+	u8 *privkey = NULL;
+	size_t privkey_len = 0;
+	int ret = -1;
+	struct dpp_configurator *conf = NULL;
+
+	curve = get_param(cmd, " curve=");
+	key = get_param(cmd, " key=");
+
+	if (key) {
+		privkey_len = os_strlen(key) / 2;
+		privkey = os_malloc(privkey_len);
+		if (!privkey ||
+		    hexstr2bin(key, privkey, privkey_len) < 0)
+			goto fail;
+	}
+
+	conf = dpp_keygen_configurator(curve, privkey, privkey_len);
+	if (!conf)
+		goto fail;
+
+	conf->id = hostapd_dpp_next_configurator_id(hapd);
+	dl_list_add(&hapd->dpp_configurator, &conf->list);
+	ret = conf->id;
+	conf = NULL;
+fail:
+	os_free(curve);
+	str_clear_free(key);
+	bin_clear_free(privkey, privkey_len);
+	dpp_configurator_free(conf);
+	return ret;
+}
+
+
+static int dpp_configurator_del(struct hostapd_data *hapd, unsigned int id)
+{
+	struct dpp_configurator *conf, *tmp;
+	int found = 0;
+
+	dl_list_for_each_safe(conf, tmp, &hapd->dpp_configurator,
+			      struct dpp_configurator, list) {
+		if (id && conf->id != id)
+			continue;
+		found = 1;
+		dl_list_del(&conf->list);
+		dpp_configurator_free(conf);
+	}
+
+	if (id == 0)
+		return 0; /* flush succeeds regardless of entries found */
+	return found ? 0 : -1;
+}
+
+
+int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id)
+{
+	unsigned int id_val;
+
+	if (os_strcmp(id, "*") == 0) {
+		id_val = 0;
+	} else {
+		id_val = atoi(id);
+		if (id_val == 0)
+			return -1;
+	}
+
+	return dpp_configurator_del(hapd, id_val);
+}
+
+
+int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd)
+{
+	struct dpp_bootstrap_info *own_bi;
+	const char *pos, *end;
+
+	pos = os_strstr(cmd, " own=");
+	if (!pos)
+		return -1;
+	pos += 5;
+	own_bi = dpp_bootstrap_get_id(hapd, atoi(pos));
+	if (!own_bi) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Identified bootstrap info not found");
+		return -1;
+	}
+	if (own_bi->type != DPP_BOOTSTRAP_PKEX) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Identified bootstrap info not for PKEX");
+		return -1;
+	}
+	hapd->dpp_pkex_bi = own_bi;
+
+	os_free(hapd->dpp_pkex_identifier);
+	hapd->dpp_pkex_identifier = NULL;
+	pos = os_strstr(cmd, " identifier=");
+	if (pos) {
+		pos += 12;
+		end = os_strchr(pos, ' ');
+		if (!end)
+			return -1;
+		hapd->dpp_pkex_identifier = os_malloc(end - pos + 1);
+		if (!hapd->dpp_pkex_identifier)
+			return -1;
+		os_memcpy(hapd->dpp_pkex_identifier, pos, end - pos);
+		hapd->dpp_pkex_identifier[end - pos] = '\0';
+	}
+
+	pos = os_strstr(cmd, " code=");
+	if (!pos)
+		return -1;
+	os_free(hapd->dpp_pkex_code);
+	hapd->dpp_pkex_code = os_strdup(pos + 6);
+	if (!hapd->dpp_pkex_code)
+		return -1;
+
+	if (os_strstr(cmd, " init=1")) {
+		struct wpabuf *msg;
+
+		wpa_printf(MSG_DEBUG, "DPP: Initiating PKEX");
+		dpp_pkex_free(hapd->dpp_pkex);
+		hapd->dpp_pkex = dpp_pkex_init(own_bi, hapd->own_addr,
+					       hapd->dpp_pkex_identifier,
+					       hapd->dpp_pkex_code);
+		if (!hapd->dpp_pkex)
+			return -1;
+
+		msg = hapd->dpp_pkex->exchange_req;
+		/* TODO: Which channel to use? */
+		hostapd_drv_send_action(hapd, 2437, 0, broadcast,
+					wpabuf_head(msg), wpabuf_len(msg));
+	}
+
+	/* TODO: Support multiple PKEX info entries */
+
+	os_free(hapd->dpp_pkex_auth_cmd);
+	hapd->dpp_pkex_auth_cmd = os_strdup(cmd);
+
+	return 1;
+}
+
+
+int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id)
+{
+	unsigned int id_val;
+
+	if (os_strcmp(id, "*") == 0) {
+		id_val = 0;
+	} else {
+		id_val = atoi(id);
+		if (id_val == 0)
+			return -1;
+	}
+
+	if ((id_val != 0 && id_val != 1) || !hapd->dpp_pkex_code)
+		return -1;
+
+	/* TODO: Support multiple PKEX entries */
+	os_free(hapd->dpp_pkex_code);
+	hapd->dpp_pkex_code = NULL;
+	os_free(hapd->dpp_pkex_identifier);
+	hapd->dpp_pkex_identifier = NULL;
+	os_free(hapd->dpp_pkex_auth_cmd);
+	hapd->dpp_pkex_auth_cmd = NULL;
+	hapd->dpp_pkex_bi = NULL;
+	/* TODO: Remove dpp_pkex only if it is for the identified PKEX code */
+	dpp_pkex_free(hapd->dpp_pkex);
+	hapd->dpp_pkex = NULL;
+	return 0;
+}
+
+
+int hostapd_dpp_init(struct hostapd_data *hapd)
+{
+	dl_list_init(&hapd->dpp_bootstrap);
+	dl_list_init(&hapd->dpp_configurator);
+	hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE;
+	hapd->dpp_init_done = 1;
+	return 0;
+}
+
+
+void hostapd_dpp_deinit(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	os_free(hapd->dpp_config_obj_override);
+	hapd->dpp_config_obj_override = NULL;
+	os_free(hapd->dpp_discovery_override);
+	hapd->dpp_discovery_override = NULL;
+	os_free(hapd->dpp_groups_override);
+	hapd->dpp_groups_override = NULL;
+	hapd->dpp_ignore_netaccesskey_mismatch = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (!hapd->dpp_init_done)
+		return;
+	dpp_bootstrap_del(hapd, 0);
+	dpp_configurator_del(hapd, 0);
+	dpp_auth_deinit(hapd->dpp_auth);
+	hapd->dpp_auth = NULL;
+	hostapd_dpp_pkex_remove(hapd, "*");
+	hapd->dpp_pkex = NULL;
+	os_free(hapd->dpp_configurator_params);
+	hapd->dpp_configurator_params = NULL;
+}
diff --git a/src/ap/dpp_hostapd.h b/src/ap/dpp_hostapd.h
new file mode 100644
index 0000000..d870b20
--- /dev/null
+++ b/src/ap/dpp_hostapd.h
@@ -0,0 +1,34 @@
+/*
+ * hostapd / DPP integration
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DPP_HOSTAPD_H
+#define DPP_HOSTAPD_H
+
+int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_bootstrap_gen(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_bootstrap_remove(struct hostapd_data *hapd, const char *id);
+const char * hostapd_dpp_bootstrap_get_uri(struct hostapd_data *hapd,
+					   unsigned int id);
+int hostapd_dpp_bootstrap_info(struct hostapd_data *hapd, int id,
+			       char *reply, int reply_size);
+int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd);
+void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
+			   const u8 *buf, size_t len, unsigned int freq);
+void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
+			   const u8 *data, size_t data_len, int ok);
+struct wpabuf *
+hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
+			    const u8 *query, size_t query_len);
+int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id);
+int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id);
+int hostapd_dpp_init(struct hostapd_data *hapd);
+void hostapd_dpp_deinit(struct hostapd_data *hapd);
+
+#endif /* DPP_HOSTAPD_H */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index f69c655..648f20e 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -31,10 +31,74 @@
 #include "wps_hostapd.h"
 #include "ap_drv_ops.h"
 #include "ap_config.h"
+#include "ap_mlme.h"
 #include "hw_features.h"
 #include "dfs.h"
 #include "beacon.h"
 #include "mbo_ap.h"
+#include "dpp_hostapd.h"
+#include "fils_hlp.h"
+
+
+#ifdef CONFIG_FILS
+void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd,
+				      struct sta_info *sta)
+{
+	u16 reply_res = WLAN_STATUS_SUCCESS;
+	struct ieee802_11_elems elems;
+	u8 buf[IEEE80211_MAX_MMPDU_SIZE], *p = buf;
+	int new_assoc;
+
+	wpa_printf(MSG_DEBUG, "%s FILS: Finish association with " MACSTR,
+		   __func__, MAC2STR(sta->addr));
+	eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+	if (!sta->fils_pending_assoc_req)
+		return;
+
+	ieee802_11_parse_elems(sta->fils_pending_assoc_req,
+			       sta->fils_pending_assoc_req_len, &elems, 0);
+	if (!elems.fils_session) {
+		wpa_printf(MSG_DEBUG, "%s failed to find FILS Session element",
+			   __func__);
+		return;
+	}
+
+	p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p,
+					   elems.fils_session,
+					   sta->fils_hlp_resp);
+
+	reply_res = hostapd_sta_assoc(hapd, sta->addr,
+				      sta->fils_pending_assoc_is_reassoc,
+				      WLAN_STATUS_SUCCESS,
+				      buf, p - buf);
+	ap_sta_set_authorized(hapd, sta, 1);
+	new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
+	sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
+	sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+	hostapd_set_sta_flags(hapd, sta);
+	wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS);
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
+	hostapd_new_assoc_sta(hapd, sta, !new_assoc);
+	os_free(sta->fils_pending_assoc_req);
+	sta->fils_pending_assoc_req = NULL;
+	sta->fils_pending_assoc_req_len = 0;
+	wpabuf_free(sta->fils_hlp_resp);
+	sta->fils_hlp_resp = NULL;
+	wpabuf_free(sta->hlp_dhcp_discover);
+	sta->hlp_dhcp_discover = NULL;
+	fils_hlp_deinit(hapd);
+
+	/*
+	 * Remove the station in case transmission of a success response fails
+	 * (the STA was added associated to the driver) or if the station was
+	 * previously added unassociated.
+	 */
+	if (reply_res != WLAN_STATUS_SUCCESS || sta->added_unassoc) {
+		hostapd_drv_sta_remove(hapd, sta->addr);
+		sta->added_unassoc = 0;
+	}
+}
+#endif /* CONFIG_FILS */
 
 
 int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
@@ -45,10 +109,10 @@
 	struct ieee802_11_elems elems;
 	const u8 *ie;
 	size_t ielen;
-#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_IEEE80211W)
+#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS)
 	u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
 	u8 *p = buf;
-#endif /* CONFIG_IEEE80211R_AP || CONFIG_IEEE80211W */
+#endif /* CONFIG_IEEE80211R_AP || CONFIG_IEEE80211W || CONFIG_FILS */
 	u16 reason = WLAN_REASON_UNSPECIFIED;
 	u16 status = WLAN_STATUS_SUCCESS;
 	const u8 *p2p_dev_addr = NULL;
@@ -231,7 +295,8 @@
 		}
 		res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
 					  ie, ielen,
-					  elems.mdie, elems.mdie_len);
+					  elems.mdie, elems.mdie_len,
+					  elems.owe_dh, elems.owe_dh_len);
 		if (res != WPA_IE_OK) {
 			wpa_printf(MSG_DEBUG,
 				   "WPA/RSN information element rejected? (res %u)",
@@ -378,16 +443,96 @@
 #ifdef CONFIG_IEEE80211R_AP
 	p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf),
 					sta->auth_alg, req_ies, req_ies_len);
+#endif /* CONFIG_IEEE80211R_AP */
 
+#ifdef CONFIG_FILS
+	if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	    sta->auth_alg == WLAN_AUTH_FILS_PK) {
+		int delay_assoc = 0;
+
+		if (!req_ies)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+		if (!wpa_fils_validate_fils_session(sta->wpa_sm, req_ies,
+						    req_ies_len,
+						    sta->fils_session)) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Session validation failed");
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		}
+
+		res = wpa_fils_validate_key_confirm(sta->wpa_sm, req_ies,
+						    req_ies_len);
+		if (res < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Key Confirm validation failed");
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		}
+
+		if (fils_process_hlp(hapd, sta, req_ies, req_ies_len) > 0) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Delaying Assoc Response (HLP)");
+			delay_assoc = 1;
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Going ahead with Assoc Response (no HLP)");
+		}
+
+		if (sta) {
+			wpa_printf(MSG_DEBUG, "FILS: HLP callback cleanup");
+			eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+			os_free(sta->fils_pending_assoc_req);
+			sta->fils_pending_assoc_req = NULL;
+			sta->fils_pending_assoc_req_len = 0;
+			wpabuf_free(sta->fils_hlp_resp);
+			sta->fils_hlp_resp = NULL;
+			sta->fils_drv_assoc_finish = 0;
+		}
+
+		if (sta && delay_assoc && status == WLAN_STATUS_SUCCESS) {
+			u8 *req_tmp;
+
+			req_tmp = os_malloc(req_ies_len);
+			if (!req_tmp) {
+				wpa_printf(MSG_DEBUG,
+					   "FILS: buffer allocation failed for assoc req");
+				goto fail;
+			}
+			os_memcpy(req_tmp, req_ies, req_ies_len);
+			sta->fils_pending_assoc_req = req_tmp;
+			sta->fils_pending_assoc_req_len = req_ies_len;
+			sta->fils_pending_assoc_is_reassoc = reassoc;
+			sta->fils_drv_assoc_finish = 1;
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Waiting for HLP processing before sending (Re)Association Response frame to "
+				   MACSTR, MAC2STR(sta->addr));
+			eloop_register_timeout(
+				0, hapd->conf->fils_hlp_wait_time * 1024,
+				fils_hlp_timeout, hapd, sta);
+			return 0;
+		}
+		p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p,
+						   elems.fils_session,
+						   sta->fils_hlp_resp);
+		wpa_hexdump(MSG_DEBUG, "FILS Assoc Resp BUF (IEs)",
+			    buf, p - buf);
+	}
+#endif /* CONFIG_FILS */
+
+#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_FILS)
 	hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
 
-	if (sta->auth_alg == WLAN_AUTH_FT)
+	if (sta->auth_alg == WLAN_AUTH_FT ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	    sta->auth_alg == WLAN_AUTH_FILS_PK)
 		ap_sta_set_authorized(hapd, sta, 1);
-#else /* CONFIG_IEEE80211R_AP */
+#else /* CONFIG_IEEE80211R_AP || CONFIG_FILS */
 	/* Keep compiler silent about unused variables */
 	if (status) {
 	}
-#endif /* CONFIG_IEEE80211R_AP */
+#endif /* CONFIG_IEEE80211R_AP || CONFIG_FILS */
 
 	new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
 	sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
@@ -397,6 +542,12 @@
 
 	if (reassoc && (sta->auth_alg == WLAN_AUTH_FT))
 		wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
+#ifdef CONFIG_FILS
+	else if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+		 sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+		 sta->auth_alg == WLAN_AUTH_FILS_PK)
+		wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS);
+#endif /* CONFIG_FILS */
 	else
 		wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
 
@@ -711,6 +862,32 @@
 #endif /* CONFIG_IEEE80211R_AP */
 
 
+#ifdef CONFIG_FILS
+static void hostapd_notify_auth_fils_finish(struct hostapd_data *hapd,
+					    struct sta_info *sta, u16 resp,
+					    struct wpabuf *data, int pub)
+{
+	if (resp == WLAN_STATUS_SUCCESS) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG, "authentication OK (FILS)");
+		sta->flags |= WLAN_STA_AUTH;
+		wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+		sta->auth_alg = WLAN_AUTH_FILS_SK;
+		mlme_authenticate_indication(hapd, sta);
+	} else {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "authentication failed (FILS)");
+	}
+
+	hostapd_sta_auth(hapd, sta->addr, 2, resp,
+			 data ? wpabuf_head(data) : NULL,
+			 data ? wpabuf_len(data) : 0);
+	wpabuf_free(data);
+}
+#endif /* CONFIG_FILS */
+
+
 static void hostapd_notif_auth(struct hostapd_data *hapd,
 			       struct auth_info *rx_auth)
 {
@@ -748,6 +925,18 @@
 		return;
 	}
 #endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_FILS
+	if (rx_auth->auth_type == WLAN_AUTH_FILS_SK) {
+		sta->auth_alg = WLAN_AUTH_FILS_SK;
+		handle_auth_fils(hapd, sta, rx_auth->ies, rx_auth->ies_len,
+				 rx_auth->auth_type, rx_auth->auth_transaction,
+				 rx_auth->status_code,
+				 hostapd_notify_auth_fils_finish);
+		return;
+	}
+#endif /* CONFIG_FILS */
+
 fail:
 	hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1,
 			 status, resp_ies, resp_ies_len);
@@ -795,18 +984,34 @@
 			mgmt->u.action.u.sa_query_resp.trans_id);
 	}
 #endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
 	if (mgmt->u.action.category == WLAN_ACTION_WNM) {
 		ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len);
 	}
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
 #ifdef CONFIG_FST
 	if (mgmt->u.action.category == WLAN_ACTION_FST && hapd->iface->fst) {
 		fst_rx_action(hapd->iface->fst, mgmt, drv_mgmt->frame_len);
 		return;
 	}
 #endif /* CONFIG_FST */
+#ifdef CONFIG_DPP
+	if (plen >= 1 + 4 &&
+	    mgmt->u.action.u.vs_public_action.action ==
+	    WLAN_PA_VENDOR_SPECIFIC &&
+	    WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
+	    OUI_WFA &&
+	    mgmt->u.action.u.vs_public_action.variable[0] ==
+	    DPP_OUI_TYPE) {
+		const u8 *pos, *end;
 
+		pos = mgmt->u.action.u.vs_public_action.oui;
+		end = drv_mgmt->frame + drv_mgmt->frame_len;
+		hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos,
+				      drv_mgmt->freq);
+		return;
+	}
+#endif /* CONFIG_DPP */
 }
 
 
@@ -1121,6 +1326,16 @@
 }
 
 
+static void hostapd_event_dfs_pre_cac_expired(struct hostapd_data *hapd,
+					      struct dfs_event *radar)
+{
+	wpa_printf(MSG_DEBUG, "DFS Pre-CAC expired on %d MHz", radar->freq);
+	hostapd_dfs_pre_cac_expired(hapd->iface, radar->freq, radar->ht_enabled,
+				    radar->chan_offset, radar->chan_width,
+				    radar->cf1, radar->cf2);
+}
+
+
 static void hostapd_event_dfs_cac_finished(struct hostapd_data *hapd,
 					   struct dfs_event *radar)
 {
@@ -1313,6 +1528,11 @@
 			break;
 		hostapd_event_dfs_radar_detected(hapd, &data->dfs_event);
 		break;
+	case EVENT_DFS_PRE_CAC_EXPIRED:
+		if (!data)
+			break;
+		hostapd_event_dfs_pre_cac_expired(hapd, &data->dfs_event);
+		break;
 	case EVENT_DFS_CAC_FINISHED:
 		if (!data)
 			break;
diff --git a/src/ap/eth_p_oui.c b/src/ap/eth_p_oui.c
new file mode 100644
index 0000000..aba901e
--- /dev/null
+++ b/src/ap/eth_p_oui.c
@@ -0,0 +1,191 @@
+/*
+ * hostapd / IEEE 802 OUI Extended EtherType 88-B7
+ * Copyright (c) 2016, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "l2_packet/l2_packet.h"
+#include "hostapd.h"
+#include "eth_p_oui.h"
+
+/*
+ * See IEEE Std 802-2014, Clause 9.2.4 for the definition of the OUI Extended
+ * EtherType 88-B7. This file implements this with OUI 00:13:74 and
+ * vendor-specific subtype 0x0001.
+ */
+static const u8 global_oui[] = { 0x00, 0x13, 0x74, 0x00, 0x01 };
+
+struct eth_p_oui_iface {
+	struct dl_list list;
+	char ifname[IFNAMSIZ + 1];
+	struct l2_packet_data *l2;
+	struct dl_list receiver;
+};
+
+struct eth_p_oui_ctx {
+	struct dl_list list;
+	struct eth_p_oui_iface *iface;
+	/* all data needed to deliver and unregister */
+	u8 oui_suffix; /* last byte of OUI */
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *dst_addr, u8 oui_suffix,
+			    const u8 *buf, size_t len);
+	void *rx_callback_ctx;
+};
+
+
+void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
+		       const u8 *dst_addr, const u8 *buf, size_t len)
+{
+	ctx->rx_callback(ctx->rx_callback_ctx, src_addr, dst_addr,
+			 ctx->oui_suffix, buf, len);
+}
+
+
+static void eth_p_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+	struct eth_p_oui_iface *iface = ctx;
+	struct eth_p_oui_ctx *receiver;
+	const struct l2_ethhdr *ethhdr;
+
+	if (len < sizeof(*ethhdr) + sizeof(global_oui) + 1) {
+		/* too short packet */
+		return;
+	}
+
+	ethhdr = (struct l2_ethhdr *) buf;
+	/* trim eth_hdr from buf and len */
+	buf += sizeof(*ethhdr);
+	len -= sizeof(*ethhdr);
+
+	/* verify OUI and vendor-specific subtype match */
+	if (os_memcmp(buf, global_oui, sizeof(global_oui)) != 0)
+		return;
+	buf += sizeof(global_oui);
+	len -= sizeof(global_oui);
+
+	dl_list_for_each(receiver, &iface->receiver,
+			 struct eth_p_oui_ctx, list) {
+		if (buf[0] != receiver->oui_suffix)
+			continue;
+
+		eth_p_oui_deliver(receiver, ethhdr->h_source, ethhdr->h_dest,
+				  buf + 1, len - 1);
+	}
+}
+
+
+struct eth_p_oui_ctx *
+eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix,
+		   void (*rx_callback)(void *ctx, const u8 *src_addr,
+				       const u8 *dst_addr, u8 oui_suffix,
+				       const u8 *buf, size_t len),
+		   void *rx_callback_ctx)
+{
+	struct eth_p_oui_iface *iface;
+	struct eth_p_oui_ctx *receiver;
+	int found = 0;
+	struct hapd_interfaces *interfaces;
+
+	receiver = os_zalloc(sizeof(*receiver));
+	if (!receiver)
+		goto err;
+
+	receiver->oui_suffix = oui_suffix;
+	receiver->rx_callback = rx_callback;
+	receiver->rx_callback_ctx = rx_callback_ctx;
+
+	interfaces = hapd->iface->interfaces;
+
+	dl_list_for_each(iface, &interfaces->eth_p_oui, struct eth_p_oui_iface,
+			 list) {
+		if (os_strcmp(iface->ifname, ifname) != 0)
+			continue;
+		found = 1;
+		break;
+	}
+
+	if (!found) {
+		iface = os_zalloc(sizeof(*iface));
+		if (!iface)
+			goto err;
+
+		os_strlcpy(iface->ifname, ifname, sizeof(iface->ifname));
+		iface->l2 = l2_packet_init(ifname, NULL, ETH_P_OUI, eth_p_rx,
+					   iface, 1);
+		if (!iface->l2) {
+			os_free(iface);
+			goto err;
+		}
+		dl_list_init(&iface->receiver);
+
+		dl_list_add_tail(&interfaces->eth_p_oui, &iface->list);
+	}
+
+	dl_list_add_tail(&iface->receiver, &receiver->list);
+	receiver->iface = iface;
+
+	return receiver;
+err:
+	os_free(receiver);
+	return NULL;
+}
+
+
+void eth_p_oui_unregister(struct eth_p_oui_ctx *ctx)
+{
+	struct eth_p_oui_iface *iface;
+
+	if (!ctx)
+		return;
+
+	iface = ctx->iface;
+
+	dl_list_del(&ctx->list);
+	os_free(ctx);
+
+	if (dl_list_empty(&iface->receiver)) {
+		dl_list_del(&iface->list);
+		l2_packet_deinit(iface->l2);
+		os_free(iface);
+	}
+}
+
+
+int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
+		   const u8 *dst_addr, const u8 *buf, size_t len)
+{
+	struct eth_p_oui_iface *iface = ctx->iface;
+	u8 *packet, *p;
+	size_t packet_len;
+	int ret;
+	struct l2_ethhdr *ethhdr;
+
+	packet_len = sizeof(*ethhdr) + sizeof(global_oui) + 1 + len;
+	packet = os_zalloc(packet_len);
+	if (!packet)
+		return -1;
+	p = packet;
+
+	ethhdr = (struct l2_ethhdr *) packet;
+	os_memcpy(ethhdr->h_source, src_addr, ETH_ALEN);
+	os_memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN);
+	ethhdr->h_proto = host_to_be16(ETH_P_OUI);
+	p += sizeof(*ethhdr);
+
+	os_memcpy(p, global_oui, sizeof(global_oui));
+	p[sizeof(global_oui)] = ctx->oui_suffix;
+	p += sizeof(global_oui) + 1;
+
+	os_memcpy(p, buf, len);
+
+	ret = l2_packet_send(iface->l2, NULL, 0, packet, packet_len);
+	os_free(packet);
+	return ret;
+}
diff --git a/src/ap/eth_p_oui.h b/src/ap/eth_p_oui.h
new file mode 100644
index 0000000..466fdc3
--- /dev/null
+++ b/src/ap/eth_p_oui.h
@@ -0,0 +1,28 @@
+/*
+ * hostapd / IEEE 802 OUI Extended Ethertype
+ * Copyright (c) 2016, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef ETH_P_OUI_H
+#define ETH_P_OUI_H
+
+struct eth_p_oui_ctx;
+struct hostapd_data;
+
+/* rx_callback only gets payload after OUI passed as buf */
+struct eth_p_oui_ctx *
+eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix,
+		   void (*rx_callback)(void *ctx, const u8 *src_addr,
+				       const u8 *dst_addr, u8 oui_suffix,
+				       const u8 *buf, size_t len),
+		   void *rx_callback_ctx);
+void eth_p_oui_unregister(struct eth_p_oui_ctx *eth_p_oui);
+int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
+		   const u8 *dst_addr, const u8 *buf, size_t len);
+void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
+		       const u8 *dst_addr, const u8 *buf, size_t len);
+
+#endif /* ETH_P_OUI_H */
diff --git a/src/ap/fils_hlp.c b/src/ap/fils_hlp.c
index 40d9be1..2a359ab 100644
--- a/src/ap/fils_hlp.c
+++ b/src/ap/fils_hlp.c
@@ -232,7 +232,7 @@
 	sta = ap_get_sta(hapd, dhcp->hw_addr);
 	if (!sta || !sta->fils_pending_assoc_req) {
 		wpa_printf(MSG_DEBUG,
-			   "FILS: No pending HLP DHCP exchange with hw_addr"
+			   "FILS: No pending HLP DHCP exchange with hw_addr "
 			   MACSTR, MAC2STR(dhcp->hw_addr));
 		return;
 	}
@@ -263,14 +263,15 @@
 	iph->ihl = sizeof(*iph) / 4;
 	iph->tot_len = htons(sizeof(*iph) + sizeof(*udph) + (end - pos));
 	iph->ttl = 1;
+	iph->protocol = 17; /* UDP */
 	iph->saddr = hapd->conf->dhcp_server.u.v4.s_addr;
 	iph->daddr = dhcp->client_ip;
 	iph->check = ip_checksum(iph, sizeof(*iph));
 	udph = wpabuf_put(resp, sizeof(*udph));
 	udph->uh_sport = htons(DHCP_SERVER_PORT);
 	udph->uh_dport = htons(DHCP_CLIENT_PORT);
-	udph->len = htons(sizeof(*udph) + (end - pos));
-	udph->check = htons(0x0000); /* TODO: calculate checksum */
+	udph->uh_ulen = htons(sizeof(*udph) + (end - pos));
+	udph->uh_sum = htons(0x0000); /* TODO: calculate checksum */
 	if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPACK &&
 	    !rapid_commit && sta->fils_dhcp_rapid_commit_proxy && end_opt) {
 		/* Add rapid commit option */
@@ -313,7 +314,11 @@
 		left -= len;
 	}
 	wpabuf_free(resp);
-	fils_hlp_finish_assoc(hapd, sta);
+
+	if (sta->fils_drv_assoc_finish)
+		hostapd_notify_assoc_fils_finish(hapd, sta);
+	else
+		fils_hlp_finish_assoc(hapd, sta);
 }
 
 
diff --git a/src/ap/gas_query_ap.c b/src/ap/gas_query_ap.c
new file mode 100644
index 0000000..fdb3cad
--- /dev/null
+++ b/src/ap/gas_query_ap.c
@@ -0,0 +1,714 @@
+/*
+ * Generic advertisement service (GAS) query (hostapd)
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011-2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "utils/list.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "common/wpa_ctrl.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "gas_query_ap.h"
+
+
+/** GAS query timeout in seconds */
+#define GAS_QUERY_TIMEOUT_PERIOD 2
+
+/* GAS query wait-time / duration in ms */
+#define GAS_QUERY_WAIT_TIME_INITIAL 1000
+#define GAS_QUERY_WAIT_TIME_COMEBACK 150
+
+/**
+ * struct gas_query_pending - Pending GAS query
+ */
+struct gas_query_pending {
+	struct dl_list list;
+	struct gas_query_ap *gas;
+	u8 addr[ETH_ALEN];
+	u8 dialog_token;
+	u8 next_frag_id;
+	unsigned int wait_comeback:1;
+	unsigned int offchannel_tx_started:1;
+	unsigned int retry:1;
+	int freq;
+	u16 status_code;
+	struct wpabuf *req;
+	struct wpabuf *adv_proto;
+	struct wpabuf *resp;
+	struct os_reltime last_oper;
+	void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+		   enum gas_query_ap_result result,
+		   const struct wpabuf *adv_proto,
+		   const struct wpabuf *resp, u16 status_code);
+	void *ctx;
+	u8 sa[ETH_ALEN];
+};
+
+/**
+ * struct gas_query_ap - Internal GAS query data
+ */
+struct gas_query_ap {
+	struct hostapd_data *hapd;
+	void *msg_ctx;
+	struct dl_list pending; /* struct gas_query_pending */
+	struct gas_query_pending *current;
+};
+
+
+static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx);
+static void gas_query_timeout(void *eloop_data, void *user_ctx);
+static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx);
+static void gas_query_tx_initial_req(struct gas_query_ap *gas,
+				     struct gas_query_pending *query);
+static int gas_query_new_dialog_token(struct gas_query_ap *gas, const u8 *dst);
+
+
+static int ms_from_time(struct os_reltime *last)
+{
+	struct os_reltime now, res;
+
+	os_get_reltime(&now);
+	os_reltime_sub(&now, last, &res);
+	return res.sec * 1000 + res.usec / 1000;
+}
+
+
+/**
+ * gas_query_ap_init - Initialize GAS query component
+ * @hapd: Pointer to hostapd data
+ * Returns: Pointer to GAS query data or %NULL on failure
+ */
+struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd,
+					void *msg_ctx)
+{
+	struct gas_query_ap *gas;
+
+	gas = os_zalloc(sizeof(*gas));
+	if (!gas)
+		return NULL;
+
+	gas->hapd = hapd;
+	gas->msg_ctx = msg_ctx;
+	dl_list_init(&gas->pending);
+
+	return gas;
+}
+
+
+static const char * gas_result_txt(enum gas_query_ap_result result)
+{
+	switch (result) {
+	case GAS_QUERY_AP_SUCCESS:
+		return "SUCCESS";
+	case GAS_QUERY_AP_FAILURE:
+		return "FAILURE";
+	case GAS_QUERY_AP_TIMEOUT:
+		return "TIMEOUT";
+	case GAS_QUERY_AP_PEER_ERROR:
+		return "PEER_ERROR";
+	case GAS_QUERY_AP_INTERNAL_ERROR:
+		return "INTERNAL_ERROR";
+	case GAS_QUERY_AP_DELETED_AT_DEINIT:
+		return "DELETED_AT_DEINIT";
+	}
+
+	return "N/A";
+}
+
+
+static void gas_query_free(struct gas_query_pending *query, int del_list)
+{
+	if (del_list)
+		dl_list_del(&query->list);
+
+	wpabuf_free(query->req);
+	wpabuf_free(query->adv_proto);
+	wpabuf_free(query->resp);
+	os_free(query);
+}
+
+
+static void gas_query_done(struct gas_query_ap *gas,
+			   struct gas_query_pending *query,
+			   enum gas_query_ap_result result)
+{
+	wpa_msg(gas->msg_ctx, MSG_INFO, GAS_QUERY_DONE "addr=" MACSTR
+		" dialog_token=%u freq=%d status_code=%u result=%s",
+		MAC2STR(query->addr), query->dialog_token, query->freq,
+		query->status_code, gas_result_txt(result));
+	if (gas->current == query)
+		gas->current = NULL;
+	eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+	eloop_cancel_timeout(gas_query_timeout, gas, query);
+	eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
+	dl_list_del(&query->list);
+	query->cb(query->ctx, query->addr, query->dialog_token, result,
+		  query->adv_proto, query->resp, query->status_code);
+	gas_query_free(query, 0);
+}
+
+
+/**
+ * gas_query_ap_deinit - Deinitialize GAS query component
+ * @gas: GAS query data from gas_query_init()
+ */
+void gas_query_ap_deinit(struct gas_query_ap *gas)
+{
+	struct gas_query_pending *query, *next;
+
+	if (gas == NULL)
+		return;
+
+	dl_list_for_each_safe(query, next, &gas->pending,
+			      struct gas_query_pending, list)
+		gas_query_done(gas, query, GAS_QUERY_AP_DELETED_AT_DEINIT);
+
+	os_free(gas);
+}
+
+
+static struct gas_query_pending *
+gas_query_get_pending(struct gas_query_ap *gas, const u8 *addr, u8 dialog_token)
+{
+	struct gas_query_pending *q;
+	dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
+		if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 &&
+		    q->dialog_token == dialog_token)
+			return q;
+	}
+	return NULL;
+}
+
+
+static int gas_query_append(struct gas_query_pending *query, const u8 *data,
+			    size_t len)
+{
+	if (wpabuf_resize(&query->resp, len) < 0) {
+		wpa_printf(MSG_DEBUG, "GAS: No memory to store the response");
+		return -1;
+	}
+	wpabuf_put_data(query->resp, data, len);
+	return 0;
+}
+
+
+void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst,
+			    const u8 *data, size_t data_len, int ok)
+{
+	struct gas_query_pending *query;
+	int dur;
+
+	if (!gas || !gas->current) {
+		wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: dst=" MACSTR
+			   " ok=%d - no query in progress", MAC2STR(dst), ok);
+		return;
+	}
+
+	query = gas->current;
+
+	dur = ms_from_time(&query->last_oper);
+	wpa_printf(MSG_DEBUG, "GAS: TX status: dst=" MACSTR
+		   " ok=%d query=%p dialog_token=%u dur=%d ms",
+		   MAC2STR(dst), ok, query, query->dialog_token, dur);
+	if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination");
+		return;
+	}
+	os_get_reltime(&query->last_oper);
+
+	eloop_cancel_timeout(gas_query_timeout, gas, query);
+	if (!ok) {
+		wpa_printf(MSG_DEBUG, "GAS: No ACK to GAS request");
+		eloop_register_timeout(0, 250000, gas_query_timeout,
+				       gas, query);
+	} else {
+		eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
+				       gas_query_timeout, gas, query);
+	}
+	if (query->wait_comeback && !query->retry) {
+		eloop_cancel_timeout(gas_query_rx_comeback_timeout,
+				     gas, query);
+		eloop_register_timeout(
+			0, (GAS_QUERY_WAIT_TIME_COMEBACK + 10) * 1000,
+			gas_query_rx_comeback_timeout, gas, query);
+	}
+}
+
+
+static int pmf_in_use(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, addr);
+	return sta && (sta->flags & WLAN_STA_MFP);
+}
+
+
+static int gas_query_tx(struct gas_query_ap *gas,
+			struct gas_query_pending *query,
+			struct wpabuf *req, unsigned int wait_time)
+{
+	int res, prot = pmf_in_use(gas->hapd, query->addr);
+
+	wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
+		   "freq=%d prot=%d using src addr " MACSTR,
+		   MAC2STR(query->addr), (unsigned int) wpabuf_len(req),
+		   query->freq, prot, MAC2STR(query->sa));
+	if (prot) {
+		u8 *categ = wpabuf_mhead_u8(req);
+		*categ = WLAN_ACTION_PROTECTED_DUAL;
+	}
+	os_get_reltime(&query->last_oper);
+	res = hostapd_drv_send_action(gas->hapd, query->freq, wait_time,
+				      query->addr, wpabuf_head(req),
+				      wpabuf_len(req));
+	return res;
+}
+
+
+static void gas_query_tx_comeback_req(struct gas_query_ap *gas,
+				      struct gas_query_pending *query)
+{
+	struct wpabuf *req;
+	unsigned int wait_time;
+
+	req = gas_build_comeback_req(query->dialog_token);
+	if (req == NULL) {
+		gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+		return;
+	}
+
+	wait_time = (query->retry || !query->offchannel_tx_started) ?
+		GAS_QUERY_WAIT_TIME_INITIAL : GAS_QUERY_WAIT_TIME_COMEBACK;
+
+	if (gas_query_tx(gas, query, req, wait_time) < 0) {
+		wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
+			   MACSTR, MAC2STR(query->addr));
+		gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+	}
+
+	wpabuf_free(req);
+}
+
+
+static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx)
+{
+	struct gas_query_ap *gas = eloop_data;
+	struct gas_query_pending *query = user_ctx;
+	int dialog_token;
+
+	wpa_printf(MSG_DEBUG,
+		   "GAS: No response to comeback request received (retry=%u)",
+		   query->retry);
+	if (gas->current != query || query->retry)
+		return;
+	dialog_token = gas_query_new_dialog_token(gas, query->addr);
+	if (dialog_token < 0)
+		return;
+	wpa_printf(MSG_DEBUG,
+		   "GAS: Retry GAS query due to comeback response timeout");
+	query->retry = 1;
+	query->dialog_token = dialog_token;
+	*(wpabuf_mhead_u8(query->req) + 2) = dialog_token;
+	query->wait_comeback = 0;
+	query->next_frag_id = 0;
+	wpabuf_free(query->adv_proto);
+	query->adv_proto = NULL;
+	eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+	eloop_cancel_timeout(gas_query_timeout, gas, query);
+	gas_query_tx_initial_req(gas, query);
+}
+
+
+static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx)
+{
+	struct gas_query_ap *gas = eloop_data;
+	struct gas_query_pending *query = user_ctx;
+
+	wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR,
+		   MAC2STR(query->addr));
+	gas_query_tx_comeback_req(gas, query);
+}
+
+
+static void gas_query_tx_comeback_req_delay(struct gas_query_ap *gas,
+					    struct gas_query_pending *query,
+					    u16 comeback_delay)
+{
+	unsigned int secs, usecs;
+
+	secs = (comeback_delay * 1024) / 1000000;
+	usecs = comeback_delay * 1024 - secs * 1000000;
+	wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR
+		   " in %u secs %u usecs", MAC2STR(query->addr), secs, usecs);
+	eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+	eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout,
+			       gas, query);
+}
+
+
+static void gas_query_rx_initial(struct gas_query_ap *gas,
+				 struct gas_query_pending *query,
+				 const u8 *adv_proto, const u8 *resp,
+				 size_t len, u16 comeback_delay)
+{
+	wpa_printf(MSG_DEBUG, "GAS: Received initial response from "
+		   MACSTR " (dialog_token=%u comeback_delay=%u)",
+		   MAC2STR(query->addr), query->dialog_token, comeback_delay);
+
+	query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]);
+	if (query->adv_proto == NULL) {
+		gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+		return;
+	}
+
+	if (comeback_delay) {
+		eloop_cancel_timeout(gas_query_timeout, gas, query);
+		query->wait_comeback = 1;
+		gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
+		return;
+	}
+
+	/* Query was completed without comeback mechanism */
+	if (gas_query_append(query, resp, len) < 0) {
+		gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+		return;
+	}
+
+	gas_query_done(gas, query, GAS_QUERY_AP_SUCCESS);
+}
+
+
+static void gas_query_rx_comeback(struct gas_query_ap *gas,
+				  struct gas_query_pending *query,
+				  const u8 *adv_proto, const u8 *resp,
+				  size_t len, u8 frag_id, u8 more_frags,
+				  u16 comeback_delay)
+{
+	wpa_printf(MSG_DEBUG, "GAS: Received comeback response from "
+		   MACSTR " (dialog_token=%u frag_id=%u more_frags=%u "
+		   "comeback_delay=%u)",
+		   MAC2STR(query->addr), query->dialog_token, frag_id,
+		   more_frags, comeback_delay);
+	eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
+
+	if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) ||
+	    os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
+		      wpabuf_len(query->adv_proto)) != 0) {
+		wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed "
+			   "between initial and comeback response from "
+			   MACSTR, MAC2STR(query->addr));
+		gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR);
+		return;
+	}
+
+	if (comeback_delay) {
+		if (frag_id) {
+			wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response "
+				   "with non-zero frag_id and comeback_delay "
+				   "from " MACSTR, MAC2STR(query->addr));
+			gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR);
+			return;
+		}
+		gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
+		return;
+	}
+
+	if (frag_id != query->next_frag_id) {
+		wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response "
+			   "from " MACSTR, MAC2STR(query->addr));
+		if (frag_id + 1 == query->next_frag_id) {
+			wpa_printf(MSG_DEBUG, "GAS: Drop frame as possible "
+				   "retry of previous fragment");
+			return;
+		}
+		gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR);
+		return;
+	}
+	query->next_frag_id++;
+
+	if (gas_query_append(query, resp, len) < 0) {
+		gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+		return;
+	}
+
+	if (more_frags) {
+		gas_query_tx_comeback_req(gas, query);
+		return;
+	}
+
+	gas_query_done(gas, query, GAS_QUERY_AP_SUCCESS);
+}
+
+
+/**
+ * gas_query_ap_rx - Indicate reception of a Public Action or Protected Dual
+ *	frame
+ * @gas: GAS query data from gas_query_init()
+ * @sa: Source MAC address of the Action frame
+ * @categ: Category of the Action frame
+ * @data: Payload of the Action frame
+ * @len: Length of @data
+ * @freq: Frequency (in MHz) on which the frame was received
+ * Returns: 0 if the Public Action frame was a GAS frame or -1 if not
+ */
+int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ,
+		    const u8 *data, size_t len, int freq)
+{
+	struct gas_query_pending *query;
+	u8 action, dialog_token, frag_id = 0, more_frags = 0;
+	u16 comeback_delay, resp_len;
+	const u8 *pos, *adv_proto;
+	int prot, pmf;
+	unsigned int left;
+
+	if (!gas || len < 4)
+		return -1;
+
+	pos = data;
+	action = *pos++;
+	dialog_token = *pos++;
+
+	if (action != WLAN_PA_GAS_INITIAL_RESP &&
+	    action != WLAN_PA_GAS_COMEBACK_RESP)
+		return -1; /* Not a GAS response */
+
+	prot = categ == WLAN_ACTION_PROTECTED_DUAL;
+	pmf = pmf_in_use(gas->hapd, sa);
+	if (prot && !pmf) {
+		wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled");
+		return 0;
+	}
+	if (!prot && pmf) {
+		wpa_printf(MSG_DEBUG, "GAS: Drop unexpected unprotected GAS frame when PMF is enabled");
+		return 0;
+	}
+
+	query = gas_query_get_pending(gas, sa, dialog_token);
+	if (query == NULL) {
+		wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR
+			   " dialog token %u", MAC2STR(sa), dialog_token);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR,
+		   ms_from_time(&query->last_oper), MAC2STR(sa));
+
+	if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
+		wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
+			   MACSTR " dialog token %u when waiting for comeback "
+			   "response", MAC2STR(sa), dialog_token);
+		return 0;
+	}
+
+	if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) {
+		wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from "
+			   MACSTR " dialog token %u when waiting for initial "
+			   "response", MAC2STR(sa), dialog_token);
+		return 0;
+	}
+
+	query->status_code = WPA_GET_LE16(pos);
+	pos += 2;
+
+	if (query->status_code == WLAN_STATUS_QUERY_RESP_OUTSTANDING &&
+	    action == WLAN_PA_GAS_COMEBACK_RESP) {
+		wpa_printf(MSG_DEBUG, "GAS: Allow non-zero status for outstanding comeback response");
+	} else if (query->status_code != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
+			   "%u failed - status code %u",
+			   MAC2STR(sa), dialog_token, query->status_code);
+		gas_query_done(gas, query, GAS_QUERY_AP_FAILURE);
+		return 0;
+	}
+
+	if (action == WLAN_PA_GAS_COMEBACK_RESP) {
+		if (pos + 1 > data + len)
+			return 0;
+		frag_id = *pos & 0x7f;
+		more_frags = (*pos & 0x80) >> 7;
+		pos++;
+	}
+
+	/* Comeback Delay */
+	if (pos + 2 > data + len)
+		return 0;
+	comeback_delay = WPA_GET_LE16(pos);
+	pos += 2;
+
+	/* Advertisement Protocol element */
+	if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) {
+		wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement "
+			   "Protocol element in the response from " MACSTR,
+			   MAC2STR(sa));
+		return 0;
+	}
+
+	if (*pos != WLAN_EID_ADV_PROTO) {
+		wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
+			   "Protocol element ID %u in response from " MACSTR,
+			   *pos, MAC2STR(sa));
+		return 0;
+	}
+
+	adv_proto = pos;
+	pos += 2 + pos[1];
+
+	/* Query Response Length */
+	if (pos + 2 > data + len) {
+		wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length");
+		return 0;
+	}
+	resp_len = WPA_GET_LE16(pos);
+	pos += 2;
+
+	left = data + len - pos;
+	if (resp_len > left) {
+		wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
+			   "response from " MACSTR, MAC2STR(sa));
+		return 0;
+	}
+
+	if (resp_len < left) {
+		wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data "
+			   "after Query Response from " MACSTR,
+			   left - resp_len, MAC2STR(sa));
+	}
+
+	if (action == WLAN_PA_GAS_COMEBACK_RESP)
+		gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len,
+				      frag_id, more_frags, comeback_delay);
+	else
+		gas_query_rx_initial(gas, query, adv_proto, pos, resp_len,
+				     comeback_delay);
+
+	return 0;
+}
+
+
+static void gas_query_timeout(void *eloop_data, void *user_ctx)
+{
+	struct gas_query_ap *gas = eloop_data;
+	struct gas_query_pending *query = user_ctx;
+
+	wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR
+		   " dialog token %u",
+		   MAC2STR(query->addr), query->dialog_token);
+	gas_query_done(gas, query, GAS_QUERY_AP_TIMEOUT);
+}
+
+
+static int gas_query_dialog_token_available(struct gas_query_ap *gas,
+					    const u8 *dst, u8 dialog_token)
+{
+	struct gas_query_pending *q;
+	dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
+		if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 &&
+		    dialog_token == q->dialog_token)
+			return 0;
+	}
+
+	return 1;
+}
+
+
+static void gas_query_tx_initial_req(struct gas_query_ap *gas,
+				     struct gas_query_pending *query)
+{
+	if (gas_query_tx(gas, query, query->req,
+			 GAS_QUERY_WAIT_TIME_INITIAL) < 0) {
+		wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
+			   MACSTR, MAC2STR(query->addr));
+		gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+		return;
+	}
+	gas->current = query;
+
+	wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u",
+		   query->dialog_token);
+	eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
+			       gas_query_timeout, gas, query);
+}
+
+
+static int gas_query_new_dialog_token(struct gas_query_ap *gas, const u8 *dst)
+{
+	static int next_start = 0;
+	int dialog_token;
+
+	for (dialog_token = 0; dialog_token < 256; dialog_token++) {
+		if (gas_query_dialog_token_available(
+			    gas, dst, (next_start + dialog_token) % 256))
+			break;
+	}
+	if (dialog_token == 256)
+		return -1; /* Too many pending queries */
+	dialog_token = (next_start + dialog_token) % 256;
+	next_start = (dialog_token + 1) % 256;
+	return dialog_token;
+}
+
+
+/**
+ * gas_query_ap_req - Request a GAS query
+ * @gas: GAS query data from gas_query_init()
+ * @dst: Destination MAC address for the query
+ * @freq: Frequency (in MHz) for the channel on which to send the query
+ * @req: GAS query payload (to be freed by gas_query module in case of success
+ *	return)
+ * @cb: Callback function for reporting GAS query result and response
+ * @ctx: Context pointer to use with the @cb call
+ * Returns: dialog token (>= 0) on success or -1 on failure
+ */
+int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq,
+		     struct wpabuf *req,
+		     void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+				enum gas_query_ap_result result,
+				const struct wpabuf *adv_proto,
+				const struct wpabuf *resp, u16 status_code),
+		     void *ctx)
+{
+	struct gas_query_pending *query;
+	int dialog_token;
+
+	if (!gas || wpabuf_len(req) < 3)
+		return -1;
+
+	dialog_token = gas_query_new_dialog_token(gas, dst);
+	if (dialog_token < 0)
+		return -1;
+
+	query = os_zalloc(sizeof(*query));
+	if (query == NULL)
+		return -1;
+
+	query->gas = gas;
+	os_memcpy(query->addr, dst, ETH_ALEN);
+	query->dialog_token = dialog_token;
+	query->freq = freq;
+	query->cb = cb;
+	query->ctx = ctx;
+	query->req = req;
+	dl_list_add(&gas->pending, &query->list);
+
+	*(wpabuf_mhead_u8(req) + 2) = dialog_token;
+
+	wpa_msg(gas->msg_ctx, MSG_INFO, GAS_QUERY_START "addr=" MACSTR
+		" dialog_token=%u freq=%d",
+		MAC2STR(query->addr), query->dialog_token, query->freq);
+
+	gas_query_tx_initial_req(gas, query);
+
+	return dialog_token;
+}
diff --git a/src/ap/gas_query_ap.h b/src/ap/gas_query_ap.h
new file mode 100644
index 0000000..70f1f05
--- /dev/null
+++ b/src/ap/gas_query_ap.h
@@ -0,0 +1,43 @@
+/*
+ * Generic advertisement service (GAS) query
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011-2017, Qualcomm Atheros
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef GAS_QUERY_AP_H
+#define GAS_QUERY_AP_H
+
+struct gas_query_ap;
+
+struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd,
+					void *msg_ctx);
+void gas_query_ap_deinit(struct gas_query_ap *gas);
+int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ,
+		    const u8 *data, size_t len, int freq);
+
+/**
+ * enum gas_query_ap_result - GAS query result
+ */
+enum gas_query_ap_result {
+	GAS_QUERY_AP_SUCCESS,
+	GAS_QUERY_AP_FAILURE,
+	GAS_QUERY_AP_TIMEOUT,
+	GAS_QUERY_AP_PEER_ERROR,
+	GAS_QUERY_AP_INTERNAL_ERROR,
+	GAS_QUERY_AP_DELETED_AT_DEINIT
+};
+
+int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq,
+		     struct wpabuf *req,
+		     void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+				enum gas_query_ap_result result,
+				const struct wpabuf *adv_proto,
+				const struct wpabuf *resp, u16 status_code),
+		     void *ctx);
+void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst,
+			    const u8 *data, size_t data_len, int ok);
+
+#endif /* GAS_QUERY_AP_H */
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
index 96cd703..fadb740 100644
--- a/src/ap/gas_serv.c
+++ b/src/ap/gas_serv.c
@@ -11,14 +11,31 @@
 #include "common.h"
 #include "common/ieee802_11_defs.h"
 #include "common/gas.h"
+#include "common/wpa_ctrl.h"
 #include "utils/eloop.h"
 #include "hostapd.h"
 #include "ap_config.h"
 #include "ap_drv_ops.h"
+#include "dpp_hostapd.h"
 #include "sta_info.h"
 #include "gas_serv.h"
 
 
+#ifdef CONFIG_DPP
+static void gas_serv_write_dpp_adv_proto(struct wpabuf *buf)
+{
+	wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
+	wpabuf_put_u8(buf, 8); /* Length */
+	wpabuf_put_u8(buf, 0x7f);
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+	wpabuf_put_u8(buf, 5);
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, DPP_OUI_TYPE);
+	wpabuf_put_u8(buf, 0x01);
+}
+#endif /* CONFIG_DPP */
+
+
 static void convert_to_protected_dual(struct wpabuf *msg)
 {
 	u8 *categ = wpabuf_mhead_u8(msg);
@@ -828,6 +845,22 @@
 #endif /* CONFIG_HS20 */
 
 
+#ifdef CONFIG_MBO
+static void anqp_add_mbo_cell_data_conn_pref(struct hostapd_data *hapd,
+					     struct wpabuf *buf)
+{
+	if (hapd->conf->mbo_cell_data_conn_pref >= 0) {
+		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+		wpabuf_put_be24(buf, OUI_WFA);
+		wpabuf_put_u8(buf, MBO_ANQP_OUI_TYPE);
+		wpabuf_put_u8(buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF);
+		wpabuf_put_u8(buf, hapd->conf->mbo_cell_data_conn_pref);
+		gas_anqp_set_element_len(buf, len);
+	}
+}
+#endif /* CONFIG_MBO */
+
+
 static size_t anqp_get_required_len(struct hostapd_data *hapd,
 				    const u16 *infoid,
 				    unsigned int num_infoid)
@@ -933,6 +966,11 @@
 		anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
 #endif /* CONFIG_HS20 */
 
+#ifdef CONFIG_MBO
+	if (request & ANQP_REQ_MBO_CELL_DATA_CONN_PREF)
+		anqp_add_mbo_cell_data_conn_pref(hapd, buf);
+#endif /* CONFIG_MBO */
+
 	return buf;
 }
 
@@ -1152,49 +1190,12 @@
 }
 
 
-static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
-				    const u8 *pos, const u8 *end,
-				    struct anqp_query_info *qi)
+static void rx_anqp_vendor_specific_hs20(struct hostapd_data *hapd,
+					 const u8 *pos, const u8 *end,
+					 struct anqp_query_info *qi)
 {
-	u32 oui;
 	u8 subtype;
 
-	if (end - pos < 4) {
-		wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
-			   "Query element");
-		return;
-	}
-
-	oui = WPA_GET_BE24(pos);
-	pos += 3;
-	if (oui != OUI_WFA) {
-		wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
-			   oui);
-		return;
-	}
-
-#ifdef CONFIG_P2P
-	if (*pos == P2P_OUI_TYPE) {
-		/*
-		 * This is for P2P SD and will be taken care of by the P2P
-		 * implementation. This query needs to be ignored in the generic
-		 * GAS server to avoid duplicated response.
-		 */
-		wpa_printf(MSG_DEBUG,
-			   "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server",
-			   *pos);
-		qi->p2p_sd = 1;
-		return;
-	}
-#endif /* CONFIG_P2P */
-
-	if (*pos != HS20_ANQP_OUI_TYPE) {
-		wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
-			   *pos);
-		return;
-	}
-	pos++;
-
 	if (end - pos <= 1)
 		return;
 
@@ -1224,6 +1225,115 @@
 #endif /* CONFIG_HS20 */
 
 
+#ifdef CONFIG_P2P
+static void rx_anqp_vendor_specific_p2p(struct hostapd_data *hapd,
+					struct anqp_query_info *qi)
+{
+	/*
+	 * This is for P2P SD and will be taken care of by the P2P
+	 * implementation. This query needs to be ignored in the generic
+	 * GAS server to avoid duplicated response.
+	 */
+	wpa_printf(MSG_DEBUG,
+		   "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server",
+		   P2P_OUI_TYPE);
+	qi->p2p_sd = 1;
+	return;
+}
+#endif /* CONFIG_P2P */
+
+
+#ifdef CONFIG_MBO
+
+static void rx_anqp_mbo_query_list(struct hostapd_data *hapd, u8 subtype,
+				  struct anqp_query_info *qi)
+{
+	switch (subtype) {
+	case MBO_ANQP_SUBTYPE_CELL_CONN_PREF:
+		set_anqp_req(ANQP_REQ_MBO_CELL_DATA_CONN_PREF,
+			     "Cellular Data Connection Preference",
+			     hapd->conf->mbo_cell_data_conn_pref >= 0, qi);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO subtype %u",
+			   subtype);
+		break;
+	}
+}
+
+
+static void rx_anqp_vendor_specific_mbo(struct hostapd_data *hapd,
+					const u8 *pos, const u8 *end,
+					struct anqp_query_info *qi)
+{
+	u8 subtype;
+
+	if (end - pos < 1)
+		return;
+
+	subtype = *pos++;
+	switch (subtype) {
+	case MBO_ANQP_SUBTYPE_QUERY_LIST:
+		wpa_printf(MSG_DEBUG, "ANQP: MBO Query List");
+		while (pos < end) {
+			rx_anqp_mbo_query_list(hapd, *pos, qi);
+			pos++;
+		}
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO query subtype %u",
+			   subtype);
+		break;
+	}
+}
+
+#endif /* CONFIG_MBO */
+
+
+static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
+				    const u8 *pos, const u8 *end,
+				    struct anqp_query_info *qi)
+{
+	u32 oui;
+
+	if (end - pos < 4) {
+		wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
+			   "Query element");
+		return;
+	}
+
+	oui = WPA_GET_BE24(pos);
+	pos += 3;
+	if (oui != OUI_WFA) {
+		wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
+			   oui);
+		return;
+	}
+
+	switch (*pos) {
+#ifdef CONFIG_P2P
+	case P2P_OUI_TYPE:
+		rx_anqp_vendor_specific_p2p(hapd, qi);
+		break;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_HS20
+	case HS20_ANQP_OUI_TYPE:
+		rx_anqp_vendor_specific_hs20(hapd, pos + 1, end, qi);
+		break;
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+	case MBO_ANQP_OUI_TYPE:
+		rx_anqp_vendor_specific_mbo(hapd, pos + 1, end, qi);
+		break;
+#endif /* CONFIG_MBO */
+	default:
+		wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
+			   *pos);
+		break;
+	}
+}
+
+
 static void gas_serv_req_local_processing(struct hostapd_data *hapd,
 					  const u8 *sa, u8 dialog_token,
 					  struct anqp_query_info *qi, int prot,
@@ -1300,6 +1410,72 @@
 }
 
 
+#ifdef CONFIG_DPP
+static void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
+					const u8 *sa, u8 dialog_token,
+					int prot, struct wpabuf *buf)
+{
+	struct wpabuf *tx_buf;
+
+	if (wpabuf_len(buf) > hapd->conf->gas_frag_limit ||
+	    hapd->conf->gas_comeback_delay) {
+		struct gas_dialog_info *di;
+		u16 comeback_delay = 1;
+
+		if (hapd->conf->gas_comeback_delay) {
+			/* Testing - allow overriding of the delay value */
+			comeback_delay = hapd->conf->gas_comeback_delay;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Too long response to fit in initial response - use GAS comeback");
+		di = gas_dialog_create(hapd, sa, dialog_token);
+		if (!di) {
+			wpa_printf(MSG_INFO, "DPP: Could not create dialog for "
+				   MACSTR " (dialog token %u)",
+				   MAC2STR(sa), dialog_token);
+			wpabuf_free(buf);
+			tx_buf = gas_build_initial_resp(
+				dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
+				0, 10);
+			if (tx_buf)
+				gas_serv_write_dpp_adv_proto(tx_buf);
+		} else {
+			di->prot = prot;
+			di->sd_resp = buf;
+			di->sd_resp_pos = 0;
+			tx_buf = gas_build_initial_resp(
+				dialog_token, WLAN_STATUS_SUCCESS,
+				comeback_delay, 10);
+			if (tx_buf)
+				gas_serv_write_dpp_adv_proto(tx_buf);
+		}
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: GAS Initial response (no comeback)");
+		tx_buf = gas_build_initial_resp(
+			dialog_token, WLAN_STATUS_SUCCESS, 0,
+			10 + 2 + wpabuf_len(buf));
+		if (tx_buf) {
+			gas_serv_write_dpp_adv_proto(tx_buf);
+			wpabuf_put_le16(tx_buf, wpabuf_len(buf));
+			wpabuf_put_buf(tx_buf, buf);
+			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+		}
+		wpabuf_free(buf);
+	}
+	if (!tx_buf)
+		return;
+	if (prot)
+		convert_to_protected_dual(tx_buf);
+	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+				wpabuf_head(tx_buf),
+				wpabuf_len(tx_buf));
+	wpabuf_free(tx_buf);
+}
+#endif /* CONFIG_DPP */
+
+
 static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
 					const u8 *sa,
 					const u8 *data, size_t len, int prot,
@@ -1312,6 +1488,9 @@
 	u16 slen;
 	struct anqp_query_info qi;
 	const u8 *adv_proto;
+#ifdef CONFIG_DPP
+	int dpp = 0;
+#endif /* CONFIG_DPP */
 
 	if (len < 1 + 2)
 		return;
@@ -1339,6 +1518,15 @@
 	next = pos + slen;
 	pos++; /* skip QueryRespLenLimit and PAME-BI */
 
+#ifdef CONFIG_DPP
+	if (slen == 8 && *pos == WLAN_EID_VENDOR_SPECIFIC &&
+	    pos[1] == 5 && WPA_GET_BE24(&pos[2]) == OUI_WFA &&
+	    pos[5] == DPP_OUI_TYPE && pos[6] == 0x01) {
+		wpa_printf(MSG_DEBUG, "DPP: Configuration Request");
+		dpp = 1;
+	} else
+#endif /* CONFIG_DPP */
+
 	if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
 		struct wpabuf *buf;
 		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
@@ -1378,6 +1566,18 @@
 		return;
 	end = pos + slen;
 
+#ifdef CONFIG_DPP
+	if (dpp) {
+		struct wpabuf *msg;
+
+		msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen);
+		if (!msg)
+			return;
+		gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg);
+		return;
+	}
+#endif /* CONFIG_DPP */
+
 	/* ANQP Query Request */
 	while (pos < end) {
 		u16 info_id, elen;
@@ -1399,11 +1599,9 @@
 		case ANQP_QUERY_LIST:
 			rx_anqp_query_list(hapd, pos, pos + elen, &qi);
 			break;
-#ifdef CONFIG_HS20
 		case ANQP_VENDOR_SPECIFIC:
 			rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
 			break;
-#endif /* CONFIG_HS20 */
 		default:
 			wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
 				   "Request element %u", info_id);
@@ -1467,6 +1665,18 @@
 		gas_serv_dialog_clear(dialog);
 		return;
 	}
+#ifdef CONFIG_DPP
+	if (dialog->dpp) {
+		tx_buf = gas_build_comeback_resp(dialog_token,
+						 WLAN_STATUS_SUCCESS,
+						 dialog->sd_frag_id, more, 0,
+						 10 + frag_len);
+		if (tx_buf) {
+			gas_serv_write_dpp_adv_proto(tx_buf);
+			wpabuf_put_buf(tx_buf, buf);
+		}
+	} else
+#endif /* CONFIG_DPP */
 	tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
 						  WLAN_STATUS_SUCCESS,
 						  dialog->sd_frag_id,
@@ -1490,6 +1700,10 @@
 	} else {
 		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
 			"SD response sent");
+#ifdef CONFIG_DPP
+		if (dialog->dpp)
+			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+#endif /* CONFIG_DPP */
 		gas_serv_dialog_clear(dialog);
 		gas_serv_free_dialogs(hapd, sa);
 	}
diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h
index 9051e4f..3a30298 100644
--- a/src/ap/gas_serv.h
+++ b/src/ap/gas_serv.h
@@ -41,7 +41,7 @@
 #define ANQP_REQ_EMERGENCY_NAI \
 	(1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST))
 /*
- * First 16 Hotspot 2.0 vendor specific ANQP-elements can be included in the
+ * First 15 Hotspot 2.0 vendor specific ANQP-elements can be included in the
  * optimized bitmap.
  */
 #define ANQP_REQ_HS_CAPABILITY_LIST \
@@ -60,6 +60,9 @@
 	(0x10000 << HS20_STYPE_OSU_PROVIDERS_LIST)
 #define ANQP_REQ_ICON_REQUEST \
 	(0x10000 << HS20_STYPE_ICON_REQUEST)
+/* The first MBO ANQP-element can be included in the optimized bitmap. */
+#define ANQP_REQ_MBO_CELL_DATA_CONN_PREF \
+	(BIT(29) << MBO_ANQP_SUBTYPE_CELL_CONN_PREF)
 
 struct gas_dialog_info {
 	u8 valid;
@@ -68,6 +71,7 @@
 	size_t sd_resp_pos; /* Offset in sd_resp */
 	u8 sd_frag_id;
 	int prot; /* whether Protected Dual of Public Action frame is used */
+	int dpp; /* whether this is a DPP Config Response */
 };
 
 struct hostapd_data;
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index cf8a8cb..12911df 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -31,6 +31,8 @@
 #include "vlan_init.h"
 #include "wpa_auth.h"
 #include "wps_hostapd.h"
+#include "dpp_hostapd.h"
+#include "gas_query_ap.h"
 #include "hw_features.h"
 #include "wpa_auth_glue.h"
 #include "ap_drv_ops.h"
@@ -46,6 +48,7 @@
 #include "neighbor_db.h"
 #include "rrm.h"
 #include "fils_hlp.h"
+#include "acs.h"
 
 
 static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
@@ -301,6 +304,10 @@
 #endif /* CONFIG_NO_RADIUS */
 
 	hostapd_deinit_wps(hapd);
+#ifdef CONFIG_DPP
+	hostapd_dpp_deinit(hapd);
+	gas_query_ap_deinit(hapd->gas);
+#endif /* CONFIG_DPP */
 
 	authsrv_deinit(hapd);
 
@@ -394,8 +401,11 @@
 	hostapd_stop_setup_timers(iface);
 #endif /* NEED_AP_MLME */
 #endif /* CONFIG_IEEE80211N */
+	if (iface->current_mode)
+		acs_cleanup(iface);
 	hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
 	iface->hw_features = NULL;
+	iface->current_mode = NULL;
 	os_free(iface->current_rates);
 	iface->current_rates = NULL;
 	os_free(iface->basic_rates);
@@ -491,9 +501,12 @@
 			ret = -1;
 		}
 	}
-	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Deauthenticate all stations");
-	os_memset(addr, 0xff, ETH_ALEN);
-	hostapd_drv_sta_deauth(hapd, addr, reason);
+	if (hapd->conf && hapd->conf->broadcast_deauth) {
+		wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+			"Deauthenticate all stations");
+		os_memset(addr, 0xff, ETH_ALEN);
+		hostapd_drv_sta_deauth(hapd, addr, reason);
+	}
 	hostapd_free_stas(hapd);
 
 	return ret;
@@ -969,7 +982,7 @@
 #endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_MESH
-	if (hapd->iface->mconf == NULL)
+	if ((hapd->conf->mesh & MESH_ENABLED) && hapd->iface->mconf == NULL)
 		flush_old_stations = 0;
 #endif /* CONFIG_MESH */
 
@@ -1070,6 +1083,14 @@
 	if (hostapd_init_wps(hapd, conf))
 		return -1;
 
+#ifdef CONFIG_DPP
+	hapd->gas = gas_query_ap_init(hapd, hapd->msg_ctx);
+	if (!hapd->gas)
+		return -1;
+	if (hostapd_dpp_init(hapd))
+		return -1;
+#endif /* CONFIG_DPP */
+
 	if (authsrv_init(hapd) < 0)
 		return -1;
 
@@ -1157,7 +1178,7 @@
 	struct hostapd_tx_queue_params *p;
 
 #ifdef CONFIG_MESH
-	if (iface->mconf == NULL)
+	if ((hapd->conf->mesh & MESH_ENABLED) && iface->mconf == NULL)
 		return;
 #endif /* CONFIG_MESH */
 
@@ -1659,6 +1680,102 @@
 }
 
 
+#ifdef CONFIG_OWE
+
+static int hostapd_owe_iface_iter(struct hostapd_iface *iface, void *ctx)
+{
+	struct hostapd_data *hapd = ctx;
+	size_t i;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		struct hostapd_data *bss = iface->bss[i];
+
+		if (os_strcmp(hapd->conf->owe_transition_ifname,
+			      bss->conf->iface) != 0)
+			continue;
+
+		wpa_printf(MSG_DEBUG,
+			   "OWE: ifname=%s found transition mode ifname=%s BSSID "
+			   MACSTR " SSID %s",
+			   hapd->conf->iface, bss->conf->iface,
+			   MAC2STR(bss->own_addr),
+			   wpa_ssid_txt(bss->conf->ssid.ssid,
+					bss->conf->ssid.ssid_len));
+		if (!bss->conf->ssid.ssid_set || !bss->conf->ssid.ssid_len ||
+		    is_zero_ether_addr(bss->own_addr))
+			continue;
+
+		os_memcpy(hapd->conf->owe_transition_bssid, bss->own_addr,
+			  ETH_ALEN);
+		os_memcpy(hapd->conf->owe_transition_ssid,
+			  bss->conf->ssid.ssid, bss->conf->ssid.ssid_len);
+		hapd->conf->owe_transition_ssid_len = bss->conf->ssid.ssid_len;
+		wpa_printf(MSG_DEBUG,
+			   "OWE: Copied transition mode information");
+		return 1;
+	}
+
+	return 0;
+}
+
+
+int hostapd_owe_trans_get_info(struct hostapd_data *hapd)
+{
+	if (hapd->conf->owe_transition_ssid_len > 0 &&
+	    !is_zero_ether_addr(hapd->conf->owe_transition_bssid))
+		return 0;
+
+	/* Find transition mode SSID/BSSID information from a BSS operated by
+	 * this hostapd instance. */
+	if (!hapd->iface->interfaces ||
+	    !hapd->iface->interfaces->for_each_interface)
+		return hostapd_owe_iface_iter(hapd->iface, hapd);
+	else
+		return hapd->iface->interfaces->for_each_interface(
+			hapd->iface->interfaces, hostapd_owe_iface_iter, hapd);
+}
+
+
+static int hostapd_owe_iface_iter2(struct hostapd_iface *iface, void *ctx)
+{
+	size_t i;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		struct hostapd_data *bss = iface->bss[i];
+		int res;
+
+		if (!bss->conf->owe_transition_ifname[0])
+			continue;
+		res = hostapd_owe_trans_get_info(bss);
+		if (res == 0)
+			continue;
+		wpa_printf(MSG_DEBUG,
+			   "OWE: Matching transition mode interface enabled - update beacon data for %s",
+			   bss->conf->iface);
+		ieee802_11_set_beacon(bss);
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_OWE */
+
+
+static void hostapd_owe_update_trans(struct hostapd_iface *iface)
+{
+#ifdef CONFIG_OWE
+	/* Check whether the enabled BSS can complete OWE transition mode
+	 * configuration for any pending interface. */
+	if (!iface->interfaces ||
+	    !iface->interfaces->for_each_interface)
+		hostapd_owe_iface_iter2(iface, NULL);
+	else
+		iface->interfaces->for_each_interface(
+			iface->interfaces, hostapd_owe_iface_iter2, NULL);
+#endif /* CONFIG_OWE */
+}
+
+
 static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
 						 int err)
 {
@@ -1834,6 +1951,7 @@
 #endif /* CONFIG_FST */
 
 	hostapd_set_state(iface, HAPD_IFACE_ENABLED);
+	hostapd_owe_update_trans(iface);
 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED);
 	if (hapd->setup_complete_cb)
 		hapd->setup_complete_cb(hapd->setup_complete_cb_ctx);
@@ -2010,6 +2128,10 @@
 	dl_list_init(&hapd->ctrl_dst);
 	dl_list_init(&hapd->nr_db);
 	hapd->dhcp_sock = -1;
+#ifdef CONFIG_IEEE80211R_AP
+	dl_list_init(&hapd->l2_queue);
+	dl_list_init(&hapd->l2_oui_queue);
+#endif /* CONFIG_IEEE80211R_AP */
 
 	return hapd;
 }
@@ -2621,6 +2743,7 @@
 				return -1;
 			}
 		}
+		hostapd_owe_update_trans(hapd_iface);
 		return 0;
 	}
 
@@ -2838,6 +2961,9 @@
 	ieee802_1x_new_station(hapd, sta);
 	if (reassoc) {
 		if (sta->auth_alg != WLAN_AUTH_FT &&
+		    sta->auth_alg != WLAN_AUTH_FILS_SK &&
+		    sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
+		    sta->auth_alg != WLAN_AUTH_FILS_PK &&
 		    !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)))
 			wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH);
 	} else
@@ -2946,60 +3072,52 @@
 		goto free_ap_params;
 
 	ret = -1;
-	beacon->head = os_malloc(params.head_len);
+	beacon->head = os_memdup(params.head, params.head_len);
 	if (!beacon->head)
 		goto free_ap_extra_ies;
 
-	os_memcpy(beacon->head, params.head, params.head_len);
 	beacon->head_len = params.head_len;
 
-	beacon->tail = os_malloc(params.tail_len);
+	beacon->tail = os_memdup(params.tail, params.tail_len);
 	if (!beacon->tail)
 		goto free_beacon;
 
-	os_memcpy(beacon->tail, params.tail, params.tail_len);
 	beacon->tail_len = params.tail_len;
 
 	if (params.proberesp != NULL) {
-		beacon->probe_resp = os_malloc(params.proberesp_len);
+		beacon->probe_resp = os_memdup(params.proberesp,
+					       params.proberesp_len);
 		if (!beacon->probe_resp)
 			goto free_beacon;
 
-		os_memcpy(beacon->probe_resp, params.proberesp,
-			  params.proberesp_len);
 		beacon->probe_resp_len = params.proberesp_len;
 	}
 
 	/* copy the extra ies */
 	if (beacon_extra) {
-		beacon->beacon_ies = os_malloc(wpabuf_len(beacon_extra));
+		beacon->beacon_ies = os_memdup(beacon_extra->buf,
+					       wpabuf_len(beacon_extra));
 		if (!beacon->beacon_ies)
 			goto free_beacon;
 
-		os_memcpy(beacon->beacon_ies,
-			  beacon_extra->buf, wpabuf_len(beacon_extra));
 		beacon->beacon_ies_len = wpabuf_len(beacon_extra);
 	}
 
 	if (proberesp_extra) {
-		beacon->proberesp_ies =
-			os_malloc(wpabuf_len(proberesp_extra));
+		beacon->proberesp_ies = os_memdup(proberesp_extra->buf,
+						  wpabuf_len(proberesp_extra));
 		if (!beacon->proberesp_ies)
 			goto free_beacon;
 
-		os_memcpy(beacon->proberesp_ies, proberesp_extra->buf,
-			  wpabuf_len(proberesp_extra));
 		beacon->proberesp_ies_len = wpabuf_len(proberesp_extra);
 	}
 
 	if (assocresp_extra) {
-		beacon->assocresp_ies =
-			os_malloc(wpabuf_len(assocresp_extra));
+		beacon->assocresp_ies = os_memdup(assocresp_extra->buf,
+						  wpabuf_len(assocresp_extra));
 		if (!beacon->assocresp_ies)
 			goto free_beacon;
 
-		os_memcpy(beacon->assocresp_ies, assocresp_extra->buf,
-			  wpabuf_len(assocresp_extra));
 		beacon->assocresp_ies_len = wpabuf_len(assocresp_extra);
 	}
 
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 5ab623d..f77e6ec 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -53,6 +53,9 @@
 #ifndef CONFIG_NO_VLAN
 	struct dynamic_iface *vlan_priv;
 #endif /* CONFIG_NO_VLAN */
+#ifdef CONFIG_ETH_P_OUI
+	struct dl_list eth_p_oui; /* OUI Extended EtherType handlers */
+#endif /* CONFIG_ETH_P_OUI */
 	int eloop_initialized;
 };
 
@@ -185,6 +188,17 @@
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
 
 	struct l2_packet_data *l2;
+
+#ifdef CONFIG_IEEE80211R_AP
+	struct dl_list l2_queue;
+	struct dl_list l2_oui_queue;
+	struct eth_p_oui_ctx *oui_pull;
+	struct eth_p_oui_ctx *oui_resp;
+	struct eth_p_oui_ctx *oui_push;
+	struct eth_p_oui_ctx *oui_sreq;
+	struct eth_p_oui_ctx *oui_sresp;
+#endif /* CONFIG_IEEE80211R_AP */
+
 	struct wps_context *wps;
 
 	int beacon_set_done;
@@ -290,10 +304,27 @@
 	unsigned int ext_eapol_frame_io:1;
 
 	struct l2_packet_data *l2_test;
+
+	enum wpa_alg last_gtk_alg;
+	int last_gtk_key_idx;
+	u8 last_gtk[WPA_GTK_MAX_LEN];
+	size_t last_gtk_len;
+
+#ifdef CONFIG_IEEE80211W
+	enum wpa_alg last_igtk_alg;
+	int last_igtk_key_idx;
+	u8 last_igtk[WPA_IGTK_MAX_LEN];
+	size_t last_igtk_len;
+#endif /* CONFIG_IEEE80211W */
 #endif /* CONFIG_TESTING_OPTIONS */
 
 #ifdef CONFIG_MBO
 	unsigned int mbo_assoc_disallow;
+	/**
+	 * enable_oce - Enable OCE if it is enabled by user and device also
+	 *		supports OCE.
+	 */
+	u8 enable_oce;
 #endif /* CONFIG_MBO */
 
 	struct dl_list nr_db;
@@ -305,6 +336,29 @@
 	unsigned int range_req_active:1;
 
 	int dhcp_sock; /* UDP socket used with the DHCP server */
+
+#ifdef CONFIG_DPP
+	struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */
+	struct dl_list dpp_configurator; /* struct dpp_configurator */
+	int dpp_init_done;
+	struct dpp_authentication *dpp_auth;
+	u8 dpp_allowed_roles;
+	int dpp_qr_mutual;
+	int dpp_auth_ok_on_ack;
+	struct gas_query_ap *gas;
+	struct dpp_pkex *dpp_pkex;
+	struct dpp_bootstrap_info *dpp_pkex_bi;
+	char *dpp_pkex_code;
+	char *dpp_pkex_identifier;
+	char *dpp_pkex_auth_cmd;
+	char *dpp_configurator_params;
+#ifdef CONFIG_TESTING_OPTIONS
+	char *dpp_config_obj_override;
+	char *dpp_discovery_override;
+	char *dpp_groups_override;
+	unsigned int dpp_ignore_netaccesskey_mismatch:1;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_DPP */
 };
 
 
@@ -461,6 +515,8 @@
 
 	struct dl_list sta_seen; /* struct hostapd_sta_info */
 	unsigned int num_sta_seen;
+
+	u8 dfs_domain;
 };
 
 /* hostapd.c */
@@ -501,6 +557,7 @@
 				const struct hostapd_freq_params *freq_params);
 void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
 void hostapd_periodic_iface(struct hostapd_iface *iface);
+int hostapd_owe_trans_get_info(struct hostapd_data *hapd);
 
 /* utils.c */
 int hostapd_register_probereq_cb(struct hostapd_data *hapd,
@@ -512,6 +569,8 @@
 void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr);
 
 /* drv_callbacks.c (TODO: move to somewhere else?) */
+void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd,
+				      struct sta_info *sta);
 int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
 			const u8 *ie, size_t ielen, int reassoc);
 void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 2d6cef1..84e74ee 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -78,10 +78,12 @@
 	int i, j;
 	u16 num_modes, flags;
 	struct hostapd_hw_modes *modes;
+	u8 dfs_domain;
 
 	if (hostapd_drv_none(hapd))
 		return -1;
-	modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags);
+	modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags,
+					    &dfs_domain);
 	if (modes == NULL) {
 		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
@@ -91,6 +93,7 @@
 	}
 
 	iface->hw_flags = flags;
+	iface->dfs_domain = dfs_domain;
 
 	hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
 	iface->hw_features = modes;
@@ -331,6 +334,7 @@
 		iface->conf->secondary_channel = 0;
 		iface->conf->vht_oper_centr_freq_seg0_idx = 0;
 		iface->conf->vht_oper_centr_freq_seg1_idx = 0;
+		iface->conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT;
 		res = 1;
 		wpa_printf(MSG_INFO, "Fallback to 20 MHz");
 	}
@@ -722,14 +726,33 @@
 
 static int hostapd_is_usable_chans(struct hostapd_iface *iface)
 {
+	int secondary_chan;
+
 	if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1))
 		return 0;
 
 	if (!iface->conf->secondary_channel)
 		return 1;
 
-	return hostapd_is_usable_chan(iface, iface->conf->channel +
-				      iface->conf->secondary_channel * 4, 0);
+	if (!iface->conf->ht40_plus_minus_allowed)
+		return hostapd_is_usable_chan(
+			iface, iface->conf->channel +
+			iface->conf->secondary_channel * 4, 0);
+
+	/* Both HT40+ and HT40- are set, pick a valid secondary channel */
+	secondary_chan = iface->conf->channel + 4;
+	if (hostapd_is_usable_chan(iface, secondary_chan, 0)) {
+		iface->conf->secondary_channel = 1;
+		return 1;
+	}
+
+	secondary_chan = iface->conf->channel - 4;
+	if (hostapd_is_usable_chan(iface, secondary_chan, 0)) {
+		iface->conf->secondary_channel = -1;
+		return 1;
+	}
+
+	return 0;
 }
 
 
@@ -909,5 +932,19 @@
 
 int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
 {
-	return hw_get_chan(hapd->iface->current_mode, freq);
+	int i, channel;
+	struct hostapd_hw_modes *mode;
+
+	channel = hw_get_chan(hapd->iface->current_mode, freq);
+	if (channel)
+		return channel;
+	/* Check other available modes since the channel list for the current
+	 * mode did not include the specified frequency. */
+	for (i = 0; i < hapd->iface->num_hw_features; i++) {
+		mode = &hapd->iface->hw_features[i];
+		channel = hw_get_chan(mode, freq);
+		if (channel)
+			return channel;
+	}
+	return 0;
 }
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index c6234dc..65c4d88 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -14,6 +14,8 @@
 #include "utils/eloop.h"
 #include "crypto/crypto.h"
 #include "crypto/sha256.h"
+#include "crypto/sha384.h"
+#include "crypto/sha512.h"
 #include "crypto/random.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
@@ -46,8 +48,20 @@
 #include "rrm.h"
 #include "taxonomy.h"
 #include "fils_hlp.h"
+#include "dpp_hostapd.h"
+#include "gas_query_ap.h"
 
 
+#ifdef CONFIG_FILS
+static struct wpabuf *
+prepare_auth_resp_fils(struct hostapd_data *hapd,
+		       struct sta_info *sta, u16 *resp,
+		       struct rsn_pmksa_cache_entry *pmksa,
+		       struct wpabuf *erp_resp,
+		       const u8 *msk, size_t msk_len,
+		       int *is_pub);
+#endif /* CONFIG_FILS */
+
 u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
 {
 	u8 *pos = eid;
@@ -347,16 +361,19 @@
 					     struct sta_info *sta, int update)
 {
 	struct wpabuf *buf;
+	const char *password;
 
-	if (hapd->conf->ssid.wpa_passphrase == NULL) {
+	password = hapd->conf->sae_password;
+	if (!password)
+		password = hapd->conf->ssid.wpa_passphrase;
+	if (!password) {
 		wpa_printf(MSG_DEBUG, "SAE: No password available");
 		return NULL;
 	}
 
 	if (update &&
 	    sae_prepare_commit(hapd->own_addr, sta->addr,
-			       (u8 *) hapd->conf->ssid.wpa_passphrase,
-			       os_strlen(hapd->conf->ssid.wpa_passphrase),
+			       (u8 *) password, os_strlen(password),
 			       sta->sae) < 0) {
 		wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
 		return NULL;
@@ -774,6 +791,27 @@
 	int resp = WLAN_STATUS_SUCCESS;
 	struct wpabuf *data = NULL;
 
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->conf->sae_reflection_attack && auth_transaction == 1) {
+		const u8 *pos, *end;
+
+		wpa_printf(MSG_DEBUG, "SAE: TESTING - reflection attack");
+		pos = mgmt->u.auth.variable;
+		end = ((const u8 *) mgmt) + len;
+		send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+				auth_transaction, resp, pos, end - pos);
+		goto remove_sta;
+	}
+
+	if (hapd->conf->sae_commit_override && auth_transaction == 1) {
+		wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override");
+		send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+				auth_transaction, resp,
+				wpabuf_head(hapd->conf->sae_commit_override),
+				wpabuf_len(hapd->conf->sae_commit_override));
+		goto remove_sta;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 	if (!sta->sae) {
 		if (auth_transaction != 1 ||
 		    status_code != WLAN_STATUS_SUCCESS) {
@@ -999,6 +1037,8 @@
 #endif /* CONFIG_IEEE80211W */
 	if (res == WPA_INVALID_MDIE)
 		return WLAN_STATUS_INVALID_MDIE;
+	if (res == WPA_INVALID_PMKID)
+		return WLAN_STATUS_INVALID_PMKID;
 	if (res != WPA_IE_OK)
 		return WLAN_STATUS_INVALID_IE;
 	return WLAN_STATUS_SUCCESS;
@@ -1009,16 +1049,17 @@
 
 static void handle_auth_fils_finish(struct hostapd_data *hapd,
 				    struct sta_info *sta, u16 resp,
-				    struct rsn_pmksa_cache_entry *pmksa,
-				    struct wpabuf *erp_resp,
-				    const u8 *msk, size_t msk_len);
+				    struct wpabuf *data, int pub);
 
-static void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
-			    const struct ieee80211_mgmt *mgmt, size_t len,
-			    u16 auth_transaction, u16 status_code)
+void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
+		      const u8 *pos, size_t len, u16 auth_alg,
+		      u16 auth_transaction, u16 status_code,
+		      void (*cb)(struct hostapd_data *hapd,
+				 struct sta_info *sta, u16 resp,
+				 struct wpabuf *data, int pub))
 {
 	u16 resp = WLAN_STATUS_SUCCESS;
-	const u8 *pos, *end;
+	const u8 *end;
 	struct ieee802_11_elems elems;
 	int res;
 	struct wpa_ie_data rsn;
@@ -1027,14 +1068,83 @@
 	if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS)
 		return;
 
-	pos = mgmt->u.auth.variable;
-	end = ((const u8 *) mgmt) + len;
+	end = pos + len;
 
 	wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields",
 		    pos, end - pos);
 
-	/* TODO: Finite Cyclic Group when using PK or PFS */
-	/* TODO: Element when using PK or PFS */
+	/* TODO: FILS PK */
+#ifdef CONFIG_FILS_SK_PFS
+	if (auth_alg == WLAN_AUTH_FILS_SK_PFS) {
+		u16 group;
+		struct wpabuf *pub;
+		size_t elem_len;
+
+		/* Using FILS PFS */
+
+		/* Finite Cyclic Group */
+		if (end - pos < 2) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: No room for Finite Cyclic Group");
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		group = WPA_GET_LE16(pos);
+		pos += 2;
+		if (group != hapd->conf->fils_dh_group) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Unsupported Finite Cyclic Group: %u (expected %u)",
+				   group, hapd->conf->fils_dh_group);
+			resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+			goto fail;
+		}
+
+		crypto_ecdh_deinit(sta->fils_ecdh);
+		sta->fils_ecdh = crypto_ecdh_init(group);
+		if (!sta->fils_ecdh) {
+			wpa_printf(MSG_INFO,
+				   "FILS: Could not initialize ECDH with group %d",
+				   group);
+			resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+			goto fail;
+		}
+
+		pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1);
+		if (!pub) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Failed to derive ECDH public key");
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		elem_len = wpabuf_len(pub);
+		wpabuf_free(pub);
+
+		/* Element */
+		if ((size_t) (end - pos) < elem_len) {
+			wpa_printf(MSG_DEBUG, "FILS: No room for Element");
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+
+		wpabuf_free(sta->fils_g_sta);
+		sta->fils_g_sta = wpabuf_alloc_copy(pos, elem_len);
+		wpabuf_clear_free(sta->fils_dh_ss);
+		sta->fils_dh_ss = crypto_ecdh_set_peerkey(sta->fils_ecdh, 1,
+							  pos, elem_len);
+		if (!sta->fils_dh_ss) {
+			wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed");
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", sta->fils_dh_ss);
+		pos += elem_len;
+	} else {
+		crypto_ecdh_deinit(sta->fils_ecdh);
+		sta->fils_ecdh = NULL;
+		wpabuf_clear_free(sta->fils_dh_ss);
+		sta->fils_dh_ss = NULL;
+	}
+#endif /* CONFIG_FILS_SK_PFS */
 
 	wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos);
 	if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) {
@@ -1066,14 +1176,11 @@
 
 	res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
 				  elems.rsn_ie - 2, elems.rsn_ie_len + 2,
-				  elems.mdie, elems.mdie_len);
+				  elems.mdie, elems.mdie_len, NULL, 0);
 	resp = wpa_res_to_status_code(res);
 	if (resp != WLAN_STATUS_SUCCESS)
 		goto fail;
 
-	/* TODO: MDE when using FILS+FT */
-	/* TODO: FTE when using FILS+FT */
-
 	if (!elems.fils_nonce) {
 		wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
 		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -1099,6 +1206,11 @@
 						   pmkid);
 			if (pmksa)
 				break;
+			pmksa = wpa_auth_pmksa_get_fils_cache_id(hapd->wpa_auth,
+								 sta->addr,
+								 pmkid);
+			if (pmksa)
+				break;
 			pmkid += PMKID_LEN;
 			num--;
 		}
@@ -1134,13 +1246,22 @@
 					ieee802_1x_alloc_eapol_sm(hapd, sta);
 			}
 			wpa_printf(MSG_DEBUG,
-				   "FILS: Forward EAP-Identity/Re-auth Start to authentication server");
+				   "FILS: Forward EAP-Initiate/Re-auth to authentication server");
 			ieee802_1x_encapsulate_radius(
 				hapd, sta, elems.fils_wrapped_data,
 				elems.fils_wrapped_data_len);
+			sta->fils_pending_cb = cb;
 			wpa_printf(MSG_DEBUG,
 				   "FILS: Will send Authentication frame once the response from authentication server is available");
 			sta->flags |= WLAN_STA_PENDING_FILS_ERP;
+			/* Calculate pending PMKID here so that we do not need
+			 * to maintain a copy of the EAP-Initiate/Reauth
+			 * message. */
+			if (fils_pmkid_erp(wpa_auth_sta_key_mgmt(sta->wpa_sm),
+					   elems.fils_wrapped_data,
+					   elems.fils_wrapped_data_len,
+					   sta->fils_erp_pmkid) == 0)
+				sta->fils_erp_pmkid_set = 1;
 			return;
 #else /* CONFIG_NO_RADIUS */
 			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -1150,15 +1271,30 @@
 	}
 
 fail:
-	handle_auth_fils_finish(hapd, sta, resp, pmksa, NULL, NULL, 0);
+	if (cb) {
+		struct wpabuf *data;
+		int pub = 0;
+
+		data = prepare_auth_resp_fils(hapd, sta, &resp, pmksa, NULL,
+					      NULL, 0, &pub);
+		if (!data) {
+			wpa_printf(MSG_DEBUG,
+				   "%s: prepare_auth_resp_fils() returned failure",
+				   __func__);
+		}
+
+		cb(hapd, sta, resp, data, pub);
+	}
 }
 
 
-static void handle_auth_fils_finish(struct hostapd_data *hapd,
-				    struct sta_info *sta, u16 resp,
-				    struct rsn_pmksa_cache_entry *pmksa,
-				    struct wpabuf *erp_resp,
-				    const u8 *msk, size_t msk_len)
+static struct wpabuf *
+prepare_auth_resp_fils(struct hostapd_data *hapd,
+		       struct sta_info *sta, u16 *resp,
+		       struct rsn_pmksa_cache_entry *pmksa,
+		       struct wpabuf *erp_resp,
+		       const u8 *msk, size_t msk_len,
+		       int *is_pub)
 {
 	u8 fils_nonce[FILS_NONCE_LEN];
 	size_t ielen;
@@ -1168,51 +1304,86 @@
 	const u8 *pmk = NULL;
 	size_t pmk_len = 0;
 	u8 pmk_buf[PMK_LEN_MAX];
+	struct wpabuf *pub = NULL;
 
-	if (resp != WLAN_STATUS_SUCCESS)
+	if (*resp != WLAN_STATUS_SUCCESS)
 		goto fail;
 
 	ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen);
 	if (!ie) {
-		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 		goto fail;
 	}
+
 	if (pmksa) {
 		/* Add PMKID of the selected PMKSA into RSNE */
 		ie_buf = os_malloc(ielen + 2 + 2 + PMKID_LEN);
 		if (!ie_buf) {
-			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 			goto fail;
 		}
+
 		os_memcpy(ie_buf, ie, ielen);
 		if (wpa_insert_pmkid(ie_buf, &ielen, pmksa->pmkid) < 0) {
-			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 			goto fail;
 		}
 		ie = ie_buf;
 	}
 
 	if (random_get_bytes(fils_nonce, FILS_NONCE_LEN) < 0) {
-		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 		goto fail;
 	}
 	wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce",
 		    fils_nonce, FILS_NONCE_LEN);
 
-	data = wpabuf_alloc(1000 + ielen);
+#ifdef CONFIG_FILS_SK_PFS
+	if (sta->fils_dh_ss && sta->fils_ecdh) {
+		pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1);
+		if (!pub) {
+			*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+	}
+#endif /* CONFIG_FILS_SK_PFS */
+
+	data = wpabuf_alloc(1000 + ielen + (pub ? wpabuf_len(pub) : 0));
 	if (!data) {
-		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 		goto fail;
 	}
 
-	/* TODO: Finite Cyclic Group when using PK or PFS */
-	/* TODO: Element when using PK or PFS */
+	/* TODO: FILS PK */
+#ifdef CONFIG_FILS_SK_PFS
+	if (pub) {
+		/* Finite Cyclic Group */
+		wpabuf_put_le16(data, hapd->conf->fils_dh_group);
+
+		/* Element */
+		wpabuf_put_buf(data, pub);
+	}
+#endif /* CONFIG_FILS_SK_PFS */
 
 	/* RSNE */
 	wpabuf_put_data(data, ie, ielen);
 
-	/* TODO: MDE when using FILS+FT */
-	/* TODO: FTE when using FILS+FT */
+	/* MDE when using FILS+FT (already included in ie,ielen with RSNE) */
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm))) {
+		/* FTE[R1KH-ID,R0KH-ID] when using FILS+FT */
+		int res;
+
+		res = wpa_auth_write_fte(hapd->wpa_auth, wpabuf_put(data, 0),
+					 wpabuf_tailroom(data));
+		if (res < 0) {
+			*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		wpabuf_put(data, res);
+	}
+#endif /* CONFIG_IEEE80211R_AP */
 
 	/* FILS Nonce */
 	wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
@@ -1238,14 +1409,41 @@
 
 		if (fils_rmsk_to_pmk(wpa_auth_sta_key_mgmt(sta->wpa_sm),
 				     msk, msk_len, sta->fils_snonce, fils_nonce,
-				     NULL, 0, pmk_buf, &pmk_len)) {
+				     sta->fils_dh_ss ?
+				     wpabuf_head(sta->fils_dh_ss) : NULL,
+				     sta->fils_dh_ss ?
+				     wpabuf_len(sta->fils_dh_ss) : 0,
+				     pmk_buf, &pmk_len)) {
 			wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK");
-			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 			wpabuf_free(data);
 			data = NULL;
 			goto fail;
 		}
 		pmk = pmk_buf;
+
+		/* Don't use DHss in PTK derivation if PMKSA caching is not
+		 * used. */
+		wpabuf_clear_free(sta->fils_dh_ss);
+		sta->fils_dh_ss = NULL;
+
+		if (sta->fils_erp_pmkid_set) {
+			/* TODO: get PMKLifetime from WPA parameters */
+			unsigned int dot11RSNAConfigPMKLifetime = 43200;
+
+			sta->fils_erp_pmkid_set = 0;
+			if (wpa_auth_pmksa_add2(
+				    hapd->wpa_auth, sta->addr,
+				    pmk, pmk_len,
+				    sta->fils_erp_pmkid,
+				    sta->session_timeout_set ?
+				    sta->session_timeout :
+				    dot11RSNAConfigPMKLifetime,
+				    wpa_auth_sta_key_mgmt(sta->wpa_sm)) < 0) {
+				wpa_printf(MSG_ERROR,
+					   "FILS: Failed to add PMKSA cache entry based on ERP");
+			}
+		}
 	} else if (pmksa) {
 		pmk = pmksa->pmk;
 		pmk_len = pmksa->pmk_len;
@@ -1253,23 +1451,50 @@
 
 	if (!pmk) {
 		wpa_printf(MSG_DEBUG, "FILS: No PMK available");
-		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 		wpabuf_free(data);
 		data = NULL;
 		goto fail;
 	}
 
 	if (fils_auth_pmk_to_ptk(sta->wpa_sm, pmk, pmk_len,
-				 sta->fils_snonce, fils_nonce) < 0) {
-		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				 sta->fils_snonce, fils_nonce,
+				 sta->fils_dh_ss ?
+				 wpabuf_head(sta->fils_dh_ss) : NULL,
+				 sta->fils_dh_ss ?
+				 wpabuf_len(sta->fils_dh_ss) : 0,
+				 sta->fils_g_sta, pub) < 0) {
+		*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 		wpabuf_free(data);
 		data = NULL;
 		goto fail;
 	}
 
 fail:
-	send_auth_reply(hapd, sta->addr, hapd->own_addr, WLAN_AUTH_FILS_SK, 2,
-			resp,
+	if (is_pub)
+		*is_pub = pub != NULL;
+	os_free(ie_buf);
+	wpabuf_free(pub);
+	wpabuf_clear_free(sta->fils_dh_ss);
+	sta->fils_dh_ss = NULL;
+#ifdef CONFIG_FILS_SK_PFS
+	crypto_ecdh_deinit(sta->fils_ecdh);
+	sta->fils_ecdh = NULL;
+#endif /* CONFIG_FILS_SK_PFS */
+	return data;
+}
+
+
+static void handle_auth_fils_finish(struct hostapd_data *hapd,
+				    struct sta_info *sta, u16 resp,
+				    struct wpabuf *data, int pub)
+{
+	u16 auth_alg;
+
+	auth_alg = (pub ||
+		    resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) ?
+		WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
+	send_auth_reply(hapd, sta->addr, hapd->own_addr, auth_alg, 2, resp,
 			data ? wpabuf_head(data) : (u8 *) "",
 			data ? wpabuf_len(data) : 0);
 	wpabuf_free(data);
@@ -1280,11 +1505,9 @@
 			       "authentication OK (FILS)");
 		sta->flags |= WLAN_STA_AUTH;
 		wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
-		sta->auth_alg = WLAN_AUTH_FILS_SK;
+		sta->auth_alg = pub ? WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
 		mlme_authenticate_indication(hapd, sta);
 	}
-
-	os_free(ie_buf);
 }
 
 
@@ -1293,10 +1516,23 @@
 				 struct wpabuf *erp_resp,
 				 const u8 *msk, size_t msk_len)
 {
+	struct wpabuf *data;
+	int pub = 0;
+	u16 resp;
+
 	sta->flags &= ~WLAN_STA_PENDING_FILS_ERP;
-	handle_auth_fils_finish(hapd, sta, success ? WLAN_STATUS_SUCCESS :
-				WLAN_STATUS_UNSPECIFIED_FAILURE, NULL,
-				erp_resp, msk, msk_len);
+
+	if (!sta->fils_pending_cb)
+		return;
+	resp = success ? WLAN_STATUS_SUCCESS : WLAN_STATUS_UNSPECIFIED_FAILURE;
+	data = prepare_auth_resp_fils(hapd, sta, &resp, NULL, erp_resp,
+				      msk, msk_len, &pub);
+	if (!data) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: prepare_auth_resp_fils() returned failure",
+			   __func__);
+	}
+	sta->fils_pending_cb(hapd, sta, resp, data, pub);
 }
 
 #endif /* CONFIG_FILS */
@@ -1467,6 +1703,9 @@
 #ifdef CONFIG_FILS
 	      (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
 	       auth_alg == WLAN_AUTH_FILS_SK) ||
+	      (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
+	       hapd->conf->fils_dh_group &&
+	       auth_alg == WLAN_AUTH_FILS_SK_PFS) ||
 #endif /* CONFIG_FILS */
 	      ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
 	       auth_alg == WLAN_AUTH_SHARED_KEY))) {
@@ -1733,8 +1972,11 @@
 #endif /* CONFIG_SAE */
 #ifdef CONFIG_FILS
 	case WLAN_AUTH_FILS_SK:
-		handle_auth_fils(hapd, sta, mgmt, len, auth_transaction,
-				 status_code);
+	case WLAN_AUTH_FILS_SK_PFS:
+		handle_auth_fils(hapd, sta, mgmt->u.auth.variable,
+				 len - IEEE80211_HDRLEN - sizeof(mgmt->u.auth),
+				 auth_alg, auth_transaction, status_code,
+				 handle_auth_fils_finish);
 		return;
 #endif /* CONFIG_FILS */
 	}
@@ -1888,6 +2130,173 @@
 }
 
 
+#ifdef CONFIG_OWE
+
+static int owe_group_supported(struct hostapd_data *hapd, u16 group)
+{
+	int i;
+	int *groups = hapd->conf->owe_groups;
+
+	if (group != 19 && group != 20 && group != 21)
+		return 0;
+
+	if (!groups)
+		return 1;
+
+	for (i = 0; groups[i] > 0; i++) {
+		if (groups[i] == group)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static u16 owe_process_assoc_req(struct hostapd_data *hapd,
+				 struct sta_info *sta, const u8 *owe_dh,
+				 u8 owe_dh_len)
+{
+	struct wpabuf *secret, *pub, *hkey;
+	int res;
+	u8 prk[SHA512_MAC_LEN], pmkid[SHA512_MAC_LEN];
+	const char *info = "OWE Key Generation";
+	const u8 *addr[2];
+	size_t len[2];
+	u16 group;
+	size_t hash_len, prime_len;
+
+	if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
+		wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching");
+		return WLAN_STATUS_SUCCESS;
+	}
+
+	group = WPA_GET_LE16(owe_dh);
+	if (!owe_group_supported(hapd, group)) {
+		wpa_printf(MSG_DEBUG, "OWE: Unsupported DH group %u", group);
+		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+	}
+	if (group == 19)
+		prime_len = 32;
+	else if (group == 20)
+		prime_len = 48;
+	else if (group == 21)
+		prime_len = 66;
+	else
+		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+
+	crypto_ecdh_deinit(sta->owe_ecdh);
+	sta->owe_ecdh = crypto_ecdh_init(group);
+	if (!sta->owe_ecdh)
+		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+	sta->owe_group = group;
+
+	secret = crypto_ecdh_set_peerkey(sta->owe_ecdh, 0, owe_dh + 2,
+					 owe_dh_len - 2);
+	secret = wpabuf_zeropad(secret, prime_len);
+	if (!secret) {
+		wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret);
+
+	/* prk = HKDF-extract(C | A | group, z) */
+
+	pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
+	if (!pub) {
+		wpabuf_clear_free(secret);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	/* PMKID = Truncate-128(Hash(C | A)) */
+	addr[0] = owe_dh + 2;
+	len[0] = owe_dh_len - 2;
+	addr[1] = wpabuf_head(pub);
+	len[1] = wpabuf_len(pub);
+	if (group == 19) {
+		res = sha256_vector(2, addr, len, pmkid);
+		hash_len = SHA256_MAC_LEN;
+	} else if (group == 20) {
+		res = sha384_vector(2, addr, len, pmkid);
+		hash_len = SHA384_MAC_LEN;
+	} else if (group == 21) {
+		res = sha512_vector(2, addr, len, pmkid);
+		hash_len = SHA512_MAC_LEN;
+	} else {
+		wpabuf_free(pub);
+		wpabuf_clear_free(secret);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	pub = wpabuf_zeropad(pub, prime_len);
+	if (res < 0 || !pub) {
+		wpabuf_free(pub);
+		wpabuf_clear_free(secret);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	hkey = wpabuf_alloc(owe_dh_len - 2 + wpabuf_len(pub) + 2);
+	if (!hkey) {
+		wpabuf_free(pub);
+		wpabuf_clear_free(secret);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	wpabuf_put_data(hkey, owe_dh + 2, owe_dh_len - 2); /* C */
+	wpabuf_put_buf(hkey, pub); /* A */
+	wpabuf_free(pub);
+	wpabuf_put_le16(hkey, group); /* group */
+	if (group == 19)
+		res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey),
+				  wpabuf_head(secret), wpabuf_len(secret), prk);
+	else if (group == 20)
+		res = hmac_sha384(wpabuf_head(hkey), wpabuf_len(hkey),
+				  wpabuf_head(secret), wpabuf_len(secret), prk);
+	else if (group == 21)
+		res = hmac_sha512(wpabuf_head(hkey), wpabuf_len(hkey),
+				  wpabuf_head(secret), wpabuf_len(secret), prk);
+	wpabuf_clear_free(hkey);
+	wpabuf_clear_free(secret);
+	if (res < 0)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, hash_len);
+
+	/* PMK = HKDF-expand(prk, "OWE Key Generation", n) */
+
+	os_free(sta->owe_pmk);
+	sta->owe_pmk = os_malloc(hash_len);
+	if (!sta->owe_pmk) {
+		os_memset(prk, 0, SHA512_MAC_LEN);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (group == 19)
+		res = hmac_sha256_kdf(prk, hash_len, NULL, (const u8 *) info,
+				      os_strlen(info), sta->owe_pmk, hash_len);
+	else if (group == 20)
+		res = hmac_sha384_kdf(prk, hash_len, NULL, (const u8 *) info,
+				      os_strlen(info), sta->owe_pmk, hash_len);
+	else if (group == 21)
+		res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info,
+				      os_strlen(info), sta->owe_pmk, hash_len);
+	os_memset(prk, 0, SHA512_MAC_LEN);
+	if (res < 0) {
+		os_free(sta->owe_pmk);
+		sta->owe_pmk = NULL;
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	sta->owe_pmk_len = hash_len;
+
+	wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sta->owe_pmk, sta->owe_pmk_len);
+	wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN);
+	wpa_auth_pmksa_add2(hapd->wpa_auth, sta->addr, sta->owe_pmk,
+			    sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE);
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+#endif /* CONFIG_OWE */
+
+
 static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 			   const u8 *ies, size_t ies_len, int reassoc)
 {
@@ -2029,7 +2438,8 @@
 		}
 		res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
 					  wpa_ie, wpa_ie_len,
-					  elems.mdie, elems.mdie_len);
+					  elems.mdie, elems.mdie_len,
+					  elems.owe_dh, elems.owe_dh_len);
 		resp = wpa_res_to_status_code(res);
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
@@ -2099,6 +2509,17 @@
 		}
 #endif /* CONFIG_SAE */
 
+#ifdef CONFIG_OWE
+		if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+		    wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
+		    elems.owe_dh) {
+			resp = owe_process_assoc_req(hapd, sta, elems.owe_dh,
+						     elems.owe_dh_len);
+			if (resp != WLAN_STATUS_SUCCESS)
+				return resp;
+		}
+#endif /* CONFIG_OWE */
+
 #ifdef CONFIG_IEEE80211N
 		if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) &&
 		    wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) {
@@ -2224,7 +2645,8 @@
 	 */
 	if (!sta->added_unassoc &&
 	    (!(sta->flags & WLAN_STA_AUTHORIZED) ||
-	     !wpa_auth_sta_ft_tk_already_set(sta->wpa_sm))) {
+	     (!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) &&
+	      !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)))) {
 		hostapd_drv_sta_remove(hapd, sta->addr);
 		wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED);
 		set = 0;
@@ -2287,6 +2709,10 @@
 	if (sta && sta->fils_hlp_resp)
 		buflen += wpabuf_len(sta->fils_hlp_resp);
 #endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+	if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
+		buflen += 150;
+#endif /* CONFIG_OWE */
 	buf = os_zalloc(buflen);
 	if (!buf) {
 		res = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -2324,6 +2750,13 @@
 	}
 #endif /* CONFIG_IEEE80211R_AP */
 
+#ifdef CONFIG_OWE
+	if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
+		p = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, p,
+						  buf + buflen - p,
+						  ies, ies_len);
+#endif /* CONFIG_OWE */
+
 #ifdef CONFIG_IEEE80211W
 	if (sta && status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY)
 		p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
@@ -2463,6 +2896,30 @@
 	}
 #endif /* CONFIG_FILS */
 
+#ifdef CONFIG_OWE
+	if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+	    sta && sta->owe_ecdh &&
+	    wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE) {
+		struct wpabuf *pub;
+
+		pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
+		if (!pub) {
+			res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto done;
+		}
+		/* OWE Diffie-Hellman Parameter element */
+		*p++ = WLAN_EID_EXTENSION; /* Element ID */
+		*p++ = 1 + 2 + wpabuf_len(pub); /* Length */
+		*p++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */
+		WPA_PUT_LE16(p, sta->owe_group);
+		p += 2;
+		os_memcpy(p, wpabuf_head(pub), wpabuf_len(pub));
+		p += wpabuf_len(pub);
+		send_len += 3 + 2 + wpabuf_len(pub);
+		wpabuf_free(pub);
+	}
+#endif /* CONFIG_OWE */
+
 	if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) {
 		wpa_printf(MSG_INFO, "Failed to send assoc resp: %s",
 			   strerror(errno));
@@ -2499,14 +2956,11 @@
 	sta->hlp_dhcp_discover = NULL;
 
 	/*
-	 * Remove the station in case tranmission of a success response fails
-	 * (the STA was added associated to the driver) or if the station was
-	 * previously added unassociated.
+	 * Remove the station in case transmission of a success response fails.
+	 * At this point the station was already added associated to the driver.
 	 */
-	if (reply_res != WLAN_STATUS_SUCCESS || sta->added_unassoc) {
+	if (reply_res != WLAN_STATUS_SUCCESS)
 		hostapd_drv_sta_remove(hapd, sta->addr);
-		sta->added_unassoc = 0;
-	}
 }
 
 
@@ -2518,7 +2972,10 @@
 	wpa_printf(MSG_DEBUG,
 		   "FILS: HLP response timeout - continue with association response for "
 		   MACSTR, MAC2STR(sta->addr));
-	fils_hlp_finish_assoc(hapd, sta);
+	if (sta->fils_drv_assoc_finish)
+		hostapd_notify_assoc_fils_finish(hapd, sta);
+	else
+		fils_hlp_finish_assoc(hapd, sta);
 }
 
 #endif /* CONFIG_FILS */
@@ -2720,12 +3177,11 @@
 		/* The end of the payload is encrypted. Need to decrypt it
 		 * before parsing. */
 
-		tmp = os_malloc(left);
+		tmp = os_memdup(pos, left);
 		if (!tmp) {
 			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 			goto fail;
 		}
-		os_memcpy(tmp, pos, left);
 
 		left = fils_decrypt_assoc(sta->wpa_sm, sta->fils_session, mgmt,
 					  len, tmp, left);
@@ -2873,6 +3329,7 @@
 		sta->fils_pending_assoc_req = tmp;
 		sta->fils_pending_assoc_req_len = left;
 		sta->fils_pending_assoc_is_reassoc = reassoc;
+		sta->fils_drv_assoc_finish = 0;
 		wpa_printf(MSG_DEBUG,
 			   "FILS: Waiting for HLP processing before sending (Re)Association Response frame to "
 			   MACSTR, MAC2STR(sta->addr));
@@ -3124,11 +3581,11 @@
 	case WLAN_ACTION_SA_QUERY:
 		return hostapd_sa_query_action(hapd, mgmt, len);
 #endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
 	case WLAN_ACTION_WNM:
 		ieee802_11_rx_wnm_action_ap(hapd, mgmt, len);
 		return 1;
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
 #ifdef CONFIG_FST
 	case WLAN_ACTION_FST:
 		if (hapd->iface->fst)
@@ -3148,8 +3605,40 @@
 				   "HT20/40 coex mgmt frame received from STA "
 				   MACSTR, MAC2STR(mgmt->sa));
 			hostapd_2040_coex_action(hapd, mgmt, len);
+			return 1;
 		}
 #endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_DPP
+		if (len >= IEEE80211_HDRLEN + 6 &&
+		    mgmt->u.action.u.vs_public_action.action ==
+		    WLAN_PA_VENDOR_SPECIFIC &&
+		    WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
+		    OUI_WFA &&
+		    mgmt->u.action.u.vs_public_action.variable[0] ==
+		    DPP_OUI_TYPE) {
+			const u8 *pos, *end;
+
+			pos = mgmt->u.action.u.vs_public_action.oui;
+			end = ((const u8 *) mgmt) + len;
+			hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos,
+					      hapd->iface->freq);
+			return 1;
+		}
+		if (len >= IEEE80211_HDRLEN + 2 &&
+		    (mgmt->u.action.u.public_action.action ==
+		     WLAN_PA_GAS_INITIAL_RESP ||
+		     mgmt->u.action.u.public_action.action ==
+		     WLAN_PA_GAS_COMEBACK_RESP)) {
+			const u8 *pos, *end;
+
+			pos = &mgmt->u.action.u.public_action.action;
+			end = ((const u8 *) mgmt) + len;
+			gas_query_ap_rx(hapd->gas, mgmt->sa,
+					mgmt->u.action.category,
+					pos, end - pos, hapd->iface->freq);
+			return 1;
+		}
+#endif /* CONFIG_DPP */
 		if (hapd->public_action_cb) {
 			hapd->public_action_cb(hapd->public_action_cb_ctx,
 					       (u8 *) mgmt, len,
@@ -3193,10 +3682,9 @@
 		 */
 		wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action "
 			   "frame back to sender");
-		resp = os_malloc(len);
+		resp = os_memdup(mgmt, len);
 		if (resp == NULL)
 			return 0;
-		os_memcpy(resp, mgmt, len);
 		os_memcpy(resp->da, resp->sa, ETH_ALEN);
 		os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
 		os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
@@ -3266,7 +3754,9 @@
 		return 1;
 	}
 
-	if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
+	if ((!is_broadcast_ether_addr(mgmt->da) ||
+	     stype != WLAN_FC_STYPE_ACTION) &&
+	    os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
 		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
 			       "MGMT: DA=" MACSTR " not our address",
@@ -3605,6 +4095,36 @@
 
 	if (is_multicast_ether_addr(mgmt->da))
 		return;
+#ifdef CONFIG_DPP
+	if (len >= IEEE80211_HDRLEN + 6 &&
+	    mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
+	    mgmt->u.action.u.vs_public_action.action ==
+	    WLAN_PA_VENDOR_SPECIFIC &&
+	    WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
+	    OUI_WFA &&
+	    mgmt->u.action.u.vs_public_action.variable[0] ==
+	    DPP_OUI_TYPE) {
+		const u8 *pos, *end;
+
+		pos = &mgmt->u.action.u.vs_public_action.variable[1];
+		end = ((const u8 *) mgmt) + len;
+		hostapd_dpp_tx_status(hapd, mgmt->da, pos, end - pos, ok);
+		return;
+	}
+	if (len >= IEEE80211_HDRLEN + 2 &&
+	    mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
+	    (mgmt->u.action.u.public_action.action ==
+	     WLAN_PA_GAS_INITIAL_REQ ||
+	     mgmt->u.action.u.public_action.action ==
+	     WLAN_PA_GAS_COMEBACK_REQ)) {
+		const u8 *pos, *end;
+
+		pos = mgmt->u.action.u.public_action.variable;
+		end = ((const u8 *) mgmt) + len;
+		gas_query_ap_tx_status(hapd->gas, mgmt->da, pos, end - pos, ok);
+		return;
+	}
+#endif /* CONFIG_DPP */
 	sta = ap_get_sta(hapd, mgmt->da);
 	if (!sta) {
 		wpa_printf(MSG_DEBUG, "handle_action_cb: STA " MACSTR
@@ -3642,8 +4162,16 @@
 
 #ifdef CONFIG_TESTING_OPTIONS
 	if (hapd->ext_mgmt_frame_handling) {
-		wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-TX-STATUS stype=%u ok=%d",
-			stype, ok);
+		size_t hex_len = 2 * len + 1;
+		char *hex = os_malloc(hex_len);
+
+		if (hex) {
+			wpa_snprintf_hex(hex, hex_len, buf, len);
+			wpa_msg(hapd->msg_ctx, MSG_INFO,
+				"MGMT-TX-STATUS stype=%u ok=%d buf=%s",
+				stype, ok, hex);
+			os_free(hex);
+		}
 		return;
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index ce3abcb..3b381b4 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -55,8 +55,8 @@
 u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid);
-u8 * hostapd_eid_vendor_he_capab(struct hostapd_data *hapd, u8 *eid);
-u8 * hostapd_eid_vendor_he_operation(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid);
 
 int hostapd_ht_operation_update(struct hostapd_iface *iface);
 void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
@@ -144,5 +144,11 @@
 				 const u8 *msk, size_t msk_len);
 void fils_hlp_timeout(void *eloop_ctx, void *eloop_data);
 void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta);
+void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
+		      const u8 *pos, size_t len, u16 auth_alg,
+		      u16 auth_transaction, u16 status_code,
+		      void (*cb)(struct hostapd_data *hapd,
+				 struct sta_info *sta,
+				 u16 resp, struct wpabuf *data, int pub));
 
 #endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index 1e0358c..3308398 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -327,14 +327,13 @@
 			return HOSTAPD_ACL_REJECT;
 		}
 
-		query->auth_msg = os_malloc(len);
+		query->auth_msg = os_memdup(msg, len);
 		if (query->auth_msg == NULL) {
 			wpa_printf(MSG_ERROR, "Failed to allocate memory for "
 				   "auth frame.");
 			hostapd_acl_query_free(query);
 			return HOSTAPD_ACL_REJECT;
 		}
-		os_memcpy(query->auth_msg, msg, len);
 		query->auth_msg_len = len;
 		query->next = hapd->acl_queries;
 		hapd->acl_queries = query;
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
index 7d6a84f..1a8d469 100644
--- a/src/ap/ieee802_11_he.c
+++ b/src/ap/ieee802_11_he.c
@@ -10,14 +10,13 @@
 
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
-#include "common/qca-vendor.h"
 #include "hostapd.h"
 #include "ap_config.h"
 #include "beacon.h"
 #include "ieee802_11.h"
 #include "dfs.h"
 
-u8 * hostapd_eid_vendor_he_capab(struct hostapd_data *hapd, u8 *eid)
+u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid)
 {
 	struct ieee80211_he_capabilities *cap;
 	u8 *pos = eid;
@@ -25,17 +24,10 @@
 	if (!hapd->iface->current_mode)
 		return eid;
 
-	/* For now, use a vendor specific element since the P802.11ax draft is
-	 * still subject to changes and the contents of this element may change.
-	 * This can be replaced with the actual element once P802.11ax is
-	 * finalized. */
-	/* Vendor HE Capabilities element */
-	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
-	*pos++ = 4 /* The Vendor OUI, subtype */ +
-		sizeof(struct ieee80211_he_capabilities);
+	*pos++ = WLAN_EID_EXTENSION;
+	*pos++ = 1 + sizeof(struct ieee80211_he_capabilities);
+	*pos++ = WLAN_EID_EXT_HE_CAPABILITIES;
 
-	WPA_PUT_BE32(pos, (OUI_QCA << 8) | QCA_VENDOR_ELEM_HE_CAPAB);
-	pos += 4;
 	cap = (struct ieee80211_he_capabilities *) pos;
 	os_memset(cap, 0, sizeof(*cap));
 
@@ -57,7 +49,7 @@
 }
 
 
-u8 * hostapd_eid_vendor_he_operation(struct hostapd_data *hapd, u8 *eid)
+u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid)
 {
 	struct ieee80211_he_operation *oper;
 	u8 *pos = eid;
@@ -65,17 +57,10 @@
 	if (!hapd->iface->current_mode)
 		return eid;
 
-	/* For now, use a vendor specific element since the P802.11ax draft is
-	 * still subject to changes and the contents of this element may change.
-	 * This can be replaced with the actual element once P802.11ax is
-	 * finalized. */
-	/* Vendor HE Operation element */
-	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
-	*pos++ = 4 /* The Vendor OUI, subtype */ +
-		sizeof(struct ieee80211_he_operation);
+	*pos++ = WLAN_EID_EXTENSION;
+	*pos++ = 1 + sizeof(struct ieee80211_he_operation);
+	*pos++ = WLAN_EID_EXT_HE_OPERATION;
 
-	WPA_PUT_BE32(pos, (OUI_QCA << 8) | QCA_VENDOR_ELEM_HE_OPER);
-	pos += 4;
 	oper = (struct ieee80211_he_operation *) pos;
 	os_memset(oper, 0, sizeof(*oper));
 
@@ -95,6 +80,8 @@
 			(hapd->iface->conf->he_op.he_rts_threshold <<
 			 HE_OPERATION_RTS_THRESHOLD_OFFSET);
 
+	/* TODO: conditional MaxBSSID Indicator subfield */
+
 	pos += sizeof(*oper);
 
 	return pos;
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index daacf7e..902f64f 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -186,9 +186,9 @@
 			*pos |= 0x08; /* Bit 19 - BSS Transition */
 		break;
 	case 3: /* Bits 24-31 */
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
 		*pos |= 0x02; /* Bit 25 - SSID List */
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
 		if (hapd->conf->time_advertisement == 2)
 			*pos |= 0x08; /* Bit 27 - UTC TSF Offset */
 		if (hapd->conf->interworking)
@@ -254,10 +254,10 @@
 	if (len < 9 &&
 	    (hapd->conf->ftm_initiator || hapd->conf->ftm_responder))
 		len = 9;
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
 	if (len < 4)
 		len = 4;
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
 #ifdef CONFIG_HS20
 	if (hapd->conf->hs20 && len < 6)
 		len = 6;
@@ -516,7 +516,7 @@
 {
 	u8 *pos = eid;
 
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
 	if (hapd->conf->ap_max_inactivity > 0) {
 		unsigned int val;
 		*pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD;
@@ -534,7 +534,7 @@
 		pos += 2;
 		*pos++ = 0x00; /* TODO: Protected Keep-Alive Required */
 	}
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
 
 	return pos;
 }
@@ -544,23 +544,38 @@
 
 u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len)
 {
-	u8 mbo[6], *mbo_pos = mbo;
+	u8 mbo[9], *mbo_pos = mbo;
 	u8 *pos = eid;
 
-	if (!hapd->conf->mbo_enabled)
+	if (!hapd->conf->mbo_enabled && !hapd->enable_oce)
 		return eid;
 
-	*mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND;
-	*mbo_pos++ = 1;
-	/* Not Cellular aware */
-	*mbo_pos++ = 0;
+	if (hapd->conf->mbo_enabled) {
+		*mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND;
+		*mbo_pos++ = 1;
+		/* Not Cellular aware */
+		*mbo_pos++ = 0;
+	}
 
-	if (hapd->mbo_assoc_disallow) {
+	if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) {
 		*mbo_pos++ = MBO_ATTR_ID_ASSOC_DISALLOW;
 		*mbo_pos++ = 1;
 		*mbo_pos++ = hapd->mbo_assoc_disallow;
 	}
 
+	if (hapd->enable_oce & (OCE_AP | OCE_STA_CFON)) {
+		u8 ctrl;
+
+		ctrl = OCE_RELEASE;
+		if ((hapd->enable_oce & (OCE_AP | OCE_STA_CFON)) ==
+		    OCE_STA_CFON)
+			ctrl |= OCE_IS_STA_CFON;
+
+		*mbo_pos++ = OCE_ATTR_ID_CAPA_IND;
+		*mbo_pos++ = 1;
+		*mbo_pos++ = ctrl;
+	}
+
 	pos += mbo_add_ie(pos, len, mbo, mbo_pos - mbo);
 
 	return pos;
@@ -569,14 +584,24 @@
 
 u8 hostapd_mbo_ie_len(struct hostapd_data *hapd)
 {
-	if (!hapd->conf->mbo_enabled)
+	u8 len;
+
+	if (!hapd->conf->mbo_enabled && !hapd->enable_oce)
 		return 0;
 
 	/*
 	 * MBO IE header (6) + Capability Indication attribute (3) +
 	 * Association Disallowed attribute (3) = 12
 	 */
-	return 6 + 3 + (hapd->mbo_assoc_disallow ? 3 : 0);
+	len = 6;
+	if (hapd->conf->mbo_enabled)
+		len += 3 + (hapd->mbo_assoc_disallow ? 3 : 0);
+
+	/* OCE capability indication attribute (3) */
+	if (hapd->enable_oce & (OCE_AP | OCE_STA_CFON))
+		len += 3;
+
+	return len;
 }
 
 #endif /* CONFIG_MBO */
@@ -630,7 +655,10 @@
 		fils_info |= BIT(8); /* HESSID Included */
 	/* FILS Shared Key Authentication without PFS Supported */
 	fils_info |= BIT(9);
-	/* TODO: B10: FILS Shared Key Authentication with PFS Supported */
+	if (hapd->conf->fils_dh_group) {
+		/* FILS Shared Key Authentication with PFS Supported */
+		fils_info |= BIT(10);
+	}
 	/* TODO: B11: FILS Public Key Authentication Supported */
 	/* B12..B15: Reserved */
 	WPA_PUT_LE16(pos, fils_info);
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index fccdc72..793d381 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -973,7 +973,9 @@
 	}
 
 	key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
-	if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) {
+	if (key_mgmt != -1 &&
+	    (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE ||
+	     key_mgmt == WPA_KEY_MGMT_DPP)) {
 		wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - "
 			   "STA is using PSK");
 		return;
@@ -1116,7 +1118,9 @@
 	}
 
 	key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
-	if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) {
+	if (key_mgmt != -1 &&
+	    (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE ||
+	     key_mgmt == WPA_KEY_MGMT_DPP)) {
 		wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - using PSK");
 		/*
 		 * Clear any possible EAPOL authenticator state to support
@@ -1420,11 +1424,10 @@
 			}
 		} while (class_len < 1);
 
-		nclass[nclass_count].data = os_malloc(class_len);
+		nclass[nclass_count].data = os_memdup(attr_class, class_len);
 		if (nclass[nclass_count].data == NULL)
 			break;
 
-		os_memcpy(nclass[nclass_count].data, attr_class, class_len);
 		nclass[nclass_count].len = class_len;
 		nclass_count++;
 	}
@@ -2072,11 +2075,10 @@
 	}
 
 	if (eap_user->password) {
-		user->password = os_malloc(eap_user->password_len);
+		user->password = os_memdup(eap_user->password,
+					   eap_user->password_len);
 		if (user->password == NULL)
 			goto out;
-		os_memcpy(user->password, eap_user->password,
-			  eap_user->password_len);
 		user->password_len = eap_user->password_len;
 		user->password_hash = eap_user->password_hash;
 	}
@@ -2228,6 +2230,7 @@
 	conf.erp_domain = hapd->conf->erp_domain;
 	conf.erp = hapd->conf->eap_server_erp;
 	conf.tls_session_lifetime = hapd->conf->tls_session_lifetime;
+	conf.tls_flags = hapd->conf->tls_flags;
 	conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key;
 	conf.eap_fast_a_id = hapd->conf->eap_fast_a_id;
 	conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len;
diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c
index 3c086bf..4d6a92e 100644
--- a/src/ap/ndisc_snoop.c
+++ b/src/ap/ndisc_snoop.c
@@ -182,4 +182,5 @@
 void ndisc_snoop_deinit(struct hostapd_data *hapd)
 {
 	l2_packet_deinit(hapd->sock_ndisc);
+	hapd->sock_ndisc = NULL;
 }
diff --git a/src/ap/peerkey_auth.c b/src/ap/peerkey_auth.c
deleted file mode 100644
index 93e775b..0000000
--- a/src/ap/peerkey_auth.c
+++ /dev/null
@@ -1,355 +0,0 @@
-/*
- * hostapd - PeerKey for Direct Link Setup (DLS)
- * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "utils/includes.h"
-
-#include "utils/common.h"
-#include "utils/eloop.h"
-#include "crypto/sha1.h"
-#include "crypto/sha256.h"
-#include "crypto/random.h"
-#include "wpa_auth.h"
-#include "wpa_auth_i.h"
-#include "wpa_auth_ie.h"
-
-#ifdef CONFIG_PEERKEY
-
-struct wpa_stsl_search {
-	const u8 *addr;
-	struct wpa_state_machine *sm;
-};
-
-
-static int wpa_stsl_select_sta(struct wpa_state_machine *sm, void *ctx)
-{
-	struct wpa_stsl_search *search = ctx;
-	if (os_memcmp(search->addr, sm->addr, ETH_ALEN) == 0) {
-		search->sm = sm;
-		return 1;
-	}
-	return 0;
-}
-
-
-static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth,
-			       struct wpa_state_machine *sm, const u8 *peer,
-			       u16 mui, u16 error_type)
-{
-	u8 kde[2 + RSN_SELECTOR_LEN + ETH_ALEN +
-	       2 + RSN_SELECTOR_LEN + sizeof(struct rsn_error_kde)];
-	u8 *pos;
-	struct rsn_error_kde error;
-
-	wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
-			"Sending SMK Error");
-
-	pos = kde;
-
-	if (peer) {
-		pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN,
-				  NULL, 0);
-	}
-
-	error.mui = host_to_be16(mui);
-	error.error_type = host_to_be16(error_type);
-	pos = wpa_add_kde(pos, RSN_KEY_DATA_ERROR,
-			  (u8 *) &error, sizeof(error), NULL, 0);
-
-	__wpa_send_eapol(wpa_auth, sm,
-			 WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
-			 WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_ERROR,
-			 NULL, NULL, kde, pos - kde, 0, 0, 0);
-}
-
-
-void wpa_smk_m1(struct wpa_authenticator *wpa_auth,
-		struct wpa_state_machine *sm, struct wpa_eapol_key *key,
-		const u8 *key_data, size_t key_data_len)
-{
-	struct wpa_eapol_ie_parse kde;
-	struct wpa_stsl_search search;
-	u8 *buf, *pos;
-	size_t buf_len;
-
-	if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
-		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1");
-		return;
-	}
-
-	if (kde.rsn_ie == NULL || kde.mac_addr == NULL ||
-	    kde.mac_addr_len < ETH_ALEN) {
-		wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in "
-			   "SMK M1");
-		return;
-	}
-
-	/* Initiator = sm->addr; Peer = kde.mac_addr */
-
-	search.addr = kde.mac_addr;
-	search.sm = NULL;
-	if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) ==
-	    0 || search.sm == NULL) {
-		wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR
-			   " aborted - STA not associated anymore",
-			   MAC2STR(kde.mac_addr));
-		wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK,
-				   STK_ERR_STA_NR);
-		return;
-	}
-
-	buf_len = kde.rsn_ie_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN;
-	buf = os_malloc(buf_len);
-	if (buf == NULL)
-		return;
-	/* Initiator RSN IE */
-	os_memcpy(buf, kde.rsn_ie, kde.rsn_ie_len);
-	pos = buf + kde.rsn_ie_len;
-	/* Initiator MAC Address */
-	pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->addr, ETH_ALEN,
-			  NULL, 0);
-
-	/* SMK M2:
-	 * EAPOL-Key(S=1, M=1, A=1, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce,
-	 *           MIC=MIC, DataKDs=(RSNIE_I, MAC_I KDE)
-	 */
-
-	wpa_auth_logger(wpa_auth, search.sm->addr, LOGGER_DEBUG,
-			"Sending SMK M2");
-
-	__wpa_send_eapol(wpa_auth, search.sm,
-			 WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
-			 WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE,
-			 NULL, key->key_nonce, buf, pos - buf, 0, 0, 0);
-
-	os_free(buf);
-}
-
-
-static void wpa_send_smk_m4(struct wpa_authenticator *wpa_auth,
-			    struct wpa_state_machine *sm,
-			    struct wpa_eapol_key *key,
-			    struct wpa_eapol_ie_parse *kde,
-			    const u8 *smk)
-{
-	u8 *buf, *pos;
-	size_t buf_len;
-	u32 lifetime;
-
-	/* SMK M4:
-	 * EAPOL-Key(S=1, M=1, A=0, I=1, K=0, SM=1, KeyRSC=0, Nonce=PNonce,
-	 *           MIC=MIC, DataKDs=(MAC_I KDE, INonce KDE, SMK KDE,
-	 *           Lifetime KDE)
-	 */
-
-	buf_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN +
-		2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN +
-		2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN +
-		2 + RSN_SELECTOR_LEN + sizeof(lifetime);
-	pos = buf = os_malloc(buf_len);
-	if (buf == NULL)
-		return;
-
-	/* Initiator MAC Address */
-	pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, kde->mac_addr, ETH_ALEN,
-			  NULL, 0);
-
-	/* Initiator Nonce */
-	pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, kde->nonce, WPA_NONCE_LEN,
-			  NULL, 0);
-
-	/* SMK with PNonce */
-	pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN,
-			  key->key_nonce, WPA_NONCE_LEN);
-
-	/* Lifetime */
-	lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */
-	pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
-			  (u8 *) &lifetime, sizeof(lifetime), NULL, 0);
-
-	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
-			"Sending SMK M4");
-
-	__wpa_send_eapol(wpa_auth, sm,
-			 WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
-			 WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_SMK_MESSAGE,
-			 NULL, key->key_nonce, buf, pos - buf, 0, 1, 0);
-
-	os_free(buf);
-}
-
-
-static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth,
-			    struct wpa_state_machine *sm,
-			    struct wpa_eapol_key *key,
-			    struct wpa_eapol_ie_parse *kde,
-			    const u8 *smk, const u8 *peer)
-{
-	u8 *buf, *pos;
-	size_t buf_len;
-	u32 lifetime;
-
-	/* SMK M5:
-	 * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce,
-	 *           MIC=MIC, DataKDs=(RSNIE_P, MAC_P KDE, PNonce, SMK KDE,
-	 *                             Lifetime KDE))
-	 */
-
-	buf_len = kde->rsn_ie_len +
-		2 + RSN_SELECTOR_LEN + ETH_ALEN +
-		2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN +
-		2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN +
-		2 + RSN_SELECTOR_LEN + sizeof(lifetime);
-	pos = buf = os_malloc(buf_len);
-	if (buf == NULL)
-		return;
-
-	/* Peer RSN IE */
-	os_memcpy(pos, kde->rsn_ie, kde->rsn_ie_len);
-	pos += kde->rsn_ie_len;
-
-	/* Peer MAC Address */
-	pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0);
-
-	/* PNonce */
-	pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, key->key_nonce,
-			  WPA_NONCE_LEN, NULL, 0);
-
-	/* SMK and INonce */
-	pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN,
-			  kde->nonce, WPA_NONCE_LEN);
-
-	/* Lifetime */
-	lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */
-	pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
-			  (u8 *) &lifetime, sizeof(lifetime), NULL, 0);
-
-	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
-			"Sending SMK M5");
-
-	__wpa_send_eapol(wpa_auth, sm,
-			 WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
-			 WPA_KEY_INFO_SMK_MESSAGE,
-			 NULL, kde->nonce, buf, pos - buf, 0, 1, 0);
-
-	os_free(buf);
-}
-
-
-void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
-		struct wpa_state_machine *sm, struct wpa_eapol_key *key,
-		const u8 *key_data, size_t key_data_len)
-{
-	struct wpa_eapol_ie_parse kde;
-	struct wpa_stsl_search search;
-	u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos;
-
-	if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
-		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3");
-		return;
-	}
-
-	if (kde.rsn_ie == NULL ||
-	    kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN ||
-	    kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN) {
-		wpa_printf(MSG_INFO, "RSN: No RSN IE, MAC address KDE, or "
-			   "Nonce KDE in SMK M3");
-		return;
-	}
-
-	/* Peer = sm->addr; Initiator = kde.mac_addr;
-	 * Peer Nonce = key->key_nonce; Initiator Nonce = kde.nonce */
-
-	search.addr = kde.mac_addr;
-	search.sm = NULL;
-	if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) ==
-	    0 || search.sm == NULL) {
-		wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR
-			   " aborted - STA not associated anymore",
-			   MAC2STR(kde.mac_addr));
-		wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK,
-				   STK_ERR_STA_NR);
-		return;
-	}
-
-	if (random_get_bytes(smk, PMK_LEN)) {
-		wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK");
-		return;
-	}
-
-	/* SMK = PRF-256(Random number, "SMK Derivation",
-	 *               AA || Time || INonce || PNonce)
-	 */
-	os_memcpy(buf, wpa_auth->addr, ETH_ALEN);
-	pos = buf + ETH_ALEN;
-	wpa_get_ntp_timestamp(pos);
-	pos += 8;
-	os_memcpy(pos, kde.nonce, WPA_NONCE_LEN);
-	pos += WPA_NONCE_LEN;
-	os_memcpy(pos, key->key_nonce, WPA_NONCE_LEN);
-#ifdef CONFIG_IEEE80211W
-	sha256_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf),
-		   smk, PMK_LEN);
-#else /* CONFIG_IEEE80211W */
-	sha1_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf),
-		 smk, PMK_LEN);
-#endif /* CONFIG_IEEE80211W */
-
-	wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", smk, PMK_LEN);
-
-	wpa_send_smk_m4(wpa_auth, sm, key, &kde, smk);
-	wpa_send_smk_m5(wpa_auth, search.sm, key, &kde, smk, sm->addr);
-
-	/* Authenticator does not need SMK anymore and it is required to forget
-	 * it. */
-	os_memset(smk, 0, sizeof(*smk));
-}
-
-
-void wpa_smk_error(struct wpa_authenticator *wpa_auth,
-		   struct wpa_state_machine *sm,
-		   const u8 *key_data, size_t key_data_len)
-{
-	struct wpa_eapol_ie_parse kde;
-	struct wpa_stsl_search search;
-	struct rsn_error_kde error;
-	u16 mui, error_type;
-
-	if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
-		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error");
-		return;
-	}
-
-	if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN ||
-	    kde.error == NULL || kde.error_len < sizeof(error)) {
-		wpa_printf(MSG_INFO, "RSN: No MAC address or Error KDE in "
-			   "SMK Error");
-		return;
-	}
-
-	search.addr = kde.mac_addr;
-	search.sm = NULL;
-	if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) ==
-	    0 || search.sm == NULL) {
-		wpa_printf(MSG_DEBUG, "RSN: Peer STA " MACSTR " not "
-			   "associated for SMK Error message from " MACSTR,
-			   MAC2STR(kde.mac_addr), MAC2STR(sm->addr));
-		return;
-	}
-
-	os_memcpy(&error, kde.error, sizeof(error));
-	mui = be_to_host16(error.mui);
-	error_type = be_to_host16(error.error_type);
-	wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
-			 "STA reported SMK Error: Peer " MACSTR
-			 " MUI %d Error Type %d",
-			 MAC2STR(kde.mac_addr), mui, error_type);
-
-	wpa_smk_send_error(wpa_auth, search.sm, sm->addr, mui, error_type);
-}
-
-#endif /* CONFIG_PEERKEY */
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index bce5abf..15e2c49 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -338,8 +338,7 @@
 	else if (wpa_key_mgmt_suite_b(akmp))
 		rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
 	else
-		rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
-			  wpa_key_mgmt_sha256(akmp));
+		rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp);
 	os_get_reltime(&now);
 	entry->expiration = now.sec;
 	if (session_timeout > 0)
@@ -518,7 +517,7 @@
 		if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0)
 			continue;
 		rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid,
-			  wpa_key_mgmt_sha256(entry->akmp));
+			  entry->akmp);
 		if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0)
 			return entry;
 	}
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
index bd1b672..2ef2174 100644
--- a/src/ap/pmksa_cache_auth.h
+++ b/src/ap/pmksa_cache_auth.h
@@ -35,6 +35,7 @@
 };
 
 struct rsn_pmksa_cache;
+struct radius_das_attrs;
 
 struct rsn_pmksa_cache *
 pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index af8c754..b1fde3c 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / Station table
- * Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -17,6 +17,7 @@
 #include "radius/radius_client.h"
 #include "p2p/p2p.h"
 #include "fst/fst.h"
+#include "crypto/crypto.h"
 #include "hostapd.h"
 #include "accounting.h"
 #include "ieee802_1x.h"
@@ -344,8 +345,18 @@
 	wpabuf_free(sta->fils_hlp_resp);
 	wpabuf_free(sta->hlp_dhcp_discover);
 	eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+#ifdef CONFIG_FILS_SK_PFS
+	crypto_ecdh_deinit(sta->fils_ecdh);
+	wpabuf_clear_free(sta->fils_dh_ss);
+	wpabuf_free(sta->fils_g_sta);
+#endif /* CONFIG_FILS_SK_PFS */
 #endif /* CONFIG_FILS */
 
+#ifdef CONFIG_OWE
+	bin_clear_free(sta->owe_pmk, sta->owe_pmk_len);
+	crypto_ecdh_deinit(sta->owe_ecdh);
+#endif /* CONFIG_OWE */
+
 	os_free(sta);
 }
 
@@ -606,7 +617,7 @@
 
 static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx)
 {
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
 	struct hostapd_data *hapd = eloop_ctx;
 	struct sta_info *sta = timeout_ctx;
 
@@ -617,7 +628,7 @@
 
 	wnm_send_ess_disassoc_imminent(hapd, sta, sta->hs20_session_info_url,
 				       sta->hs20_disassoc_timer);
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
 }
 
 
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 6f55403..3fb60f6 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -1,6 +1,6 @@
 /*
  * hostapd / Station table
- * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -12,11 +12,11 @@
 #ifdef CONFIG_MESH
 /* needed for mesh_plink_state enum */
 #include "common/defs.h"
-#include "common/wpa_common.h"
 #endif /* CONFIG_MESH */
 
 #include "list.h"
 #include "vlan.h"
+#include "common/wpa_common.h"
 #include "common/ieee802_11_defs.h"
 
 /* STA flags */
@@ -48,6 +48,7 @@
  * Supported Rates IEs). */
 #define WLAN_SUPP_RATES_MAX 32
 
+struct hostapd_data;
 
 struct mbo_non_pref_chan_info {
 	struct mbo_non_pref_chan_info *next;
@@ -173,11 +174,11 @@
 	struct os_reltime sa_query_start;
 #endif /* CONFIG_IEEE80211W */
 
-#ifdef CONFIG_INTERWORKING
+#if defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP)
 #define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */
 	struct gas_dialog_info *gas_dialog;
 	u8 gas_dialog_next;
-#endif /* CONFIG_INTERWORKING */
+#endif /* CONFIG_INTERWORKING || CONFIG_DPP */
 
 	struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
 	struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */
@@ -225,13 +226,37 @@
 #ifdef CONFIG_FILS
 	u8 fils_snonce[FILS_NONCE_LEN];
 	u8 fils_session[FILS_SESSION_LEN];
+	u8 fils_erp_pmkid[PMKID_LEN];
 	u8 *fils_pending_assoc_req;
 	size_t fils_pending_assoc_req_len;
 	unsigned int fils_pending_assoc_is_reassoc:1;
 	unsigned int fils_dhcp_rapid_commit_proxy:1;
+	unsigned int fils_erp_pmkid_set:1;
+	unsigned int fils_drv_assoc_finish:1;
 	struct wpabuf *fils_hlp_resp;
 	struct wpabuf *hlp_dhcp_discover;
+	void (*fils_pending_cb)(struct hostapd_data *hapd, struct sta_info *sta,
+				u16 resp, struct wpabuf *data, int pub);
+#ifdef CONFIG_FILS_SK_PFS
+	struct crypto_ecdh *fils_ecdh;
+#endif /* CONFIG_FILS_SK_PFS */
+	struct wpabuf *fils_dh_ss;
+	struct wpabuf *fils_g_sta;
 #endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+	u8 *owe_pmk;
+	size_t owe_pmk_len;
+	struct crypto_ecdh *owe_ecdh;
+	u16 owe_group;
+#endif /* CONFIG_OWE */
+
+#ifdef CONFIG_TESTING_OPTIONS
+	enum wpa_alg last_tk_alg;
+	int last_tk_key_idx;
+	u8 last_tk[WPA_TK_MAX_LEN];
+	size_t last_tk_len;
+#endif /* CONFIG_TESTING_OPTIONS */
 };
 
 
@@ -251,8 +276,6 @@
 #define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5)
 
 
-struct hostapd_data;
-
 int ap_for_each_sta(struct hostapd_data *hapd,
 		    int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
 			      void *ctx),
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 85e85e0..8265fa1 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -35,11 +35,13 @@
 
 static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx);
 static int wpa_sm_step(struct wpa_state_machine *sm);
-static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data,
-			      size_t data_len);
+static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK,
+			      u8 *data, size_t data_len);
 #ifdef CONFIG_FILS
 static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
 			    u8 *buf, size_t buf_len, u16 *_key_data_len);
+static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
+					     const struct wpabuf *hlp);
 #endif /* CONFIG_FILS */
 static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx);
 static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
@@ -63,6 +65,7 @@
 static const u32 eapol_key_timeout_first = 100; /* ms */
 static const u32 eapol_key_timeout_subseq = 1000; /* ms */
 static const u32 eapol_key_timeout_first_group = 500; /* ms */
+static const u32 eapol_key_timeout_no_retrans = 4000; /* ms */
 
 /* TODO: make these configurable */
 static const int dot11RSNAConfigPMKLifetime = 43200;
@@ -108,12 +111,12 @@
 static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth,
 					  const u8 *addr,
 					  const u8 *p2p_dev_addr,
-					  const u8 *prev_psk)
+					  const u8 *prev_psk, size_t *psk_len)
 {
 	if (wpa_auth->cb->get_psk == NULL)
 		return NULL;
 	return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr,
-				     prev_psk);
+				     prev_psk, psk_len);
 }
 
 
@@ -224,13 +227,13 @@
 
 
 static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth,
-			       const u8 *addr)
+			       const u8 *addr, u16 reason)
 {
 	if (wpa_auth->cb->disconnect == NULL)
 		return;
-	wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR, MAC2STR(addr));
-	wpa_auth->cb->disconnect(wpa_auth->cb_ctx, addr,
-				 WLAN_REASON_PREV_AUTH_NOT_VALID);
+	wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR " (reason %u)",
+		   MAC2STR(addr), reason);
+	wpa_auth->cb->disconnect(wpa_auth->cb_ctx, addr, reason);
 }
 
 
@@ -518,6 +521,7 @@
 #ifdef CONFIG_IEEE80211R_AP
 	wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache);
 	wpa_auth->ft_pmk_cache = NULL;
+	wpa_ft_deinit(wpa_auth);
 #endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_P2P
@@ -703,6 +707,9 @@
 	sm->pending_1_of_4_timeout = 0;
 	eloop_cancel_timeout(wpa_sm_call_step, sm, NULL);
 	eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
+#ifdef CONFIG_IEEE80211R_AP
+	wpa_ft_sta_deinit(sm);
+#endif /* CONFIG_IEEE80211R_AP */
 	if (sm->in_step_loop) {
 		/* Must not free state machine while wpa_sm_step() is running.
 		 * Freeing will be completed in the end of wpa_sm_step(). */
@@ -842,16 +849,16 @@
 	struct wpa_ptk PTK;
 	int ok = 0;
 	const u8 *pmk = NULL;
-	unsigned int pmk_len;
+	size_t pmk_len;
 
 	os_memset(&PTK, 0, sizeof(PTK));
 	for (;;) {
-		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
+		    !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
 			pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
-					       sm->p2p_dev_addr, pmk);
+					       sm->p2p_dev_addr, pmk, &pmk_len);
 			if (pmk == NULL)
 				break;
-			pmk_len = PMK_LEN;
 		} else {
 			pmk = sm->PMK;
 			pmk_len = sm->pmk_len;
@@ -860,8 +867,8 @@
 		if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK) < 0)
 			break;
 
-		if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len)
-		    == 0) {
+		if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
+				       data, data_len) == 0) {
 			ok = 1;
 			break;
 		}
@@ -894,8 +901,7 @@
 	struct ieee802_1x_hdr *hdr;
 	struct wpa_eapol_key *key;
 	u16 key_info, key_data_length;
-	enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST,
-	       SMK_M1, SMK_M3, SMK_ERROR } msg;
+	enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST } msg;
 	char *msgtxt;
 	struct wpa_eapol_ie_parse kde;
 	const u8 *key_data;
@@ -906,7 +912,7 @@
 		return;
 	wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL data", data, data_len);
 
-	mic_len = wpa_mic_len(sm->wpa_key_mgmt);
+	mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
 	keyhdrlen = sizeof(*key) + mic_len + 2;
 
 	if (data_len < sizeof(*hdr) + keyhdrlen) {
@@ -969,19 +975,12 @@
 	/* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys
 	 * are set */
 
-	if ((key_info & (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) ==
-	    (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) {
-		if (key_info & WPA_KEY_INFO_ERROR) {
-			msg = SMK_ERROR;
-			msgtxt = "SMK Error";
-		} else {
-			msg = SMK_M1;
-			msgtxt = "SMK M1";
-		}
-	} else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
-		msg = SMK_M3;
-		msgtxt = "SMK M3";
-	} else if (key_info & WPA_KEY_INFO_REQUEST) {
+	if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
+		wpa_printf(MSG_DEBUG, "WPA: Ignore SMK message");
+		return;
+	}
+
+	if (key_info & WPA_KEY_INFO_REQUEST) {
 		msg = REQUEST;
 		msgtxt = "Request";
 	} else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) {
@@ -997,7 +996,6 @@
 		msgtxt = "2/4 Pairwise";
 	}
 
-	/* TODO: key_info type validation for PeerKey */
 	if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 ||
 	    msg == GROUP_2) {
 		u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK;
@@ -1017,7 +1015,10 @@
 			}
 
 			if (!wpa_use_aes_cmac(sm) &&
+			    !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
 			    !wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
+			    sm->wpa_key_mgmt != WPA_KEY_MGMT_OWE &&
+			    sm->wpa_key_mgmt != WPA_KEY_MGMT_DPP &&
 			    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
 				wpa_auth_logger(wpa_auth, sm->addr,
 						LOGGER_WARNING,
@@ -1028,7 +1029,9 @@
 		}
 
 		if ((wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
-		     wpa_key_mgmt_fils(sm->wpa_key_mgmt)) &&
+		     wpa_key_mgmt_fils(sm->wpa_key_mgmt) ||
+		     sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
+		     sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) &&
 		    ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
 			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING,
 					"did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
@@ -1152,7 +1155,8 @@
 				   "collect more entropy for random number "
 				   "generation");
 			random_mark_pool_ready();
-			wpa_sta_disconnect(wpa_auth, sm->addr);
+			wpa_sta_disconnect(wpa_auth, sm->addr,
+					   WLAN_REASON_PREV_AUTH_NOT_VALID);
 			return;
 		}
 		break;
@@ -1176,28 +1180,6 @@
 			return;
 		}
 		break;
-#ifdef CONFIG_PEERKEY
-	case SMK_M1:
-	case SMK_M3:
-	case SMK_ERROR:
-		if (!wpa_auth->conf.peerkey) {
-			wpa_printf(MSG_DEBUG, "RSN: SMK M1/M3/Error, but "
-				   "PeerKey use disabled - ignoring message");
-			return;
-		}
-		if (!sm->PTK_valid) {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
-					"received EAPOL-Key msg SMK in "
-					"invalid state - dropped");
-			return;
-		}
-		break;
-#else /* CONFIG_PEERKEY */
-	case SMK_M1:
-	case SMK_M3:
-	case SMK_ERROR:
-		return; /* STSL disabled - ignore SMK messages */
-#endif /* CONFIG_PEERKEY */
 	case REQUEST:
 		break;
 	}
@@ -1230,8 +1212,8 @@
 	sm->MICVerified = FALSE;
 	if (sm->PTK_valid && !sm->update_snonce) {
 		if (mic_len &&
-		    wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data,
-				       data_len) &&
+		    wpa_verify_key_mic(sm->wpa_key_mgmt, sm->pmk_len, &sm->PTK,
+				       data, data_len) &&
 		    (msg != PAIRWISE_4 || !sm->alt_snonce_valid ||
 		     wpa_try_alt_snonce(sm, data, data_len))) {
 			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
@@ -1269,12 +1251,7 @@
 		 * even though MAC address KDE is not normally encrypted,
 		 * supplicant is allowed to encrypt it.
 		 */
-		if (msg == SMK_ERROR) {
-#ifdef CONFIG_PEERKEY
-			wpa_smk_error(wpa_auth, sm, key_data, key_data_length);
-#endif /* CONFIG_PEERKEY */
-			return;
-		} else if (key_info & WPA_KEY_INFO_ERROR) {
+		if (key_info & WPA_KEY_INFO_ERROR) {
 			if (wpa_receive_error_report(
 				    wpa_auth, sm,
 				    !(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0)
@@ -1284,11 +1261,6 @@
 					"received EAPOL-Key Request for new "
 					"4-Way Handshake");
 			wpa_request_new_ptk(sm);
-#ifdef CONFIG_PEERKEY
-		} else if (msg == SMK_M1) {
-			wpa_smk_m1(wpa_auth, sm, key, key_data,
-				   key_data_length);
-#endif /* CONFIG_PEERKEY */
 		} else if (key_data_length > 0 &&
 			   wpa_parse_kde_ies(key_data, key_data_length,
 					     &kde) == 0 &&
@@ -1327,18 +1299,10 @@
 		wpa_replay_counter_mark_invalid(sm->key_replay, NULL);
 	}
 
-#ifdef CONFIG_PEERKEY
-	if (msg == SMK_M3) {
-		wpa_smk_m3(wpa_auth, sm, key, key_data, key_data_length);
-		return;
-	}
-#endif /* CONFIG_PEERKEY */
-
 	os_free(sm->last_rx_eapol_key);
-	sm->last_rx_eapol_key = os_malloc(data_len);
+	sm->last_rx_eapol_key = os_memdup(data, data_len);
 	if (sm->last_rx_eapol_key == NULL)
 		return;
-	os_memcpy(sm->last_rx_eapol_key, data, data_len);
 	sm->last_rx_eapol_key_len = data_len;
 
 	sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE);
@@ -1412,7 +1376,7 @@
 	int i;
 	u8 *key_mic, *key_data;
 
-	mic_len = wpa_mic_len(sm->wpa_key_mgmt);
+	mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
 	keyhdrlen = sizeof(*key) + mic_len + 2;
 
 	len = sizeof(struct ieee802_1x_hdr) + keyhdrlen;
@@ -1420,6 +1384,8 @@
 	if (force_version)
 		version = force_version;
 	else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
+		 sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE ||
+		 sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
 		 wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
 		 wpa_key_mgmt_fils(sm->wpa_key_mgmt))
 		version = WPA_KEY_INFO_TYPE_AKM_DEFINED;
@@ -1445,6 +1411,8 @@
 	key_data_len = kde_len;
 
 	if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+	     sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE ||
+	     sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
 	     sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
 	     wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
 	     version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) {
@@ -1478,13 +1446,11 @@
 	WPA_PUT_BE16(key->key_info, key_info);
 
 	alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group;
-	if ((key_info & WPA_KEY_INFO_SMK_MESSAGE) ||
-	    (sm->wpa == WPA_VERSION_WPA2 && !pairwise))
+	if (sm->wpa == WPA_VERSION_WPA2 && !pairwise)
 		WPA_PUT_BE16(key->key_length, 0);
 	else
 		WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg));
 
-	/* FIX: STSL: what to use as key_replay_counter? */
 	for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) {
 		sm->key_replay[i].valid = sm->key_replay[i - 1].valid;
 		os_memcpy(sm->key_replay[i].counter,
@@ -1547,9 +1513,14 @@
 		wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
 				buf, key_data_len);
 		if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+		    sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE ||
+		    sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
 		    sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
 		    wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
 		    version == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+			wpa_printf(MSG_DEBUG,
+				   "WPA: Encrypt Key Data using AES-WRAP (KEK length %u)",
+				   (unsigned int) sm->PTK.kek_len);
 			if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len,
 				     (key_data_len - 8) / 8, buf, key_data)) {
 				os_free(hdr);
@@ -1560,6 +1531,9 @@
 #ifndef CONFIG_NO_RC4
 		} else if (sm->PTK.kek_len == 16) {
 			u8 ek[32];
+
+			wpa_printf(MSG_DEBUG,
+				   "WPA: Encrypt Key Data using RC4");
 			os_memcpy(key->key_iv,
 				  sm->group->Counter + WPA_NONCE_LEN - 16, 16);
 			inc_byte_array(sm->group->Counter, WPA_NONCE_LEN);
@@ -1631,6 +1605,9 @@
 			eapol_key_timeout_first_group;
 	else
 		timeout_ms = eapol_key_timeout_subseq;
+	if (wpa_auth->conf.wpa_disable_eapol_key_retries &&
+	    (!pairwise || (key_info & WPA_KEY_INFO_MIC)))
+		timeout_ms = eapol_key_timeout_no_retrans;
 	if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC))
 		sm->pending_1_of_4_timeout = 1;
 	wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry "
@@ -1640,15 +1617,15 @@
 }
 
 
-static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data,
-			      size_t data_len)
+static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK,
+			      u8 *data, size_t data_len)
 {
 	struct ieee802_1x_hdr *hdr;
 	struct wpa_eapol_key *key;
 	u16 key_info;
 	int ret = 0;
 	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN], *mic_pos;
-	size_t mic_len = wpa_mic_len(akmp);
+	size_t mic_len = wpa_mic_len(akmp, pmk_len);
 
 	if (data_len < sizeof(*hdr) + sizeof(*key))
 		return -1;
@@ -1751,6 +1728,16 @@
 #else /* CONFIG_IEEE80211R_AP */
 		break;
 #endif /* CONFIG_IEEE80211R_AP */
+	case WPA_ASSOC_FILS:
+#ifdef CONFIG_FILS
+		wpa_printf(MSG_DEBUG,
+			   "FILS: TK configuration after association");
+		fils_set_tk(sm);
+		sm->fils_completed = 1;
+		return 0;
+#else /* CONFIG_FILS */
+		break;
+#endif /* CONFIG_FILS */
 	case WPA_DRV_STA_REMOVED:
 		sm->tk_already_set = FALSE;
 		return 0;
@@ -1813,7 +1800,9 @@
 	wpa_remove_ptk(sm);
 	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0);
 	sm->TimeoutCtr = 0;
-	if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+	if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+	    sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
+	    sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) {
 		wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
 				   WPA_EAPOL_authorized, 0);
 	}
@@ -1822,9 +1811,14 @@
 
 SM_STATE(WPA_PTK, DISCONNECT)
 {
+	u16 reason = sm->disconnect_reason;
+
 	SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk);
 	sm->Disconnect = FALSE;
-	wpa_sta_disconnect(sm->wpa_auth, sm->addr);
+	sm->disconnect_reason = 0;
+	if (!reason)
+		reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
+	wpa_sta_disconnect(sm->wpa_auth, sm->addr, reason);
 }
 
 
@@ -1940,6 +1934,14 @@
 		wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache");
 		os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
 		sm->pmk_len = sm->pmksa->pmk_len;
+#ifdef CONFIG_DPP
+	} else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No PMKSA cache entry for STA - reject connection");
+		sm->Disconnect = TRUE;
+		sm->disconnect_reason = WLAN_REASON_INVALID_PMKID;
+		return;
+#endif /* CONFIG_DPP */
 	} else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) {
 		unsigned int pmk_len;
 
@@ -1989,16 +1991,26 @@
 SM_STATE(WPA_PTK, INITPSK)
 {
 	const u8 *psk;
+	size_t psk_len;
+
 	SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk);
-	psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL);
+	psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL,
+			       &psk_len);
 	if (psk) {
-		os_memcpy(sm->PMK, psk, PMK_LEN);
-		sm->pmk_len = PMK_LEN;
+		os_memcpy(sm->PMK, psk, psk_len);
+		sm->pmk_len = psk_len;
 #ifdef CONFIG_IEEE80211R_AP
 		os_memcpy(sm->xxkey, psk, PMK_LEN);
 		sm->xxkey_len = PMK_LEN;
 #endif /* CONFIG_IEEE80211R_AP */
 	}
+#ifdef CONFIG_SAE
+	if (wpa_auth_uses_sae(sm) && sm->pmksa) {
+		wpa_printf(MSG_DEBUG, "SAE: PMK from PMKSA cache");
+		os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
+		sm->pmk_len = sm->pmksa->pmk_len;
+	}
+#endif /* CONFIG_SAE */
 	sm->req_replay_counter_used = 0;
 }
 
@@ -2027,7 +2039,9 @@
 	 * one possible PSK for this STA.
 	 */
 	if (sm->wpa == WPA_VERSION_WPA2 &&
-	    wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) &&
+	    (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) ||
+	     (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && sm->pmksa) ||
+	     wpa_key_mgmt_sae(sm->wpa_key_mgmt)) &&
 	    sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) {
 		pmkid = buf;
 		pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
@@ -2047,7 +2061,7 @@
 			 */
 			rsn_pmkid(sm->PMK, sm->pmk_len, sm->wpa_auth->addr,
 				  sm->addr, &pmkid[2 + RSN_SELECTOR_LEN],
-				  wpa_key_mgmt_sha256(sm->wpa_key_mgmt));
+				  sm->wpa_key_mgmt);
 		}
 	}
 	wpa_send_eapol(sm->wpa_auth, sm,
@@ -2074,22 +2088,55 @@
 #ifdef CONFIG_FILS
 
 int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
-			 size_t pmk_len, const u8 *snonce, const u8 *anonce)
+			 size_t pmk_len, const u8 *snonce, const u8 *anonce,
+			 const u8 *dhss, size_t dhss_len,
+			 struct wpabuf *g_sta, struct wpabuf *g_ap)
 {
 	u8 ick[FILS_ICK_MAX_LEN];
 	size_t ick_len;
 	int res;
+	u8 fils_ft[FILS_FT_MAX_LEN];
+	size_t fils_ft_len = 0;
 
 	res = fils_pmk_to_ptk(pmk, pmk_len, sm->addr, sm->wpa_auth->addr,
-			      snonce, anonce, &sm->PTK, ick, &ick_len,
-			      sm->wpa_key_mgmt, sm->pairwise);
+			      snonce, anonce, dhss, dhss_len,
+			      &sm->PTK, ick, &ick_len,
+			      sm->wpa_key_mgmt, sm->pairwise,
+			      fils_ft, &fils_ft_len);
 	if (res < 0)
 		return res;
 	sm->PTK_valid = TRUE;
+	sm->tk_already_set = FALSE;
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (fils_ft_len) {
+		struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+		struct wpa_auth_config *conf = &wpa_auth->conf;
+		u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
+
+		if (wpa_derive_pmk_r0(fils_ft, fils_ft_len,
+				      conf->ssid, conf->ssid_len,
+				      conf->mobility_domain,
+				      conf->r0_key_holder,
+				      conf->r0_key_holder_len,
+				      sm->addr, pmk_r0, pmk_r0_name) < 0)
+			return -1;
+
+		wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0", pmk_r0, PMK_LEN);
+		wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name",
+			    pmk_r0_name, WPA_PMK_NAME_LEN);
+		wpa_ft_store_pmk_r0(wpa_auth, sm->addr, pmk_r0, pmk_r0_name,
+				    sm->pairwise);
+		os_memset(fils_ft, 0, sizeof(fils_ft));
+	}
+#endif /* CONFIG_IEEE80211R_AP */
 
 	res = fils_key_auth_sk(ick, ick_len, snonce, anonce,
 			       sm->addr, sm->wpa_auth->addr,
-			       NULL, 0, NULL, 0, /* TODO: SK+PFS */
+			       g_sta ? wpabuf_head(g_sta) : NULL,
+			       g_sta ? wpabuf_len(g_sta) : 0,
+			       g_ap ? wpabuf_head(g_ap) : NULL,
+			       g_ap ? wpabuf_len(g_ap) : 0,
 			       sm->wpa_key_mgmt, sm->fils_key_auth_sta,
 			       sm->fils_key_auth_ap,
 			       &sm->fils_key_auth_len);
@@ -2158,13 +2205,109 @@
 }
 
 
+const u8 * wpa_fils_validate_fils_session(struct wpa_state_machine *sm,
+					  const u8 *ies, size_t ies_len,
+					  const u8 *fils_session)
+{
+	const u8 *ie, *end;
+	const u8 *session = NULL;
+
+	if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Not a FILS AKM - reject association");
+		return NULL;
+	}
+
+	/* Verify Session element */
+	ie = ies;
+	end = ((const u8 *) ie) + ies_len;
+	while (ie + 1 < end) {
+		if (ie + 2 + ie[1] > end)
+			break;
+		if (ie[0] == WLAN_EID_EXTENSION &&
+		    ie[1] >= 1 + FILS_SESSION_LEN &&
+		    ie[2] == WLAN_EID_EXT_FILS_SESSION) {
+			session = ie;
+			break;
+		}
+		ie += 2 + ie[1];
+	}
+
+	if (!session) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: %s: Could not find FILS Session element in Assoc Req - reject",
+			   __func__);
+		return NULL;
+	}
+
+	if (!fils_session) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: %s: Could not find FILS Session element in STA entry - reject",
+			   __func__);
+		return NULL;
+	}
+
+	if (os_memcmp(fils_session, session + 3, FILS_SESSION_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FILS: Session mismatch");
+		wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
+			    fils_session, FILS_SESSION_LEN);
+		wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session",
+			    session + 3, FILS_SESSION_LEN);
+		return NULL;
+	}
+	return session;
+}
+
+
+int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies,
+				  size_t ies_len)
+{
+	struct ieee802_11_elems elems;
+
+	if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Failed to parse decrypted elements");
+		return -1;
+	}
+
+	if (!elems.fils_session) {
+		wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
+		return -1;
+	}
+
+	if (!elems.fils_key_confirm) {
+		wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element");
+		return -1;
+	}
+
+	if (elems.fils_key_confirm_len != sm->fils_key_auth_len) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Unexpected Key-Auth length %d (expected %d)",
+			   elems.fils_key_confirm_len,
+			   (int) sm->fils_key_auth_len);
+		return -1;
+	}
+
+	if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_sta,
+		      sm->fils_key_auth_len) != 0) {
+		wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch");
+		wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth",
+			    elems.fils_key_confirm, elems.fils_key_confirm_len);
+		wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth",
+			    sm->fils_key_auth_sta, sm->fils_key_auth_len);
+		return -1;
+	}
+
+	return 0;
+}
+
+
 int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
 		       const struct ieee80211_mgmt *mgmt, size_t frame_len,
 		       u8 *pos, size_t left)
 {
 	u16 fc, stype;
 	const u8 *end, *ie_start, *ie, *session, *crypt;
-	struct ieee802_11_elems elems;
 	const u8 *aad[5];
 	size_t aad_len[5];
 
@@ -2193,32 +2336,13 @@
 	 * Find FILS Session element which is the last unencrypted element in
 	 * the frame.
 	 */
-	session = NULL;
-	while (ie + 1 < end) {
-		if (ie + 2 + ie[1] > end)
-			break;
-		if (ie[0] == WLAN_EID_EXTENSION &&
-		    ie[1] >= 1 + FILS_SESSION_LEN &&
-		    ie[2] == WLAN_EID_EXT_FILS_SESSION) {
-			session = ie;
-			break;
-		}
-		ie += 2 + ie[1];
+	session = wpa_fils_validate_fils_session(sm, ie, end - ie,
+						 fils_session);
+	if (!session) {
+		wpa_printf(MSG_DEBUG, "FILS: Session validation failed");
+		return -1;
 	}
 
-	if (!session) {
-		wpa_printf(MSG_DEBUG,
-			   "FILS: Could not find FILS Session element in Association Request frame - reject");
-		return -1;
-	}
-	if (os_memcmp(fils_session, session + 3, FILS_SESSION_LEN) != 0) {
-		wpa_printf(MSG_DEBUG, "FILS: Session mismatch");
-		wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
-			    fils_session, FILS_SESSION_LEN);
-		wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session",
-			    session + 3, FILS_SESSION_LEN);
-		return -1;
-	}
 	crypt = session + 2 + session[1];
 
 	if (end - crypt < AES_BLOCK_SIZE) {
@@ -2257,31 +2381,8 @@
 	wpa_hexdump(MSG_DEBUG, "FILS: Decrypted Association Request elements",
 		    pos, left - AES_BLOCK_SIZE);
 
-	if (ieee802_11_parse_elems(pos, left - AES_BLOCK_SIZE, &elems, 1) ==
-	    ParseFailed) {
-		wpa_printf(MSG_DEBUG,
-			   "FILS: Failed to parse decrypted elements");
-		return -1;
-	}
-	if (!elems.fils_key_confirm) {
-		wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element");
-		return -1;
-	}
-	if (elems.fils_key_confirm_len != sm->fils_key_auth_len) {
-		wpa_printf(MSG_DEBUG,
-			   "FILS: Unexpected Key-Auth length %d (expected %d)",
-			   elems.fils_key_confirm_len,
-			   (int) sm->fils_key_auth_len);
-		return -1;
-	}
-	if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_sta,
-		      sm->fils_key_auth_len) != 0) {
-		wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch");
-		wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth",
-			    elems.fils_key_confirm,
-			    elems.fils_key_confirm_len);
-		wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth",
-			    sm->fils_key_auth_sta, sm->fils_key_auth_len);
+	if (wpa_fils_validate_key_confirm(sm, pos, left - AES_BLOCK_SIZE) < 0) {
+		wpa_printf(MSG_DEBUG, "FILS: Key Confirm validation failed");
 		return -1;
 	}
 
@@ -2297,11 +2398,6 @@
 	u8 *pos = buf + current_len;
 	struct ieee80211_mgmt *mgmt;
 	struct wpabuf *plain;
-	u8 *len, *tmp, *tmp2;
-	u8 hdr[2];
-	u8 *gtk, dummy_gtk[32];
-	size_t gtk_len;
-	struct wpa_group *gsm;
 	const u8 *aad[5];
 	size_t aad_len[5];
 
@@ -2337,10 +2433,54 @@
 	aad_len[4] = pos - aad[4];
 
 	/* The following elements will be encrypted with AES-SIV */
+	plain = fils_prepare_plainbuf(sm, hlp);
+	if (!plain) {
+		wpa_printf(MSG_DEBUG, "FILS: Plain buffer prep failed");
+		return -1;
+	}
+
+	if (pos + wpabuf_len(plain) + AES_BLOCK_SIZE > end) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Not enough room for FILS elements");
+		wpabuf_free(plain);
+		return -1;
+	}
+
+	wpa_hexdump_buf_key(MSG_DEBUG, "FILS: Association Response plaintext",
+			    plain);
+
+	if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len,
+			    wpabuf_head(plain), wpabuf_len(plain),
+			    5, aad, aad_len, pos) < 0) {
+		wpabuf_free(plain);
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG,
+		    "FILS: Encrypted Association Response elements",
+		    pos, AES_BLOCK_SIZE + wpabuf_len(plain));
+	current_len += wpabuf_len(plain) + AES_BLOCK_SIZE;
+	wpabuf_free(plain);
+
+	sm->fils_completed = 1;
+
+	return current_len;
+}
+
+
+static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
+					     const struct wpabuf *hlp)
+{
+	struct wpabuf *plain;
+	u8 *len, *tmp, *tmp2;
+	u8 hdr[2];
+	u8 *gtk, dummy_gtk[32];
+	size_t gtk_len;
+	struct wpa_group *gsm;
 
 	plain = wpabuf_alloc(1000);
 	if (!plain)
-		return -1;
+		return NULL;
 
 	/* TODO: FILS Public Key */
 
@@ -2374,7 +2514,7 @@
 		 */
 		if (random_get_bytes(dummy_gtk, gtk_len) < 0) {
 			wpabuf_free(plain);
-			return -1;
+			return NULL;
 		}
 		gtk = dummy_gtk;
 	}
@@ -2391,33 +2531,7 @@
 	wpabuf_put(plain, tmp2 - tmp);
 
 	*len = (u8 *) wpabuf_put(plain, 0) - len - 1;
-
-	if (pos + wpabuf_len(plain) + AES_BLOCK_SIZE > end) {
-		wpa_printf(MSG_DEBUG,
-			   "FILS: Not enough room for FILS elements");
-		wpabuf_free(plain);
-		return -1;
-	}
-
-	wpa_hexdump_buf_key(MSG_DEBUG, "FILS: Association Response plaintext",
-			    plain);
-
-	if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len,
-			    wpabuf_head(plain), wpabuf_len(plain),
-			    5, aad, aad_len, pos) < 0) {
-		wpabuf_free(plain);
-		return -1;
-	}
-
-	wpa_hexdump(MSG_DEBUG,
-		    "FILS: Encrypted Association Response elements",
-		    pos, AES_BLOCK_SIZE + wpabuf_len(plain));
-	current_len += wpabuf_len(plain) + AES_BLOCK_SIZE;
-	wpabuf_free(plain);
-
-	sm->fils_completed = 1;
-
-	return current_len;
+	return plain;
 }
 
 
@@ -2426,8 +2540,14 @@
 	enum wpa_alg alg;
 	int klen;
 
-	if (!sm || !sm->PTK_valid)
+	if (!sm || !sm->PTK_valid) {
+		wpa_printf(MSG_DEBUG, "FILS: No valid PTK available to set TK");
 		return -1;
+	}
+	if (sm->tk_already_set) {
+		wpa_printf(MSG_DEBUG, "FILS: TK already set to the driver");
+		return -1;
+	}
 
 	alg = wpa_cipher_to_alg(sm->pairwise);
 	klen = wpa_cipher_key_len(sm->pairwise);
@@ -2438,10 +2558,41 @@
 		wpa_printf(MSG_DEBUG, "FILS: Failed to set TK to the driver");
 		return -1;
 	}
+	sm->tk_already_set = TRUE;
 
 	return 0;
 }
 
+
+u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *buf,
+				    const u8 *fils_session, struct wpabuf *hlp)
+{
+	struct wpabuf *plain;
+	u8 *pos = buf;
+
+	/* FILS Session */
+	*pos++ = WLAN_EID_EXTENSION; /* Element ID */
+	*pos++ = 1 + FILS_SESSION_LEN; /* Length */
+	*pos++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */
+	os_memcpy(pos, fils_session, FILS_SESSION_LEN);
+	pos += FILS_SESSION_LEN;
+
+	plain = fils_prepare_plainbuf(sm, hlp);
+	if (!plain) {
+		wpa_printf(MSG_DEBUG, "FILS: Plain buffer prep failed");
+		return NULL;
+	}
+
+	os_memcpy(pos, wpabuf_head(plain), wpabuf_len(plain));
+	pos += wpabuf_len(plain);
+
+	wpa_printf(MSG_DEBUG, "%s: plain buf_len: %u", __func__,
+		   (unsigned int) wpabuf_len(plain));
+	wpabuf_free(plain);
+	sm->fils_completed = 1;
+	return pos;
+}
+
 #endif /* CONFIG_FILS */
 
 
@@ -2451,7 +2602,7 @@
 	struct wpa_ptk PTK;
 	int ok = 0, psk_found = 0;
 	const u8 *pmk = NULL;
-	unsigned int pmk_len;
+	size_t pmk_len;
 	int ft;
 	const u8 *eapol_key_ie, *key_data, *mic;
 	u16 key_data_length;
@@ -2465,19 +2616,19 @@
 	sm->update_snonce = FALSE;
 	os_memset(&PTK, 0, sizeof(PTK));
 
-	mic_len = wpa_mic_len(sm->wpa_key_mgmt);
+	mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
 
 	/* WPA with IEEE 802.1X: use the derived PMK from EAP
 	 * WPA-PSK: iterate through possible PSKs and select the one matching
 	 * the packet */
 	for (;;) {
-		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
+		    !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
 			pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
-					       sm->p2p_dev_addr, pmk);
+					       sm->p2p_dev_addr, pmk, &pmk_len);
 			if (pmk == NULL)
 				break;
 			psk_found = 1;
-			pmk_len = PMK_LEN;
 		} else {
 			pmk = sm->PMK;
 			pmk_len = sm->pmk_len;
@@ -2487,7 +2638,7 @@
 			break;
 
 		if (mic_len &&
-		    wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK,
+		    wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
 				       sm->last_rx_eapol_key,
 				       sm->last_rx_eapol_key_len) == 0) {
 			ok = 1;
@@ -2556,12 +2707,14 @@
 		wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4",
 			    eapol_key_ie, eapol_key_ie_len);
 		/* MLME-DEAUTHENTICATE.request */
-		wpa_sta_disconnect(wpa_auth, sm->addr);
+		wpa_sta_disconnect(wpa_auth, sm->addr,
+				   WLAN_REASON_PREV_AUTH_NOT_VALID);
 		return;
 	}
 #ifdef CONFIG_IEEE80211R_AP
 	if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
-		wpa_sta_disconnect(wpa_auth, sm->addr);
+		wpa_sta_disconnect(wpa_auth, sm->addr,
+				   WLAN_REASON_PREV_AUTH_NOT_VALID);
 		return;
 	}
 #endif /* CONFIG_IEEE80211R_AP */
@@ -2706,6 +2859,11 @@
 	sm->TimeoutEvt = FALSE;
 
 	sm->TimeoutCtr++;
+	if (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
+	    sm->TimeoutCtr > 1) {
+		/* Do not allow retransmission of EAPOL-Key msg 3/4 */
+		return;
+	}
 	if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) {
 		/* No point in sending the EAPOL-Key - we will disconnect
 		 * immediately following this. */
@@ -2871,7 +3029,8 @@
 
 	wpa_send_eapol(sm->wpa_auth, sm,
 		       (secure ? WPA_KEY_INFO_SECURE : 0) |
-		       (wpa_mic_len(sm->wpa_key_mgmt) ? WPA_KEY_INFO_MIC : 0) |
+		       (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
+			WPA_KEY_INFO_MIC : 0) |
 		       WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
 		       WPA_KEY_INFO_KEY_TYPE,
 		       _rsc, sm->ANonce, kde, pos - kde, keyidx, encr);
@@ -2888,7 +3047,8 @@
 		int klen = wpa_cipher_key_len(sm->pairwise);
 		if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
 				     sm->PTK.tk, klen)) {
-			wpa_sta_disconnect(sm->wpa_auth, sm->addr);
+			wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+					   WLAN_REASON_PREV_AUTH_NOT_VALID);
 			return;
 		}
 		/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
@@ -2901,7 +3061,9 @@
 					       sm->wpa_auth, sm);
 		}
 
-		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+		    sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
+		    sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) {
 			wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
 					   WPA_EAPOL_authorized, 1);
 		}
@@ -2973,15 +3135,22 @@
 		    wpa_auth_get_eapol(sm->wpa_auth, sm->addr,
 				       WPA_EAPOL_keyRun) > 0)
 			SM_ENTER(WPA_PTK, INITPMK);
-		else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)
+		else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+			 sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE
 			 /* FIX: && 802.1X::keyRun */)
 			SM_ENTER(WPA_PTK, INITPSK);
+		else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP)
+			SM_ENTER(WPA_PTK, INITPMK);
 		break;
 	case WPA_PTK_INITPMK:
 		if (wpa_auth_get_eapol(sm->wpa_auth, sm->addr,
-				       WPA_EAPOL_keyAvailable) > 0)
+				       WPA_EAPOL_keyAvailable) > 0) {
 			SM_ENTER(WPA_PTK, PTKSTART);
-		else {
+#ifdef CONFIG_DPP
+		} else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && sm->pmksa) {
+			SM_ENTER(WPA_PTK, PTKSTART);
+#endif /* CONFIG_DPP */
+		} else {
 			wpa_auth->dot11RSNA4WayHandshakeFailures++;
 			wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
 					"INITPMK - keyAvailable = false");
@@ -2990,9 +3159,13 @@
 		break;
 	case WPA_PTK_INITPSK:
 		if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr,
-				     NULL))
+				     NULL, NULL)) {
 			SM_ENTER(WPA_PTK, PTKSTART);
-		else {
+#ifdef CONFIG_SAE
+		} else if (wpa_auth_uses_sae(sm) && sm->pmksa) {
+			SM_ENTER(WPA_PTK, PTKSTART);
+#endif /* CONFIG_SAE */
+		} else {
 			wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
 					"no PSK configured for the STA");
 			wpa_auth->dot11RSNA4WayHandshakeFailures++;
@@ -3033,7 +3206,9 @@
 			 sm->EAPOLKeyPairwise && sm->MICVerified)
 			SM_ENTER(WPA_PTK, PTKINITDONE);
 		else if (sm->TimeoutCtr >
-			 sm->wpa_auth->conf.wpa_pairwise_update_count) {
+			 sm->wpa_auth->conf.wpa_pairwise_update_count ||
+			 (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
+			  sm->TimeoutCtr > 1)) {
 			wpa_auth->dot11RSNA4WayHandshakeFailures++;
 			wpa_auth_vlogger(
 				sm->wpa_auth, sm->addr, LOGGER_DEBUG,
@@ -3073,6 +3248,11 @@
 	SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group);
 
 	sm->GTimeoutCtr++;
+	if (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
+	    sm->GTimeoutCtr > 1) {
+		/* Do not allow retransmission of EAPOL-Key group msg 1/2 */
+		return;
+	}
 	if (sm->GTimeoutCtr > sm->wpa_auth->conf.wpa_group_update_count) {
 		/* No point in sending the EAPOL-Key - we will disconnect
 		 * immediately following this. */
@@ -3120,7 +3300,8 @@
 
 	wpa_send_eapol(sm->wpa_auth, sm,
 		       WPA_KEY_INFO_SECURE |
-		       (wpa_mic_len(sm->wpa_key_mgmt) ? WPA_KEY_INFO_MIC : 0) |
+		       (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
+			WPA_KEY_INFO_MIC : 0) |
 		       WPA_KEY_INFO_ACK |
 		       (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
 		       rsc, NULL, kde, kde_len, gsm->GN, 1);
@@ -3175,7 +3356,9 @@
 		    !sm->EAPOLKeyPairwise && sm->MICVerified)
 			SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED);
 		else if (sm->GTimeoutCtr >
-			 sm->wpa_auth->conf.wpa_group_update_count)
+			 sm->wpa_auth->conf.wpa_group_update_count ||
+			 (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
+			  sm->GTimeoutCtr > 1))
 			SM_ENTER(WPA_PTK_GROUP, KEYERROR);
 		else if (sm->TimeoutEvt)
 			SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING);
@@ -3278,7 +3461,7 @@
 }
 
 
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
 /* update GTK when exiting WNM-Sleep Mode */
 void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm)
 {
@@ -3357,7 +3540,7 @@
 	return pos - start;
 }
 #endif /* CONFIG_IEEE80211W */
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
 
 
 static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
@@ -3763,6 +3946,14 @@
 }
 
 
+int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm)
+{
+	if (!sm || !wpa_key_mgmt_fils(sm->wpa_key_mgmt))
+		return 0;
+	return sm->tk_already_set;
+}
+
+
 int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
 			     struct rsn_pmksa_cache_entry *entry)
 {
@@ -3856,6 +4047,22 @@
 }
 
 
+int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
+			const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+			int session_timeout, int akmp)
+{
+	if (wpa_auth->conf.disable_pmksa_caching)
+		return -1;
+
+	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
+				 NULL, 0, wpa_auth->addr, addr, session_timeout,
+				 NULL, akmp))
+		return 0;
+
+	return -1;
+}
+
+
 void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
 			   const u8 *sta_addr)
 {
@@ -4199,6 +4406,14 @@
 				       (timeout_ms % 1000) * 1000,
 				       wpa_send_eapol_timeout, wpa_auth, sm);
 	}
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (sm->eapol_status_cb) {
+		sm->eapol_status_cb(sm->eapol_status_cb_ctx1,
+				    sm->eapol_status_cb_ctx2);
+		sm->eapol_status_cb = NULL;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 }
 
 
@@ -4245,3 +4460,317 @@
 	for (group = wpa_auth->group; group; group = group->next)
 		wpa_group_config_group_keys(wpa_auth, group);
 }
+
+
+#ifdef CONFIG_FILS
+
+struct wpa_auth_fils_iter_data {
+	struct wpa_authenticator *auth;
+	const u8 *cache_id;
+	struct rsn_pmksa_cache_entry *pmksa;
+	const u8 *spa;
+	const u8 *pmkid;
+};
+
+
+static int wpa_auth_fils_iter(struct wpa_authenticator *a, void *ctx)
+{
+	struct wpa_auth_fils_iter_data *data = ctx;
+
+	if (a == data->auth || !a->conf.fils_cache_id_set ||
+	    os_memcmp(a->conf.fils_cache_id, data->cache_id,
+		      FILS_CACHE_ID_LEN) != 0)
+		return 0;
+	data->pmksa = pmksa_cache_auth_get(a->pmksa, data->spa, data->pmkid);
+	return data->pmksa != NULL;
+}
+
+
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth,
+				 const u8 *sta_addr, const u8 *pmkid)
+{
+	struct wpa_auth_fils_iter_data idata;
+
+	if (!wpa_auth->conf.fils_cache_id_set)
+		return NULL;
+	idata.auth = wpa_auth;
+	idata.cache_id = wpa_auth->conf.fils_cache_id;
+	idata.pmksa = NULL;
+	idata.spa = sta_addr;
+	idata.pmkid = pmkid;
+	wpa_auth_for_each_auth(wpa_auth, wpa_auth_fils_iter, &idata);
+	return idata.pmksa;
+}
+
+
+#ifdef CONFIG_IEEE80211R_AP
+int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, u8 *buf, size_t len)
+{
+	struct wpa_auth_config *conf = &wpa_auth->conf;
+
+	return wpa_write_ftie(conf, conf->r0_key_holder,
+			      conf->r0_key_holder_len,
+			      NULL, NULL, buf, len, NULL, 0);
+}
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
+				   u8 *fils_anonce, u8 *fils_snonce,
+				   u8 *fils_kek, size_t *fils_kek_len)
+{
+	os_memcpy(fils_anonce, sm->ANonce, WPA_NONCE_LEN);
+	os_memcpy(fils_snonce, sm->SNonce, WPA_NONCE_LEN);
+	os_memcpy(fils_kek, sm->PTK.kek, WPA_KEK_MAX_LEN);
+	*fils_kek_len = sm->PTK.kek_len;
+}
+
+#endif /* CONFIG_FILS */
+
+
+#if CONFIG_TESTING_OPTIONS
+
+int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
+		       void (*cb)(void *ctx1, void *ctx2),
+		       void *ctx1, void *ctx2)
+{
+	const u8 *anonce = sm->ANonce;
+	u8 anonce_buf[WPA_NONCE_LEN];
+
+	if (change_anonce) {
+		if (random_get_bytes(anonce_buf, WPA_NONCE_LEN))
+			return -1;
+		anonce = anonce_buf;
+	}
+
+	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+			"sending 1/4 msg of 4-Way Handshake (TESTING)");
+	wpa_send_eapol(sm->wpa_auth, sm,
+		       WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL,
+		       anonce, NULL, 0, 0, 0);
+	return 0;
+}
+
+
+int wpa_auth_resend_m3(struct wpa_state_machine *sm,
+		       void (*cb)(void *ctx1, void *ctx2),
+		       void *ctx1, void *ctx2)
+{
+	u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, *opos;
+	size_t gtk_len, kde_len;
+	struct wpa_group *gsm = sm->group;
+	u8 *wpa_ie;
+	int wpa_ie_len, secure, keyidx, encr = 0;
+
+	/* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
+	   GTK[GN], IGTK, [FTIE], [TIE * 2])
+	 */
+
+	/* Use 0 RSC */
+	os_memset(rsc, 0, WPA_KEY_RSC_LEN);
+	/* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */
+	wpa_ie = sm->wpa_auth->wpa_ie;
+	wpa_ie_len = sm->wpa_auth->wpa_ie_len;
+	if (sm->wpa == WPA_VERSION_WPA &&
+	    (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+	    wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) {
+		/* WPA-only STA, remove RSN IE and possible MDIE */
+		wpa_ie = wpa_ie + wpa_ie[1] + 2;
+		if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN)
+			wpa_ie = wpa_ie + wpa_ie[1] + 2;
+		wpa_ie_len = wpa_ie[1] + 2;
+	}
+	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+			"sending 3/4 msg of 4-Way Handshake (TESTING)");
+	if (sm->wpa == WPA_VERSION_WPA2) {
+		/* WPA2 send GTK in the 4-way handshake */
+		secure = 1;
+		gtk = gsm->GTK[gsm->GN - 1];
+		gtk_len = gsm->GTK_len;
+		keyidx = gsm->GN;
+		_rsc = rsc;
+		encr = 1;
+	} else {
+		/* WPA does not include GTK in msg 3/4 */
+		secure = 0;
+		gtk = NULL;
+		gtk_len = 0;
+		keyidx = 0;
+		_rsc = NULL;
+		if (sm->rx_eapol_key_secure) {
+			/*
+			 * It looks like Windows 7 supplicant tries to use
+			 * Secure bit in msg 2/4 after having reported Michael
+			 * MIC failure and it then rejects the 4-way handshake
+			 * if msg 3/4 does not set Secure bit. Work around this
+			 * by setting the Secure bit here even in the case of
+			 * WPA if the supplicant used it first.
+			 */
+			wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+					"STA used Secure bit in WPA msg 2/4 - "
+					"set Secure for 3/4 as workaround");
+			secure = 1;
+		}
+	}
+
+	kde_len = wpa_ie_len + ieee80211w_kde_len(sm);
+	if (gtk)
+		kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
+#ifdef CONFIG_IEEE80211R_AP
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */
+		kde_len += 300; /* FTIE + 2 * TIE */
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+	kde = os_malloc(kde_len);
+	if (kde == NULL)
+		return -1;
+
+	pos = kde;
+	os_memcpy(pos, wpa_ie, wpa_ie_len);
+	pos += wpa_ie_len;
+#ifdef CONFIG_IEEE80211R_AP
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		int res;
+		size_t elen;
+
+		elen = pos - kde;
+		res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name);
+		if (res < 0) {
+			wpa_printf(MSG_ERROR, "FT: Failed to insert "
+				   "PMKR1Name into RSN IE in EAPOL-Key data");
+			os_free(kde);
+			return -1;
+		}
+		pos -= wpa_ie_len;
+		pos += elen;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+	if (gtk) {
+		u8 hdr[2];
+		hdr[0] = keyidx & 0x03;
+		hdr[1] = 0;
+		pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+				  gtk, gtk_len);
+	}
+	opos = pos;
+	pos = ieee80211w_kde_add(sm, pos);
+	if (pos - opos >= WPA_IGTK_KDE_PREFIX_LEN) {
+		opos += 2; /* skip keyid */
+		os_memset(opos, 0, 6); /* clear PN */
+	}
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		int res;
+		struct wpa_auth_config *conf;
+
+		conf = &sm->wpa_auth->conf;
+		if (sm->assoc_resp_ftie &&
+		    kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) {
+			os_memcpy(pos, sm->assoc_resp_ftie,
+				  2 + sm->assoc_resp_ftie[1]);
+			res = 2 + sm->assoc_resp_ftie[1];
+		} else {
+			res = wpa_write_ftie(conf, conf->r0_key_holder,
+					     conf->r0_key_holder_len,
+					     NULL, NULL, pos,
+					     kde + kde_len - pos,
+					     NULL, 0);
+		}
+		if (res < 0) {
+			wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE "
+				   "into EAPOL-Key Key Data");
+			os_free(kde);
+			return -1;
+		}
+		pos += res;
+
+		/* TIE[ReassociationDeadline] (TU) */
+		*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+		*pos++ = 5;
+		*pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE;
+		WPA_PUT_LE32(pos, conf->reassociation_deadline);
+		pos += 4;
+
+		/* TIE[KeyLifetime] (seconds) */
+		*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+		*pos++ = 5;
+		*pos++ = WLAN_TIMEOUT_KEY_LIFETIME;
+		WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60);
+		pos += 4;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+
+	wpa_send_eapol(sm->wpa_auth, sm,
+		       (secure ? WPA_KEY_INFO_SECURE : 0) |
+		       (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
+			WPA_KEY_INFO_MIC : 0) |
+		       WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
+		       WPA_KEY_INFO_KEY_TYPE,
+		       _rsc, sm->ANonce, kde, pos - kde, keyidx, encr);
+	os_free(kde);
+	return 0;
+}
+
+
+int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
+			     void (*cb)(void *ctx1, void *ctx2),
+			     void *ctx1, void *ctx2)
+{
+	u8 rsc[WPA_KEY_RSC_LEN];
+	struct wpa_group *gsm = sm->group;
+	const u8 *kde;
+	u8 *kde_buf = NULL, *pos, *opos, hdr[2];
+	size_t kde_len;
+	u8 *gtk;
+
+	/* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */
+	os_memset(rsc, 0, WPA_KEY_RSC_LEN);
+	/* Use 0 RSC */
+	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+			"sending 1/2 msg of Group Key Handshake (TESTING)");
+
+	gtk = gsm->GTK[gsm->GN - 1];
+	if (sm->wpa == WPA_VERSION_WPA2) {
+		kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
+			ieee80211w_kde_len(sm);
+		kde_buf = os_malloc(kde_len);
+		if (kde_buf == NULL)
+			return -1;
+
+		kde = pos = kde_buf;
+		hdr[0] = gsm->GN & 0x03;
+		hdr[1] = 0;
+		pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+				  gtk, gsm->GTK_len);
+		opos = pos;
+		pos = ieee80211w_kde_add(sm, pos);
+		if (pos - opos >= WPA_IGTK_KDE_PREFIX_LEN) {
+			opos += 2; /* skip keyid */
+			os_memset(opos, 0, 6); /* clear PN */
+		}
+		kde_len = pos - kde;
+	} else {
+		kde = gtk;
+		kde_len = gsm->GTK_len;
+	}
+
+	sm->eapol_status_cb = cb;
+	sm->eapol_status_cb_ctx1 = ctx1;
+	sm->eapol_status_cb_ctx2 = ctx2;
+
+	wpa_send_eapol(sm->wpa_auth, sm,
+		       WPA_KEY_INFO_SECURE |
+		       (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
+			WPA_KEY_INFO_MIC : 0) |
+		       WPA_KEY_INFO_ACK |
+		       (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
+		       rsc, NULL, kde, kde_len, gsm->GN, 1);
+
+	os_free(kde_buf);
+	return 0;
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 387d146..22f33dd 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -1,6 +1,6 @@
 /*
  * hostapd - IEEE 802.11i-2004 / WPA Authenticator
- * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -37,73 +37,89 @@
 
 #define FT_PACKET_REQUEST 0
 #define FT_PACKET_RESPONSE 1
-/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r */
-#define FT_PACKET_R0KH_R1KH_PULL 200
-#define FT_PACKET_R0KH_R1KH_RESP 201
-#define FT_PACKET_R0KH_R1KH_PUSH 202
 
-#define FT_R0KH_R1KH_PULL_NONCE_LEN 16
-#define FT_R0KH_R1KH_PULL_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \
-				    WPA_PMK_NAME_LEN + FT_R1KH_ID_LEN + \
-				    ETH_ALEN)
-#define FT_R0KH_R1KH_PULL_PAD_LEN ((8 - FT_R0KH_R1KH_PULL_DATA_LEN % 8) % 8)
+/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r. These
+ * use OUI Extended EtherType as the encapsulating format. */
+#define FT_PACKET_R0KH_R1KH_PULL 0x01
+#define FT_PACKET_R0KH_R1KH_RESP 0x02
+#define FT_PACKET_R0KH_R1KH_PUSH 0x03
+#define FT_PACKET_R0KH_R1KH_SEQ_REQ 0x04
+#define FT_PACKET_R0KH_R1KH_SEQ_RESP 0x05
 
-struct ft_r0kh_r1kh_pull_frame {
-	u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
-	u8 packet_type; /* FT_PACKET_R0KH_R1KH_PULL */
-	le16 data_length; /* little endian length of data (44) */
-	u8 ap_address[ETH_ALEN];
+/* packet layout
+ *  IEEE 802 extended OUI ethertype frame header
+ *  u16 authlen (little endian)
+ *  multiple of struct ft_rrb_tlv (authenticated only, length = authlen)
+ *  multiple of struct ft_rrb_tlv (AES-SIV encrypted, AES-SIV needs an extra
+ *                                 blocksize length)
+ *
+ * AES-SIV AAD;
+ *  source MAC address (6)
+ *  authenticated-only TLVs (authlen)
+ *  subtype (1; FT_PACKET_*)
+ */
 
-	u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
-	u8 pmk_r0_name[WPA_PMK_NAME_LEN];
-	u8 r1kh_id[FT_R1KH_ID_LEN];
-	u8 s1kh_id[ETH_ALEN];
-	u8 pad[FT_R0KH_R1KH_PULL_PAD_LEN]; /* 8-octet boundary for AES block */
-	u8 key_wrap_extra[8];
+#define FT_RRB_NONCE_LEN 16
+
+#define FT_RRB_LAST_EMPTY     0 /* placeholder or padding */
+
+#define FT_RRB_SEQ            1 /* struct ft_rrb_seq */
+#define FT_RRB_NONCE          2 /* size FT_RRB_NONCE_LEN */
+#define FT_RRB_TIMESTAMP      3 /* le32 unix seconds */
+
+#define FT_RRB_R0KH_ID        4 /* FT_R0KH_ID_MAX_LEN */
+#define FT_RRB_R1KH_ID        5 /* FT_R1KH_ID_LEN */
+#define FT_RRB_S1KH_ID        6 /* ETH_ALEN */
+
+#define FT_RRB_PMK_R0_NAME    7 /* WPA_PMK_NAME_LEN */
+#define FT_RRB_PMK_R0         8 /* PMK_LEN */
+#define FT_RRB_PMK_R1_NAME    9 /* WPA_PMK_NAME_LEN */
+#define FT_RRB_PMK_R1        10 /* PMK_LEN */
+
+#define FT_RRB_PAIRWISE      11 /* le16 */
+
+struct ft_rrb_tlv {
+	le16 type;
+	le16 len;
+	/* followed by data of length len */
 } STRUCT_PACKED;
 
-#define FT_R0KH_R1KH_RESP_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \
-				    FT_R1KH_ID_LEN + ETH_ALEN + PMK_LEN + \
-				    WPA_PMK_NAME_LEN + 2)
-#define FT_R0KH_R1KH_RESP_PAD_LEN ((8 - FT_R0KH_R1KH_RESP_DATA_LEN % 8) % 8)
-struct ft_r0kh_r1kh_resp_frame {
-	u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
-	u8 packet_type; /* FT_PACKET_R0KH_R1KH_RESP */
-	le16 data_length; /* little endian length of data (78) */
-	u8 ap_address[ETH_ALEN];
-
-	u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; /* copied from pull */
-	u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */
-	u8 s1kh_id[ETH_ALEN]; /* copied from pull */
-	u8 pmk_r1[PMK_LEN];
-	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
-	le16 pairwise;
-	u8 pad[FT_R0KH_R1KH_RESP_PAD_LEN]; /* 8-octet boundary for AES block */
-	u8 key_wrap_extra[8];
+struct ft_rrb_seq {
+	le32 dom;
+	le32 seq;
+	le32 ts;
 } STRUCT_PACKED;
 
-#define FT_R0KH_R1KH_PUSH_DATA_LEN (4 + FT_R1KH_ID_LEN + ETH_ALEN + \
-				    WPA_PMK_NAME_LEN + PMK_LEN + \
-				    WPA_PMK_NAME_LEN + 2)
-#define FT_R0KH_R1KH_PUSH_PAD_LEN ((8 - FT_R0KH_R1KH_PUSH_DATA_LEN % 8) % 8)
-struct ft_r0kh_r1kh_push_frame {
-	u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
-	u8 packet_type; /* FT_PACKET_R0KH_R1KH_PUSH */
-	le16 data_length; /* little endian length of data (82) */
-	u8 ap_address[ETH_ALEN];
-
-	/* Encrypted with AES key-wrap */
-	u8 timestamp[4]; /* current time in seconds since unix epoch, little
-			  * endian */
-	u8 r1kh_id[FT_R1KH_ID_LEN];
-	u8 s1kh_id[ETH_ALEN];
-	u8 pmk_r0_name[WPA_PMK_NAME_LEN];
-	u8 pmk_r1[PMK_LEN];
-	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
-	le16 pairwise;
-	u8 pad[FT_R0KH_R1KH_PUSH_PAD_LEN]; /* 8-octet boundary for AES block */
-	u8 key_wrap_extra[8];
-} STRUCT_PACKED;
+/* session TLVs:
+ *   required: PMK_R1, PMK_R1_NAME, PAIRWISE
+ *
+ * pull frame TLVs:
+ *   auth:
+ *     required: SEQ, NONCE, R0KH_ID, R1KH_ID
+ *   encrypted:
+ *     required: PMK_R0_NAME, S1KH_ID
+ *
+ * response frame TLVs:
+ *   auth:
+ *     required: SEQ, NONCE, R0KH_ID, R1KH_ID
+ *   encrypted:
+ *     required: S1KH_ID
+ *     optional: session TLVs
+ *
+ * push frame TLVs:
+ *   auth:
+ *     required: SEQ, R0KH_ID, R1KH_ID
+ *   encrypted:
+ *     required: S1KH_ID, PMK_R0_NAME, session TLVs
+ *
+ * sequence number request frame TLVs:
+ *   auth:
+ *     required: R0KH_ID, R1KH_ID, NONCE
+ *
+ * sequence number response frame TLVs:
+ *   auth:
+ *     required: SEQ, NONCE, R0KH_ID, R1KH_ID
+ */
 
 #ifdef _MSC_VER
 #pragma pack(pop)
@@ -116,6 +132,7 @@
 struct wpa_state_machine;
 struct rsn_pmksa_cache_entry;
 struct eapol_state_machine;
+struct ft_remote_seq;
 
 
 struct ft_remote_r0kh {
@@ -123,7 +140,8 @@
 	u8 addr[ETH_ALEN];
 	u8 id[FT_R0KH_ID_MAX_LEN];
 	size_t id_len;
-	u8 key[16];
+	u8 key[32];
+	struct ft_remote_seq *seq;
 };
 
 
@@ -131,7 +149,8 @@
 	struct ft_remote_r1kh *next;
 	u8 addr[ETH_ALEN];
 	u8 id[FT_R1KH_ID_LEN];
-	u8 key[16];
+	u8 key[32];
+	struct ft_remote_seq *seq;
 };
 
 
@@ -146,10 +165,10 @@
 	int wpa_ptk_rekey;
 	u32 wpa_group_update_count;
 	u32 wpa_pairwise_update_count;
+	int wpa_disable_eapol_key_retries;
 	int rsn_pairwise;
 	int rsn_preauth;
 	int eapol_version;
-	int peerkey;
 	int wmm_enabled;
 	int wmm_uapsd;
 	int disable_pmksa_caching;
@@ -167,9 +186,13 @@
 	size_t r0_key_holder_len;
 	u8 r1_key_holder[FT_R1KH_ID_LEN];
 	u32 r0_key_lifetime;
+	int rkh_pos_timeout;
+	int rkh_neg_timeout;
+	int rkh_pull_timeout; /* ms */
+	int rkh_pull_retries;
 	u32 reassociation_deadline;
-	struct ft_remote_r0kh *r0kh_list;
-	struct ft_remote_r1kh *r1kh_list;
+	struct ft_remote_r0kh **r0kh_list;
+	struct ft_remote_r1kh **r1kh_list;
 	int pmk_r1_push;
 	int ft_over_ds;
 	int ft_psk_generate_local;
@@ -187,6 +210,10 @@
 	u8 ip_addr_start[4];
 	u8 ip_addr_end[4];
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_FILS
+	unsigned int fils_cache_id_set:1;
+	u8 fils_cache_id[FILS_CACHE_ID_LEN];
+#endif /* CONFIG_FILS */
 };
 
 typedef enum {
@@ -209,7 +236,7 @@
 			  int value);
 	int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var);
 	const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr,
-			      const u8 *prev_psk);
+			      const u8 *prev_psk, size_t *psk_len);
 	int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len);
 	int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg,
 		       const u8 *addr, int idx, u8 *key, size_t key_len);
@@ -222,6 +249,8 @@
 						  void *ctx), void *cb_ctx);
 	int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data,
 			  size_t data_len);
+	int (*send_oui)(void *ctx, const u8 *dst, u8 oui_suffix, const u8 *data,
+			size_t data_len);
 #ifdef CONFIG_IEEE80211R_AP
 	struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr);
 	int (*send_ft_action)(void *ctx, const u8 *dst,
@@ -247,13 +276,14 @@
 	WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE,
 	WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL,
 	WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER,
-	WPA_INVALID_MDIE, WPA_INVALID_PROTO
+	WPA_INVALID_MDIE, WPA_INVALID_PROTO, WPA_INVALID_PMKID
 };
 
 int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
 			struct wpa_state_machine *sm,
 			const u8 *wpa_ie, size_t wpa_ie_len,
-			const u8 *mdie, size_t mdie_len);
+			const u8 *mdie, size_t mdie_len,
+			const u8 *owe_dh, size_t owe_dh_len);
 int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
 		      struct wpa_state_machine *sm,
 		      const u8 *osen_ie, size_t osen_ie_len);
@@ -270,7 +300,7 @@
 		 u8 *data, size_t data_len);
 enum wpa_event {
 	WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH,
-	WPA_REAUTH_EAPOL, WPA_ASSOC_FT, WPA_DRV_STA_REMOVED
+	WPA_REAUTH_EAPOL, WPA_ASSOC_FT, WPA_ASSOC_FILS, WPA_DRV_STA_REMOVED
 };
 void wpa_remove_ptk(struct wpa_state_machine *sm);
 int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event);
@@ -284,6 +314,7 @@
 int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm);
 int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm);
 int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm);
+int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm);
 int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
 			     struct rsn_pmksa_cache_entry *entry);
 struct rsn_pmksa_cache_entry *
@@ -300,6 +331,9 @@
 			       struct eapol_state_machine *eapol);
 int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
 			   const u8 *pmk, const u8 *pmkid);
+int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
+			const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+			int session_timeout, int akmp);
 void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
 			   const u8 *sta_addr);
 int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
@@ -315,6 +349,9 @@
 struct rsn_pmksa_cache_entry *
 wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
 		   const u8 *pmkid);
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth,
+				 const u8 *sta_addr, const u8 *pmkid);
 void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
 			      struct wpa_state_machine *sm,
 			      struct wpa_authenticator *wpa_auth,
@@ -338,7 +375,12 @@
 int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len);
 int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
 		  const u8 *data, size_t data_len);
+void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
+		       const u8 *dst_addr, u8 oui_suffix, const u8 *data,
+		       size_t data_len);
 void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr);
+void wpa_ft_deinit(struct wpa_authenticator *wpa_auth);
+void wpa_ft_sta_deinit(struct wpa_state_machine *sm);
 #endif /* CONFIG_IEEE80211R_AP */
 
 void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm);
@@ -359,7 +401,9 @@
 int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id);
 int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id);
 int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
-			 size_t pmk_len, const u8 *snonce, const u8 *anonce);
+			 size_t pmk_len, const u8 *snonce, const u8 *anonce,
+			 const u8 *dhss, size_t dhss_len,
+			 struct wpabuf *g_sta, struct wpabuf *g_ap);
 int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
 		       const struct ieee80211_mgmt *mgmt, size_t frame_len,
 		       u8 *pos, size_t left);
@@ -367,5 +411,31 @@
 		       size_t current_len, size_t max_len,
 		       const struct wpabuf *hlp);
 int fils_set_tk(struct wpa_state_machine *sm);
+u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *eid,
+				    const u8 *fils_session,
+				    struct wpabuf *fils_hlp_resp);
+const u8 *  wpa_fils_validate_fils_session(struct wpa_state_machine *sm,
+					   const u8 *ies, size_t ies_len,
+					   const u8 *fils_session);
+int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies,
+				  size_t ies_len);
+
+int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, u8 *buf, size_t len);
+void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
+				   u8 *fils_anonce, u8 *fils_snonce,
+				   u8 *fils_kek, size_t *fils_kek_len);
+u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm,
+				   u8 *pos, size_t max_len,
+				   const u8 *req_ies, size_t req_ies_len);
+
+int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
+		       void (*cb)(void *ctx1, void *ctx2),
+		       void *ctx1, void *ctx2);
+int wpa_auth_resend_m3(struct wpa_state_machine *sm,
+		       void (*cb)(void *ctx1, void *ctx2),
+		       void *ctx1, void *ctx2);
+int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
+			     void (*cb)(void *ctx1, void *ctx2),
+			     void *ctx1, void *ctx2);
 
 #endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 210d300..153752d 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -13,6 +13,8 @@
 #include "utils/list.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/random.h"
 #include "ap_config.h"
@@ -24,11 +26,381 @@
 
 #ifdef CONFIG_IEEE80211R_AP
 
+const unsigned int ftRRBseqTimeout = 10;
+const unsigned int ftRRBmaxQueueLen = 100;
+
+
 static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
 				     const u8 *current_ap, const u8 *sta_addr,
 				     u16 status, const u8 *resp_ies,
 				     size_t resp_ies_len);
+static void ft_finish_pull(struct wpa_state_machine *sm);
+static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx);
+static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx);
 
+struct tlv_list {
+	u16 type;
+	size_t len;
+	const u8 *data;
+};
+
+
+/**
+ * wpa_ft_rrb_decrypt - Decrypt FT RRB message
+ * @key: AES-SIV key for AEAD
+ * @key_len: Length of key in octets
+ * @enc: Pointer to encrypted TLVs
+ * @enc_len: Length of encrypted TLVs in octets
+ * @auth: Pointer to authenticated TLVs
+ * @auth_len: Length of authenticated TLVs in octets
+ * @src_addr: MAC address of the frame sender
+ * @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*)
+ * @plain: Pointer to return the pointer to the allocated plaintext buffer;
+ *	needs to be freed by the caller if not NULL;
+ *	will only be returned on success
+ * @plain_len: Pointer to return the length of the allocated plaintext buffer
+ *	in octets
+ * Returns: 0 on success, -1 on error
+ */
+static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len,
+			      const u8 *enc, const size_t enc_len,
+			      const u8 *auth, const size_t auth_len,
+			      const u8 *src_addr, u8 type,
+			      u8 **plain, size_t *plain_size)
+{
+	const u8 *ad[3] = { src_addr, auth, &type };
+	size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) };
+
+	wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypt using key", key, key_len);
+
+	if (!key) { /* skip decryption */
+		*plain = os_memdup(enc, enc_len);
+		if (enc_len > 0 && !*plain)
+			goto err;
+
+		*plain_size = enc_len;
+
+		return 0;
+	}
+
+	*plain = NULL;
+
+	/* SIV overhead */
+	if (enc_len < AES_BLOCK_SIZE)
+		goto err;
+
+	*plain = os_zalloc(enc_len - AES_BLOCK_SIZE);
+	if (!*plain)
+		goto err;
+
+	if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len,
+			    *plain) < 0)
+		goto err;
+
+	*plain_size = enc_len - AES_BLOCK_SIZE;
+	wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypted TLVs",
+			*plain, *plain_size);
+	return 0;
+err:
+	os_free(*plain);
+	*plain = NULL;
+	*plain_size = 0;
+
+	wpa_printf(MSG_ERROR, "FT(RRB): Failed to decrypt");
+
+	return -1;
+}
+
+
+/* get first tlv record in packet matching type
+ * @data (decrypted) packet
+ * @return 0 on success else -1
+ */
+static int wpa_ft_rrb_get_tlv(const u8 *plain, size_t plain_len,
+			      u16 type, size_t *tlv_len, const u8 **tlv_data)
+{
+	const struct ft_rrb_tlv *f;
+	size_t left;
+	le16 type16;
+	size_t len;
+
+	left = plain_len;
+	type16 = host_to_le16(type);
+
+	while (left >= sizeof(*f)) {
+		f = (const struct ft_rrb_tlv *) plain;
+
+		left -= sizeof(*f);
+		plain += sizeof(*f);
+		len = le_to_host16(f->len);
+
+		if (left < len) {
+			wpa_printf(MSG_DEBUG, "FT: RRB message truncated");
+			break;
+		}
+
+		if (f->type == type16) {
+			*tlv_len = len;
+			*tlv_data = plain;
+			return 0;
+		}
+
+		left -= len;
+		plain += len;
+	}
+
+	return -1;
+}
+
+
+static void wpa_ft_rrb_dump(const u8 *plain, const size_t plain_len)
+{
+	const struct ft_rrb_tlv *f;
+	size_t left;
+	size_t len;
+
+	left = plain_len;
+
+	wpa_printf(MSG_DEBUG, "FT: RRB dump message");
+	while (left >= sizeof(*f)) {
+		f = (const struct ft_rrb_tlv *) plain;
+
+		left -= sizeof(*f);
+		plain += sizeof(*f);
+		len = le_to_host16(f->len);
+
+		wpa_printf(MSG_DEBUG, "FT: RRB TLV type = %d, len = %zu",
+			   le_to_host16(f->type), len);
+
+		if (left < len) {
+			wpa_printf(MSG_DEBUG,
+				   "FT: RRB message truncated: left %zu bytes, need %zu",
+				   left, len);
+			break;
+		}
+
+		wpa_hexdump(MSG_DEBUG, "FT: RRB TLV data", plain, len);
+
+		left -= len;
+		plain += len;
+	}
+
+	if (left > 0)
+		wpa_hexdump(MSG_DEBUG, "FT: RRB TLV padding", plain, left);
+
+	wpa_printf(MSG_DEBUG, "FT: RRB dump message end");
+}
+
+
+static size_t wpa_ft_tlv_len(const struct tlv_list *tlvs)
+{
+	size_t tlv_len = 0;
+	int i;
+
+	if (!tlvs)
+		return 0;
+
+	for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) {
+		tlv_len += sizeof(struct ft_rrb_tlv);
+		tlv_len += tlvs[i].len;
+	}
+
+	return tlv_len;
+}
+
+
+static size_t wpa_ft_tlv_lin(const struct tlv_list *tlvs, u8 *start,
+			     u8 *endpos)
+{
+	int i;
+	size_t tlv_len;
+	struct ft_rrb_tlv *hdr;
+	u8 *pos;
+
+	if (!tlvs)
+		return 0;
+
+	tlv_len = 0;
+	pos = start;
+	for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) {
+		if (tlv_len + sizeof(*hdr) > (size_t) (endpos - start))
+			return tlv_len;
+		tlv_len += sizeof(*hdr);
+		hdr = (struct ft_rrb_tlv *) pos;
+		hdr->type = host_to_le16(tlvs[i].type);
+		hdr->len = host_to_le16(tlvs[i].len);
+		pos = start + tlv_len;
+
+		if (tlv_len + tlvs[i].len > (size_t) (endpos - start))
+			return tlv_len;
+		tlv_len += tlvs[i].len;
+		os_memcpy(pos, tlvs[i].data, tlvs[i].len);
+		pos = start + tlv_len;
+	}
+
+	return tlv_len;
+}
+
+
+static int wpa_ft_rrb_lin(const struct tlv_list *tlvs1,
+			  const struct tlv_list *tlvs2,
+			  u8 **plain, size_t *plain_len)
+{
+	u8 *pos, *endpos;
+	size_t tlv_len;
+
+	tlv_len = wpa_ft_tlv_len(tlvs1);
+	tlv_len += wpa_ft_tlv_len(tlvs2);
+
+	*plain_len = tlv_len;
+	*plain = os_zalloc(tlv_len);
+	if (!*plain) {
+		wpa_printf(MSG_ERROR, "FT: Failed to allocate plaintext");
+		goto err;
+	}
+
+	pos = *plain;
+	endpos = *plain + tlv_len;
+	pos += wpa_ft_tlv_lin(tlvs1, pos, endpos);
+	pos += wpa_ft_tlv_lin(tlvs2, pos, endpos);
+
+	/* sanity check */
+	if (pos != endpos) {
+		wpa_printf(MSG_ERROR, "FT: Length error building RRB");
+		goto err;
+	}
+
+	return 0;
+
+err:
+	os_free(*plain);
+	*plain = NULL;
+	*plain_len = 0;
+	return -1;
+}
+
+
+static int wpa_ft_rrb_encrypt(const u8 *key, const size_t key_len,
+			      const u8 *plain, const size_t plain_len,
+			      const u8 *auth, const size_t auth_len,
+			      const u8 *src_addr, u8 type, u8 *enc)
+{
+	const u8 *ad[3] = { src_addr, auth, &type };
+	size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) };
+
+	wpa_hexdump_key(MSG_DEBUG, "FT(RRB): plaintext message",
+			plain, plain_len);
+	wpa_hexdump_key(MSG_DEBUG, "FT(RRB): encrypt using key", key, key_len);
+
+	if (!key) {
+		/* encryption not needed, return plaintext as packet */
+		os_memcpy(enc, plain, plain_len);
+	} else if (aes_siv_encrypt(key, key_len, plain, plain_len,
+				   3, ad, ad_len, enc) < 0) {
+		wpa_printf(MSG_ERROR, "FT: Failed to encrypt RRB-OUI message");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * wpa_ft_rrb_build - Build and encrypt an FT RRB message
+ * @key: AES-SIV key for AEAD
+ * @key_len: Length of key in octets
+ * @tlvs_enc0: First set of to-be-encrypted TLVs
+ * @tlvs_enc1: Second set of to-be-encrypted TLVs
+ * @tlvs_auth: Set of to-be-authenticated TLVs
+ * @src_addr: MAC address of the frame sender
+ * @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*)
+ * @packet Pointer to return the pointer to the allocated packet buffer;
+ *         needs to be freed by the caller if not null;
+ *         will only be returned on success
+ * @packet_len: Pointer to return the length of the allocated buffer in octets
+ * Returns: 0 on success, -1 on error
+ */
+static int wpa_ft_rrb_build(const u8 *key, const size_t key_len,
+			    const struct tlv_list *tlvs_enc0,
+			    const struct tlv_list *tlvs_enc1,
+			    const struct tlv_list *tlvs_auth,
+			    const u8 *src_addr, u8 type,
+			    u8 **packet, size_t *packet_len)
+{
+	u8 *plain = NULL, *auth = NULL, *pos;
+	size_t plain_len = 0, auth_len = 0;
+	int ret = -1;
+
+	if (wpa_ft_rrb_lin(tlvs_enc0, tlvs_enc1, &plain, &plain_len) < 0)
+		goto out;
+
+	if (wpa_ft_rrb_lin(tlvs_auth, NULL, &auth, &auth_len) < 0)
+		goto out;
+
+	*packet_len = sizeof(u16) + auth_len + plain_len;
+	if (key)
+		*packet_len += AES_BLOCK_SIZE;
+	*packet = os_zalloc(*packet_len);
+	if (!*packet)
+		goto out;
+
+	pos = *packet;
+	WPA_PUT_LE16(pos, auth_len);
+	pos += 2;
+	os_memcpy(pos, auth, auth_len);
+	pos += auth_len;
+	if (wpa_ft_rrb_encrypt(key, key_len, plain, plain_len, auth,
+			       auth_len, src_addr, type, pos) < 0)
+		goto out;
+
+	ret = 0;
+
+out:
+	bin_clear_free(plain, plain_len);
+	os_free(auth);
+
+	if (ret) {
+		wpa_printf(MSG_ERROR, "FT: Failed to build RRB-OUI message");
+		os_free(*packet);
+		*packet = NULL;
+		*packet_len = 0;
+	}
+
+	return ret;
+}
+
+
+#define RRB_GET_SRC(srcfield, type, field, txt, checklength) do { \
+	if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \
+				&f_##field##_len, &f_##field) < 0 || \
+	    (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \
+		wpa_printf(MSG_INFO, "FT: Missing required " #field \
+			   " in %s from " MACSTR, txt, MAC2STR(src_addr)); \
+		wpa_ft_rrb_dump(srcfield, srcfield##_len); \
+		goto out; \
+	} \
+} while (0)
+
+#define RRB_GET(type, field, txt, checklength) \
+	RRB_GET_SRC(plain, type, field, txt, checklength)
+#define RRB_GET_AUTH(type, field, txt, checklength) \
+	RRB_GET_SRC(auth, type, field, txt, checklength)
+
+#define RRB_GET_OPTIONAL_SRC(srcfield, type, field, txt, checklength) do { \
+	if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \
+				&f_##field##_len, &f_##field) < 0 || \
+	    (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \
+		wpa_printf(MSG_DEBUG, "FT: Missing optional " #field \
+			   " in %s from " MACSTR, txt, MAC2STR(src_addr)); \
+		f_##field##_len = 0; \
+		f_##field = NULL; \
+	} \
+} while (0)
+
+#define RRB_GET_OPTIONAL(type, field, txt, checklength) \
+	RRB_GET_OPTIONAL_SRC(plain, type, field, txt, checklength)
+#define RRB_GET_OPTIONAL_AUTH(type, field, txt, checklength) \
+	RRB_GET_OPTIONAL_SRC(auth, type, field, txt, checklength)
 
 static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst,
 			   const u8 *data, size_t data_len)
@@ -41,6 +413,19 @@
 }
 
 
+static int wpa_ft_rrb_oui_send(struct wpa_authenticator *wpa_auth,
+			       const u8 *dst, u8 oui_suffix,
+			       const u8 *data, size_t data_len)
+{
+	if (!wpa_auth->cb->send_oui)
+		return -1;
+	wpa_printf(MSG_DEBUG, "FT: RRB-OUI type %u send to " MACSTR,
+		   oui_suffix, MAC2STR(dst));
+	return wpa_auth->cb->send_oui(wpa_auth->cb_ctx, dst, oui_suffix, data,
+				      data_len);
+}
+
+
 static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth,
 			      const u8 *dst, const u8 *data, size_t data_len)
 {
@@ -58,7 +443,7 @@
 	if (wpa_auth->cb->get_psk == NULL)
 		return NULL;
 	return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr,
-				     prev_psk);
+				     prev_psk, NULL);
 }
 
 
@@ -153,6 +538,301 @@
 }
 
 
+/* A packet to be handled after seq response */
+struct ft_remote_item {
+	struct dl_list list;
+
+	u8 nonce[FT_RRB_NONCE_LEN];
+	struct os_reltime nonce_ts;
+
+	u8 src_addr[ETH_ALEN];
+	u8 *enc;
+	size_t enc_len;
+	u8 *auth;
+	size_t auth_len;
+	int (*cb)(struct wpa_authenticator *wpa_auth,
+		  const u8 *src_addr,
+		  const u8 *enc, size_t enc_len,
+		  const u8 *auth, size_t auth_len,
+		  int no_defer);
+};
+
+
+static void wpa_ft_rrb_seq_free(struct ft_remote_item *item)
+{
+	eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, ELOOP_ALL_CTX, item);
+	dl_list_del(&item->list);
+	bin_clear_free(item->enc, item->enc_len);
+	os_free(item->auth);
+	os_free(item);
+}
+
+
+static void wpa_ft_rrb_seq_flush(struct wpa_authenticator *wpa_auth,
+				 struct ft_remote_seq *rkh_seq, int cb)
+{
+	struct ft_remote_item *item, *n;
+
+	dl_list_for_each_safe(item, n, &rkh_seq->rx.queue,
+			      struct ft_remote_item, list) {
+		if (cb && item->cb)
+			item->cb(wpa_auth, item->src_addr, item->enc,
+				 item->enc_len, item->auth, item->auth_len, 1);
+		wpa_ft_rrb_seq_free(item);
+	}
+}
+
+
+static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct ft_remote_item *item = timeout_ctx;
+
+	wpa_ft_rrb_seq_free(item);
+}
+
+
+static int
+wpa_ft_rrb_seq_req(struct wpa_authenticator *wpa_auth,
+		   struct ft_remote_seq *rkh_seq, const u8 *src_addr,
+		   const u8 *f_r0kh_id, size_t f_r0kh_id_len,
+		   const u8 *f_r1kh_id, const u8 *key, size_t key_len,
+		   const u8 *enc, size_t enc_len,
+		   const u8 *auth, size_t auth_len,
+		   int (*cb)(struct wpa_authenticator *wpa_auth,
+			     const u8 *src_addr,
+			     const u8 *enc, size_t enc_len,
+			     const u8 *auth, size_t auth_len,
+			     int no_defer))
+{
+	struct ft_remote_item *item = NULL;
+	u8 *packet = NULL;
+	size_t packet_len;
+	struct tlv_list seq_req_auth[] = {
+		{ .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN,
+		  .data = NULL /* to be filled: item->nonce */ },
+		{ .type = FT_RRB_R0KH_ID, .len = f_r0kh_id_len,
+		  .data = f_r0kh_id },
+		{ .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
+		  .data = f_r1kh_id },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
+
+	if (dl_list_len(&rkh_seq->rx.queue) >= ftRRBmaxQueueLen) {
+		wpa_printf(MSG_DEBUG, "FT: Sequence number queue too long");
+		goto err;
+	}
+
+	item = os_zalloc(sizeof(*item));
+	if (!item)
+		goto err;
+
+	os_memcpy(item->src_addr, src_addr, ETH_ALEN);
+	item->cb = cb;
+
+	if (random_get_bytes(item->nonce, FT_RRB_NONCE_LEN) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Seq num nonce: out of random bytes");
+		goto err;
+	}
+
+	if (os_get_reltime(&item->nonce_ts) < 0)
+		goto err;
+
+	if (enc && enc_len > 0) {
+		item->enc = os_memdup(enc, enc_len);
+		item->enc_len = enc_len;
+		if (!item->enc)
+			goto err;
+	}
+
+	if (auth && auth_len > 0) {
+		item->auth = os_memdup(auth, auth_len);
+		item->auth_len = auth_len;
+		if (!item->auth)
+			goto err;
+	}
+
+	eloop_register_timeout(ftRRBseqTimeout, 0, wpa_ft_rrb_seq_timeout,
+			       wpa_auth, item);
+
+	seq_req_auth[0].data = item->nonce;
+
+	if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_req_auth,
+			     wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
+			     &packet, &packet_len) < 0) {
+		item = NULL; /* some other seq resp might still accept this */
+		goto err;
+	}
+
+	dl_list_add(&rkh_seq->rx.queue, &item->list);
+
+	wpa_ft_rrb_oui_send(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
+			    packet, packet_len);
+
+	os_free(packet);
+
+	return 0;
+err:
+	wpa_printf(MSG_DEBUG, "FT: Failed to send sequence number request");
+	if (item) {
+		os_free(item->auth);
+		bin_clear_free(item->enc, item->enc_len);
+		os_free(item);
+	}
+
+	return -1;
+}
+
+
+#define FT_RRB_SEQ_OK    0
+#define FT_RRB_SEQ_DROP  1
+#define FT_RRB_SEQ_DEFER 2
+
+static int
+wpa_ft_rrb_seq_chk(struct ft_remote_seq *rkh_seq, const u8 *src_addr,
+		   const u8 *enc, size_t enc_len,
+		   const u8 *auth, size_t auth_len,
+		   const char *msgtype, int no_defer)
+{
+	const u8 *f_seq;
+	size_t f_seq_len;
+	const struct ft_rrb_seq *msg_both;
+	u32 msg_seq, msg_off, rkh_off;
+	struct os_reltime now;
+	unsigned int i;
+
+	RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both));
+	wpa_hexdump(MSG_DEBUG, "FT: sequence number", f_seq, f_seq_len);
+	msg_both = (const struct ft_rrb_seq *) f_seq;
+
+	if (rkh_seq->rx.num_last == 0) {
+		/* first packet from remote */
+		goto defer;
+	}
+
+	if (le_to_host32(msg_both->dom) != rkh_seq->rx.dom) {
+		/* remote might have rebooted */
+		goto defer;
+	}
+
+	if (os_get_reltime(&now) == 0) {
+		u32 msg_ts_now_remote, msg_ts_off;
+		struct os_reltime now_remote;
+
+		os_reltime_sub(&now, &rkh_seq->rx.time_offset, &now_remote);
+		msg_ts_now_remote = now_remote.sec;
+		msg_ts_off = le_to_host32(msg_both->ts) -
+			(msg_ts_now_remote - ftRRBseqTimeout);
+		if (msg_ts_off > 2 * ftRRBseqTimeout)
+			goto defer;
+	}
+
+	msg_seq = le_to_host32(msg_both->seq);
+	rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx];
+	msg_off = msg_seq - rkh_off;
+	if (msg_off > 0xC0000000)
+		goto out; /* too old message, drop it */
+
+	if (msg_off <= 0x40000000) {
+		for (i = 0; i < rkh_seq->rx.num_last; i++) {
+			if (rkh_seq->rx.last[i] == msg_seq)
+				goto out; /* duplicate message, drop it */
+		}
+
+		return FT_RRB_SEQ_OK;
+	}
+
+defer:
+	if (no_defer)
+		goto out;
+
+	wpa_printf(MSG_DEBUG, "FT: Possibly invalid sequence number in %s from "
+		   MACSTR, msgtype, MAC2STR(src_addr));
+
+	return FT_RRB_SEQ_DEFER;
+out:
+	wpa_printf(MSG_DEBUG, "FT: Invalid sequence number in %s from " MACSTR,
+		   msgtype, MAC2STR(src_addr));
+
+	return FT_RRB_SEQ_DROP;
+}
+
+
+static void
+wpa_ft_rrb_seq_accept(struct wpa_authenticator *wpa_auth,
+		      struct ft_remote_seq *rkh_seq, const u8 *src_addr,
+		      const u8 *auth, size_t auth_len,
+		      const char *msgtype)
+{
+	const u8 *f_seq;
+	size_t f_seq_len;
+	const struct ft_rrb_seq *msg_both;
+	u32 msg_seq, msg_off, min_off, rkh_off;
+	int minidx = 0;
+	unsigned int i;
+
+	RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both));
+	msg_both = (const struct ft_rrb_seq *) f_seq;
+
+	msg_seq = le_to_host32(msg_both->seq);
+
+	if (rkh_seq->rx.num_last < FT_REMOTE_SEQ_BACKLOG) {
+		rkh_seq->rx.last[rkh_seq->rx.num_last] = msg_seq;
+		rkh_seq->rx.num_last++;
+		return;
+	}
+
+	rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx];
+	for (i = 0; i < rkh_seq->rx.num_last; i++) {
+		msg_off = rkh_seq->rx.last[i] - rkh_off;
+		min_off = rkh_seq->rx.last[minidx] - rkh_off;
+		if (msg_off < min_off && i != rkh_seq->rx.offsetidx)
+			minidx = i;
+	}
+	rkh_seq->rx.last[rkh_seq->rx.offsetidx] = msg_seq;
+	rkh_seq->rx.offsetidx = minidx;
+
+	return;
+out:
+	/* RRB_GET_AUTH should never fail here as
+	 * wpa_ft_rrb_seq_chk() verified FT_RRB_SEQ presence. */
+	wpa_printf(MSG_ERROR, "FT: %s() failed", __func__);
+}
+
+
+static int wpa_ft_new_seq(struct ft_remote_seq *rkh_seq,
+			  struct ft_rrb_seq *f_seq)
+{
+	struct os_reltime now;
+
+	if (os_get_reltime(&now) < 0)
+		return -1;
+
+	if (!rkh_seq->tx.dom) {
+		if (random_get_bytes((u8 *) &rkh_seq->tx.seq,
+				     sizeof(rkh_seq->tx.seq))) {
+			wpa_printf(MSG_ERROR,
+				   "FT: Failed to get random data for sequence number initialization");
+			rkh_seq->tx.seq = now.usec;
+		}
+		if (random_get_bytes((u8 *) &rkh_seq->tx.dom,
+				     sizeof(rkh_seq->tx.dom))) {
+			wpa_printf(MSG_ERROR,
+				   "FT: Failed to get random data for sequence number initialization");
+			rkh_seq->tx.dom = now.usec;
+		}
+		rkh_seq->tx.dom |= 1;
+	}
+
+	f_seq->dom = host_to_le32(rkh_seq->tx.dom);
+	f_seq->seq = host_to_le32(rkh_seq->tx.seq);
+	f_seq->ts = host_to_le32(now.sec);
+
+	rkh_seq->tx.seq++;
+
+	return 0;
+}
+
+
 struct wpa_ft_pmk_r0_sa {
 	struct wpa_ft_pmk_r0_sa *next;
 	u8 pmk_r0[PMK_LEN];
@@ -212,9 +892,9 @@
 }
 
 
-static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
-			       const u8 *spa, const u8 *pmk_r0,
-			       const u8 *pmk_r0_name, int pairwise)
+int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
+			const u8 *spa, const u8 *pmk_r0,
+			const u8 *pmk_r0_name, int pairwise)
 {
 	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
 	struct wpa_ft_pmk_r0_sa *r0;
@@ -239,7 +919,7 @@
 
 static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth,
 			       const u8 *spa, const u8 *pmk_r0_name,
-			       u8 *pmk_r0, int *pairwise)
+			       const struct wpa_ft_pmk_r0_sa **r0_out)
 {
 	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
 	struct wpa_ft_pmk_r0_sa *r0;
@@ -249,15 +929,14 @@
 		if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 &&
 		    os_memcmp_const(r0->pmk_r0_name, pmk_r0_name,
 				    WPA_PMK_NAME_LEN) == 0) {
-			os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN);
-			if (pairwise)
-				*pairwise = r0->pairwise;
+			*r0_out = r0;
 			return 0;
 		}
 
 		r0 = r0->next;
 	}
 
+	*r0_out = NULL;
 	return -1;
 }
 
@@ -312,61 +991,522 @@
 }
 
 
+static int wpa_ft_rrb_init_r0kh_seq(struct ft_remote_r0kh *r0kh)
+{
+	if (r0kh->seq)
+		return 0;
+
+	r0kh->seq = os_zalloc(sizeof(*r0kh->seq));
+	if (!r0kh->seq) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to allocate r0kh->seq");
+		return -1;
+	}
+
+	dl_list_init(&r0kh->seq->rx.queue);
+
+	return 0;
+}
+
+
+static void wpa_ft_rrb_lookup_r0kh(struct wpa_authenticator *wpa_auth,
+				   const u8 *f_r0kh_id, size_t f_r0kh_id_len,
+				   struct ft_remote_r0kh **r0kh_out,
+				   struct ft_remote_r0kh **r0kh_wildcard)
+{
+	struct ft_remote_r0kh *r0kh;
+
+	*r0kh_wildcard = NULL;
+	*r0kh_out = NULL;
+
+	if (wpa_auth->conf.r0kh_list)
+		r0kh = *wpa_auth->conf.r0kh_list;
+	else
+		r0kh = NULL;
+	for (; r0kh; r0kh = r0kh->next) {
+		if (r0kh->id_len == 1 && r0kh->id[0] == '*')
+			*r0kh_wildcard = r0kh;
+		if (f_r0kh_id && r0kh->id_len == f_r0kh_id_len &&
+		    os_memcmp_const(f_r0kh_id, r0kh->id, f_r0kh_id_len) == 0)
+			*r0kh_out = r0kh;
+	}
+
+	if (!*r0kh_out && !*r0kh_wildcard)
+		wpa_printf(MSG_DEBUG, "FT: No matching R0KH found");
+
+	if (*r0kh_out && wpa_ft_rrb_init_r0kh_seq(*r0kh_out) < 0)
+		*r0kh_out = NULL;
+}
+
+
+static int wpa_ft_rrb_init_r1kh_seq(struct ft_remote_r1kh *r1kh)
+{
+	if (r1kh->seq)
+		return 0;
+
+	r1kh->seq = os_zalloc(sizeof(*r1kh->seq));
+	if (!r1kh->seq) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to allocate r1kh->seq");
+		return -1;
+	}
+
+	dl_list_init(&r1kh->seq->rx.queue);
+
+	return 0;
+}
+
+
+static void wpa_ft_rrb_lookup_r1kh(struct wpa_authenticator *wpa_auth,
+				   const u8 *f_r1kh_id,
+				   struct ft_remote_r1kh **r1kh_out,
+				   struct ft_remote_r1kh **r1kh_wildcard)
+{
+	struct ft_remote_r1kh *r1kh;
+
+	*r1kh_wildcard = NULL;
+	*r1kh_out = NULL;
+
+	if (wpa_auth->conf.r1kh_list)
+		r1kh = *wpa_auth->conf.r1kh_list;
+	else
+		r1kh = NULL;
+	for (; r1kh; r1kh = r1kh->next) {
+		if (is_zero_ether_addr(r1kh->addr) &&
+		    is_zero_ether_addr(r1kh->id))
+			*r1kh_wildcard = r1kh;
+		if (f_r1kh_id &&
+		    os_memcmp_const(r1kh->id, f_r1kh_id, FT_R1KH_ID_LEN) == 0)
+			*r1kh_out = r1kh;
+	}
+
+	if (!*r1kh_out && !*r1kh_wildcard)
+		wpa_printf(MSG_DEBUG, "FT: No matching R1KH found");
+
+	if (*r1kh_out && wpa_ft_rrb_init_r1kh_seq(*r1kh_out) < 0)
+		*r1kh_out = NULL;
+}
+
+
+static int wpa_ft_rrb_check_r0kh(struct wpa_authenticator *wpa_auth,
+				 const u8 *f_r0kh_id, size_t f_r0kh_id_len)
+{
+	if (f_r0kh_id_len != wpa_auth->conf.r0_key_holder_len ||
+	    os_memcmp_const(f_r0kh_id, wpa_auth->conf.r0_key_holder,
+			    f_r0kh_id_len) != 0)
+		return -1;
+
+	return 0;
+}
+
+
+static int wpa_ft_rrb_check_r1kh(struct wpa_authenticator *wpa_auth,
+				 const u8 *f_r1kh_id)
+{
+	if (os_memcmp_const(f_r1kh_id, wpa_auth->conf.r1_key_holder,
+			    FT_R1KH_ID_LEN) != 0)
+		return -1;
+
+	return 0;
+}
+
+
+static void wpa_ft_rrb_del_r0kh(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_authenticator *wpa_auth = eloop_ctx;
+	struct ft_remote_r0kh *r0kh, *prev = NULL;
+
+	if (!wpa_auth->conf.r0kh_list)
+		return;
+
+	for (r0kh = *wpa_auth->conf.r0kh_list; r0kh; r0kh = r0kh->next) {
+		if (r0kh == timeout_ctx)
+			break;
+		prev = r0kh;
+	}
+	if (!r0kh)
+		return;
+	if (prev)
+		prev->next = r0kh->next;
+	else
+		*wpa_auth->conf.r0kh_list = r0kh->next;
+	if (r0kh->seq)
+		wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0);
+	os_free(r0kh->seq);
+	os_free(r0kh);
+}
+
+
+static void wpa_ft_rrb_r0kh_replenish(struct wpa_authenticator *wpa_auth,
+				      struct ft_remote_r0kh *r0kh, int timeout)
+{
+	if (timeout > 0)
+		eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
+					wpa_auth, r0kh);
+}
+
+
+static void wpa_ft_rrb_r0kh_timeout(struct wpa_authenticator *wpa_auth,
+				    struct ft_remote_r0kh *r0kh, int timeout)
+{
+	eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth, r0kh);
+
+	if (timeout > 0)
+		eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
+				       wpa_auth, r0kh);
+}
+
+
+static struct ft_remote_r0kh *
+wpa_ft_rrb_add_r0kh(struct wpa_authenticator *wpa_auth,
+		    struct ft_remote_r0kh *r0kh_wildcard,
+		    const u8 *src_addr, const u8 *r0kh_id, size_t id_len,
+		    int timeout)
+{
+	struct ft_remote_r0kh *r0kh;
+
+	if (!wpa_auth->conf.r0kh_list)
+		return NULL;
+
+	r0kh = os_zalloc(sizeof(*r0kh));
+	if (!r0kh)
+		return NULL;
+
+	if (src_addr)
+		os_memcpy(r0kh->addr, src_addr, sizeof(r0kh->addr));
+
+	if (id_len > FT_R0KH_ID_MAX_LEN)
+		id_len = FT_R0KH_ID_MAX_LEN;
+	os_memcpy(r0kh->id, r0kh_id, id_len);
+	r0kh->id_len = id_len;
+
+	os_memcpy(r0kh->key, r0kh_wildcard->key, sizeof(r0kh->key));
+
+	r0kh->next = *wpa_auth->conf.r0kh_list;
+	*wpa_auth->conf.r0kh_list = r0kh;
+
+	if (timeout > 0)
+		eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
+				       wpa_auth, r0kh);
+
+	if (wpa_ft_rrb_init_r0kh_seq(r0kh) < 0)
+		return NULL;
+
+	return r0kh;
+}
+
+
+static void wpa_ft_rrb_del_r1kh(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_authenticator *wpa_auth = eloop_ctx;
+	struct ft_remote_r1kh *r1kh, *prev = NULL;
+
+	if (!wpa_auth->conf.r1kh_list)
+		return;
+
+	for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) {
+		if (r1kh == timeout_ctx)
+			break;
+		prev = r1kh;
+	}
+	if (!r1kh)
+		return;
+	if (prev)
+		prev->next = r1kh->next;
+	else
+		*wpa_auth->conf.r1kh_list = r1kh->next;
+	if (r1kh->seq)
+		wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0);
+	os_free(r1kh->seq);
+	os_free(r1kh);
+}
+
+
+static void wpa_ft_rrb_r1kh_replenish(struct wpa_authenticator *wpa_auth,
+				      struct ft_remote_r1kh *r1kh, int timeout)
+{
+	if (timeout > 0)
+		eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r1kh,
+					wpa_auth, r1kh);
+}
+
+
+static struct ft_remote_r1kh *
+wpa_ft_rrb_add_r1kh(struct wpa_authenticator *wpa_auth,
+		    struct ft_remote_r1kh *r1kh_wildcard,
+		    const u8 *src_addr, const u8 *r1kh_id, int timeout)
+{
+	struct ft_remote_r1kh *r1kh;
+
+	if (!wpa_auth->conf.r1kh_list)
+		return NULL;
+
+	r1kh = os_zalloc(sizeof(*r1kh));
+	if (!r1kh)
+		return NULL;
+
+	os_memcpy(r1kh->addr, src_addr, sizeof(r1kh->addr));
+	os_memcpy(r1kh->id, r1kh_id, sizeof(r1kh->id));
+	os_memcpy(r1kh->key, r1kh_wildcard->key, sizeof(r1kh->key));
+	r1kh->next = *wpa_auth->conf.r1kh_list;
+	*wpa_auth->conf.r1kh_list = r1kh;
+
+	if (timeout > 0)
+		eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r1kh,
+				       wpa_auth, r1kh);
+
+	if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0)
+		return NULL;
+
+	return r1kh;
+}
+
+
+void wpa_ft_sta_deinit(struct wpa_state_machine *sm)
+{
+	eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL);
+}
+
+
+static void wpa_ft_deinit_seq(struct wpa_authenticator *wpa_auth)
+{
+	struct ft_remote_r0kh *r0kh;
+	struct ft_remote_r1kh *r1kh;
+
+	eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, wpa_auth, ELOOP_ALL_CTX);
+
+	if (wpa_auth->conf.r0kh_list)
+		r0kh = *wpa_auth->conf.r0kh_list;
+	else
+		r0kh = NULL;
+	for (; r0kh; r0kh = r0kh->next) {
+		if (!r0kh->seq)
+			continue;
+		wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0);
+		os_free(r0kh->seq);
+		r0kh->seq = NULL;
+	}
+
+	if (wpa_auth->conf.r1kh_list)
+		r1kh = *wpa_auth->conf.r1kh_list;
+	else
+		r1kh = NULL;
+	for (; r1kh; r1kh = r1kh->next) {
+		if (!r1kh->seq)
+			continue;
+		wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0);
+		os_free(r1kh->seq);
+		r1kh->seq = NULL;
+	}
+}
+
+
+static void wpa_ft_deinit_rkh_tmp(struct wpa_authenticator *wpa_auth)
+{
+	struct ft_remote_r0kh *r0kh, *r0kh_next, *r0kh_prev = NULL;
+	struct ft_remote_r1kh *r1kh, *r1kh_next, *r1kh_prev = NULL;
+
+	if (wpa_auth->conf.r0kh_list)
+		r0kh = *wpa_auth->conf.r0kh_list;
+	else
+		r0kh = NULL;
+	while (r0kh) {
+		r0kh_next = r0kh->next;
+		if (eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth,
+					 r0kh) > 0) {
+			if (r0kh_prev)
+				r0kh_prev->next = r0kh_next;
+			else
+				*wpa_auth->conf.r0kh_list = r0kh_next;
+			os_free(r0kh);
+		} else {
+			r0kh_prev = r0kh;
+		}
+		r0kh = r0kh_next;
+	}
+
+	if (wpa_auth->conf.r1kh_list)
+		r1kh = *wpa_auth->conf.r1kh_list;
+	else
+		r1kh = NULL;
+	while (r1kh) {
+		r1kh_next = r1kh->next;
+		if (eloop_cancel_timeout(wpa_ft_rrb_del_r1kh, wpa_auth,
+					 r1kh) > 0) {
+			if (r1kh_prev)
+				r1kh_prev->next = r1kh_next;
+			else
+				*wpa_auth->conf.r1kh_list = r1kh_next;
+			os_free(r1kh);
+		} else {
+			r1kh_prev = r1kh;
+		}
+		r1kh = r1kh_next;
+	}
+}
+
+
+void wpa_ft_deinit(struct wpa_authenticator *wpa_auth)
+{
+	wpa_ft_deinit_seq(wpa_auth);
+	wpa_ft_deinit_rkh_tmp(wpa_auth);
+}
+
+
+static void wpa_ft_block_r0kh(struct wpa_authenticator *wpa_auth,
+			      const u8 *f_r0kh_id, size_t f_r0kh_id_len)
+{
+	struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
+
+	if (!wpa_auth->conf.rkh_neg_timeout)
+		return;
+
+	wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len,
+			       &r0kh, &r0kh_wildcard);
+
+	if (!r0kh_wildcard) {
+		/* r0kh removed after neg_timeout and might need re-adding */
+		return;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "FT: Blacklist R0KH-ID",
+		    f_r0kh_id, f_r0kh_id_len);
+
+	if (r0kh) {
+		wpa_ft_rrb_r0kh_timeout(wpa_auth, r0kh,
+					wpa_auth->conf.rkh_neg_timeout);
+		os_memset(r0kh->addr, 0, ETH_ALEN);
+	} else
+		wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, NULL, f_r0kh_id,
+				    f_r0kh_id_len,
+				    wpa_auth->conf.rkh_neg_timeout);
+}
+
+
+static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_state_machine *sm = eloop_ctx;
+
+	wpa_printf(MSG_DEBUG, "FT: Timeout pending pull request for " MACSTR,
+		   MAC2STR(sm->addr));
+	if (sm->ft_pending_pull_left_retries <= 0)
+		wpa_ft_block_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len);
+
+	/* cancel multiple timeouts */
+	eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL);
+	ft_finish_pull(sm);
+}
+
+
 static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
 			      const u8 *ies, size_t ies_len,
 			      const u8 *pmk_r0_name)
 {
-	struct ft_remote_r0kh *r0kh;
-	struct ft_r0kh_r1kh_pull_frame frame, f;
+	struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
+	u8 *packet = NULL;
+	const u8 *key, *f_r1kh_id = sm->wpa_auth->conf.r1_key_holder;
+	size_t packet_len, key_len;
+	struct ft_rrb_seq f_seq;
+	int tsecs, tusecs, first;
+	struct wpabuf *ft_pending_req_ies;
+	int r0kh_timeout;
+	struct tlv_list req_enc[] = {
+		{ .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN,
+		  .data = pmk_r0_name },
+		{ .type = FT_RRB_S1KH_ID, .len = ETH_ALEN,
+		  .data = sm->addr },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
+	struct tlv_list req_auth[] = {
+		{ .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN,
+		  .data = sm->ft_pending_pull_nonce },
+		{ .type = FT_RRB_SEQ, .len = sizeof(f_seq),
+		  .data = (u8 *) &f_seq },
+		{ .type = FT_RRB_R0KH_ID, .len = sm->r0kh_id_len,
+		  .data = sm->r0kh_id },
+		{ .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
+		  .data = f_r1kh_id },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
 
-	r0kh = sm->wpa_auth->conf.r0kh_list;
-	while (r0kh) {
-		if (r0kh->id_len == sm->r0kh_id_len &&
-		    os_memcmp_const(r0kh->id, sm->r0kh_id, sm->r0kh_id_len) ==
-		    0)
-			break;
-		r0kh = r0kh->next;
+	if (sm->ft_pending_pull_left_retries <= 0)
+		return -1;
+	first = sm->ft_pending_pull_left_retries ==
+		sm->wpa_auth->conf.rkh_pull_retries;
+	sm->ft_pending_pull_left_retries--;
+
+	wpa_ft_rrb_lookup_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len,
+			       &r0kh, &r0kh_wildcard);
+
+	/* Keep r0kh sufficiently long in the list for seq num check */
+	r0kh_timeout = sm->wpa_auth->conf.rkh_pull_timeout / 1000 +
+		1 + ftRRBseqTimeout;
+	if (r0kh) {
+		wpa_ft_rrb_r0kh_replenish(sm->wpa_auth, r0kh, r0kh_timeout);
+	} else if (r0kh_wildcard) {
+		wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID");
+		/* r0kh->addr: updated by SEQ_RESP and wpa_ft_expire_pull */
+		r0kh = wpa_ft_rrb_add_r0kh(sm->wpa_auth, r0kh_wildcard,
+					   r0kh_wildcard->addr,
+					   sm->r0kh_id, sm->r0kh_id_len,
+					   r0kh_timeout);
 	}
 	if (r0kh == NULL) {
 		wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
 			    sm->r0kh_id, sm->r0kh_id_len);
 		return -1;
 	}
+	if (is_zero_ether_addr(r0kh->addr)) {
+		wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID is blacklisted",
+			    sm->r0kh_id, sm->r0kh_id_len);
+		return -1;
+	}
+
+	key = r0kh->key;
+	key_len = sizeof(r0kh->key);
 
 	wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH "
 		   "address " MACSTR, MAC2STR(r0kh->addr));
 
-	os_memset(&frame, 0, sizeof(frame));
-	frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
-	frame.packet_type = FT_PACKET_R0KH_R1KH_PULL;
-	frame.data_length = host_to_le16(FT_R0KH_R1KH_PULL_DATA_LEN);
-	os_memcpy(frame.ap_address, sm->wpa_auth->addr, ETH_ALEN);
+	if (r0kh->seq->rx.num_last == 0) {
+		/* A sequence request will be sent out anyway when pull
+		 * response is received. Send it out now to avoid one RTT. */
+		wpa_ft_rrb_seq_req(sm->wpa_auth, r0kh->seq, r0kh->addr,
+				   r0kh->id, r0kh->id_len, f_r1kh_id, key,
+				   key_len, NULL, 0, NULL, 0, NULL);
+	}
 
-	/* aes_wrap() does not support inplace encryption, so use a temporary
-	 * buffer for the data. */
-	if (random_get_bytes(f.nonce, FT_R0KH_R1KH_PULL_NONCE_LEN)) {
+	if (first &&
+	    random_get_bytes(sm->ft_pending_pull_nonce, FT_RRB_NONCE_LEN) < 0) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
 			   "nonce");
 		return -1;
 	}
-	os_memcpy(sm->ft_pending_pull_nonce, f.nonce,
-		  FT_R0KH_R1KH_PULL_NONCE_LEN);
-	os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
-	os_memcpy(f.r1kh_id, sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
-	os_memcpy(f.s1kh_id, sm->addr, ETH_ALEN);
-	os_memset(f.pad, 0, sizeof(f.pad));
 
-	if (aes_wrap(r0kh->key, sizeof(r0kh->key),
-		     (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
-		     f.nonce, frame.nonce) < 0)
+	if (wpa_ft_new_seq(r0kh->seq, &f_seq) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+		return -1;
+	}
+
+	if (wpa_ft_rrb_build(key, key_len, req_enc, NULL, req_auth,
+			     sm->wpa_auth->addr, FT_PACKET_R0KH_R1KH_PULL,
+			     &packet, &packet_len) < 0)
 		return -1;
 
+	ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len);
 	wpabuf_free(sm->ft_pending_req_ies);
-	sm->ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len);
-	if (sm->ft_pending_req_ies == NULL)
+	sm->ft_pending_req_ies = ft_pending_req_ies;
+	if (!sm->ft_pending_req_ies) {
+		os_free(packet);
 		return -1;
+	}
 
-	wpa_ft_rrb_send(sm->wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame));
+	tsecs = sm->wpa_auth->conf.rkh_pull_timeout / 1000;
+	tusecs = (sm->wpa_auth->conf.rkh_pull_timeout % 1000) * 1000;
+	eloop_register_timeout(tsecs, tusecs, wpa_ft_expire_pull, sm, NULL);
+
+	wpa_ft_rrb_oui_send(sm->wpa_auth, r0kh->addr, FT_PACKET_R0KH_R1KH_PULL,
+			    packet, packet_len);
+
+	os_free(packet);
 
 	return 0;
 }
@@ -891,6 +2031,12 @@
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
 	else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
+#ifdef CONFIG_FILS
+	else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
+	else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384;
+#endif /* CONFIG_FILS */
 	ciphers = parse->pairwise_cipher & sm->wpa_auth->conf.rsn_pairwise;
 	if (!ciphers) {
 		wpa_printf(MSG_DEBUG, "FT: Invalid pairwise cipher (0x%x) from "
@@ -984,8 +2130,8 @@
 	} else if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name,
 				       pmk_r1, &pairwise) < 0) {
 		if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
-			wpa_printf(MSG_DEBUG, "FT: Did not have matching "
-				   "PMK-R1 and unknown R0KH-ID");
+			wpa_printf(MSG_DEBUG,
+				   "FT: Did not have matching PMK-R1 and either unknown or blocked R0KH-ID or NAK from R0KH");
 			return WLAN_STATUS_INVALID_PMKID;
 		}
 
@@ -1077,6 +2223,7 @@
 	sm->ft_pending_cb = cb;
 	sm->ft_pending_cb_ctx = ctx;
 	sm->ft_pending_auth_transaction = auth_transaction;
+	sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries;
 	res = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies,
 				      &resp_ies_len);
 	if (res < 0) {
@@ -1358,6 +2505,7 @@
 	sm->ft_pending_cb = wpa_ft_rrb_rx_request_cb;
 	sm->ft_pending_cb_ctx = sm;
 	os_memcpy(sm->ft_pending_current_ap, current_ap, ETH_ALEN);
+	sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries;
 	res = wpa_ft_process_auth_req(sm, body, len, &resp_ies,
 				      &resp_ies_len);
 	if (res < 0) {
@@ -1423,115 +2571,339 @@
 }
 
 
+static int wpa_ft_rrb_build_r0(const u8 *key, const size_t key_len,
+			       const struct tlv_list *tlvs,
+			       const struct wpa_ft_pmk_r0_sa *pmk_r0,
+			       const u8 *r1kh_id, const u8 *s1kh_id,
+			       const struct tlv_list *tlv_auth,
+			       const u8 *src_addr, u8 type,
+			       u8 **packet, size_t *packet_len)
+{
+	u8 pmk_r1[PMK_LEN];
+	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+	u8 f_pairwise[sizeof(le16)];
+	int ret;
+	struct tlv_list sess_tlv[] = {
+		{ .type = FT_RRB_PMK_R1, .len = sizeof(pmk_r1),
+		  .data = pmk_r1 },
+		{ .type = FT_RRB_PMK_R1_NAME, .len = sizeof(pmk_r1_name),
+		  .data = pmk_r1_name },
+		{ .type = FT_RRB_PAIRWISE, .len = sizeof(f_pairwise),
+		  .data = f_pairwise },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
+
+	if (!pmk_r0)
+		return wpa_ft_rrb_build(key, key_len, tlvs, NULL, tlv_auth,
+					src_addr, type, packet, packet_len);
+
+	if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh_id,
+			      s1kh_id, pmk_r1, pmk_r1_name) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
+	WPA_PUT_LE16(f_pairwise, pmk_r0->pairwise);
+
+	ret = wpa_ft_rrb_build(key, key_len, tlvs, sess_tlv, tlv_auth,
+			       src_addr, type, packet, packet_len);
+
+	os_memset(pmk_r1, 0, sizeof(pmk_r1));
+
+	return ret;
+}
+
+
 static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
 			      const u8 *src_addr,
-			      const u8 *data, size_t data_len)
+			      const u8 *enc, size_t enc_len,
+			      const u8 *auth, size_t auth_len,
+			      int no_defer)
 {
-	struct ft_r0kh_r1kh_pull_frame f;
-	const u8 *crypt;
-	u8 *plain;
-	struct ft_remote_r1kh *r1kh;
-	struct ft_r0kh_r1kh_resp_frame resp, r;
-	u8 pmk_r0[PMK_LEN];
-	int pairwise;
+	const char *msgtype = "pull request";
+	u8 *plain = NULL, *packet = NULL;
+	size_t plain_len = 0, packet_len = 0;
+	struct ft_remote_r1kh *r1kh, *r1kh_wildcard;
+	const u8 *key;
+	size_t key_len;
+	int seq_ret;
+	const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id, *f_s1kh_id, *f_pmk_r0_name;
+	size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len, f_s1kh_id_len;
+	size_t f_pmk_r0_name_len;
+	const struct wpa_ft_pmk_r0_sa *r0;
+	int ret;
+	struct tlv_list resp[2];
+	struct tlv_list resp_auth[5];
+	struct ft_rrb_seq f_seq;
 
 	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull");
 
-	if (data_len < sizeof(f))
-		return -1;
+	RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1);
+	wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len);
 
-	r1kh = wpa_auth->conf.r1kh_list;
-	while (r1kh) {
-		if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0)
-			break;
-		r1kh = r1kh->next;
-	}
-	if (r1kh == NULL) {
-		wpa_printf(MSG_DEBUG, "FT: No matching R1KH address found for "
-			   "PMK-R1 pull source address " MACSTR,
-			   MAC2STR(src_addr));
-		return -1;
+	if (wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len)) {
+		wpa_printf(MSG_DEBUG, "FT: R0KH-ID mismatch");
+		goto out;
 	}
 
-	crypt = data + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce);
-	os_memset(&f, 0, sizeof(f));
-	plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce);
-	/* aes_unwrap() does not support inplace decryption, so use a temporary
-	 * buffer for the data. */
-	if (aes_unwrap(r1kh->key, sizeof(r1kh->key),
-		       (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
-		       crypt, plain) < 0) {
-		wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
-			   "request from " MACSTR, MAC2STR(src_addr));
-		return -1;
+	RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN);
+	wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id));
+
+	wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh, &r1kh_wildcard);
+	if (r1kh) {
+		key = r1kh->key;
+		key_len = sizeof(r1kh->key);
+	} else if (r1kh_wildcard) {
+		wpa_printf(MSG_DEBUG, "FT: Using wildcard R1KH-ID");
+		key = r1kh_wildcard->key;
+		key_len = sizeof(r1kh_wildcard->key);
+	} else {
+		goto out;
 	}
 
-	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce",
-		    f.nonce, sizeof(f.nonce));
-	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name",
-		    f.pmk_r0_name, WPA_PMK_NAME_LEN);
-	wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID="
-		   MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id));
+	RRB_GET_AUTH(FT_RRB_NONCE, nonce, "pull request", FT_RRB_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len);
 
-	os_memset(&resp, 0, sizeof(resp));
-	resp.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
-	resp.packet_type = FT_PACKET_R0KH_R1KH_RESP;
-	resp.data_length = host_to_le16(FT_R0KH_R1KH_RESP_DATA_LEN);
-	os_memcpy(resp.ap_address, wpa_auth->addr, ETH_ALEN);
-
-	/* aes_wrap() does not support inplace encryption, so use a temporary
-	 * buffer for the data. */
-	os_memcpy(r.nonce, f.nonce, sizeof(f.nonce));
-	os_memcpy(r.r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN);
-	os_memcpy(r.s1kh_id, f.s1kh_id, ETH_ALEN);
-	if (wpa_ft_fetch_pmk_r0(wpa_auth, f.s1kh_id, f.pmk_r0_name, pmk_r0,
-				&pairwise) < 0) {
-		wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name found for "
-			   "PMK-R1 pull");
-		return -1;
+	seq_ret = FT_RRB_SEQ_DROP;
+	if (r1kh)
+		seq_ret = wpa_ft_rrb_seq_chk(r1kh->seq, src_addr, enc, enc_len,
+					     auth, auth_len, msgtype, no_defer);
+	if (!no_defer && r1kh_wildcard &&
+	    (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) {
+		/* wildcard: r1kh-id unknown or changed addr -> do a seq req */
+		seq_ret = FT_RRB_SEQ_DEFER;
 	}
 
-	if (wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id,
-			      r.pmk_r1, r.pmk_r1_name) < 0) {
-		os_memset(pmk_r0, 0, PMK_LEN);
-		return -1;
-	}
-	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", r.pmk_r1, PMK_LEN);
-	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name,
-		    WPA_PMK_NAME_LEN);
-	r.pairwise = host_to_le16(pairwise);
-	os_memset(r.pad, 0, sizeof(r.pad));
+	if (seq_ret == FT_RRB_SEQ_DROP)
+		goto out;
 
-	if (aes_wrap(r1kh->key, sizeof(r1kh->key),
-		     (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
-		     r.nonce, resp.nonce) < 0) {
-		os_memset(pmk_r0, 0, PMK_LEN);
-		return -1;
+	if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len,
+			       src_addr, FT_PACKET_R0KH_R1KH_PULL,
+			       &plain, &plain_len) < 0)
+		goto out;
+
+	if (!r1kh)
+		r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard, src_addr,
+					   f_r1kh_id,
+					   wpa_auth->conf.rkh_pos_timeout);
+	if (!r1kh)
+		goto out;
+
+	if (seq_ret == FT_RRB_SEQ_DEFER) {
+		wpa_ft_rrb_seq_req(wpa_auth, r1kh->seq, src_addr, f_r0kh_id,
+				   f_r0kh_id_len, f_r1kh_id, key, key_len,
+				   enc, enc_len, auth, auth_len,
+				   &wpa_ft_rrb_rx_pull);
+		goto out;
 	}
 
-	os_memset(pmk_r0, 0, PMK_LEN);
+	wpa_ft_rrb_seq_accept(wpa_auth, r1kh->seq, src_addr, auth, auth_len,
+			      msgtype);
+	wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh,
+				  wpa_auth->conf.rkh_pos_timeout);
 
-	wpa_ft_rrb_send(wpa_auth, src_addr, (u8 *) &resp, sizeof(resp));
+	RRB_GET(FT_RRB_PMK_R0_NAME, pmk_r0_name, msgtype, WPA_PMK_NAME_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", f_pmk_r0_name,
+		    f_pmk_r0_name_len);
+
+	RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id));
+
+	if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+		goto out;
+	}
+
+	resp[0].type = FT_RRB_S1KH_ID;
+	resp[0].len = f_s1kh_id_len;
+	resp[0].data = f_s1kh_id;
+	resp[1].type = FT_RRB_LAST_EMPTY;
+	resp[1].len = 0;
+	resp[1].data = NULL;
+
+	resp_auth[0].type = FT_RRB_NONCE;
+	resp_auth[0].len = f_nonce_len;
+	resp_auth[0].data = f_nonce;
+	resp_auth[1].type = FT_RRB_SEQ;
+	resp_auth[1].len = sizeof(f_seq);
+	resp_auth[1].data = (u8 *) &f_seq;
+	resp_auth[2].type = FT_RRB_R0KH_ID;
+	resp_auth[2].len = f_r0kh_id_len;
+	resp_auth[2].data = f_r0kh_id;
+	resp_auth[3].type = FT_RRB_R1KH_ID;
+	resp_auth[3].len = f_r1kh_id_len;
+	resp_auth[3].data = f_r1kh_id;
+	resp_auth[4].type = FT_RRB_LAST_EMPTY;
+	resp_auth[4].len = 0;
+	resp_auth[4].data = NULL;
+
+	if (wpa_ft_fetch_pmk_r0(wpa_auth, f_s1kh_id, f_pmk_r0_name, &r0) < 0)
+		wpa_printf(MSG_DEBUG, "FT: No matching PMK-R0-Name found");
+
+	ret = wpa_ft_rrb_build_r0(key, key_len, resp, r0, f_r1kh_id, f_s1kh_id,
+				  resp_auth, wpa_auth->addr,
+				  FT_PACKET_R0KH_R1KH_RESP,
+				  &packet, &packet_len);
+
+	if (!ret)
+		wpa_ft_rrb_oui_send(wpa_auth, src_addr,
+				    FT_PACKET_R0KH_R1KH_RESP, packet,
+				    packet_len);
+
+out:
+	os_free(plain);
+	os_free(packet);
 
 	return 0;
 }
 
 
-static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx)
+/* @returns  0 on success
+ *          -1 on error
+ *          -2 if FR_RRB_PAIRWISE is missing
+ */
+static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth,
+			    const u8 *src_addr, u8 type,
+			    const u8 *enc, size_t enc_len,
+			    const u8 *auth, size_t auth_len,
+			    const char *msgtype, u8 *s1kh_id_out,
+			    int (*cb)(struct wpa_authenticator *wpa_auth,
+				      const u8 *src_addr,
+				      const u8 *enc, size_t enc_len,
+				      const u8 *auth, size_t auth_len,
+				      int no_defer))
 {
-	struct wpa_state_machine *sm = eloop_ctx;
+	u8 *plain = NULL;
+	size_t plain_len = 0;
+	struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
+	const u8 *key;
+	size_t key_len;
+	int seq_ret;
+	const u8 *f_r1kh_id, *f_s1kh_id, *f_r0kh_id;
+	const u8 *f_pmk_r1_name, *f_pairwise, *f_pmk_r1;
+	size_t f_r1kh_id_len, f_s1kh_id_len, f_r0kh_id_len;
+	size_t f_pmk_r1_name_len, f_pairwise_len, f_pmk_r1_len;
+	int pairwise;
+	int ret = -1;
+
+	RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1);
+	wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len);
+
+	RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN);
+	wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id));
+
+	if (wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id)) {
+		wpa_printf(MSG_DEBUG, "FT: R1KH-ID mismatch");
+		goto out;
+	}
+
+	wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, &r0kh,
+			       &r0kh_wildcard);
+	if (r0kh) {
+		key = r0kh->key;
+		key_len = sizeof(r0kh->key);
+	} else if (r0kh_wildcard) {
+		wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID");
+		key = r0kh_wildcard->key;
+		key_len = sizeof(r0kh_wildcard->key);
+	} else {
+		goto out;
+	}
+
+	seq_ret = FT_RRB_SEQ_DROP;
+	if (r0kh) {
+		seq_ret = wpa_ft_rrb_seq_chk(r0kh->seq, src_addr, enc, enc_len,
+					     auth, auth_len, msgtype,
+					     cb ? 0 : 1);
+	}
+	if (cb && r0kh_wildcard &&
+	    (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) {
+		/* wildcard: r0kh-id unknown or changed addr -> do a seq req */
+		seq_ret = FT_RRB_SEQ_DEFER;
+	}
+
+	if (seq_ret == FT_RRB_SEQ_DROP)
+		goto out;
+
+	if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len,
+			       src_addr, type, &plain, &plain_len) < 0)
+		goto out;
+
+	if (!r0kh)
+		r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, src_addr,
+					   f_r0kh_id, f_r0kh_id_len,
+					   wpa_auth->conf.rkh_pos_timeout);
+	if (!r0kh)
+		goto out;
+
+	if (seq_ret == FT_RRB_SEQ_DEFER) {
+		wpa_ft_rrb_seq_req(wpa_auth, r0kh->seq, src_addr, f_r0kh_id,
+				   f_r0kh_id_len, f_r1kh_id, key, key_len,
+				   enc, enc_len, auth, auth_len, cb);
+		goto out;
+	}
+
+	wpa_ft_rrb_seq_accept(wpa_auth, r0kh->seq, src_addr, auth, auth_len,
+			      msgtype);
+	wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh,
+				  wpa_auth->conf.rkh_pos_timeout);
+
+	RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id));
+
+	if (s1kh_id_out)
+		os_memcpy(s1kh_id_out, f_s1kh_id, ETH_ALEN);
+
+	ret = -2;
+	RRB_GET(FT_RRB_PAIRWISE, pairwise, msgtype, sizeof(le16));
+	wpa_hexdump(MSG_DEBUG, "FT: pairwise", f_pairwise, f_pairwise_len);
+
+	ret = -1;
+	RRB_GET(FT_RRB_PMK_R1_NAME, pmk_r1_name, msgtype, WPA_PMK_NAME_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name",
+		    f_pmk_r1_name, WPA_PMK_NAME_LEN);
+
+	RRB_GET(FT_RRB_PMK_R1, pmk_r1, msgtype, PMK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f_pmk_r1, PMK_LEN);
+
+	pairwise = WPA_GET_LE16(f_pairwise);
+
+	if (wpa_ft_store_pmk_r1(wpa_auth, f_s1kh_id, f_pmk_r1, f_pmk_r1_name,
+				pairwise) < 0)
+		goto out;
+
+	ret = 0;
+out:
+	if (plain) {
+		os_memset(plain, 0, plain_len);
+		os_free(plain);
+	}
+
+	return ret;
+
+}
+
+
+static void ft_finish_pull(struct wpa_state_machine *sm)
+{
 	int res;
 	u8 *resp_ies;
 	size_t resp_ies_len;
 	u16 status;
 
+	if (!sm->ft_pending_cb || !sm->ft_pending_req_ies)
+		return;
+
 	res = wpa_ft_process_auth_req(sm, wpabuf_head(sm->ft_pending_req_ies),
 				      wpabuf_len(sm->ft_pending_req_ies),
 				      &resp_ies, &resp_ies_len);
+	if (res < 0) {
+		/* this loop is broken by ft_pending_pull_left_retries */
+		wpa_printf(MSG_DEBUG,
+			   "FT: Callback postponed until response is available");
+		return;
+	}
 	wpabuf_free(sm->ft_pending_req_ies);
 	sm->ft_pending_req_ies = NULL;
-	if (res < 0)
-		res = WLAN_STATUS_UNSPECIFIED_FAILURE;
 	status = res;
 	wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR
 		   " - status %u", MAC2STR(sm->addr), status);
@@ -1543,172 +2915,386 @@
 }
 
 
-static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx)
-{
-	struct ft_r0kh_r1kh_resp_frame *frame = ctx;
+struct ft_get_sta_ctx {
+	const u8 *nonce;
+	const u8 *s1kh_id;
+	struct wpa_state_machine *sm;
+};
 
-	if (os_memcmp(frame->s1kh_id, sm->addr, ETH_ALEN) != 0 ||
-	    os_memcmp(frame->nonce, sm->ft_pending_pull_nonce,
-		      FT_R0KH_R1KH_PULL_NONCE_LEN) != 0 ||
+
+static int ft_get_sta_cb(struct wpa_state_machine *sm, void *ctx)
+{
+	struct ft_get_sta_ctx *info = ctx;
+
+	if ((info->s1kh_id &&
+	     os_memcmp(info->s1kh_id, sm->addr, ETH_ALEN) != 0) ||
+	    os_memcmp(info->nonce, sm->ft_pending_pull_nonce,
+		      FT_RRB_NONCE_LEN) != 0 ||
 	    sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
 		return 0;
 
-	wpa_printf(MSG_DEBUG, "FT: Response to a pending pull request for "
-		   MACSTR " - process from timeout", MAC2STR(sm->addr));
-	eloop_register_timeout(0, 0, ft_pull_resp_cb_finish, sm, NULL);
+	info->sm = sm;
+
 	return 1;
 }
 
 
 static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
 			      const u8 *src_addr,
-			      const u8 *data, size_t data_len)
+			      const u8 *enc, size_t enc_len,
+			      const u8 *auth, size_t auth_len,
+			      int no_defer)
 {
-	struct ft_r0kh_r1kh_resp_frame f;
-	const u8 *crypt;
-	u8 *plain;
-	struct ft_remote_r0kh *r0kh;
-	int pairwise, res;
+	const char *msgtype = "pull response";
+	int nak, ret = -1;
+	struct ft_get_sta_ctx ctx;
+	u8 s1kh_id[ETH_ALEN];
+	const u8 *f_nonce;
+	size_t f_nonce_len;
 
 	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response");
 
-	if (data_len < sizeof(f))
-		return -1;
+	RRB_GET_AUTH(FT_RRB_NONCE, nonce, msgtype, FT_RRB_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len);
 
-	r0kh = wpa_auth->conf.r0kh_list;
-	while (r0kh) {
-		if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0)
-			break;
-		r0kh = r0kh->next;
-	}
-	if (r0kh == NULL) {
-		wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for "
-			   "PMK-R0 pull response source address " MACSTR,
-			   MAC2STR(src_addr));
+	os_memset(&ctx, 0, sizeof(ctx));
+	ctx.nonce = f_nonce;
+	if (!wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) {
+		/* nonce not found */
+		wpa_printf(MSG_DEBUG, "FT: Invalid nonce");
 		return -1;
 	}
 
-	crypt = data + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce);
-	os_memset(&f, 0, sizeof(f));
-	plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce);
-	/* aes_unwrap() does not support inplace decryption, so use a temporary
-	 * buffer for the data. */
-	if (aes_unwrap(r0kh->key, sizeof(r0kh->key),
-		       (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
-		       crypt, plain) < 0) {
-		wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
-			   "response from " MACSTR, MAC2STR(src_addr));
+	ret = wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_RESP,
+			       enc, enc_len, auth, auth_len, msgtype, s1kh_id,
+			       no_defer ? NULL : &wpa_ft_rrb_rx_resp);
+	if (ret == -2) {
+		ret = 0;
+		nak = 1;
+	} else {
+		nak = 0;
+	}
+	if (ret < 0)
 		return -1;
+
+	ctx.s1kh_id = s1kh_id;
+	if (wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: Response to a pending pull request for " MACSTR,
+			   MAC2STR(ctx.sm->addr));
+		eloop_cancel_timeout(wpa_ft_expire_pull, ctx.sm, NULL);
+		if (nak)
+			ctx.sm->ft_pending_pull_left_retries = 0;
+		ft_finish_pull(ctx.sm);
 	}
 
-	if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder,
-			    FT_R1KH_ID_LEN) != 0) {
-		wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a "
-			   "matching R1KH-ID");
-		return -1;
-	}
-
-	pairwise = le_to_host16(f.pairwise);
-	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce",
-		    f.nonce, sizeof(f.nonce));
-	wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID="
-		   MACSTR " pairwise=0x%x",
-		   MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise);
-	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1",
-			f.pmk_r1, PMK_LEN);
-	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name",
-			f.pmk_r1_name, WPA_PMK_NAME_LEN);
-
-	res = wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
-				  pairwise);
-	wpa_printf(MSG_DEBUG, "FT: Look for pending pull request");
-	wpa_auth_for_each_sta(wpa_auth, ft_pull_resp_cb, &f);
-	os_memset(f.pmk_r1, 0, PMK_LEN);
-
-	return res ? 0 : -1;
+out:
+	return ret;
 }
 
 
 static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
 			      const u8 *src_addr,
-			      const u8 *data, size_t data_len)
+			      const u8 *enc, size_t enc_len,
+			      const u8 *auth, size_t auth_len, int no_defer)
 {
-	struct ft_r0kh_r1kh_push_frame f;
-	const u8 *crypt;
-	u8 *plain;
-	struct ft_remote_r0kh *r0kh;
-	struct os_time now;
-	os_time_t tsend;
-	int pairwise;
+	const char *msgtype = "push";
 
 	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push");
 
-	if (data_len < sizeof(f))
+	if (wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_PUSH,
+			     enc, enc_len, auth, auth_len, msgtype, NULL,
+			     no_defer ? NULL : wpa_ft_rrb_rx_push) < 0)
 		return -1;
 
-	r0kh = wpa_auth->conf.r0kh_list;
-	while (r0kh) {
-		if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0)
-			break;
-		r0kh = r0kh->next;
-	}
-	if (r0kh == NULL) {
-		wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for "
-			   "PMK-R0 push source address " MACSTR,
-			   MAC2STR(src_addr));
-		return -1;
-	}
-
-	crypt = data + offsetof(struct ft_r0kh_r1kh_push_frame, timestamp);
-	os_memset(&f, 0, sizeof(f));
-	plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame,
-				       timestamp);
-	/* aes_unwrap() does not support inplace decryption, so use a temporary
-	 * buffer for the data. */
-	if (aes_unwrap(r0kh->key, sizeof(r0kh->key),
-		       (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
-		       crypt, plain) < 0) {
-		wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from "
-			   MACSTR, MAC2STR(src_addr));
-		return -1;
-	}
-
-	os_get_time(&now);
-	tsend = WPA_GET_LE32(f.timestamp);
-	if ((now.sec > tsend && now.sec - tsend > 60) ||
-	    (now.sec < tsend && tsend - now.sec > 60)) {
-		wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not have a valid "
-			   "timestamp: sender time %d own time %d\n",
-			   (int) tsend, (int) now.sec);
-		return -1;
-	}
-
-	if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder,
-			    FT_R1KH_ID_LEN) != 0) {
-		wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching "
-			   "R1KH-ID (received " MACSTR " own " MACSTR ")",
-			   MAC2STR(f.r1kh_id),
-			   MAC2STR(wpa_auth->conf.r1_key_holder));
-		return -1;
-	}
-
-	pairwise = le_to_host16(f.pairwise);
-	wpa_printf(MSG_DEBUG, "FT: PMK-R1 push - R1KH-ID=" MACSTR " S1KH-ID="
-		   MACSTR " pairwise=0x%x",
-		   MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise);
-	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 push - PMK-R1",
-			f.pmk_r1, PMK_LEN);
-	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 push - PMKR1Name",
-			f.pmk_r1_name, WPA_PMK_NAME_LEN);
-
-	wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
-			    pairwise);
-	os_memset(f.pmk_r1, 0, PMK_LEN);
-
 	return 0;
 }
 
 
+static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth,
+			     const u8 *src_addr, int type,
+			     const u8 *enc, size_t enc_len,
+			     const u8 *auth, size_t auth_len,
+			     struct ft_remote_seq **rkh_seq,
+			     u8 **key, size_t *key_len,
+			     struct ft_remote_r0kh **r0kh_out,
+			     struct ft_remote_r1kh **r1kh_out,
+			     struct ft_remote_r0kh **r0kh_wildcard_out,
+			     struct ft_remote_r1kh **r1kh_wildcard_out)
+{
+	struct ft_remote_r0kh *r0kh = NULL;
+	struct ft_remote_r1kh *r1kh = NULL;
+	const u8 *f_r0kh_id, *f_r1kh_id;
+	size_t f_r0kh_id_len, f_r1kh_id_len;
+	int to_r0kh, to_r1kh;
+	u8 *plain = NULL;
+	size_t plain_len = 0;
+	struct ft_remote_r0kh *r0kh_wildcard;
+	struct ft_remote_r1kh *r1kh_wildcard;
+
+	RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1);
+	RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN);
+
+	to_r0kh = !wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len);
+	to_r1kh = !wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id);
+
+	if (to_r0kh && to_r1kh) {
+		wpa_printf(MSG_DEBUG, "FT: seq - local R0KH-ID and R1KH-ID");
+		goto out;
+	}
+
+	if (!to_r0kh && !to_r1kh) {
+		wpa_printf(MSG_DEBUG, "FT: seq - remote R0KH-ID and R1KH-ID");
+		goto out;
+	}
+
+	if (!to_r0kh) {
+		wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len,
+				       &r0kh, &r0kh_wildcard);
+		if (!r0kh_wildcard &&
+		    (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) {
+			wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
+				    f_r0kh_id, f_r0kh_id_len);
+			goto out;
+		}
+		if (r0kh) {
+			*key = r0kh->key;
+			*key_len = sizeof(r0kh->key);
+		} else {
+			*key = r0kh_wildcard->key;
+			*key_len = sizeof(r0kh_wildcard->key);
+		}
+	}
+
+	if (!to_r1kh) {
+		wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh,
+				       &r1kh_wildcard);
+		if (!r1kh_wildcard &&
+		    (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) {
+			wpa_hexdump(MSG_DEBUG, "FT: Did not find R1KH-ID",
+				    f_r1kh_id, FT_R1KH_ID_LEN);
+			goto out;
+		}
+		if (r1kh) {
+			*key = r1kh->key;
+			*key_len = sizeof(r1kh->key);
+		} else {
+			*key = r1kh_wildcard->key;
+			*key_len = sizeof(r1kh_wildcard->key);
+		}
+	}
+
+	if (wpa_ft_rrb_decrypt(*key, *key_len, enc, enc_len, auth, auth_len,
+			       src_addr, type, &plain, &plain_len) < 0)
+		goto out;
+
+	os_free(plain);
+
+	if (!to_r0kh) {
+		if (!r0kh)
+			r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard,
+						   src_addr, f_r0kh_id,
+						   f_r0kh_id_len,
+						   ftRRBseqTimeout);
+		if (!r0kh)
+			goto out;
+
+		wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, ftRRBseqTimeout);
+		*rkh_seq = r0kh->seq;
+		if (r0kh_out)
+			*r0kh_out = r0kh;
+		if (r0kh_wildcard_out)
+			*r0kh_wildcard_out = r0kh_wildcard;
+	}
+
+	if (!to_r1kh) {
+		if (!r1kh)
+			r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard,
+						   src_addr, f_r1kh_id,
+						   ftRRBseqTimeout);
+		if (!r1kh)
+			goto out;
+
+		wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, ftRRBseqTimeout);
+		*rkh_seq = r1kh->seq;
+		if (r1kh_out)
+			*r1kh_out = r1kh;
+		if (r1kh_wildcard_out)
+			*r1kh_wildcard_out = r1kh_wildcard;
+	}
+
+	return 0;
+out:
+	return -1;
+}
+
+
+static int wpa_ft_rrb_rx_seq_req(struct wpa_authenticator *wpa_auth,
+				 const u8 *src_addr,
+				 const u8 *enc, size_t enc_len,
+				 const u8 *auth, size_t auth_len,
+				 int no_defer)
+{
+	int ret = -1;
+	struct ft_rrb_seq f_seq;
+	const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id;
+	size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len;
+	struct ft_remote_seq *rkh_seq = NULL;
+	u8 *packet = NULL, *key = NULL;
+	size_t packet_len = 0, key_len = 0;
+	struct tlv_list seq_resp_auth[5];
+
+	wpa_printf(MSG_DEBUG, "FT: Received sequence number request");
+
+	if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
+			      enc, enc_len, auth, auth_len, &rkh_seq, &key,
+			      &key_len, NULL, NULL, NULL, NULL) < 0)
+		goto out;
+
+	RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq request", FT_RRB_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: seq request - nonce", f_nonce, f_nonce_len);
+
+	RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1);
+	RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN);
+
+	if (wpa_ft_new_seq(rkh_seq, &f_seq) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+		goto out;
+	}
+
+	seq_resp_auth[0].type = FT_RRB_NONCE;
+	seq_resp_auth[0].len = f_nonce_len;
+	seq_resp_auth[0].data = f_nonce;
+	seq_resp_auth[1].type = FT_RRB_SEQ;
+	seq_resp_auth[1].len = sizeof(f_seq);
+	seq_resp_auth[1].data = (u8 *) &f_seq;
+	seq_resp_auth[2].type = FT_RRB_R0KH_ID;
+	seq_resp_auth[2].len = f_r0kh_id_len;
+	seq_resp_auth[2].data = f_r0kh_id;
+	seq_resp_auth[3].type = FT_RRB_R1KH_ID;
+	seq_resp_auth[3].len = FT_R1KH_ID_LEN;
+	seq_resp_auth[3].data = f_r1kh_id;
+	seq_resp_auth[4].type = FT_RRB_LAST_EMPTY;
+	seq_resp_auth[4].len = 0;
+	seq_resp_auth[4].data = NULL;
+
+	if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_resp_auth,
+			     wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_RESP,
+			     &packet, &packet_len) < 0)
+		goto out;
+
+	wpa_ft_rrb_oui_send(wpa_auth, src_addr,
+			    FT_PACKET_R0KH_R1KH_SEQ_RESP, packet,
+			    packet_len);
+
+out:
+	os_free(packet);
+
+	return ret;
+}
+
+
+static int wpa_ft_rrb_rx_seq_resp(struct wpa_authenticator *wpa_auth,
+				  const u8 *src_addr,
+				  const u8 *enc, size_t enc_len,
+				  const u8 *auth, size_t auth_len,
+				  int no_defer)
+{
+	u8 *key = NULL;
+	size_t key_len = 0;
+	struct ft_remote_r0kh *r0kh = NULL, *r0kh_wildcard = NULL;
+	struct ft_remote_r1kh *r1kh = NULL, *r1kh_wildcard = NULL;
+	const u8 *f_nonce, *f_seq;
+	size_t f_nonce_len, f_seq_len;
+	struct ft_remote_seq *rkh_seq = NULL;
+	struct ft_remote_item *item;
+	struct os_reltime now, now_remote;
+	int seq_ret, found;
+	const struct ft_rrb_seq *msg_both;
+	u32 msg_dom, msg_seq;
+
+	wpa_printf(MSG_DEBUG, "FT: Received sequence number response");
+
+	if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_RESP,
+			      enc, enc_len, auth, auth_len, &rkh_seq, &key,
+			      &key_len, &r0kh, &r1kh, &r0kh_wildcard,
+			      &r1kh_wildcard) < 0)
+		goto out;
+
+	RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq response", FT_RRB_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: seq response - nonce", f_nonce,
+		    f_nonce_len);
+
+	found = 0;
+	dl_list_for_each(item, &rkh_seq->rx.queue, struct ft_remote_item,
+			 list) {
+		if (os_memcmp_const(f_nonce, item->nonce,
+				    FT_RRB_NONCE_LEN) != 0 ||
+		    os_get_reltime(&now) < 0 ||
+		    os_reltime_expired(&now, &item->nonce_ts, ftRRBseqTimeout))
+			continue;
+
+		found = 1;
+		break;
+	}
+	if (!found) {
+		wpa_printf(MSG_DEBUG, "FT: seq response - bad nonce");
+		goto out;
+	}
+
+	if (r0kh) {
+		wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh,
+					  wpa_auth->conf.rkh_pos_timeout);
+		if (r0kh_wildcard)
+			os_memcpy(r0kh->addr, src_addr, ETH_ALEN);
+	}
+
+	if (r1kh) {
+		wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh,
+					  wpa_auth->conf.rkh_pos_timeout);
+		if (r1kh_wildcard)
+			os_memcpy(r1kh->addr, src_addr, ETH_ALEN);
+	}
+
+	seq_ret = wpa_ft_rrb_seq_chk(rkh_seq, src_addr, enc, enc_len, auth,
+				     auth_len, "seq response", 1);
+	if (seq_ret == FT_RRB_SEQ_OK) {
+		wpa_printf(MSG_DEBUG, "FT: seq response - valid seq number");
+		wpa_ft_rrb_seq_accept(wpa_auth, rkh_seq, src_addr, auth,
+				      auth_len, "seq response");
+	} else {
+		wpa_printf(MSG_DEBUG, "FT: seq response - reset seq number");
+
+		RRB_GET_AUTH(FT_RRB_SEQ, seq, "seq response",
+			     sizeof(*msg_both));
+		msg_both = (const struct ft_rrb_seq *) f_seq;
+
+		msg_dom = le_to_host32(msg_both->dom);
+		msg_seq = le_to_host32(msg_both->seq);
+		now_remote.sec = le_to_host32(msg_both->ts);
+		now_remote.usec = 0;
+
+		rkh_seq->rx.num_last = 2;
+		rkh_seq->rx.dom = msg_dom;
+		rkh_seq->rx.offsetidx = 0;
+		/* Accept some older, possibly cached packets as well */
+		rkh_seq->rx.last[0] = msg_seq - FT_REMOTE_SEQ_BACKLOG -
+			dl_list_len(&rkh_seq->rx.queue);
+		rkh_seq->rx.last[1] = msg_seq;
+
+		/* local time - offset = remote time
+		 * <=> local time - remote time = offset */
+		os_reltime_sub(&now, &now_remote, &rkh_seq->rx.time_offset);
+	}
+
+	wpa_ft_rrb_seq_flush(wpa_auth, rkh_seq, 1);
+
+	return 0;
+out:
+	return -1;
+}
+
+
 int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
 		  const u8 *data, size_t data_len)
 {
@@ -1750,13 +3336,6 @@
 		return -1;
 	}
 
-	if (frame->packet_type == FT_PACKET_R0KH_R1KH_PULL)
-		return wpa_ft_rrb_rx_pull(wpa_auth, src_addr, data, data_len);
-	if (frame->packet_type == FT_PACKET_R0KH_R1KH_RESP)
-		return wpa_ft_rrb_rx_resp(wpa_auth, src_addr, data, data_len);
-	if (frame->packet_type == FT_PACKET_R0KH_R1KH_PUSH)
-		return wpa_ft_rrb_rx_push(wpa_auth, src_addr, data, data_len);
-
 	wpa_hexdump(MSG_MSGDUMP, "FT: RRB - FT Action frame", pos, alen);
 
 	if (alen < 1 + 1 + 2 * ETH_ALEN) {
@@ -1834,48 +3413,114 @@
 }
 
 
+void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
+		       const u8 *dst_addr, u8 oui_suffix, const u8 *data,
+		       size_t data_len)
+{
+	const u8 *auth, *enc;
+	size_t alen, elen;
+	int no_defer = 0;
+
+	wpa_printf(MSG_DEBUG, "FT: RRB-OUI received frame from remote AP "
+		   MACSTR, MAC2STR(src_addr));
+	wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame - oui_suffix=%d", oui_suffix);
+
+	if (is_multicast_ether_addr(src_addr)) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: RRB-OUI received frame from multicast address "
+			   MACSTR, MAC2STR(src_addr));
+		return;
+	}
+
+	if (is_multicast_ether_addr(dst_addr)) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: RRB-OUI received frame from remote AP " MACSTR
+			   " to multicast address " MACSTR,
+			   MAC2STR(src_addr), MAC2STR(dst_addr));
+		no_defer = 1;
+	}
+
+	if (data_len < sizeof(u16)) {
+		wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short");
+		return;
+	}
+
+	alen = WPA_GET_LE16(data);
+	if (data_len < sizeof(u16) + alen) {
+		wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short");
+		return;
+	}
+
+	auth = data + sizeof(u16);
+	enc = data + sizeof(u16) + alen;
+	elen = data_len - sizeof(u16) - alen;
+
+	switch (oui_suffix) {
+	case FT_PACKET_R0KH_R1KH_PULL:
+		wpa_ft_rrb_rx_pull(wpa_auth, src_addr, enc, elen, auth, alen,
+				   no_defer);
+		break;
+	case FT_PACKET_R0KH_R1KH_RESP:
+		wpa_ft_rrb_rx_resp(wpa_auth, src_addr, enc, elen, auth, alen,
+				   no_defer);
+		break;
+	case FT_PACKET_R0KH_R1KH_PUSH:
+		wpa_ft_rrb_rx_push(wpa_auth, src_addr, enc, elen, auth, alen,
+				   no_defer);
+		break;
+	case FT_PACKET_R0KH_R1KH_SEQ_REQ:
+		wpa_ft_rrb_rx_seq_req(wpa_auth, src_addr, enc, elen, auth, alen,
+				      no_defer);
+		break;
+	case FT_PACKET_R0KH_R1KH_SEQ_RESP:
+		wpa_ft_rrb_rx_seq_resp(wpa_auth, src_addr, enc, elen, auth,
+				       alen, no_defer);
+		break;
+	}
+}
+
+
 static int wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
 				  struct wpa_ft_pmk_r0_sa *pmk_r0,
 				  struct ft_remote_r1kh *r1kh,
-				  const u8 *s1kh_id, int pairwise)
+				  const u8 *s1kh_id)
 {
-	struct ft_r0kh_r1kh_push_frame frame, f;
-	struct os_time now;
-	const u8 *plain;
-	u8 *crypt;
+	u8 *packet;
+	size_t packet_len;
+	struct ft_rrb_seq f_seq;
+	struct tlv_list push[] = {
+		{ .type = FT_RRB_S1KH_ID, .len = ETH_ALEN,
+		  .data = s1kh_id },
+		{ .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN,
+		  .data = pmk_r0->pmk_r0_name },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
+	struct tlv_list push_auth[] = {
+		{ .type = FT_RRB_SEQ, .len = sizeof(f_seq),
+		  .data = (u8 *) &f_seq },
+		{ .type = FT_RRB_R0KH_ID,
+		  .len = wpa_auth->conf.r0_key_holder_len,
+		  .data = wpa_auth->conf.r0_key_holder },
+		{ .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
+		  .data = r1kh->id },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
 
-	os_memset(&frame, 0, sizeof(frame));
-	frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
-	frame.packet_type = FT_PACKET_R0KH_R1KH_PUSH;
-	frame.data_length = host_to_le16(FT_R0KH_R1KH_PUSH_DATA_LEN);
-	os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN);
-
-	/* aes_wrap() does not support inplace encryption, so use a temporary
-	 * buffer for the data. */
-	os_memcpy(f.r1kh_id, r1kh->id, FT_R1KH_ID_LEN);
-	os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN);
-	os_memcpy(f.pmk_r0_name, pmk_r0->pmk_r0_name, WPA_PMK_NAME_LEN);
-	if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id,
-			      s1kh_id, f.pmk_r1, f.pmk_r1_name) < 0)
+	if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
 		return -1;
-	wpa_printf(MSG_DEBUG, "FT: R1KH-ID " MACSTR, MAC2STR(r1kh->id));
-	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f.pmk_r1, PMK_LEN);
-	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", f.pmk_r1_name,
-		    WPA_PMK_NAME_LEN);
-	os_get_time(&now);
-	WPA_PUT_LE32(f.timestamp, now.sec);
-	f.pairwise = host_to_le16(pairwise);
-	os_memset(f.pad, 0, sizeof(f.pad));
-	plain = ((const u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame,
-					     timestamp);
-	crypt = ((u8 *) &frame) + offsetof(struct ft_r0kh_r1kh_push_frame,
-					   timestamp);
-	if (aes_wrap(r1kh->key, sizeof(r1kh->key),
-		     (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
-		     plain, crypt) < 0)
+	}
+
+	if (wpa_ft_rrb_build_r0(r1kh->key, sizeof(r1kh->key), push, pmk_r0,
+				r1kh->id, s1kh_id, push_auth, wpa_auth->addr,
+				FT_PACKET_R0KH_R1KH_PUSH,
+				&packet, &packet_len) < 0)
 		return -1;
 
-	wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame));
+	wpa_ft_rrb_oui_send(wpa_auth, r1kh->addr, FT_PACKET_R0KH_R1KH_PUSH,
+			    packet, packet_len);
+
+	os_free(packet);
 	return 0;
 }
 
@@ -1887,6 +3532,8 @@
 
 	if (!wpa_auth->conf.pmk_r1_push)
 		return;
+	if (!wpa_auth->conf.r1kh_list)
+		return;
 
 	r0 = wpa_auth->ft_pmk_cache->pmk_r0;
 	while (r0) {
@@ -1902,10 +3549,13 @@
 	wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs "
 		   "for STA " MACSTR, MAC2STR(addr));
 
-	r1kh = wpa_auth->conf.r1kh_list;
-	while (r1kh) {
-		wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr, r0->pairwise);
-		r1kh = r1kh->next;
+	for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) {
+		if (is_zero_ether_addr(r1kh->addr) ||
+		    is_zero_ether_addr(r1kh->id))
+			continue;
+		if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0)
+			continue;
+		wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr);
 	}
 }
 
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 394f77a..98133a0 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -9,6 +9,8 @@
 #include "utils/includes.h"
 
 #include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/list.h"
 #include "common/ieee802_11_defs.h"
 #include "common/sae.h"
 #include "common/wpa_ctrl.h"
@@ -17,6 +19,7 @@
 #include "eapol_auth/eapol_auth_sm_i.h"
 #include "eap_server/eap.h"
 #include "l2_packet/l2_packet.h"
+#include "eth_p_oui.h"
 #include "hostapd.h"
 #include "ieee802_1x.h"
 #include "preauth_auth.h"
@@ -24,6 +27,7 @@
 #include "tkip_countermeasures.h"
 #include "ap_drv_ops.h"
 #include "ap_config.h"
+#include "pmksa_cache_auth.h"
 #include "wpa_auth.h"
 #include "wpa_auth_glue.h"
 
@@ -42,11 +46,12 @@
 	wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey;
 	wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey;
 	wconf->wpa_group_update_count = conf->wpa_group_update_count;
+	wconf->wpa_disable_eapol_key_retries =
+		conf->wpa_disable_eapol_key_retries;
 	wconf->wpa_pairwise_update_count = conf->wpa_pairwise_update_count;
 	wconf->rsn_pairwise = conf->rsn_pairwise;
 	wconf->rsn_preauth = conf->rsn_preauth;
 	wconf->eapol_version = conf->eapol_version;
-	wconf->peerkey = conf->peerkey;
 	wconf->wmm_enabled = conf->wmm_enabled;
 	wconf->wmm_uapsd = conf->wmm_uapsd;
 	wconf->disable_pmksa_caching = conf->disable_pmksa_caching;
@@ -71,8 +76,12 @@
 	os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN);
 	wconf->r0_key_lifetime = conf->r0_key_lifetime;
 	wconf->reassociation_deadline = conf->reassociation_deadline;
-	wconf->r0kh_list = conf->r0kh_list;
-	wconf->r1kh_list = conf->r1kh_list;
+	wconf->rkh_pos_timeout = conf->rkh_pos_timeout;
+	wconf->rkh_neg_timeout = conf->rkh_neg_timeout;
+	wconf->rkh_pull_timeout = conf->rkh_pull_timeout;
+	wconf->rkh_pull_retries = conf->rkh_pull_retries;
+	wconf->r0kh_list = &conf->r0kh_list;
+	wconf->r1kh_list = &conf->r1kh_list;
 	wconf->pmk_r1_push = conf->pmk_r1_push;
 	wconf->ft_over_ds = conf->ft_over_ds;
 	wconf->ft_psk_generate_local = conf->ft_psk_generate_local;
@@ -110,6 +119,11 @@
 	os_memcpy(wconf->ip_addr_start, conf->ip_addr_start, 4);
 	os_memcpy(wconf->ip_addr_end, conf->ip_addr_end, 4);
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_FILS
+	wconf->fils_cache_id_set = conf->fils_cache_id_set;
+	os_memcpy(wconf->fils_cache_id, conf->fils_cache_id,
+		  FILS_CACHE_ID_LEN);
+#endif /* CONFIG_FILS */
 }
 
 
@@ -226,20 +240,47 @@
 
 static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
 					   const u8 *p2p_dev_addr,
-					   const u8 *prev_psk)
+					   const u8 *prev_psk, size_t *psk_len)
 {
 	struct hostapd_data *hapd = ctx;
 	struct sta_info *sta = ap_get_sta(hapd, addr);
 	const u8 *psk;
 
+	if (psk_len)
+		*psk_len = PMK_LEN;
+
 #ifdef CONFIG_SAE
 	if (sta && sta->auth_alg == WLAN_AUTH_SAE) {
 		if (!sta->sae || prev_psk)
 			return NULL;
 		return sta->sae->pmk;
 	}
+	if (sta && wpa_auth_uses_sae(sta->wpa_sm)) {
+		wpa_printf(MSG_DEBUG,
+			   "No PSK for STA trying to use SAE with PMKSA caching");
+		return NULL;
+	}
 #endif /* CONFIG_SAE */
 
+#ifdef CONFIG_OWE
+	if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+	    sta && sta->owe_pmk) {
+		if (psk_len)
+			*psk_len = sta->owe_pmk_len;
+		return sta->owe_pmk;
+	}
+	if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && sta) {
+		struct rsn_pmksa_cache_entry *sa;
+
+		sa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
+		if (sa && sa->akmp == WPA_KEY_MGMT_OWE) {
+			if (psk_len)
+				*psk_len = sa->pmk_len;
+			return sa->pmk;
+		}
+	}
+#endif /* CONFIG_OWE */
+
 	psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk);
 	/*
 	 * This is about to iterate over all psks, prev_psk gives the last
@@ -310,6 +351,37 @@
 			return -1;
 	}
 
+#ifdef CONFIG_TESTING_OPTIONS
+	if (addr && !is_broadcast_ether_addr(addr)) {
+		struct sta_info *sta;
+
+		sta = ap_get_sta(hapd, addr);
+		if (sta) {
+			sta->last_tk_alg = alg;
+			sta->last_tk_key_idx = idx;
+			if (key)
+				os_memcpy(sta->last_tk, key, key_len);
+			sta->last_tk_len = key_len;
+		}
+#ifdef CONFIG_IEEE80211W
+	} else if (alg == WPA_CIPHER_AES_128_CMAC ||
+		   alg == WPA_CIPHER_BIP_GMAC_128 ||
+		   alg == WPA_CIPHER_BIP_GMAC_256 ||
+		   alg == WPA_CIPHER_BIP_CMAC_256) {
+		hapd->last_igtk_alg = alg;
+		hapd->last_igtk_key_idx = idx;
+		if (key)
+			os_memcpy(hapd->last_igtk, key, key_len);
+		hapd->last_igtk_len = key_len;
+#endif /* CONFIG_IEEE80211W */
+	} else {
+		hapd->last_gtk_alg = alg;
+		hapd->last_gtk_key_idx = idx;
+		if (key)
+			os_memcpy(hapd->last_gtk, key, key_len);
+		hapd->last_gtk_len = key_len;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 	return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0,
 				   key, key_len);
 }
@@ -406,6 +478,31 @@
 
 #ifdef CONFIG_IEEE80211R_AP
 
+struct wpa_ft_rrb_rx_later_data {
+	struct dl_list list;
+	u8 addr[ETH_ALEN];
+	size_t data_len;
+	/* followed by data_len octets of data */
+};
+
+static void hostapd_wpa_ft_rrb_rx_later(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct wpa_ft_rrb_rx_later_data *data, *n;
+
+	dl_list_for_each_safe(data, n, &hapd->l2_queue,
+			      struct wpa_ft_rrb_rx_later_data, list) {
+		if (hapd->wpa_auth) {
+			wpa_ft_rrb_rx(hapd->wpa_auth, data->addr,
+				      (const u8 *) (data + 1),
+				      data->data_len);
+		}
+		dl_list_del(&data->list);
+		os_free(data);
+	}
+}
+
+
 struct wpa_auth_ft_iface_iter_data {
 	struct hostapd_data *src_hapd;
 	const u8 *dst;
@@ -417,27 +514,48 @@
 static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx)
 {
 	struct wpa_auth_ft_iface_iter_data *idata = ctx;
+	struct wpa_ft_rrb_rx_later_data *data;
 	struct hostapd_data *hapd;
 	size_t j;
 
 	for (j = 0; j < iface->num_bss; j++) {
 		hapd = iface->bss[j];
-		if (hapd == idata->src_hapd)
+		if (hapd == idata->src_hapd ||
+		    !hapd->wpa_auth ||
+		    os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) != 0)
 			continue;
-		if (!hapd->wpa_auth)
-			continue;
-		if (os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) == 0) {
-			wpa_printf(MSG_DEBUG, "FT: Send RRB data directly to "
-				   "locally managed BSS " MACSTR "@%s -> "
-				   MACSTR "@%s",
-				   MAC2STR(idata->src_hapd->own_addr),
-				   idata->src_hapd->conf->iface,
-				   MAC2STR(hapd->own_addr), hapd->conf->iface);
-			wpa_ft_rrb_rx(hapd->wpa_auth,
-				      idata->src_hapd->own_addr,
-				      idata->data, idata->data_len);
+
+		wpa_printf(MSG_DEBUG,
+			   "FT: Send RRB data directly to locally managed BSS "
+			   MACSTR "@%s -> " MACSTR "@%s",
+			   MAC2STR(idata->src_hapd->own_addr),
+			   idata->src_hapd->conf->iface,
+			   MAC2STR(hapd->own_addr), hapd->conf->iface);
+
+		/* Defer wpa_ft_rrb_rx() until next eloop step as this is
+		 * when it would be triggered when reading from a socket.
+		 * This avoids
+		 * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv,
+		 * that is calling hapd0:recv handler from within
+		 * hapd0:send directly.
+		 */
+		data = os_zalloc(sizeof(*data) + idata->data_len);
+		if (!data)
 			return 1;
-		}
+
+		os_memcpy(data->addr, idata->src_hapd->own_addr, ETH_ALEN);
+		os_memcpy(data + 1, idata->data, idata->data_len);
+		data->data_len = idata->data_len;
+
+		dl_list_add(&hapd->l2_queue, &data->list);
+
+		if (!eloop_is_timeout_registered(hostapd_wpa_ft_rrb_rx_later,
+						 hapd, NULL))
+			eloop_register_timeout(0, 0,
+					       hostapd_wpa_ft_rrb_rx_later,
+					       hapd, NULL);
+
+		return 1;
 	}
 
 	return 0;
@@ -506,6 +624,156 @@
 }
 
 
+#ifdef CONFIG_ETH_P_OUI
+static struct eth_p_oui_ctx * hostapd_wpa_get_oui(struct hostapd_data *hapd,
+						  u8 oui_suffix)
+{
+	switch (oui_suffix) {
+#ifdef CONFIG_IEEE80211R_AP
+	case FT_PACKET_R0KH_R1KH_PULL:
+		return hapd->oui_pull;
+	case FT_PACKET_R0KH_R1KH_RESP:
+		return hapd->oui_resp;
+	case FT_PACKET_R0KH_R1KH_PUSH:
+		return hapd->oui_push;
+	case FT_PACKET_R0KH_R1KH_SEQ_REQ:
+		return hapd->oui_sreq;
+	case FT_PACKET_R0KH_R1KH_SEQ_RESP:
+		return hapd->oui_sresp;
+#endif /* CONFIG_IEEE80211R_AP */
+	default:
+		return NULL;
+	}
+}
+#endif /* CONFIG_ETH_P_OUI */
+
+
+#ifdef CONFIG_IEEE80211R_AP
+
+struct oui_deliver_later_data {
+	struct dl_list list;
+	u8 src_addr[ETH_ALEN];
+	u8 dst_addr[ETH_ALEN];
+	size_t data_len;
+	u8 oui_suffix;
+	/* followed by data_len octets of data */
+};
+
+static void hostapd_oui_deliver_later(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct oui_deliver_later_data *data, *n;
+	struct eth_p_oui_ctx *oui_ctx;
+
+	dl_list_for_each_safe(data, n, &hapd->l2_oui_queue,
+			      struct oui_deliver_later_data, list) {
+		oui_ctx = hostapd_wpa_get_oui(hapd, data->oui_suffix);
+		if (hapd->wpa_auth && oui_ctx) {
+			eth_p_oui_deliver(oui_ctx, data->src_addr,
+					  data->dst_addr,
+					  (const u8 *) (data + 1),
+					  data->data_len);
+		}
+		dl_list_del(&data->list);
+		os_free(data);
+	}
+}
+
+
+struct wpa_auth_oui_iface_iter_data {
+	struct hostapd_data *src_hapd;
+	const u8 *dst_addr;
+	const u8 *data;
+	size_t data_len;
+	u8 oui_suffix;
+};
+
+static int hostapd_wpa_auth_oui_iter(struct hostapd_iface *iface, void *ctx)
+{
+	struct wpa_auth_oui_iface_iter_data *idata = ctx;
+	struct oui_deliver_later_data *data;
+	struct hostapd_data *hapd;
+	size_t j;
+
+	for (j = 0; j < iface->num_bss; j++) {
+		hapd = iface->bss[j];
+		if (hapd == idata->src_hapd)
+			continue;
+		if (!is_multicast_ether_addr(idata->dst_addr) &&
+		    os_memcmp(hapd->own_addr, idata->dst_addr, ETH_ALEN) != 0)
+			continue;
+
+		/* defer eth_p_oui_deliver until next eloop step as this is
+		 * when it would be triggerd from reading from sock
+		 * This avoids
+		 * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv,
+		 * that is calling hapd0:recv handler from within
+		 * hapd0:send directly.
+		 */
+		data = os_zalloc(sizeof(*data) + idata->data_len);
+		if (!data)
+			return 1;
+
+		os_memcpy(data->src_addr, idata->src_hapd->own_addr, ETH_ALEN);
+		os_memcpy(data->dst_addr, idata->dst_addr, ETH_ALEN);
+		os_memcpy(data + 1, idata->data, idata->data_len);
+		data->data_len = idata->data_len;
+		data->oui_suffix = idata->oui_suffix;
+
+		dl_list_add(&hapd->l2_oui_queue, &data->list);
+
+		if (!eloop_is_timeout_registered(hostapd_oui_deliver_later,
+						 hapd, NULL))
+			eloop_register_timeout(0, 0,
+					       hostapd_oui_deliver_later,
+					       hapd, NULL);
+
+		return 1;
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+static int hostapd_wpa_auth_send_oui(void *ctx, const u8 *dst, u8 oui_suffix,
+				     const u8 *data, size_t data_len)
+{
+#ifdef CONFIG_ETH_P_OUI
+	struct hostapd_data *hapd = ctx;
+	struct eth_p_oui_ctx *oui_ctx;
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (hapd->iface->interfaces &&
+	    hapd->iface->interfaces->for_each_interface) {
+		struct wpa_auth_oui_iface_iter_data idata;
+		int res;
+
+		idata.src_hapd = hapd;
+		idata.dst_addr = dst;
+		idata.data = data;
+		idata.data_len = data_len;
+		idata.oui_suffix = oui_suffix;
+		res = hapd->iface->interfaces->for_each_interface(
+			hapd->iface->interfaces, hostapd_wpa_auth_oui_iter,
+			&idata);
+		if (res == 1)
+			return data_len;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+
+	oui_ctx = hostapd_wpa_get_oui(hapd, oui_suffix);
+	if (!oui_ctx)
+		return -1;
+
+	return eth_p_oui_send(oui_ctx, hapd->own_addr, dst, data, data_len);
+#else /* CONFIG_ETH_P_OUI */
+	return -1;
+#endif /* CONFIG_ETH_P_OUI */
+}
+
+
 #ifdef CONFIG_IEEE80211R_AP
 
 static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst,
@@ -584,6 +852,22 @@
 }
 
 
+static void hostapd_rrb_oui_receive(void *ctx, const u8 *src_addr,
+				    const u8 *dst_addr, u8 oui_suffix,
+				    const u8 *buf, size_t len)
+{
+	struct hostapd_data *hapd = ctx;
+
+	wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> "
+		   MACSTR, MAC2STR(src_addr), MAC2STR(dst_addr));
+	if (!is_multicast_ether_addr(dst_addr) &&
+	    os_memcmp(hapd->own_addr, dst_addr, ETH_ALEN) != 0)
+		return;
+	wpa_ft_rrb_oui_rx(hapd->wpa_auth, src_addr, dst_addr, oui_suffix, buf,
+			  len);
+}
+
+
 static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr,
 				      u8 *tspec_ie, size_t tspec_ielen)
 {
@@ -591,6 +875,58 @@
 	return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen);
 }
 
+
+
+static int hostapd_wpa_register_ft_oui(struct hostapd_data *hapd,
+				       const char *ft_iface)
+{
+	hapd->oui_pull = eth_p_oui_register(hapd, ft_iface,
+					    FT_PACKET_R0KH_R1KH_PULL,
+					    hostapd_rrb_oui_receive, hapd);
+	if (!hapd->oui_pull)
+		return -1;
+
+	hapd->oui_resp = eth_p_oui_register(hapd, ft_iface,
+					    FT_PACKET_R0KH_R1KH_RESP,
+					    hostapd_rrb_oui_receive, hapd);
+	if (!hapd->oui_resp)
+		return -1;
+
+	hapd->oui_push = eth_p_oui_register(hapd, ft_iface,
+					    FT_PACKET_R0KH_R1KH_PUSH,
+					    hostapd_rrb_oui_receive, hapd);
+	if (!hapd->oui_push)
+		return -1;
+
+	hapd->oui_sreq = eth_p_oui_register(hapd, ft_iface,
+					    FT_PACKET_R0KH_R1KH_SEQ_REQ,
+					    hostapd_rrb_oui_receive, hapd);
+	if (!hapd->oui_sreq)
+		return -1;
+
+	hapd->oui_sresp = eth_p_oui_register(hapd, ft_iface,
+					     FT_PACKET_R0KH_R1KH_SEQ_RESP,
+					     hostapd_rrb_oui_receive, hapd);
+	if (!hapd->oui_sresp)
+		return -1;
+
+	return 0;
+}
+
+
+static void hostapd_wpa_unregister_ft_oui(struct hostapd_data *hapd)
+{
+	eth_p_oui_unregister(hapd->oui_pull);
+	hapd->oui_pull = NULL;
+	eth_p_oui_unregister(hapd->oui_resp);
+	hapd->oui_resp = NULL;
+	eth_p_oui_unregister(hapd->oui_push);
+	hapd->oui_push = NULL;
+	eth_p_oui_unregister(hapd->oui_sreq);
+	hapd->oui_sreq = NULL;
+	eth_p_oui_unregister(hapd->oui_sresp);
+	hapd->oui_sresp = NULL;
+}
 #endif /* CONFIG_IEEE80211R_AP */
 
 
@@ -612,6 +948,7 @@
 		.for_each_sta = hostapd_wpa_auth_for_each_sta,
 		.for_each_auth = hostapd_wpa_auth_for_each_auth,
 		.send_ether = hostapd_wpa_auth_send_ether,
+		.send_oui = hostapd_wpa_auth_send_oui,
 #ifdef CONFIG_IEEE80211R_AP
 		.send_ft_action = hostapd_wpa_auth_send_ft_action,
 		.add_sta = hostapd_wpa_auth_add_sta,
@@ -654,9 +991,11 @@
 #ifdef CONFIG_IEEE80211R_AP
 	if (!hostapd_drv_none(hapd) &&
 	    wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) {
-		hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ?
-					  hapd->conf->bridge :
-					  hapd->conf->iface, NULL, ETH_P_RRB,
+		const char *ft_iface;
+
+		ft_iface = hapd->conf->bridge[0] ? hapd->conf->bridge :
+			   hapd->conf->iface;
+		hapd->l2 = l2_packet_init(ft_iface, NULL, ETH_P_RRB,
 					  hostapd_rrb_receive, hapd, 1);
 		if (hapd->l2 == NULL &&
 		    (hapd->driver == NULL ||
@@ -665,6 +1004,12 @@
 				   "interface");
 			return -1;
 		}
+
+		if (hostapd_wpa_register_ft_oui(hapd, ft_iface)) {
+			wpa_printf(MSG_ERROR,
+				   "Failed to open ETH_P_OUI interface");
+			return -1;
+		}
 	}
 #endif /* CONFIG_IEEE80211R_AP */
 
@@ -705,7 +1050,12 @@
 	ieee802_1x_deinit(hapd);
 
 #ifdef CONFIG_IEEE80211R_AP
+	eloop_cancel_timeout(hostapd_wpa_ft_rrb_rx_later, hapd, ELOOP_ALL_CTX);
+	hostapd_wpa_ft_rrb_rx_later(hapd, NULL); /* flush without delivering */
+	eloop_cancel_timeout(hostapd_oui_deliver_later, hapd, ELOOP_ALL_CTX);
+	hostapd_oui_deliver_later(hapd, NULL); /* flush without delivering */
 	l2_packet_deinit(hapd->l2);
 	hapd->l2 = NULL;
+	hostapd_wpa_unregister_ft_oui(hapd);
 #endif /* CONFIG_IEEE80211R_AP */
 }
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 9564f24..befa800 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -9,6 +9,8 @@
 #ifndef WPA_AUTH_I_H
 #define WPA_AUTH_I_H
 
+#include "utils/list.h"
+
 /* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */
 #define RSNA_MAX_EAPOL_RETRIES 4
 
@@ -41,6 +43,7 @@
 	Boolean AuthenticationRequest;
 	Boolean ReAuthenticationRequest;
 	Boolean Disconnect;
+	u16 disconnect_reason; /* specific reason code to use with Disconnect */
 	u32 TimeoutCtr;
 	u32 GTimeoutCtr;
 	Boolean TimeoutEvt;
@@ -122,9 +125,10 @@
 			      const u8 *ies, size_t ies_len);
 	void *ft_pending_cb_ctx;
 	struct wpabuf *ft_pending_req_ies;
-	u8 ft_pending_pull_nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
+	u8 ft_pending_pull_nonce[FT_RRB_NONCE_LEN];
 	u8 ft_pending_auth_transaction;
 	u8 ft_pending_current_ap[ETH_ALEN];
+	int ft_pending_pull_left_retries;
 #endif /* CONFIG_IEEE80211R_AP */
 
 	int pending_1_of_4_timeout;
@@ -139,6 +143,12 @@
 	size_t fils_key_auth_len;
 	unsigned int fils_completed:1;
 #endif /* CONFIG_FILS */
+
+#ifdef CONFIG_TESTING_OPTIONS
+	void (*eapol_status_cb)(void *ctx1, void *ctx2);
+	void *eapol_status_cb_ctx1;
+	void *eapol_status_cb_ctx2;
+#endif /* CONFIG_TESTING_OPTIONS */
 };
 
 
@@ -212,6 +222,38 @@
 };
 
 
+#ifdef CONFIG_IEEE80211R_AP
+
+#define FT_REMOTE_SEQ_BACKLOG 16
+struct ft_remote_seq_rx {
+	u32 dom;
+	struct os_reltime time_offset; /* local time - offset = remote time */
+
+	/* accepted sequence numbers: (offset ... offset + 0x40000000]
+	 *   (except those in last)
+	 * dropped sequence numbers: (offset - 0x40000000 ... offset]
+	 * all others trigger SEQ_REQ message (except first message)
+	 */
+	u32 last[FT_REMOTE_SEQ_BACKLOG];
+	unsigned int num_last;
+	u32 offsetidx;
+
+	struct dl_list queue; /* send nonces + rrb msgs awaiting seq resp */
+};
+
+struct ft_remote_seq_tx {
+	u32 dom; /* non zero if initialized */
+	u32 seq;
+};
+
+struct ft_remote_seq {
+	struct ft_remote_seq_rx rx;
+	struct ft_remote_seq_tx tx;
+};
+
+#endif /* CONFIG_IEEE80211R_AP */
+
+
 int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
 		     const u8 *pmkid);
 void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
@@ -230,18 +272,6 @@
 			   int (*cb)(struct wpa_authenticator *a, void *ctx),
 			   void *cb_ctx);
 
-#ifdef CONFIG_PEERKEY
-void wpa_smk_error(struct wpa_authenticator *wpa_auth,
-		   struct wpa_state_machine *sm,
-		   const u8 *key_data, size_t key_data_len);
-void wpa_smk_m1(struct wpa_authenticator *wpa_auth,
-		struct wpa_state_machine *sm, struct wpa_eapol_key *key,
-		const u8 *key_data, size_t key_data_len);
-void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
-		struct wpa_state_machine *sm, struct wpa_eapol_key *key,
-		const u8 *key_data, size_t key_data_len);
-#endif /* CONFIG_PEERKEY */
-
 #ifdef CONFIG_IEEE80211R_AP
 int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len);
 int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
@@ -254,6 +284,9 @@
 struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void);
 void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache);
 void wpa_ft_install_ptk(struct wpa_state_machine *sm);
+int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
+			const u8 *spa, const u8 *pmk_r0,
+			const u8 *pmk_r0_name, int pairwise);
 #endif /* CONFIG_IEEE80211R_AP */
 
 #endif /* WPA_AUTH_I_H */
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index c770d62..0196d00 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -234,6 +234,20 @@
 	}
 #endif /* CONFIG_IEEE80211R_AP */
 #endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OWE);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_DPP */
 
 #ifdef CONFIG_RSN_TESTING
 	if (rsn_testing) {
@@ -254,8 +268,6 @@
 	capab = 0;
 	if (conf->rsn_preauth)
 		capab |= WPA_CAPABILITY_PREAUTH;
-	if (conf->peerkey)
-		capab |= WPA_CAPABILITY_PEERKEY_ENABLED;
 	if (conf->wmm_enabled) {
 		/* 4 PTKSA replay counters when using WMM */
 		capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
@@ -498,7 +510,8 @@
 int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
 			struct wpa_state_machine *sm,
 			const u8 *wpa_ie, size_t wpa_ie_len,
-			const u8 *mdie, size_t mdie_len)
+			const u8 *mdie, size_t mdie_len,
+			const u8 *owe_dh, size_t owe_dh_len)
 {
 	struct wpa_ie_data data;
 	int ciphers, key_mgmt, res, version;
@@ -567,6 +580,14 @@
 			selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
 		else if (data.key_mgmt & WPA_KEY_MGMT_PSK)
 			selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+#ifdef CONFIG_OWE
+		else if (data.key_mgmt & WPA_KEY_MGMT_OWE)
+			selector = RSN_AUTH_KEY_MGMT_OWE;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+		else if (data.key_mgmt & WPA_KEY_MGMT_DPP)
+			selector = RSN_AUTH_KEY_MGMT_DPP;
+#endif /* CONFIG_DPP */
 		wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
 
 		selector = wpa_cipher_to_suite(WPA_PROTO_RSN,
@@ -659,6 +680,14 @@
 #endif /* CONFIG_SAE */
 	else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+#ifdef CONFIG_OWE
+	else if (key_mgmt & WPA_KEY_MGMT_OWE)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_OWE;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+	else if (key_mgmt & WPA_KEY_MGMT_DPP)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_DPP;
+#endif /* CONFIG_DPP */
 	else
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
 
@@ -723,6 +752,19 @@
 	}
 #endif /* CONFIG_IEEE80211R_AP */
 
+#ifdef CONFIG_OWE
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && !owe_dh) {
+		wpa_printf(MSG_DEBUG,
+			   "OWE: No Diffie-Hellman Parameter element");
+		return WPA_INVALID_AKMP;
+	}
+	if (sm->wpa_key_mgmt != WPA_KEY_MGMT_OWE && owe_dh) {
+		wpa_printf(MSG_DEBUG,
+			   "OWE: Unexpected Diffie-Hellman Parameter element with non-OWE AKM");
+		return WPA_INVALID_AKMP;
+	}
+#endif /* CONFIG_OWE */
+
 	sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
 	if (sm->pairwise < 0)
 		return WPA_INVALID_PAIRWISE;
@@ -775,6 +817,14 @@
 		os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN);
 	}
 
+#ifdef CONFIG_DPP
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && !sm->pmksa) {
+		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+				 "No PMKSA cache entry found for DPP");
+		return WPA_INVALID_PMKID;
+	}
+#endif /* CONFIG_DPP */
+
 	if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) {
 		os_free(sm->wpa_ie);
 		sm->wpa_ie = os_malloc(wpa_ie_len);
@@ -867,36 +917,6 @@
 		return 0;
 	}
 
-#ifdef CONFIG_PEERKEY
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) {
-		ie->smk = pos + 2 + RSN_SELECTOR_LEN;
-		ie->smk_len = pos[1] - RSN_SELECTOR_LEN;
-		return 0;
-	}
-
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) {
-		ie->nonce = pos + 2 + RSN_SELECTOR_LEN;
-		ie->nonce_len = pos[1] - RSN_SELECTOR_LEN;
-		return 0;
-	}
-
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) {
-		ie->lifetime = pos + 2 + RSN_SELECTOR_LEN;
-		ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN;
-		return 0;
-	}
-
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) {
-		ie->error = pos + 2 + RSN_SELECTOR_LEN;
-		ie->error_len = pos[1] - RSN_SELECTOR_LEN;
-		return 0;
-	}
-#endif /* CONFIG_PEERKEY */
-
 #ifdef CONFIG_IEEE80211W
 	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
 	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
@@ -990,3 +1010,19 @@
 {
 	return sm ? sm->mgmt_frame_prot : 0;
 }
+
+
+#ifdef CONFIG_OWE
+u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm,
+				   u8 *pos, size_t max_len,
+				   const u8 *req_ies, size_t req_ies_len)
+{
+	int res;
+
+	res = wpa_write_rsn_ie(&sm->wpa_auth->conf, pos, max_len,
+			       sm->pmksa ? sm->pmksa->pmkid : NULL);
+	if (res < 0)
+		return pos;
+	return pos + res;
+}
+#endif /* CONFIG_OWE */
diff --git a/src/ap/wpa_auth_ie.h b/src/ap/wpa_auth_ie.h
index 5c3bd18..73e4333 100644
--- a/src/ap/wpa_auth_ie.h
+++ b/src/ap/wpa_auth_ie.h
@@ -19,16 +19,6 @@
 	size_t gtk_len;
 	const u8 *mac_addr;
 	size_t mac_addr_len;
-#ifdef CONFIG_PEERKEY
-	const u8 *smk;
-	size_t smk_len;
-	const u8 *nonce;
-	size_t nonce_len;
-	const u8 *lifetime;
-	size_t lifetime_len;
-	const u8 *error;
-	size_t error_len;
-#endif /* CONFIG_PEERKEY */
 #ifdef CONFIG_IEEE80211W
 	const u8 *igtk;
 	size_t igtk_len;
diff --git a/src/common/defs.h b/src/common/defs.h
index eaccced..1de099f 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -55,6 +55,8 @@
 #define WPA_KEY_MGMT_FILS_SHA384 BIT(19)
 #define WPA_KEY_MGMT_FT_FILS_SHA256 BIT(20)
 #define WPA_KEY_MGMT_FT_FILS_SHA384 BIT(21)
+#define WPA_KEY_MGMT_OWE BIT(22)
+#define WPA_KEY_MGMT_DPP BIT(23)
 
 static inline int wpa_key_mgmt_wpa_ieee8021x(int akm)
 {
@@ -136,7 +138,9 @@
 	return wpa_key_mgmt_wpa_ieee8021x(akm) ||
 		wpa_key_mgmt_wpa_psk(akm) ||
 		wpa_key_mgmt_fils(akm) ||
-		wpa_key_mgmt_sae(akm);
+		wpa_key_mgmt_sae(akm) ||
+		akm == WPA_KEY_MGMT_OWE ||
+		akm == WPA_KEY_MGMT_DPP;
 }
 
 static inline int wpa_key_mgmt_wpa_any(int akm)
@@ -161,7 +165,12 @@
 #define WPA_AUTH_ALG_FT BIT(3)
 #define WPA_AUTH_ALG_SAE BIT(4)
 #define WPA_AUTH_ALG_FILS BIT(5)
+#define WPA_AUTH_ALG_FILS_SK_PFS BIT(6)
 
+static inline int wpa_auth_alg_fils(int alg)
+{
+	return !!(alg & (WPA_AUTH_ALG_FILS | WPA_AUTH_ALG_FILS_SK_PFS));
+}
 
 enum wpa_alg {
 	WPA_ALG_NONE,
@@ -380,4 +389,8 @@
 	SIM_STATE_ERROR,
 };
 
+#define OCE_STA BIT(0)
+#define OCE_STA_CFON BIT(1)
+#define OCE_AP BIT(2)
+
 #endif /* DEFS_H */
diff --git a/src/common/dhcp.h b/src/common/dhcp.h
index f2ef61e..e38512c 100644
--- a/src/common/dhcp.h
+++ b/src/common/dhcp.h
@@ -10,7 +10,13 @@
 #define DHCP_H
 
 #include <netinet/ip.h>
+#if __FAVOR_BSD
 #include <netinet/udp.h>
+#else
+#define __FAVOR_BSD 1
+#include <netinet/udp.h>
+#undef __FAVOR_BSD
+#endif
 
 #define DHCP_SERVER_PORT 67
 #define DHCP_CLIENT_PORT 68
diff --git a/src/common/dpp.c b/src/common/dpp.c
new file mode 100644
index 0000000..5107950
--- /dev/null
+++ b/src/common/dpp.c
@@ -0,0 +1,5998 @@
+/*
+ * DPP functionality shared between hostapd and wpa_supplicant
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <openssl/opensslv.h>
+#include <openssl/err.h>
+
+#include "utils/common.h"
+#include "utils/base64.h"
+#include "utils/json.h"
+#include "common/ieee802_11_common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "crypto/sha384.h"
+#include "crypto/sha512.h"
+#include "dpp.h"
+
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+/* Compatibility wrappers for older versions. */
+
+static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
+{
+	sig->r = r;
+	sig->s = s;
+	return 1;
+}
+
+
+static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr,
+			   const BIGNUM **ps)
+{
+	if (pr)
+		*pr = sig->r;
+	if (ps)
+		*ps = sig->s;
+}
+
+#endif
+
+
+static const struct dpp_curve_params dpp_curves[] = {
+	/* The mandatory to support and the default NIST P-256 curve needs to
+	 * be the first entry on this list. */
+	{ "prime256v1", 32, 32, 16, 32, "P-256", 19, "ES256" },
+	{ "secp384r1", 48, 48, 24, 48, "P-384", 20, "ES384" },
+	{ "secp521r1", 64, 64, 32, 66, "P-521", 21, "ES512" },
+	{ "brainpoolP256r1", 32, 32, 16, 32, "BP-256", 28, "BS256" },
+	{ "brainpoolP384r1", 48, 48, 24, 48, "BP-384", 29, "BS384" },
+	{ "brainpoolP512r1", 64, 64, 32, 64, "BP-512", 30, "BS512" },
+	{ NULL, 0, 0, 0, 0, NULL, 0, NULL }
+};
+
+
+/* Role-specific elements for PKEX */
+
+/* NIST P-256 */
+static const u8 pkex_init_x_p256[32] = {
+	0x56, 0x26, 0x12, 0xcf, 0x36, 0x48, 0xfe, 0x0b,
+	0x07, 0x04, 0xbb, 0x12, 0x22, 0x50, 0xb2, 0x54,
+	0xb1, 0x94, 0x64, 0x7e, 0x54, 0xce, 0x08, 0x07,
+	0x2e, 0xec, 0xca, 0x74, 0x5b, 0x61, 0x2d, 0x25
+ };
+static const u8 pkex_init_y_p256[32] = {
+	0x3e, 0x44, 0xc7, 0xc9, 0x8c, 0x1c, 0xa1, 0x0b,
+	0x20, 0x09, 0x93, 0xb2, 0xfd, 0xe5, 0x69, 0xdc,
+	0x75, 0xbc, 0xad, 0x33, 0xc1, 0xe7, 0xc6, 0x45,
+	0x4d, 0x10, 0x1e, 0x6a, 0x3d, 0x84, 0x3c, 0xa4
+ };
+static const u8 pkex_resp_x_p256[32] = {
+	0x1e, 0xa4, 0x8a, 0xb1, 0xa4, 0xe8, 0x42, 0x39,
+	0xad, 0x73, 0x07, 0xf2, 0x34, 0xdf, 0x57, 0x4f,
+	0xc0, 0x9d, 0x54, 0xbe, 0x36, 0x1b, 0x31, 0x0f,
+	0x59, 0x91, 0x52, 0x33, 0xac, 0x19, 0x9d, 0x76
+};
+static const u8 pkex_resp_y_p256[32] = {
+	0x26, 0x04, 0x09, 0x45, 0x0a, 0x05, 0x20, 0xe7,
+	0xa7, 0x27, 0xc1, 0x36, 0x76, 0x85, 0xca, 0x3e,
+	0x42, 0x16, 0xf4, 0x89, 0x85, 0x34, 0x6e, 0xd5,
+	0x17, 0xde, 0xc0, 0xb8, 0xad, 0xfd, 0xb2, 0x98
+};
+
+/* NIST P-384 */
+static const u8 pkex_init_x_p384[48] = {
+	0x95, 0x3f, 0x42, 0x9e, 0x50, 0x7f, 0xf9, 0xaa,
+	0xac, 0x1a, 0xf2, 0x85, 0x2e, 0x64, 0x91, 0x68,
+	0x64, 0xc4, 0x3c, 0xb7, 0x5c, 0xf8, 0xc9, 0x53,
+	0x6e, 0x58, 0x4c, 0x7f, 0xc4, 0x64, 0x61, 0xac,
+	0x51, 0x8a, 0x6f, 0xfe, 0xab, 0x74, 0xe6, 0x12,
+	0x81, 0xac, 0x38, 0x5d, 0x41, 0xe6, 0xb9, 0xa3
+};
+static const u8 pkex_init_y_p384[48] = {
+	0x89, 0xd0, 0x97, 0x7b, 0x59, 0x4f, 0xa6, 0xd6,
+	0x7c, 0x5d, 0x93, 0x5b, 0x93, 0xc4, 0x07, 0xa9,
+	0x89, 0xee, 0xd5, 0xcd, 0x6f, 0x42, 0xf8, 0x38,
+	0xc8, 0xc6, 0x62, 0x24, 0x69, 0x0c, 0xd4, 0x48,
+	0xd8, 0x44, 0xd6, 0xc2, 0xe8, 0xcc, 0x62, 0x6b,
+	0x3c, 0x25, 0x53, 0xba, 0x4f, 0x71, 0xf8, 0xe7
+};
+static const u8 pkex_resp_x_p384[48] = {
+	0xad, 0xbe, 0xd7, 0x1d, 0x3a, 0x71, 0x64, 0x98,
+	0x5f, 0xb4, 0xd6, 0x4b, 0x50, 0xd0, 0x84, 0x97,
+	0x4b, 0x7e, 0x57, 0x70, 0xd2, 0xd9, 0xf4, 0x92,
+	0x2a, 0x3f, 0xce, 0x99, 0xc5, 0x77, 0x33, 0x44,
+	0x14, 0x56, 0x92, 0xcb, 0xae, 0x46, 0x64, 0xdf,
+	0xe0, 0xbb, 0xd7, 0xb1, 0x29, 0x20, 0x72, 0xdf
+};
+static const u8 pkex_resp_y_p384[48] = {
+	0x54, 0x58, 0x20, 0xad, 0x55, 0x1d, 0xca, 0xf3,
+	0x1c, 0x8a, 0xcd, 0x19, 0x40, 0xf9, 0x37, 0x83,
+	0xc7, 0xd6, 0xb3, 0x13, 0x7d, 0x53, 0x28, 0x5c,
+	0xf6, 0x2d, 0xf1, 0xdd, 0xa5, 0x8b, 0xad, 0x5d,
+	0x81, 0xab, 0xb1, 0x00, 0x39, 0xd6, 0xcc, 0x9c,
+	0xea, 0x1e, 0x84, 0x1d, 0xbf, 0xe3, 0x35, 0xf9
+};
+
+/* NIST P-521 */
+static const u8 pkex_init_x_p521[66] = {
+	0x00, 0x16, 0x20, 0x45, 0x19, 0x50, 0x95, 0x23,
+	0x0d, 0x24, 0xbe, 0x00, 0x87, 0xdc, 0xfa, 0xf0,
+	0x58, 0x9a, 0x01, 0x60, 0x07, 0x7a, 0xca, 0x76,
+	0x01, 0xab, 0x2d, 0x5a, 0x46, 0xcd, 0x2c, 0xb5,
+	0x11, 0x9a, 0xff, 0xaa, 0x48, 0x04, 0x91, 0x38,
+	0xcf, 0x86, 0xfc, 0xa4, 0xa5, 0x0f, 0x47, 0x01,
+	0x80, 0x1b, 0x30, 0xa3, 0xae, 0xe8, 0x1c, 0x2e,
+	0xea, 0xcc, 0xf0, 0x03, 0x9f, 0x77, 0x4c, 0x8d,
+	0x97, 0x76
+};
+static const u8 pkex_init_y_p521[66] = {
+	0x01, 0x4c, 0x71, 0xfd, 0x1b, 0xd5, 0x9c, 0xa6,
+	0xed, 0x39, 0xef, 0x45, 0xc5, 0x06, 0xfd, 0x66,
+	0xc0, 0xeb, 0x0f, 0xbf, 0x21, 0xa3, 0x36, 0x74,
+	0xfd, 0xaa, 0x05, 0x6e, 0x4e, 0x33, 0x95, 0x42,
+	0x1a, 0x9d, 0x3f, 0x3a, 0x1c, 0x5e, 0xa8, 0x60,
+	0xf7, 0xe5, 0x59, 0x1d, 0x07, 0xaa, 0x6f, 0x40,
+	0x0a, 0x59, 0x3c, 0x27, 0xad, 0xe0, 0x48, 0xfd,
+	0xd1, 0x83, 0x37, 0x4c, 0xdf, 0xe1, 0x86, 0x72,
+	0xfc, 0x57
+};
+static const u8 pkex_resp_x_p521[66] = {
+	0x00, 0x79, 0xe4, 0x4d, 0x6b, 0x5e, 0x12, 0x0a,
+	0x18, 0x2c, 0xb3, 0x05, 0x77, 0x0f, 0xc3, 0x44,
+	0x1a, 0xcd, 0x78, 0x46, 0x14, 0xee, 0x46, 0x3f,
+	0xab, 0xc9, 0x59, 0x7c, 0x85, 0xa0, 0xc2, 0xfb,
+	0x02, 0x32, 0x99, 0xde, 0x5d, 0xe1, 0x0d, 0x48,
+	0x2d, 0x71, 0x7d, 0x8d, 0x3f, 0x61, 0x67, 0x9e,
+	0x2b, 0x8b, 0x12, 0xde, 0x10, 0x21, 0x55, 0x0a,
+	0x5b, 0x2d, 0xe8, 0x05, 0x09, 0xf6, 0x20, 0x97,
+	0x84, 0xb4
+};
+static const u8 pkex_resp_y_p521[66] = {
+	0x01, 0xb9, 0x9c, 0xc6, 0x41, 0x32, 0x5b, 0xd2,
+	0x35, 0xd8, 0x8b, 0x2b, 0xe4, 0x6e, 0xcc, 0xdf,
+	0x7c, 0x38, 0xc4, 0x5b, 0xf6, 0x74, 0x71, 0x5c,
+	0x77, 0x16, 0x8a, 0x80, 0xa9, 0x84, 0xc7, 0x7b,
+	0x9d, 0xfd, 0x83, 0x6f, 0xae, 0xf8, 0x24, 0x16,
+	0x2f, 0x21, 0x25, 0x65, 0xa2, 0x1a, 0x6b, 0x2d,
+	0x30, 0x62, 0xb3, 0xcc, 0x6e, 0x59, 0x3c, 0x7f,
+	0x58, 0x91, 0x81, 0x72, 0x07, 0x8c, 0x91, 0xac,
+	0x31, 0x1e
+};
+
+/* Brainpool P-256r1 */
+static const u8 pkex_init_x_bp_p256r1[32] = {
+	0x46, 0x98, 0x18, 0x6c, 0x27, 0xcd, 0x4b, 0x10,
+	0x7d, 0x55, 0xa3, 0xdd, 0x89, 0x1f, 0x9f, 0xca,
+	0xc7, 0x42, 0x5b, 0x8a, 0x23, 0xed, 0xf8, 0x75,
+	0xac, 0xc7, 0xe9, 0x8d, 0xc2, 0x6f, 0xec, 0xd8
+};
+static const u8 pkex_init_y_bp_p256r1[32] = {
+	0x16, 0x30, 0x68, 0x32, 0x3b, 0xb0, 0x21, 0xee,
+	0xeb, 0xf7, 0xb6, 0x7c, 0xae, 0x52, 0x26, 0x42,
+	0x59, 0x28, 0x58, 0xb6, 0x14, 0x90, 0xed, 0x69,
+	0xd0, 0x67, 0xea, 0x25, 0x60, 0x0f, 0xa9, 0x6c
+};
+static const u8 pkex_resp_x_bp_p256r1[32] = {
+	0x90, 0x18, 0x84, 0xc9, 0xdc, 0xcc, 0xb5, 0x2f,
+	0x4a, 0x3f, 0x4f, 0x18, 0x0a, 0x22, 0x56, 0x6a,
+	0xa9, 0xef, 0xd4, 0xe6, 0xc3, 0x53, 0xc2, 0x1a,
+	0x23, 0x54, 0xdd, 0x08, 0x7e, 0x10, 0xd8, 0xe3
+};
+static const u8 pkex_resp_y_bp_p256r1[32] = {
+	0x2a, 0xfa, 0x98, 0x9b, 0xe3, 0xda, 0x30, 0xfd,
+	0x32, 0x28, 0xcb, 0x66, 0xfb, 0x40, 0x7f, 0xf2,
+	0xb2, 0x25, 0x80, 0x82, 0x44, 0x85, 0x13, 0x7e,
+	0x4b, 0xb5, 0x06, 0xc0, 0x03, 0x69, 0x23, 0x64
+};
+
+/* Brainpool P-384r1 */
+static const u8 pkex_init_x_bp_p384r1[48] = {
+	0x0a, 0x2c, 0xeb, 0x49, 0x5e, 0xb7, 0x23, 0xbd,
+	0x20, 0x5b, 0xe0, 0x49, 0xdf, 0xcf, 0xcf, 0x19,
+	0x37, 0x36, 0xe1, 0x2f, 0x59, 0xdb, 0x07, 0x06,
+	0xb5, 0xeb, 0x2d, 0xae, 0xc2, 0xb2, 0x38, 0x62,
+	0xa6, 0x73, 0x09, 0xa0, 0x6c, 0x0a, 0xa2, 0x30,
+	0x99, 0xeb, 0xf7, 0x1e, 0x47, 0xb9, 0x5e, 0xbe
+};
+static const u8 pkex_init_y_bp_p384r1[48] = {
+	0x54, 0x76, 0x61, 0x65, 0x75, 0x5a, 0x2f, 0x99,
+	0x39, 0x73, 0xca, 0x6c, 0xf9, 0xf7, 0x12, 0x86,
+	0x54, 0xd5, 0xd4, 0xad, 0x45, 0x7b, 0xbf, 0x32,
+	0xee, 0x62, 0x8b, 0x9f, 0x52, 0xe8, 0xa0, 0xc9,
+	0xb7, 0x9d, 0xd1, 0x09, 0xb4, 0x79, 0x1c, 0x3e,
+	0x1a, 0xbf, 0x21, 0x45, 0x66, 0x6b, 0x02, 0x52
+};
+static const u8 pkex_resp_x_bp_p384r1[48] = {
+	0x03, 0xa2, 0x57, 0xef, 0xe8, 0x51, 0x21, 0xa0,
+	0xc8, 0x9e, 0x21, 0x02, 0xb5, 0x9a, 0x36, 0x25,
+	0x74, 0x22, 0xd1, 0xf2, 0x1b, 0xa8, 0x9a, 0x9b,
+	0x97, 0xbc, 0x5a, 0xeb, 0x26, 0x15, 0x09, 0x71,
+	0x77, 0x59, 0xec, 0x8b, 0xb7, 0xe1, 0xe8, 0xce,
+	0x65, 0xb8, 0xaf, 0xf8, 0x80, 0xae, 0x74, 0x6c
+};
+static const u8 pkex_resp_y_bp_p384r1[48] = {
+	0x2f, 0xd9, 0x6a, 0xc7, 0x3e, 0xec, 0x76, 0x65,
+	0x2d, 0x38, 0x7f, 0xec, 0x63, 0x26, 0x3f, 0x04,
+	0xd8, 0x4e, 0xff, 0xe1, 0x0a, 0x51, 0x74, 0x70,
+	0xe5, 0x46, 0x63, 0x7f, 0x5c, 0xc0, 0xd1, 0x7c,
+	0xfb, 0x2f, 0xea, 0xe2, 0xd8, 0x0f, 0x84, 0xcb,
+	0xe9, 0x39, 0x5c, 0x64, 0xfe, 0xcb, 0x2f, 0xf1
+};
+
+/* Brainpool P-512r1 */
+static const u8 pkex_init_x_bp_p512r1[64] = {
+	0x4c, 0xe9, 0xb6, 0x1c, 0xe2, 0x00, 0x3c, 0x9c,
+	0xa9, 0xc8, 0x56, 0x52, 0xaf, 0x87, 0x3e, 0x51,
+	0x9c, 0xbb, 0x15, 0x31, 0x1e, 0xc1, 0x05, 0xfc,
+	0x7c, 0x77, 0xd7, 0x37, 0x61, 0x27, 0xd0, 0x95,
+	0x98, 0xee, 0x5d, 0xa4, 0x3d, 0x09, 0xdb, 0x3d,
+	0xfa, 0x89, 0x9e, 0x7f, 0xa6, 0xa6, 0x9c, 0xff,
+	0x83, 0x5c, 0x21, 0x6c, 0x3e, 0xf2, 0xfe, 0xdc,
+	0x63, 0xe4, 0xd1, 0x0e, 0x75, 0x45, 0x69, 0x0f
+};
+static const u8 pkex_init_y_bp_p512r1[64] = {
+	0x5a, 0x28, 0x01, 0xbe, 0x96, 0x82, 0x4e, 0xf6,
+	0xfa, 0xed, 0x7d, 0xfd, 0x48, 0x8b, 0x48, 0x4e,
+	0xd1, 0x97, 0x87, 0xc4, 0x05, 0x5d, 0x15, 0x2a,
+	0xf4, 0x91, 0x4b, 0x75, 0x90, 0xd9, 0x34, 0x2c,
+	0x3c, 0x12, 0xf2, 0xf5, 0x25, 0x94, 0x24, 0x34,
+	0xa7, 0x6d, 0x66, 0xbc, 0x27, 0xa4, 0xa0, 0x8d,
+	0xd5, 0xe1, 0x54, 0xa3, 0x55, 0x26, 0xd4, 0x14,
+	0x17, 0x0f, 0xc1, 0xc7, 0x3d, 0x68, 0x7f, 0x5a
+};
+static const u8 pkex_resp_x_bp_p512r1[64] = {
+	0x2a, 0x60, 0x32, 0x27, 0xa1, 0xe6, 0x94, 0x72,
+	0x1c, 0x48, 0xbe, 0xc5, 0x77, 0x14, 0x30, 0x76,
+	0xe4, 0xbf, 0xf7, 0x7b, 0xc5, 0xfd, 0xdf, 0x19,
+	0x1e, 0x0f, 0xdf, 0x1c, 0x40, 0xfa, 0x34, 0x9e,
+	0x1f, 0x42, 0x24, 0xa3, 0x2c, 0xd5, 0xc7, 0xc9,
+	0x7b, 0x47, 0x78, 0x96, 0xf1, 0x37, 0x0e, 0x88,
+	0xcb, 0xa6, 0x52, 0x29, 0xd7, 0xa8, 0x38, 0x29,
+	0x8e, 0x6e, 0x23, 0x47, 0xd4, 0x4b, 0x70, 0x3e
+};
+static const u8 pkex_resp_y_bp_p512r1[64] = {
+	0x2a, 0xbe, 0x59, 0xe6, 0xc4, 0xb3, 0xd8, 0x09,
+	0x66, 0x89, 0x0a, 0x2d, 0x19, 0xf0, 0x9c, 0x9f,
+	0xb4, 0xab, 0x8f, 0x50, 0x68, 0x3c, 0x74, 0x64,
+	0x4e, 0x19, 0x55, 0x81, 0x9b, 0x48, 0x5c, 0xf4,
+	0x12, 0x8d, 0xb9, 0xd8, 0x02, 0x5b, 0xe1, 0x26,
+	0x7e, 0x19, 0x5c, 0xfd, 0x70, 0xf7, 0x4b, 0xdc,
+	0xb5, 0x5d, 0xc1, 0x7a, 0xe9, 0xd1, 0x05, 0x2e,
+	0xd1, 0xfd, 0x2f, 0xce, 0x63, 0x77, 0x48, 0x2c
+};
+
+
+static int dpp_hash_vector(const struct dpp_curve_params *curve,
+			   size_t num_elem, const u8 *addr[], const size_t *len,
+			   u8 *mac)
+{
+	if (curve->hash_len == 32)
+		return sha256_vector(num_elem, addr, len, mac);
+	if (curve->hash_len == 48)
+		return sha384_vector(num_elem, addr, len, mac);
+	if (curve->hash_len == 64)
+		return sha512_vector(num_elem, addr, len, mac);
+	return -1;
+}
+
+
+static int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len,
+			   const char *label, u8 *out, size_t outlen)
+{
+	if (hash_len == 32)
+		return hmac_sha256_kdf(secret, secret_len, NULL,
+				       (const u8 *) label, os_strlen(label),
+				       out, outlen);
+	if (hash_len == 48)
+		return hmac_sha384_kdf(secret, secret_len, NULL,
+				       (const u8 *) label, os_strlen(label),
+				       out, outlen);
+	if (hash_len == 64)
+		return hmac_sha512_kdf(secret, secret_len, NULL,
+				       (const u8 *) label, os_strlen(label),
+				       out, outlen);
+	return -1;
+}
+
+
+static int dpp_hmac_vector(size_t hash_len, const u8 *key, size_t key_len,
+			   size_t num_elem, const u8 *addr[],
+			   const size_t *len, u8 *mac)
+{
+	if (hash_len == 32)
+		return hmac_sha256_vector(key, key_len, num_elem, addr, len,
+					  mac);
+	if (hash_len == 48)
+		return hmac_sha384_vector(key, key_len, num_elem, addr, len,
+					  mac);
+	if (hash_len == 64)
+		return hmac_sha512_vector(key, key_len, num_elem, addr, len,
+					  mac);
+	return -1;
+}
+
+
+static int dpp_hmac(size_t hash_len, const u8 *key, size_t key_len,
+		    const u8 *data, size_t data_len, u8 *mac)
+{
+	if (hash_len == 32)
+		return hmac_sha256(key, key_len, data, data_len, mac);
+	if (hash_len == 48)
+		return hmac_sha384(key, key_len, data, data_len, mac);
+	if (hash_len == 64)
+		return hmac_sha512(key, key_len, data, data_len, mac);
+	return -1;
+}
+
+
+static struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix)
+{
+	int len, res;
+	EC_KEY *eckey;
+	struct wpabuf *buf;
+	unsigned char *pos;
+
+	eckey = EVP_PKEY_get1_EC_KEY(pkey);
+	if (!eckey)
+		return NULL;
+	EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED);
+	len = i2o_ECPublicKey(eckey, NULL);
+	if (len <= 0) {
+		wpa_printf(MSG_ERROR,
+			   "DDP: Failed to determine public key encoding length");
+		EC_KEY_free(eckey);
+		return NULL;
+	}
+
+	buf = wpabuf_alloc(len);
+	if (!buf) {
+		EC_KEY_free(eckey);
+		return NULL;
+	}
+
+	pos = wpabuf_put(buf, len);
+	res = i2o_ECPublicKey(eckey, &pos);
+	EC_KEY_free(eckey);
+	if (res != len) {
+		wpa_printf(MSG_ERROR,
+			   "DDP: Failed to encode public key (res=%d/%d)",
+			   res, len);
+		wpabuf_free(buf);
+		return NULL;
+	}
+
+	if (!prefix) {
+		/* Remove 0x04 prefix to match DPP definition */
+		pos = wpabuf_mhead(buf);
+		os_memmove(pos, pos + 1, len - 1);
+		buf->used--;
+	}
+
+	return buf;
+}
+
+
+static EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group,
+					     const u8 *buf_x, const u8 *buf_y,
+					     size_t len)
+{
+	EC_KEY *eckey = NULL;
+	BN_CTX *ctx;
+	EC_POINT *point = NULL;
+	BIGNUM *x = NULL, *y = NULL;
+	EVP_PKEY *pkey = NULL;
+
+	ctx = BN_CTX_new();
+	if (!ctx) {
+		wpa_printf(MSG_ERROR, "DPP: Out of memory");
+		return NULL;
+	}
+
+	point = EC_POINT_new(group);
+	x = BN_bin2bn(buf_x, len, NULL);
+	y = BN_bin2bn(buf_y, len, NULL);
+	if (!point || !x || !y) {
+		wpa_printf(MSG_ERROR, "DPP: Out of memory");
+		goto fail;
+	}
+
+	if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	if (!EC_POINT_is_on_curve(group, point, ctx) ||
+	    EC_POINT_is_at_infinity(group, point)) {
+		wpa_printf(MSG_ERROR, "DPP: Invalid point");
+		goto fail;
+	}
+
+	eckey = EC_KEY_new();
+	if (!eckey ||
+	    EC_KEY_set_group(eckey, group) != 1 ||
+	    EC_KEY_set_public_key(eckey, point) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to set EC_KEY: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
+
+	pkey = EVP_PKEY_new();
+	if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) {
+		wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY");
+		goto fail;
+	}
+
+out:
+	BN_free(x);
+	BN_free(y);
+	EC_KEY_free(eckey);
+	EC_POINT_free(point);
+	BN_CTX_free(ctx);
+	return pkey;
+fail:
+	EVP_PKEY_free(pkey);
+	pkey = NULL;
+	goto out;
+}
+
+
+static EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key,
+				       const u8 *buf, size_t len)
+{
+	EC_KEY *eckey;
+	const EC_GROUP *group;
+	EVP_PKEY *pkey = NULL;
+
+	if (len & 1)
+		return NULL;
+
+	eckey = EVP_PKEY_get1_EC_KEY(group_key);
+	if (!eckey) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Could not get EC_KEY from group_key");
+		return NULL;
+	}
+
+	group = EC_KEY_get0_group(eckey);
+	if (group)
+		pkey = dpp_set_pubkey_point_group(group, buf, buf + len / 2,
+						  len / 2);
+	else
+		wpa_printf(MSG_ERROR, "DPP: Could not get EC group");
+
+	EC_KEY_free(eckey);
+	return pkey;
+}
+
+
+struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type,
+			      size_t len)
+{
+	struct wpabuf *msg;
+
+	msg = wpabuf_alloc(8 + len);
+	if (!msg)
+		return NULL;
+	wpabuf_put_u8(msg, WLAN_ACTION_PUBLIC);
+	wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC);
+	wpabuf_put_be24(msg, OUI_WFA);
+	wpabuf_put_u8(msg, DPP_OUI_TYPE);
+	wpabuf_put_u8(msg, 1); /* Crypto Suite */
+	wpabuf_put_u8(msg, type);
+	return msg;
+}
+
+
+const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len)
+{
+	u16 id, alen;
+	const u8 *pos = buf, *end = buf + len;
+
+	while (end - pos >= 4) {
+		id = WPA_GET_LE16(pos);
+		pos += 2;
+		alen = WPA_GET_LE16(pos);
+		pos += 2;
+		if (alen > end - pos)
+			return NULL;
+		if (id == req_id) {
+			*ret_len = alen;
+			return pos;
+		}
+		pos += alen;
+	}
+
+	return NULL;
+}
+
+
+int dpp_check_attrs(const u8 *buf, size_t len)
+{
+	const u8 *pos, *end;
+
+	pos = buf;
+	end = buf + len;
+	while (end - pos >= 4) {
+		u16 id, alen;
+
+		id = WPA_GET_LE16(pos);
+		pos += 2;
+		alen = WPA_GET_LE16(pos);
+		pos += 2;
+		wpa_printf(MSG_MSGDUMP, "DPP: Attribute ID %04x len %u",
+			   id, alen);
+		if (alen > end - pos) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Truncated message - not enough room for the attribute - dropped");
+			return -1;
+		}
+		pos += alen;
+	}
+
+	if (end != pos) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected octets (%d) after the last attribute",
+			   (int) (end - pos));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info)
+{
+	if (!info)
+		return;
+	os_free(info->uri);
+	os_free(info->info);
+	EVP_PKEY_free(info->pubkey);
+	os_free(info);
+}
+
+
+const char * dpp_bootstrap_type_txt(enum dpp_bootstrap_type type)
+{
+	switch (type) {
+	case DPP_BOOTSTRAP_QR_CODE:
+		return "QRCODE";
+	case DPP_BOOTSTRAP_PKEX:
+		return "PKEX";
+	}
+	return "??";
+}
+
+
+static int dpp_uri_valid_info(const char *info)
+{
+	while (*info) {
+		unsigned char val = *info++;
+
+		if (val < 0x20 || val > 0x7e || val == 0x3b)
+			return 0;
+	}
+
+	return 1;
+}
+
+
+static int dpp_clone_uri(struct dpp_bootstrap_info *bi, const char *uri)
+{
+	bi->uri = os_strdup(uri);
+	return bi->uri ? 0 : -1;
+}
+
+
+int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi,
+			    const char *chan_list)
+{
+	const char *pos = chan_list;
+	int opclass, channel, freq;
+
+	while (pos && *pos && *pos != ';') {
+		opclass = atoi(pos);
+		if (opclass <= 0)
+			goto fail;
+		pos = os_strchr(pos, '/');
+		if (!pos)
+			goto fail;
+		pos++;
+		channel = atoi(pos);
+		if (channel <= 0)
+			goto fail;
+		while (*pos >= '0' && *pos <= '9')
+			pos++;
+		freq = ieee80211_chan_to_freq(NULL, opclass, channel);
+		wpa_printf(MSG_DEBUG,
+			   "DPP: URI channel-list: opclass=%d channel=%d ==> freq=%d",
+			   opclass, channel, freq);
+		if (freq < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Ignore unknown URI channel-list channel (opclass=%d channel=%d)",
+				   opclass, channel);
+		} else if (bi->num_freq == DPP_BOOTSTRAP_MAX_FREQ) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Too many channels in URI channel-list - ignore list");
+			bi->num_freq = 0;
+			break;
+		} else {
+			bi->freq[bi->num_freq++] = freq;
+		}
+
+		if (*pos == ';' || *pos == '\0')
+			break;
+		if (*pos != ',')
+			goto fail;
+		pos++;
+	}
+
+	return 0;
+fail:
+	wpa_printf(MSG_DEBUG, "DPP: Invalid URI channel-list");
+	return -1;
+}
+
+
+int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac)
+{
+	if (!mac)
+		return 0;
+
+	if (hwaddr_aton2(mac, bi->mac_addr) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Invalid URI mac");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: URI mac: " MACSTR, MAC2STR(bi->mac_addr));
+
+	return 0;
+}
+
+
+int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info)
+{
+	const char *end;
+
+	if (!info)
+		return 0;
+
+	end = os_strchr(info, ';');
+	if (!end)
+		end = info + os_strlen(info);
+	bi->info = os_malloc(end - info + 1);
+	if (!bi->info)
+		return -1;
+	os_memcpy(bi->info, info, end - info);
+	bi->info[end - info] = '\0';
+	wpa_printf(MSG_DEBUG, "DPP: URI(information): %s", bi->info);
+	if (!dpp_uri_valid_info(bi->info)) {
+		wpa_printf(MSG_DEBUG, "DPP: Invalid URI information payload");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static const struct dpp_curve_params *
+dpp_get_curve_oid(const ASN1_OBJECT *poid)
+{
+	ASN1_OBJECT *oid;
+	int i;
+
+	for (i = 0; dpp_curves[i].name; i++) {
+		oid = OBJ_txt2obj(dpp_curves[i].name, 0);
+		if (oid && OBJ_cmp(poid, oid) == 0)
+			return &dpp_curves[i];
+	}
+	return NULL;
+}
+
+
+static const struct dpp_curve_params * dpp_get_curve_nid(int nid)
+{
+	int i, tmp;
+
+	if (!nid)
+		return NULL;
+	for (i = 0; dpp_curves[i].name; i++) {
+		tmp = OBJ_txt2nid(dpp_curves[i].name);
+		if (tmp == nid)
+			return &dpp_curves[i];
+	}
+	return NULL;
+}
+
+
+static int dpp_parse_uri_pk(struct dpp_bootstrap_info *bi, const char *info)
+{
+	const char *end;
+	u8 *data;
+	size_t data_len;
+	EVP_PKEY *pkey;
+	const unsigned char *p;
+	int res;
+	X509_PUBKEY *pub = NULL;
+	ASN1_OBJECT *ppkalg;
+	const unsigned char *pk;
+	int ppklen;
+	X509_ALGOR *pa;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+	ASN1_OBJECT *pa_oid;
+#else
+	const ASN1_OBJECT *pa_oid;
+#endif
+	const void *pval;
+	int ptype;
+	const ASN1_OBJECT *poid;
+	char buf[100];
+
+	end = os_strchr(info, ';');
+	if (!end)
+		return -1;
+
+	data = base64_decode((const unsigned char *) info, end - info,
+			     &data_len);
+	if (!data) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid base64 encoding on URI public-key");
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Base64 decoded URI public-key",
+		    data, data_len);
+
+	if (sha256_vector(1, (const u8 **) &data, &data_len,
+			  bi->pubkey_hash) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Public key hash",
+		    bi->pubkey_hash, SHA256_MAC_LEN);
+
+	/* DER encoded ASN.1 SubjectPublicKeyInfo
+	 *
+	 * SubjectPublicKeyInfo  ::=  SEQUENCE  {
+	 *      algorithm            AlgorithmIdentifier,
+	 *      subjectPublicKey     BIT STRING  }
+	 *
+	 * AlgorithmIdentifier  ::=  SEQUENCE  {
+	 *      algorithm               OBJECT IDENTIFIER,
+	 *      parameters              ANY DEFINED BY algorithm OPTIONAL  }
+	 *
+	 * subjectPublicKey = compressed format public key per ANSI X9.63
+	 * algorithm = ecPublicKey (1.2.840.10045.2.1)
+	 * parameters = shall be present and shall be OBJECT IDENTIFIER; e.g.,
+	 *       prime256v1 (1.2.840.10045.3.1.7)
+	 */
+
+	p = data;
+	pkey = d2i_PUBKEY(NULL, &p, data_len);
+	os_free(data);
+
+	if (!pkey) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not parse URI public-key SubjectPublicKeyInfo");
+		return -1;
+	}
+
+	if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: SubjectPublicKeyInfo does not describe an EC key");
+		EVP_PKEY_free(pkey);
+		return -1;
+	}
+
+	res = X509_PUBKEY_set(&pub, pkey);
+	if (res != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: Could not set pubkey");
+		goto fail;
+	}
+
+	res = X509_PUBKEY_get0_param(&ppkalg, &pk, &ppklen, &pa, pub);
+	if (res != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not extract SubjectPublicKeyInfo parameters");
+		goto fail;
+	}
+	res = OBJ_obj2txt(buf, sizeof(buf), ppkalg, 0);
+	if (res < 0 || (size_t) res >= sizeof(buf)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not extract SubjectPublicKeyInfo algorithm");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey algorithm: %s", buf);
+	if (os_strcmp(buf, "id-ecPublicKey") != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unsupported SubjectPublicKeyInfo algorithm");
+		goto fail;
+	}
+
+	X509_ALGOR_get0(&pa_oid, &ptype, (void *) &pval, pa);
+	if (ptype != V_ASN1_OBJECT) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: SubjectPublicKeyInfo parameters did not contain an OID");
+		goto fail;
+	}
+	poid = pval;
+	res = OBJ_obj2txt(buf, sizeof(buf), poid, 0);
+	if (res < 0 || (size_t) res >= sizeof(buf)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not extract SubjectPublicKeyInfo parameters OID");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey parameters: %s", buf);
+	bi->curve = dpp_get_curve_oid(poid);
+	if (!bi->curve) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unsupported SubjectPublicKeyInfo curve: %s",
+			   buf);
+		goto fail;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "DPP: URI subjectPublicKey", pk, ppklen);
+
+	X509_PUBKEY_free(pub);
+	bi->pubkey = pkey;
+	return 0;
+fail:
+	X509_PUBKEY_free(pub);
+	EVP_PKEY_free(pkey);
+	return -1;
+}
+
+
+static struct dpp_bootstrap_info * dpp_parse_uri(const char *uri)
+{
+	const char *pos = uri;
+	const char *end;
+	const char *chan_list = NULL, *mac = NULL, *info = NULL, *pk = NULL;
+	struct dpp_bootstrap_info *bi;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "DPP: URI", uri, os_strlen(uri));
+
+	if (os_strncmp(pos, "DPP:", 4) != 0) {
+		wpa_printf(MSG_INFO, "DPP: Not a DPP URI");
+		return NULL;
+	}
+	pos += 4;
+
+	for (;;) {
+		end = os_strchr(pos, ';');
+		if (!end)
+			break;
+
+		if (end == pos) {
+			/* Handle terminating ";;" and ignore unexpected ";"
+			 * for parsing robustness. */
+			pos++;
+			continue;
+		}
+
+		if (pos[0] == 'C' && pos[1] == ':' && !chan_list)
+			chan_list = pos + 2;
+		else if (pos[0] == 'M' && pos[1] == ':' && !mac)
+			mac = pos + 2;
+		else if (pos[0] == 'I' && pos[1] == ':' && !info)
+			info = pos + 2;
+		else if (pos[0] == 'K' && pos[1] == ':' && !pk)
+			pk = pos + 2;
+		else
+			wpa_hexdump_ascii(MSG_DEBUG,
+					  "DPP: Ignore unrecognized URI parameter",
+					  pos, end - pos);
+		pos = end + 1;
+	}
+
+	if (!pk) {
+		wpa_printf(MSG_INFO, "DPP: URI missing public-key");
+		return NULL;
+	}
+
+	bi = os_zalloc(sizeof(*bi));
+	if (!bi)
+		return NULL;
+
+	if (dpp_clone_uri(bi, uri) < 0 ||
+	    dpp_parse_uri_chan_list(bi, chan_list) < 0 ||
+	    dpp_parse_uri_mac(bi, mac) < 0 ||
+	    dpp_parse_uri_info(bi, info) < 0 ||
+	    dpp_parse_uri_pk(bi, pk) < 0) {
+		dpp_bootstrap_info_free(bi);
+		bi = NULL;
+	}
+
+	return bi;
+}
+
+
+struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri)
+{
+	struct dpp_bootstrap_info *bi;
+
+	bi = dpp_parse_uri(uri);
+	if (bi)
+		bi->type = DPP_BOOTSTRAP_QR_CODE;
+	return bi;
+}
+
+
+static void dpp_debug_print_key(const char *title, EVP_PKEY *key)
+{
+	EC_KEY *eckey;
+	BIO *out;
+	size_t rlen;
+	char *txt;
+	int res;
+	unsigned char *der = NULL;
+	int der_len;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return;
+
+	EVP_PKEY_print_private(out, key, 0, NULL);
+	rlen = BIO_ctrl_pending(out);
+	txt = os_malloc(rlen + 1);
+	if (txt) {
+		res = BIO_read(out, txt, rlen);
+		if (res > 0) {
+			txt[res] = '\0';
+			wpa_printf(MSG_DEBUG, "%s: %s", title, txt);
+		}
+		os_free(txt);
+	}
+	BIO_free(out);
+
+	eckey = EVP_PKEY_get1_EC_KEY(key);
+	if (!eckey)
+		return;
+
+	der_len = i2d_ECPrivateKey(eckey, &der);
+	if (der_len > 0)
+		wpa_hexdump_key(MSG_DEBUG, "DPP: ECPrivateKey", der, der_len);
+	OPENSSL_free(der);
+	if (der_len <= 0) {
+		der = NULL;
+		der_len = i2d_EC_PUBKEY(eckey, &der);
+		if (der_len > 0)
+			wpa_hexdump(MSG_DEBUG, "DPP: EC_PUBKEY", der, der_len);
+		OPENSSL_free(der);
+	}
+
+	EC_KEY_free(eckey);
+}
+
+
+static EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve)
+{
+#ifdef OPENSSL_IS_BORINGSSL
+	EVP_PKEY_CTX *kctx = NULL;
+	const EC_GROUP *group;
+	EC_KEY *ec_params;
+#else
+	EVP_PKEY_CTX *pctx, *kctx = NULL;
+#endif
+	EVP_PKEY *params = NULL, *key = NULL;
+	int nid;
+
+	wpa_printf(MSG_DEBUG, "DPP: Generating a keypair");
+
+	nid = OBJ_txt2nid(curve->name);
+	if (nid == NID_undef) {
+		wpa_printf(MSG_INFO, "DPP: Unsupported curve %s", curve->name);
+		return NULL;
+	}
+#ifdef OPENSSL_IS_BORINGSSL
+	group = EC_GROUP_new_by_curve_name(nid);
+	ec_params = EC_KEY_new();
+	if (!ec_params || EC_KEY_set_group(ec_params, group) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to generate EC_KEY parameters");
+		goto fail;
+	}
+	EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE);
+	params = EVP_PKEY_new();
+	if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to generate EVP_PKEY parameters");
+		goto fail;
+	}
+#else
+	pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
+	if (!pctx ||
+	    EVP_PKEY_paramgen_init(pctx) != 1 ||
+	    EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, nid) != 1 ||
+	    EVP_PKEY_CTX_set_ec_param_enc(pctx, OPENSSL_EC_NAMED_CURVE) != 1 ||
+	    EVP_PKEY_paramgen(pctx, &params) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to generate EVP_PKEY parameters");
+		EVP_PKEY_CTX_free(pctx);
+		goto fail;
+	}
+	EVP_PKEY_CTX_free(pctx);
+#endif
+
+	kctx = EVP_PKEY_CTX_new(params, NULL);
+	if (!kctx ||
+	    EVP_PKEY_keygen_init(kctx) != 1 ||
+	    EVP_PKEY_keygen(kctx, &key) != 1) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to generate EC key");
+		goto fail;
+	}
+
+	if (wpa_debug_show_keys)
+		dpp_debug_print_key("Own generated key", key);
+
+	EVP_PKEY_free(params);
+	EVP_PKEY_CTX_free(kctx);
+	return key;
+fail:
+	EVP_PKEY_CTX_free(kctx);
+	EVP_PKEY_free(params);
+	return NULL;
+}
+
+
+static const struct dpp_curve_params *
+dpp_get_curve_name(const char *name)
+{
+	int i;
+
+	for (i = 0; dpp_curves[i].name; i++) {
+		if (os_strcmp(name, dpp_curves[i].name) == 0 ||
+		    (dpp_curves[i].jwk_crv &&
+		     os_strcmp(name, dpp_curves[i].jwk_crv) == 0))
+			return &dpp_curves[i];
+	}
+	return NULL;
+}
+
+
+static const struct dpp_curve_params *
+dpp_get_curve_jwk_crv(const char *name)
+{
+	int i;
+
+	for (i = 0; dpp_curves[i].name; i++) {
+		if (dpp_curves[i].jwk_crv &&
+		    os_strcmp(name, dpp_curves[i].jwk_crv) == 0)
+			return &dpp_curves[i];
+	}
+	return NULL;
+}
+
+
+static EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve,
+				  const u8 *privkey, size_t privkey_len)
+{
+	EVP_PKEY *pkey;
+	EC_KEY *eckey;
+	const EC_GROUP *group;
+	int nid;
+
+	pkey = EVP_PKEY_new();
+	if (!pkey)
+		return NULL;
+	eckey = d2i_ECPrivateKey(NULL, &privkey, privkey_len);
+	if (!eckey) {
+		wpa_printf(MSG_INFO,
+			   "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		EVP_PKEY_free(pkey);
+		return NULL;
+	}
+	group = EC_KEY_get0_group(eckey);
+	if (!group) {
+		EC_KEY_free(eckey);
+		EVP_PKEY_free(pkey);
+		return NULL;
+	}
+	nid = EC_GROUP_get_curve_name(group);
+	*curve = dpp_get_curve_nid(nid);
+	if (!*curve) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Unsupported curve (nid=%d) in pre-assigned key",
+			   nid);
+		EC_KEY_free(eckey);
+		EVP_PKEY_free(pkey);
+		return NULL;
+	}
+
+	if (EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) {
+		EC_KEY_free(eckey);
+		EVP_PKEY_free(pkey);
+		return NULL;
+	}
+	return pkey;
+}
+
+
+int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi)
+{
+	unsigned char *der = NULL;
+	int der_len;
+	EC_KEY *eckey;
+	int res;
+	size_t len;
+
+	/* Need to get the compressed form of the public key through EC_KEY, so
+	 * cannot use the simpler i2d_PUBKEY() here. */
+	eckey = EVP_PKEY_get1_EC_KEY(bi->pubkey);
+	if (!eckey)
+		return -1;
+	EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED);
+	der_len = i2d_EC_PUBKEY(eckey, &der);
+	EC_KEY_free(eckey);
+	if (der_len <= 0) {
+		wpa_printf(MSG_ERROR,
+			   "DDP: Failed to build DER encoded public key");
+		OPENSSL_free(der);
+		return -1;
+	}
+
+	len = der_len;
+	res = sha256_vector(1, (const u8 **) &der, &len, bi->pubkey_hash);
+	OPENSSL_free(der);
+	if (res < 0)
+		wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+	return res;
+}
+
+
+char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
+		  const u8 *privkey, size_t privkey_len)
+{
+	unsigned char *base64 = NULL;
+	char *pos, *end;
+	size_t len;
+	unsigned char *der = NULL;
+	int der_len;
+	EC_KEY *eckey;
+
+	if (!curve) {
+		bi->curve = &dpp_curves[0];
+	} else {
+		bi->curve = dpp_get_curve_name(curve);
+		if (!bi->curve) {
+			wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
+				   curve);
+			return NULL;
+		}
+	}
+	if (privkey)
+		bi->pubkey = dpp_set_keypair(&bi->curve, privkey, privkey_len);
+	else
+		bi->pubkey = dpp_gen_keypair(bi->curve);
+	if (!bi->pubkey)
+		goto fail;
+	bi->own = 1;
+
+	/* Need to get the compressed form of the public key through EC_KEY, so
+	 * cannot use the simpler i2d_PUBKEY() here. */
+	eckey = EVP_PKEY_get1_EC_KEY(bi->pubkey);
+	if (!eckey)
+		goto fail;
+	EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED);
+	der_len = i2d_EC_PUBKEY(eckey, &der);
+	EC_KEY_free(eckey);
+	if (der_len <= 0) {
+		wpa_printf(MSG_ERROR,
+			   "DDP: Failed to build DER encoded public key");
+		goto fail;
+	}
+
+	len = der_len;
+	if (sha256_vector(1, (const u8 **) &der, &len, bi->pubkey_hash) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+		goto fail;
+	}
+
+	base64 = base64_encode(der, der_len, &len);
+	OPENSSL_free(der);
+	der = NULL;
+	if (!base64)
+		goto fail;
+	pos = (char *) base64;
+	end = pos + len;
+	for (;;) {
+		pos = os_strchr(pos, '\n');
+		if (!pos)
+			break;
+		os_memmove(pos, pos + 1, end - pos);
+	}
+	return (char *) base64;
+fail:
+	os_free(base64);
+	OPENSSL_free(der);
+	return NULL;
+}
+
+
+static int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1,
+			 unsigned int hash_len)
+{
+	u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+	const char *info = "first intermediate key";
+	int res;
+
+	/* k1 = HKDF(<>, "first intermediate key", M.x) */
+
+	/* HKDF-Extract(<>, M.x) */
+	os_memset(salt, 0, hash_len);
+	if (dpp_hmac(hash_len, salt, hash_len, Mx, Mx_len, prk) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=M.x)",
+			prk, hash_len);
+
+	/* HKDF-Expand(PRK, info, L) */
+	res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k1, hash_len);
+	os_memset(prk, 0, hash_len);
+	if (res < 0)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: k1 = HKDF-Expand(PRK, info, L)",
+			k1, hash_len);
+	return 0;
+}
+
+
+static int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2,
+			 unsigned int hash_len)
+{
+	u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+	const char *info = "second intermediate key";
+	int res;
+
+	/* k2 = HKDF(<>, "second intermediate key", N.x) */
+
+	/* HKDF-Extract(<>, N.x) */
+	os_memset(salt, 0, hash_len);
+	res = dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk);
+	if (res < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
+			prk, hash_len);
+
+	/* HKDF-Expand(PRK, info, L) */
+	res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k2, hash_len);
+	os_memset(prk, 0, hash_len);
+	if (res < 0)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: k2 = HKDF-Expand(PRK, info, L)",
+			k2, hash_len);
+	return 0;
+}
+
+
+static int dpp_derive_ke(struct dpp_authentication *auth, u8 *ke,
+			 unsigned int hash_len)
+{
+	size_t nonce_len;
+	u8 nonces[2 * DPP_MAX_NONCE_LEN];
+	const char *info_ke = "DPP Key";
+	u8 prk[DPP_MAX_HASH_LEN];
+	int res;
+	const u8 *addr[3];
+	size_t len[3];
+	size_t num_elem = 0;
+
+	/* ke = HKDF(I-nonce | R-nonce, "DPP Key", M.x | N.x [| L.x]) */
+
+	/* HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x]) */
+	nonce_len = auth->curve->nonce_len;
+	os_memcpy(nonces, auth->i_nonce, nonce_len);
+	os_memcpy(&nonces[nonce_len], auth->r_nonce, nonce_len);
+	addr[num_elem] = auth->Mx;
+	len[num_elem] = auth->secret_len;
+	num_elem++;
+	addr[num_elem] = auth->Nx;
+	len[num_elem] = auth->secret_len;
+	num_elem++;
+	if (auth->peer_bi && auth->own_bi) {
+		addr[num_elem] = auth->Lx;
+		len[num_elem] = auth->secret_len;
+		num_elem++;
+	}
+	res = dpp_hmac_vector(hash_len, nonces, 2 * nonce_len,
+			      num_elem, addr, len, prk);
+	if (res < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
+			prk, hash_len);
+
+	/* HKDF-Expand(PRK, info, L) */
+	res = dpp_hkdf_expand(hash_len, prk, hash_len, info_ke, ke, hash_len);
+	os_memset(prk, 0, hash_len);
+	if (res < 0)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ke = HKDF-Expand(PRK, info, L)",
+			ke, hash_len);
+	return 0;
+}
+
+
+struct dpp_authentication * dpp_auth_init(void *msg_ctx,
+					  struct dpp_bootstrap_info *peer_bi,
+					  struct dpp_bootstrap_info *own_bi,
+					  int configurator)
+{
+	struct dpp_authentication *auth;
+	size_t nonce_len;
+	EVP_PKEY_CTX *ctx = NULL;
+	size_t secret_len;
+	struct wpabuf *msg, *pi = NULL;
+	u8 clear[4 + DPP_MAX_NONCE_LEN + 4 + 1];
+	u8 wrapped_data[4 + DPP_MAX_NONCE_LEN + 4 + 1 + AES_BLOCK_SIZE];
+	u8 *pos;
+	const u8 *addr[2];
+	size_t len[2], siv_len, attr_len;
+	u8 *attr_start, *attr_end;
+
+	auth = os_zalloc(sizeof(*auth));
+	if (!auth)
+		return NULL;
+	auth->msg_ctx = msg_ctx;
+	auth->initiator = 1;
+	auth->configurator = configurator;
+	auth->peer_bi = peer_bi;
+	auth->own_bi = own_bi;
+	auth->curve = peer_bi->curve;
+
+	nonce_len = auth->curve->nonce_len;
+	if (random_get_bytes(auth->i_nonce, nonce_len)) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", auth->i_nonce, nonce_len);
+
+	auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+	if (!auth->own_protocol_key)
+		goto fail;
+
+	pi = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+	if (!pi)
+		goto fail;
+
+	/* ECDH: M = pI * BR */
+	ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, auth->peer_bi->pubkey) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
+	    secret_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	auth->secret_len = secret_len;
+	EVP_PKEY_CTX_free(ctx);
+	ctx = NULL;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
+			auth->Mx, auth->secret_len);
+
+	if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
+			  auth->curve->hash_len) < 0)
+		goto fail;
+
+	/* Build DPP Authentication Request frame attributes */
+	attr_len = 2 * (4 + SHA256_MAC_LEN) + 4 + wpabuf_len(pi) +
+		4 + sizeof(wrapped_data);
+	msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ, attr_len);
+	if (!msg)
+		goto fail;
+	auth->req_msg = msg;
+
+	attr_start = wpabuf_put(msg, 0);
+
+	/* Responder Bootstrapping Key Hash */
+	wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
+	wpabuf_put_le16(msg, SHA256_MAC_LEN);
+	wpabuf_put_data(msg, auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+
+	/* Initiator Bootstrapping Key Hash */
+	wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
+	wpabuf_put_le16(msg, SHA256_MAC_LEN);
+	if (auth->own_bi)
+		wpabuf_put_data(msg, auth->own_bi->pubkey_hash, SHA256_MAC_LEN);
+	else
+		os_memset(wpabuf_put(msg, SHA256_MAC_LEN), 0, SHA256_MAC_LEN);
+
+	/* Initiator Protocol Key */
+	wpabuf_put_le16(msg, DPP_ATTR_I_PROTOCOL_KEY);
+	wpabuf_put_le16(msg, wpabuf_len(pi));
+	wpabuf_put_buf(msg, pi);
+	wpabuf_free(pi);
+	pi = NULL;
+
+	/* Wrapped data ({I-nonce, I-capabilities}k1) */
+	pos = clear;
+	/* I-nonce */
+	WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+	pos += 2;
+	WPA_PUT_LE16(pos, nonce_len);
+	pos += 2;
+	os_memcpy(pos, auth->i_nonce, nonce_len);
+	pos += nonce_len;
+	/* I-capabilities */
+	WPA_PUT_LE16(pos, DPP_ATTR_I_CAPABILITIES);
+	pos += 2;
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	auth->i_capab = configurator ? DPP_CAPAB_CONFIGURATOR :
+		DPP_CAPAB_ENROLLEE;
+	*pos++ = auth->i_capab;
+
+	attr_end = wpabuf_put(msg, 0);
+
+	/* OUI, OUI type, Crypto Suite, DPP frame type */
+	addr[0] = wpabuf_head_u8(msg) + 2;
+	len[0] = 3 + 1 + 1 + 1;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+	/* Attributes before Wrapped Data */
+	addr[1] = attr_start;
+	len[1] = attr_end - attr_start;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+	siv_len = pos - clear;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
+	if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len,
+			    2, addr, len, wrapped_data) < 0)
+		goto fail;
+	siv_len += AES_BLOCK_SIZE;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, siv_len);
+
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, siv_len);
+	wpabuf_put_data(msg, wrapped_data, siv_len);
+
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Authentication Request frame attributes", msg);
+
+	return auth;
+fail:
+	wpabuf_free(pi);
+	EVP_PKEY_CTX_free(ctx);
+	dpp_auth_deinit(auth);
+	return NULL;
+}
+
+
+struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
+				   const char *json)
+{
+	size_t nonce_len;
+	size_t json_len, clear_len;
+	struct wpabuf *clear = NULL, *msg = NULL;
+	u8 *wrapped;
+
+	wpa_printf(MSG_DEBUG, "DPP: Build configuration request");
+
+	nonce_len = auth->curve->nonce_len;
+	if (random_get_bytes(auth->e_nonce, nonce_len)) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to generate E-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", auth->e_nonce, nonce_len);
+	json_len = os_strlen(json);
+	wpa_hexdump_ascii(MSG_DEBUG, "DPP: configAttr JSON", json, json_len);
+
+	/* { E-nonce, configAttrib }ke */
+	clear_len = 4 + nonce_len + 4 + json_len;
+	clear = wpabuf_alloc(clear_len);
+	msg = wpabuf_alloc(4 + clear_len + AES_BLOCK_SIZE);
+	if (!clear || !msg)
+		goto fail;
+
+	/* E-nonce */
+	wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
+	wpabuf_put_le16(clear, nonce_len);
+	wpabuf_put_data(clear, auth->e_nonce, nonce_len);
+
+	/* configAttrib */
+	wpabuf_put_le16(clear, DPP_ATTR_CONFIG_ATTR_OBJ);
+	wpabuf_put_le16(clear, json_len);
+	wpabuf_put_data(clear, json, json_len);
+
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+	wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+	/* No AES-SIV AD */
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+	if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+			    wpabuf_head(clear), wpabuf_len(clear),
+			    0, NULL, NULL, wrapped) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Configuration Request frame attributes", msg);
+	wpabuf_free(clear);
+	return msg;
+
+fail:
+	wpabuf_free(clear);
+	wpabuf_free(msg);
+	return NULL;
+}
+
+
+static void dpp_auth_success(struct dpp_authentication *auth)
+{
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Authentication success - clear temporary keys");
+	os_memset(auth->Mx, 0, sizeof(auth->Mx));
+	os_memset(auth->Nx, 0, sizeof(auth->Nx));
+	os_memset(auth->Lx, 0, sizeof(auth->Lx));
+	os_memset(auth->k1, 0, sizeof(auth->k1));
+	os_memset(auth->k2, 0, sizeof(auth->k2));
+
+	auth->auth_success = 1;
+}
+
+
+static int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth)
+{
+	struct wpabuf *pix, *prx, *bix, *brx;
+	const u8 *addr[7];
+	size_t len[7];
+	size_t i, num_elem = 0;
+	size_t nonce_len;
+	u8 zero = 0;
+	int res = -1;
+
+	/* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+	nonce_len = auth->curve->nonce_len;
+
+	if (auth->initiator) {
+		pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+		prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+		if (auth->own_bi)
+			bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+		else
+			bix = NULL;
+		brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+	} else {
+		pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+		prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+		if (auth->peer_bi)
+			bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+		else
+			bix = NULL;
+		brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+	}
+	if (!pix || !prx || !brx)
+		goto fail;
+
+	addr[num_elem] = auth->i_nonce;
+	len[num_elem] = nonce_len;
+	num_elem++;
+
+	addr[num_elem] = auth->r_nonce;
+	len[num_elem] = nonce_len;
+	num_elem++;
+
+	addr[num_elem] = wpabuf_head(pix);
+	len[num_elem] = wpabuf_len(pix) / 2;
+	num_elem++;
+
+	addr[num_elem] = wpabuf_head(prx);
+	len[num_elem] = wpabuf_len(prx) / 2;
+	num_elem++;
+
+	if (bix) {
+		addr[num_elem] = wpabuf_head(bix);
+		len[num_elem] = wpabuf_len(bix) / 2;
+		num_elem++;
+	}
+
+	addr[num_elem] = wpabuf_head(brx);
+	len[num_elem] = wpabuf_len(brx) / 2;
+	num_elem++;
+
+	addr[num_elem] = &zero;
+	len[num_elem] = 1;
+	num_elem++;
+
+	wpa_printf(MSG_DEBUG, "DPP: R-auth hash components");
+	for (i = 0; i < num_elem; i++)
+		wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
+	res = dpp_hash_vector(auth->curve, num_elem, addr, len, r_auth);
+	if (res == 0)
+		wpa_hexdump(MSG_DEBUG, "DPP: R-auth", r_auth,
+			    auth->curve->hash_len);
+fail:
+	wpabuf_free(pix);
+	wpabuf_free(prx);
+	wpabuf_free(bix);
+	wpabuf_free(brx);
+	return res;
+}
+
+
+static int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth)
+{
+	struct wpabuf *pix = NULL, *prx = NULL, *bix = NULL, *brx = NULL;
+	const u8 *addr[7];
+	size_t len[7];
+	size_t i, num_elem = 0;
+	size_t nonce_len;
+	u8 one = 1;
+	int res = -1;
+
+	/* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
+	nonce_len = auth->curve->nonce_len;
+
+	if (auth->initiator) {
+		pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+		prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+		if (auth->own_bi)
+			bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+		else
+			bix = NULL;
+		if (!auth->peer_bi)
+			goto fail;
+		brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+	} else {
+		pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+		prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+		if (auth->peer_bi)
+			bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+		else
+			bix = NULL;
+		if (!auth->own_bi)
+			goto fail;
+		brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+	}
+	if (!pix || !prx || !brx)
+		goto fail;
+
+	addr[num_elem] = auth->r_nonce;
+	len[num_elem] = nonce_len;
+	num_elem++;
+
+	addr[num_elem] = auth->i_nonce;
+	len[num_elem] = nonce_len;
+	num_elem++;
+
+	addr[num_elem] = wpabuf_head(prx);
+	len[num_elem] = wpabuf_len(prx) / 2;
+	num_elem++;
+
+	addr[num_elem] = wpabuf_head(pix);
+	len[num_elem] = wpabuf_len(pix) / 2;
+	num_elem++;
+
+	addr[num_elem] = wpabuf_head(brx);
+	len[num_elem] = wpabuf_len(brx) / 2;
+	num_elem++;
+
+	if (bix) {
+		addr[num_elem] = wpabuf_head(bix);
+		len[num_elem] = wpabuf_len(bix) / 2;
+		num_elem++;
+	}
+
+	addr[num_elem] = &one;
+	len[num_elem] = 1;
+	num_elem++;
+
+	wpa_printf(MSG_DEBUG, "DPP: I-auth hash components");
+	for (i = 0; i < num_elem; i++)
+		wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
+	res = dpp_hash_vector(auth->curve, num_elem, addr, len, i_auth);
+	if (res == 0)
+		wpa_hexdump(MSG_DEBUG, "DPP: I-auth", i_auth,
+			    auth->curve->hash_len);
+fail:
+	wpabuf_free(pix);
+	wpabuf_free(prx);
+	wpabuf_free(bix);
+	wpabuf_free(brx);
+	return res;
+}
+
+
+static int dpp_auth_derive_l_responder(struct dpp_authentication *auth)
+{
+	const EC_GROUP *group;
+	EC_POINT *l = NULL;
+	EC_KEY *BI = NULL, *bR = NULL, *pR = NULL;
+	const EC_POINT *BI_point;
+	BN_CTX *bnctx;
+	BIGNUM *lx, *sum, *q;
+	const BIGNUM *bR_bn, *pR_bn;
+	int ret = -1;
+	int num_bytes, offset;
+
+	/* L = ((bR + pR) modulo q) * BI */
+
+	bnctx = BN_CTX_new();
+	sum = BN_new();
+	q = BN_new();
+	lx = BN_new();
+	if (!bnctx || !sum || !q || !lx)
+		goto fail;
+	BI = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey);
+	if (!BI)
+		goto fail;
+	BI_point = EC_KEY_get0_public_key(BI);
+	group = EC_KEY_get0_group(BI);
+	if (!group)
+		goto fail;
+
+	bR = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey);
+	pR = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key);
+	if (!bR || !pR)
+		goto fail;
+	bR_bn = EC_KEY_get0_private_key(bR);
+	pR_bn = EC_KEY_get0_private_key(pR);
+	if (!bR_bn || !pR_bn)
+		goto fail;
+	if (EC_GROUP_get_order(group, q, bnctx) != 1 ||
+	    BN_mod_add(sum, bR_bn, pR_bn, q, bnctx) != 1)
+		goto fail;
+	l = EC_POINT_new(group);
+	if (!l ||
+	    EC_POINT_mul(group, l, NULL, BI_point, sum, bnctx) != 1 ||
+	    EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
+						bnctx) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	num_bytes = BN_num_bytes(lx);
+	if ((size_t) num_bytes > auth->secret_len)
+		goto fail;
+	if (auth->secret_len > (size_t) num_bytes)
+		offset = auth->secret_len - num_bytes;
+	else
+		offset = 0;
+
+	os_memset(auth->Lx, 0, offset);
+	BN_bn2bin(lx, auth->Lx + offset);
+	wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
+	ret = 0;
+fail:
+	EC_POINT_clear_free(l);
+	EC_KEY_free(BI);
+	EC_KEY_free(bR);
+	EC_KEY_free(pR);
+	BN_clear_free(lx);
+	BN_clear_free(sum);
+	BN_free(q);
+	BN_CTX_free(bnctx);
+	return ret;
+}
+
+
+static int dpp_auth_derive_l_initiator(struct dpp_authentication *auth)
+{
+	const EC_GROUP *group;
+	EC_POINT *l = NULL, *sum = NULL;
+	EC_KEY *bI = NULL, *BR = NULL, *PR = NULL;
+	const EC_POINT *BR_point, *PR_point;
+	BN_CTX *bnctx;
+	BIGNUM *lx;
+	const BIGNUM *bI_bn;
+	int ret = -1;
+	int num_bytes, offset;
+
+	/* L = bI * (BR + PR) */
+
+	bnctx = BN_CTX_new();
+	lx = BN_new();
+	if (!bnctx || !lx)
+		goto fail;
+	BR = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey);
+	PR = EVP_PKEY_get1_EC_KEY(auth->peer_protocol_key);
+	if (!BR || !PR)
+		goto fail;
+	BR_point = EC_KEY_get0_public_key(BR);
+	PR_point = EC_KEY_get0_public_key(PR);
+
+	bI = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey);
+	if (!bI)
+		goto fail;
+	group = EC_KEY_get0_group(bI);
+	bI_bn = EC_KEY_get0_private_key(bI);
+	if (!group || !bI_bn)
+		goto fail;
+	sum = EC_POINT_new(group);
+	l = EC_POINT_new(group);
+	if (!sum || !l ||
+	    EC_POINT_add(group, sum, BR_point, PR_point, bnctx) != 1 ||
+	    EC_POINT_mul(group, l, NULL, sum, bI_bn, bnctx) != 1 ||
+	    EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
+						bnctx) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	num_bytes = BN_num_bytes(lx);
+	if ((size_t) num_bytes > auth->secret_len)
+		goto fail;
+	if (auth->secret_len > (size_t) num_bytes)
+		offset = auth->secret_len - num_bytes;
+	else
+		offset = 0;
+
+	os_memset(auth->Lx, 0, offset);
+	BN_bn2bin(lx, auth->Lx + offset);
+	wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
+	ret = 0;
+fail:
+	EC_POINT_clear_free(l);
+	EC_KEY_free(bI);
+	EC_KEY_free(BR);
+	EC_KEY_free(PR);
+	BN_clear_free(lx);
+	BN_CTX_free(bnctx);
+	return ret;
+}
+
+
+static int dpp_auth_build_resp(struct dpp_authentication *auth)
+{
+	size_t nonce_len;
+	EVP_PKEY_CTX *ctx = NULL;
+	size_t secret_len;
+	struct wpabuf *msg, *pr = NULL;
+	u8 r_auth[4 + DPP_MAX_HASH_LEN];
+	u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE];
+#define DPP_AUTH_RESP_CLEAR_LEN 2 * (4 + DPP_MAX_NONCE_LEN) + 4 + 1 + \
+		4 + sizeof(wrapped_r_auth)
+	size_t wrapped_r_auth_len;
+	u8 clear[DPP_AUTH_RESP_CLEAR_LEN];
+	u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN + AES_BLOCK_SIZE];
+	u8 *pos;
+	const u8 *addr[2];
+	size_t len[2], siv_len, attr_len;
+	u8 *attr_start, *attr_end;
+
+	wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
+
+	nonce_len = auth->curve->nonce_len;
+	if (random_get_bytes(auth->r_nonce, nonce_len)) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", auth->r_nonce, nonce_len);
+
+	auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+	if (!auth->own_protocol_key)
+		goto fail;
+
+	pr = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+	if (!pr)
+		goto fail;
+
+	/* ECDH: N = pR * PI */
+	ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, auth->peer_protocol_key) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
+	    secret_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	EVP_PKEY_CTX_free(ctx);
+	ctx = NULL;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
+			auth->Nx, auth->secret_len);
+
+	if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
+			  auth->curve->hash_len) < 0)
+		goto fail;
+
+	if (auth->own_bi && auth->peer_bi) {
+		/* Mutual authentication */
+		if (dpp_auth_derive_l_responder(auth) < 0)
+			goto fail;
+	}
+
+	if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0)
+		goto fail;
+
+	/* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+	WPA_PUT_LE16(r_auth, DPP_ATTR_R_AUTH_TAG);
+	WPA_PUT_LE16(&r_auth[2], auth->curve->hash_len);
+	if (dpp_gen_r_auth(auth, r_auth + 4) < 0 ||
+	    aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+			    r_auth, 4 + auth->curve->hash_len,
+			    0, NULL, NULL, wrapped_r_auth) < 0)
+		goto fail;
+	wrapped_r_auth_len = 4 + auth->curve->hash_len + AES_BLOCK_SIZE;
+	wpa_hexdump(MSG_DEBUG, "DPP: {R-auth}ke",
+		    wrapped_r_auth, wrapped_r_auth_len);
+
+	/* Build DPP Authentication Response frame attributes */
+	attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
+		4 + wpabuf_len(pr) + 4 + sizeof(wrapped_data);
+	msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len);
+	if (!msg)
+		goto fail;
+	wpabuf_free(auth->resp_msg);
+	auth->resp_msg = msg;
+
+	attr_start = wpabuf_put(msg, 0);
+
+	/* DPP Status */
+	wpabuf_put_le16(msg, DPP_ATTR_STATUS);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, DPP_STATUS_OK);
+
+	/* Responder Bootstrapping Key Hash */
+	wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
+	wpabuf_put_le16(msg, SHA256_MAC_LEN);
+	wpabuf_put_data(msg, auth->own_bi->pubkey_hash, SHA256_MAC_LEN);
+
+	if (auth->peer_bi) {
+		/* Mutual authentication */
+		/* Initiator Bootstrapping Key Hash */
+		wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
+		wpabuf_put_le16(msg, SHA256_MAC_LEN);
+		wpabuf_put_data(msg, auth->peer_bi->pubkey_hash,
+				SHA256_MAC_LEN);
+	}
+
+	/* Responder Protocol Key */
+	wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
+	wpabuf_put_le16(msg, wpabuf_len(pr));
+	wpabuf_put_buf(msg, pr);
+	wpabuf_free(pr);
+	pr = NULL;
+
+	attr_end = wpabuf_put(msg, 0);
+
+	/* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */
+	pos = clear;
+	/* R-nonce */
+	WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE);
+	pos += 2;
+	WPA_PUT_LE16(pos, nonce_len);
+	pos += 2;
+	os_memcpy(pos, auth->r_nonce, nonce_len);
+	pos += nonce_len;
+	/* I-nonce */
+	WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+	pos += 2;
+	WPA_PUT_LE16(pos, nonce_len);
+	pos += 2;
+	os_memcpy(pos, auth->i_nonce, nonce_len);
+	pos += nonce_len;
+	/* R-capabilities */
+	WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES);
+	pos += 2;
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
+		DPP_CAPAB_ENROLLEE;
+	*pos++ = auth->r_capab;
+	/* {R-auth}ke */
+	WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA);
+	pos += 2;
+	WPA_PUT_LE16(pos, wrapped_r_auth_len);
+	pos += 2;
+	os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len);
+	pos += wrapped_r_auth_len;
+
+	/* OUI, OUI type, Crypto Suite, DPP frame type */
+	addr[0] = wpabuf_head_u8(msg) + 2;
+	len[0] = 3 + 1 + 1 + 1;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+	/* Attributes before Wrapped Data */
+	addr[1] = attr_start;
+	len[1] = attr_end - attr_start;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+	siv_len = pos - clear;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
+	if (aes_siv_encrypt(auth->k2, auth->curve->hash_len, clear, siv_len,
+			    2, addr, len, wrapped_data) < 0)
+		goto fail;
+	siv_len += AES_BLOCK_SIZE;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, siv_len);
+
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, siv_len);
+	wpabuf_put_data(msg, wrapped_data, siv_len);
+
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Authentication Response frame attributes", msg);
+
+	return 0;
+
+fail:
+	wpabuf_free(pr);
+	return -1;
+}
+
+
+static int dpp_auth_build_resp_status(struct dpp_authentication *auth,
+				      enum dpp_status_error status)
+{
+	size_t nonce_len;
+	struct wpabuf *msg;
+#define DPP_AUTH_RESP_CLEAR_LEN2 4 + DPP_MAX_NONCE_LEN + 4 + 1
+	u8 clear[DPP_AUTH_RESP_CLEAR_LEN2];
+	u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN2 + AES_BLOCK_SIZE];
+	u8 *pos;
+	const u8 *addr[2];
+	size_t len[2], siv_len, attr_len;
+	u8 *attr_start, *attr_end;
+
+	wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
+
+	/* Build DPP Authentication Response frame attributes */
+	attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) + 4 + sizeof(wrapped_data);
+	msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len);
+	if (!msg)
+		goto fail;
+	wpabuf_free(auth->resp_msg);
+	auth->resp_msg = msg;
+
+	attr_start = wpabuf_put(msg, 0);
+
+	/* DPP Status */
+	wpabuf_put_le16(msg, DPP_ATTR_STATUS);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, status);
+
+	/* Responder Bootstrapping Key Hash */
+	wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
+	wpabuf_put_le16(msg, SHA256_MAC_LEN);
+	wpabuf_put_data(msg, auth->own_bi->pubkey_hash, SHA256_MAC_LEN);
+
+	if (auth->peer_bi) {
+		/* Mutual authentication */
+		/* Initiator Bootstrapping Key Hash */
+		wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
+		wpabuf_put_le16(msg, SHA256_MAC_LEN);
+		wpabuf_put_data(msg, auth->peer_bi->pubkey_hash,
+				SHA256_MAC_LEN);
+	}
+
+	attr_end = wpabuf_put(msg, 0);
+
+	/* Wrapped data ({I-nonce, R-capabilities}k1) */
+	pos = clear;
+	/* I-nonce */
+	nonce_len = auth->curve->nonce_len;
+	WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+	pos += 2;
+	WPA_PUT_LE16(pos, nonce_len);
+	pos += 2;
+	os_memcpy(pos, auth->i_nonce, nonce_len);
+	pos += nonce_len;
+	/* R-capabilities */
+	WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES);
+	pos += 2;
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
+		DPP_CAPAB_ENROLLEE;
+	*pos++ = auth->r_capab;
+
+	/* OUI, OUI type, Crypto Suite, DPP frame type */
+	addr[0] = wpabuf_head_u8(msg) + 2;
+	len[0] = 3 + 1 + 1 + 1;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+	/* Attributes before Wrapped Data */
+	addr[1] = attr_start;
+	len[1] = attr_end - attr_start;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+	siv_len = pos - clear;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
+	if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len,
+			    2, addr, len, wrapped_data) < 0)
+		goto fail;
+	siv_len += AES_BLOCK_SIZE;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, siv_len);
+
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, siv_len);
+	wpabuf_put_data(msg, wrapped_data, siv_len);
+
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Authentication Response frame attributes", msg);
+
+	return 0;
+
+fail:
+	return -1;
+}
+
+
+struct dpp_authentication *
+dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
+		struct dpp_bootstrap_info *peer_bi,
+		struct dpp_bootstrap_info *own_bi,
+		unsigned int freq, const u8 *hdr, const u8 *attr_start,
+		const u8 *wrapped_data,	u16 wrapped_data_len)
+{
+	EVP_PKEY *pi = NULL;
+	EVP_PKEY_CTX *ctx = NULL;
+	size_t secret_len;
+	const u8 *addr[2];
+	size_t len[2];
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	const u8 *i_proto, *i_nonce, *i_capab, *i_bootstrap;
+	u16 i_proto_len, i_nonce_len, i_capab_len, i_bootstrap_len;
+	struct dpp_authentication *auth = NULL;
+	size_t attr_len;
+
+	if (wrapped_data_len < AES_BLOCK_SIZE)
+		return NULL;
+
+	attr_len = wrapped_data - 4 - attr_start;
+
+	auth = os_zalloc(sizeof(*auth));
+	if (!auth)
+		goto fail;
+	auth->msg_ctx = msg_ctx;
+	auth->peer_bi = peer_bi;
+	auth->own_bi = own_bi;
+	auth->curve = own_bi->curve;
+	auth->curr_freq = freq;
+
+	i_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_PROTOCOL_KEY,
+			       &i_proto_len);
+	if (!i_proto) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing required Initiator Protocol Key attribute");
+		goto fail;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key",
+		    i_proto, i_proto_len);
+
+	/* M = bR * PI */
+	pi = dpp_set_pubkey_point(own_bi->pubkey, i_proto, i_proto_len);
+	if (!pi) {
+		wpa_printf(MSG_DEBUG, "DPP: Invalid Initiator Protocol Key");
+		goto fail;
+	}
+	dpp_debug_print_key("Peer (Initiator) Protocol Key", pi);
+
+	ctx = EVP_PKEY_CTX_new(own_bi->pubkey, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, pi) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
+	    secret_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	auth->secret_len = secret_len;
+	EVP_PKEY_CTX_free(ctx);
+	ctx = NULL;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
+			auth->Mx, auth->secret_len);
+
+	if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
+			  auth->curve->hash_len) < 0)
+		goto fail;
+
+	addr[0] = hdr;
+	len[0] = DPP_HDR_LEN;
+	addr[1] = attr_start;
+	len[1] = attr_len;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		goto fail;
+	if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    2, addr, len, unwrapped) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped, unwrapped_len);
+
+	if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+			       &i_nonce_len);
+	if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing or invalid I-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+	os_memcpy(auth->i_nonce, i_nonce, i_nonce_len);
+
+	i_capab = dpp_get_attr(unwrapped, unwrapped_len,
+			       DPP_ATTR_I_CAPABILITIES,
+			       &i_capab_len);
+	if (!i_capab || i_capab_len < 1) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing or invalid I-capabilities");
+		goto fail;
+	}
+	auth->i_capab = i_capab[0];
+	wpa_printf(MSG_DEBUG, "DPP: I-capabilities: 0x%02x", auth->i_capab);
+
+	bin_clear_free(unwrapped, unwrapped_len);
+	unwrapped = NULL;
+
+	switch (auth->i_capab & DPP_CAPAB_ROLE_MASK) {
+	case DPP_CAPAB_ENROLLEE:
+		if (!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Local policy does not allow Configurator role");
+			goto not_compatible;
+		}
+		wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
+		auth->configurator = 1;
+		break;
+	case DPP_CAPAB_CONFIGURATOR:
+		if (!(dpp_allowed_roles & DPP_CAPAB_ENROLLEE)) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Local policy does not allow Enrollee role");
+			goto not_compatible;
+		}
+		wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
+		auth->configurator = 0;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "DPP: Unexpected role in I-capabilities");
+		goto not_compatible;
+	}
+
+	auth->peer_protocol_key = pi;
+	pi = NULL;
+	if (qr_mutual && !peer_bi && own_bi->type == DPP_BOOTSTRAP_QR_CODE) {
+		char hex[SHA256_MAC_LEN * 2 + 1];
+
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Mutual authentication required with QR Codes, but peer info is not yet available - request more time");
+		if (dpp_auth_build_resp_status(auth,
+					       DPP_STATUS_RESPONSE_PENDING) < 0)
+			goto fail;
+		i_bootstrap = dpp_get_attr(attr_start, attr_len,
+					   DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+					   &i_bootstrap_len);
+		if (i_bootstrap && i_bootstrap_len == SHA256_MAC_LEN) {
+			auth->response_pending = 1;
+			os_memcpy(auth->waiting_pubkey_hash,
+				  i_bootstrap, i_bootstrap_len);
+			wpa_snprintf_hex(hex, sizeof(hex), i_bootstrap,
+					 i_bootstrap_len);
+		} else {
+			hex[0] = '\0';
+		}
+
+		wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_SCAN_PEER_QR_CODE
+			"%s", hex);
+		return auth;
+	}
+	if (dpp_auth_build_resp(auth) < 0)
+		goto fail;
+
+	return auth;
+
+not_compatible:
+	wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
+		"i-capab=0x%02x", auth->i_capab);
+	if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)
+		auth->configurator = 1;
+	else
+		auth->configurator = 0;
+	auth->peer_protocol_key = pi;
+	pi = NULL;
+	if (dpp_auth_build_resp_status(auth, DPP_STATUS_NOT_COMPATIBLE) < 0)
+		goto fail;
+
+	auth->remove_on_tx_status = 1;
+	return auth;
+fail:
+	bin_clear_free(unwrapped, unwrapped_len);
+	EVP_PKEY_free(pi);
+	EVP_PKEY_CTX_free(ctx);
+	dpp_auth_deinit(auth);
+	return NULL;
+}
+
+
+int dpp_notify_new_qr_code(struct dpp_authentication *auth,
+			   struct dpp_bootstrap_info *peer_bi)
+{
+	if (!auth || !auth->response_pending ||
+	    os_memcmp(auth->waiting_pubkey_hash, peer_bi->pubkey_hash,
+		      SHA256_MAC_LEN) != 0)
+		return 0;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: New scanned QR Code has matching public key that was needed to continue DPP Authentication exchange with "
+		   MACSTR, MAC2STR(auth->peer_mac_addr));
+	auth->peer_bi = peer_bi;
+
+	if (dpp_auth_build_resp(auth) < 0)
+		return -1;
+
+	return 1;
+}
+
+
+static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth)
+{
+	struct wpabuf *msg;
+	u8 i_auth[4 + DPP_MAX_HASH_LEN];
+	size_t i_auth_len;
+	const u8 *addr[2];
+	size_t len[2], attr_len;
+	u8 *wrapped_i_auth;
+	u8 *attr_start, *attr_end;
+
+	wpa_printf(MSG_DEBUG, "DPP: Build Authentication Confirmation");
+
+	i_auth_len = 4 + auth->curve->hash_len;
+	/* Build DPP Authentication Confirmation frame attributes */
+	attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
+		4 + i_auth_len + AES_BLOCK_SIZE;
+	msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, attr_len);
+	if (!msg)
+		goto fail;
+
+	attr_start = wpabuf_put(msg, 0);
+
+	/* DPP Status */
+	wpabuf_put_le16(msg, DPP_ATTR_STATUS);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, DPP_STATUS_OK);
+
+	/* Responder Bootstrapping Key Hash */
+	wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
+	wpabuf_put_le16(msg, SHA256_MAC_LEN);
+	wpabuf_put_data(msg, auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+
+	if (auth->own_bi) {
+		/* Mutual authentication */
+		/* Initiator Bootstrapping Key Hash */
+		wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
+		wpabuf_put_le16(msg, SHA256_MAC_LEN);
+		wpabuf_put_data(msg, auth->own_bi->pubkey_hash, SHA256_MAC_LEN);
+	}
+
+	attr_end = wpabuf_put(msg, 0);
+
+	/* OUI, OUI type, Crypto Suite, DPP frame type */
+	addr[0] = wpabuf_head_u8(msg) + 2;
+	len[0] = 3 + 1 + 1 + 1;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+	/* Attributes before Wrapped Data */
+	addr[1] = attr_start;
+	len[1] = attr_end - attr_start;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, i_auth_len + AES_BLOCK_SIZE);
+	wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE);
+	/* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
+	WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG);
+	WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len);
+	if (dpp_gen_i_auth(auth, i_auth + 4) < 0 ||
+	    aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+			    i_auth, i_auth_len,
+			    2, addr, len, wrapped_i_auth) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke",
+		    wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE);
+
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Authentication Confirmation frame attributes",
+			msg);
+	dpp_auth_success(auth);
+
+	return msg;
+
+fail:
+	return NULL;
+}
+
+
+static void
+dpp_auth_resp_rx_status(struct dpp_authentication *auth, const u8 *hdr,
+			const u8 *attr_start, size_t attr_len,
+			const u8 *wrapped_data, u16 wrapped_data_len,
+			enum dpp_status_error status)
+{
+	const u8 *addr[2];
+	size_t len[2];
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	const u8 *i_nonce, *r_capab;
+	u16 i_nonce_len, r_capab_len;
+
+	if (status == DPP_STATUS_NOT_COMPATIBLE) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Responder reported incompatible roles");
+	} else if (status == DPP_STATUS_RESPONSE_PENDING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Responder reported more time needed");
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Responder reported failure (status %d)",
+			   status);
+		return;
+	}
+
+	addr[0] = hdr;
+	len[0] = DPP_HDR_LEN;
+	addr[1] = attr_start;
+	len[1] = attr_len;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		goto fail;
+	if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    2, addr, len, unwrapped) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped, unwrapped_len);
+
+	if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+			       &i_nonce_len);
+	if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing or invalid I-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+	if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: I-nonce mismatch");
+		goto fail;
+	}
+
+	r_capab = dpp_get_attr(unwrapped, unwrapped_len,
+			       DPP_ATTR_R_CAPABILITIES,
+			       &r_capab_len);
+	if (!r_capab || r_capab_len < 1) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing or invalid R-capabilities");
+		goto fail;
+	}
+	auth->r_capab = r_capab[0];
+	wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
+	if (status == DPP_STATUS_NOT_COMPATIBLE) {
+		wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
+			"r-capab=0x%02x", auth->r_capab);
+	} else if (status == DPP_STATUS_RESPONSE_PENDING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Continue waiting for full DPP Authentication Response");
+		wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_RESPONSE_PENDING);
+	}
+fail:
+	bin_clear_free(unwrapped, unwrapped_len);
+}
+
+
+struct wpabuf *
+dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
+		 const u8 *attr_start, size_t attr_len)
+{
+	EVP_PKEY *pr;
+	EVP_PKEY_CTX *ctx = NULL;
+	size_t secret_len;
+	const u8 *addr[2];
+	size_t len[2];
+	u8 *unwrapped = NULL, *unwrapped2 = NULL;
+	size_t unwrapped_len = 0, unwrapped2_len = 0;
+	const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *r_proto,
+		*r_nonce, *i_nonce, *r_capab, *wrapped2, *r_auth;
+	u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
+		r_proto_len, r_nonce_len, i_nonce_len, r_capab_len,
+		wrapped2_len, r_auth_len;
+	u8 r_auth2[DPP_MAX_HASH_LEN];
+
+	wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing required Wrapped data attribute");
+		return NULL;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
+		    wrapped_data, wrapped_data_len);
+
+	if (wrapped_data_len < AES_BLOCK_SIZE)
+		return NULL;
+
+	attr_len = wrapped_data - 4 - attr_start;
+
+	r_bootstrap = dpp_get_attr(attr_start, attr_len,
+				   DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+				   &r_bootstrap_len);
+	if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
+		return NULL;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
+		    r_bootstrap, r_bootstrap_len);
+	if (os_memcmp(r_bootstrap, auth->peer_bi->pubkey_hash,
+		      SHA256_MAC_LEN) != 0) {
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Expected Responder Bootstrapping Key Hash",
+			    auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+		return NULL;
+	}
+
+	i_bootstrap = dpp_get_attr(attr_start, attr_len,
+				   DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+				   &i_bootstrap_len);
+	if (i_bootstrap) {
+		if (i_bootstrap_len != SHA256_MAC_LEN) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Invalid Initiator Bootstrapping Key Hash attribute");
+			return NULL;
+		}
+		wpa_hexdump(MSG_MSGDUMP,
+			    "DPP: Initiator Bootstrapping Key Hash",
+			    i_bootstrap, i_bootstrap_len);
+		if (!auth->own_bi ||
+		    os_memcmp(i_bootstrap, auth->own_bi->pubkey_hash,
+			      SHA256_MAC_LEN) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Initiator Bootstrapping Key Hash attribute did not match");
+			return NULL;
+		}
+	}
+
+	status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
+			      &status_len);
+	if (!status || status_len < 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required DPP Status attribute");
+		return NULL;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+	auth->auth_resp_status = status[0];
+	if (status[0] != DPP_STATUS_OK) {
+		dpp_auth_resp_rx_status(auth, hdr, attr_start,
+					attr_len, wrapped_data,
+					wrapped_data_len, status[0]);
+		return NULL;
+	}
+
+	r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY,
+			       &r_proto_len);
+	if (!r_proto) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing required Responder Protocol Key attribute");
+		return NULL;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key",
+		    r_proto, r_proto_len);
+
+	/* N = pI * PR */
+	pr = dpp_set_pubkey_point(auth->own_protocol_key, r_proto, r_proto_len);
+	if (!pr) {
+		wpa_printf(MSG_DEBUG, "DPP: Invalid Responder Protocol Key");
+		return NULL;
+	}
+	dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
+
+	ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, pr) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
+	    secret_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	EVP_PKEY_CTX_free(ctx);
+	ctx = NULL;
+	auth->peer_protocol_key = pr;
+	pr = NULL;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
+			auth->Nx, auth->secret_len);
+
+	if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
+			  auth->curve->hash_len) < 0)
+		goto fail;
+
+	addr[0] = hdr;
+	len[0] = DPP_HDR_LEN;
+	addr[1] = attr_start;
+	len[1] = attr_len;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		goto fail;
+	if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    2, addr, len, unwrapped) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped, unwrapped_len);
+
+	if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
+			       &r_nonce_len);
+	if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing or invalid R-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
+	os_memcpy(auth->r_nonce, r_nonce, r_nonce_len);
+
+	i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+			       &i_nonce_len);
+	if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing or invalid I-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+	if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: I-nonce mismatch");
+		goto fail;
+	}
+
+	if (auth->own_bi && auth->peer_bi) {
+		/* Mutual authentication */
+		if (dpp_auth_derive_l_initiator(auth) < 0)
+			goto fail;
+	}
+
+	if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0)
+		goto fail;
+
+	r_capab = dpp_get_attr(unwrapped, unwrapped_len,
+			       DPP_ATTR_R_CAPABILITIES,
+			       &r_capab_len);
+	if (!r_capab || r_capab_len < 1) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing or invalid R-capabilities");
+		goto fail;
+	}
+	auth->r_capab = r_capab[0];
+	wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
+	if ((auth->configurator && (auth->r_capab & DPP_CAPAB_CONFIGURATOR)) ||
+	    (!auth->configurator && (auth->r_capab & DPP_CAPAB_ENROLLEE))) {
+		wpa_printf(MSG_DEBUG, "DPP: Incompatible role selection");
+		goto fail;
+	}
+
+	wrapped2 = dpp_get_attr(unwrapped, unwrapped_len,
+				DPP_ATTR_WRAPPED_DATA, &wrapped2_len);
+	if (!wrapped2 || wrapped2_len < AES_BLOCK_SIZE) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid Secondary Wrapped Data");
+		goto fail;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped2, wrapped2_len);
+	unwrapped2_len = wrapped2_len - AES_BLOCK_SIZE;
+	unwrapped2 = os_malloc(unwrapped2_len);
+	if (!unwrapped2)
+		goto fail;
+	if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+			    wrapped2, wrapped2_len,
+			    0, NULL, NULL, unwrapped2) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped2, unwrapped2_len);
+
+	if (dpp_check_attrs(unwrapped2, unwrapped2_len) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in secondary unwrapped data");
+		goto fail;
+	}
+
+	r_auth = dpp_get_attr(unwrapped2, unwrapped2_len, DPP_ATTR_R_AUTH_TAG,
+			       &r_auth_len);
+	if (!r_auth || r_auth_len != auth->curve->hash_len) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid Responder Authenticating Tag");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Received Responder Authenticating Tag",
+		    r_auth, r_auth_len);
+	/* R-auth' = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+	if (dpp_gen_r_auth(auth, r_auth2) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: Calculated Responder Authenticating Tag",
+		    r_auth2, r_auth_len);
+	if (os_memcmp(r_auth, r_auth2, r_auth_len) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Mismatching Responder Authenticating Tag");
+		goto fail;
+	}
+
+	bin_clear_free(unwrapped, unwrapped_len);
+	bin_clear_free(unwrapped2, unwrapped2_len);
+
+	return dpp_auth_build_conf(auth);
+
+fail:
+	bin_clear_free(unwrapped, unwrapped_len);
+	bin_clear_free(unwrapped2, unwrapped2_len);
+	EVP_PKEY_free(pr);
+	EVP_PKEY_CTX_free(ctx);
+	return NULL;
+}
+
+
+int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
+		     const u8 *attr_start, size_t attr_len)
+{
+	const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *i_auth;
+	u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
+		i_auth_len;
+	const u8 *addr[2];
+	size_t len[2];
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	u8 i_auth2[DPP_MAX_HASH_LEN];
+
+	wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing required Wrapped data attribute");
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
+		    wrapped_data, wrapped_data_len);
+
+	if (wrapped_data_len < AES_BLOCK_SIZE)
+		return -1;
+
+	attr_len = wrapped_data - 4 - attr_start;
+
+	r_bootstrap = dpp_get_attr(attr_start, attr_len,
+				   DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+				   &r_bootstrap_len);
+	if (!r_bootstrap || r_bootstrap > wrapped_data ||
+	    r_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
+		    r_bootstrap, r_bootstrap_len);
+	if (os_memcmp(r_bootstrap, auth->own_bi->pubkey_hash,
+		      SHA256_MAC_LEN) != 0) {
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Expected Responder Bootstrapping Key Hash",
+			    auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+		return -1;
+	}
+
+	i_bootstrap = dpp_get_attr(attr_start, attr_len,
+				   DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+				   &i_bootstrap_len);
+	if (i_bootstrap) {
+		if (i_bootstrap > wrapped_data ||
+		    i_bootstrap_len != SHA256_MAC_LEN) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Invalid Initiator Bootstrapping Key Hash attribute");
+			return -1;
+		}
+		wpa_hexdump(MSG_MSGDUMP,
+			    "DPP: Initiator Bootstrapping Key Hash",
+			    i_bootstrap, i_bootstrap_len);
+		if (!auth->peer_bi ||
+		    os_memcmp(i_bootstrap, auth->peer_bi->pubkey_hash,
+			      SHA256_MAC_LEN) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Initiator Bootstrapping Key Hash attribute did not match");
+			return -1;
+		}
+	}
+
+	status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
+			      &status_len);
+	if (!status || status_len < 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required DPP Status attribute");
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+	if (status[0] != DPP_STATUS_OK) {
+		wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
+		return -1;
+	}
+
+	addr[0] = hdr;
+	len[0] = DPP_HDR_LEN;
+	addr[1] = attr_start;
+	len[1] = attr_len;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		return -1;
+	if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    2, addr, len, unwrapped) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped, unwrapped_len);
+
+	if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	i_auth = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
+			      &i_auth_len);
+	if (!i_auth || i_auth_len != auth->curve->hash_len) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid Initiator Authenticating Tag");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Received Initiator Authenticating Tag",
+		    i_auth, i_auth_len);
+	/* I-auth' = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
+	if (dpp_gen_i_auth(auth, i_auth2) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: Calculated Initiator Authenticating Tag",
+		    i_auth2, i_auth_len);
+	if (os_memcmp(i_auth, i_auth2, i_auth_len) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Mismatching Initiator Authenticating Tag");
+		goto fail;
+	}
+
+	bin_clear_free(unwrapped, unwrapped_len);
+	dpp_auth_success(auth);
+	return 0;
+fail:
+	bin_clear_free(unwrapped, unwrapped_len);
+	return -1;
+}
+
+
+void dpp_configuration_free(struct dpp_configuration *conf)
+{
+	if (!conf)
+		return;
+	str_clear_free(conf->passphrase);
+	bin_clear_free(conf, sizeof(*conf));
+}
+
+
+void dpp_auth_deinit(struct dpp_authentication *auth)
+{
+	if (!auth)
+		return;
+	dpp_configuration_free(auth->conf_ap);
+	dpp_configuration_free(auth->conf_sta);
+	EVP_PKEY_free(auth->own_protocol_key);
+	EVP_PKEY_free(auth->peer_protocol_key);
+	wpabuf_free(auth->req_msg);
+	wpabuf_free(auth->resp_msg);
+	wpabuf_free(auth->conf_req);
+	os_free(auth->connector);
+	wpabuf_free(auth->net_access_key);
+	wpabuf_free(auth->c_sign_key);
+#ifdef CONFIG_TESTING_OPTIONS
+	os_free(auth->config_obj_override);
+	os_free(auth->discovery_override);
+	os_free(auth->groups_override);
+#endif /* CONFIG_TESTING_OPTIONS */
+	bin_clear_free(auth, sizeof(*auth));
+}
+
+
+static struct wpabuf *
+dpp_build_conf_start(struct dpp_authentication *auth,
+		     struct dpp_configuration *conf, size_t tailroom)
+{
+	struct wpabuf *buf;
+	char ssid[6 * sizeof(conf->ssid) + 1];
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (auth->discovery_override)
+		tailroom += os_strlen(auth->discovery_override);
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	buf = wpabuf_alloc(200 + tailroom);
+	if (!buf)
+		return NULL;
+	wpabuf_put_str(buf, "{\"wi-fi_tech\":\"infra\",\"discovery\":");
+#ifdef CONFIG_TESTING_OPTIONS
+	if (auth->discovery_override) {
+		wpa_printf(MSG_DEBUG, "DPP: TESTING - discovery override: '%s'",
+			   auth->discovery_override);
+		wpabuf_put_str(buf, auth->discovery_override);
+		wpabuf_put_u8(buf, ',');
+		return buf;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	wpabuf_put_str(buf, "{\"ssid\":\"");
+	json_escape_string(ssid, sizeof(ssid),
+			   (const char *) conf->ssid, conf->ssid_len);
+	wpabuf_put_str(buf, ssid);
+	wpabuf_put_str(buf, "\"");
+	/* TODO: optional channel information */
+	wpabuf_put_str(buf, "},");
+
+	return buf;
+}
+
+
+static int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len)
+{
+	int num_bytes, offset;
+
+	num_bytes = BN_num_bytes(bn);
+	if ((size_t) num_bytes > len)
+		return -1;
+	offset = len - num_bytes;
+	os_memset(pos, 0, offset);
+	BN_bn2bin(bn, pos + offset);
+	return 0;
+}
+
+
+static int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key,
+			 const char *kid, const struct dpp_curve_params *curve)
+{
+	struct wpabuf *pub;
+	const u8 *pos;
+	char *x = NULL, *y = NULL;
+	int ret = -1;
+
+	pub = dpp_get_pubkey_point(key, 0);
+	if (!pub)
+		goto fail;
+	pos = wpabuf_head(pub);
+	x = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0);
+	pos += curve->prime_len;
+	y = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0);
+	if (!x || !y)
+		goto fail;
+
+	wpabuf_put_str(buf, "\"");
+	wpabuf_put_str(buf, name);
+	wpabuf_put_str(buf, "\":{\"kty\":\"EC\",\"crv\":\"");
+	wpabuf_put_str(buf, curve->jwk_crv);
+	wpabuf_put_str(buf, "\",\"x\":\"");
+	wpabuf_put_str(buf, x);
+	wpabuf_put_str(buf, "\",\"y\":\"");
+	wpabuf_put_str(buf, y);
+	if (kid) {
+		wpabuf_put_str(buf, "\",\"kid\":\"");
+		wpabuf_put_str(buf, kid);
+	}
+	wpabuf_put_str(buf, "\"}");
+	ret = 0;
+fail:
+	wpabuf_free(pub);
+	os_free(x);
+	os_free(y);
+	return ret;
+}
+
+
+static struct wpabuf *
+dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap,
+		       struct dpp_configuration *conf)
+{
+	struct wpabuf *buf = NULL;
+	char *signed1 = NULL, *signed2 = NULL, *signed3 = NULL;
+	size_t tailroom;
+	const struct dpp_curve_params *curve;
+	char jws_prot_hdr[100];
+	size_t signed1_len, signed2_len, signed3_len;
+	struct wpabuf *dppcon = NULL;
+	unsigned char *signature = NULL;
+	const unsigned char *p;
+	size_t signature_len;
+	EVP_MD_CTX *md_ctx = NULL;
+	ECDSA_SIG *sig = NULL;
+	char *dot = ".";
+	const EVP_MD *sign_md;
+	const BIGNUM *r, *s;
+	size_t extra_len = 1000;
+
+	if (!auth->conf) {
+		wpa_printf(MSG_INFO,
+			   "DPP: No configurator specified - cannot generate DPP config object");
+		goto fail;
+	}
+	curve = auth->conf->curve;
+	if (curve->hash_len == SHA256_MAC_LEN) {
+		sign_md = EVP_sha256();
+	} else if (curve->hash_len == SHA384_MAC_LEN) {
+		sign_md = EVP_sha384();
+	} else if (curve->hash_len == SHA512_MAC_LEN) {
+		sign_md = EVP_sha512();
+	} else {
+		wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm");
+		goto fail;
+	}
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (auth->groups_override)
+		extra_len += os_strlen(auth->groups_override);
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* Connector (JSON dppCon object) */
+	dppcon = wpabuf_alloc(extra_len + 2 * auth->curve->prime_len * 4 / 3);
+	if (!dppcon)
+		goto fail;
+#ifdef CONFIG_TESTING_OPTIONS
+	if (auth->groups_override) {
+		wpabuf_put_u8(dppcon, '{');
+		if (auth->groups_override) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: TESTING - groups override: '%s'",
+				   auth->groups_override);
+			wpabuf_put_str(dppcon, "\"groups\":");
+			wpabuf_put_str(dppcon, auth->groups_override);
+			wpabuf_put_u8(dppcon, ',');
+		}
+		goto skip_groups;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	wpabuf_put_str(dppcon, "{\"groups\":[{\"groupId\":\"*\",");
+	wpabuf_printf(dppcon, "\"netRole\":\"%s\"}],", ap ? "ap" : "sta");
+#ifdef CONFIG_TESTING_OPTIONS
+skip_groups:
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL,
+			  auth->curve) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK");
+		goto fail;
+	}
+	if (conf->netaccesskey_expiry) {
+		struct os_tm tm;
+
+		if (os_gmtime(conf->netaccesskey_expiry, &tm) < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Failed to generate expiry string");
+			goto fail;
+		}
+		wpabuf_printf(dppcon,
+			      ",\"expiry\":\"%04u-%02u-%02uT%02u:%02u:%02uZ\"",
+			      tm.year, tm.month, tm.day,
+			      tm.hour, tm.min, tm.sec);
+	}
+	wpabuf_put_u8(dppcon, '}');
+	wpa_printf(MSG_DEBUG, "DPP: dppCon: %s",
+		   (const char *) wpabuf_head(dppcon));
+
+	os_snprintf(jws_prot_hdr, sizeof(jws_prot_hdr),
+		    "{\"typ\":\"dppCon\",\"kid\":\"%s\",\"alg\":\"%s\"}",
+		    auth->conf->kid, curve->jws_alg);
+	signed1 = (char *) base64_url_encode((unsigned char *) jws_prot_hdr,
+					     os_strlen(jws_prot_hdr),
+					     &signed1_len, 0);
+	signed2 = (char *) base64_url_encode(wpabuf_head(dppcon),
+					     wpabuf_len(dppcon),
+					     &signed2_len, 0);
+	if (!signed1 || !signed2)
+		goto fail;
+
+	md_ctx = EVP_MD_CTX_create();
+	if (!md_ctx)
+		goto fail;
+
+	ERR_clear_error();
+	if (EVP_DigestSignInit(md_ctx, NULL, sign_md, NULL,
+			       auth->conf->csign) != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignInit failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	if (EVP_DigestSignUpdate(md_ctx, signed1, signed1_len) != 1 ||
+	    EVP_DigestSignUpdate(md_ctx, dot, 1) != 1 ||
+	    EVP_DigestSignUpdate(md_ctx, signed2, signed2_len) != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignUpdate failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	if (EVP_DigestSignFinal(md_ctx, NULL, &signature_len) != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	signature = os_malloc(signature_len);
+	if (!signature)
+		goto fail;
+	if (EVP_DigestSignFinal(md_ctx, signature, &signature_len) != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (DER)",
+		    signature, signature_len);
+	/* Convert to raw coordinates r,s */
+	p = signature;
+	sig = d2i_ECDSA_SIG(NULL, &p, signature_len);
+	if (!sig)
+		goto fail;
+	ECDSA_SIG_get0(sig, &r, &s);
+	if (dpp_bn2bin_pad(r, signature, curve->prime_len) < 0 ||
+	    dpp_bn2bin_pad(s, signature + curve->prime_len,
+			   curve->prime_len) < 0)
+		goto fail;
+	signature_len = 2 * curve->prime_len;
+	wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (raw r,s)",
+		    signature, signature_len);
+	signed3 = (char *) base64_url_encode(signature, signature_len,
+					     &signed3_len, 0);
+	if (!signed3)
+		goto fail;
+
+	tailroom = 1000;
+	tailroom += 2 * curve->prime_len * 4 / 3 + os_strlen(auth->conf->kid);
+	tailroom += signed1_len + signed2_len + signed3_len;
+	buf = dpp_build_conf_start(auth, conf, tailroom);
+	if (!buf)
+		return NULL;
+
+	wpabuf_put_str(buf, "\"cred\":{\"akm\":\"dpp\",\"signedConnector\":\"");
+	wpabuf_put_str(buf, signed1);
+	wpabuf_put_u8(buf, '.');
+	wpabuf_put_str(buf, signed2);
+	wpabuf_put_u8(buf, '.');
+	wpabuf_put_str(buf, signed3);
+	wpabuf_put_str(buf, "\",");
+	if (dpp_build_jwk(buf, "csign", auth->conf->csign, auth->conf->kid,
+			  curve) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to build csign JWK");
+		goto fail;
+	}
+
+	wpabuf_put_str(buf, "}}");
+
+	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object",
+			      wpabuf_head(buf), wpabuf_len(buf));
+
+out:
+	EVP_MD_CTX_destroy(md_ctx);
+	ECDSA_SIG_free(sig);
+	os_free(signed1);
+	os_free(signed2);
+	os_free(signed3);
+	os_free(signature);
+	wpabuf_free(dppcon);
+	return buf;
+fail:
+	wpa_printf(MSG_DEBUG, "DPP: Failed to build configuration object");
+	wpabuf_free(buf);
+	buf = NULL;
+	goto out;
+}
+
+
+static struct wpabuf *
+dpp_build_conf_obj_legacy(struct dpp_authentication *auth, int ap,
+			  struct dpp_configuration *conf)
+{
+	struct wpabuf *buf;
+
+	buf = dpp_build_conf_start(auth, conf, 1000);
+	if (!buf)
+		return NULL;
+
+	wpabuf_put_str(buf, "\"cred\":{\"akm\":\"psk\",");
+	if (conf->passphrase) {
+		char pass[63 * 6 + 1];
+
+		if (os_strlen(conf->passphrase) > 63) {
+			wpabuf_free(buf);
+			return NULL;
+		}
+
+		json_escape_string(pass, sizeof(pass), conf->passphrase,
+				   os_strlen(conf->passphrase));
+		wpabuf_put_str(buf, "\"pass\":\"");
+		wpabuf_put_str(buf, pass);
+		wpabuf_put_str(buf, "\"");
+	} else {
+		char psk[2 * sizeof(conf->psk) + 1];
+
+		wpa_snprintf_hex(psk, sizeof(psk),
+				 conf->psk, sizeof(conf->psk));
+		wpabuf_put_str(buf, "\"psk_hex\":\"");
+		wpabuf_put_str(buf, psk);
+		wpabuf_put_str(buf, "\"");
+	}
+	wpabuf_put_str(buf, "}}");
+
+	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)",
+			      wpabuf_head(buf), wpabuf_len(buf));
+
+	return buf;
+}
+
+
+static struct wpabuf *
+dpp_build_conf_obj(struct dpp_authentication *auth, int ap)
+{
+	struct dpp_configuration *conf;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (auth->config_obj_override) {
+		wpa_printf(MSG_DEBUG, "DPP: Testing - Config Object override");
+		return wpabuf_alloc_copy(auth->config_obj_override,
+					 os_strlen(auth->config_obj_override));
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	conf = ap ? auth->conf_ap : auth->conf_sta;
+	if (!conf) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No configuration available for Enrollee(%s) - reject configuration request",
+			   ap ? "ap" : "sta");
+		return NULL;
+	}
+
+	if (conf->dpp)
+		return dpp_build_conf_obj_dpp(auth, ap, conf);
+	return dpp_build_conf_obj_legacy(auth, ap, conf);
+}
+
+
+static struct wpabuf *
+dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
+		    u16 e_nonce_len, int ap)
+{
+	struct wpabuf *conf;
+	size_t clear_len;
+	struct wpabuf *clear = NULL, *msg = NULL;
+	u8 *wrapped;
+	const u8 *addr[1];
+	size_t len[1];
+	enum dpp_status_error status;
+
+	conf = dpp_build_conf_obj(auth, ap);
+	if (conf) {
+		wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON",
+				  wpabuf_head(conf), wpabuf_len(conf));
+	}
+	status = conf ? DPP_STATUS_OK : DPP_STATUS_CONFIGURE_FAILURE;
+
+	/* { E-nonce, configurationObject}ke */
+	clear_len = 4 + e_nonce_len;
+	if (conf)
+		clear_len += 4 + wpabuf_len(conf);
+	clear = wpabuf_alloc(clear_len);
+	msg = wpabuf_alloc(4 + 1 + 4 + clear_len + AES_BLOCK_SIZE);
+	if (!clear || !msg)
+		goto fail;
+
+	/* E-nonce */
+	wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
+	wpabuf_put_le16(clear, e_nonce_len);
+	wpabuf_put_data(clear, e_nonce, e_nonce_len);
+
+	if (conf) {
+		wpabuf_put_le16(clear, DPP_ATTR_CONFIG_OBJ);
+		wpabuf_put_le16(clear, wpabuf_len(conf));
+		wpabuf_put_buf(clear, conf);
+		wpabuf_free(conf);
+		conf = NULL;
+	}
+
+	/* DPP Status */
+	wpabuf_put_le16(msg, DPP_ATTR_STATUS);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, status);
+
+	addr[0] = wpabuf_head(msg);
+	len[0] = wpabuf_len(msg);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
+
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+	wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+	if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+			    wpabuf_head(clear), wpabuf_len(clear),
+			    1, addr, len, wrapped) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+	wpabuf_free(clear);
+	clear = NULL;
+
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Configuration Response attributes", msg);
+	return msg;
+fail:
+	wpabuf_free(conf);
+	wpabuf_free(clear);
+	wpabuf_free(msg);
+	return NULL;
+}
+
+
+struct wpabuf *
+dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
+		size_t attr_len)
+{
+	const u8 *wrapped_data, *e_nonce, *config_attr;
+	u16 wrapped_data_len, e_nonce_len, config_attr_len;
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	struct wpabuf *resp = NULL;
+	struct json_token *root = NULL, *token;
+	int ap;
+
+	if (dpp_check_attrs(attr_start, attr_len) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in config request");
+		return NULL;
+	}
+
+	wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Wrapped data attribute");
+		return NULL;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		return NULL;
+	if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    0, NULL, NULL, unwrapped) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped, unwrapped_len);
+
+	if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	e_nonce = dpp_get_attr(unwrapped, unwrapped_len,
+			       DPP_ATTR_ENROLLEE_NONCE,
+			       &e_nonce_len);
+	if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid Enrollee Nonce attribute");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
+
+	config_attr = dpp_get_attr(unwrapped, unwrapped_len,
+				   DPP_ATTR_CONFIG_ATTR_OBJ,
+				   &config_attr_len);
+	if (!config_attr) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid Config Attributes attribute");
+		goto fail;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "DPP: Config Attributes",
+			  config_attr, config_attr_len);
+
+	root = json_parse((const char *) config_attr, config_attr_len);
+	if (!root) {
+		wpa_printf(MSG_DEBUG, "DPP: Could not parse Config Attributes");
+		goto fail;
+	}
+
+	token = json_get_member(root, "name");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG, "DPP: No Config Attributes - name");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Enrollee name = '%s'", token->string);
+
+	token = json_get_member(root, "wi-fi_tech");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG, "DPP: No Config Attributes - wi-fi_tech");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: wi-fi_tech = '%s'", token->string);
+	if (os_strcmp(token->string, "infra") != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech '%s'",
+			   token->string);
+		goto fail;
+	}
+
+	token = json_get_member(root, "netRole");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG, "DPP: No Config Attributes - netRole");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: netRole = '%s'", token->string);
+	if (os_strcmp(token->string, "sta") == 0) {
+		ap = 0;
+	} else if (os_strcmp(token->string, "ap") == 0) {
+		ap = 1;
+	} else {
+		wpa_printf(MSG_DEBUG, "DPP: Unsupported netRole '%s'",
+			   token->string);
+		goto fail;
+	}
+
+	resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, ap);
+
+fail:
+	json_free(root);
+	os_free(unwrapped);
+	return resp;
+}
+
+
+static struct wpabuf *
+dpp_parse_jws_prot_hdr(const struct dpp_curve_params *curve,
+		       const u8 *prot_hdr, u16 prot_hdr_len,
+		       const EVP_MD **ret_md)
+{
+	struct json_token *root, *token;
+	struct wpabuf *kid = NULL;
+
+	root = json_parse((const char *) prot_hdr, prot_hdr_len);
+	if (!root) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: JSON parsing failed for JWS Protected Header");
+		goto fail;
+	}
+
+	if (root->type != JSON_OBJECT) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: JWS Protected Header root is not an object");
+		goto fail;
+	}
+
+	token = json_get_member(root, "typ");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG, "DPP: No typ string value found");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header typ=%s",
+		   token->string);
+	if (os_strcmp(token->string, "dppCon") != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unsupported JWS Protected Header typ=%s",
+			   token->string);
+		goto fail;
+	}
+
+	token = json_get_member(root, "alg");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG, "DPP: No alg string value found");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header alg=%s",
+		   token->string);
+	if (os_strcmp(token->string, curve->jws_alg) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected JWS Protected Header alg=%s (expected %s based on C-sign-key)",
+			   token->string, curve->jws_alg);
+		goto fail;
+	}
+	if (os_strcmp(token->string, "ES256") == 0 ||
+	    os_strcmp(token->string, "BS256") == 0)
+		*ret_md = EVP_sha256();
+	else if (os_strcmp(token->string, "ES384") == 0 ||
+		 os_strcmp(token->string, "BS384") == 0)
+		*ret_md = EVP_sha384();
+	else if (os_strcmp(token->string, "ES512") == 0 ||
+		 os_strcmp(token->string, "BS512") == 0)
+		*ret_md = EVP_sha512();
+	else
+		*ret_md = NULL;
+	if (!*ret_md) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unsupported JWS Protected Header alg=%s",
+			   token->string);
+		goto fail;
+	}
+
+	kid = json_get_member_base64url(root, "kid");
+	if (!kid) {
+		wpa_printf(MSG_DEBUG, "DPP: No kid string value found");
+		goto fail;
+	}
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: JWS Protected Header kid (decoded)",
+			kid);
+
+fail:
+	json_free(root);
+	return kid;
+}
+
+
+static int dpp_parse_cred_legacy(struct dpp_authentication *auth,
+				 struct json_token *cred)
+{
+	struct json_token *pass, *psk_hex;
+
+	wpa_printf(MSG_DEBUG, "DPP: Legacy akm=psk credential");
+
+	pass = json_get_member(cred, "pass");
+	psk_hex = json_get_member(cred, "psk_hex");
+
+	if (pass && pass->type == JSON_STRING) {
+		size_t len = os_strlen(pass->string);
+
+		wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Legacy passphrase",
+				      pass->string, len);
+		if (len < 8 || len > 63)
+			return -1;
+		os_strlcpy(auth->passphrase, pass->string,
+			   sizeof(auth->passphrase));
+	} else if (psk_hex && psk_hex->type == JSON_STRING) {
+		if (os_strlen(psk_hex->string) != PMK_LEN * 2 ||
+		    hexstr2bin(psk_hex->string, auth->psk, PMK_LEN) < 0) {
+			wpa_printf(MSG_DEBUG, "DPP: Invalid psk_hex encoding");
+			return -1;
+		}
+		wpa_hexdump_key(MSG_DEBUG, "DPP: Legacy PSK",
+				auth->psk, PMK_LEN);
+		auth->psk_set = 1;
+	} else {
+		wpa_printf(MSG_DEBUG, "DPP: No pass or psk_hex strings found");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static EVP_PKEY * dpp_parse_jwk(struct json_token *jwk,
+				const struct dpp_curve_params **key_curve)
+{
+	struct json_token *token;
+	const struct dpp_curve_params *curve;
+	struct wpabuf *x = NULL, *y = NULL;
+	EC_GROUP *group;
+	EVP_PKEY *pkey = NULL;
+
+	token = json_get_member(jwk, "kty");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG, "DPP: No kty in JWK");
+		goto fail;
+	}
+	if (os_strcmp(token->string, "EC") != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Unexpected JWK kty '%s",
+			   token->string);
+		goto fail;
+	}
+
+	token = json_get_member(jwk, "crv");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG, "DPP: No crv in JWK");
+		goto fail;
+	}
+	curve = dpp_get_curve_jwk_crv(token->string);
+	if (!curve) {
+		wpa_printf(MSG_DEBUG, "DPP: Unsupported JWK crv '%s'",
+			   token->string);
+		goto fail;
+	}
+
+	x = json_get_member_base64url(jwk, "x");
+	if (!x) {
+		wpa_printf(MSG_DEBUG, "DPP: No x in JWK");
+		goto fail;
+	}
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK x", x);
+	if (wpabuf_len(x) != curve->prime_len) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected JWK x length %u (expected %u for curve %s)",
+			   (unsigned int) wpabuf_len(x),
+			   (unsigned int) curve->prime_len, curve->name);
+		goto fail;
+	}
+
+	y = json_get_member_base64url(jwk, "y");
+	if (!y) {
+		wpa_printf(MSG_DEBUG, "DPP: No y in JWK");
+		goto fail;
+	}
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK y", y);
+	if (wpabuf_len(y) != curve->prime_len) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected JWK y length %u (expected %u for curve %s)",
+			   (unsigned int) wpabuf_len(y),
+			   (unsigned int) curve->prime_len, curve->name);
+		goto fail;
+	}
+
+	group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
+	if (!group) {
+		wpa_printf(MSG_DEBUG, "DPP: Could not prepare group for JWK");
+		goto fail;
+	}
+
+	pkey = dpp_set_pubkey_point_group(group, wpabuf_head(x), wpabuf_head(y),
+					  wpabuf_len(x));
+	*key_curve = curve;
+
+fail:
+	wpabuf_free(x);
+	wpabuf_free(y);
+
+	return pkey;
+}
+
+
+int dpp_key_expired(const char *timestamp, os_time_t *expiry)
+{
+	struct os_time now;
+	unsigned int year, month, day, hour, min, sec;
+	os_time_t utime;
+	const char *pos;
+
+	/* ISO 8601 date and time:
+	 * <date>T<time>
+	 * YYYY-MM-DDTHH:MM:SSZ
+	 * YYYY-MM-DDTHH:MM:SS+03:00
+	 */
+	if (os_strlen(timestamp) < 19) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Too short timestamp - assume expired key");
+		return 1;
+	}
+	if (sscanf(timestamp, "%04u-%02u-%02uT%02u:%02u:%02u",
+		   &year, &month, &day, &hour, &min, &sec) != 6) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to parse expiration day - assume expired key");
+		return 1;
+	}
+
+	if (os_mktime(year, month, day, hour, min, sec, &utime) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid date/time information - assume expired key");
+		return 1;
+	}
+
+	pos = timestamp + 19;
+	if (*pos == 'Z' || *pos == '\0') {
+		/* In UTC - no need to adjust */
+	} else if (*pos == '-' || *pos == '+') {
+		int items;
+
+		/* Adjust local time to UTC */
+		items = sscanf(pos + 1, "%02u:%02u", &hour, &min);
+		if (items < 1) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Invalid time zone designator (%s) - assume expired key",
+				   pos);
+			return 1;
+		}
+		if (*pos == '-')
+			utime += 3600 * hour;
+		if (*pos == '+')
+			utime -= 3600 * hour;
+		if (items > 1) {
+			if (*pos == '-')
+				utime += 60 * min;
+			if (*pos == '+')
+				utime -= 60 * min;
+		}
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid time zone designator (%s) - assume expired key",
+			   pos);
+		return 1;
+	}
+	if (expiry)
+		*expiry = utime;
+
+	if (os_get_time(&now) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Cannot get current time - assume expired key");
+		return 1;
+	}
+
+	if (now.sec > utime) {
+		wpa_printf(MSG_DEBUG, "DPP: Key has expired (%lu < %lu)",
+			   utime, now.sec);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static int dpp_parse_connector(struct dpp_authentication *auth,
+			       const unsigned char *payload,
+			       u16 payload_len)
+{
+	struct json_token *root, *groups, *netkey, *token;
+	int ret = -1;
+	EVP_PKEY *key = NULL;
+	const struct dpp_curve_params *curve;
+	unsigned int rules = 0;
+
+	root = json_parse((const char *) payload, payload_len);
+	if (!root) {
+		wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed");
+		goto fail;
+	}
+
+	groups = json_get_member(root, "groups");
+	if (!groups || groups->type != JSON_ARRAY) {
+		wpa_printf(MSG_DEBUG, "DPP: No groups array found");
+		goto skip_groups;
+	}
+	for (token = groups->child; token; token = token->sibling) {
+		struct json_token *id, *role;
+
+		id = json_get_member(token, "groupId");
+		if (!id || id->type != JSON_STRING) {
+			wpa_printf(MSG_DEBUG, "DPP: Missing groupId string");
+			goto fail;
+		}
+
+		role = json_get_member(token, "netRole");
+		if (!role || role->type != JSON_STRING) {
+			wpa_printf(MSG_DEBUG, "DPP: Missing netRole string");
+			goto fail;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "DPP: connector group: groupId='%s' netRole='%s'",
+			   id->string, role->string);
+		rules++;
+	}
+skip_groups:
+
+	if (!rules) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Connector includes no groups");
+		goto fail;
+	}
+
+	token = json_get_member(root, "expiry");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No expiry string found - connector does not expire");
+	} else {
+		wpa_printf(MSG_DEBUG, "DPP: expiry = %s", token->string);
+		if (dpp_key_expired(token->string,
+				    &auth->net_access_key_expiry)) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Connector (netAccessKey) has expired");
+			goto fail;
+		}
+	}
+
+	netkey = json_get_member(root, "netAccessKey");
+	if (!netkey || netkey->type != JSON_OBJECT) {
+		wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found");
+		goto fail;
+	}
+
+	key = dpp_parse_jwk(netkey, &curve);
+	if (!key)
+		goto fail;
+	dpp_debug_print_key("DPP: Received netAccessKey", key);
+
+	if (EVP_PKEY_cmp(key, auth->own_protocol_key) != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: netAccessKey in connector does not match own protocol key");
+#ifdef CONFIG_TESTING_OPTIONS
+		if (auth->ignore_netaccesskey_mismatch) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: TESTING - skip netAccessKey mismatch");
+		} else {
+			goto fail;
+		}
+#else /* CONFIG_TESTING_OPTIONS */
+		goto fail;
+#endif /* CONFIG_TESTING_OPTIONS */
+	}
+
+	ret = 0;
+fail:
+	EVP_PKEY_free(key);
+	json_free(root);
+	return ret;
+}
+
+
+static int dpp_check_pubkey_match(EVP_PKEY *pub, struct wpabuf *r_hash)
+{
+	struct wpabuf *uncomp;
+	int res;
+	u8 hash[SHA256_MAC_LEN];
+	const u8 *addr[1];
+	size_t len[1];
+
+	if (wpabuf_len(r_hash) != SHA256_MAC_LEN)
+		return -1;
+	uncomp = dpp_get_pubkey_point(pub, 1);
+	if (!uncomp)
+		return -1;
+	addr[0] = wpabuf_head(uncomp);
+	len[0] = wpabuf_len(uncomp);
+	wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed public key",
+		    addr[0], len[0]);
+	res = sha256_vector(1, addr, len, hash);
+	wpabuf_free(uncomp);
+	if (res < 0)
+		return -1;
+	if (os_memcmp(hash, wpabuf_head(r_hash), SHA256_MAC_LEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Received hash value does not match calculated public key hash value");
+		wpa_hexdump(MSG_DEBUG, "DPP: Calculated hash",
+			    hash, SHA256_MAC_LEN);
+		return -1;
+	}
+	return 0;
+}
+
+
+static void dpp_copy_csign(struct dpp_authentication *auth, EVP_PKEY *csign)
+{
+	unsigned char *der = NULL;
+	int der_len;
+
+	der_len = i2d_PUBKEY(csign, &der);
+	if (der_len <= 0)
+		return;
+	wpabuf_free(auth->c_sign_key);
+	auth->c_sign_key = wpabuf_alloc_copy(der, der_len);
+	OPENSSL_free(der);
+}
+
+
+static void dpp_copy_netaccesskey(struct dpp_authentication *auth)
+{
+	unsigned char *der = NULL;
+	int der_len;
+	EC_KEY *eckey;
+
+	eckey = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key);
+	if (!eckey)
+		return;
+
+	der_len = i2d_ECPrivateKey(eckey, &der);
+	if (der_len <= 0) {
+		EC_KEY_free(eckey);
+		return;
+	}
+	wpabuf_free(auth->net_access_key);
+	auth->net_access_key = wpabuf_alloc_copy(der, der_len);
+	OPENSSL_free(der);
+	EC_KEY_free(eckey);
+}
+
+
+struct dpp_signed_connector_info {
+	unsigned char *payload;
+	size_t payload_len;
+};
+
+static int
+dpp_process_signed_connector(struct dpp_signed_connector_info *info,
+			     EVP_PKEY *csign_pub, const char *connector)
+{
+	int ret = -1;
+	const char *pos, *end, *signed_start, *signed_end;
+	struct wpabuf *kid = NULL;
+	unsigned char *prot_hdr = NULL, *signature = NULL;
+	size_t prot_hdr_len = 0, signature_len = 0;
+	const EVP_MD *sign_md = NULL;
+	unsigned char *der = NULL;
+	int der_len;
+	int res;
+	EVP_MD_CTX *md_ctx = NULL;
+	ECDSA_SIG *sig = NULL;
+	BIGNUM *r = NULL, *s = NULL;
+	const struct dpp_curve_params *curve;
+	EC_KEY *eckey;
+	const EC_GROUP *group;
+	int nid;
+
+	eckey = EVP_PKEY_get1_EC_KEY(csign_pub);
+	if (!eckey)
+		goto fail;
+	group = EC_KEY_get0_group(eckey);
+	if (!group)
+		goto fail;
+	nid = EC_GROUP_get_curve_name(group);
+	curve = dpp_get_curve_nid(nid);
+	if (!curve)
+		goto fail;
+	wpa_printf(MSG_DEBUG, "DPP: C-sign-key group: %s", curve->jwk_crv);
+	os_memset(info, 0, sizeof(*info));
+
+	signed_start = pos = connector;
+	end = os_strchr(pos, '.');
+	if (!end) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing dot(1) in signedConnector");
+		goto fail;
+	}
+	prot_hdr = base64_url_decode((const unsigned char *) pos,
+				     end - pos, &prot_hdr_len);
+	if (!prot_hdr) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to base64url decode signedConnector JWS Protected Header");
+		goto fail;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG,
+			  "DPP: signedConnector - JWS Protected Header",
+			  prot_hdr, prot_hdr_len);
+	kid = dpp_parse_jws_prot_hdr(curve, prot_hdr, prot_hdr_len, &sign_md);
+	if (!kid)
+		goto fail;
+	if (wpabuf_len(kid) != SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected signedConnector JWS Protected Header kid length: %u (expected %u)",
+			   (unsigned int) wpabuf_len(kid), SHA256_MAC_LEN);
+		goto fail;
+	}
+
+	pos = end + 1;
+	end = os_strchr(pos, '.');
+	if (!end) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing dot(2) in signedConnector");
+		goto fail;
+	}
+	signed_end = end - 1;
+	info->payload = base64_url_decode((const unsigned char *) pos,
+					  end - pos, &info->payload_len);
+	if (!info->payload) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to base64url decode signedConnector JWS Payload");
+		goto fail;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG,
+			  "DPP: signedConnector - JWS Payload",
+			  info->payload, info->payload_len);
+	pos = end + 1;
+	signature = base64_url_decode((const unsigned char *) pos,
+				      os_strlen(pos), &signature_len);
+	if (!signature) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to base64url decode signedConnector signature");
+		goto fail;
+		}
+	wpa_hexdump(MSG_DEBUG, "DPP: signedConnector - signature",
+		    signature, signature_len);
+
+	if (dpp_check_pubkey_match(csign_pub, kid) < 0)
+		goto fail;
+
+	if (signature_len & 0x01) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected signedConnector signature length (%d)",
+			   (int) signature_len);
+		goto fail;
+	}
+
+	/* JWS Signature encodes the signature (r,s) as two octet strings. Need
+	 * to convert that to DER encoded ECDSA_SIG for OpenSSL EVP routines. */
+	r = BN_bin2bn(signature, signature_len / 2, NULL);
+	s = BN_bin2bn(signature + signature_len / 2, signature_len / 2, NULL);
+	sig = ECDSA_SIG_new();
+	if (!r || !s || !sig || ECDSA_SIG_set0(sig, r, s) != 1)
+		goto fail;
+	r = NULL;
+	s = NULL;
+
+	der_len = i2d_ECDSA_SIG(sig, &der);
+	if (der_len <= 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Could not DER encode signature");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: DER encoded signature", der, der_len);
+	md_ctx = EVP_MD_CTX_create();
+	if (!md_ctx)
+		goto fail;
+
+	ERR_clear_error();
+	if (EVP_DigestVerifyInit(md_ctx, NULL, sign_md, NULL, csign_pub) != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyInit failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	if (EVP_DigestVerifyUpdate(md_ctx, signed_start,
+				   signed_end - signed_start + 1) != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyUpdate failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	res = EVP_DigestVerifyFinal(md_ctx, der, der_len);
+	if (res != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: EVP_DigestVerifyFinal failed (res=%d): %s",
+			   res, ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	ret = 0;
+fail:
+	EC_KEY_free(eckey);
+	EVP_MD_CTX_destroy(md_ctx);
+	os_free(prot_hdr);
+	wpabuf_free(kid);
+	os_free(signature);
+	ECDSA_SIG_free(sig);
+	BN_free(r);
+	BN_free(s);
+	OPENSSL_free(der);
+	return ret;
+}
+
+
+static int dpp_parse_cred_dpp(struct dpp_authentication *auth,
+			      struct json_token *cred)
+{
+	struct dpp_signed_connector_info info;
+	struct json_token *token, *csign;
+	int ret = -1;
+	EVP_PKEY *csign_pub = NULL;
+	const struct dpp_curve_params *key_curve = NULL;
+	const char *signed_connector;
+
+	os_memset(&info, 0, sizeof(info));
+
+	wpa_printf(MSG_DEBUG, "DPP: Connector credential");
+
+	csign = json_get_member(cred, "csign");
+	if (!csign || csign->type != JSON_OBJECT) {
+		wpa_printf(MSG_DEBUG, "DPP: No csign JWK in JSON");
+		goto fail;
+	}
+
+	csign_pub = dpp_parse_jwk(csign, &key_curve);
+	if (!csign_pub) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to parse csign JWK");
+		goto fail;
+	}
+	dpp_debug_print_key("DPP: Received C-sign-key", csign_pub);
+
+	token = json_get_member(cred, "signedConnector");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG, "DPP: No signedConnector string found");
+		goto fail;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "DPP: signedConnector",
+			  token->string, os_strlen(token->string));
+	signed_connector = token->string;
+
+	if (os_strchr(signed_connector, '"') ||
+	    os_strchr(signed_connector, '\n')) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected character in signedConnector");
+		goto fail;
+	}
+
+	if (dpp_process_signed_connector(&info, csign_pub,
+					 signed_connector) < 0)
+		goto fail;
+
+	if (dpp_parse_connector(auth, info.payload, info.payload_len) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to parse connector");
+		goto fail;
+	}
+
+	os_free(auth->connector);
+	auth->connector = os_strdup(signed_connector);
+
+	dpp_copy_csign(auth, csign_pub);
+	dpp_copy_netaccesskey(auth);
+
+	ret = 0;
+fail:
+	EVP_PKEY_free(csign_pub);
+	os_free(info.payload);
+	return ret;
+}
+
+
+static int dpp_parse_conf_obj(struct dpp_authentication *auth,
+			      const u8 *conf_obj, u16 conf_obj_len)
+{
+	int ret = -1;
+	struct json_token *root, *token, *discovery, *cred;
+
+	root = json_parse((const char *) conf_obj, conf_obj_len);
+	if (!root)
+		return -1;
+	if (root->type != JSON_OBJECT) {
+		wpa_printf(MSG_DEBUG, "DPP: JSON root is not an object");
+		goto fail;
+	}
+
+	token = json_get_member(root, "wi-fi_tech");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG, "DPP: No wi-fi_tech string value found");
+		goto fail;
+	}
+	if (os_strcmp(token->string, "infra") != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech value: '%s'",
+			   token->string);
+		goto fail;
+	}
+
+	discovery = json_get_member(root, "discovery");
+	if (!discovery || discovery->type != JSON_OBJECT) {
+		wpa_printf(MSG_DEBUG, "DPP: No discovery object in JSON");
+		goto fail;
+	}
+
+	token = json_get_member(discovery, "ssid");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No discovery::ssid string value found");
+		goto fail;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "DPP: discovery::ssid",
+			  token->string, os_strlen(token->string));
+	if (os_strlen(token->string) > SSID_MAX_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Too long discovery::ssid string value");
+		goto fail;
+	}
+	auth->ssid_len = os_strlen(token->string);
+	os_memcpy(auth->ssid, token->string, auth->ssid_len);
+
+	cred = json_get_member(root, "cred");
+	if (!cred || cred->type != JSON_OBJECT) {
+		wpa_printf(MSG_DEBUG, "DPP: No cred object in JSON");
+		goto fail;
+	}
+
+	token = json_get_member(cred, "akm");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No cred::akm string value found");
+		goto fail;
+	}
+	if (os_strcmp(token->string, "psk") == 0) {
+		if (dpp_parse_cred_legacy(auth, cred) < 0)
+			goto fail;
+	} else if (os_strcmp(token->string, "dpp") == 0) {
+		if (dpp_parse_cred_dpp(auth, cred) < 0)
+			goto fail;
+	} else {
+		wpa_printf(MSG_DEBUG, "DPP: Unsupported akm: %s",
+			   token->string);
+		goto fail;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: JSON parsing completed successfully");
+	ret = 0;
+fail:
+	json_free(root);
+	return ret;
+}
+
+
+int dpp_conf_resp_rx(struct dpp_authentication *auth,
+		     const struct wpabuf *resp)
+{
+	const u8 *wrapped_data, *e_nonce, *status, *conf_obj;
+	u16 wrapped_data_len, e_nonce_len, status_len, conf_obj_len;
+	const u8 *addr[1];
+	size_t len[1];
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	int ret = -1;
+
+	if (dpp_check_attrs(wpabuf_head(resp), wpabuf_len(resp)) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in config response");
+		return -1;
+	}
+
+	wrapped_data = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp),
+				    DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Wrapped data attribute");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		return -1;
+
+	addr[0] = wpabuf_head(resp);
+	len[0] = wrapped_data - 4 - (const u8 *) wpabuf_head(resp);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
+
+	if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    1, addr, len, unwrapped) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped, unwrapped_len);
+
+	if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	e_nonce = dpp_get_attr(unwrapped, unwrapped_len,
+			       DPP_ATTR_ENROLLEE_NONCE,
+			       &e_nonce_len);
+	if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid Enrollee Nonce attribute");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
+	if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) {
+		wpa_printf(MSG_DEBUG, "Enrollee Nonce mismatch");
+		goto fail;
+	}
+
+	status = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp),
+			      DPP_ATTR_STATUS, &status_len);
+	if (!status || status_len < 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required DPP Status attribute");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+	if (status[0] != DPP_STATUS_OK) {
+		wpa_printf(MSG_DEBUG, "DPP: Configuration failed");
+		goto fail;
+	}
+
+	conf_obj = dpp_get_attr(unwrapped, unwrapped_len,
+				DPP_ATTR_CONFIG_OBJ, &conf_obj_len);
+	if (!conf_obj) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing required Configuration Object attribute");
+		goto fail;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON",
+			  conf_obj, conf_obj_len);
+	if (dpp_parse_conf_obj(auth, conf_obj, conf_obj_len) < 0)
+		goto fail;
+
+	ret = 0;
+
+fail:
+	os_free(unwrapped);
+	return ret;
+}
+
+
+void dpp_configurator_free(struct dpp_configurator *conf)
+{
+	if (!conf)
+		return;
+	EVP_PKEY_free(conf->csign);
+	os_free(conf->kid);
+	os_free(conf);
+}
+
+
+struct dpp_configurator *
+dpp_keygen_configurator(const char *curve, const u8 *privkey,
+			size_t privkey_len)
+{
+	struct dpp_configurator *conf;
+	struct wpabuf *csign_pub = NULL;
+	u8 kid_hash[SHA256_MAC_LEN];
+	const u8 *addr[1];
+	size_t len[1];
+
+	conf = os_zalloc(sizeof(*conf));
+	if (!conf)
+		return NULL;
+
+	if (!curve) {
+		conf->curve = &dpp_curves[0];
+	} else {
+		conf->curve = dpp_get_curve_name(curve);
+		if (!conf->curve) {
+			wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
+				   curve);
+			return NULL;
+		}
+	}
+	if (privkey)
+		conf->csign = dpp_set_keypair(&conf->curve, privkey,
+					      privkey_len);
+	else
+		conf->csign = dpp_gen_keypair(conf->curve);
+	if (!conf->csign)
+		goto fail;
+	conf->own = 1;
+
+	csign_pub = dpp_get_pubkey_point(conf->csign, 1);
+	if (!csign_pub) {
+		wpa_printf(MSG_INFO, "DPP: Failed to extract C-sign-key");
+		goto fail;
+	}
+
+	/* kid = SHA256(ANSI X9.63 uncompressed C-sign-key) */
+	addr[0] = wpabuf_head(csign_pub);
+	len[0] = wpabuf_len(csign_pub);
+	if (sha256_vector(1, addr, len, kid_hash) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to derive kid for C-sign-key");
+		goto fail;
+	}
+
+	conf->kid = (char *) base64_url_encode(kid_hash, sizeof(kid_hash),
+					       NULL, 0);
+	if (!conf->kid)
+		goto fail;
+out:
+	wpabuf_free(csign_pub);
+	return conf;
+fail:
+	dpp_configurator_free(conf);
+	conf = NULL;
+	goto out;
+}
+
+
+int dpp_configurator_own_config(struct dpp_authentication *auth,
+				const char *curve)
+{
+	struct wpabuf *conf_obj;
+	int ret = -1;
+
+	if (!auth->conf) {
+		wpa_printf(MSG_DEBUG, "DPP: No configurator specified");
+		return -1;
+	}
+
+	if (!curve) {
+		auth->curve = &dpp_curves[0];
+	} else {
+		auth->curve = dpp_get_curve_name(curve);
+		if (!auth->curve) {
+			wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
+				   curve);
+			return -1;
+		}
+	}
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Building own configuration/connector with curve %s",
+		   auth->curve->name);
+
+	auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+	if (!auth->own_protocol_key)
+		return -1;
+	dpp_copy_netaccesskey(auth);
+	auth->peer_protocol_key = auth->own_protocol_key;
+	dpp_copy_csign(auth, auth->conf->csign);
+
+	conf_obj = dpp_build_conf_obj(auth, 0);
+	if (!conf_obj)
+		goto fail;
+	ret = dpp_parse_conf_obj(auth, wpabuf_head(conf_obj),
+				 wpabuf_len(conf_obj));
+fail:
+	wpabuf_free(conf_obj);
+	auth->peer_protocol_key = NULL;
+	return ret;
+}
+
+
+static int dpp_compatible_netrole(const char *role1, const char *role2)
+{
+	return (os_strcmp(role1, "sta") == 0 && os_strcmp(role2, "ap") == 0) ||
+		(os_strcmp(role1, "ap") == 0 && os_strcmp(role2, "sta") == 0);
+}
+
+
+static int dpp_connector_compatible_group(struct json_token *root,
+					  const char *group_id,
+					  const char *net_role)
+{
+	struct json_token *groups, *token;
+
+	groups = json_get_member(root, "groups");
+	if (!groups || groups->type != JSON_ARRAY)
+		return 0;
+
+	for (token = groups->child; token; token = token->sibling) {
+		struct json_token *id, *role;
+
+		id = json_get_member(token, "groupId");
+		if (!id || id->type != JSON_STRING)
+			continue;
+
+		role = json_get_member(token, "netRole");
+		if (!role || role->type != JSON_STRING)
+			continue;
+
+		if (os_strcmp(id->string, "*") != 0 &&
+		    os_strcmp(group_id, "*") != 0 &&
+		    os_strcmp(id->string, group_id) != 0)
+			continue;
+
+		if (dpp_compatible_netrole(role->string, net_role))
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int dpp_connector_match_groups(struct json_token *own_root,
+				      struct json_token *peer_root)
+{
+	struct json_token *groups, *token;
+
+	groups = json_get_member(peer_root, "groups");
+	if (!groups || groups->type != JSON_ARRAY) {
+		wpa_printf(MSG_DEBUG, "DPP: No peer groups array found");
+		return 0;
+	}
+
+	for (token = groups->child; token; token = token->sibling) {
+		struct json_token *id, *role;
+
+		id = json_get_member(token, "groupId");
+		if (!id || id->type != JSON_STRING) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Missing peer groupId string");
+			continue;
+		}
+
+		role = json_get_member(token, "netRole");
+		if (!role || role->type != JSON_STRING) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Missing peer groups::netRole string");
+			continue;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "DPP: peer connector group: groupId='%s' netRole='%s'",
+			   id->string, role->string);
+		if (dpp_connector_compatible_group(own_root, id->string,
+						   role->string)) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Compatible group/netRole in own connector");
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+
+static int dpp_derive_pmk(const u8 *Nx, size_t Nx_len, u8 *pmk,
+			  unsigned int hash_len)
+{
+	u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+	const char *info = "DPP PMK";
+	int res;
+
+	/* PMK = HKDF(<>, "DPP PMK", N.x) */
+
+	/* HKDF-Extract(<>, N.x) */
+	os_memset(salt, 0, hash_len);
+	if (dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
+			prk, hash_len);
+
+	/* HKDF-Expand(PRK, info, L) */
+	res = dpp_hkdf_expand(hash_len, prk, hash_len, info, pmk, hash_len);
+	os_memset(prk, 0, hash_len);
+	if (res < 0)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PMK = HKDF-Expand(PRK, info, L)",
+			pmk, hash_len);
+	return 0;
+}
+
+
+static int dpp_derive_pmkid(const struct dpp_curve_params *curve,
+			    EVP_PKEY *own_key, EVP_PKEY *peer_key, u8 *pmkid)
+{
+	struct wpabuf *nkx, *pkx;
+	int ret = -1, res;
+	const u8 *addr[2];
+	size_t len[2];
+	u8 hash[SHA256_MAC_LEN];
+
+	/* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */
+	nkx = dpp_get_pubkey_point(own_key, 0);
+	pkx = dpp_get_pubkey_point(peer_key, 0);
+	if (!nkx || !pkx)
+		goto fail;
+	addr[0] = wpabuf_head(nkx);
+	len[0] = wpabuf_len(nkx) / 2;
+	addr[1] = wpabuf_head(pkx);
+	len[1] = wpabuf_len(pkx) / 2;
+	if (len[0] != len[1])
+		goto fail;
+	if (os_memcmp(addr[0], addr[1], len[0]) > 0) {
+		addr[0] = wpabuf_head(pkx);
+		addr[1] = wpabuf_head(nkx);
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 1", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 2", addr[1], len[1]);
+	res = sha256_vector(2, addr, len, hash);
+	if (res < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash output", hash, SHA256_MAC_LEN);
+	os_memcpy(pmkid, hash, PMKID_LEN);
+	wpa_hexdump(MSG_DEBUG, "DPP: PMKID", pmkid, PMKID_LEN);
+	ret = 0;
+fail:
+	wpabuf_free(nkx);
+	wpabuf_free(pkx);
+	return ret;
+}
+
+
+int dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
+		   const u8 *net_access_key, size_t net_access_key_len,
+		   const u8 *csign_key, size_t csign_key_len,
+		   const u8 *peer_connector, size_t peer_connector_len,
+		   os_time_t *expiry)
+{
+	struct json_token *root = NULL, *netkey, *token;
+	struct json_token *own_root = NULL;
+	int ret = -1;
+	EVP_PKEY *own_key = NULL, *peer_key = NULL;
+	struct wpabuf *own_key_pub = NULL;
+	const struct dpp_curve_params *curve, *own_curve;
+	struct dpp_signed_connector_info info;
+	const unsigned char *p;
+	EVP_PKEY *csign = NULL;
+	char *signed_connector = NULL;
+	const char *pos, *end;
+	unsigned char *own_conn = NULL;
+	size_t own_conn_len;
+	EVP_PKEY_CTX *ctx = NULL;
+	size_t Nx_len;
+	u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
+
+	os_memset(intro, 0, sizeof(*intro));
+	os_memset(&info, 0, sizeof(info));
+	if (expiry)
+		*expiry = 0;
+
+	p = csign_key;
+	csign = d2i_PUBKEY(NULL, &p, csign_key_len);
+	if (!csign) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to parse local C-sign-key information");
+		goto fail;
+	}
+
+	own_key = dpp_set_keypair(&own_curve, net_access_key,
+				  net_access_key_len);
+	if (!own_key) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
+		goto fail;
+	}
+
+	pos = os_strchr(own_connector, '.');
+	if (!pos) {
+		wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the first dot (.)");
+		goto fail;
+	}
+	pos++;
+	end = os_strchr(pos, '.');
+	if (!end) {
+		wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the second dot (.)");
+		goto fail;
+	}
+	own_conn = base64_url_decode((const unsigned char *) pos,
+				     end - pos, &own_conn_len);
+	if (!own_conn) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to base64url decode own signedConnector JWS Payload");
+		goto fail;
+	}
+
+	own_root = json_parse((const char *) own_conn, own_conn_len);
+	if (!own_root) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to parse local connector");
+		goto fail;
+	}
+
+	wpa_hexdump_ascii(MSG_DEBUG, "DPP: Peer signedConnector",
+			  peer_connector, peer_connector_len);
+	signed_connector = os_malloc(peer_connector_len + 1);
+	if (!signed_connector)
+		goto fail;
+	os_memcpy(signed_connector, peer_connector, peer_connector_len);
+	signed_connector[peer_connector_len] = '\0';
+
+	if (dpp_process_signed_connector(&info, csign, signed_connector) < 0)
+		goto fail;
+
+	root = json_parse((const char *) info.payload, info.payload_len);
+	if (!root) {
+		wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed");
+		goto fail;
+	}
+
+	if (!dpp_connector_match_groups(own_root, root)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer connector does not include compatible group netrole with own connector");
+		goto fail;
+	}
+
+	token = json_get_member(root, "expiry");
+	if (!token || token->type != JSON_STRING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No expiry string found - connector does not expire");
+	} else {
+		wpa_printf(MSG_DEBUG, "DPP: expiry = %s", token->string);
+		if (dpp_key_expired(token->string, expiry)) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Connector (netAccessKey) has expired");
+			goto fail;
+		}
+	}
+
+	netkey = json_get_member(root, "netAccessKey");
+	if (!netkey || netkey->type != JSON_OBJECT) {
+		wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found");
+		goto fail;
+	}
+
+	peer_key = dpp_parse_jwk(netkey, &curve);
+	if (!peer_key)
+		goto fail;
+	dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
+
+	if (own_curve != curve) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Mismatching netAccessKey curves (%s != %s)",
+			   own_curve->name, curve->name);
+		goto fail;
+	}
+
+	/* ECDH: N = nk * PK */
+	ctx = EVP_PKEY_CTX_new(own_key, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, peer_key) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &Nx_len) != 1 ||
+	    Nx_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, Nx, &Nx_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
+			Nx, Nx_len);
+
+	/* PMK = HKDF(<>, "DPP PMK", N.x) */
+	if (dpp_derive_pmk(Nx, Nx_len, intro->pmk, curve->hash_len) < 0) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to derive PMK");
+		goto fail;
+	}
+	intro->pmk_len = curve->hash_len;
+
+	/* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */
+	if (dpp_derive_pmkid(curve, own_key, peer_key, intro->pmkid) < 0) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to derive PMKID");
+		goto fail;
+	}
+
+	ret = 0;
+fail:
+	if (ret < 0)
+		os_memset(intro, 0, sizeof(*intro));
+	os_memset(Nx, 0, sizeof(Nx));
+	EVP_PKEY_CTX_free(ctx);
+	os_free(own_conn);
+	os_free(signed_connector);
+	os_free(info.payload);
+	EVP_PKEY_free(own_key);
+	wpabuf_free(own_key_pub);
+	EVP_PKEY_free(peer_key);
+	EVP_PKEY_free(csign);
+	json_free(root);
+	json_free(own_root);
+	return ret;
+}
+
+
+static EVP_PKEY * dpp_pkex_get_role_elem(const struct dpp_curve_params *curve,
+					 int init)
+{
+	EC_GROUP *group;
+	size_t len = curve->prime_len;
+	const u8 *x, *y;
+
+	switch (curve->ike_group) {
+	case 19:
+		x = init ? pkex_init_x_p256 : pkex_resp_x_p256;
+		y = init ? pkex_init_y_p256 : pkex_resp_y_p256;
+		break;
+	case 20:
+		x = init ? pkex_init_x_p384 : pkex_resp_x_p384;
+		y = init ? pkex_init_y_p384 : pkex_resp_y_p384;
+		break;
+	case 21:
+		x = init ? pkex_init_x_p521 : pkex_resp_x_p521;
+		y = init ? pkex_init_y_p521 : pkex_resp_y_p521;
+		break;
+	case 28:
+		x = init ? pkex_init_x_bp_p256r1 : pkex_resp_x_bp_p256r1;
+		y = init ? pkex_init_y_bp_p256r1 : pkex_resp_y_bp_p256r1;
+		break;
+	case 29:
+		x = init ? pkex_init_x_bp_p384r1 : pkex_resp_x_bp_p384r1;
+		y = init ? pkex_init_y_bp_p384r1 : pkex_resp_y_bp_p384r1;
+		break;
+	case 30:
+		x = init ? pkex_init_x_bp_p512r1 : pkex_resp_x_bp_p512r1;
+		y = init ? pkex_init_y_bp_p512r1 : pkex_resp_y_bp_p512r1;
+		break;
+	default:
+		return NULL;
+	}
+
+	group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
+	if (!group)
+		return NULL;
+	return dpp_set_pubkey_point_group(group, x, y, len);
+}
+
+
+static EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve,
+				     const u8 *mac_init, const char *code,
+				     const char *identifier, BN_CTX *bnctx,
+				     const EC_GROUP **ret_group)
+{
+	u8 hash[DPP_MAX_HASH_LEN];
+	const u8 *addr[3];
+	size_t len[3];
+	unsigned int num_elem = 0;
+	EC_POINT *Qi = NULL;
+	EVP_PKEY *Pi = NULL;
+	EC_KEY *Pi_ec = NULL;
+	const EC_POINT *Pi_point;
+	BIGNUM *hash_bn = NULL;
+	const EC_GROUP *group = NULL;
+	EC_GROUP *group2 = NULL;
+
+	/* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
+
+	wpa_printf(MSG_DEBUG, "DPP: MAC-Initiator: " MACSTR, MAC2STR(mac_init));
+	addr[num_elem] = mac_init;
+	len[num_elem] = ETH_ALEN;
+	num_elem++;
+	if (identifier) {
+		wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
+			   identifier);
+		addr[num_elem] = (const u8 *) identifier;
+		len[num_elem] = os_strlen(identifier);
+		num_elem++;
+	}
+	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
+	addr[num_elem] = (const u8 *) code;
+	len[num_elem] = os_strlen(code);
+	num_elem++;
+	if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG,
+			"DPP: H(MAC-Initiator | [identifier |] code)",
+			hash, curve->hash_len);
+	Pi = dpp_pkex_get_role_elem(curve, 1);
+	if (!Pi)
+		goto fail;
+	dpp_debug_print_key("DPP: Pi", Pi);
+	Pi_ec = EVP_PKEY_get1_EC_KEY(Pi);
+	if (!Pi_ec)
+		goto fail;
+	Pi_point = EC_KEY_get0_public_key(Pi_ec);
+
+	group = EC_KEY_get0_group(Pi_ec);
+	if (!group)
+		goto fail;
+	group2 = EC_GROUP_dup(group);
+	if (!group2)
+		goto fail;
+	Qi = EC_POINT_new(group2);
+	if (!Qi) {
+		EC_GROUP_free(group2);
+		goto fail;
+	}
+	hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
+	if (!hash_bn ||
+	    EC_POINT_mul(group2, Qi, NULL, Pi_point, hash_bn, bnctx) != 1)
+		goto fail;
+	if (EC_POINT_is_at_infinity(group, Qi)) {
+		wpa_printf(MSG_INFO, "PDP: Qi is the point-at-infinity");
+		goto fail;
+	}
+out:
+	EC_KEY_free(Pi_ec);
+	EVP_PKEY_free(Pi);
+	BN_clear_free(hash_bn);
+	if (ret_group)
+		*ret_group = group2;
+	return Qi;
+fail:
+	EC_POINT_free(Qi);
+	Qi = NULL;
+	goto out;
+}
+
+
+static EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve,
+				     const u8 *mac_resp, const char *code,
+				     const char *identifier, BN_CTX *bnctx,
+				     const EC_GROUP **ret_group)
+{
+	u8 hash[DPP_MAX_HASH_LEN];
+	const u8 *addr[3];
+	size_t len[3];
+	unsigned int num_elem = 0;
+	EC_POINT *Qr = NULL;
+	EVP_PKEY *Pr = NULL;
+	EC_KEY *Pr_ec = NULL;
+	const EC_POINT *Pr_point;
+	BIGNUM *hash_bn = NULL;
+	const EC_GROUP *group = NULL;
+	EC_GROUP *group2 = NULL;
+
+	/* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
+
+	wpa_printf(MSG_DEBUG, "DPP: MAC-Responder: " MACSTR, MAC2STR(mac_resp));
+	addr[num_elem] = mac_resp;
+	len[num_elem] = ETH_ALEN;
+	num_elem++;
+	if (identifier) {
+		wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
+			   identifier);
+		addr[num_elem] = (const u8 *) identifier;
+		len[num_elem] = os_strlen(identifier);
+		num_elem++;
+	}
+	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
+	addr[num_elem] = (const u8 *) code;
+	len[num_elem] = os_strlen(code);
+	num_elem++;
+	if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG,
+			"DPP: H(MAC-Responder | [identifier |] code)",
+			hash, curve->hash_len);
+	Pr = dpp_pkex_get_role_elem(curve, 0);
+	if (!Pr)
+		goto fail;
+	dpp_debug_print_key("DPP: Pr", Pr);
+	Pr_ec = EVP_PKEY_get1_EC_KEY(Pr);
+	if (!Pr_ec)
+		goto fail;
+	Pr_point = EC_KEY_get0_public_key(Pr_ec);
+
+	group = EC_KEY_get0_group(Pr_ec);
+	if (!group)
+		goto fail;
+	group2 = EC_GROUP_dup(group);
+	if (!group2)
+		goto fail;
+	Qr = EC_POINT_new(group2);
+	if (!Qr) {
+		EC_GROUP_free(group2);
+		goto fail;
+	}
+	hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
+	if (!hash_bn ||
+	    EC_POINT_mul(group2, Qr, NULL, Pr_point, hash_bn, bnctx) != 1)
+		goto fail;
+out:
+	EC_KEY_free(Pr_ec);
+	EVP_PKEY_free(Pr);
+	BN_clear_free(hash_bn);
+	if (ret_group)
+		*ret_group = group2;
+	return Qr;
+fail:
+	EC_POINT_free(Qr);
+	Qr = NULL;
+	goto out;
+}
+
+
+static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex)
+{
+	EC_KEY *X_ec = NULL;
+	const EC_POINT *X_point;
+	BN_CTX *bnctx = NULL;
+	const EC_GROUP *group;
+	EC_POINT *Qi = NULL, *M = NULL;
+	struct wpabuf *M_buf = NULL;
+	BIGNUM *Mx = NULL, *My = NULL;
+	struct wpabuf *msg = NULL;
+	size_t attr_len;
+	const struct dpp_curve_params *curve = pkex->own_bi->curve;
+	int num_bytes, offset;
+
+	wpa_printf(MSG_DEBUG, "DPP: Build PKEX Exchange Request");
+
+	/* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
+	bnctx = BN_CTX_new();
+	if (!bnctx)
+		goto fail;
+	Qi = dpp_pkex_derive_Qi(curve, pkex->own_mac, pkex->code,
+				pkex->identifier, bnctx, &group);
+	if (!Qi)
+		goto fail;
+
+	/* Generate a random ephemeral keypair x/X */
+	pkex->x = dpp_gen_keypair(curve);
+	if (!pkex->x)
+		goto fail;
+
+	/* M = X + Qi */
+	X_ec = EVP_PKEY_get1_EC_KEY(pkex->x);
+	if (!X_ec)
+		goto fail;
+	X_point = EC_KEY_get0_public_key(X_ec);
+	if (!X_point)
+		goto fail;
+	M = EC_POINT_new(group);
+	Mx = BN_new();
+	My = BN_new();
+	if (!M || !Mx || !My ||
+	    EC_POINT_add(group, M, X_point, Qi, bnctx) != 1 ||
+	    EC_POINT_get_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1)
+		goto fail;
+
+	/* Initiator -> Responder: group, [identifier,] M */
+	attr_len = 4 + 2;
+	if (pkex->identifier)
+		attr_len += 4 + os_strlen(pkex->identifier);
+	attr_len += 4 + 2 * curve->prime_len;
+	msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_REQ, attr_len);
+	if (!msg)
+		goto fail;
+
+	/* Finite Cyclic Group attribute */
+	wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
+	wpabuf_put_le16(msg, 2);
+	wpabuf_put_le16(msg, curve->ike_group);
+
+	/* Code Identifier attribute */
+	if (pkex->identifier) {
+		wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
+		wpabuf_put_le16(msg, os_strlen(pkex->identifier));
+		wpabuf_put_str(msg, pkex->identifier);
+	}
+
+	/* M in Encrypted Key attribute */
+	wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
+	wpabuf_put_le16(msg, 2 * curve->prime_len);
+
+	num_bytes = BN_num_bytes(Mx);
+	if ((size_t) num_bytes > curve->prime_len)
+		goto fail;
+	if (curve->prime_len > (size_t) num_bytes)
+		offset = curve->prime_len - num_bytes;
+	else
+		offset = 0;
+	os_memset(wpabuf_put(msg, offset), 0, offset);
+	BN_bn2bin(Mx, wpabuf_put(msg, num_bytes));
+	os_memset(pkex->Mx, 0, offset);
+	BN_bn2bin(Mx, pkex->Mx + offset);
+
+	num_bytes = BN_num_bytes(My);
+	if ((size_t) num_bytes > curve->prime_len)
+		goto fail;
+	if (curve->prime_len > (size_t) num_bytes)
+		offset = curve->prime_len - num_bytes;
+	else
+		offset = 0;
+	os_memset(wpabuf_put(msg, offset), 0, offset);
+	BN_bn2bin(My, wpabuf_put(msg, num_bytes));
+
+out:
+	wpabuf_free(M_buf);
+	EC_KEY_free(X_ec);
+	EC_POINT_free(M);
+	EC_POINT_free(Qi);
+	BN_clear_free(Mx);
+	BN_clear_free(My);
+	BN_CTX_free(bnctx);
+	return msg;
+fail:
+	wpa_printf(MSG_INFO, "DPP: Failed to build PKEX Exchange Request");
+	wpabuf_free(msg);
+	msg = NULL;
+	goto out;
+}
+
+
+struct dpp_pkex * dpp_pkex_init(struct dpp_bootstrap_info *bi,
+				const u8 *own_mac,
+				const char *identifier,
+				const char *code)
+{
+	struct dpp_pkex *pkex;
+
+	pkex = os_zalloc(sizeof(*pkex));
+	if (!pkex)
+		return NULL;
+	pkex->initiator = 1;
+	pkex->own_bi = bi;
+	os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
+	if (identifier) {
+		pkex->identifier = os_strdup(identifier);
+		if (!pkex->identifier)
+			goto fail;
+	}
+	pkex->code = os_strdup(code);
+	if (!pkex->code)
+		goto fail;
+	pkex->exchange_req = dpp_pkex_build_exchange_req(pkex);
+	if (!pkex->exchange_req)
+		goto fail;
+	return pkex;
+fail:
+	dpp_pkex_free(pkex);
+	return NULL;
+}
+
+
+struct dpp_pkex * dpp_pkex_rx_exchange_req(struct dpp_bootstrap_info *bi,
+					   const u8 *own_mac,
+					   const u8 *peer_mac,
+					   const char *identifier,
+					   const char *code,
+					   const u8 *buf, size_t len)
+{
+	const u8 *attr_group, *attr_id, *attr_key;
+	u16 attr_group_len, attr_id_len, attr_key_len;
+	const struct dpp_curve_params *curve = bi->curve;
+	u16 ike_group;
+	struct dpp_pkex *pkex = NULL;
+	EC_POINT *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL, *N = NULL;
+	BN_CTX *bnctx = NULL;
+	const EC_GROUP *group;
+	BIGNUM *Mx = NULL, *My = NULL;
+	EC_KEY *Y_ec = NULL, *X_ec = NULL;;
+	const EC_POINT *Y_point;
+	BIGNUM *Nx = NULL, *Ny = NULL;
+	struct wpabuf *msg = NULL;
+	size_t attr_len;
+	int num_bytes, offset;
+
+	attr_id = dpp_get_attr(buf, len, DPP_ATTR_CODE_IDENTIFIER,
+			       &attr_id_len);
+	if (!attr_id && identifier) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No PKEX code identifier received, but expected one");
+		return NULL;
+	}
+	if (attr_id && identifier &&
+	    (os_strlen(identifier) != attr_id_len ||
+	     os_memcmp(identifier, attr_id, attr_id_len) != 0)) {
+		wpa_printf(MSG_DEBUG, "DPP: PKEX code identifier mismatch");
+		return NULL;
+	}
+
+	attr_group = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
+				  &attr_group_len);
+	if (!attr_group || attr_group_len != 2) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid Finite Cyclic Group attribute");
+		return NULL;
+	}
+	ike_group = WPA_GET_LE16(attr_group);
+	if (ike_group != curve->ike_group) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Mismatching PKEX curve: peer=%u own=%u",
+			   ike_group, curve->ike_group);
+		/* TODO: error response with suggested curve:
+		 * DPP Status, group */
+		return NULL;
+	}
+
+	/* M in Encrypted Key attribute */
+	attr_key = dpp_get_attr(buf, len, DPP_ATTR_ENCRYPTED_KEY,
+				&attr_key_len);
+	if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2 ||
+	    attr_key_len / 2 > DPP_MAX_SHARED_SECRET_LEN) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing Encrypted Key attribute");
+		return NULL;
+	}
+
+	/* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
+	bnctx = BN_CTX_new();
+	if (!bnctx)
+		goto fail;
+	Qi = dpp_pkex_derive_Qi(curve, peer_mac, code, identifier, bnctx,
+				&group);
+	if (!Qi)
+		goto fail;
+
+	/* X' = M - Qi */
+	X = EC_POINT_new(group);
+	M = EC_POINT_new(group);
+	Mx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
+	My = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
+	if (!X || !M || !Mx || !My ||
+	    EC_POINT_set_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1 ||
+	    EC_POINT_is_at_infinity(group, M) ||
+	    !EC_POINT_is_on_curve(group, M, bnctx) ||
+	    EC_POINT_invert(group, Qi, bnctx) != 1 ||
+	    EC_POINT_add(group, X, M, Qi, bnctx) != 1 ||
+	    EC_POINT_is_at_infinity(group, X) ||
+	    !EC_POINT_is_on_curve(group, X, bnctx))
+		goto fail;
+
+	pkex = os_zalloc(sizeof(*pkex));
+	if (!pkex)
+		goto fail;
+	pkex->own_bi = bi;
+	os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
+	os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
+	if (identifier) {
+		pkex->identifier = os_strdup(identifier);
+		if (!pkex->identifier)
+			goto fail;
+	}
+	pkex->code = os_strdup(code);
+	if (!pkex->code)
+		goto fail;
+
+	os_memcpy(pkex->Mx, attr_key, attr_key_len / 2);
+
+	X_ec = EC_KEY_new();
+	if (!X_ec ||
+	    EC_KEY_set_group(X_ec, group) != 1 ||
+	    EC_KEY_set_public_key(X_ec, X) != 1)
+		goto fail;
+	pkex->x = EVP_PKEY_new();
+	if (!pkex->x ||
+	    EVP_PKEY_set1_EC_KEY(pkex->x, X_ec) != 1)
+		goto fail;
+
+	/* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
+	Qr = dpp_pkex_derive_Qr(curve, own_mac, code, identifier, bnctx, NULL);
+	if (!Qr)
+		goto fail;
+
+	/* Generate a random ephemeral keypair y/Y */
+	pkex->y = dpp_gen_keypair(curve);
+	if (!pkex->y)
+		goto fail;
+
+	/* N = Y + Qr */
+	Y_ec = EVP_PKEY_get1_EC_KEY(pkex->y);
+	if (!Y_ec)
+		goto fail;
+	Y_point = EC_KEY_get0_public_key(Y_ec);
+	if (!Y_point)
+		goto fail;
+	N = EC_POINT_new(group);
+	Nx = BN_new();
+	Ny = BN_new();
+	if (!N || !Nx || !Ny ||
+	    EC_POINT_add(group, N, Y_point, Qr, bnctx) != 1 ||
+	    EC_POINT_get_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1)
+		goto fail;
+
+	/* Initiator -> Responder: DPP Status, [identifier,] N */
+	attr_len = 4 + 1;
+	if (identifier)
+		attr_len += 4 + os_strlen(identifier);
+	attr_len += 4 + 2 * curve->prime_len;
+	msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_RESP, attr_len);
+	if (!msg)
+		goto fail;
+
+	/* DPP Status */
+	wpabuf_put_le16(msg, DPP_ATTR_STATUS);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, DPP_STATUS_OK);
+
+	/* Code Identifier attribute */
+	if (pkex->identifier) {
+		wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
+		wpabuf_put_le16(msg, os_strlen(pkex->identifier));
+		wpabuf_put_str(msg, pkex->identifier);
+	}
+
+	/* N in Encrypted Key attribute */
+	wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
+	wpabuf_put_le16(msg, 2 * curve->prime_len);
+
+	num_bytes = BN_num_bytes(Nx);
+	if ((size_t) num_bytes > curve->prime_len)
+		goto fail;
+	if (curve->prime_len > (size_t) num_bytes)
+		offset = curve->prime_len - num_bytes;
+	else
+		offset = 0;
+	os_memset(wpabuf_put(msg, offset), 0, offset);
+	BN_bn2bin(Nx, wpabuf_put(msg, num_bytes));
+	os_memset(pkex->Nx, 0, offset);
+	BN_bn2bin(Nx, pkex->Nx + offset);
+
+	num_bytes = BN_num_bytes(Ny);
+	if ((size_t) num_bytes > curve->prime_len)
+		goto fail;
+	if (curve->prime_len > (size_t) num_bytes)
+		offset = curve->prime_len - num_bytes;
+	else
+		offset = 0;
+	os_memset(wpabuf_put(msg, offset), 0, offset);
+	BN_bn2bin(Ny, wpabuf_put(msg, num_bytes));
+
+	pkex->exchange_resp = msg;
+	msg = NULL;
+	pkex->exchange_done = 1;
+
+out:
+	wpabuf_free(msg);
+	BN_CTX_free(bnctx);
+	EC_POINT_free(Qi);
+	EC_POINT_free(Qr);
+	BN_free(Mx);
+	BN_free(My);
+	BN_free(Nx);
+	BN_free(Ny);
+	EC_POINT_free(M);
+	EC_POINT_free(N);
+	EC_POINT_free(X);
+	EC_KEY_free(X_ec);
+	EC_KEY_free(Y_ec);
+	return pkex;
+fail:
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request processing faileed");
+	dpp_pkex_free(pkex);
+	pkex = NULL;
+	goto out;
+}
+
+
+static int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp,
+			     const u8 *Mx, size_t Mx_len,
+			     const u8 *Nx, size_t Nx_len,
+			     const char *code,
+			     const u8 *Kx, size_t Kx_len,
+			     u8 *z, unsigned int hash_len)
+{
+	u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+	int res;
+	u8 *info, *pos;
+	size_t info_len;
+
+	/* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
+	 */
+
+	/* HKDF-Extract(<>, IKM=K.x) */
+	os_memset(salt, 0, hash_len);
+	if (dpp_hmac(hash_len, salt, hash_len, Kx, Kx_len, prk) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
+			prk, hash_len);
+	info_len = 2 * ETH_ALEN + Mx_len + Nx_len + os_strlen(code);
+	info = os_malloc(info_len);
+	if (!info)
+		return -1;
+	pos = info;
+	os_memcpy(pos, mac_init, ETH_ALEN);
+	pos += ETH_ALEN;
+	os_memcpy(pos, mac_resp, ETH_ALEN);
+	pos += ETH_ALEN;
+	os_memcpy(pos, Mx, Mx_len);
+	pos += Mx_len;
+	os_memcpy(pos, Nx, Nx_len);
+	pos += Nx_len;
+	os_memcpy(pos, code, os_strlen(code));
+
+	/* HKDF-Expand(PRK, info, L) */
+	if (hash_len == 32)
+		res = hmac_sha256_kdf(prk, hash_len, NULL, info, info_len,
+				      z, hash_len);
+	else if (hash_len == 48)
+		res = hmac_sha384_kdf(prk, hash_len, NULL, info, info_len,
+				      z, hash_len);
+	else if (hash_len == 64)
+		res = hmac_sha512_kdf(prk, hash_len, NULL, info, info_len,
+				      z, hash_len);
+	else
+		res = -1;
+	os_free(info);
+	os_memset(prk, 0, hash_len);
+	if (res < 0)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: z = HKDF-Expand(PRK, info, L)",
+			z, hash_len);
+	return 0;
+}
+
+
+struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
+					  const u8 *buf, size_t buflen)
+{
+	const u8 *attr_status, *attr_id, *attr_key;
+	u16 attr_status_len, attr_id_len, attr_key_len;
+	const EC_GROUP *group;
+	BN_CTX *bnctx = NULL;
+	size_t clear_len;
+	struct wpabuf *clear = NULL;
+	u8 *wrapped;
+	struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
+	const struct dpp_curve_params *curve = pkex->own_bi->curve;
+	EC_POINT *Qr = NULL, *Y = NULL, *N = NULL;
+	BIGNUM *Nx = NULL, *Ny = NULL;
+	EVP_PKEY_CTX *ctx = NULL;
+	EC_KEY *Y_ec = NULL;
+	size_t Jx_len, Kx_len;
+	u8 Jx[DPP_MAX_SHARED_SECRET_LEN], Kx[DPP_MAX_SHARED_SECRET_LEN];
+	const u8 *addr[4];
+	size_t len[4];
+	u8 u[DPP_MAX_HASH_LEN];
+	u8 octet;
+	int res;
+
+	attr_status = dpp_get_attr(buf, buflen, DPP_ATTR_STATUS,
+				   &attr_status_len);
+	if (!attr_status || attr_status_len != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: No DPP Status attribute");
+		return NULL;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Status %u", attr_status[0]);
+	if (attr_status[0] != DPP_STATUS_OK) {
+		wpa_printf(MSG_DEBUG, "DPP: PKEX failed");
+		return NULL;
+	}
+
+	attr_id = dpp_get_attr(buf, buflen, DPP_ATTR_CODE_IDENTIFIER,
+			       &attr_id_len);
+	if (!attr_id && pkex->identifier) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No PKEX code identifier received, but expected one");
+		return NULL;
+	}
+	if (attr_id && pkex->identifier &&
+	    (os_strlen(pkex->identifier) != attr_id_len ||
+	     os_memcmp(pkex->identifier, attr_id, attr_id_len) != 0)) {
+		wpa_printf(MSG_DEBUG, "DPP: PKEX code identifier mismatch");
+		return NULL;
+	}
+
+	/* N in Encrypted Key attribute */
+	attr_key = dpp_get_attr(buf, buflen, DPP_ATTR_ENCRYPTED_KEY,
+				&attr_key_len);
+	if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing Encrypted Key attribute");
+		return NULL;
+	}
+
+	/* Qr = H(MAC-Responder | [identifier |] code) * Pr */
+	bnctx = BN_CTX_new();
+	if (!bnctx)
+		goto fail;
+	Qr = dpp_pkex_derive_Qr(curve, pkex->peer_mac, pkex->code,
+				pkex->identifier, bnctx, &group);
+	if (!Qr)
+		goto fail;
+
+	/* Y' = N - Qr */
+	Y = EC_POINT_new(group);
+	N = EC_POINT_new(group);
+	Nx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
+	Ny = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
+	if (!Y || !N || !Nx || !Ny ||
+	    EC_POINT_set_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1 ||
+	    EC_POINT_is_at_infinity(group, N) ||
+	    !EC_POINT_is_on_curve(group, N, bnctx) ||
+	    EC_POINT_invert(group, Qr, bnctx) != 1 ||
+	    EC_POINT_add(group, Y, N, Qr, bnctx) != 1 ||
+	    EC_POINT_is_at_infinity(group, Y) ||
+	    !EC_POINT_is_on_curve(group, Y, bnctx))
+		goto fail;
+
+	pkex->exchange_done = 1;
+
+	/* ECDH: J = a * Y’ */
+	Y_ec = EC_KEY_new();
+	if (!Y_ec ||
+	    EC_KEY_set_group(Y_ec, group) != 1 ||
+	    EC_KEY_set_public_key(Y_ec, Y) != 1)
+		goto fail;
+	pkex->y = EVP_PKEY_new();
+	if (!pkex->y ||
+	    EVP_PKEY_set1_EC_KEY(pkex->y, Y_ec) != 1)
+		goto fail;
+	ctx = EVP_PKEY_CTX_new(pkex->own_bi->pubkey, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, pkex->y) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &Jx_len) != 1 ||
+	    Jx_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, Jx, &Jx_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
+			Jx, Jx_len);
+
+	/* u = HMAC(J.x,  MAC-Initiator | A.x | Y’.x | X.x ) */
+	A_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
+	Y_pub = dpp_get_pubkey_point(pkex->y, 0);
+	X_pub = dpp_get_pubkey_point(pkex->x, 0);
+	if (!A_pub || !Y_pub || !X_pub)
+		goto fail;
+	addr[0] = pkex->own_mac;
+	len[0] = ETH_ALEN;
+	addr[1] = wpabuf_head(A_pub);
+	len[1] = wpabuf_len(A_pub) / 2;
+	addr[2] = wpabuf_head(Y_pub);
+	len[2] = wpabuf_len(Y_pub) / 2;
+	addr[3] = wpabuf_head(X_pub);
+	len[3] = wpabuf_len(X_pub) / 2;
+	if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: u", u, curve->hash_len);
+
+	/* K = x * Y’ */
+	EVP_PKEY_CTX_free(ctx);
+	ctx = EVP_PKEY_CTX_new(pkex->x, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, pkex->y) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &Kx_len) != 1 ||
+	    Kx_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, Kx, &Kx_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
+			Kx, Kx_len);
+
+	/* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
+	 */
+	res = dpp_pkex_derive_z(pkex->own_mac, pkex->peer_mac,
+				pkex->Mx, curve->prime_len,
+				attr_key /* N.x */, attr_key_len / 2,
+				pkex->code, Kx, Kx_len,
+				pkex->z, curve->hash_len);
+	os_memset(Kx, 0, Kx_len);
+	if (res < 0)
+		goto fail;
+
+	/* {A, u, [bootstrapping info]}z */
+	clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
+	clear = wpabuf_alloc(clear_len);
+	msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_REQ,
+			    4 + clear_len + AES_BLOCK_SIZE);
+	if (!clear || !msg)
+		goto fail;
+
+	/* A in Bootstrap Key attribute */
+	wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+	wpabuf_put_le16(clear, wpabuf_len(A_pub));
+	wpabuf_put_buf(clear, A_pub);
+
+	/* u in I-Auth tag attribute */
+	wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
+	wpabuf_put_le16(clear, curve->hash_len);
+	wpabuf_put_data(clear, u, curve->hash_len);
+
+	addr[0] = wpabuf_head_u8(msg) + 2;
+	len[0] = DPP_HDR_LEN;
+	octet = 0;
+	addr[1] = &octet;
+	len[1] = sizeof(octet);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+	wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+	if (aes_siv_encrypt(pkex->z, curve->hash_len,
+			    wpabuf_head(clear), wpabuf_len(clear),
+			    2, addr, len, wrapped) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+out:
+	wpabuf_free(clear);
+	wpabuf_free(A_pub);
+	wpabuf_free(X_pub);
+	wpabuf_free(Y_pub);
+	EC_POINT_free(Qr);
+	EC_POINT_free(Y);
+	EC_POINT_free(N);
+	BN_free(Nx);
+	BN_free(Ny);
+	EC_KEY_free(Y_ec);
+	EVP_PKEY_CTX_free(ctx);
+	BN_CTX_free(bnctx);
+	return msg;
+fail:
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response processing faileed");
+	wpabuf_free(msg);
+	msg = NULL;
+	goto out;
+}
+
+
+struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
+					      const u8 *hdr,
+					      const u8 *buf, size_t buflen)
+{
+	const struct dpp_curve_params *curve = pkex->own_bi->curve;
+	EVP_PKEY_CTX *ctx;
+	size_t Jx_len, Kx_len, Lx_len;
+	u8 Jx[DPP_MAX_SHARED_SECRET_LEN], Kx[DPP_MAX_SHARED_SECRET_LEN];
+	u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
+	const u8 *wrapped_data, *b_key, *peer_u;
+	u16 wrapped_data_len, b_key_len, peer_u_len = 0;
+	const u8 *addr[4];
+	size_t len[4];
+	u8 octet;
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
+	struct wpabuf *B_pub = NULL;
+	u8 u[DPP_MAX_HASH_LEN], v[DPP_MAX_HASH_LEN];
+	size_t clear_len;
+	struct wpabuf *clear = NULL;
+	u8 *wrapped;
+	int res;
+
+	/* K = y * X' */
+	ctx = EVP_PKEY_CTX_new(pkex->y, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, pkex->x) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &Kx_len) != 1 ||
+	    Kx_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, Kx, &Kx_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
+			Kx, Kx_len);
+
+	/* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
+	 */
+	res = dpp_pkex_derive_z(pkex->peer_mac, pkex->own_mac,
+				pkex->Mx, curve->prime_len,
+				pkex->Nx, curve->prime_len, pkex->code,
+				Kx, Kx_len, pkex->z, curve->hash_len);
+	os_memset(Kx, 0, Kx_len);
+	if (res < 0)
+		goto fail;
+
+	wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Wrapped data attribute");
+		goto fail;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		goto fail;
+
+	addr[0] = hdr;
+	len[0] = DPP_HDR_LEN;
+	octet = 0;
+	addr[1] = &octet;
+	len[1] = sizeof(octet);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+	if (aes_siv_decrypt(pkex->z, curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    2, addr, len, unwrapped) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped, unwrapped_len);
+
+	if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
+			     &b_key_len);
+	if (!b_key || b_key_len != 2 * curve->prime_len) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No valid peer bootstrapping key found");
+		goto fail;
+	}
+	pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
+							b_key_len);
+	if (!pkex->peer_bootstrap_key)
+		goto fail;
+	dpp_debug_print_key("DPP: Peer bootstrap public key",
+			    pkex->peer_bootstrap_key);
+
+	/* ECDH: J' = y * A' */
+	EVP_PKEY_CTX_free(ctx);
+	ctx = EVP_PKEY_CTX_new(pkex->y, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, pkex->peer_bootstrap_key) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &Jx_len) != 1 ||
+	    Jx_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, Jx, &Jx_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
+			Jx, Jx_len);
+
+	/* u' = HMAC(J'.x, MAC-Initiator | A'.x | Y.x | X'.x) */
+	A_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
+	Y_pub = dpp_get_pubkey_point(pkex->y, 0);
+	X_pub = dpp_get_pubkey_point(pkex->x, 0);
+	if (!A_pub || !Y_pub || !X_pub)
+		goto fail;
+	addr[0] = pkex->peer_mac;
+	len[0] = ETH_ALEN;
+	addr[1] = wpabuf_head(A_pub);
+	len[1] = wpabuf_len(A_pub) / 2;
+	addr[2] = wpabuf_head(Y_pub);
+	len[2] = wpabuf_len(Y_pub) / 2;
+	addr[3] = wpabuf_head(X_pub);
+	len[3] = wpabuf_len(X_pub) / 2;
+	if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
+		goto fail;
+
+	peer_u = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
+			      &peer_u_len);
+	if (!peer_u || peer_u_len != curve->hash_len ||
+	    os_memcmp(peer_u, u, curve->hash_len) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: No valid u (I-Auth tag) found");
+		wpa_hexdump(MSG_DEBUG, "DPP: Calculated u'",
+			    u, curve->hash_len);
+		wpa_hexdump(MSG_DEBUG, "DPP: Received u", peer_u, peer_u_len);
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Valid u (I-Auth tag) received");
+
+	/* ECDH: L = b * X' */
+	EVP_PKEY_CTX_free(ctx);
+	ctx = EVP_PKEY_CTX_new(pkex->own_bi->pubkey, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, pkex->x) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &Lx_len) != 1 ||
+	    Lx_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, Lx, &Lx_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
+			Lx, Lx_len);
+
+	/* v = HMAC(L.x, MAC-Responder | B.x | X'.x | Y.x) */
+	B_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
+	if (!B_pub)
+		goto fail;
+	addr[0] = pkex->own_mac;
+	len[0] = ETH_ALEN;
+	addr[1] = wpabuf_head(B_pub);
+	len[1] = wpabuf_len(B_pub) / 2;
+	addr[2] = wpabuf_head(X_pub);
+	len[2] = wpabuf_len(X_pub) / 2;
+	addr[3] = wpabuf_head(Y_pub);
+	len[3] = wpabuf_len(Y_pub) / 2;
+	if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: v", v, curve->hash_len);
+
+	/* {B, v [bootstrapping info]}z */
+	clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
+	clear = wpabuf_alloc(clear_len);
+	msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_RESP,
+			    4 + clear_len + AES_BLOCK_SIZE);
+	if (!clear || !msg)
+		goto fail;
+
+	/* A in Bootstrap Key attribute */
+	wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+	wpabuf_put_le16(clear, wpabuf_len(B_pub));
+	wpabuf_put_buf(clear, B_pub);
+
+	/* v in R-Auth tag attribute */
+	wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
+	wpabuf_put_le16(clear, curve->hash_len);
+	wpabuf_put_data(clear, v, curve->hash_len);
+
+	addr[0] = wpabuf_head_u8(msg) + 2;
+	len[0] = DPP_HDR_LEN;
+	octet = 1;
+	addr[1] = &octet;
+	len[1] = sizeof(octet);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+	wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+	if (aes_siv_encrypt(pkex->z, curve->hash_len,
+			    wpabuf_head(clear), wpabuf_len(clear),
+			    2, addr, len, wrapped) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+out:
+	EVP_PKEY_CTX_free(ctx);
+	os_free(unwrapped);
+	wpabuf_free(A_pub);
+	wpabuf_free(B_pub);
+	wpabuf_free(X_pub);
+	wpabuf_free(Y_pub);
+	wpabuf_free(clear);
+	return msg;
+fail:
+	wpabuf_free(msg);
+	msg = NULL;
+	goto out;
+}
+
+
+int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr,
+				   const u8 *buf, size_t buflen)
+{
+	const struct dpp_curve_params *curve = pkex->own_bi->curve;
+	const u8 *wrapped_data, *b_key, *peer_v;
+	u16 wrapped_data_len, b_key_len, peer_v_len = 0;
+	const u8 *addr[4];
+	size_t len[4];
+	u8 octet;
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	int ret = -1;
+	u8 v[DPP_MAX_HASH_LEN];
+	size_t Lx_len;
+	u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
+	EVP_PKEY_CTX *ctx = NULL;
+	struct wpabuf *B_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
+
+	wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Wrapped data attribute");
+		goto fail;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		goto fail;
+
+	addr[0] = hdr;
+	len[0] = DPP_HDR_LEN;
+	octet = 1;
+	addr[1] = &octet;
+	len[1] = sizeof(octet);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+	if (aes_siv_decrypt(pkex->z, curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    2, addr, len, unwrapped) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped, unwrapped_len);
+
+	if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
+			     &b_key_len);
+	if (!b_key || b_key_len != 2 * curve->prime_len) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No valid peer bootstrapping key found");
+		goto fail;
+	}
+	pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
+							b_key_len);
+	if (!pkex->peer_bootstrap_key)
+		goto fail;
+	dpp_debug_print_key("DPP: Peer bootstrap public key",
+			    pkex->peer_bootstrap_key);
+
+	/* ECDH: L' = x * B' */
+	ctx = EVP_PKEY_CTX_new(pkex->x, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, pkex->peer_bootstrap_key) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &Lx_len) != 1 ||
+	    Lx_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, Lx, &Lx_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
+			Lx, Lx_len);
+
+	/* v' = HMAC(L.x, MAC-Responder | B'.x | X.x | Y'.x) */
+	B_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
+	X_pub = dpp_get_pubkey_point(pkex->x, 0);
+	Y_pub = dpp_get_pubkey_point(pkex->y, 0);
+	if (!B_pub || !X_pub || !Y_pub)
+		goto fail;
+	addr[0] = pkex->peer_mac;
+	len[0] = ETH_ALEN;
+	addr[1] = wpabuf_head(B_pub);
+	len[1] = wpabuf_len(B_pub) / 2;
+	addr[2] = wpabuf_head(X_pub);
+	len[2] = wpabuf_len(X_pub) / 2;
+	addr[3] = wpabuf_head(Y_pub);
+	len[3] = wpabuf_len(Y_pub) / 2;
+	if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
+		goto fail;
+
+	peer_v = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_AUTH_TAG,
+			      &peer_v_len);
+	if (!peer_v || peer_v_len != curve->hash_len ||
+	    os_memcmp(peer_v, v, curve->hash_len) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: No valid v (R-Auth tag) found");
+		wpa_hexdump(MSG_DEBUG, "DPP: Calculated v'",
+			    v, curve->hash_len);
+		wpa_hexdump(MSG_DEBUG, "DPP: Received v", peer_v, peer_v_len);
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Valid v (R-Auth tag) received");
+
+	ret = 0;
+out:
+	wpabuf_free(B_pub);
+	wpabuf_free(X_pub);
+	wpabuf_free(Y_pub);
+	EVP_PKEY_CTX_free(ctx);
+	os_free(unwrapped);
+	return ret;
+fail:
+	goto out;
+}
+
+
+void dpp_pkex_free(struct dpp_pkex *pkex)
+{
+	if (!pkex)
+		return;
+
+	os_free(pkex->identifier);
+	os_free(pkex->code);
+	EVP_PKEY_free(pkex->x);
+	EVP_PKEY_free(pkex->y);
+	EVP_PKEY_free(pkex->peer_bootstrap_key);
+	wpabuf_free(pkex->exchange_req);
+	wpabuf_free(pkex->exchange_resp);
+	os_free(pkex);
+}
diff --git a/src/common/dpp.h b/src/common/dpp.h
new file mode 100644
index 0000000..dfee499
--- /dev/null
+++ b/src/common/dpp.h
@@ -0,0 +1,277 @@
+/*
+ * DPP functionality shared between hostapd and wpa_supplicant
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DPP_H
+#define DPP_H
+
+#include <openssl/x509.h>
+
+#include "utils/list.h"
+#include "common/wpa_common.h"
+#include "crypto/sha256.h"
+
+#define DPP_HDR_LEN (4 + 2) /* OUI, OUI Type, Crypto Suite, DPP frame type */
+
+enum dpp_public_action_frame_type {
+	DPP_PA_AUTHENTICATION_REQ = 0,
+	DPP_PA_AUTHENTICATION_RESP = 1,
+	DPP_PA_AUTHENTICATION_CONF = 2,
+	DPP_PA_PEER_DISCOVERY_REQ = 5,
+	DPP_PA_PEER_DISCOVERY_RESP = 6,
+	DPP_PA_PKEX_EXCHANGE_REQ = 7,
+	DPP_PA_PKEX_EXCHANGE_RESP = 8,
+	DPP_PA_PKEX_COMMIT_REVEAL_REQ = 9,
+	DPP_PA_PKEX_COMMIT_REVEAL_RESP = 10,
+};
+
+enum dpp_attribute_id {
+	DPP_ATTR_STATUS = 0x1000,
+	DPP_ATTR_I_BOOTSTRAP_KEY_HASH = 0x1001,
+	DPP_ATTR_R_BOOTSTRAP_KEY_HASH = 0x1002,
+	DPP_ATTR_I_PROTOCOL_KEY = 0x1003,
+	DPP_ATTR_WRAPPED_DATA = 0x1004,
+	DPP_ATTR_I_NONCE = 0x1005,
+	DPP_ATTR_I_CAPABILITIES = 0x1006,
+	DPP_ATTR_R_NONCE = 0x1007,
+	DPP_ATTR_R_CAPABILITIES = 0x1008,
+	DPP_ATTR_R_PROTOCOL_KEY = 0x1009,
+	DPP_ATTR_I_AUTH_TAG = 0x100A,
+	DPP_ATTR_R_AUTH_TAG = 0x100B,
+	DPP_ATTR_CONFIG_OBJ = 0x100C,
+	DPP_ATTR_CONNECTOR = 0x100D,
+	DPP_ATTR_CONFIG_ATTR_OBJ = 0x100E,
+	DPP_ATTR_BOOTSTRAP_KEY = 0x100F,
+	DPP_ATTR_OWN_NET_NK_HASH = 0x1011,
+	DPP_ATTR_FINITE_CYCLIC_GROUP = 0x1012,
+	DPP_ATTR_ENCRYPTED_KEY = 0x1013,
+	DPP_ATTR_ENROLLEE_NONCE = 0x1014,
+	DPP_ATTR_CODE_IDENTIFIER = 0x1015,
+	DPP_ATTR_TRANSACTION_ID = 0x1016,
+};
+
+enum dpp_status_error {
+	DPP_STATUS_OK = 0,
+	DPP_STATUS_NOT_COMPATIBLE = 1,
+	DPP_STATUS_AUTH_FAILURE = 2,
+	DPP_STATUS_UNWRAP_FAILURE = 3,
+	DPP_STATUS_BAD_GROUP = 4,
+	DPP_STATUS_CONFIGURE_FAILURE = 5,
+	DPP_STATUS_RESPONSE_PENDING = 6,
+};
+
+#define DPP_CAPAB_ENROLLEE BIT(0)
+#define DPP_CAPAB_CONFIGURATOR BIT(1)
+#define DPP_CAPAB_ROLE_MASK (BIT(0) | BIT(1))
+
+#define DPP_BOOTSTRAP_MAX_FREQ 30
+#define DPP_MAX_NONCE_LEN 32
+#define DPP_MAX_HASH_LEN 64
+#define DPP_MAX_SHARED_SECRET_LEN 66
+
+struct dpp_curve_params {
+	const char *name;
+	size_t hash_len;
+	size_t aes_siv_key_len;
+	size_t nonce_len;
+	size_t prime_len;
+	const char *jwk_crv;
+	u16 ike_group;
+	const char *jws_alg;
+};
+
+enum dpp_bootstrap_type {
+	DPP_BOOTSTRAP_QR_CODE,
+	DPP_BOOTSTRAP_PKEX,
+};
+
+struct dpp_bootstrap_info {
+	struct dl_list list;
+	unsigned int id;
+	enum dpp_bootstrap_type type;
+	char *uri;
+	u8 mac_addr[ETH_ALEN];
+	char *info;
+	unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ];
+	unsigned int num_freq;
+	int own;
+	EVP_PKEY *pubkey;
+	u8 pubkey_hash[SHA256_MAC_LEN];
+	const struct dpp_curve_params *curve;
+};
+
+struct dpp_pkex {
+	unsigned int initiator:1;
+	unsigned int exchange_done:1;
+	struct dpp_bootstrap_info *own_bi;
+	u8 own_mac[ETH_ALEN];
+	u8 peer_mac[ETH_ALEN];
+	char *identifier;
+	char *code;
+	EVP_PKEY *x;
+	EVP_PKEY *y;
+	u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
+	u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
+	u8 z[DPP_MAX_HASH_LEN];
+	EVP_PKEY *peer_bootstrap_key;
+	struct wpabuf *exchange_req;
+	struct wpabuf *exchange_resp;
+};
+
+struct dpp_configuration {
+	u8 ssid[32];
+	size_t ssid_len;
+	int dpp; /* whether to use DPP or legacy configuration */
+
+	/* For DPP configuration (connector) */
+	os_time_t netaccesskey_expiry;
+
+	/* TODO: groups */
+
+	/* For legacy configuration */
+	char *passphrase;
+	u8 psk[32];
+};
+
+struct dpp_authentication {
+	void *msg_ctx;
+	const struct dpp_curve_params *curve;
+	struct dpp_bootstrap_info *peer_bi;
+	struct dpp_bootstrap_info *own_bi;
+	u8 waiting_pubkey_hash[SHA256_MAC_LEN];
+	int response_pending;
+	enum dpp_status_error auth_resp_status;
+	u8 peer_mac_addr[ETH_ALEN];
+	u8 i_nonce[DPP_MAX_NONCE_LEN];
+	u8 r_nonce[DPP_MAX_NONCE_LEN];
+	u8 e_nonce[DPP_MAX_NONCE_LEN];
+	u8 i_capab;
+	u8 r_capab;
+	EVP_PKEY *own_protocol_key;
+	EVP_PKEY *peer_protocol_key;
+	struct wpabuf *req_msg;
+	struct wpabuf *resp_msg;
+	unsigned int curr_freq;
+	size_t secret_len;
+	u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
+	u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
+	u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
+	u8 k1[DPP_MAX_HASH_LEN];
+	u8 k2[DPP_MAX_HASH_LEN];
+	u8 ke[DPP_MAX_HASH_LEN];
+	int initiator;
+	int configurator;
+	int remove_on_tx_status;
+	int auth_success;
+	struct wpabuf *conf_req;
+	struct dpp_configuration *conf_ap;
+	struct dpp_configuration *conf_sta;
+	struct dpp_configurator *conf;
+	char *connector; /* received signedConnector */
+	u8 ssid[SSID_MAX_LEN];
+	u8 ssid_len;
+	char passphrase[64];
+	u8 psk[PMK_LEN];
+	int psk_set;
+	struct wpabuf *net_access_key;
+	os_time_t net_access_key_expiry;
+	struct wpabuf *c_sign_key;
+#ifdef CONFIG_TESTING_OPTIONS
+	char *config_obj_override;
+	char *discovery_override;
+	char *groups_override;
+	unsigned int ignore_netaccesskey_mismatch:1;
+#endif /* CONFIG_TESTING_OPTIONS */
+};
+
+struct dpp_configurator {
+	struct dl_list list;
+	unsigned int id;
+	int own;
+	EVP_PKEY *csign;
+	char *kid;
+	const struct dpp_curve_params *curve;
+};
+
+struct dpp_introduction {
+	u8 pmkid[PMKID_LEN];
+	u8 pmk[PMK_LEN_MAX];
+	size_t pmk_len;
+};
+
+void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info);
+const char * dpp_bootstrap_type_txt(enum dpp_bootstrap_type type);
+int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi);
+int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi,
+			    const char *chan_list);
+int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac);
+int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info);
+struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri);
+char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
+		  const u8 *privkey, size_t privkey_len);
+struct dpp_authentication * dpp_auth_init(void *msg_ctx,
+					  struct dpp_bootstrap_info *peer_bi,
+					  struct dpp_bootstrap_info *own_bi,
+					  int configurator);
+struct dpp_authentication *
+dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
+		struct dpp_bootstrap_info *peer_bi,
+		struct dpp_bootstrap_info *own_bi,
+		unsigned int freq, const u8 *hdr, const u8 *attr_start,
+		const u8 *wrapped_data, u16 wrapped_data_len);
+struct wpabuf *
+dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
+		 const u8 *attr_start, size_t attr_len);
+struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
+				   const char *json);
+int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
+		     const u8 *attr_start, size_t attr_len);
+int dpp_notify_new_qr_code(struct dpp_authentication *auth,
+			   struct dpp_bootstrap_info *peer_bi);
+void dpp_configuration_free(struct dpp_configuration *conf);
+void dpp_auth_deinit(struct dpp_authentication *auth);
+struct wpabuf *
+dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
+		size_t attr_len);
+int dpp_conf_resp_rx(struct dpp_authentication *auth,
+		     const struct wpabuf *resp);
+struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type,
+			      size_t len);
+const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len);
+int dpp_check_attrs(const u8 *buf, size_t len);
+int dpp_key_expired(const char *timestamp, os_time_t *expiry);
+void dpp_configurator_free(struct dpp_configurator *conf);
+struct dpp_configurator *
+dpp_keygen_configurator(const char *curve, const u8 *privkey,
+			size_t privkey_len);
+int dpp_configurator_own_config(struct dpp_authentication *auth,
+				const char *curve);
+int dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
+		   const u8 *net_access_key, size_t net_access_key_len,
+		   const u8 *csign_key, size_t csign_key_len,
+		   const u8 *peer_connector, size_t peer_connector_len,
+		   os_time_t *expiry);
+struct dpp_pkex * dpp_pkex_init(struct dpp_bootstrap_info *bi,
+				const u8 *own_mac,
+				const char *identifier,
+				const char *code);
+struct dpp_pkex * dpp_pkex_rx_exchange_req(struct dpp_bootstrap_info *bi,
+					   const u8 *own_mac,
+					   const u8 *peer_mac,
+					   const char *identifier,
+					   const char *code,
+					   const u8 *buf, size_t len);
+struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
+					  const u8 *buf, size_t len);
+struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
+					      const u8 *hdr,
+					      const u8 *buf, size_t len);
+int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr,
+				   const u8 *buf, size_t len);
+void dpp_pkex_free(struct dpp_pkex *pkex);
+
+#endif /* DPP_H */
diff --git a/src/common/gas.c b/src/common/gas.c
index cff9254..ba21b22 100644
--- a/src/common/gas.c
+++ b/src/common/gas.c
@@ -75,7 +75,7 @@
 }
 
 
-static struct wpabuf *
+struct wpabuf *
 gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more,
 			u16 comeback_delay, size_t size)
 {
diff --git a/src/common/gas.h b/src/common/gas.h
index 306adc5..4c93e31 100644
--- a/src/common/gas.h
+++ b/src/common/gas.h
@@ -14,6 +14,9 @@
 struct wpabuf * gas_build_comeback_req(u8 dialog_token);
 struct wpabuf * gas_build_initial_resp(u8 dialog_token, u16 status_code,
 				       u16 comeback_delay, size_t size);
+struct wpabuf *
+gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more,
+			u16 comeback_delay, size_t size);
 struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size);
 struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code,
 					    u16 comeback_delay, size_t size);
diff --git a/src/common/gas_server.c b/src/common/gas_server.c
new file mode 100644
index 0000000..b258675
--- /dev/null
+++ b/src/common/gas_server.c
@@ -0,0 +1,483 @@
+/*
+ * Generic advertisement service (GAS) server
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "utils/common.h"
+#include "utils/list.h"
+#include "utils/eloop.h"
+#include "ieee802_11_defs.h"
+#include "gas.h"
+#include "gas_server.h"
+
+
+#define MAX_ADV_PROTO_ID_LEN 10
+#define GAS_QUERY_TIMEOUT 10
+
+struct gas_server_handler {
+	struct dl_list list;
+	u8 adv_proto_id[MAX_ADV_PROTO_ID_LEN];
+	u8 adv_proto_id_len;
+	struct wpabuf * (*req_cb)(void *ctx, const u8 *sa,
+				  const u8 *query, size_t query_len);
+	void (*status_cb)(void *ctx, struct wpabuf *resp, int ok);
+	void *ctx;
+	struct gas_server *gas;
+};
+
+struct gas_server_response {
+	struct dl_list list;
+	size_t offset;
+	u8 frag_id;
+	struct wpabuf *resp;
+	int freq;
+	u8 dst[ETH_ALEN];
+	u8 dialog_token;
+	struct gas_server_handler *handler;
+};
+
+struct gas_server {
+	struct dl_list handlers; /* struct gas_server_handler::list */
+	struct dl_list responses; /* struct gas_server_response::list */
+	void (*tx)(void *ctx, int freq, const u8 *da, struct wpabuf *resp,
+		   unsigned int wait_time);
+	void *ctx;
+};
+
+static void gas_server_free_response(struct gas_server_response *response);
+
+
+static void gas_server_response_timeout(void *eloop_ctx, void *user_ctx)
+{
+	struct gas_server_response *response = eloop_ctx;
+
+	wpa_printf(MSG_DEBUG, "GAS: Response @%p timeout for " MACSTR
+		   " (dialog_token=%u freq=%d frag_id=%u sent=%lu/%lu) - drop pending data",
+		   response, MAC2STR(response->dst), response->dialog_token,
+		   response->freq, response->frag_id,
+		   (unsigned long) response->offset,
+		   (unsigned long) wpabuf_len(response->resp));
+	response->handler->status_cb(response->handler->ctx,
+				     response->resp, 0);
+	response->resp = NULL;
+	dl_list_del(&response->list);
+	gas_server_free_response(response);
+}
+
+
+static void gas_server_free_response(struct gas_server_response *response)
+{
+	if (!response)
+		return;
+	wpa_printf(MSG_DEBUG, "DPP: Free GAS response @%p", response);
+	eloop_cancel_timeout(gas_server_response_timeout, response, NULL);
+	wpabuf_free(response->resp);
+	os_free(response);
+}
+
+
+static void
+gas_server_send_resp(struct gas_server *gas, struct gas_server_handler *handler,
+		     const u8 *da, int freq, u8 dialog_token,
+		     struct wpabuf *query_resp)
+{
+	size_t max_len = (freq > 56160) ? 928 : 1400;
+	size_t hdr_len = 24 + 2 + 5 + 3 + handler->adv_proto_id_len + 2;
+	size_t resp_frag_len;
+	struct wpabuf *resp;
+	u16 comeback_delay;
+	struct gas_server_response *response;
+
+	if (!query_resp)
+		return;
+
+	response = os_zalloc(sizeof(*response));
+	if (!response)
+		return;
+	wpa_printf(MSG_DEBUG, "DPP: Allocated GAS response @%p", response);
+	response->freq = freq;
+	response->handler = handler;
+	os_memcpy(response->dst, da, ETH_ALEN);
+	response->dialog_token = dialog_token;
+	if (hdr_len + wpabuf_len(query_resp) > max_len) {
+		/* Need to use comeback to initiate fragmentation */
+		comeback_delay = 1;
+		resp_frag_len = 0;
+	} else {
+		/* Full response fits into the initial response */
+		comeback_delay = 0;
+		resp_frag_len = wpabuf_len(query_resp);
+	}
+
+	resp = gas_build_initial_resp(dialog_token, WLAN_STATUS_SUCCESS,
+				      comeback_delay,
+				      handler->adv_proto_id_len +
+				      resp_frag_len);
+	if (!resp) {
+		gas_server_free_response(response);
+		return;
+	}
+
+	/* Advertisement Protocol element */
+	wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO);
+	wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */
+	wpabuf_put_u8(resp, 0x7f);
+	/* Advertisement Protocol ID */
+	wpabuf_put_data(resp, handler->adv_proto_id, handler->adv_proto_id_len);
+
+	/* Query Response Length */
+	wpabuf_put_le16(resp, resp_frag_len);
+	if (!comeback_delay)
+		wpabuf_put_buf(resp, query_resp);
+
+	if (comeback_delay) {
+		wpa_printf(MSG_DEBUG,
+			   "GAS: Need to fragment query response");
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "GAS: Full query response fits in the GAS Initial Response frame");
+	}
+	response->offset = resp_frag_len;
+	response->resp = query_resp;
+	dl_list_add(&gas->responses, &response->list);
+	gas->tx(gas->ctx, freq, da, resp, comeback_delay ? 2000 : 0);
+	wpabuf_free(resp);
+	eloop_register_timeout(GAS_QUERY_TIMEOUT, 0,
+			       gas_server_response_timeout, response, NULL);
+}
+
+
+static int
+gas_server_rx_initial_req(struct gas_server *gas, const u8 *da, const u8 *sa,
+			  const u8 *bssid, int freq, u8 dialog_token,
+			  const u8 *data, size_t len)
+{
+	const u8 *pos, *end, *adv_proto, *query_req;
+	u8 adv_proto_len;
+	u16 query_req_len;
+	struct gas_server_handler *handler;
+	struct wpabuf *resp;
+
+	wpa_hexdump(MSG_MSGDUMP, "GAS: Received GAS Initial Request frame",
+		    data, len);
+	pos = data;
+	end = data + len;
+
+	if (end - pos < 2 || pos[0] != WLAN_EID_ADV_PROTO) {
+		wpa_printf(MSG_DEBUG,
+			   "GAS: No Advertisement Protocol element found");
+		return -1;
+	}
+	pos++;
+	adv_proto_len = *pos++;
+	if (end - pos < adv_proto_len || adv_proto_len < 2) {
+		wpa_printf(MSG_DEBUG,
+			   "GAS: Truncated Advertisement Protocol element");
+		return -1;
+	}
+
+	adv_proto = pos;
+	pos += adv_proto_len;
+	wpa_hexdump(MSG_MSGDUMP, "GAS: Advertisement Protocol element",
+		    adv_proto, adv_proto_len);
+
+	if (end - pos < 2) {
+		wpa_printf(MSG_DEBUG, "GAS: No Query Request Length field");
+		return -1;
+	}
+	query_req_len = WPA_GET_LE16(pos);
+	pos += 2;
+	if (end - pos < query_req_len) {
+		wpa_printf(MSG_DEBUG, "GAS: Truncated Query Request field");
+		return -1;
+	}
+	query_req = pos;
+	pos += query_req_len;
+	wpa_hexdump(MSG_MSGDUMP, "GAS: Query Request",
+		    query_req, query_req_len);
+
+	if (pos < end) {
+		wpa_hexdump(MSG_MSGDUMP,
+			    "GAS: Ignored extra data after Query Request field",
+			    pos, end - pos);
+	}
+
+	dl_list_for_each(handler, &gas->handlers, struct gas_server_handler,
+			 list) {
+		if (adv_proto_len < 1 + handler->adv_proto_id_len ||
+		    os_memcmp(adv_proto + 1, handler->adv_proto_id,
+			      handler->adv_proto_id_len) != 0)
+			continue;
+
+		wpa_printf(MSG_DEBUG,
+			   "GAS: Calling handler for the requested Advertisement Protocol ID");
+		resp = handler->req_cb(handler->ctx, sa, query_req,
+				       query_req_len);
+		wpa_hexdump_buf(MSG_MSGDUMP, "GAS: Response from the handler",
+				resp);
+		gas_server_send_resp(gas, handler, sa, freq, dialog_token,
+				     resp);
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "GAS: No registered handler for the requested Advertisement Protocol ID");
+	return -1;
+}
+
+
+static void
+gas_server_handle_rx_comeback_req(struct gas_server_response *response)
+{
+	struct gas_server_handler *handler = response->handler;
+	struct gas_server *gas = handler->gas;
+	size_t max_len = (response->freq > 56160) ? 928 : 1400;
+	size_t hdr_len = 24 + 2 + 6 + 3 + handler->adv_proto_id_len + 2;
+	size_t remaining, resp_frag_len;
+	struct wpabuf *resp;
+
+	remaining = wpabuf_len(response->resp) - response->offset;
+	if (hdr_len + remaining > max_len)
+		resp_frag_len = max_len - hdr_len;
+	else
+		resp_frag_len = remaining;
+	wpa_printf(MSG_DEBUG,
+		   "GAS: Sending out %u/%u remaining Query Response octets",
+		   (unsigned int) resp_frag_len, (unsigned int) remaining);
+
+	resp = gas_build_comeback_resp(response->dialog_token,
+				       WLAN_STATUS_SUCCESS,
+				       response->frag_id++,
+				       resp_frag_len < remaining, 0,
+				       handler->adv_proto_id_len +
+				       resp_frag_len);
+	if (!resp) {
+		gas_server_free_response(response);
+		return;
+	}
+
+	/* Advertisement Protocol element */
+	wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO);
+	wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */
+	wpabuf_put_u8(resp, 0x7f);
+	/* Advertisement Protocol ID */
+	wpabuf_put_data(resp, handler->adv_proto_id, handler->adv_proto_id_len);
+
+	/* Query Response Length */
+	wpabuf_put_le16(resp, resp_frag_len);
+	wpabuf_put_data(resp, wpabuf_head_u8(response->resp) + response->offset,
+			resp_frag_len);
+
+	response->offset += resp_frag_len;
+
+	gas->tx(gas->ctx, response->freq, response->dst, resp,
+		remaining > resp_frag_len ? 2000 : 0);
+	wpabuf_free(resp);
+}
+
+
+static int
+gas_server_rx_comeback_req(struct gas_server *gas, const u8 *da, const u8 *sa,
+			   const u8 *bssid, int freq, u8 dialog_token)
+{
+	struct gas_server_response *response;
+
+	dl_list_for_each(response, &gas->responses, struct gas_server_response,
+			 list) {
+		if (response->dialog_token != dialog_token ||
+		    os_memcmp(sa, response->dst, ETH_ALEN) != 0)
+			continue;
+		gas_server_handle_rx_comeback_req(response);
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "GAS: No pending GAS response for " MACSTR
+		   " (dialog token %u)", MAC2STR(sa), dialog_token);
+	return -1;
+}
+
+
+/**
+ * gas_query_rx - Indicate reception of a Public Action or Protected Dual frame
+ * @gas: GAS query data from gas_server_init()
+ * @da: Destination MAC address of the Action frame
+ * @sa: Source MAC address of the Action frame
+ * @bssid: BSSID of the Action frame
+ * @categ: Category of the Action frame
+ * @data: Payload of the Action frame
+ * @len: Length of @data
+ * @freq: Frequency (in MHz) on which the frame was received
+ * Returns: 0 if the Public Action frame was a GAS request frame or -1 if not
+ */
+int gas_server_rx(struct gas_server *gas, const u8 *da, const u8 *sa,
+		  const u8 *bssid, u8 categ, const u8 *data, size_t len,
+		  int freq)
+{
+	u8 action, dialog_token;
+	const u8 *pos, *end;
+
+	if (!gas || len < 2)
+		return -1;
+
+	if (categ == WLAN_ACTION_PROTECTED_DUAL)
+		return -1; /* Not supported for now */
+
+	pos = data;
+	end = data + len;
+	action = *pos++;
+	dialog_token = *pos++;
+
+	if (action != WLAN_PA_GAS_INITIAL_REQ &&
+	    action != WLAN_PA_GAS_COMEBACK_REQ)
+		return -1; /* Not a GAS request */
+
+	wpa_printf(MSG_DEBUG, "GAS: Received GAS %s Request frame DA=" MACSTR
+		   " SA=" MACSTR " BSSID=" MACSTR
+		   " freq=%d dialog_token=%u len=%u",
+		   action == WLAN_PA_GAS_INITIAL_REQ ? "Initial" : "Comeback",
+		   MAC2STR(da), MAC2STR(sa), MAC2STR(bssid), freq, dialog_token,
+		   (unsigned int) len);
+
+	if (action == WLAN_PA_GAS_INITIAL_REQ)
+		return gas_server_rx_initial_req(gas, da, sa, bssid,
+						 freq, dialog_token,
+						 pos, end - pos);
+	return gas_server_rx_comeback_req(gas, da, sa, bssid,
+					  freq, dialog_token);
+}
+
+
+static void gas_server_handle_tx_status(struct gas_server_response *response,
+					int ack)
+{
+	if (ack && response->offset < wpabuf_len(response->resp)) {
+		wpa_printf(MSG_DEBUG,
+			   "GAS: More fragments remaining - keep pending entry");
+		return;
+	}
+
+	if (!ack)
+		wpa_printf(MSG_DEBUG,
+			   "GAS: No ACK received - drop pending entry");
+	else
+		wpa_printf(MSG_DEBUG,
+			   "GAS: Last fragment of the response sent out - drop pending entry");
+
+	response->handler->status_cb(response->handler->ctx,
+				     response->resp, ack);
+	response->resp = NULL;
+	dl_list_del(&response->list);
+	gas_server_free_response(response);
+}
+
+
+void gas_server_tx_status(struct gas_server *gas, const u8 *dst, const u8 *data,
+			  size_t data_len, int ack)
+{
+	const u8 *pos;
+	u8 action, code, dialog_token;
+	struct gas_server_response *response;
+
+	if (data_len < 24 + 3)
+		return;
+	pos = data + 24;
+	action = *pos++;
+	code = *pos++;
+	dialog_token = *pos++;
+	if (action != WLAN_ACTION_PUBLIC ||
+	    (code != WLAN_PA_GAS_INITIAL_RESP &&
+	     code != WLAN_PA_GAS_COMEBACK_RESP))
+		return;
+	wpa_printf(MSG_DEBUG, "GAS: TX status dst=" MACSTR
+		   " ack=%d %s dialog_token=%u",
+		   MAC2STR(dst), ack,
+		   code == WLAN_PA_GAS_INITIAL_RESP ? "initial" : "comeback",
+		   dialog_token);
+	dl_list_for_each(response, &gas->responses, struct gas_server_response,
+			 list) {
+		if (response->dialog_token != dialog_token ||
+		    os_memcmp(dst, response->dst, ETH_ALEN) != 0)
+			continue;
+		gas_server_handle_tx_status(response, ack);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "GAS: No pending response matches TX status");
+}
+
+
+struct gas_server * gas_server_init(void *ctx,
+				    void (*tx)(void *ctx, int freq,
+					       const u8 *da,
+					       struct wpabuf *buf,
+					       unsigned int wait_time))
+{
+	struct gas_server *gas;
+
+	gas = os_zalloc(sizeof(*gas));
+	if (!gas)
+		return NULL;
+	gas->ctx = ctx;
+	gas->tx = tx;
+	dl_list_init(&gas->handlers);
+	dl_list_init(&gas->responses);
+	return gas;
+}
+
+
+void gas_server_deinit(struct gas_server *gas)
+{
+	struct gas_server_handler *handler, *tmp;
+	struct gas_server_response *response, *tmp_r;
+
+	if (!gas)
+		return;
+
+	dl_list_for_each_safe(handler, tmp, &gas->handlers,
+			      struct gas_server_handler, list) {
+		dl_list_del(&handler->list);
+		os_free(handler);
+	}
+
+	dl_list_for_each_safe(response, tmp_r, &gas->responses,
+			      struct gas_server_response, list) {
+		dl_list_del(&response->list);
+		gas_server_free_response(response);
+	}
+
+	os_free(gas);
+}
+
+
+int gas_server_register(struct gas_server *gas,
+			const u8 *adv_proto_id, u8 adv_proto_id_len,
+			struct wpabuf *
+			(*req_cb)(void *ctx, const u8 *sa,
+				  const u8 *query, size_t query_len),
+			void (*status_cb)(void *ctx, struct wpabuf *resp,
+					  int ok),
+			void *ctx)
+{
+	struct gas_server_handler *handler;
+
+	if (!gas || adv_proto_id_len > MAX_ADV_PROTO_ID_LEN)
+		return -1;
+	handler = os_zalloc(sizeof(*handler));
+	if (!handler)
+		return -1;
+
+	os_memcpy(handler->adv_proto_id, adv_proto_id, adv_proto_id_len);
+	handler->adv_proto_id_len = adv_proto_id_len;
+	handler->req_cb = req_cb;
+	handler->status_cb = status_cb;
+	handler->ctx = ctx;
+	handler->gas = gas;
+	dl_list_add(&gas->handlers, &handler->list);
+
+	return 0;
+}
diff --git a/src/common/gas_server.h b/src/common/gas_server.h
new file mode 100644
index 0000000..299f529
--- /dev/null
+++ b/src/common/gas_server.h
@@ -0,0 +1,44 @@
+/*
+ * Generic advertisement service (GAS) server
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef GAS_SERVER_H
+#define GAS_SERVER_H
+
+#ifdef CONFIG_GAS_SERVER
+
+struct gas_server;
+
+struct gas_server * gas_server_init(void *ctx,
+				    void (*tx)(void *ctx, int freq,
+					       const u8 *da,
+					       struct wpabuf *buf,
+					       unsigned int wait_time));
+void gas_server_deinit(struct gas_server *gas);
+int gas_server_register(struct gas_server *gas,
+			const u8 *adv_proto_id, u8 adv_proto_id_len,
+			struct wpabuf *
+			(*req_cb)(void *ctx, const u8 *sa,
+				  const u8 *query, size_t query_len),
+			void (*status_cb)(void *ctx, struct wpabuf *resp,
+					  int ok),
+			void *ctx);
+int gas_server_rx(struct gas_server *gas, const u8 *da, const u8 *sa,
+		  const u8 *bssid, u8 categ, const u8 *data, size_t len,
+		  int freq);
+void gas_server_tx_status(struct gas_server *gas, const u8 *dst, const u8 *data,
+			  size_t data_len, int ack);
+
+#else /* CONFIG_GAS_SERVER */
+
+static inline void gas_server_deinit(struct gas_server *gas)
+{
+}
+
+#endif /* CONFIG_GAS_SERVER */
+
+#endif /* GAS_SERVER_H */
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index 7a96f96..fdd5e73 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -526,8 +526,7 @@
 	} while (0)
 
 	VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK);
-	VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ);
-	VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ);
+	VHT_CAP_CHECK_MAX(VHT_CAP_SUPP_CHAN_WIDTH_MASK);
 	VHT_CAP_CHECK(VHT_CAP_RXLDPC);
 	VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80);
 	VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160);
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index a8d68e5..120d4e8 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -250,6 +250,12 @@
 			break;
 		elems->fils_nonce = pos;
 		break;
+	case WLAN_EID_EXT_OWE_DH_PARAM:
+		if (elen < 2)
+			break;
+		elems->owe_dh = pos;
+		elems->owe_dh_len = elen;
+		break;
 	default:
 		if (show_errors) {
 			wpa_printf(MSG_MSGDUMP,
@@ -1113,7 +1119,7 @@
 			return -1;
 		return 5000 + 5 * chan;
 	case 129: /* center freqs 50, 114; 160 MHz */
-		if (chan < 50 || chan > 114)
+		if (chan < 36 || chan > 128)
 			return -1;
 		return 5000 + 5 * chan;
 	case 180: /* 60 GHz band, channels 1..4 */
@@ -1427,6 +1433,40 @@
 }
 
 
+/**
+ * get_ie_ext - Fetch a specified extended information element from IEs buffer
+ * @ies: Information elements buffer
+ * @len: Information elements buffer length
+ * @ext: Information element extension identifier (WLAN_EID_EXT_*)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the first matching information element in the IEs
+ * buffer or %NULL in case the element is not found.
+ */
+const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext)
+{
+	const u8 *end;
+
+	if (!ies)
+		return NULL;
+
+	end = ies + len;
+
+	while (end - ies > 1) {
+		if (2 + ies[1] > end - ies)
+			break;
+
+		if (ies[0] == WLAN_EID_EXTENSION && ies[1] >= 1 &&
+		    ies[2] == ext)
+			return ies;
+
+		ies += 2 + ies[1];
+	}
+
+	return NULL;
+}
+
+
 size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len)
 {
 	/*
@@ -1594,3 +1634,105 @@
 
 	return op;
 }
+
+
+int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
+				    size_t nei_rep_len)
+{
+	u8 *nei_pos = nei_rep;
+	const char *end;
+
+	/*
+	 * BSS Transition Candidate List Entries - Neighbor Report elements
+	 * neighbor=<BSSID>,<BSSID Information>,<Operating Class>,
+	 * <Channel Number>,<PHY Type>[,<hexdump of Optional Subelements>]
+	 */
+	while (pos) {
+		u8 *nei_start;
+		long int val;
+		char *endptr, *tmp;
+
+		pos = os_strstr(pos, " neighbor=");
+		if (!pos)
+			break;
+		if (nei_pos + 15 > nei_rep + nei_rep_len) {
+			wpa_printf(MSG_DEBUG,
+				   "Not enough room for additional neighbor");
+			return -1;
+		}
+		pos += 10;
+
+		nei_start = nei_pos;
+		*nei_pos++ = WLAN_EID_NEIGHBOR_REPORT;
+		nei_pos++; /* length to be filled in */
+
+		if (hwaddr_aton(pos, nei_pos)) {
+			wpa_printf(MSG_DEBUG, "Invalid BSSID");
+			return -1;
+		}
+		nei_pos += ETH_ALEN;
+		pos += 17;
+		if (*pos != ',') {
+			wpa_printf(MSG_DEBUG, "Missing BSSID Information");
+			return -1;
+		}
+		pos++;
+
+		val = strtol(pos, &endptr, 0);
+		WPA_PUT_LE32(nei_pos, val);
+		nei_pos += 4;
+		if (*endptr != ',') {
+			wpa_printf(MSG_DEBUG, "Missing Operating Class");
+			return -1;
+		}
+		pos = endptr + 1;
+
+		*nei_pos++ = atoi(pos); /* Operating Class */
+		pos = os_strchr(pos, ',');
+		if (pos == NULL) {
+			wpa_printf(MSG_DEBUG, "Missing Channel Number");
+			return -1;
+		}
+		pos++;
+
+		*nei_pos++ = atoi(pos); /* Channel Number */
+		pos = os_strchr(pos, ',');
+		if (pos == NULL) {
+			wpa_printf(MSG_DEBUG, "Missing PHY Type");
+			return -1;
+		}
+		pos++;
+
+		*nei_pos++ = atoi(pos); /* PHY Type */
+		end = os_strchr(pos, ' ');
+		tmp = os_strchr(pos, ',');
+		if (tmp && (!end || tmp < end)) {
+			/* Optional Subelements (hexdump) */
+			size_t len;
+
+			pos = tmp + 1;
+			end = os_strchr(pos, ' ');
+			if (end)
+				len = end - pos;
+			else
+				len = os_strlen(pos);
+			if (nei_pos + len / 2 > nei_rep + nei_rep_len) {
+				wpa_printf(MSG_DEBUG,
+					   "Not enough room for neighbor subelements");
+				return -1;
+			}
+			if (len & 0x01 ||
+			    hexstr2bin(pos, nei_pos, len / 2) < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "Invalid neighbor subelement info");
+				return -1;
+			}
+			nei_pos += len / 2;
+			pos = end;
+		}
+
+		nei_start[1] = nei_pos - nei_start - 2;
+	}
+
+	return nei_pos - nei_rep;
+}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 966eeac..9276158 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -78,6 +78,7 @@
 	const u8 *fils_wrapped_data;
 	const u8 *fils_pk;
 	const u8 *fils_nonce;
+	const u8 *owe_dh;
 
 	u8 ssid_len;
 	u8 supp_rates_len;
@@ -120,6 +121,7 @@
 	u8 key_delivery_len;
 	u8 fils_wrapped_data_len;
 	u8 fils_pk_len;
+	u8 owe_dh_len;
 
 	struct mb_ies_info mb_ies;
 };
@@ -174,6 +176,7 @@
 extern size_t global_op_class_size;
 
 const u8 * get_ie(const u8 *ies, size_t len, u8 eid);
+const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext);
 
 size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len);
 
@@ -186,4 +189,7 @@
 
 const struct oper_class_map * get_oper_class(const char *country, u8 op_class);
 
+int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
+				    size_t nei_rep_len);
+
 #endif /* IEEE802_11_COMMON_H */
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 15f6d42..b7fa563 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -462,6 +462,9 @@
 #define WLAN_EID_EXT_FILS_PUBLIC_KEY 12
 #define WLAN_EID_EXT_FILS_NONCE 13
 #define WLAN_EID_EXT_FUTURE_CHANNEL_GUIDANCE 14
+#define WLAN_EID_EXT_OWE_DH_PARAM 32
+#define WLAN_EID_EXT_HE_CAPABILITIES 35
+#define WLAN_EID_EXT_HE_OPERATION 36
 
 
 /* Action frame categories (IEEE Std 802.11-2016, 9.4.1.11, Table 9-76) */
@@ -1126,6 +1129,7 @@
 #define VHT_CAP_SUPP_CHAN_WIDTH_160MHZ              ((u32) BIT(2))
 #define VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ     ((u32) BIT(3))
 #define VHT_CAP_SUPP_CHAN_WIDTH_MASK                ((u32) BIT(2) | BIT(3))
+#define VHT_CAP_SUPP_CHAN_WIDTH_MASK_SHIFT          2
 #define VHT_CAP_RXLDPC                              ((u32) BIT(4))
 #define VHT_CAP_SHORT_GI_80                         ((u32) BIT(5))
 #define VHT_CAP_SHORT_GI_160                        ((u32) BIT(6))
@@ -1192,6 +1196,8 @@
 #define OSEN_IE_VENDOR_TYPE 0x506f9a12
 #define MBO_IE_VENDOR_TYPE 0x506f9a16
 #define MBO_OUI_TYPE 22
+#define OWE_IE_VENDOR_TYPE 0x506f9a1c
+#define OWE_OUI_TYPE 28
 
 #define WMM_OUI_TYPE 2
 #define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0
@@ -1336,6 +1342,7 @@
 
 /* MBO v0.0_r19, 4.2: MBO Attributes */
 /* Table 4-5: MBO Attributes */
+/* OCE v0.0.10, Table 4-3: OCE Attributes */
 enum mbo_attr_id {
 	MBO_ATTR_ID_AP_CAPA_IND = 1,
 	MBO_ATTR_ID_NON_PREF_CHAN_REPORT = 2,
@@ -1345,6 +1352,10 @@
 	MBO_ATTR_ID_TRANSITION_REASON = 6,
 	MBO_ATTR_ID_TRANSITION_REJECT_REASON = 7,
 	MBO_ATTR_ID_ASSOC_RETRY_DELAY = 8,
+	OCE_ATTR_ID_CAPA_IND = 101,
+	OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT = 102,
+	OCE_ATTR_ID_REDUCED_WAN_METRICS = 103,
+	OCE_ATTR_ID_RNR_COMPLETENESS = 104,
 };
 
 /* MBO v0.0_r19, 4.2.1: MBO AP Capability Indication Attribute */
@@ -1419,9 +1430,17 @@
 	WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA = 3,
 };
 
-/* MBO v0.0_r25, 4.3: MBO ANQP-elements */
+/* MBO v0.0_r27, 4.3: MBO ANQP-elements */
 #define MBO_ANQP_OUI_TYPE 0x12
-#define MBO_ANQP_SUBTYPE_CELL_CONN_PREF 1
+#define MBO_ANQP_SUBTYPE_QUERY_LIST 1
+#define MBO_ANQP_SUBTYPE_CELL_CONN_PREF 2
+#define MAX_MBO_ANQP_SUBTYPE MBO_ANQP_SUBTYPE_CELL_CONN_PREF
+
+/* OCE v0.0.10, 4.2.1: OCE Capability Indication Attribute */
+#define OCE_RELEASE 1
+#define OCE_RELEASE_MASK (BIT(0) | BIT(1) | BIT(2))
+#define OCE_IS_STA_CFON BIT(3)
+#define OCE_IS_NON_OCE_AP_PRESENT BIT(4)
 
 /* Wi-Fi Direct (P2P) */
 
@@ -1570,7 +1589,9 @@
 	WFD_SUBELEM_COUPLED_SINK = 6,
 	WFD_SUBELEM_EXT_CAPAB = 7,
 	WFD_SUBELEM_LOCAL_IP_ADDRESS = 8,
-	WFD_SUBELEM_SESSION_INFO = 9
+	WFD_SUBELEM_SESSION_INFO = 9,
+	WFD_SUBELEM_MAC_INFO = 10,
+	WFD_SUBELEM_R2_DEVICE_INFO = 11,
 };
 
 /* 802.11s */
@@ -1963,18 +1984,17 @@
 struct ieee80211_he_capabilities {
 	u8 he_mac_capab_info[5];
 	u8 he_phy_capab_info[9];
-	u16 he_txrx_mcs_support;
-	/* possibly followed by Tx Rx MCS NSS descriptor */
-	u8 variable[];
+	u8 he_txrx_mcs_support[12]; /* TODO: 4, 8, or 12 octets */
 	/* PPE Thresholds (optional) */
 } STRUCT_PACKED;
 
 struct ieee80211_he_operation {
 	u32 he_oper_params;
-	u8 he_mcs_nss_set[3];
+	u8 he_mcs_nss_set[2];
 	u8 vht_op_info_chwidth;
 	u8 vht_op_info_chan_center_freq_seg0_idx;
 	u8 vht_op_info_chan_center_freq_seg1_idx;
+	/* Followed by conditional MaxBSSID Indicator subfield (u8) */
 } STRUCT_PACKED;
 
 /* HE Capabilities Information defines */
@@ -2009,4 +2029,7 @@
 #define HE_OPERATION_BSS_COLOR_DISABLED		((u32) BIT(30))
 #define HE_OPERATION_BSS_DUAL_BEACON		((u32) BIT(31))
 
+/* DPP Public Action frame identifiers - OUI_WFA */
+#define DPP_OUI_TYPE 0x1A
+
 #endif /* IEEE802_11_DEFS_H */
diff --git a/src/common/privsep_commands.h b/src/common/privsep_commands.h
index 0f47518..b85c6c3 100644
--- a/src/common/privsep_commands.h
+++ b/src/common/privsep_commands.h
@@ -92,7 +92,6 @@
 	PRIVSEP_EVENT_MICHAEL_MIC_FAILURE,
 	PRIVSEP_EVENT_INTERFACE_STATUS,
 	PRIVSEP_EVENT_PMKID_CANDIDATE,
-	PRIVSEP_EVENT_STKSTART,
 	PRIVSEP_EVENT_FT_RESPONSE,
 	PRIVSEP_EVENT_RX_EAPOL,
 	PRIVSEP_EVENT_SCAN_STARTED,
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index 934f09d..b4f7d12 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -90,6 +90,27 @@
  *	which supports DFS offloading, to indicate a radar pattern has been
  *	detected. The channel is now unusable.
  *
+ * @QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG: Command used to set configuration
+ *	for IEEE 802.11 communicating outside the context of a basic service
+ *	set, called OCB command. Uses the attributes defines in
+ *	enum qca_wlan_vendor_attr_ocb_set_config.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME: Command used to set OCB
+ *	UTC time. Use the attributes defines in
+ *	enum qca_wlan_vendor_attr_ocb_set_utc_time.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_OCB_START_TIMING_ADVERT: Command used to start
+ *	sending OCB timing advert frames. Uses the attributes defines in
+ *	enum qca_wlan_vendor_attr_ocb_start_timing_advert.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_OCB_STOP_TIMING_ADVERT: Command used to stop
+ *	OCB timing advert. Uses the attributes defines in
+ *	enum qca_wlan_vendor_attr_ocb_stop_timing_advert.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_OCB_GET_TSF_TIMER: Command used to get TSF
+ *	timer value. Uses the attributes defines in
+ *	enum qca_wlan_vendor_attr_ocb_get_tsf_resp.
+ *
  * @QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START: Command used to
  *	start the P2P Listen offload function in device and pass the listen
  *	channel, period, interval, count, device types, and vendor specific
@@ -288,6 +309,35 @@
  * @QCA_NL80211_VENDOR_SUBCMD_SET_TRACE_LEVEL: Set the trace level for a
  *	specific QCA module. The trace levels are represented by
  *	enum qca_attr_trace_level attributes.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT: Set the Beam Refinement
+ *	Protocol antenna limit in different modes. See enum
+ *	qca_wlan_vendor_attr_brp_ant_limit_mode.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START: Start spectral scan. The scan
+ *	parameters are specified by enum qca_wlan_vendor_attr_spectral_scan.
+ *	This returns a cookie (%QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE)
+ *	identifying the operation in success case.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP: Stop spectral scan. This uses
+ *	a cookie (%QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE) from
+ *	@QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START to identify the scan to
+ *	be stopped.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS: Set the active Type Of Service on the
+ *	specific interface. This can be used to modify some of the low level
+ *	scan parameters (off channel dwell time, home channel time) in the
+ *	driver/firmware. These parameters are maintained within the host driver.
+ *	This command is valid only when the interface is in the connected state.
+ *	These scan parameters shall be reset by the driver/firmware once
+ *	disconnected. The attributes used with this command are defined in
+ *	enum qca_wlan_vendor_attr_active_tos.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_HANG: Event indicating to the user space that the
+ *	driver has detected an internal failure. This event carries the
+ *	information indicating the reason that triggered this detection. The
+ *	attributes for this command are defined in
+ *	enum qca_wlan_vendor_attr_hang.
  */
 enum qca_nl80211_vendor_subcmds {
 	QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -415,6 +465,11 @@
 	QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET = 150,
 	QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS = 151,
 	QCA_NL80211_VENDOR_SUBCMD_SET_TRACE_LEVEL = 152,
+	QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT = 153,
+	QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START = 154,
+	QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP = 155,
+	QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS = 156,
+	QCA_NL80211_VENDOR_SUBCMD_HANG = 157,
 };
 
 
@@ -573,6 +628,22 @@
 	 * driver.
 	 */
 	QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO = 37,
+	/* Used in QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT command
+	 * See enum qca_wlan_vendor_attr_brp_ant_limit_mode.
+	 */
+	QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE = 38,
+	/* Used in QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT command
+	 * to define the number of antennas to use for BRP.
+	 * different purpose in each ANT_LIMIT_MODE:
+	 * DISABLE - ignored
+	 * EFFECTIVE - upper limit to number of antennas to be used
+	 * FORCE - exact number of antennas to be used
+	 * unsigned 8 bit value
+	 */
+	QCA_WLAN_VENDOR_ATTR_BRP_ANT_NUM_LIMIT = 39,
+	/* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command
+	 * to report the corresponding antenna index to the chain RSSI value */
+	QCA_WLAN_VENDOR_ATTR_ANTENNA_INFO = 40,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
@@ -595,6 +666,38 @@
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK,
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK,
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS,
+	/* Indicates the status of re-association requested by user space for
+	 * the BSSID specified by QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID.
+	 * Type u16.
+	 * Represents the status code from AP. Use
+	 * %WLAN_STATUS_UNSPECIFIED_FAILURE if the device cannot give you the
+	 * real status code for failures.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_STATUS,
+	/* This attribute indicates that the old association was maintained when
+	 * a re-association is requested by user space and that re-association
+	 * attempt fails (i.e., cannot connect to the requested BSS, but can
+	 * remain associated with the BSS with which the association was in
+	 * place when being requested to roam). Used along with
+	 * WLAN_VENDOR_ATTR_ROAM_AUTH_STATUS to indicate the current
+	 * re-association status. Type flag.
+	 * This attribute is applicable only for re-association failure cases.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RETAIN_CONNECTION,
+	/* This attribute specifies the PMK if one was newly generated during
+	 * FILS roaming. This is added to the PMKSA cache and is used in
+	 * subsequent connections with PMKSA caching.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK = 11,
+	/* This attribute specifies the PMKID used/generated for the current
+	 * FILS roam. This is used in subsequent connections with PMKSA caching.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID = 12,
+	/* A 16-bit unsigned value specifying the next sequence number to use
+	 * in ERP message in the currently associated realm. This is used in
+	 * doing subsequent ERP based connections in the same realm.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM = 13,
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX =
@@ -682,6 +785,15 @@
  *	Devices whilst in Listen state, rather than having the user space
  *	wpa_supplicant do it. Information from received P2P requests are
  *	forwarded from firmware to host whenever the host processor wakes up.
+ * @QCA_WLAN_VENDOR_FEATURE_OCE_STA: Device supports all OCE non-AP STA
+ *	specific features.
+ * @QCA_WLAN_VENDOR_FEATURE_OCE_AP: Device supports all OCE AP specific
+ *	features.
+ * @QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON: Device supports OCE STA-CFON
+ *	specific features only. If a Device sets this bit but not the
+ *	%QCA_WLAN_VENDOR_FEATURE_OCE_AP, the userspace shall assume that
+ *	this Device may not support all OCE AP functionalities but can support
+ *	only OCE STA-CFON functionalities.
  * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
  */
 enum qca_wlan_vendor_features {
@@ -689,6 +801,9 @@
 	QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY     = 1,
 	QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS = 2,
 	QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD	= 3,
+	QCA_WLAN_VENDOR_FEATURE_OCE_STA                 = 4,
+	QCA_WLAN_VENDOR_FEATURE_OCE_AP                  = 5,
+	QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON            = 6,
 	NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
 };
 
@@ -714,6 +829,112 @@
 	QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_AFTER_LAST - 1
 };
 
+/**
+ * enum qca_wlan_vendor_attr_ocb_set_config - Vendor subcmd attributes to set
+ *	OCB config
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT: Number of channels in the
+ *	configuration
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE: Size of the schedule
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY: Array of channels
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY: Array of channels to be
+ *	scheduled
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY: Array of NDL channel
+ *	information
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY: Array of NDL
+ *	active state configuration
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS: Configuration flags such as
+ *	OCB_CONFIG_FLAG_80211_FRAME_MODE
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_DEF_TX_PARAM: Default TX parameters to
+ *	use in the case that a packet is sent without a TX control header
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_TA_MAX_DURATION: Max duration after the
+ *	last TA received that the local time set by TA is synchronous to other
+ *	communicating OCB STAs.
+ */
+enum qca_wlan_vendor_attr_ocb_set_config {
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT = 1,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE = 2,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY = 3,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY = 4,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY = 5,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY = 6,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS = 7,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_DEF_TX_PARAM = 8,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_TA_MAX_DURATION = 9,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_MAX =
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ocb_set_utc_time - Vendor subcmd attributes to set
+ *	UTC time
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE: The UTC time as an array of
+ *	10 bytes
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR: The time error as an array of
+ *	5 bytes
+ */
+enum qca_wlan_vendor_attr_ocb_set_utc_time {
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE = 1,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR = 2,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_MAX =
+	QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ocb_start_timing_advert - Vendor subcmd attributes
+ *	to start sending timing advert frames
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ: Cannel frequency
+ *	on which to send the frames
+ * @QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE: Number of times
+ *	the frame is sent in 5 seconds
+ */
+enum qca_wlan_vendor_attr_ocb_start_timing_advert {
+	QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ = 1,
+	QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE = 2,
+	QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_MAX =
+	QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ocb_stop_timing_advert - Vendor subcmd attributes
+ *	to stop timing advert
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ: The channel
+ *	frequency on which to stop the timing advert
+ */
+enum qca_wlan_vendor_attr_ocb_stop_timing_advert {
+	QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ = 1,
+	QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_MAX =
+	QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ocb_get_tsf_response - Vendor subcmd attributes to
+ *	get TSF timer value
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH: Higher 32 bits of the
+ *	timer
+ * @QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW: Lower 32 bits of the timer
+ */
+enum qca_wlan_vendor_attr_ocb_get_tsf_resp {
+	QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH = 1,
+	QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW = 2,
+	QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_MAX =
+	QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_AFTER_LAST - 1
+};
+
 enum qca_vendor_attr_get_preferred_freq_list {
 	QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_INVALID,
 	/* A 32-unsigned value; the interface type/mode for which the preferred
@@ -1133,6 +1354,97 @@
 	QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_24 = 37,
 	/* 8-bit unsigned value to set the beacon miss threshold in 5 GHz */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_5 = 38,
+	/* 32-bit unsigned value to configure 5 or 10 MHz channel width for
+	 * station device while in disconnect state. The attribute use the
+	 * value of enum nl80211_chan_width: NL80211_CHAN_WIDTH_5 means 5 MHz,
+	 * NL80211_CHAN_WIDTH_10 means 10 MHz. If set, the device work in 5 or
+	 * 10 MHz channel width, the station will not connect to a BSS using 20
+	 * MHz or higher bandwidth. Set to NL80211_CHAN_WIDTH_20_NOHT to
+	 * clear this constraint. */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_SUB20_CHAN_WIDTH = 39,
+	/* 32-bit unsigned value to configure the propagation absolute delay
+	 * for 2G/5G band (units in us) */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_ABS_DELAY = 40,
+	/* 32-bit unsigned value to set probe period */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_PERIOD = 41,
+	/* 32-bit unsigned value to set stay period */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_STAY_PERIOD = 42,
+	/* 32-bit unsigned value to set snr diff */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SNR_DIFF = 43,
+	/* 32-bit unsigned value to set probe dwell time */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_DWELL_TIME = 44,
+	/* 32-bit unsigned value to set mgmt snr weight */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_MGMT_SNR_WEIGHT = 45,
+	/* 32-bit unsigned value to set data snr weight */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_DATA_SNR_WEIGHT = 46,
+	/* 32-bit unsigned value to set ack snr weight */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ACK_SNR_WEIGHT = 47,
+	/* 32-bit unsigned value to configure the listen interval.
+	 * This is in units of beacon intervals. This configuration alters
+	 * the negotiated listen interval with the AP during the connection.
+	 * It is highly recommended to configure a value less than or equal to
+	 * the one negotiated during the association. Configuring any greater
+	 * value can have adverse effects (frame loss, AP disassociating STA,
+	 * etc.).
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_LISTEN_INTERVAL = 48,
+	/*
+	 * 8 bit unsigned value that is set on an AP/GO virtual interface to
+	 * disable operations that would cause the AP/GO to leave its operating
+	 * channel.
+	 *
+	 * This will restrict the scans to the AP/GO operating channel and the
+	 * channels of the other band, if DBS is supported.A STA/CLI interface
+	 * brought up after this setting is enabled, will be restricted to
+	 * connecting to devices only on the AP/GO interface's operating channel
+	 * or on the other band in DBS case. P2P supported channel list is
+	 * modified, to only include AP interface's operating-channel and the
+	 * channels of the other band if DBS is supported.
+	 *
+	 * These restrictions are only applicable as long as the AP/GO interface
+	 * is alive. If the AP/GO interface is brought down then this
+	 * setting/restriction is forgotten.
+	 *
+	 * If this variable is set on an AP/GO interface while a multi-channel
+	 * concurrent session is active, it has no effect on the operation of
+	 * the current interfaces, other than restricting the scan to the AP/GO
+	 * operating channel and the other band channels if DBS is supported.
+	 * However, if the STA is brought down and restarted then the new STA
+	 * connection will either be formed on the AP/GO channel or on the
+	 * other band in a DBS case. This is because of the scan being
+	 * restricted on these channels as mentioned above.
+	 *
+	 * 1-Restrict / 0-Don't restrict offchannel operations.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_RESTRICT_OFFCHANNEL = 49,
+	/*
+	 * 8 bit unsigned value to enable/disable LRO (Large Receive Offload)
+	 * on an interface.
+	 * 1 - Enable, 0 - Disable.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_LRO = 50,
+
+	/*
+	 * 8 bit unsigned value to globally enable/disable scan
+	 * 1 - Enable, 0 - Disable.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_ENABLE = 51,
+
+	/* 8-bit unsigned value to set the total beacon miss count
+	 * This paramater will set the total beacon miss count.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TOTAL_BEACON_MISS_COUNT = 52,
+
+	/* Unsigned 32-bit value to configure the number of continuous
+	 * Beacon Miss which shall be used by the firmware to penalize
+	 * the RSSI for BTC.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_PENALIZE_AFTER_NCONS_BEACON_MISS_BTC = 53,
+
+	/* 8-bit unsigned value to configure the driver and below layers to
+	 * enable/disable all FILS features.
+	 * 0-enable, 1-disable */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_DISABLE_FILS = 54,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST,
@@ -1835,6 +2147,24 @@
 };
 
 /**
+ * BRP antenna limit mode
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_DISABLE: Disable BRP force
+ *	antenna limit, BRP will be performed as usual.
+ * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_EFFECTIVE: Define maximal
+ *	antennas limit. the hardware may use less antennas than the
+ *	maximum limit.
+ * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_FORCE: The hardware will
+ *	use exactly the specified number of antennas for BRP.
+ */
+enum qca_wlan_vendor_attr_brp_ant_limit_mode {
+	QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_DISABLE,
+	QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_EFFECTIVE,
+	QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_FORCE,
+	QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_MAX
+};
+
+/**
  * enum qca_wlan_vendor_attr_dmg_rf_sector_cfg - Attributes for
  * DMG RF sector configuration for a single RF module.
  * The values are defined in a compact way which closely matches
@@ -2389,7 +2719,7 @@
 /**
  * enum qca_vendor_attr_sar_limits - Attributes for SAR power limits
  *
- * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT: Optional (u32) value to
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE: Optional (u32) value to
  *	select which SAR power limit table should be used. Valid
  *	values are enumerated in enum
  *	%qca_vendor_attr_sar_limits_selections. The existing SAR
@@ -3000,6 +3330,8 @@
 	QCA_WLAN_VENDOR_ATTR_EPNO_SAME_NETWORK_BONUS = 20,
 	QCA_WLAN_VENDOR_ATTR_EPNO_SECURE_BONUS = 21,
 	QCA_WLAN_VENDOR_ATTR_EPNO_BAND5GHZ_BONUS = 22,
+	/* Unsigned 32-bit value, representing the PNO Request ID */
+	QCA_WLAN_VENDOR_ATTR_PNO_CONFIG_REQUEST_ID = 23,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_PNO_AFTER_LAST,
@@ -3023,6 +3355,24 @@
 };
 
 /**
+ * qca_wlan_vendor_attr_external_acs_policy: Attribute values for
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_POLICY to the vendor subcmd
+ * QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS. This represents the
+ * external ACS policies to select the channels w.r.t. the PCL weights.
+ * (QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL represents the channels and
+ * their PCL weights.)
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_MANDATORY: Mandatory to
+ * select a channel with non-zero PCL weight.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_PREFERRED: Prefer a
+ * channel with non-zero PCL weight.
+ *
+ */
+enum qca_wlan_vendor_attr_external_acs_policy {
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_PREFERRED,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_MANDATORY,
+};
+
+/**
  * qca_wlan_vendor_channel_prop_flags: This represent the flags for a channel.
  * This is used by QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS.
  */
@@ -3077,6 +3427,30 @@
 	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT160        = 1 << 26,
 	/* VHT 80+80 channel */
 	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT80_80      = 1 << 27,
+	/* HE 20 channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE20          = 1 << 28,
+	/* HE 40 with extension channel above */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40PLUS      = 1 << 29,
+	/* HE 40 with extension channel below */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40MINUS     = 1 << 30,
+	/* HE 40 intolerant */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40INTOL     = 1 << 31,
+};
+
+/**
+ * qca_wlan_vendor_channel_prop_flags_2: This represents the flags for a
+ * channel, and is a continuation of qca_wlan_vendor_channel_prop_flags. This is
+ * used by QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS_2.
+ */
+enum qca_wlan_vendor_channel_prop_flags_2 {
+	/* HE 40 intolerant mark bit for ACS use */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40INTOLMARK = 1 << 0,
+	/* HE 80 channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE80          = 1 << 1,
+	/* HE 160 channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE160         = 1 << 2,
+	/* HE 80+80 channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE80_80       = 1 << 3,
 };
 
 /**
@@ -3140,6 +3514,10 @@
 	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_0 = 9,
 	/* VHT segment 1 (u8) */
 	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_1 = 10,
+	/* A bitmask (u32) with flags specified in
+	 * enum qca_wlan_vendor_channel_prop_flags_2.
+	 */
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS_2 = 11,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_LAST,
@@ -3208,6 +3586,11 @@
 	 * in enum qca_wlan_vendor_external_acs_event_chan_info_attr.
 	 */
 	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO = 12,
+	/* External ACS policy such as PCL mandatory, PCL preferred, etc.
+	 * It uses values defined in enum
+	 * qca_wlan_vendor_attr_external_acs_policy.
+	 */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_POLICY = 13,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_LAST,
@@ -3331,11 +3714,11 @@
 	/* Flag indicating if the station's link to the AP is active.
 	 * Active Link - If included, Inactive link - If not included
 	 */
-	QCA_ATTR_NUD_STATS_AP_LINK_ACTIVE= 9,
+	QCA_ATTR_NUD_STATS_AP_LINK_ACTIVE = 9,
 	/* Flag indicating if there is any duplicate address detected (DAD).
 	 * Yes - If detected, No - If not detected.
 	 */
-	QCA_ATTR_NUD_STATS_IS_DAD = 9,
+	QCA_ATTR_NUD_STATS_IS_DAD = 10,
 
 	/* keep last */
 	QCA_ATTR_NUD_STATS_GET_LAST,
@@ -3422,4 +3805,168 @@
 	QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_AFTER_LAST - 1,
 };
 
+/**
+ * enum qca_wlan_vendor_attr_spectral_scan - Spectral scan config parameters
+ */
+enum qca_wlan_vendor_attr_spectral_scan {
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_INVALID = 0,
+	/* Number of times the chip enters spectral scan mode before
+	 * deactivating spectral scans. When set to 0, chip will enter spectral
+	 * scan mode continuously. u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_SCAN_COUNT = 1,
+	/* Spectral scan period. Period increment resolution is 256*Tclk,
+	 * where Tclk = 1/44 MHz (Gmode), 1/40 MHz (Amode). u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_SCAN_PERIOD = 2,
+	/* Spectral scan priority. u32 attribute. */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_PRIORITY = 3,
+	/* Number of FFT data points to compute. u32 attribute. */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FFT_SIZE = 4,
+	/* Enable targeted gain change before starting the spectral scan FFT.
+	 * u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_GC_ENA = 5,
+	/* Restart a queued spectral scan. u32 attribute. */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RESTART_ENA = 6,
+	/* Noise floor reference number for the calculation of bin power.
+	 * u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_NOISE_FLOOR_REF = 7,
+	/* Disallow spectral scan triggers after TX/RX packets by setting
+	 * this delay value to roughly SIFS time period or greater.
+	 * u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_INIT_DELAY = 8,
+	/* Number of strong bins (inclusive) per sub-channel, below
+	 * which a signal is declared a narrow band tone. u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_NB_TONE_THR = 9,
+	/* Specify the threshold over which a bin is declared strong (for
+	 * scan bandwidth analysis). u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_STR_BIN_THR = 10,
+	/* Spectral scan report mode. u32 attribute. */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_WB_RPT_MODE = 11,
+	/* RSSI report mode, if the ADC RSSI is below
+	 * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RSSI_THR,
+	 * then FFTs will not trigger, but timestamps and summaries get
+	 * reported. u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RSSI_RPT_MODE = 12,
+	/* ADC RSSI must be greater than or equal to this threshold (signed dB)
+	 * to ensure spectral scan reporting with normal error code.
+	 * u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RSSI_THR = 13,
+	/* Format of frequency bin magnitude for spectral scan triggered FFTs:
+	 * 0: linear magnitude, 1: log magnitude (20*log10(lin_mag)).
+	 * u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_PWR_FORMAT = 14,
+	/* Format of FFT report to software for spectral scan triggered FFTs.
+	 * 0: No FFT report (only spectral scan summary report)
+	 * 1: 2-dword summary of metrics for each completed FFT + spectral scan
+	 * report
+	 * 2: 2-dword summary of metrics for each completed FFT + 1x-oversampled
+	 * bins (in-band) per FFT + spectral scan summary report
+	 * 3: 2-dword summary of metrics for each completed FFT + 2x-oversampled
+	 * bins (all) per FFT + spectral scan summary report
+	 * u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RPT_MODE = 15,
+	/* Number of LSBs to shift out in order to scale the FFT bins.
+	 * u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_BIN_SCALE = 16,
+	/* Set to 1 (with spectral_scan_pwr_format=1), to report bin magnitudes
+	 * in dBm power. u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_DBM_ADJ = 17,
+	/* Per chain enable mask to select input ADC for search FFT.
+	 * u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_CHN_MASK = 18,
+	/* An unsigned 64-bit integer provided by host driver to identify the
+	 * spectral scan request. This attribute is included in the scan
+	 * response message for @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START
+	 * and used as an attribute in
+	 * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP to identify the
+	 * specific scan to be stopped.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE = 19,
+
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_MAX =
+		QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_tos {
+	QCA_WLAN_VENDOR_TOS_BK = 0,
+	QCA_WLAN_VENDOR_TOS_BE = 1,
+	QCA_WLAN_VENDOR_TOS_VI = 2,
+	QCA_WLAN_VENDOR_TOS_VO = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_active_tos - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS.
+ */
+enum qca_wlan_vendor_attr_active_tos {
+	QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_INVALID = 0,
+	/* Type Of Service - Represented by qca_wlan_vendor_tos */
+	QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS = 1,
+	/* Flag attribute representing the start (attribute included) or stop
+	 * (attribute not included) of the respective TOS.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_START = 2,
+};
+
+enum qca_wlan_vendor_hang_reason {
+	/* Unspecified reason */
+	QCA_WLAN_HANG_REASON_UNSPECIFIED = 0,
+	/* No Map for the MAC entry for the received frame */
+	QCA_WLAN_HANG_RX_HASH_NO_ENTRY_FOUND = 1,
+	/* Peer deletion timeout happened */
+	QCA_WLAN_HANG_PEER_DELETION_TIMEDOUT = 2,
+	/* Peer unmap timeout */
+	QCA_WLAN_HANG_PEER_UNMAP_TIMEDOUT = 3,
+	/* Scan request timed out */
+	QCA_WLAN_HANG_SCAN_REQ_EXPIRED = 4,
+	/* Consecutive Scan attempt failures */
+	QCA_WLAN_HANG_SCAN_ATTEMPT_FAILURES = 5,
+	/* Unable to get the message buffer */
+	QCA_WLAN_HANG_GET_MSG_BUFF_FAILURE = 6,
+	/* Current command processing is timedout */
+	QCA_WLAN_HANG_ACTIVE_LIST_TIMEOUT = 7,
+	/* Timeout for an ACK from FW for suspend request */
+	QCA_WLAN_HANG_SUSPEND_TIMEOUT = 8,
+	/* Timeout for an ACK from FW for resume request */
+	QCA_WLAN_HANG_RESUME_TIMEOUT = 9,
+	/* Transmission timeout for consecutive data frames */
+	QCA_WLAN_HANG_TRANSMISSIONS_TIMEOUT = 10,
+	/* Timeout for the TX completion status of data frame */
+	QCA_WLAN_HANG_TX_COMPLETE_TIMEOUT = 11,
+	/* DXE failure for TX/RX, DXE resource unavailability */
+	QCA_WLAN_HANG_DXE_FAILURE = 12,
+	/* WMI pending commands exceed the maximum count */
+	QCA_WLAN_HANG_WMI_EXCEED_MAX_PENDING_CMDS = 13,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_hang - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_HANG.
+ */
+enum qca_wlan_vendor_attr_hang {
+	QCA_WLAN_VENDOR_ATTR_HANG_INVALID = 0,
+	/* Reason for the hang - u32 attribute with a value from enum
+	 * qca_wlan_vendor_hang_reason.
+	 */
+	QCA_WLAN_VENDOR_ATTR_HANG_REASON = 1,
+
+	QCA_WLAN_VENDOR_ATTR_HANG_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_HANG_MAX =
+		QCA_WLAN_VENDOR_ATTR_HANG_AFTER_LAST - 1,
+};
+
 #endif /* QCA_VENDOR_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index fd167d6..4bab6b9 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -13,6 +13,7 @@
 #include "crypto/sha1.h"
 #include "crypto/sha256.h"
 #include "crypto/sha384.h"
+#include "crypto/sha512.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/crypto.h"
 #include "ieee802_11_defs.h"
@@ -20,7 +21,7 @@
 #include "wpa_common.h"
 
 
-static unsigned int wpa_kck_len(int akmp)
+static unsigned int wpa_kck_len(int akmp, size_t pmk_len)
 {
 	switch (akmp) {
 	case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
@@ -30,13 +31,17 @@
 	case WPA_KEY_MGMT_FILS_SHA384:
 	case WPA_KEY_MGMT_FT_FILS_SHA384:
 		return 0;
+	case WPA_KEY_MGMT_DPP:
+		return pmk_len / 2;
+	case WPA_KEY_MGMT_OWE:
+		return pmk_len / 2;
 	default:
 		return 16;
 	}
 }
 
 
-static unsigned int wpa_kek_len(int akmp)
+static unsigned int wpa_kek_len(int akmp, size_t pmk_len)
 {
 	switch (akmp) {
 	case WPA_KEY_MGMT_FILS_SHA384:
@@ -46,13 +51,17 @@
 	case WPA_KEY_MGMT_FILS_SHA256:
 	case WPA_KEY_MGMT_FT_FILS_SHA256:
 		return 32;
+	case WPA_KEY_MGMT_DPP:
+		return pmk_len <= 32 ? 16 : 32;
+	case WPA_KEY_MGMT_OWE:
+		return pmk_len <= 32 ? 16 : 32;
 	default:
 		return 16;
 	}
 }
 
 
-unsigned int wpa_mic_len(int akmp)
+unsigned int wpa_mic_len(int akmp, size_t pmk_len)
 {
 	switch (akmp) {
 	case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
@@ -62,6 +71,10 @@
 	case WPA_KEY_MGMT_FT_FILS_SHA256:
 	case WPA_KEY_MGMT_FT_FILS_SHA384:
 		return 0;
+	case WPA_KEY_MGMT_DPP:
+		return pmk_len / 2;
+	case WPA_KEY_MGMT_OWE:
+		return pmk_len / 2;
 	default:
 		return 16;
 	}
@@ -91,30 +104,43 @@
 int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
 		      const u8 *buf, size_t len, u8 *mic)
 {
-	u8 hash[SHA384_MAC_LEN];
+	u8 hash[SHA512_MAC_LEN];
+
+	if (key_len == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "WPA: KCK not set - cannot calculate MIC");
+		return -1;
+	}
 
 	switch (ver) {
 #ifndef CONFIG_FIPS
 	case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4:
+		wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using HMAC-MD5");
 		return hmac_md5(key, key_len, buf, len, mic);
 #endif /* CONFIG_FIPS */
 	case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES:
+		wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using HMAC-SHA1");
 		if (hmac_sha1(key, key_len, buf, len, hash))
 			return -1;
 		os_memcpy(mic, hash, MD5_MAC_LEN);
 		break;
 #if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
 	case WPA_KEY_INFO_TYPE_AES_128_CMAC:
+		wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using AES-CMAC");
 		return omac1_aes_128(key, buf, len, mic);
 #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
 	case WPA_KEY_INFO_TYPE_AKM_DEFINED:
 		switch (akmp) {
 #ifdef CONFIG_HS20
 		case WPA_KEY_MGMT_OSEN:
+			wpa_printf(MSG_DEBUG,
+				   "WPA: EAPOL-Key MIC using AES-CMAC (AKM-defined - OSEN)");
 			return omac1_aes_128(key, buf, len, mic);
 #endif /* CONFIG_HS20 */
 #ifdef CONFIG_SUITEB
 		case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
+			wpa_printf(MSG_DEBUG,
+				   "WPA: EAPOL-Key MIC using HMAC-SHA256 (AKM-defined - Suite B)");
 			if (hmac_sha256(key, key_len, buf, len, hash))
 				return -1;
 			os_memcpy(mic, hash, MD5_MAC_LEN);
@@ -122,16 +148,70 @@
 #endif /* CONFIG_SUITEB */
 #ifdef CONFIG_SUITEB192
 		case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+			wpa_printf(MSG_DEBUG,
+				   "WPA: EAPOL-Key MIC using HMAC-SHA384 (AKM-defined - Suite B 192-bit)");
 			if (hmac_sha384(key, key_len, buf, len, hash))
 				return -1;
 			os_memcpy(mic, hash, 24);
 			break;
 #endif /* CONFIG_SUITEB192 */
+#ifdef CONFIG_OWE
+		case WPA_KEY_MGMT_OWE:
+			wpa_printf(MSG_DEBUG,
+				   "WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - OWE)",
+				   (unsigned int) key_len * 8 * 2);
+			if (key_len == 128 / 8) {
+				if (hmac_sha256(key, key_len, buf, len, hash))
+					return -1;
+			} else if (key_len == 192 / 8) {
+				if (hmac_sha384(key, key_len, buf, len, hash))
+					return -1;
+			} else if (key_len == 256 / 8) {
+				if (hmac_sha512(key, key_len, buf, len, hash))
+					return -1;
+			} else {
+				wpa_printf(MSG_INFO,
+					   "OWE: Unsupported KCK length: %u",
+					   (unsigned int) key_len);
+				return -1;
+			}
+			os_memcpy(mic, hash, key_len);
+			break;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+		case WPA_KEY_MGMT_DPP:
+			wpa_printf(MSG_DEBUG,
+				   "WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - DPP)",
+				   (unsigned int) key_len * 8 * 2);
+			if (key_len == 128 / 8) {
+				if (hmac_sha256(key, key_len, buf, len, hash))
+					return -1;
+			} else if (key_len == 192 / 8) {
+				if (hmac_sha384(key, key_len, buf, len, hash))
+					return -1;
+			} else if (key_len == 256 / 8) {
+				if (hmac_sha512(key, key_len, buf, len, hash))
+					return -1;
+			} else {
+				wpa_printf(MSG_INFO,
+					   "DPP: Unsupported KCK length: %u",
+					   (unsigned int) key_len);
+				return -1;
+			}
+			os_memcpy(mic, hash, key_len);
+			break;
+#endif /* CONFIG_DPP */
 		default:
+			wpa_printf(MSG_DEBUG,
+				   "WPA: EAPOL-Key MIC algorithm not known (AKM-defined - akmp=0x%x)",
+				   akmp);
 			return -1;
 		}
 		break;
 	default:
+		wpa_printf(MSG_DEBUG,
+			   "WPA: EAPOL-Key MIC algorithm not known (ver=%d)",
+			   ver);
 		return -1;
 	}
 
@@ -157,10 +237,6 @@
  * PTK = PRF-X(PMK, "Pairwise key expansion",
  *             Min(AA, SA) || Max(AA, SA) ||
  *             Min(ANonce, SNonce) || Max(ANonce, SNonce))
- *
- * STK = PRF-X(SMK, "Peer key expansion",
- *             Min(MAC_I, MAC_P) || Max(MAC_I, MAC_P) ||
- *             Min(INonce, PNonce) || Max(INonce, PNonce))
  */
 int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
 		   const u8 *addr1, const u8 *addr2,
@@ -171,6 +247,11 @@
 	u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
 	size_t ptk_len;
 
+	if (pmk_len == 0) {
+		wpa_printf(MSG_ERROR, "WPA: No PMK set for PT derivation");
+		return -1;
+	}
+
 	if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) {
 		os_memcpy(data, addr1, ETH_ALEN);
 		os_memcpy(data + ETH_ALEN, addr2, ETH_ALEN);
@@ -189,24 +270,56 @@
 			  WPA_NONCE_LEN);
 	}
 
-	ptk->kck_len = wpa_kck_len(akmp);
-	ptk->kek_len = wpa_kek_len(akmp);
+	ptk->kck_len = wpa_kck_len(akmp, pmk_len);
+	ptk->kek_len = wpa_kek_len(akmp, pmk_len);
 	ptk->tk_len = wpa_cipher_key_len(cipher);
 	ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
 
+	if (wpa_key_mgmt_sha384(akmp)) {
 #if defined(CONFIG_SUITEB192) || defined(CONFIG_FILS)
-	if (wpa_key_mgmt_sha384(akmp))
-		sha384_prf(pmk, pmk_len, label, data, sizeof(data),
-			   tmp, ptk_len);
-	else
+		wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)");
+		if (sha384_prf(pmk, pmk_len, label, data, sizeof(data),
+			       tmp, ptk_len) < 0)
+			return -1;
+#else /* CONFIG_SUITEB192 || CONFIG_FILS */
+		return -1;
 #endif /* CONFIG_SUITEB192 || CONFIG_FILS */
+	} else if (wpa_key_mgmt_sha256(akmp) || akmp == WPA_KEY_MGMT_OWE) {
 #ifdef CONFIG_IEEE80211W
-	if (wpa_key_mgmt_sha256(akmp))
-		sha256_prf(pmk, pmk_len, label, data, sizeof(data),
-			   tmp, ptk_len);
-	else
+		wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)");
+		if (sha256_prf(pmk, pmk_len, label, data, sizeof(data),
+			       tmp, ptk_len) < 0)
+			return -1;
+#else /* CONFIG_IEEE80211W */
+		return -1;
 #endif /* CONFIG_IEEE80211W */
-		sha1_prf(pmk, pmk_len, label, data, sizeof(data), tmp, ptk_len);
+#ifdef CONFIG_DPP
+	} else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 32) {
+		wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)");
+		if (sha256_prf(pmk, pmk_len, label, data, sizeof(data),
+			       tmp, ptk_len) < 0)
+			return -1;
+	} else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 48) {
+		wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)");
+		if (sha384_prf(pmk, pmk_len, label, data, sizeof(data),
+			       tmp, ptk_len) < 0)
+			return -1;
+	} else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 64) {
+		wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA512)");
+		if (sha512_prf(pmk, pmk_len, label, data, sizeof(data),
+			       tmp, ptk_len) < 0)
+			return -1;
+	} else if (akmp == WPA_KEY_MGMT_DPP) {
+		wpa_printf(MSG_INFO, "DPP: Unknown PMK length %u",
+			   (unsigned int) pmk_len);
+		return -1;
+#endif /* CONFIG_DPP */
+	} else {
+		wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA1)");
+		if (sha1_prf(pmk, pmk_len, label, data, sizeof(data), tmp,
+			     ptk_len) < 0)
+			return -1;
+	}
 
 	wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR,
 		   MAC2STR(addr1), MAC2STR(addr2));
@@ -303,17 +416,22 @@
 
 
 int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
-		    const u8 *snonce, const u8 *anonce, struct wpa_ptk *ptk,
-		    u8 *ick, size_t *ick_len, int akmp, int cipher)
+		    const u8 *snonce, const u8 *anonce, const u8 *dhss,
+		    size_t dhss_len, struct wpa_ptk *ptk,
+		    u8 *ick, size_t *ick_len, int akmp, int cipher,
+		    u8 *fils_ft, size_t *fils_ft_len)
 {
-	u8 data[2 * ETH_ALEN + 2 * FILS_NONCE_LEN];
-	u8 tmp[FILS_ICK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
+	u8 *data, *pos;
+	size_t data_len;
+	u8 tmp[FILS_ICK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN +
+	       FILS_FT_MAX_LEN];
 	size_t key_data_len;
 	const char *label = "FILS PTK Derivation";
+	int ret = -1;
 
 	/*
 	 * FILS-Key-Data = PRF-X(PMK, "FILS PTK Derivation",
-	 *                       SPA || AA || SNonce || ANonce)
+	 *                       SPA || AA || SNonce || ANonce [ || DHss ])
 	 * ICK = L(FILS-Key-Data, 0, ICK_bits)
 	 * KEK = L(FILS-Key-Data, ICK_bits, KEK_bits)
 	 * TK = L(FILS-Key-Data, ICK_bits + KEK_bits, TK_bits)
@@ -321,33 +439,63 @@
 	 * FILS-FT = L(FILS-Key-Data, ICK_bits + KEK_bits + TK_bits,
 	 *             FILS-FT_bits)
 	 */
-	os_memcpy(data, spa, ETH_ALEN);
-	os_memcpy(data + ETH_ALEN, aa, ETH_ALEN);
-	os_memcpy(data + 2 * ETH_ALEN, snonce, FILS_NONCE_LEN);
-	os_memcpy(data + 2 * ETH_ALEN + FILS_NONCE_LEN, anonce, FILS_NONCE_LEN);
+	data_len = 2 * ETH_ALEN + 2 * FILS_NONCE_LEN + dhss_len;
+	data = os_malloc(data_len);
+	if (!data)
+		goto err;
+	pos = data;
+	os_memcpy(pos, spa, ETH_ALEN);
+	pos += ETH_ALEN;
+	os_memcpy(pos, aa, ETH_ALEN);
+	pos += ETH_ALEN;
+	os_memcpy(pos, snonce, FILS_NONCE_LEN);
+	pos += FILS_NONCE_LEN;
+	os_memcpy(pos, anonce, FILS_NONCE_LEN);
+	pos += FILS_NONCE_LEN;
+	if (dhss)
+		os_memcpy(pos, dhss, dhss_len);
 
 	ptk->kck_len = 0;
-	ptk->kek_len = wpa_kek_len(akmp);
+	ptk->kek_len = wpa_kek_len(akmp, pmk_len);
 	ptk->tk_len = wpa_cipher_key_len(cipher);
 	if (wpa_key_mgmt_sha384(akmp))
 		*ick_len = 48;
 	else if (wpa_key_mgmt_sha256(akmp))
 		*ick_len = 32;
 	else
-		return -1;
+		goto err;
 	key_data_len = *ick_len + ptk->kek_len + ptk->tk_len;
 
-	if (wpa_key_mgmt_sha384(akmp))
-		sha384_prf(pmk, pmk_len, label, data, sizeof(data),
-			   tmp, key_data_len);
-	else if (sha256_prf(pmk, pmk_len, label, data, sizeof(data),
-			    tmp, key_data_len) < 0)
-		return -1;
+	if (fils_ft && fils_ft_len) {
+		if (akmp == WPA_KEY_MGMT_FT_FILS_SHA256) {
+			*fils_ft_len = 32;
+		} else if (akmp == WPA_KEY_MGMT_FT_FILS_SHA384) {
+			*fils_ft_len = 48;
+		} else {
+			*fils_ft_len = 0;
+			fils_ft = NULL;
+		}
+		key_data_len += *fils_ft_len;
+	}
+
+	if (wpa_key_mgmt_sha384(akmp)) {
+		wpa_printf(MSG_DEBUG, "FILS: PTK derivation using PRF(SHA384)");
+		if (sha384_prf(pmk, pmk_len, label, data, data_len,
+			       tmp, key_data_len) < 0)
+			goto err;
+	} else {
+		wpa_printf(MSG_DEBUG, "FILS: PTK derivation using PRF(SHA256)");
+		if (sha256_prf(pmk, pmk_len, label, data, data_len,
+			       tmp, key_data_len) < 0)
+			goto err;
+	}
 
 	wpa_printf(MSG_DEBUG, "FILS: PTK derivation - SPA=" MACSTR
 		   " AA=" MACSTR, MAC2STR(spa), MAC2STR(aa));
 	wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN);
 	wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN);
+	if (dhss)
+		wpa_hexdump_key(MSG_DEBUG, "FILS: DHss", dhss, dhss_len);
 	wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, pmk_len);
 	wpa_hexdump_key(MSG_DEBUG, "FILS: FILS-Key-Data", tmp, key_data_len);
 
@@ -360,10 +508,18 @@
 	os_memcpy(ptk->tk, tmp + *ick_len + ptk->kek_len, ptk->tk_len);
 	wpa_hexdump_key(MSG_DEBUG, "FILS: TK", ptk->tk, ptk->tk_len);
 
-	/* TODO: FILS-FT */
+	if (fils_ft && fils_ft_len) {
+		os_memcpy(fils_ft, tmp + *ick_len + ptk->kek_len + ptk->tk_len,
+			  *fils_ft_len);
+		wpa_hexdump_key(MSG_DEBUG, "FILS: FILS-FT",
+				fils_ft, *fils_ft_len);
+	}
 
 	os_memset(tmp, 0, sizeof(tmp));
-	return 0;
+	ret = 0;
+err:
+	bin_clear_free(data, data_len);
+	return ret;
 }
 
 
@@ -379,6 +535,14 @@
 	size_t num_elem = 4;
 	int res;
 
+	wpa_printf(MSG_DEBUG, "FILS: Key-Auth derivation: STA-MAC=" MACSTR
+		   " AP-BSSID=" MACSTR, MAC2STR(sta_addr), MAC2STR(bssid));
+	wpa_hexdump_key(MSG_DEBUG, "FILS: ICK", ick, ick_len);
+	wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FILS: gSTA", g_sta, g_sta_len);
+	wpa_hexdump(MSG_DEBUG, "FILS: gAP", g_ap, g_ap_len);
+
 	/*
 	 * For (Re)Association Request frame (STA->AP):
 	 * Key-Auth = HMAC-Hash(ICK, SNonce || ANonce || STA-MAC || AP-BSSID
@@ -765,6 +929,14 @@
 		return WPA_KEY_MGMT_FT_FILS_SHA256;
 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_FILS_SHA384)
 		return WPA_KEY_MGMT_FT_FILS_SHA384;
+#ifdef CONFIG_OWE
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OWE)
+		return WPA_KEY_MGMT_OWE;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_DPP)
+		return WPA_KEY_MGMT_DPP;
+#endif /* CONFIG_DPP */
 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OSEN)
 		return WPA_KEY_MGMT_OSEN;
 	return 0;
@@ -1248,8 +1420,8 @@
 	os_memcpy(pos, sta_addr, ETH_ALEN);
 	pos += ETH_ALEN;
 
-	ptk->kck_len = wpa_kck_len(akmp);
-	ptk->kek_len = wpa_kek_len(akmp);
+	ptk->kck_len = wpa_kck_len(akmp, PMK_LEN);
+	ptk->kek_len = wpa_kek_len(akmp, PMK_LEN);
 	ptk->tk_len = wpa_cipher_key_len(cipher);
 	ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
 
@@ -1302,29 +1474,48 @@
  * @aa: Authenticator address
  * @spa: Supplicant address
  * @pmkid: Buffer for PMKID
- * @use_sha256: Whether to use SHA256-based KDF
+ * @akmp: Negotiated key management protocol
  *
- * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
- * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA)
+ * IEEE Std 802.11-2016 - 12.7.1.3 Pairwise key hierarchy
+ * AKM: 00-0F-AC:5, 00-0F-AC:6, 00-0F-AC:14, 00-0F-AC:16
+ * PMKID = Truncate-128(HMAC-SHA-256(PMK, "PMK Name" || AA || SPA))
+ * AKM: 00-0F-AC:11
+ * See rsn_pmkid_suite_b()
+ * AKM: 00-0F-AC:12
+ * See rsn_pmkid_suite_b_192()
+ * AKM: 00-0F-AC:15, 00-0F-AC:17
+ * PMKID = Truncate-128(HMAC-SHA-384(PMK, "PMK Name" || AA || SPA))
+ * Otherwise:
+ * PMKID = Truncate-128(HMAC-SHA-1(PMK, "PMK Name" || AA || SPA))
  */
 void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
-	       u8 *pmkid, int use_sha256)
+	       u8 *pmkid, int akmp)
 {
 	char *title = "PMK Name";
 	const u8 *addr[3];
 	const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
-	unsigned char hash[SHA256_MAC_LEN];
+	unsigned char hash[SHA384_MAC_LEN];
 
 	addr[0] = (u8 *) title;
 	addr[1] = aa;
 	addr[2] = spa;
 
-#ifdef CONFIG_IEEE80211W
-	if (use_sha256)
+	if (0) {
+#ifdef CONFIG_FILS
+	} else if (wpa_key_mgmt_sha384(akmp)) {
+		wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-384");
+		hmac_sha384_vector(pmk, pmk_len, 3, addr, len, hash);
+#endif /* CONFIG_FILS */
+#if defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS)
+	} else if (wpa_key_mgmt_sha256(akmp)) {
+		wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-256");
 		hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash);
-	else
-#endif /* CONFIG_IEEE80211W */
+#endif /* CONFIG_IEEE80211W || CONFIG_FILS */
+	} else {
+		wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-1");
 		hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash);
+	}
+	wpa_hexdump(MSG_DEBUG, "RSN: Derived PMKID", hash, PMKID_LEN);
 	os_memcpy(pmkid, hash, PMKID_LEN);
 }
 
@@ -1421,6 +1612,14 @@
 		return "GCMP-256";
 	case WPA_CIPHER_CCMP_256:
 		return "CCMP-256";
+	case WPA_CIPHER_AES_128_CMAC:
+		return "BIP";
+	case WPA_CIPHER_BIP_GMAC_128:
+		return "BIP-GMAC-128";
+	case WPA_CIPHER_BIP_GMAC_256:
+		return "BIP-GMAC-256";
+	case WPA_CIPHER_BIP_CMAC_256:
+		return "BIP-CMAC-256";
 	case WPA_CIPHER_GTK_NOT_USED:
 		return "GTK_NOT_USED";
 	default:
@@ -1486,6 +1685,10 @@
 		return "FT-FILS-SHA256";
 	case WPA_KEY_MGMT_FT_FILS_SHA384:
 		return "FT-FILS-SHA384";
+	case WPA_KEY_MGMT_OWE:
+		return "OWE";
+	case WPA_KEY_MGMT_DPP:
+		return "DPP";
 	default:
 		return "UNKNOWN";
 	}
@@ -1896,6 +2099,14 @@
 			val |= WPA_CIPHER_NONE;
 		else if (os_strcmp(start, "GTK_NOT_USED") == 0)
 			val |= WPA_CIPHER_GTK_NOT_USED;
+		else if (os_strcmp(start, "AES-128-CMAC") == 0)
+			val |= WPA_CIPHER_AES_128_CMAC;
+		else if (os_strcmp(start, "BIP-GMAC-128") == 0)
+			val |= WPA_CIPHER_BIP_GMAC_128;
+		else if (os_strcmp(start, "BIP-GMAC-256") == 0)
+			val |= WPA_CIPHER_BIP_GMAC_256;
+		else if (os_strcmp(start, "BIP-CMAC-256") == 0)
+			val |= WPA_CIPHER_BIP_CMAC_256;
 		else {
 			os_free(buf);
 			return -1;
@@ -1951,6 +2162,34 @@
 			return -1;
 		pos += ret;
 	}
+	if (ciphers & WPA_CIPHER_AES_128_CMAC) {
+		ret = os_snprintf(pos, end - pos, "%sAES-128-CMAC",
+				  pos == start ? "" : delim);
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+	if (ciphers & WPA_CIPHER_BIP_GMAC_128) {
+		ret = os_snprintf(pos, end - pos, "%sBIP-GMAC-128",
+				  pos == start ? "" : delim);
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+	if (ciphers & WPA_CIPHER_BIP_GMAC_256) {
+		ret = os_snprintf(pos, end - pos, "%sBIP-GMAC-256",
+				  pos == start ? "" : delim);
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+	if (ciphers & WPA_CIPHER_BIP_CMAC_256) {
+		ret = os_snprintf(pos, end - pos, "%sBIP-CMAC-256",
+				  pos == start ? "" : delim);
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
 	if (ciphers & WPA_CIPHER_NONE) {
 		ret = os_snprintf(pos, end - pos, "%sNONE",
 				  pos == start ? "" : delim);
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 5733c75..3b8c1fb 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -1,6 +1,6 @@
 /*
  * WPA definitions shared between hostapd and wpa_supplicant
- * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -13,13 +13,15 @@
 #define PMKID_LEN 16
 #define PMK_LEN 32
 #define PMK_LEN_SUITE_B_192 48
-#define PMK_LEN_MAX 48
+#define PMK_LEN_MAX 64
 #define WPA_REPLAY_COUNTER_LEN 8
 #define WPA_NONCE_LEN 32
 #define WPA_KEY_RSC_LEN 8
 #define WPA_GMK_LEN 32
 #define WPA_GTK_MAX_LEN 32
 
+#define OWE_DH_GROUP 19
+
 #define WPA_ALLOWED_PAIRWISE_CIPHERS \
 (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE | \
 WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256)
@@ -27,6 +29,9 @@
 (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | \
 WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \
 WPA_CIPHER_GTK_NOT_USED)
+#define WPA_ALLOWED_GROUP_MGMT_CIPHERS \
+(WPA_CIPHER_AES_128_CMAC | WPA_CIPHER_BIP_GMAC_128 | WPA_CIPHER_BIP_GMAC_256 | \
+WPA_CIPHER_BIP_CMAC_256)
 
 #define WPA_SELECTOR_LEN 4
 #define WPA_VERSION 1
@@ -63,8 +68,10 @@
 #define RSN_AUTH_KEY_MGMT_FILS_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 15)
 #define RSN_AUTH_KEY_MGMT_FT_FILS_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 16)
 #define RSN_AUTH_KEY_MGMT_FT_FILS_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 17)
+#define RSN_AUTH_KEY_MGMT_OWE RSN_SELECTOR(0x00, 0x0f, 0xac, 18)
 #define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00)
 #define RSN_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01)
+#define RSN_AUTH_KEY_MGMT_DPP RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x02)
 
 #define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0)
 #define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
@@ -98,12 +105,6 @@
 #endif
 #define RSN_KEY_DATA_MAC_ADDR RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
 #define RSN_KEY_DATA_PMKID RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
-#ifdef CONFIG_PEERKEY
-#define RSN_KEY_DATA_SMK RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
-#define RSN_KEY_DATA_NONCE RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
-#define RSN_KEY_DATA_LIFETIME RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
-#define RSN_KEY_DATA_ERROR RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
-#endif /* CONFIG_PEERKEY */
 #ifdef CONFIG_IEEE80211W
 #define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
 #endif /* CONFIG_IEEE80211W */
@@ -194,11 +195,12 @@
 	/* followed by Key Data Length bytes of Key Data */
 } STRUCT_PACKED;
 
-#define WPA_EAPOL_KEY_MIC_MAX_LEN 24
-#define WPA_KCK_MAX_LEN 24
+#define WPA_EAPOL_KEY_MIC_MAX_LEN 32
+#define WPA_KCK_MAX_LEN 32
 #define WPA_KEK_MAX_LEN 64
 #define WPA_TK_MAX_LEN 32
 #define FILS_ICK_MAX_LEN 48
+#define FILS_FT_MAX_LEN 48
 
 /**
  * struct wpa_ptk - WPA Pairwise Transient Key
@@ -279,22 +281,6 @@
 } STRUCT_PACKED;
 
 
-#ifdef CONFIG_PEERKEY
-enum {
-	STK_MUI_4WAY_STA_AP = 1,
-	STK_MUI_4WAY_STAT_STA = 2,
-	STK_MUI_GTK = 3,
-	STK_MUI_SMK = 4
-};
-
-enum {
-	STK_ERR_STA_NR = 1,
-	STK_ERR_STA_NRSN = 2,
-	STK_ERR_CPHR_NS = 3,
-	STK_ERR_NO_STSL = 4
-};
-#endif /* CONFIG_PEERKEY */
-
 struct rsn_error_kde {
 	be16 mui;
 	be16 error_type;
@@ -354,8 +340,10 @@
 int fils_pmkid_erp(int akmp, const u8 *reauth, size_t reauth_len,
 		   u8 *pmkid);
 int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
-		    const u8 *snonce, const u8 *anonce, struct wpa_ptk *ptk,
-		    u8 *ick, size_t *ick_len, int akmp, int cipher);
+		    const u8 *snonce, const u8 *anonce, const u8 *dhss,
+		    size_t dhss_len, struct wpa_ptk *ptk,
+		    u8 *ick, size_t *ick_len, int akmp, int cipher,
+		    u8 *fils_ft, size_t *fils_ft_len);
 int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce,
 		     const u8 *anonce, const u8 *sta_addr, const u8 *bssid,
 		     const u8 *g_sta, size_t g_sta_len,
@@ -403,7 +391,7 @@
 			 struct wpa_ie_data *data);
 
 void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
-	       u8 *pmkid, int use_sha256);
+	       u8 *pmkid, int akmp);
 #ifdef CONFIG_SUITEB
 int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa,
 		       const u8 *spa, u8 *pmkid);
@@ -472,7 +460,7 @@
 int wpa_parse_cipher(const char *value);
 int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim);
 int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise);
-unsigned int wpa_mic_len(int akmp);
+unsigned int wpa_mic_len(int akmp, size_t pmk_len);
 int fils_domain_name_hash(const char *domain, u8 *hash);
 
 #endif /* WPA_COMMON_H */
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 4649eab..c59bc60 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -143,6 +143,23 @@
 #define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS "
 #define WPS_EVENT_ER_SET_SEL_REG "WPS-ER-AP-SET-SEL-REG "
 
+/* DPP events */
+#define DPP_EVENT_AUTH_SUCCESS "DPP-AUTH-SUCCESS "
+#define DPP_EVENT_NOT_COMPATIBLE "DPP-NOT-COMPATIBLE "
+#define DPP_EVENT_RESPONSE_PENDING "DPP-RESPONSE-PENDING "
+#define DPP_EVENT_SCAN_PEER_QR_CODE "DPP-SCAN-PEER-QR-CODE "
+#define DPP_EVENT_CONF_RECEIVED "DPP-CONF-RECEIVED "
+#define DPP_EVENT_CONF_SENT "DPP-CONF-SENT "
+#define DPP_EVENT_CONF_FAILED "DPP-CONF-FAILED "
+#define DPP_EVENT_CONFOBJ_SSID "DPP-CONFOBJ-SSID "
+#define DPP_EVENT_CONFOBJ_PASS "DPP-CONFOBJ-PASS "
+#define DPP_EVENT_CONFOBJ_PSK "DPP-CONFOBJ-PSK "
+#define DPP_EVENT_CONNECTOR "DPP-CONNECTOR "
+#define DPP_EVENT_C_SIGN_KEY "DPP-C-SIGN-KEY "
+#define DPP_EVENT_NET_ACCESS_KEY "DPP-NET-ACCESS-KEY "
+#define DPP_EVENT_MISSING_CONNECTOR "DPP-MISSING-CONNECTOR "
+#define DPP_EVENT_NETWORK_ID "DPP-NETWORK-ID "
+
 /* MESH events */
 #define MESH_GROUP_STARTED "MESH-GROUP-STARTED "
 #define MESH_GROUP_REMOVED "MESH-GROUP-REMOVED "
@@ -234,6 +251,7 @@
 #define RX_HS20_ANQP "RX-HS20-ANQP "
 #define RX_HS20_ANQP_ICON "RX-HS20-ANQP-ICON "
 #define RX_HS20_ICON "RX-HS20-ICON "
+#define RX_MBO_ANQP "RX-MBO-ANQP "
 
 #define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION "
 #define HS20_DEAUTH_IMMINENT_NOTICE "HS20-DEAUTH-IMMINENT-NOTICE "
@@ -275,6 +293,7 @@
 #define DFS_EVENT_CAC_START "DFS-CAC-START "
 #define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED "
 #define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED "
+#define DFS_EVENT_PRE_CAC_EXPIRED "DFS-PRE-CAC-EXPIRED "
 
 #define AP_CSA_FINISHED "AP-CSA-FINISHED "
 
diff --git a/src/common/wpa_helpers.c b/src/common/wpa_helpers.c
index f159421..8e1c09e 100644
--- a/src/common/wpa_helpers.c
+++ b/src/common/wpa_helpers.c
@@ -222,7 +222,8 @@
 		if (get_wpa_status(ifname, "ip_address", ip, sizeof(ip)) == 0
 		    && strlen(ip) > 0) {
 			printf("IP address found: '%s'\n", ip);
-			return 0;
+			if (strncmp(ip, "169.254.", 8) != 0)
+				return 0;
 		}
 		ctrl = wpa_open_ctrl(ifname);
 		if (ctrl == NULL)
diff --git a/src/crypto/Makefile b/src/crypto/Makefile
index d181e72..ee93e41 100644
--- a/src/crypto/Makefile
+++ b/src/crypto/Makefile
@@ -14,6 +14,9 @@
 CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
 #CFLAGS += -DALL_DH_GROUPS
 CFLAGS += -DCONFIG_SHA256
+CFLAGS += -DCONFIG_SHA384
+CFLAGS += -DCONFIG_HMAC_SHA384_KDF
+CFLAGS += -DCONFIG_INTERNAL_SHA384
 
 LIB_OBJS= \
 	aes-cbc.o \
@@ -48,6 +51,8 @@
 	sha256-prf.o \
 	sha256-tlsprf.o \
 	sha256-internal.o \
+	sha384.o \
+	sha384-prf.o \
 	sha384-internal.o \
 	sha512-internal.o
 
diff --git a/src/crypto/aes-internal-dec.c b/src/crypto/aes-internal-dec.c
index 720c703..7482295 100644
--- a/src/crypto/aes-internal-dec.c
+++ b/src/crypto/aes-internal-dec.c
@@ -147,10 +147,12 @@
 	PUTU32(pt + 12, s3);
 }
 
-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
 {
 	u32 *rk = ctx;
 	rijndaelDecrypt(ctx, rk[AES_PRIV_NR_POS], crypt, plain);
+	return 0;
 }
 
 
diff --git a/src/crypto/aes-internal-enc.c b/src/crypto/aes-internal-enc.c
index f3c61b8..9fdb4f3 100644
--- a/src/crypto/aes-internal-enc.c
+++ b/src/crypto/aes-internal-enc.c
@@ -112,10 +112,11 @@
 }
 
 
-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
 {
 	u32 *rk = ctx;
 	rijndaelEncrypt(ctx, rk[AES_PRIV_NR_POS], plain, crypt);
+	return 0;
 }
 
 
diff --git a/src/crypto/aes-siv.c b/src/crypto/aes-siv.c
index 2bb79b5..b682f3a 100644
--- a/src/crypto/aes-siv.c
+++ b/src/crypto/aes-siv.c
@@ -95,11 +95,10 @@
 		xor(tmp, tmp2);
 	}
 	if (len[i] >= AES_BLOCK_SIZE) {
-		buf = os_malloc(len[i]);
+		buf = os_memdup(addr[i], len[i]);
 		if (!buf)
 			return -ENOMEM;
 
-		os_memcpy(buf, addr[i], len[i]);
 		xorend(buf, len[i], tmp, AES_BLOCK_SIZE);
 		data[0] = buf;
 		ret = omac1_aes_vector(key, key_len, 1, data, &len[i], mac);
diff --git a/src/crypto/aes.h b/src/crypto/aes.h
index 2de59e0..8ab3de2 100644
--- a/src/crypto/aes.h
+++ b/src/crypto/aes.h
@@ -12,10 +12,10 @@
 #define AES_BLOCK_SIZE 16
 
 void * aes_encrypt_init(const u8 *key, size_t len);
-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
 void aes_encrypt_deinit(void *ctx);
 void * aes_decrypt_init(const u8 *key, size_t len);
-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
 void aes_decrypt_deinit(void *ctx);
 
 #endif /* AES_H */
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index bdc3ba6..a723201 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -1,6 +1,6 @@
 /*
  * Wrapper functions for crypto libraries
- * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -106,8 +106,9 @@
  * @clear: 8 octets (in)
  * @key: 7 octets (in) (no parity bits included)
  * @cypher: 8 octets (out)
+ * Returns: 0 on success, -1 on failure
  */
-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher);
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher);
 
 /**
  * aes_encrypt_init - Initialize AES for encryption
@@ -122,8 +123,9 @@
  * @ctx: Context pointer from aes_encrypt_init()
  * @plain: Plaintext data to be encrypted (16 bytes)
  * @crypt: Buffer for the encrypted data (16 bytes)
+ * Returns: 0 on success, -1 on failure
  */
-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
 
 /**
  * aes_encrypt_deinit - Deinitialize AES encryption
@@ -144,8 +146,9 @@
  * @ctx: Context pointer from aes_encrypt_init()
  * @crypt: Encrypted data (16 bytes)
  * @plain: Buffer for the decrypted data (16 bytes)
+ * Returns: 0 on success, -1 on failure
  */
-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
 
 /**
  * aes_decrypt_deinit - Deinitialize AES decryption
@@ -829,4 +832,12 @@
 			const struct crypto_ec_point *a,
 			const struct crypto_ec_point *b);
 
+struct crypto_ecdh;
+
+struct crypto_ecdh * crypto_ecdh_init(int group);
+struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y);
+struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
+					const u8 *key, size_t len);
+void crypto_ecdh_deinit(struct crypto_ecdh *ecdh);
+
 #endif /* CRYPTO_H */
diff --git a/src/crypto/crypto_gnutls.c b/src/crypto/crypto_gnutls.c
index 0dfd54d..31a580e 100644
--- a/src/crypto/crypto_gnutls.c
+++ b/src/crypto/crypto_gnutls.c
@@ -30,7 +30,7 @@
 }
 
 
-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
 {
 	gcry_cipher_hd_t hd;
 	u8 pkey[8], next, tmp;
@@ -49,6 +49,7 @@
 	gcry_err_code(gcry_cipher_setkey(hd, pkey, 8));
 	gcry_cipher_encrypt(hd, cypher, 8, clear, 8);
 	gcry_cipher_close(hd);
+	return 0;
 }
 
 
@@ -107,10 +108,11 @@
 }
 
 
-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
 {
 	gcry_cipher_hd_t hd = ctx;
 	gcry_cipher_encrypt(hd, crypt, 16, plain, 16);
+	return 0;
 }
 
 
@@ -137,10 +139,11 @@
 }
 
 
-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
 {
 	gcry_cipher_hd_t hd = ctx;
 	gcry_cipher_decrypt(hd, plain, 16, crypt, 16);
+	return 0;
 }
 
 
diff --git a/src/crypto/crypto_libtomcrypt.c b/src/crypto/crypto_libtomcrypt.c
index a55edd1..b80ad57 100644
--- a/src/crypto/crypto_libtomcrypt.c
+++ b/src/crypto/crypto_libtomcrypt.c
@@ -35,7 +35,7 @@
 }
 
 
-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
 {
 	u8 pkey[8], next, tmp;
 	int i;
@@ -53,6 +53,7 @@
 	des_setup(pkey, 8, 0, &skey);
 	des_ecb_encrypt(clear, cypher, &skey);
 	des_done(&skey);
+	return 0;
 }
 
 
@@ -96,10 +97,10 @@
 }
 
 
-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
 {
 	symmetric_key *skey = ctx;
-	aes_ecb_encrypt(plain, crypt, skey);
+	return aes_ecb_encrypt(plain, crypt, skey) == CRYPT_OK ? 0 : -1;
 }
 
 
@@ -125,10 +126,10 @@
 }
 
 
-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
 {
 	symmetric_key *skey = ctx;
-	aes_ecb_encrypt(plain, (u8 *) crypt, skey);
+	return aes_ecb_encrypt(plain, (u8 *) crypt, skey) == CRYPT_OK ? 0 : -1;
 }
 
 
@@ -297,7 +298,7 @@
 struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
 					  const u8 *iv, const u8 *key,
 					  size_t key_len)
-{	
+{
 	struct crypto_cipher *ctx;
 	int idx, res, rc4 = 0;
 
diff --git a/src/crypto/crypto_linux.c b/src/crypto/crypto_linux.c
new file mode 100644
index 0000000..8099193
--- /dev/null
+++ b/src/crypto/crypto_linux.c
@@ -0,0 +1,1006 @@
+/*
+ * Crypto wrapper for Linux kernel AF_ALG
+ * Copyright (c) 2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <linux/if_alg.h>
+
+#include "common.h"
+#include "crypto.h"
+#include "md5.h"
+#include "sha1.h"
+#include "sha256.h"
+#include "sha384.h"
+#include "aes.h"
+
+
+#ifndef SOL_ALG
+#define SOL_ALG 279
+#endif /* SOL_ALG */
+
+
+static int linux_af_alg_socket(const char *type, const char *name)
+{
+	struct sockaddr_alg sa;
+	int s;
+
+	if (TEST_FAIL())
+		return -1;
+
+	s = socket(AF_ALG, SOCK_SEQPACKET, 0);
+	if (s < 0) {
+		wpa_printf(MSG_ERROR, "%s: Failed to open AF_ALG socket: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+
+	os_memset(&sa, 0, sizeof(sa));
+	sa.salg_family = AF_ALG;
+	os_strlcpy((char *) sa.salg_type, type, sizeof(sa.salg_type));
+	os_strlcpy((char *) sa.salg_name, name, sizeof(sa.salg_type));
+	if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "%s: Failed to bind AF_ALG socket(%s,%s): %s",
+			   __func__, type, name, strerror(errno));
+		close(s);
+		return -1;
+	}
+
+	return s;
+}
+
+
+static int linux_af_alg_hash_vector(const char *alg, const u8 *key,
+				    size_t key_len, size_t num_elem,
+				    const u8 *addr[], const size_t *len,
+				    u8 *mac, size_t mac_len)
+{
+	int s, t;
+	size_t i;
+	ssize_t res;
+	int ret = -1;
+
+	s = linux_af_alg_socket("hash", alg);
+	if (s < 0)
+		return -1;
+
+	if (key && setsockopt(s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) {
+		wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s",
+			   __func__, strerror(errno));
+		close(s);
+		return -1;
+	}
+
+	t = accept(s, NULL, NULL);
+	if (t < 0) {
+		wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s",
+			   __func__, strerror(errno));
+		close(s);
+		return -1;
+	}
+
+	for (i = 0; i < num_elem; i++) {
+		res = send(t, addr[i], len[i], i + 1 < num_elem ? MSG_MORE : 0);
+		if (res < 0) {
+			wpa_printf(MSG_ERROR,
+				   "%s: send on AF_ALG socket failed: %s",
+				   __func__, strerror(errno));
+			goto fail;
+		}
+		if ((size_t) res < len[i]) {
+			wpa_printf(MSG_ERROR,
+				   "%s: send on AF_ALG socket did not accept full buffer (%d/%d)",
+				   __func__, (int) res, (int) len[i]);
+			goto fail;
+		}
+	}
+
+	res = recv(t, mac, mac_len, 0);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR,
+			   "%s: recv on AF_ALG socket failed: %s",
+			   __func__, strerror(errno));
+		goto fail;
+	}
+	if ((size_t) res < mac_len) {
+		wpa_printf(MSG_ERROR,
+			   "%s: recv on AF_ALG socket did not return full buffer (%d/%d)",
+			   __func__, (int) res, (int) mac_len);
+		goto fail;
+	}
+
+	ret = 0;
+fail:
+	close(t);
+	close(s);
+
+	return ret;
+}
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return linux_af_alg_hash_vector("md4", NULL, 0, num_elem, addr, len,
+					mac, 16);
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return linux_af_alg_hash_vector("md5", NULL, 0, num_elem, addr, len,
+					mac, MD5_MAC_LEN);
+}
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		u8 *mac)
+{
+	return linux_af_alg_hash_vector("sha1", NULL, 0, num_elem, addr, len,
+					mac, SHA1_MAC_LEN);
+}
+
+
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	return linux_af_alg_hash_vector("sha256", NULL, 0, num_elem, addr, len,
+					mac, SHA256_MAC_LEN);
+}
+
+
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	return linux_af_alg_hash_vector("sha384", NULL, 0, num_elem, addr, len,
+					mac, SHA384_MAC_LEN);
+}
+
+
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	return linux_af_alg_hash_vector("sha512", NULL, 0, num_elem, addr, len,
+					mac, 64);
+}
+
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+		    const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return linux_af_alg_hash_vector("hmac(md5)", key, key_len, num_elem,
+					addr, len, mac, 16);
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	     u8 *mac)
+{
+	return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return linux_af_alg_hash_vector("hmac(sha1)", key, key_len, num_elem,
+					addr, len, mac, SHA1_MAC_LEN);
+}
+
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	      u8 *mac)
+{
+	return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return linux_af_alg_hash_vector("hmac(sha256)", key, key_len, num_elem,
+					addr, len, mac, SHA256_MAC_LEN);
+}
+
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return linux_af_alg_hash_vector("hmac(sha384)", key, key_len, num_elem,
+					addr, len, mac, SHA384_MAC_LEN);
+}
+
+
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+struct crypto_hash {
+	int s;
+	int t;
+	size_t mac_len;
+	int failed;
+};
+
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+				      size_t key_len)
+{
+	struct crypto_hash *ctx;
+	const char *name;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (!ctx)
+		return NULL;
+
+	switch (alg) {
+	case CRYPTO_HASH_ALG_MD5:
+		name = "md5";
+		ctx->mac_len = MD5_MAC_LEN;
+		break;
+	case CRYPTO_HASH_ALG_SHA1:
+		name = "sha1";
+		ctx->mac_len = SHA1_MAC_LEN;
+		break;
+	case CRYPTO_HASH_ALG_HMAC_MD5:
+		name = "hmac(md5)";
+		ctx->mac_len = MD5_MAC_LEN;
+		break;
+	case CRYPTO_HASH_ALG_HMAC_SHA1:
+		name = "hmac(sha1)";
+		ctx->mac_len = SHA1_MAC_LEN;
+		break;
+	case CRYPTO_HASH_ALG_SHA256:
+		name = "sha256";
+		ctx->mac_len = SHA256_MAC_LEN;
+		break;
+	case CRYPTO_HASH_ALG_HMAC_SHA256:
+		name = "hmac(sha256)";
+		ctx->mac_len = SHA256_MAC_LEN;
+		break;
+	case CRYPTO_HASH_ALG_SHA384:
+		name = "sha384";
+		ctx->mac_len = SHA384_MAC_LEN;
+		break;
+	case CRYPTO_HASH_ALG_SHA512:
+		name = "sha512";
+		ctx->mac_len = 64;
+		break;
+	default:
+		os_free(ctx);
+		return NULL;
+	}
+
+	ctx->s = linux_af_alg_socket("hash", name);
+	if (ctx->s < 0) {
+		os_free(ctx);
+		return NULL;
+	}
+
+	if (key && key_len &&
+	    setsockopt(ctx->s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) {
+		wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s",
+			   __func__, strerror(errno));
+		close(ctx->s);
+		os_free(ctx);
+		return NULL;
+	}
+
+	ctx->t = accept(ctx->s, NULL, NULL);
+	if (ctx->t < 0) {
+		wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s",
+			   __func__, strerror(errno));
+		close(ctx->s);
+		os_free(ctx);
+		return NULL;
+	}
+
+	return ctx;
+}
+
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+	ssize_t res;
+
+	if (!ctx)
+		return;
+
+	res = send(ctx->t, data, len, MSG_MORE);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR,
+			   "%s: send on AF_ALG socket failed: %s",
+			   __func__, strerror(errno));
+		ctx->failed = 1;
+		return;
+	}
+	if ((size_t) res < len) {
+		wpa_printf(MSG_ERROR,
+			   "%s: send on AF_ALG socket did not accept full buffer (%d/%d)",
+			   __func__, (int) res, (int) len);
+		ctx->failed = 1;
+		return;
+	}
+}
+
+
+static void crypto_hash_deinit(struct crypto_hash *ctx)
+{
+	close(ctx->s);
+	close(ctx->t);
+	os_free(ctx);
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+	ssize_t res;
+
+	if (!ctx)
+		return -2;
+
+	if (!mac || !len) {
+		crypto_hash_deinit(ctx);
+		return 0;
+	}
+
+	if (ctx->failed) {
+		crypto_hash_deinit(ctx);
+		return -2;
+	}
+
+	if (*len < ctx->mac_len) {
+		crypto_hash_deinit(ctx);
+		*len = ctx->mac_len;
+		return -1;
+	}
+	*len = ctx->mac_len;
+
+	res = recv(ctx->t, mac, ctx->mac_len, 0);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR,
+			   "%s: recv on AF_ALG socket failed: %s",
+			   __func__, strerror(errno));
+		crypto_hash_deinit(ctx);
+		return -2;
+	}
+	if ((size_t) res < ctx->mac_len) {
+		wpa_printf(MSG_ERROR,
+			   "%s: recv on AF_ALG socket did not return full buffer (%d/%d)",
+			   __func__, (int) res, (int) ctx->mac_len);
+		crypto_hash_deinit(ctx);
+		return -2;
+	}
+
+	crypto_hash_deinit(ctx);
+	return 0;
+}
+
+
+struct linux_af_alg_skcipher {
+	int s;
+	int t;
+};
+
+
+static void linux_af_alg_skcipher_deinit(struct linux_af_alg_skcipher *skcipher)
+{
+	if (!skcipher)
+		return;
+	if (skcipher->s >= 0)
+		close(skcipher->s);
+	if (skcipher->t >= 0)
+		close(skcipher->t);
+	os_free(skcipher);
+}
+
+
+static struct linux_af_alg_skcipher *
+linux_af_alg_skcipher(const char *alg, const u8 *key, size_t key_len)
+{
+	struct linux_af_alg_skcipher *skcipher;
+
+	skcipher = os_zalloc(sizeof(*skcipher));
+	if (!skcipher)
+		goto fail;
+	skcipher->t = -1;
+
+	skcipher->s = linux_af_alg_socket("skcipher", alg);
+	if (skcipher->s < 0)
+		goto fail;
+
+	if (setsockopt(skcipher->s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) {
+		wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s",
+			   __func__, strerror(errno));
+		goto fail;
+	}
+
+	skcipher->t = accept(skcipher->s, NULL, NULL);
+	if (skcipher->t < 0) {
+		wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s",
+			   __func__, strerror(errno));
+		goto fail;
+	}
+
+	return skcipher;
+fail:
+	linux_af_alg_skcipher_deinit(skcipher);
+	return NULL;
+}
+
+
+static int linux_af_alg_skcipher_oper(struct linux_af_alg_skcipher *skcipher,
+				      int enc, const u8 *in, u8 *out)
+{
+	char buf[CMSG_SPACE(sizeof(u32))];
+	struct iovec io[1];
+	struct msghdr msg;
+	struct cmsghdr *hdr;
+	ssize_t ret;
+	u32 *op;
+
+	io[0].iov_base = (void *) in;
+	io[0].iov_len = AES_BLOCK_SIZE;
+	os_memset(&msg, 0, sizeof(msg));
+	os_memset(buf, 0, sizeof(buf));
+	msg.msg_control = buf;
+	msg.msg_controllen = CMSG_SPACE(sizeof(u32));
+	msg.msg_iov = io;
+	msg.msg_iovlen = 1;
+	hdr = CMSG_FIRSTHDR(&msg);
+	hdr->cmsg_level = SOL_ALG;
+	hdr->cmsg_type = ALG_SET_OP;
+	hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+	op = (u32 *) CMSG_DATA(hdr);
+	*op = enc ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT;
+
+	ret = sendmsg(skcipher->t, &msg, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+
+	ret = read(skcipher->t, out, AES_BLOCK_SIZE);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: read failed: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+	if (ret < AES_BLOCK_SIZE) {
+		wpa_printf(MSG_ERROR,
+			   "%s: read did not return full data (%d/%d)",
+			   __func__, (int) ret, AES_BLOCK_SIZE);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+	return linux_af_alg_skcipher("ecb(aes)", key, len);
+}
+
+
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+	struct linux_af_alg_skcipher *skcipher = ctx;
+
+	return linux_af_alg_skcipher_oper(skcipher, 1, plain, crypt);
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+	linux_af_alg_skcipher_deinit(ctx);
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+	return linux_af_alg_skcipher("ecb(aes)", key, len);
+}
+
+
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+	struct linux_af_alg_skcipher *skcipher = ctx;
+
+	return linux_af_alg_skcipher_oper(skcipher, 0, crypt, plain);
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+	linux_af_alg_skcipher_deinit(ctx);
+}
+
+
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+	     u8 *data, size_t data_len)
+{
+	struct linux_af_alg_skcipher *skcipher;
+	u8 *skip_buf;
+	char buf[CMSG_SPACE(sizeof(u32))];
+	struct iovec io[2];
+	struct msghdr msg;
+	struct cmsghdr *hdr;
+	ssize_t ret;
+	u32 *op;
+
+	skip_buf = os_zalloc(skip + 1);
+	if (!skip_buf)
+		return -1;
+	skcipher = linux_af_alg_skcipher("ecb(arc4)", key, keylen);
+	if (!skcipher) {
+		os_free(skip_buf);
+		return -1;
+	}
+
+	io[0].iov_base = skip_buf;
+	io[0].iov_len = skip;
+	io[1].iov_base = data;
+	io[1].iov_len = data_len;
+	os_memset(&msg, 0, sizeof(msg));
+	os_memset(buf, 0, sizeof(buf));
+	msg.msg_control = buf;
+	msg.msg_controllen = CMSG_SPACE(sizeof(u32));
+	msg.msg_iov = io;
+	msg.msg_iovlen = 2;
+	hdr = CMSG_FIRSTHDR(&msg);
+	hdr->cmsg_level = SOL_ALG;
+	hdr->cmsg_type = ALG_SET_OP;
+	hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+	op = (u32 *) CMSG_DATA(hdr);
+	*op = ALG_OP_ENCRYPT;
+
+	ret = sendmsg(skcipher->t, &msg, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+			   __func__, strerror(errno));
+		os_free(skip_buf);
+		linux_af_alg_skcipher_deinit(skcipher);
+		return -1;
+	}
+	os_free(skip_buf);
+
+	msg.msg_control = NULL;
+	msg.msg_controllen = 0;
+	ret = recvmsg(skcipher->t, &msg, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: recvmsg failed: %s",
+			   __func__, strerror(errno));
+		linux_af_alg_skcipher_deinit(skcipher);
+		return -1;
+	}
+	linux_af_alg_skcipher_deinit(skcipher);
+
+	if ((size_t) ret < skip + data_len) {
+		wpa_printf(MSG_ERROR,
+			   "%s: recvmsg did not return full data (%d/%d)",
+			   __func__, (int) ret, (int) (skip + data_len));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+	u8 pkey[8], next, tmp;
+	int i;
+	struct linux_af_alg_skcipher *skcipher;
+	char buf[CMSG_SPACE(sizeof(u32))];
+	struct iovec io[1];
+	struct msghdr msg;
+	struct cmsghdr *hdr;
+	ssize_t ret;
+	u32 *op;
+	int res = -1;
+
+	/* Add parity bits to the key */
+	next = 0;
+	for (i = 0; i < 7; i++) {
+		tmp = key[i];
+		pkey[i] = (tmp >> i) | next | 1;
+		next = tmp << (7 - i);
+	}
+	pkey[i] = next | 1;
+
+	skcipher = linux_af_alg_skcipher("ecb(des)", pkey, sizeof(pkey));
+	if (!skcipher)
+		goto fail;
+
+	io[0].iov_base = (void *) clear;
+	io[0].iov_len = 8;
+	os_memset(&msg, 0, sizeof(msg));
+	os_memset(buf, 0, sizeof(buf));
+	msg.msg_control = buf;
+	msg.msg_controllen = CMSG_SPACE(sizeof(u32));
+	msg.msg_iov = io;
+	msg.msg_iovlen = 1;
+	hdr = CMSG_FIRSTHDR(&msg);
+	hdr->cmsg_level = SOL_ALG;
+	hdr->cmsg_type = ALG_SET_OP;
+	hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+	op = (u32 *) CMSG_DATA(hdr);
+	*op = ALG_OP_ENCRYPT;
+
+	ret = sendmsg(skcipher->t, &msg, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+			   __func__, strerror(errno));
+		goto fail;
+	}
+
+	ret = read(skcipher->t, cypher, 8);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: read failed: %s",
+			   __func__, strerror(errno));
+		goto fail;
+	}
+	if (ret < 8) {
+		wpa_printf(MSG_ERROR,
+			   "%s: read did not return full data (%d/8)",
+			   __func__, (int) ret);
+		goto fail;
+	}
+
+	res = 0;
+fail:
+	linux_af_alg_skcipher_deinit(skcipher);
+	return res;
+}
+
+
+static int aes_128_cbc_oper(const u8 *key, int enc, const u8 *iv,
+			    u8 *data, size_t data_len)
+{
+	struct linux_af_alg_skcipher *skcipher;
+	char buf[100];
+	struct iovec io[1];
+	struct msghdr msg;
+	struct cmsghdr *hdr;
+	ssize_t ret;
+	u32 *op;
+	struct af_alg_iv *alg_iv;
+	size_t iv_len = AES_BLOCK_SIZE;
+
+	skcipher = linux_af_alg_skcipher("cbc(aes)", key, 16);
+	if (!skcipher)
+		return -1;
+
+	io[0].iov_base = (void *) data;
+	io[0].iov_len = data_len;
+	os_memset(&msg, 0, sizeof(msg));
+	os_memset(buf, 0, sizeof(buf));
+	msg.msg_control = buf;
+	msg.msg_controllen = CMSG_SPACE(sizeof(u32)) +
+		CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+	msg.msg_iov = io;
+	msg.msg_iovlen = 1;
+
+	hdr = CMSG_FIRSTHDR(&msg);
+	hdr->cmsg_level = SOL_ALG;
+	hdr->cmsg_type = ALG_SET_OP;
+	hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+	op = (u32 *) CMSG_DATA(hdr);
+	*op = enc ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT;
+
+	hdr = CMSG_NXTHDR(&msg, hdr);
+	hdr->cmsg_level = SOL_ALG;
+	hdr->cmsg_type = ALG_SET_IV;
+	hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+	alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr);
+	alg_iv->ivlen = iv_len;
+	os_memcpy(alg_iv->iv, iv, iv_len);
+
+	ret = sendmsg(skcipher->t, &msg, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+			   __func__, strerror(errno));
+		linux_af_alg_skcipher_deinit(skcipher);
+		return -1;
+	}
+
+	ret = recvmsg(skcipher->t, &msg, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: recvmsg failed: %s",
+			   __func__, strerror(errno));
+		linux_af_alg_skcipher_deinit(skcipher);
+		return -1;
+	}
+	if ((size_t) ret < data_len) {
+		wpa_printf(MSG_ERROR,
+			   "%s: recvmsg not return full data (%d/%d)",
+			   __func__, (int) ret, (int) data_len);
+		linux_af_alg_skcipher_deinit(skcipher);
+		return -1;
+	}
+
+	linux_af_alg_skcipher_deinit(skcipher);
+	return 0;
+}
+
+
+int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+	return aes_128_cbc_oper(key, 1, iv, data, data_len);
+}
+
+
+int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+	return aes_128_cbc_oper(key, 0, iv, data, data_len);
+}
+
+
+int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return linux_af_alg_hash_vector("cmac(aes)", key, key_len, num_elem,
+					addr, len, mac, AES_BLOCK_SIZE);
+}
+
+
+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
+			 const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
+}
+
+
+int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+	return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
+}
+
+
+int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+	return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
+}
+
+
+int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
+	       u8 *plain)
+{
+	struct linux_af_alg_skcipher *skcipher;
+	char buf[100];
+	struct iovec io[1];
+	struct msghdr msg;
+	struct cmsghdr *hdr;
+	ssize_t ret;
+	u32 *op;
+	struct af_alg_iv *alg_iv;
+	size_t iv_len = 8;
+
+	skcipher = linux_af_alg_skcipher("kw(aes)", kek, kek_len);
+	if (!skcipher)
+		return -1;
+
+	io[0].iov_base = (void *) (cipher + iv_len);
+	io[0].iov_len = n * 8;
+	os_memset(&msg, 0, sizeof(msg));
+	os_memset(buf, 0, sizeof(buf));
+	msg.msg_control = buf;
+	msg.msg_controllen = CMSG_SPACE(sizeof(u32)) +
+		CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+	msg.msg_iov = io;
+	msg.msg_iovlen = 1;
+
+	hdr = CMSG_FIRSTHDR(&msg);
+	hdr->cmsg_level = SOL_ALG;
+	hdr->cmsg_type = ALG_SET_OP;
+	hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+	op = (u32 *) CMSG_DATA(hdr);
+	*op = ALG_OP_DECRYPT;
+
+	hdr = CMSG_NXTHDR(&msg, hdr);
+	hdr->cmsg_level = SOL_ALG;
+	hdr->cmsg_type = ALG_SET_IV;
+	hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+	alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr);
+	alg_iv->ivlen = iv_len;
+	os_memcpy(alg_iv->iv, cipher, iv_len);
+
+	ret = sendmsg(skcipher->t, &msg, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+
+	ret = read(skcipher->t, plain, n * 8);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: read failed: %s",
+			   __func__, strerror(errno));
+		linux_af_alg_skcipher_deinit(skcipher);
+		return -1;
+	}
+	if (ret < n * 8) {
+		wpa_printf(MSG_ERROR,
+			   "%s: read not return full data (%d/%d)",
+			   __func__, (int) ret, n * 8);
+		linux_af_alg_skcipher_deinit(skcipher);
+		return -1;
+	}
+
+	linux_af_alg_skcipher_deinit(skcipher);
+	return 0;
+}
+
+
+struct crypto_cipher {
+	struct linux_af_alg_skcipher *skcipher;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+					  const u8 *iv, const u8 *key,
+					  size_t key_len)
+{
+	struct crypto_cipher *ctx;
+	const char *name;
+	struct af_alg_iv *alg_iv;
+	size_t iv_len = 0;
+	char buf[100];
+	struct msghdr msg;
+	struct cmsghdr *hdr;
+	ssize_t ret;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (!ctx)
+		return NULL;
+
+	switch (alg) {
+	case CRYPTO_CIPHER_ALG_RC4:
+		name = "ecb(arc4)";
+		break;
+	case CRYPTO_CIPHER_ALG_AES:
+		name = "cbc(aes)";
+		iv_len = AES_BLOCK_SIZE;
+		break;
+	case CRYPTO_CIPHER_ALG_3DES:
+		name = "cbc(des3_ede)";
+		iv_len = 8;
+		break;
+	case CRYPTO_CIPHER_ALG_DES:
+		name = "cbc(des)";
+		iv_len = 8;
+		break;
+	default:
+		os_free(ctx);
+		return NULL;
+	}
+
+	ctx->skcipher = linux_af_alg_skcipher(name, key, key_len);
+	if (!ctx->skcipher) {
+		os_free(ctx);
+		return NULL;
+	}
+
+	if (iv && iv_len) {
+		os_memset(&msg, 0, sizeof(msg));
+		os_memset(buf, 0, sizeof(buf));
+		msg.msg_control = buf;
+		msg.msg_controllen = CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+		hdr = CMSG_FIRSTHDR(&msg);
+		hdr->cmsg_level = SOL_ALG;
+		hdr->cmsg_type = ALG_SET_IV;
+		hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+		alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr);
+		alg_iv->ivlen = iv_len;
+		os_memcpy(alg_iv->iv, iv, iv_len);
+
+		ret = sendmsg(ctx->skcipher->t, &msg, 0);
+		if (ret < 0) {
+			wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+				   __func__, strerror(errno));
+			linux_af_alg_skcipher_deinit(ctx->skcipher);
+			os_free(ctx);
+			return NULL;
+		}
+	}
+
+	return ctx;
+}
+
+
+static int crypto_cipher_oper(struct crypto_cipher *ctx, u32 type, const u8 *in,
+			      u8 *out, size_t len)
+{
+	char buf[CMSG_SPACE(sizeof(u32))];
+	struct iovec io[1];
+	struct msghdr msg;
+	struct cmsghdr *hdr;
+	ssize_t ret;
+	u32 *op;
+
+	io[0].iov_base = (void *) in;
+	io[0].iov_len = len;
+	os_memset(&msg, 0, sizeof(msg));
+	os_memset(buf, 0, sizeof(buf));
+	msg.msg_control = buf;
+	msg.msg_controllen = CMSG_SPACE(sizeof(u32));
+	msg.msg_iov = io;
+	msg.msg_iovlen = 1;
+	hdr = CMSG_FIRSTHDR(&msg);
+	hdr->cmsg_level = SOL_ALG;
+	hdr->cmsg_type = ALG_SET_OP;
+	hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+	op = (u32 *) CMSG_DATA(hdr);
+	*op = type;
+
+	ret = sendmsg(ctx->skcipher->t, &msg, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+
+	ret = read(ctx->skcipher->t, out, len);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: read failed: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+	if (ret < (ssize_t) len) {
+		wpa_printf(MSG_ERROR,
+			   "%s: read did not return full data (%d/%d)",
+			   __func__, (int) ret, (int) len);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+			  u8 *crypt, size_t len)
+{
+	return crypto_cipher_oper(ctx, ALG_OP_ENCRYPT, plain, crypt, len);
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+			  u8 *plain, size_t len)
+{
+	return crypto_cipher_oper(ctx, ALG_OP_DECRYPT, crypt, plain, len);
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+	if (ctx) {
+		linux_af_alg_skcipher_deinit(ctx->skcipher);
+		os_free(ctx);
+	}
+}
+
+
+int crypto_global_init(void)
+{
+	return 0;
+}
+
+
+void crypto_global_deinit(void)
+{
+}
diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c
index 0fa06d9..1cc73d8 100644
--- a/src/crypto/crypto_module_tests.c
+++ b/src/crypto/crypto_module_tests.c
@@ -1912,6 +1912,135 @@
 }
 
 
+static int test_extract_expand_hkdf(void)
+{
+	u8 prk[SHA256_MAC_LEN];
+	u8 okm[82];
+
+	/* RFC 5869, A.1 */
+	u8 ikm1[22] = {
+		0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+		0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+		0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b
+	};
+	u8 salt1[13] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b, 0x0c
+	};
+	u8 info1[10] = {
+		0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+		0xf8, 0xf9
+	};
+	u8 prk1[32] = {
+		0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf,
+		0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63,
+		0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31,
+		0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5
+	};
+	u8 okm1[42] = {
+		0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a,
+		0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a,
+		0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c,
+		0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf,
+		0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18,
+		0x58, 0x65
+	};
+
+	/* RFC 5869, A.2 */
+	u8 ikm2[80] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+		0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+		0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+		0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+		0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+		0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+		0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+		0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f
+	};
+	u8 salt2[80] = {
+		0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+		0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+		0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+		0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+		0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+		0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+		0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+		0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+		0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+		0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf
+	};
+	u8 info2[80] = {
+		0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+		0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+		0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+		0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+		0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+		0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+		0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+		0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+		0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+		0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+	};
+	u8 prk2[32] = {
+		0x06, 0xa6, 0xb8, 0x8c, 0x58, 0x53, 0x36, 0x1a,
+		0x06, 0x10, 0x4c, 0x9c, 0xeb, 0x35, 0xb4, 0x5c,
+		0xef, 0x76, 0x00, 0x14, 0x90, 0x46, 0x71, 0x01,
+		0x4a, 0x19, 0x3f, 0x40, 0xc1, 0x5f, 0xc2, 0x44
+	};
+	u8 okm2[82] = {
+		0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1,
+		0xc8, 0xe7, 0xf7, 0x8c, 0x59, 0x6a, 0x49, 0x34,
+		0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e, 0xfa, 0xd8,
+		0xa0, 0x50, 0xcc, 0x4c, 0x19, 0xaf, 0xa9, 0x7c,
+		0x59, 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72,
+		0x71, 0xcb, 0x41, 0xc6, 0x5e, 0x59, 0x0e, 0x09,
+		0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8,
+		0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71,
+		0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec, 0x3e, 0x87,
+		0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f,
+		0x1d, 0x87
+	};
+
+	wpa_printf(MSG_INFO, "Testing Extract-and-Expand HKDF (RFC 5869)");
+
+	wpa_printf(MSG_INFO, "RFC 5869 - Test Case 1");
+	if (hmac_sha256(salt1, sizeof(salt1), ikm1, sizeof(ikm1), prk) < 0)
+		return -1;
+	if (os_memcmp(prk, prk1, SHA256_MAC_LEN) != 0) {
+		wpa_printf(MSG_INFO, "HKDF-Extract mismatch in PRK");
+		return -1;
+	}
+	if (hmac_sha256_kdf(prk1, sizeof(prk1), NULL, info1, sizeof(info1),
+			    okm, sizeof(okm1)) < 0)
+		return -1;
+	if (os_memcmp(okm, okm1, sizeof(okm1)) != 0) {
+		wpa_printf(MSG_INFO, "HKDF-Expand mismatch in OKM");
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "RFC 5869 - Test Case 2");
+	if (hmac_sha256(salt2, sizeof(salt2), ikm2, sizeof(ikm2), prk) < 0)
+		return -1;
+	if (os_memcmp(prk, prk2, SHA256_MAC_LEN) != 0) {
+		wpa_printf(MSG_INFO, "HKDF-Extract mismatch in PRK");
+		return -1;
+	}
+	if (hmac_sha256_kdf(prk2, sizeof(prk2), NULL, info2, sizeof(info2),
+			    okm, sizeof(okm2)) < 0)
+		return -1;
+	if (os_memcmp(okm, okm2, sizeof(okm2)) != 0) {
+		wpa_printf(MSG_INFO, "HKDF-Expand mismatch in OKM");
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "Extract-and-Expand HKDF test cases passed");
+
+	return 0;
+}
+
+
 static int test_ms_funcs(void)
 {
 #ifndef CONFIG_FIPS
@@ -2030,6 +2159,7 @@
 	    test_sha256() ||
 	    test_sha384() ||
 	    test_fips186_2_prf() ||
+	    test_extract_expand_hkdf() ||
 	    test_ms_funcs())
 		ret = -1;
 
diff --git a/src/crypto/crypto_none.c b/src/crypto/crypto_none.c
index 011f3f3..5479194 100644
--- a/src/crypto/crypto_none.c
+++ b/src/crypto/crypto_none.c
@@ -18,6 +18,7 @@
 }
 
 
-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
 {
+	return 0;
 }
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 5f7896c..6bff202 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -1,6 +1,6 @@
 /*
  * Wrapper functions for OpenSSL libcrypto
- * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -161,7 +161,7 @@
 #endif /* CONFIG_FIPS */
 
 
-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
 {
 	u8 pkey[8], next, tmp;
 	int i;
@@ -179,6 +179,7 @@
 	DES_set_key((DES_cblock *) &pkey, &ks);
 	DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks,
 			DES_ENCRYPT);
+	return 0;
 }
 
 
@@ -245,6 +246,7 @@
 }
 #endif /* NO_SHA256_WRAPPER */
 
+
 #ifndef NO_SHA384_WRAPPER
 int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
 		  u8 *mac)
@@ -254,6 +256,15 @@
 #endif /* NO_SHA384_WRAPPER */
 
 
+#ifndef NO_SHA512_WRAPPER
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	return openssl_digest_vector(EVP_sha512(), num_elem, addr, len, mac);
+}
+#endif /* NO_SHA512_WRAPPER */
+
+
 static const EVP_CIPHER * aes_get_evp_cipher(size_t keylen)
 {
 	switch (keylen) {
@@ -295,14 +306,16 @@
 }
 
 
-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
 {
 	EVP_CIPHER_CTX *c = ctx;
 	int clen = 16;
 	if (EVP_EncryptUpdate(c, crypt, &clen, plain, 16) != 1) {
 		wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptUpdate failed: %s",
 			   ERR_error_string(ERR_get_error(), NULL));
+		return -1;
 	}
+	return 0;
 }
 
 
@@ -347,14 +360,16 @@
 }
 
 
-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
 {
 	EVP_CIPHER_CTX *c = ctx;
 	int plen = 16;
 	if (EVP_DecryptUpdate(c, plain, &plen, crypt, 16) != 1) {
 		wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptUpdate failed: %s",
 			   ERR_error_string(ERR_get_error(), NULL));
+		return -1;
 	}
+	return 0;
 }
 
 
@@ -1044,6 +1059,25 @@
 #endif /* CONFIG_SHA384 */
 
 
+#ifdef CONFIG_SHA512
+
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_hmac_vector(EVP_sha512(), key, key_len, num_elem, addr,
+				   len, mac, 64);
+}
+
+
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA512 */
+
+
 int crypto_get_random(void *buf, size_t len)
 {
 	if (RAND_bytes(buf, len) != 1)
@@ -1358,6 +1392,7 @@
 
 struct crypto_ec {
 	EC_GROUP *group;
+	int nid;
 	BN_CTX *bnctx;
 	BIGNUM *prime;
 	BIGNUM *order;
@@ -1415,6 +1450,7 @@
 	if (e == NULL)
 		return NULL;
 
+	e->nid = nid;
 	e->bnctx = BN_CTX_new();
 	e->group = EC_GROUP_new_by_curve_name(nid);
 	e->prime = BN_new();
@@ -1655,4 +1691,239 @@
 			    (const EC_POINT *) b, e->bnctx);
 }
 
+
+struct crypto_ecdh {
+	struct crypto_ec *ec;
+	EVP_PKEY *pkey;
+};
+
+struct crypto_ecdh * crypto_ecdh_init(int group)
+{
+	struct crypto_ecdh *ecdh;
+	EVP_PKEY *params = NULL;
+	EVP_PKEY_CTX *pctx = NULL;
+	EVP_PKEY_CTX *kctx = NULL;
+
+	ecdh = os_zalloc(sizeof(*ecdh));
+	if (!ecdh)
+		goto fail;
+
+	ecdh->ec = crypto_ec_init(group);
+	if (!ecdh->ec)
+		goto fail;
+
+	pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
+	if (!pctx)
+		goto fail;
+
+	if (EVP_PKEY_paramgen_init(pctx) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: EVP_PKEY_paramgen_init failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, ecdh->ec->nid) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: EVP_PKEY_CTX_set_ec_paramgen_curve_nid failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	if (EVP_PKEY_paramgen(pctx, &params) != 1) {
+		wpa_printf(MSG_ERROR, "OpenSSL: EVP_PKEY_paramgen failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	kctx = EVP_PKEY_CTX_new(params, NULL);
+	if (!kctx)
+		goto fail;
+
+	if (EVP_PKEY_keygen_init(kctx) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: EVP_PKEY_keygen_init failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	if (EVP_PKEY_keygen(kctx, &ecdh->pkey) != 1) {
+		wpa_printf(MSG_ERROR, "OpenSSL: EVP_PKEY_keygen failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+done:
+	EVP_PKEY_free(params);
+	EVP_PKEY_CTX_free(pctx);
+	EVP_PKEY_CTX_free(kctx);
+
+	return ecdh;
+fail:
+	crypto_ecdh_deinit(ecdh);
+	ecdh = NULL;
+	goto done;
+}
+
+
+struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y)
+{
+	struct wpabuf *buf = NULL;
+	EC_KEY *eckey;
+	const EC_POINT *pubkey;
+	BIGNUM *x, *y = NULL;
+	int len = BN_num_bytes(ecdh->ec->prime);
+	int res;
+
+	eckey = EVP_PKEY_get1_EC_KEY(ecdh->pkey);
+	if (!eckey)
+		return NULL;
+
+	pubkey = EC_KEY_get0_public_key(eckey);
+	if (!pubkey)
+		return NULL;
+
+	x = BN_new();
+	if (inc_y) {
+		y = BN_new();
+		if (!y)
+			goto fail;
+	}
+	buf = wpabuf_alloc(inc_y ? 2 * len : len);
+	if (!x || !buf)
+		goto fail;
+
+	if (EC_POINT_get_affine_coordinates_GFp(ecdh->ec->group, pubkey,
+						x, y, ecdh->ec->bnctx) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: EC_POINT_get_affine_coordinates_GFp failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	res = crypto_bignum_to_bin((struct crypto_bignum *) x,
+				   wpabuf_put(buf, len), len, len);
+	if (res < 0)
+		goto fail;
+
+	if (inc_y) {
+		res = crypto_bignum_to_bin((struct crypto_bignum *) y,
+					   wpabuf_put(buf, len), len, len);
+		if (res < 0)
+			goto fail;
+	}
+
+done:
+	BN_clear_free(x);
+	BN_clear_free(y);
+	EC_KEY_free(eckey);
+
+	return buf;
+fail:
+	wpabuf_free(buf);
+	buf = NULL;
+	goto done;
+}
+
+
+struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
+					const u8 *key, size_t len)
+{
+	BIGNUM *x, *y = NULL;
+	EVP_PKEY_CTX *ctx = NULL;
+	EVP_PKEY *peerkey = NULL;
+	struct wpabuf *secret = NULL;
+	size_t secret_len;
+	EC_POINT *pub;
+	EC_KEY *eckey = NULL;
+
+	x = BN_bin2bn(key, inc_y ? len / 2 : len, NULL);
+	pub = EC_POINT_new(ecdh->ec->group);
+	if (!x || !pub)
+		goto fail;
+
+	if (inc_y) {
+		y = BN_bin2bn(key + len / 2, len / 2, NULL);
+		if (!y)
+			goto fail;
+		if (!EC_POINT_set_affine_coordinates_GFp(ecdh->ec->group, pub,
+							 x, y,
+							 ecdh->ec->bnctx)) {
+			wpa_printf(MSG_ERROR,
+				   "OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s",
+				   ERR_error_string(ERR_get_error(), NULL));
+			goto fail;
+		}
+	} else if (!EC_POINT_set_compressed_coordinates_GFp(ecdh->ec->group,
+							    pub, x, 0,
+							    ecdh->ec->bnctx)) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: EC_POINT_set_compressed_coordinates_GFp failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	if (!EC_POINT_is_on_curve(ecdh->ec->group, pub, ecdh->ec->bnctx)) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: ECDH peer public key is not on curve");
+		goto fail;
+	}
+
+	eckey = EC_KEY_new_by_curve_name(ecdh->ec->nid);
+	if (!eckey || EC_KEY_set_public_key(eckey, pub) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: EC_KEY_set_public_key failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	peerkey = EVP_PKEY_new();
+	if (!peerkey || EVP_PKEY_set1_EC_KEY(peerkey, eckey) != 1)
+		goto fail;
+
+	ctx = EVP_PKEY_CTX_new(ecdh->pkey, NULL);
+	if (!ctx || EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, peerkey) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &secret_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: EVP_PKEY_derive(1) failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	secret = wpabuf_alloc(secret_len);
+	if (!secret)
+		goto fail;
+	if (EVP_PKEY_derive(ctx, wpabuf_put(secret, secret_len),
+			    &secret_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: EVP_PKEY_derive(2) failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+done:
+	BN_free(x);
+	BN_free(y);
+	EC_KEY_free(eckey);
+	EC_POINT_free(pub);
+	EVP_PKEY_CTX_free(ctx);
+	EVP_PKEY_free(peerkey);
+	return secret;
+fail:
+	wpabuf_free(secret);
+	secret = NULL;
+	goto done;
+}
+
+
+void crypto_ecdh_deinit(struct crypto_ecdh *ecdh)
+{
+	if (ecdh) {
+		crypto_ec_deinit(ecdh->ec);
+		EVP_PKEY_free(ecdh->pkey);
+		os_free(ecdh);
+	}
+}
+
 #endif /* CONFIG_ECC */
diff --git a/src/crypto/des-internal.c b/src/crypto/des-internal.c
index ebd9952..4ed6957 100644
--- a/src/crypto/des-internal.c
+++ b/src/crypto/des-internal.c
@@ -396,7 +396,7 @@
 
 /* wpa_supplicant/hostapd specific wrapper */
 
-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
 {
 	u8 pkey[8], next, tmp;
 	int i;
@@ -421,6 +421,7 @@
 
 	os_memset(pkey, 0, sizeof(pkey));
 	os_memset(ek, 0, sizeof(ek));
+	return 0;
 }
 
 
diff --git a/src/crypto/ms_funcs.c b/src/crypto/ms_funcs.c
index d0d6a96..aff7d33 100644
--- a/src/crypto/ms_funcs.c
+++ b/src/crypto/ms_funcs.c
@@ -140,17 +140,20 @@
  * @challenge: 8-octet Challenge (IN)
  * @password_hash: 16-octet PasswordHash (IN)
  * @response: 24-octet Response (OUT)
+ * Returns: 0 on success, -1 on failure
  */
-void challenge_response(const u8 *challenge, const u8 *password_hash,
-			u8 *response)
+int challenge_response(const u8 *challenge, const u8 *password_hash,
+		       u8 *response)
 {
 	u8 zpwd[7];
-	des_encrypt(challenge, password_hash, response);
-	des_encrypt(challenge, password_hash + 7, response + 8);
+
+	if (des_encrypt(challenge, password_hash, response) < 0 ||
+	    des_encrypt(challenge, password_hash + 7, response + 8) < 0)
+		return -1;
 	zpwd[0] = password_hash[14];
 	zpwd[1] = password_hash[15];
 	os_memset(zpwd + 2, 0, 5);
-	des_encrypt(challenge, zpwd, response + 16);
+	return des_encrypt(challenge, zpwd, response + 16);
 }
 
 
@@ -175,9 +178,9 @@
 
 	if (challenge_hash(peer_challenge, auth_challenge, username,
 			   username_len, challenge) ||
-	    nt_password_hash(password, password_len, password_hash))
+	    nt_password_hash(password, password_len, password_hash) ||
+	    challenge_response(challenge, password_hash, response))
 		return -1;
-	challenge_response(challenge, password_hash, response);
 	return 0;
 }
 
@@ -202,9 +205,9 @@
 
 	if (challenge_hash(peer_challenge, auth_challenge,
 			   username, username_len,
-			   challenge))
+			   challenge) ||
+	    challenge_response(challenge, password_hash, response))
 		return -1;
-	challenge_response(challenge, password_hash, response);
 	return 0;
 }
 
@@ -304,9 +307,10 @@
 			  size_t password_len, u8 *response)
 {
 	u8 password_hash[16];
-	if (nt_password_hash(password, password_len, password_hash))
+
+	if (nt_password_hash(password, password_len, password_hash) ||
+	    challenge_response(challenge, password_hash, response))
 		return -1;
-	challenge_response(challenge, password_hash, response);
 	return 0;
 }
 
@@ -487,12 +491,15 @@
  * @password_hash: 16-octer PasswordHash (IN)
  * @block: 16-octet Block (IN)
  * @cypher: 16-octer Cypher (OUT)
+ * Returns: 0 on success, -1 on failure
  */
-void nt_password_hash_encrypted_with_block(const u8 *password_hash,
-					   const u8 *block, u8 *cypher)
+int nt_password_hash_encrypted_with_block(const u8 *password_hash,
+					  const u8 *block, u8 *cypher)
 {
-	des_encrypt(password_hash, block, cypher);
-	des_encrypt(password_hash + 8, block + 7, cypher + 8);
+	if (des_encrypt(password_hash, block, cypher) < 0 ||
+	    des_encrypt(password_hash + 8, block + 7, cypher + 8) < 0)
+		return -1;
+	return 0;
 }
 
 
@@ -515,10 +522,10 @@
 	if (nt_password_hash(old_password, old_password_len,
 			     old_password_hash) ||
 	    nt_password_hash(new_password, new_password_len,
-			     new_password_hash))
+			     new_password_hash) ||
+	    nt_password_hash_encrypted_with_block(old_password_hash,
+						  new_password_hash,
+						  encrypted_password_hash))
 		return -1;
-	nt_password_hash_encrypted_with_block(old_password_hash,
-					      new_password_hash,
-					      encrypted_password_hash);
 	return 0;
 }
diff --git a/src/crypto/ms_funcs.h b/src/crypto/ms_funcs.h
index b5b5918..b8d55f0 100644
--- a/src/crypto/ms_funcs.h
+++ b/src/crypto/ms_funcs.h
@@ -31,8 +31,8 @@
 int nt_challenge_response(const u8 *challenge, const u8 *password,
 			  size_t password_len, u8 *response);
 
-void challenge_response(const u8 *challenge, const u8 *password_hash,
-			u8 *response);
+int challenge_response(const u8 *challenge, const u8 *password_hash,
+		       u8 *response);
 int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
 		   const u8 *username, size_t username_len, u8 *challenge);
 int nt_password_hash(const u8 *password, size_t password_len,
@@ -50,8 +50,8 @@
 	const u8 *new_password, size_t new_password_len,
 	const u8 *old_password, size_t old_password_len,
 	u8 *encrypted_pw_block);
-void nt_password_hash_encrypted_with_block(const u8 *password_hash,
-					   const u8 *block, u8 *cypher);
+int nt_password_hash_encrypted_with_block(const u8 *password_hash,
+					  const u8 *block, u8 *cypher);
 int old_nt_password_hash_encrypted_with_new_nt_password_hash(
 	const u8 *new_password, size_t new_password_len,
 	const u8 *old_password, size_t old_password_len,
diff --git a/src/crypto/random.c b/src/crypto/random.c
index 3a86a93..fb92417 100644
--- a/src/crypto/random.c
+++ b/src/crypto/random.c
@@ -66,6 +66,9 @@
 
 static u32 __ROL32(u32 x, u32 y)
 {
+	if (y == 0)
+		return x;
+
 	return (x << (y & 31)) | (x >> (32 - (y & 31)));
 }
 
diff --git a/src/crypto/sha256-kdf.c b/src/crypto/sha256-kdf.c
index e7509ce..af7d954 100644
--- a/src/crypto/sha256-kdf.c
+++ b/src/crypto/sha256-kdf.c
@@ -1,6 +1,6 @@
 /*
- * HMAC-SHA256 KDF (RFC 5295)
- * Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+ * HMAC-SHA256 KDF (RFC 5295) and HKDF-Expand(SHA256) (RFC 5869)
+ * Copyright (c) 2014-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -16,7 +16,8 @@
  * hmac_sha256_kdf - HMAC-SHA256 based KDF (RFC 5295)
  * @secret: Key for KDF
  * @secret_len: Length of the key in bytes
- * @label: A unique label for each purpose of the KDF
+ * @label: A unique label for each purpose of the KDF or %NULL to select
+ *	RFC 5869 HKDF-Expand() with arbitrary seed (= info)
  * @seed: Seed value to bind into the key
  * @seed_len: Length of the seed
  * @out: Buffer for the generated pseudo-random key
@@ -24,7 +25,9 @@
  * Returns: 0 on success, -1 on failure.
  *
  * This function is used to derive new, cryptographically separate keys from a
- * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2.
+ * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used
+ * with label = NULL and seed = info, this matches HKDF-Expand() defined in
+ * RFC 5869, Chapter 2.3.
  */
 int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
 		    const char *label, const u8 *seed, size_t seed_len,
@@ -38,8 +41,13 @@
 
 	addr[0] = T;
 	len[0] = SHA256_MAC_LEN;
-	addr[1] = (const unsigned char *) label;
-	len[1] = os_strlen(label) + 1;
+	if (label) {
+		addr[1] = (const unsigned char *) label;
+		len[1] = os_strlen(label) + 1;
+	} else {
+		addr[1] = (const u8 *) "";
+		len[1] = 0;
+	}
 	addr[2] = seed;
 	len[2] = seed_len;
 	addr[3] = &iter;
diff --git a/src/crypto/sha384-kdf.c b/src/crypto/sha384-kdf.c
new file mode 100644
index 0000000..1d19627
--- /dev/null
+++ b/src/crypto/sha384-kdf.c
@@ -0,0 +1,87 @@
+/*
+ * HMAC-SHA384 KDF (RFC 5295) and HKDF-Expand(SHA384) (RFC 5869)
+ * Copyright (c) 2014-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha384.h"
+
+
+/**
+ * hmac_sha384_kdf - HMAC-SHA384 based KDF (RFC 5295)
+ * @secret: Key for KDF
+ * @secret_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the KDF or %NULL to select
+ *	RFC 5869 HKDF-Expand() with arbitrary seed (= info)
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used
+ * with label = NULL and seed = info, this matches HKDF-Expand() defined in
+ * RFC 5869, Chapter 2.3.
+ */
+int hmac_sha384_kdf(const u8 *secret, size_t secret_len,
+		    const char *label, const u8 *seed, size_t seed_len,
+		    u8 *out, size_t outlen)
+{
+	u8 T[SHA384_MAC_LEN];
+	u8 iter = 1;
+	const unsigned char *addr[4];
+	size_t len[4];
+	size_t pos, clen;
+
+	addr[0] = T;
+	len[0] = SHA384_MAC_LEN;
+	if (label) {
+		addr[1] = (const unsigned char *) label;
+		len[1] = os_strlen(label) + 1;
+	} else {
+		addr[1] = (const u8 *) "";
+		len[1] = 0;
+	}
+	addr[2] = seed;
+	len[2] = seed_len;
+	addr[3] = &iter;
+	len[3] = 1;
+
+	if (hmac_sha384_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0)
+		return -1;
+
+	pos = 0;
+	for (;;) {
+		clen = outlen - pos;
+		if (clen > SHA384_MAC_LEN)
+			clen = SHA384_MAC_LEN;
+		os_memcpy(out + pos, T, clen);
+		pos += clen;
+
+		if (pos == outlen)
+			break;
+
+		if (iter == 255) {
+			os_memset(out, 0, outlen);
+			os_memset(T, 0, SHA384_MAC_LEN);
+			return -1;
+		}
+		iter++;
+
+		if (hmac_sha384_vector(secret, secret_len, 4, addr, len, T) < 0)
+		{
+			os_memset(out, 0, outlen);
+			os_memset(T, 0, SHA384_MAC_LEN);
+			return -1;
+		}
+	}
+
+	os_memset(T, 0, SHA384_MAC_LEN);
+	return 0;
+}
diff --git a/src/crypto/sha384-prf.c b/src/crypto/sha384-prf.c
index 653920b..03e3cb3 100644
--- a/src/crypto/sha384-prf.c
+++ b/src/crypto/sha384-prf.c
@@ -1,6 +1,6 @@
 /*
  * SHA384-based KDF (IEEE 802.11ac)
- * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -22,14 +22,16 @@
  * @data_len: Length of the data
  * @buf: Buffer for the generated pseudo-random key
  * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure
  *
  * This function is used to derive new, cryptographically separate keys from a
  * given key.
  */
-void sha384_prf(const u8 *key, size_t key_len, const char *label,
-		const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+int sha384_prf(const u8 *key, size_t key_len, const char *label,
+	       const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
 {
-	sha384_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8);
+	return sha384_prf_bits(key, key_len, label, data, data_len, buf,
+			       buf_len * 8);
 }
 
 
@@ -42,15 +44,16 @@
  * @data_len: Length of the data
  * @buf: Buffer for the generated pseudo-random key
  * @buf_len: Number of bits of key to generate
+ * Returns: 0 on success, -1 on failure
  *
  * This function is used to derive new, cryptographically separate keys from a
  * given key. If the requested buf_len is not divisible by eight, the least
  * significant 1-7 bits of the last octet in the output are not part of the
  * requested output.
  */
-void sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
-		     const u8 *data, size_t data_len, u8 *buf,
-		     size_t buf_len_bits)
+int sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
+		    const u8 *data, size_t data_len, u8 *buf,
+		    size_t buf_len_bits)
 {
 	u16 counter = 1;
 	size_t pos, plen;
@@ -75,11 +78,14 @@
 		plen = buf_len - pos;
 		WPA_PUT_LE16(counter_le, counter);
 		if (plen >= SHA384_MAC_LEN) {
-			hmac_sha384_vector(key, key_len, 4, addr, len,
-					   &buf[pos]);
+			if (hmac_sha384_vector(key, key_len, 4, addr, len,
+					       &buf[pos]) < 0)
+				return -1;
 			pos += SHA384_MAC_LEN;
 		} else {
-			hmac_sha384_vector(key, key_len, 4, addr, len, hash);
+			if (hmac_sha384_vector(key, key_len, 4, addr, len,
+					       hash) < 0)
+				return -1;
 			os_memcpy(&buf[pos], hash, plen);
 			pos += plen;
 			break;
@@ -97,4 +103,6 @@
 	}
 
 	os_memset(hash, 0, sizeof(hash));
+
+	return 0;
 }
diff --git a/src/crypto/sha384.h b/src/crypto/sha384.h
index 3deafa5..2241425 100644
--- a/src/crypto/sha384.h
+++ b/src/crypto/sha384.h
@@ -1,6 +1,6 @@
 /*
  * SHA384 hash implementation and interface functions
- * Copyright (c) 2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2015-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -15,10 +15,13 @@
 		       const u8 *addr[], const size_t *len, u8 *mac);
 int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
 		size_t data_len, u8 *mac);
-void sha384_prf(const u8 *key, size_t key_len, const char *label,
-		const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
-void sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
-		     const u8 *data, size_t data_len, u8 *buf,
-		     size_t buf_len_bits);
+int sha384_prf(const u8 *key, size_t key_len, const char *label,
+	       const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+int sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
+		    const u8 *data, size_t data_len, u8 *buf,
+		    size_t buf_len_bits);
+int hmac_sha384_kdf(const u8 *secret, size_t secret_len,
+		    const char *label, const u8 *seed, size_t seed_len,
+		    u8 *out, size_t outlen);
 
 #endif /* SHA384_H */
diff --git a/src/crypto/sha512-kdf.c b/src/crypto/sha512-kdf.c
new file mode 100644
index 0000000..8b71f9b
--- /dev/null
+++ b/src/crypto/sha512-kdf.c
@@ -0,0 +1,87 @@
+/*
+ * HMAC-SHA512 KDF (RFC 5295) and HKDF-Expand(SHA512) (RFC 5869)
+ * Copyright (c) 2014-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha512.h"
+
+
+/**
+ * hmac_sha512_kdf - HMAC-SHA512 based KDF (RFC 5295)
+ * @secret: Key for KDF
+ * @secret_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the KDF or %NULL to select
+ *	RFC 5869 HKDF-Expand() with arbitrary seed (= info)
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used
+ * with label = NULL and seed = info, this matches HKDF-Expand() defined in
+ * RFC 5869, Chapter 2.3.
+ */
+int hmac_sha512_kdf(const u8 *secret, size_t secret_len,
+		    const char *label, const u8 *seed, size_t seed_len,
+		    u8 *out, size_t outlen)
+{
+	u8 T[SHA512_MAC_LEN];
+	u8 iter = 1;
+	const unsigned char *addr[4];
+	size_t len[4];
+	size_t pos, clen;
+
+	addr[0] = T;
+	len[0] = SHA512_MAC_LEN;
+	if (label) {
+		addr[1] = (const unsigned char *) label;
+		len[1] = os_strlen(label) + 1;
+	} else {
+		addr[1] = (const u8 *) "";
+		len[1] = 0;
+	}
+	addr[2] = seed;
+	len[2] = seed_len;
+	addr[3] = &iter;
+	len[3] = 1;
+
+	if (hmac_sha512_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0)
+		return -1;
+
+	pos = 0;
+	for (;;) {
+		clen = outlen - pos;
+		if (clen > SHA512_MAC_LEN)
+			clen = SHA512_MAC_LEN;
+		os_memcpy(out + pos, T, clen);
+		pos += clen;
+
+		if (pos == outlen)
+			break;
+
+		if (iter == 255) {
+			os_memset(out, 0, outlen);
+			os_memset(T, 0, SHA512_MAC_LEN);
+			return -1;
+		}
+		iter++;
+
+		if (hmac_sha512_vector(secret, secret_len, 4, addr, len, T) < 0)
+		{
+			os_memset(out, 0, outlen);
+			os_memset(T, 0, SHA512_MAC_LEN);
+			return -1;
+		}
+	}
+
+	os_memset(T, 0, SHA512_MAC_LEN);
+	return 0;
+}
diff --git a/src/crypto/sha512-prf.c b/src/crypto/sha512-prf.c
new file mode 100644
index 0000000..3b2ad88
--- /dev/null
+++ b/src/crypto/sha512-prf.c
@@ -0,0 +1,108 @@
+/*
+ * SHA512-based KDF (IEEE 802.11ac)
+ * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha512.h"
+#include "crypto.h"
+
+
+/**
+ * sha512_prf - SHA512-based Key derivation function (IEEE 802.11ac, 11.6.1.7.2)
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key.
+ */
+int sha512_prf(const u8 *key, size_t key_len, const char *label,
+	       const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+	return sha512_prf_bits(key, key_len, label, data, data_len, buf,
+			       buf_len * 8);
+}
+
+
+/**
+ * sha512_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bits of key to generate
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key. If the requested buf_len is not divisible by eight, the least
+ * significant 1-7 bits of the last octet in the output are not part of the
+ * requested output.
+ */
+int sha512_prf_bits(const u8 *key, size_t key_len, const char *label,
+		    const u8 *data, size_t data_len, u8 *buf,
+		    size_t buf_len_bits)
+{
+	u16 counter = 1;
+	size_t pos, plen;
+	u8 hash[SHA512_MAC_LEN];
+	const u8 *addr[4];
+	size_t len[4];
+	u8 counter_le[2], length_le[2];
+	size_t buf_len = (buf_len_bits + 7) / 8;
+
+	addr[0] = counter_le;
+	len[0] = 2;
+	addr[1] = (u8 *) label;
+	len[1] = os_strlen(label);
+	addr[2] = data;
+	len[2] = data_len;
+	addr[3] = length_le;
+	len[3] = sizeof(length_le);
+
+	WPA_PUT_LE16(length_le, buf_len_bits);
+	pos = 0;
+	while (pos < buf_len) {
+		plen = buf_len - pos;
+		WPA_PUT_LE16(counter_le, counter);
+		if (plen >= SHA512_MAC_LEN) {
+			if (hmac_sha512_vector(key, key_len, 4, addr, len,
+					       &buf[pos]) < 0)
+				return -1;
+			pos += SHA512_MAC_LEN;
+		} else {
+			if (hmac_sha512_vector(key, key_len, 4, addr, len,
+					       hash) < 0)
+				return -1;
+			os_memcpy(&buf[pos], hash, plen);
+			pos += plen;
+			break;
+		}
+		counter++;
+	}
+
+	/*
+	 * Mask out unused bits in the last octet if it does not use all the
+	 * bits.
+	 */
+	if (buf_len_bits % 8) {
+		u8 mask = 0xff << (8 - buf_len_bits % 8);
+		buf[pos - 1] &= mask;
+	}
+
+	os_memset(hash, 0, sizeof(hash));
+
+	return 0;
+}
diff --git a/src/crypto/sha512.h b/src/crypto/sha512.h
new file mode 100644
index 0000000..8e64c8b
--- /dev/null
+++ b/src/crypto/sha512.h
@@ -0,0 +1,27 @@
+/*
+ * SHA512 hash implementation and interface functions
+ * Copyright (c) 2015-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA512_H
+#define SHA512_H
+
+#define SHA512_MAC_LEN 64
+
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac);
+int sha512_prf(const u8 *key, size_t key_len, const char *label,
+	       const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+int sha512_prf_bits(const u8 *key, size_t key_len, const char *label,
+		    const u8 *data, size_t data_len, u8 *buf,
+		    size_t buf_len_bits);
+int hmac_sha512_kdf(const u8 *secret, size_t secret_len,
+		    const char *label, const u8 *seed, size_t seed_len,
+		    u8 *out, size_t outlen);
+
+#endif /* SHA512_H */
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index 11d504a..dc4117c 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -41,6 +41,7 @@
 	TLS_FAIL_SERVER_CHAIN_PROBE = 8,
 	TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9,
 	TLS_FAIL_DOMAIN_MISMATCH = 10,
+	TLS_FAIL_INSUFFICIENT_KEY_LEN = 11,
 };
 
 
@@ -80,6 +81,7 @@
 	int cert_in_cb;
 	const char *openssl_ciphers;
 	unsigned int tls_session_lifetime;
+	unsigned int tls_flags;
 
 	void (*event_cb)(void *ctx, enum tls_event ev,
 			 union tls_event_data *data);
@@ -97,6 +99,8 @@
 #define TLS_CONN_DISABLE_TLSv1_0 BIT(8)
 #define TLS_CONN_EXT_CERT_CHECK BIT(9)
 #define TLS_CONN_REQUIRE_OCSP_ALL BIT(10)
+#define TLS_CONN_SUITEB BIT(11)
+#define TLS_CONN_SUITEB_NO_ECDH BIT(12)
 
 /**
  * struct tls_connection_params - Parameters for TLS connection
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index 4521891..988c9d2 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -103,6 +103,15 @@
 
 #endif
 
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#ifdef CONFIG_SUITEB
+static int RSA_bits(const RSA *r)
+{
+	return BN_num_bits(r->n);
+}
+#endif /* CONFIG_SUITEB */
+#endif
+
 #ifdef ANDROID
 #include <openssl/pem.h>
 #include <keystore/keystore_get.h>
@@ -233,6 +242,9 @@
 
 	unsigned char client_random[SSL3_RANDOM_SIZE];
 	unsigned char server_random[SSL3_RANDOM_SIZE];
+
+	u16 cipher_suite;
+	int server_dh_prime_len;
 };
 
 
@@ -1025,7 +1037,7 @@
 	if (conf && conf->openssl_ciphers)
 		ciphers = conf->openssl_ciphers;
 	else
-		ciphers = "DEFAULT:!EXP:!LOW";
+		ciphers = TLS_DEFAULT_CIPHERS;
 	if (SSL_CTX_set_cipher_list(ssl, ciphers) != 1) {
 		wpa_printf(MSG_ERROR,
 			   "OpenSSL: Failed to set cipher string '%s'",
@@ -1304,6 +1316,95 @@
 }
 
 
+#ifdef CONFIG_SUITEB
+
+static void check_server_hello(struct tls_connection *conn,
+			       const u8 *pos, const u8 *end)
+{
+	size_t payload_len, id_len;
+
+	/*
+	 * Parse ServerHello to get the selected cipher suite since OpenSSL does
+	 * not make it cleanly available during handshake and we need to know
+	 * whether DHE was selected.
+	 */
+
+	if (end - pos < 3)
+		return;
+	payload_len = WPA_GET_BE24(pos);
+	pos += 3;
+
+	if ((size_t) (end - pos) < payload_len)
+		return;
+	end = pos + payload_len;
+
+	/* Skip Version and Random */
+	if (end - pos < 2 + SSL3_RANDOM_SIZE)
+		return;
+	pos += 2 + SSL3_RANDOM_SIZE;
+
+	/* Skip Session ID */
+	if (end - pos < 1)
+		return;
+	id_len = *pos++;
+	if ((size_t) (end - pos) < id_len)
+		return;
+	pos += id_len;
+
+	if (end - pos < 2)
+		return;
+	conn->cipher_suite = WPA_GET_BE16(pos);
+	wpa_printf(MSG_DEBUG, "OpenSSL: Server selected cipher suite 0x%x",
+		   conn->cipher_suite);
+}
+
+
+static void check_server_key_exchange(SSL *ssl, struct tls_connection *conn,
+				      const u8 *pos, const u8 *end)
+{
+	size_t payload_len;
+	u16 dh_len;
+	BIGNUM *p;
+	int bits;
+
+	if (!(conn->flags & TLS_CONN_SUITEB))
+		return;
+
+	/* DHE is enabled only with DHE-RSA-AES256-GCM-SHA384 */
+	if (conn->cipher_suite != 0x9f)
+		return;
+
+	if (end - pos < 3)
+		return;
+	payload_len = WPA_GET_BE24(pos);
+	pos += 3;
+
+	if ((size_t) (end - pos) < payload_len)
+		return;
+	end = pos + payload_len;
+
+	if (end - pos < 2)
+		return;
+	dh_len = WPA_GET_BE16(pos);
+	pos += 2;
+
+	if ((size_t) (end - pos) < dh_len)
+		return;
+	p = BN_bin2bn(pos, dh_len, NULL);
+	if (!p)
+		return;
+
+	bits = BN_num_bits(p);
+	BN_free(p);
+
+	conn->server_dh_prime_len = bits;
+	wpa_printf(MSG_DEBUG, "OpenSSL: Server DH prime length: %d bits",
+		   conn->server_dh_prime_len);
+}
+
+#endif /* CONFIG_SUITEB */
+
+
 static void tls_msg_cb(int write_p, int version, int content_type,
 		       const void *buf, size_t len, SSL *ssl, void *arg)
 {
@@ -1330,6 +1431,18 @@
 			conn->invalid_hb_used = 1;
 		}
 	}
+
+#ifdef CONFIG_SUITEB
+	/*
+	 * Need to parse these handshake messages to be able to check DH prime
+	 * length since OpenSSL does not expose the new cipher suite and DH
+	 * parameters during handshake (e.g., for cert_cb() callback).
+	 */
+	if (content_type == 22 && pos && len > 0 && pos[0] == 2)
+		check_server_hello(conn, pos + 1, pos + len);
+	if (content_type == 22 && pos && len > 0 && pos[0] == 12)
+		check_server_key_exchange(ssl, conn, pos + 1, pos + len);
+#endif /* CONFIG_SUITEB */
 }
 
 
@@ -1924,6 +2037,37 @@
 				       TLS_FAIL_SERVER_CHAIN_PROBE);
 	}
 
+#ifdef CONFIG_SUITEB
+	if (conn->flags & TLS_CONN_SUITEB) {
+		EVP_PKEY *pk;
+		RSA *rsa;
+		int len = -1;
+
+		pk = X509_get_pubkey(err_cert);
+		if (pk) {
+			rsa = EVP_PKEY_get1_RSA(pk);
+			if (rsa) {
+				len = RSA_bits(rsa);
+				RSA_free(rsa);
+			}
+			EVP_PKEY_free(pk);
+		}
+
+		if (len >= 0) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: RSA modulus size: %d bits", len);
+			if (len < 3072) {
+				preverify_ok = 0;
+				openssl_tls_fail_event(
+					conn, err_cert, err,
+					depth, buf,
+					"Insufficient RSA modulus size",
+					TLS_FAIL_INSUFFICIENT_KEY_LEN);
+			}
+		}
+	}
+#endif /* CONFIG_SUITEB */
+
 #ifdef OPENSSL_IS_BORINGSSL
 	if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) &&
 	    preverify_ok) {
@@ -2257,8 +2401,42 @@
 }
 
 
-static void tls_set_conn_flags(SSL *ssl, unsigned int flags)
+#ifdef CONFIG_SUITEB
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+static int suiteb_cert_cb(SSL *ssl, void *arg)
 {
+	struct tls_connection *conn = arg;
+
+	/*
+	 * This cert_cb() is not really the best location for doing a
+	 * constraint check for the ServerKeyExchange message, but this seems to
+	 * be the only place where the current OpenSSL sequence can be
+	 * terminated cleanly with an TLS alert going out to the server.
+	 */
+
+	if (!(conn->flags & TLS_CONN_SUITEB))
+		return 1;
+
+	/* DHE is enabled only with DHE-RSA-AES256-GCM-SHA384 */
+	if (conn->cipher_suite != 0x9f)
+		return 1;
+
+	if (conn->server_dh_prime_len >= 3072)
+		return 1;
+
+	wpa_printf(MSG_DEBUG,
+		   "OpenSSL: Server DH prime length (%d bits) not sufficient for Suite B RSA - reject handshake",
+		   conn->server_dh_prime_len);
+	return 0;
+}
+#endif /* OPENSSL_VERSION_NUMBER */
+#endif /* CONFIG_SUITEB */
+
+
+static int tls_set_conn_flags(struct tls_connection *conn, unsigned int flags)
+{
+	SSL *ssl = conn->ssl;
+
 #ifdef SSL_OP_NO_TICKET
 	if (flags & TLS_CONN_DISABLE_SESSION_TICKET)
 		SSL_set_options(ssl, SSL_OP_NO_TICKET);
@@ -2284,6 +2462,64 @@
 	else
 		SSL_clear_options(ssl, SSL_OP_NO_TLSv1_2);
 #endif /* SSL_OP_NO_TLSv1_2 */
+#ifdef CONFIG_SUITEB
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+	if (flags & TLS_CONN_SUITEB_NO_ECDH) {
+		const char *ciphers = "DHE-RSA-AES256-GCM-SHA384";
+
+		if (SSL_set_cipher_list(ssl, ciphers) != 1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set Suite B ciphers");
+			return -1;
+		}
+	} else if (flags & TLS_CONN_SUITEB) {
+		EC_KEY *ecdh;
+		const char *ciphers =
+			"ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384";
+
+		if (SSL_set_cipher_list(ssl, ciphers) != 1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set Suite B ciphers");
+			return -1;
+		}
+
+		if (SSL_set1_curves_list(ssl, "P-384") != 1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set Suite B curves");
+			return -1;
+		}
+
+		ecdh = EC_KEY_new_by_curve_name(NID_secp384r1);
+		if (!ecdh || SSL_set_tmp_ecdh(ssl, ecdh) != 1) {
+			EC_KEY_free(ecdh);
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set ECDH parameter");
+			return -1;
+		}
+		EC_KEY_free(ecdh);
+	}
+	if (flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) {
+		/* ECDSA+SHA384 if need to add EC support here */
+		if (SSL_set1_sigalgs_list(ssl, "RSA+SHA384") != 1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set Suite B sigalgs");
+			return -1;
+		}
+
+		SSL_set_options(ssl, SSL_OP_NO_TLSv1);
+		SSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
+		SSL_set_cert_cb(ssl, suiteb_cert_cb, conn);
+	}
+#else /* OPENSSL_VERSION_NUMBER < 0x10002000L */
+	if (flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: Suite B RSA case not supported with this OpenSSL version");
+		return -1;
+	}
+#endif /* OPENSSL_VERSION_NUMBER */
+#endif /* CONFIG_SUITEB */
+
+	return 0;
 }
 
 
@@ -2307,7 +2543,8 @@
 		SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
 	}
 
-	tls_set_conn_flags(conn->ssl, flags);
+	if (tls_set_conn_flags(conn, flags) < 0)
+		return -1;
 	conn->flags = flags;
 
 	SSL_set_accept_state(conn->ssl);
@@ -2369,9 +2606,9 @@
 		BIO *bio = BIO_from_keystore(&client_cert[11]);
 		X509 *x509 = NULL;
 		int ret = -1;
-		if (bio)
+		if (bio) {
 			x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
-
+		}
 		if (x509) {
 			if (SSL_use_certificate(conn->ssl, x509) == 1)
 				ret = 0;
@@ -2775,6 +3012,19 @@
 }
 
 
+static void tls_clear_default_passwd_cb(SSL_CTX *ssl_ctx, SSL *ssl)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL)
+	if (ssl) {
+		SSL_set_default_passwd_cb(ssl, NULL);
+		SSL_set_default_passwd_cb_userdata(ssl, NULL);
+	}
+#endif /* >= 1.1.0f && !LibreSSL && !BoringSSL */
+	SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
+	SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, NULL);
+}
+
+
 static int tls_connection_private_key(struct tls_data *data,
 				      struct tls_connection *conn,
 				      const char *private_key,
@@ -2796,6 +3046,15 @@
 	} else
 		passwd = NULL;
 
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL)
+	/*
+	 * In OpenSSL >= 1.1.0f SSL_use_PrivateKey_file() uses the callback
+	 * from the SSL object. See OpenSSL commit d61461a75253.
+	 */
+	SSL_set_default_passwd_cb(conn->ssl, tls_passwd_cb);
+	SSL_set_default_passwd_cb_userdata(conn->ssl, passwd);
+#endif /* >= 1.1.0f && !LibreSSL && !BoringSSL */
+	/* Keep these for OpenSSL < 1.1.0f */
 	SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb);
 	SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd);
 
@@ -2882,11 +3141,12 @@
 	if (!ok) {
 		tls_show_errors(MSG_INFO, __func__,
 				"Failed to load private key");
+		tls_clear_default_passwd_cb(ssl_ctx, conn->ssl);
 		os_free(passwd);
 		return -1;
 	}
 	ERR_clear_error();
-	SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
+	tls_clear_default_passwd_cb(ssl_ctx, conn->ssl);
 	os_free(passwd);
 
 	if (!SSL_check_private_key(conn->ssl)) {
@@ -2929,13 +3189,14 @@
 	    tls_read_pkcs12(data, NULL, private_key, passwd)) {
 		tls_show_errors(MSG_INFO, __func__,
 				"Failed to load private key");
+		tls_clear_default_passwd_cb(ssl_ctx, NULL);
 		os_free(passwd);
 		ERR_clear_error();
 		return -1;
 	}
+	tls_clear_default_passwd_cb(ssl_ctx, NULL);
 	os_free(passwd);
 	ERR_clear_error();
-	SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
 
 	if (!SSL_CTX_check_private_key(ssl_ctx)) {
 		tls_show_errors(MSG_INFO, __func__,
@@ -3306,6 +3567,41 @@
 		}
 	}
 
+#ifdef CONFIG_SUITEB
+	if ((conn->flags & TLS_CONN_SUITEB) && !server &&
+	    os_strncmp(SSL_get_cipher(conn->ssl), "DHE-", 4) == 0 &&
+	    conn->server_dh_prime_len < 3072) {
+		struct tls_context *context = conn->context;
+
+		/*
+		 * This should not be reached since earlier cert_cb should have
+		 * terminated the handshake. Keep this check here for extra
+		 * protection if anything goes wrong with the more low-level
+		 * checks based on having to parse the TLS handshake messages.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Server DH prime length: %d bits",
+			   conn->server_dh_prime_len);
+
+		if (context->event_cb) {
+			union tls_event_data ev;
+
+			os_memset(&ev, 0, sizeof(ev));
+			ev.alert.is_local = 1;
+			ev.alert.type = "fatal";
+			ev.alert.description = "insufficient security";
+			context->event_cb(context->cb_ctx, TLS_ALERT, &ev);
+		}
+		/*
+		 * Could send a TLS Alert to the server, but for now, simply
+		 * terminate handshake.
+		 */
+		conn->failed++;
+		conn->write_alerts++;
+		return NULL;
+	}
+#endif /* CONFIG_SUITEB */
+
 	/* Get the TLS handshake data to be sent to the server */
 	res = BIO_ctrl_pending(conn->ssl_out);
 	wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res);
@@ -3764,7 +4060,7 @@
 {
 	struct tls_connection *conn = arg;
 	const unsigned char *p;
-	int len, status, reason;
+	int len, status, reason, res;
 	OCSP_RESPONSE *rsp;
 	OCSP_BASICRESP *basic;
 	OCSP_CERTID *id;
@@ -3859,16 +4155,33 @@
 		return 0;
 	}
 
-	id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer);
+	id = OCSP_cert_to_id(EVP_sha256(), conn->peer_cert, conn->peer_issuer);
 	if (!id) {
-		wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier");
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Could not create OCSP certificate identifier (SHA256)");
 		OCSP_BASICRESP_free(basic);
 		OCSP_RESPONSE_free(rsp);
 		return 0;
 	}
 
-	if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at,
-				   &this_update, &next_update)) {
+	res = OCSP_resp_find_status(basic, id, &status, &reason, &produced_at,
+				    &this_update, &next_update);
+	if (!res) {
+		id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer);
+		if (!id) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: Could not create OCSP certificate identifier (SHA1)");
+			OCSP_BASICRESP_free(basic);
+			OCSP_RESPONSE_free(rsp);
+			return 0;
+		}
+
+		res = OCSP_resp_find_status(basic, id, &status, &reason,
+					    &produced_at, &this_update,
+					    &next_update);
+	}
+
+	if (!res) {
 		wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s",
 			   (conn->flags & TLS_CONN_REQUIRE_OCSP) ? "" :
 			   " (OCSP not required)");
@@ -4070,7 +4383,8 @@
 		return -1;
 	}
 
-	tls_set_conn_flags(conn->ssl, params->flags);
+	if (tls_set_conn_flags(conn, params->flags) < 0)
+		return -1;
 
 #ifdef OPENSSL_IS_BORINGSSL
 	if (params->flags & TLS_CONN_REQUEST_OCSP) {
@@ -4227,11 +4541,10 @@
 	wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket "
 		    "extension", data, len);
 
-	conn->session_ticket = os_malloc(len);
+	conn->session_ticket = os_memdup(data, len);
 	if (conn->session_ticket == NULL)
 		return 0;
 
-	os_memcpy(conn->session_ticket, data, len);
 	conn->session_ticket_len = len;
 
 	return 1;
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 7b3a6bd..a6307e3 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -21,6 +21,7 @@
 
 #include "common/defs.h"
 #include "common/ieee802_11_defs.h"
+#include "common/wpa_common.h"
 #ifdef CONFIG_MACSEC
 #include "pae/ieee802_1x_kay.h"
 #endif /* CONFIG_MACSEC */
@@ -64,6 +65,10 @@
 /* Filter unicast IP packets encrypted using the GTK */
 #define WPA_DATA_FRAME_FILTER_FLAG_GTK BIT(2)
 
+#define HOSTAPD_DFS_REGION_FCC	1
+#define HOSTAPD_DFS_REGION_ETSI	2
+#define HOSTAPD_DFS_REGION_JP	3
+
 /**
  * enum reg_change_initiator - Regulatory change initiator
  */
@@ -483,6 +488,15 @@
 	 unsigned int sched_scan_plans_num;
 
 	/**
+	 * sched_scan_start_delay - Delay to use before starting the first scan
+	 *
+	 * Delay (in seconds) before scheduling first scan plan cycle. The
+	 * driver may ignore this parameter and start immediately (or at any
+	 * other time), if this feature is not supported.
+	 */
+	 u32 sched_scan_start_delay;
+
+	/**
 	 * bssid - Specific BSSID to scan for
 	 *
 	 * This optional parameter can be used to replace the default wildcard
@@ -676,6 +690,68 @@
 };
 
 /**
+ * struct wpa_driver_sta_auth_params - Authentication parameters
+ * Data for struct wpa_driver_ops::sta_auth().
+ */
+struct wpa_driver_sta_auth_params {
+
+	/**
+	 * own_addr - Source address and BSSID for authentication frame
+	 */
+	const u8 *own_addr;
+
+	/**
+	 * addr - MAC address of the station to associate
+	 */
+	const u8 *addr;
+
+	/**
+	 * seq - authentication sequence number
+	 */
+	u16 seq;
+
+	/**
+	 * status - authentication response status code
+	 */
+	u16 status;
+
+	/**
+	 * ie - authentication frame ie buffer
+	 */
+	const u8 *ie;
+
+	/**
+	 * len - ie buffer length
+	 */
+	size_t len;
+
+	/**
+	 * fils_auth - Indicates whether FILS authentication is being performed
+	 */
+	int fils_auth;
+
+	/**
+	 * fils_anonce - ANonce (required for FILS)
+	 */
+	u8 fils_anonce[WPA_NONCE_LEN];
+
+	/**
+	 * fils_snonce - SNonce (required for FILS)
+	*/
+	u8 fils_snonce[WPA_NONCE_LEN];
+
+	/**
+	 * fils_kek - key for encryption (required for FILS)
+	 */
+	u8 fils_kek[WPA_KEK_MAX_LEN];
+
+	/**
+	 * fils_kek_len - Length of the fils_kek in octets (required for FILS)
+	 */
+	size_t fils_kek_len;
+};
+
+/**
  * struct wpa_driver_associate_params - Association parameters
  * Data for struct wpa_driver_ops::associate().
  */
@@ -738,7 +814,7 @@
 	 * WPA information element to be included in (Re)Association
 	 * Request (including information element id and length). Use
 	 * of this WPA IE is optional. If the driver generates the WPA
-	 * IE, it can use pairwise_suite, group_suite, and
+	 * IE, it can use pairwise_suite, group_suite, group_mgmt_suite, and
 	 * key_mgmt_suite to select proper algorithms. In this case,
 	 * the driver has to notify wpa_supplicant about the used WPA
 	 * IE by generating an event that the interface code will
@@ -778,6 +854,13 @@
 	unsigned int group_suite;
 
 	/**
+	 * mgmt_group_suite - Selected group management cipher suite (WPA_CIPHER_*)
+	 *
+	 * This is usually ignored if @wpa_ie is used.
+	 */
+	unsigned int mgmt_group_suite;
+
+	/**
 	 * key_mgmt_suite - Selected key management suite (WPA_KEY_MGMT_*)
 	 *
 	 * This is usually ignored if @wpa_ie is used.
@@ -816,43 +899,6 @@
 	enum mfp_options mgmt_frame_protection;
 
 	/**
-	 * ft_ies - IEEE 802.11r / FT information elements
-	 * If the supplicant is using IEEE 802.11r (FT) and has the needed keys
-	 * for fast transition, this parameter is set to include the IEs that
-	 * are to be sent in the next FT Authentication Request message.
-	 * update_ft_ies() handler is called to update the IEs for further
-	 * FT messages in the sequence.
-	 *
-	 * The driver should use these IEs only if the target AP is advertising
-	 * the same mobility domain as the one included in the MDIE here.
-	 *
-	 * In ap_scan=2 mode, the driver can use these IEs when moving to a new
-	 * AP after the initial association. These IEs can only be used if the
-	 * target AP is advertising support for FT and is using the same MDIE
-	 * and SSID as the current AP.
-	 *
-	 * The driver is responsible for reporting the FT IEs received from the
-	 * AP's response using wpa_supplicant_event() with EVENT_FT_RESPONSE
-	 * type. update_ft_ies() handler will then be called with the FT IEs to
-	 * include in the next frame in the authentication sequence.
-	 */
-	const u8 *ft_ies;
-
-	/**
-	 * ft_ies_len - Length of ft_ies in bytes
-	 */
-	size_t ft_ies_len;
-
-	/**
-	 * ft_md - FT Mobility domain (6 octets) (also included inside ft_ies)
-	 *
-	 * This value is provided to allow the driver interface easier access
-	 * to the current mobility domain. This value is set to %NULL if no
-	 * mobility domain is currently active.
-	 */
-	const u8 *ft_md;
-
-	/**
 	 * passphrase - RSN passphrase for PSK
 	 *
 	 * This value is made available only for WPA/WPA2-Personal (PSK) and
@@ -1002,6 +1048,43 @@
 	 * fils_nonces_len: Length of fils_nonce in bytes
 	 */
 	size_t fils_nonces_len;
+
+	/**
+	 * fils_erp_username - Username part of keyName-NAI
+	 */
+	const u8 *fils_erp_username;
+
+	/**
+	 * fils_erp_username_len - Length of fils_erp_username in bytes
+	 */
+	size_t fils_erp_username_len;
+
+	/**
+	 * fils_erp_realm - Realm/domain name to use in FILS ERP
+	 */
+	const u8 *fils_erp_realm;
+
+	/**
+	 * fils_erp_realm_len - Length of fils_erp_realm in bytes
+	 */
+	size_t fils_erp_realm_len;
+
+	/**
+	 * fils_erp_next_seq_num - The next sequence number to use in FILS ERP
+	 * messages
+	 */
+	u16 fils_erp_next_seq_num;
+
+	/**
+	 * fils_erp_rrk - Re-authentication root key (rRK) for the keyName-NAI
+	 * specified by fils_erp_username@fils_erp_realm.
+	 */
+	const u8 *fils_erp_rrk;
+
+	/**
+	 * fils_erp_rrk_len - Length of fils_erp_rrk in bytes
+	 */
+	size_t fils_erp_rrk_len;
 };
 
 enum hide_ssid {
@@ -1279,6 +1362,7 @@
 #define WPA_DRIVER_MESH_CONF_FLAG_PEER_LINK_TIMEOUT	0x00000002
 #define WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS	0x00000004
 #define WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE		0x00000008
+#define WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD	0x00000010
 	/*
 	 * TODO: Other mesh configuration parameters would go here.
 	 * See NL80211_MESHCONF_* for all the mesh config parameters.
@@ -1287,6 +1371,7 @@
 	int auto_plinks;
 	int peer_link_timeout;
 	int max_peer_links;
+	int rssi_threshold;
 	u16 ht_opmode;
 };
 
@@ -1321,6 +1406,12 @@
 #define WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK	0x00000080
 #define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B	0x00000100
 #define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192	0x00000200
+#define WPA_DRIVER_CAPA_KEY_MGMT_OWE		0x00000400
+#define WPA_DRIVER_CAPA_KEY_MGMT_DPP		0x00000800
+#define WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256    0x00001000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384    0x00002000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 0x00004000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 0x00008000
 	/** Bitfield of supported key management suites */
 	unsigned int key_mgmt;
 
@@ -1459,6 +1550,19 @@
 #define WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI	0x0001000000000000ULL
 /** Driver supports HE capabilities */
 #define WPA_DRIVER_FLAGS_HE_CAPABILITIES	0x0002000000000000ULL
+/** Driver supports FILS shared key offload */
+#define WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD	0x0004000000000000ULL
+/** Driver supports all OCE STA specific mandatory features */
+#define WPA_DRIVER_FLAGS_OCE_STA		0x0008000000000000ULL
+/** Driver supports all OCE AP specific mandatory features */
+#define WPA_DRIVER_FLAGS_OCE_AP			0x0010000000000000ULL
+/**
+ * Driver supports all OCE STA-CFON specific mandatory features only.
+ * If a driver sets this bit but not the %WPA_DRIVER_FLAGS_OCE_AP, the
+ * userspace shall assume that this driver may not support all OCE AP
+ * functionality but can support only OCE STA-CFON functionality.
+ */
+#define WPA_DRIVER_FLAGS_OCE_STA_CFON		0x0020000000000000ULL
 	u64 flags;
 
 #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -1580,18 +1684,35 @@
 
 struct hostapd_data;
 
+#define STA_DRV_DATA_TX_MCS BIT(0)
+#define STA_DRV_DATA_RX_MCS BIT(1)
+#define STA_DRV_DATA_TX_VHT_MCS BIT(2)
+#define STA_DRV_DATA_RX_VHT_MCS BIT(3)
+#define STA_DRV_DATA_TX_VHT_NSS BIT(4)
+#define STA_DRV_DATA_RX_VHT_NSS BIT(5)
+#define STA_DRV_DATA_TX_SHORT_GI BIT(6)
+#define STA_DRV_DATA_RX_SHORT_GI BIT(7)
+
 struct hostap_sta_driver_data {
 	unsigned long rx_packets, tx_packets;
 	unsigned long long rx_bytes, tx_bytes;
 	int bytes_64bit; /* whether 64-bit byte counters are supported */
 	unsigned long current_tx_rate;
+	unsigned long current_rx_rate;
 	unsigned long inactive_msec;
-	unsigned long flags;
+	unsigned long flags; /* bitfield of STA_DRV_DATA_* */
 	unsigned long num_ps_buf_frames;
 	unsigned long tx_retry_failed;
 	unsigned long tx_retry_count;
 	int last_rssi;
 	int last_ack_rssi;
+	s8 signal;
+	u8 rx_vhtmcs;
+	u8 tx_vhtmcs;
+	u8 rx_mcs;
+	u8 tx_mcs;
+	u8 rx_vht_nss;
+	u8 tx_vht_nss;
 };
 
 struct hostapd_sta_add_params {
@@ -1898,6 +2019,37 @@
 	const int *freq_list;
 };
 
+struct wpa_bss_trans_info {
+	u8 mbo_transition_reason;
+	u8 n_candidates;
+	u8 *bssid;
+};
+
+struct wpa_bss_candidate_info {
+	u8 num;
+	struct candidate_list {
+		u8 bssid[ETH_ALEN];
+		u8 is_accept;
+		u32 reject_reason;
+	} *candidates;
+};
+
+struct wpa_pmkid_params {
+	const u8 *bssid;
+	const u8 *ssid;
+	size_t ssid_len;
+	const u8 *fils_cache_id;
+	const u8 *pmkid;
+	const u8 *pmk;
+	size_t pmk_len;
+};
+
+/* Mask used to specify which connection parameters have to be updated */
+enum wpa_drv_update_connect_params_mask {
+	WPA_DRV_UPDATE_ASSOC_IES	= BIT(0),
+	WPA_DRV_UPDATE_FILS_ERP_INFO	= BIT(1),
+	WPA_DRV_UPDATE_AUTH_TYPE	= BIT(2),
+};
 
 /**
  * struct wpa_driver_ops - Driver interface API definition
@@ -2080,13 +2232,14 @@
 	/**
 	 * add_pmkid - Add PMKSA cache entry to the driver
 	 * @priv: private driver interface data
-	 * @bssid: BSSID for the PMKSA cache entry
-	 * @pmkid: PMKID for the PMKSA cache entry
+	 * @params: PMKSA parameters
 	 *
 	 * Returns: 0 on success, -1 on failure
 	 *
 	 * This function is called when a new PMK is received, as a result of
-	 * either normal authentication or RSN pre-authentication.
+	 * either normal authentication or RSN pre-authentication. The PMKSA
+	 * parameters are either a set of bssid, pmkid, and pmk; or a set of
+	 * ssid, fils_cache_id, pmkid, and pmk.
 	 *
 	 * If the driver generates RSN IE, i.e., it does not use wpa_ie in
 	 * associate(), add_pmkid() can be used to add new PMKSA cache entries
@@ -2094,18 +2247,18 @@
 	 * driver_ops function does not need to be implemented. Likewise, if
 	 * the driver does not support WPA, this function is not needed.
 	 */
-	int (*add_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid);
+	int (*add_pmkid)(void *priv, struct wpa_pmkid_params *params);
 
 	/**
 	 * remove_pmkid - Remove PMKSA cache entry to the driver
 	 * @priv: private driver interface data
-	 * @bssid: BSSID for the PMKSA cache entry
-	 * @pmkid: PMKID for the PMKSA cache entry
+	 * @params: PMKSA parameters
 	 *
 	 * Returns: 0 on success, -1 on failure
 	 *
 	 * This function is called when the supplicant drops a PMKSA cache
-	 * entry for any reason.
+	 * entry for any reason. The PMKSA parameters are either a set of
+	 * bssid and pmkid; or a set of ssid, fils_cache_id, and pmkid.
 	 *
 	 * If the driver generates RSN IE, i.e., it does not use wpa_ie in
 	 * associate(), remove_pmkid() can be used to synchronize PMKSA caches
@@ -2114,7 +2267,7 @@
 	 * implemented. Likewise, if the driver does not support WPA, this
 	 * function is not needed.
 	 */
-	int (*remove_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid);
+	int (*remove_pmkid)(void *priv, struct wpa_pmkid_params *params);
 
 	/**
 	 * flush_pmkid - Flush PMKSA cache
@@ -2229,12 +2382,13 @@
 	 * @priv: Private driver interface data
 	 * @num_modes: Variable for returning the number of returned modes
 	 * flags: Variable for returning hardware feature flags
+	 * @dfs: Variable for returning DFS region (HOSTAPD_DFS_REGION_*)
 	 * Returns: Pointer to allocated hardware data on success or %NULL on
 	 * failure. Caller is responsible for freeing this.
 	 */
 	struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv,
 							 u16 *num_modes,
-							 u16 *flags);
+							 u16 *flags, u8 *dfs);
 
 	/**
 	 * send_mlme - Send management frame from MLME
@@ -3239,19 +3393,13 @@
 
 	/**
 	 * sta_auth - Station authentication indication
-	 * @priv: Private driver interface data
-	 * @own_addr: Source address and BSSID for authentication frame
-	 * @addr: MAC address of the station to associate
-	 * @seq: authentication sequence number
-	 * @status: authentication response status code
-	 * @ie: authentication frame ie buffer
-	 * @len: ie buffer length
+	 * @priv: private driver interface data
+	 * @params: Station authentication parameters
 	 *
-	 * This function indicates the driver to send Authentication frame
-	 * to the station.
+	 * Returns: 0 on success, -1 on failure
 	 */
-	 int (*sta_auth)(void *priv, const u8 *own_addr, const u8 *addr,
-			 u16 seq, u16 status, const u8 *ie, size_t len);
+	 int (*sta_auth)(void *priv,
+			 struct wpa_driver_sta_auth_params *params);
 
 	/**
 	 * add_tspec - Add traffic stream
@@ -3808,8 +3956,52 @@
 	 * trigger control mode to the host driver.
 	 */
 	int (*set_tdls_mode)(void *priv, int tdls_external_control);
-};
 
+	/**
+	 * get_bss_transition_status - Get candidate BSS's transition status
+	 * @priv: Private driver interface data
+	 * @params: Candidate BSS list
+	 *
+	 * Get the accept or reject reason code for a list of BSS transition
+	 * candidates.
+	 */
+	struct wpa_bss_candidate_info *
+	(*get_bss_transition_status)(void *priv,
+				     struct wpa_bss_trans_info *params);
+	/**
+	 * ignore_assoc_disallow - Configure driver to ignore assoc_disallow
+	 * @priv: Private driver interface data
+	 * @ignore_disallow: 0 to not ignore, 1 to ignore
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*ignore_assoc_disallow)(void *priv, int ignore_disallow);
+
+	/**
+	 * set_bssid_blacklist - Set blacklist of BSSIDs to the driver
+	 * @priv: Private driver interface data
+	 * @num_bssid: Number of blacklist BSSIDs
+	 * @bssids: List of blacklisted BSSIDs
+	 */
+	int (*set_bssid_blacklist)(void *priv, unsigned int num_bssid,
+				   const u8 *bssid);
+
+	/**
+	 * update_connect_params - Update the connection parameters
+	 * @priv: Private driver interface data
+	 * @params: Association parameters
+	 * @mask: Bit mask indicating which parameters in @params have to be
+	 *	updated
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * Update the connection parameters when in connected state so that the
+	 * driver uses the updated parameters for subsequent roaming. This is
+	 * used only with drivers that implement internal BSS selection and
+	 * roaming.
+	 */
+	int (*update_connect_params)(
+		void *priv, struct wpa_driver_associate_params *params,
+		enum wpa_drv_update_connect_params_mask mask);
+};
 
 /**
  * enum wpa_event_type - Event type for wpa_supplicant_event() calls
@@ -3919,17 +4111,6 @@
 	EVENT_PMKID_CANDIDATE,
 
 	/**
-	 * EVENT_STKSTART - Request STK handshake (MLME-STKSTART.request)
-	 *
-	 * This event can be used to inform wpa_supplicant about desire to set
-	 * up secure direct link connection between two stations as defined in
-	 * IEEE 802.11e with a new PeerKey mechanism that replaced the original
-	 * STAKey negotiation. The caller will need to set peer address for the
-	 * event.
-	 */
-	EVENT_STKSTART,
-
-	/**
 	 * EVENT_TDLS - Request TDLS operation
 	 *
 	 * This event can be used to request a TDLS operation to be performed.
@@ -4306,6 +4487,15 @@
 	 * range.
 	 */
 	EVENT_BEACON_LOSS,
+
+	/**
+	 * EVENT_DFS_PRE_CAC_EXPIRED - Notify that channel availability check
+	 * done previously (Pre-CAC) on the channel has expired. This would
+	 * normally be on a non-ETSI DFS regulatory domain. DFS state of the
+	 * channel will be moved from available to usable. A new CAC has to be
+	 * performed before start operating on this channel.
+	 */
+	EVENT_DFS_PRE_CAC_EXPIRED,
 };
 
 
@@ -4484,6 +4674,8 @@
 
 		/**
 		 * ptk_kek - The derived PTK KEK
+		 * This is used in key management offload and also in FILS SK
+		 * offload.
 		 */
 		const u8 *ptk_kek;
 
@@ -4497,6 +4689,36 @@
 		 * 0 = unknown, 1 = unchanged, 2 = changed
 		 */
 		u8 subnet_status;
+
+		/**
+		 * The following information is used in FILS SK offload
+		 * @fils_erp_next_seq_num
+		 * @fils_pmk
+		 * @fils_pmk_len
+		 * @fils_pmkid
+		 */
+
+		/**
+		 * fils_erp_next_seq_num - The next sequence number to use in
+		 * FILS ERP messages
+		 */
+		u16 fils_erp_next_seq_num;
+
+		/**
+		 * fils_pmk - A new PMK if generated in case of FILS
+		 * authentication
+		 */
+		const u8 *fils_pmk;
+
+		/**
+		 * fils_pmk_len - Length of fils_pmk
+		 */
+		size_t fils_pmk_len;
+
+		/**
+		 * fils_pmkid - PMKID used or generated in FILS authentication
+		 */
+		const u8 *fils_pmkid;
 	} assoc_info;
 
 	/**
@@ -4593,13 +4815,6 @@
 	} pmkid_candidate;
 
 	/**
-	 * struct stkstart - Data for EVENT_STKSTART
-	 */
-	struct stkstart {
-		u8 peer[ETH_ALEN];
-	} stkstart;
-
-	/**
 	 * struct tdls - Data for EVENT_TDLS
 	 */
 	struct tdls {
@@ -4712,6 +4927,12 @@
 		 * timeout_reason - Reason for the timeout
 		 */
 		const char *timeout_reason;
+
+		/**
+		 * fils_erp_next_seq_num - The next sequence number to use in
+		 * FILS ERP messages
+		 */
+		u16 fils_erp_next_seq_num;
 	} assoc_reject;
 
 	struct timeout_event {
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index a88345f..d2b355c 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -36,6 +36,10 @@
 
 #include "ieee80211_external.h"
 
+/* Avoid conflicting definition from the driver header files with
+ * common/wpa_common.h */
+#undef WPA_OUI_TYPE
+
 
 #ifdef CONFIG_WPS
 #include <netpacket/packet.h>
@@ -55,7 +59,7 @@
 #include "netlink.h"
 #include "linux_ioctl.h"
 
-#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20) || defined(CONFIG_WNM) || defined(CONFIG_WPS)
+#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20) || defined(CONFIG_WNM) || defined(CONFIG_WPS) || defined(CONFIG_FILS)
 #define ATHEROS_USE_RAW_RECEIVE
 #endif
 
@@ -70,6 +74,7 @@
 	int	ioctl_sock;			/* socket for ioctl() use */
 	struct netlink_data *netlink;
 	int	we_version;
+	int fils_en;			/* FILS enable/disable in driver */
 	u8	acct_mac[ETH_ALEN];
 	struct hostap_sta_driver_data acct_data;
 
@@ -178,6 +183,25 @@
 }
 
 
+#ifdef CONFIG_FILS
+static int
+get80211param(struct atheros_driver_data *drv, int op, int *data)
+{
+	struct iwreq iwr;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+	iwr.u.mode = op;
+
+	if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_GETPARAM, &iwr) < 0)
+		return -1;
+
+	*data = iwr.u.mode;
+	return 0;
+}
+#endif /* CONFIG_FILS */
+
+
 static int
 set80211priv(struct atheros_driver_data *drv, int op, void *data, int len)
 {
@@ -692,10 +716,14 @@
 	wpa_hexdump(MSG_DEBUG, "atheros: set_generic_elem", ie, ie_len);
 
 	wpabuf_free(drv->wpa_ie);
-	drv->wpa_ie = wpabuf_alloc_copy(ie, ie_len);
+	if (ie)
+		drv->wpa_ie = wpabuf_alloc_copy(ie, ie_len);
+	else
+		drv->wpa_ie = NULL;
 
 	app_ie = (struct ieee80211req_getset_appiebuf *) buf;
-	os_memcpy(&(app_ie->app_buf[0]), ie, ie_len);
+	if (ie)
+		os_memcpy(&(app_ie->app_buf[0]), ie, ie_len);
 	app_ie->app_buflen = ie_len;
 
 	app_ie->app_frmtype = IEEE80211_APPIE_FRAME_BEACON;
@@ -931,11 +959,11 @@
 #ifdef CONFIG_WPS
 	filt.app_filterype |= IEEE80211_FILTER_TYPE_PROBE_REQ;
 #endif /* CONFIG_WPS */
-#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R)
+#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS)
 	filt.app_filterype |= (IEEE80211_FILTER_TYPE_ASSOC_REQ |
 			       IEEE80211_FILTER_TYPE_AUTH |
 			       IEEE80211_FILTER_TYPE_ACTION);
-#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W || CONFIG_FILS */
 #ifdef CONFIG_WNM
 	filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION;
 #endif /* CONFIG_WNM */
@@ -949,12 +977,12 @@
 			return ret;
 	}
 
-#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R)
+#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS)
 	drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW,
 				       atheros_raw_receive, drv, 1);
 	if (drv->sock_raw == NULL)
 		return -1;
-#endif /* CONFIG_WPS || CONFIG_IEEE80211R */
+#endif /* CONFIG_WPS || CONFIG_IEEE80211R || CONFIG_FILS */
 	return ret;
 }
 
@@ -981,7 +1009,8 @@
 	beac_ie = (struct ieee80211req_getset_appiebuf *) buf;
 	beac_ie->app_frmtype = frametype;
 	beac_ie->app_buflen = len;
-	os_memcpy(&(beac_ie->app_buf[0]), ie, len);
+	if (ie)
+		os_memcpy(&(beac_ie->app_buf[0]), ie, len);
 
 	/* append the WPA/RSN IE if it is set already */
 	if (((frametype == IEEE80211_APPIE_FRAME_BEACON) ||
@@ -1034,32 +1063,56 @@
 #define atheros_set_ap_wps_ie NULL
 #endif /* CONFIG_WPS */
 
-#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS)
 static int
-atheros_sta_auth(void *priv, const u8 *own_addr, const u8 *addr, u16 seq,
-		 u16 status_code, const u8 *ie, size_t len)
+atheros_sta_auth(void *priv, struct wpa_driver_sta_auth_params *params)
 {
 	struct atheros_driver_data *drv = priv;
 	struct ieee80211req_mlme mlme;
 	int ret;
 
 	wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d",
-		   __func__, ether_sprintf(addr), status_code);
+		   __func__, ether_sprintf(params->addr), params->status);
 
+#ifdef CONFIG_FILS
+	/* Copy FILS AAD parameters if the driver supports FILS */
+	if (params->fils_auth && drv->fils_en) {
+		wpa_printf(MSG_DEBUG, "%s: im_op IEEE80211_MLME_AUTH_FILS",
+			   __func__);
+		os_memcpy(mlme.fils_aad.ANonce, params->fils_anonce,
+			  IEEE80211_FILS_NONCE_LEN);
+		os_memcpy(mlme.fils_aad.SNonce, params->fils_snonce,
+			  IEEE80211_FILS_NONCE_LEN);
+		os_memcpy(mlme.fils_aad.kek, params->fils_kek,
+			  IEEE80211_MAX_WPA_KEK_LEN);
+		mlme.fils_aad.kek_len = params->fils_kek_len;
+		mlme.im_op = IEEE80211_MLME_AUTH_FILS;
+		wpa_hexdump(MSG_DEBUG, "FILS: ANonce",
+			    mlme.fils_aad.ANonce, FILS_NONCE_LEN);
+		wpa_hexdump(MSG_DEBUG, "FILS: SNonce",
+			    mlme.fils_aad.SNonce, FILS_NONCE_LEN);
+		wpa_hexdump_key(MSG_DEBUG, "FILS: KEK",
+				mlme.fils_aad.kek, mlme.fils_aad.kek_len);
+	} else {
+		mlme.im_op = IEEE80211_MLME_AUTH;
+	}
+#else /* CONFIG_FILS */
 	mlme.im_op = IEEE80211_MLME_AUTH;
-	mlme.im_reason = status_code;
-	mlme.im_seq = seq;
-	os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
-	mlme.im_optie_len = len;
-	if (len) {
-		if (len < IEEE80211_MAX_OPT_IE) {
-			os_memcpy(mlme.im_optie, ie, len);
+#endif /* CONFIG_FILS */
+
+	mlme.im_reason = params->status;
+	mlme.im_seq = params->seq;
+	os_memcpy(mlme.im_macaddr, params->addr, IEEE80211_ADDR_LEN);
+	mlme.im_optie_len = params->len;
+	if (params->len) {
+		if (params->len < IEEE80211_MAX_OPT_IE) {
+			os_memcpy(mlme.im_optie, params->ie, params->len);
 		} else {
 			wpa_printf(MSG_DEBUG, "%s: Not enough space to copy "
 				   "opt_ie STA (addr " MACSTR " reason %d, "
 				   "ie_len %d)",
-				   __func__, MAC2STR(addr), status_code,
-				   (int) len);
+				   __func__, MAC2STR(params->addr),
+				   params->status, (int) params->len);
 			return -1;
 		}
 	}
@@ -1067,7 +1120,7 @@
 	if (ret < 0) {
 		wpa_printf(MSG_DEBUG, "%s: Failed to auth STA (addr " MACSTR
 			   " reason %d)",
-			   __func__, MAC2STR(addr), status_code);
+			   __func__, MAC2STR(params->addr), params->status);
 	}
 	return ret;
 }
@@ -1110,7 +1163,7 @@
 	}
 	return ret;
 }
-#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W || CONFIG_FILS */
 
 static void
 atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
@@ -1257,7 +1310,7 @@
 		atheros_raw_receive(drv, NULL,
 				    (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
 #endif /* CONFIG_WPS */
-#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS)
 	} else if (os_strncmp(custom, "Manage.assoc_req ", 17) == 0) {
 		/* Format: "Manage.assoc_req <frame len>" | zero padding |
 		 * frame */
@@ -1281,7 +1334,7 @@
 		}
 		atheros_raw_receive(drv, NULL,
 				    (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
-#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R || CONFIG_FILS */
 #ifdef ATHEROS_USE_RAW_RECEIVE
 		} else if (os_strncmp(custom, "Manage.action ", 14) == 0) {
 		/* Format: "Manage.assoc_req <frame len>" | zero padding | frame
@@ -1603,6 +1656,27 @@
 			   len - sizeof(struct l2_ethhdr));
 }
 
+
+static void atheros_read_fils_cap(struct atheros_driver_data *drv)
+{
+	int fils = 0;
+
+#ifdef CONFIG_FILS
+	/* TODO: Would be better to have #ifdef on the IEEE80211_PARAM_* value
+	 * to automatically check this against the driver header files. */
+	if (get80211param(drv, IEEE80211_PARAM_ENABLE_FILS, &fils) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: Failed to get FILS capability from driver",
+			   __func__);
+		/* Assume driver does not support FILS */
+		fils = 0;
+	}
+#endif /* CONFIG_FILS */
+	drv->fils_en = fils;
+	wpa_printf(MSG_DEBUG, "atheros: fils_en=%d", drv->fils_en);
+}
+
+
 static void *
 atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params)
 {
@@ -1683,6 +1757,9 @@
 	if (atheros_wireless_event_init(drv))
 		goto bad;
 
+	/* Read FILS capability from the driver */
+	atheros_read_fils_cap(drv);
+
 	return drv;
 bad:
 	atheros_reset_appfilter(drv);
@@ -2158,7 +2235,7 @@
 	.set_ap_wps_ie		= atheros_set_ap_wps_ie,
 	.set_authmode		= atheros_set_authmode,
 	.set_ap			= atheros_set_ap,
-#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS)
 	.sta_assoc              = atheros_sta_assoc,
 	.sta_auth               = atheros_sta_auth,
 	.send_mlme       	= atheros_send_mgmt,
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index b6bcbca..0464304 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -35,7 +35,6 @@
 	E2S(ASSOCINFO);
 	E2S(INTERFACE_STATUS);
 	E2S(PMKID_CANDIDATE);
-	E2S(STKSTART);
 	E2S(TDLS);
 	E2S(FT_RESPONSE);
 	E2S(IBSS_RSN_START);
@@ -82,6 +81,7 @@
 	E2S(DFS_CAC_STARTED);
 	E2S(P2P_LO_STOP);
 	E2S(BEACON_LOSS);
+	E2S(DFS_PRE_CAC_EXPIRED);
 	}
 
 	return "UNKNOWN";
diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c
index 517a3bb..597da33 100644
--- a/src/drivers/driver_hostap.c
+++ b/src/drivers/driver_hostap.c
@@ -741,10 +741,9 @@
 	drv->generic_ie = NULL;
 	drv->generic_ie_len = 0;
 	if (elem) {
-		drv->generic_ie = os_malloc(elem_len);
+		drv->generic_ie = os_memdup(elem, elem_len);
 		if (drv->generic_ie == NULL)
 			return -1;
-		os_memcpy(drv->generic_ie, elem, elem_len);
 		drv->generic_ie_len = elem_len;
 	}
 
@@ -768,11 +767,10 @@
 	drv->wps_ie = NULL;
 	drv->wps_ie_len = 0;
 	if (proberesp) {
-		drv->wps_ie = os_malloc(wpabuf_len(proberesp));
+		drv->wps_ie = os_memdup(wpabuf_head(proberesp),
+					wpabuf_len(proberesp));
 		if (drv->wps_ie == NULL)
 			return -1;
-		os_memcpy(drv->wps_ie, wpabuf_head(proberesp),
-			  wpabuf_len(proberesp));
 		drv->wps_ie_len = wpabuf_len(proberesp);
 	}
 
@@ -1090,7 +1088,7 @@
 
 static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv,
 							    u16 *num_modes,
-							    u16 *flags)
+							    u16 *flags, u8 *dfs)
 {
 	struct hostapd_hw_modes *mode;
 	int i, clen, rlen;
@@ -1105,6 +1103,7 @@
 
 	*num_modes = 1;
 	*flags = 0;
+	*dfs = 0;
 
 	mode->mode = HOSTAPD_MODE_IEEE80211B;
 	mode->num_channels = 14;
diff --git a/src/drivers/driver_macsec_linux.c b/src/drivers/driver_macsec_linux.c
index 5dab77a..e89b3ba 100644
--- a/src/drivers/driver_macsec_linux.c
+++ b/src/drivers/driver_macsec_linux.c
@@ -168,6 +168,9 @@
 {
 	int err;
 
+	if (!drv->sk)
+		return 0;
+
 	if (!drv->link)
 		return 0;
 
@@ -231,10 +234,44 @@
 }
 
 
+static int macsec_check_macsec(void)
+{
+	struct nl_sock *sk;
+	int err = -1;
+
+	sk = nl_socket_alloc();
+	if (!sk) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc genl socket");
+		return -1;
+	}
+
+	if (genl_connect(sk) < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "connection to genl socket failed");
+		goto out_free;
+	}
+
+	if (genl_ctrl_resolve(sk, "macsec") < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "genl resolve failed - macsec kernel module not present?");
+		goto out_free;
+	}
+
+	err = 0;
+
+out_free:
+	nl_socket_free(sk);
+	return err;
+}
+
+
 static void * macsec_drv_wpa_init(void *ctx, const char *ifname)
 {
 	struct macsec_drv_data *drv;
 
+	if (macsec_check_macsec() < 0)
+		return NULL;
+
 	drv = os_zalloc(sizeof(*drv));
 	if (!drv)
 		return NULL;
@@ -982,6 +1019,11 @@
 
 	wpa_printf(MSG_DEBUG, "%s", __func__);
 
+	if (!drv->sk) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "NULL rtnl socket");
+		return -1;
+	}
+
 	link = rtnl_link_macsec_alloc();
 	if (!link) {
 		wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't allocate link");
@@ -1048,6 +1090,9 @@
 
 	wpa_printf(MSG_DEBUG, "%s", __func__);
 
+	if (!drv->sk)
+		return 0;
+
 	if (!drv->created_link) {
 		rtnl_link_put(drv->link);
 		drv->link = NULL;
diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c
index 9440f01..614c452 100644
--- a/src/drivers/driver_ndis.c
+++ b/src/drivers/driver_ndis.c
@@ -1220,12 +1220,16 @@
 }
 
 
-static int wpa_driver_ndis_add_pmkid(void *priv, const u8 *bssid,
-				     const u8 *pmkid)
+static int wpa_driver_ndis_add_pmkid(void *priv,
+				     struct wpa_pmkid_params *params)
 {
 	struct wpa_driver_ndis_data *drv = priv;
 	struct ndis_pmkid_entry *entry, *prev;
+	const u8 *bssid = params->bssid;
+	const u8 *pmkid = params->pmkid;
 
+	if (!bssid || !pmkid)
+		return -1;
 	if (drv->no_of_pmkid == 0)
 		return 0;
 
@@ -1261,12 +1265,16 @@
 }
 
 
-static int wpa_driver_ndis_remove_pmkid(void *priv, const u8 *bssid,
-		 			const u8 *pmkid)
+static int wpa_driver_ndis_remove_pmkid(void *priv,
+					struct wpa_pmkid_params *params)
 {
 	struct wpa_driver_ndis_data *drv = priv;
 	struct ndis_pmkid_entry *entry, *prev;
+	const u8 *bssid = params->bssid;
+	const u8 *pmkid = params->pmkid;
 
+	if (!bssid || !pmkid)
+		return -1;
 	if (drv->no_of_pmkid == 0)
 		return 0;
 
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index e9107b3..2ce03ed 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -911,7 +911,8 @@
 		nl80211_check_global(drv->global);
 		wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed "
 			   "interface");
-		wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL);
+		if (wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL) < 0)
+			return -1;
 		return 1;
 	}
 
@@ -920,19 +921,55 @@
 
 
 static struct wpa_driver_nl80211_data *
-nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len)
+nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len,
+		 int *init_failed)
 {
 	struct wpa_driver_nl80211_data *drv;
+	int res;
+
+	if (init_failed)
+		*init_failed = 0;
 	dl_list_for_each(drv, &global->interfaces,
 			 struct wpa_driver_nl80211_data, list) {
-		if (wpa_driver_nl80211_own_ifindex(drv, idx, buf, len) ||
-		    have_ifidx(drv, idx, IFIDX_ANY))
+		res = wpa_driver_nl80211_own_ifindex(drv, idx, buf, len);
+		if (res < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Found matching own interface, but failed to complete reinitialization");
+			if (init_failed)
+				*init_failed = 1;
+			return drv;
+		}
+		if (res > 0 || have_ifidx(drv, idx, IFIDX_ANY))
 			return drv;
 	}
 	return NULL;
 }
 
 
+static void nl80211_refresh_mac(struct wpa_driver_nl80211_data *drv,
+				int ifindex)
+{
+	struct i802_bss *bss;
+	u8 addr[ETH_ALEN];
+
+	bss = get_bss_ifindex(drv, ifindex);
+	if (bss &&
+	    linux_get_ifhwaddr(drv->global->ioctl_sock,
+			       bss->ifname, addr) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: %s: failed to re-read MAC address",
+			   bss->ifname);
+	} else if (bss && os_memcmp(addr, bss->addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Own MAC address on ifindex %d (%s) changed from "
+			   MACSTR " to " MACSTR,
+			   ifindex, bss->ifname,
+			   MAC2STR(bss->addr), MAC2STR(addr));
+		os_memcpy(bss->addr, addr, ETH_ALEN);
+	}
+}
+
+
 static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
 						 struct ifinfomsg *ifi,
 						 u8 *buf, size_t len)
@@ -945,6 +982,7 @@
 	char namebuf[IFNAMSIZ];
 	char ifname[IFNAMSIZ + 1];
 	char extra[100], *pos, *end;
+	int init_failed;
 
 	extra[0] = '\0';
 	pos = extra;
@@ -989,14 +1027,18 @@
 		   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
 		   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
 
-	drv = nl80211_find_drv(global, ifi->ifi_index, buf, len);
+	drv = nl80211_find_drv(global, ifi->ifi_index, buf, len, &init_failed);
 	if (!drv)
 		goto event_newlink;
+	if (init_failed)
+		return; /* do not update interface state */
 
 	if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
 		namebuf[0] = '\0';
 		if (if_indextoname(ifi->ifi_index, namebuf) &&
 		    linux_iface_up(drv->global->ioctl_sock, namebuf) > 0) {
+			/* Re-read MAC address as it may have changed */
+			nl80211_refresh_mac(drv, ifi->ifi_index);
 			wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
 				   "event since interface %s is up", namebuf);
 			drv->ignore_if_down_event = 0;
@@ -1023,7 +1065,7 @@
 			 * dynamic interfaces
 			 */
 			drv = nl80211_find_drv(global, ifi->ifi_index,
-					       buf, len);
+					       buf, len, NULL);
 			if (!drv)
 				return;
 		}
@@ -1044,27 +1086,8 @@
 				   "event since interface %s is marked "
 				   "removed", drv->first_bss->ifname);
 		} else {
-			struct i802_bss *bss;
-			u8 addr[ETH_ALEN];
-
 			/* Re-read MAC address as it may have changed */
-			bss = get_bss_ifindex(drv, ifi->ifi_index);
-			if (bss &&
-			    linux_get_ifhwaddr(drv->global->ioctl_sock,
-					       bss->ifname, addr) < 0) {
-				wpa_printf(MSG_DEBUG,
-					   "nl80211: %s: failed to re-read MAC address",
-					   bss->ifname);
-			} else if (bss &&
-				   os_memcmp(addr, bss->addr, ETH_ALEN) != 0) {
-				wpa_printf(MSG_DEBUG,
-					   "nl80211: Own MAC address on ifindex %d (%s) changed from "
-					   MACSTR " to " MACSTR,
-					   ifi->ifi_index, bss->ifname,
-					   MAC2STR(bss->addr),
-					   MAC2STR(addr));
-				os_memcpy(bss->addr, addr, ETH_ALEN);
-			}
+			nl80211_refresh_mac(drv, ifi->ifi_index);
 
 			wpa_printf(MSG_DEBUG, "nl80211: Interface up");
 			drv->if_disabled = 0;
@@ -1168,7 +1191,7 @@
 		   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
 		   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
 
-	drv = nl80211_find_drv(global, ifi->ifi_index, buf, len);
+	drv = nl80211_find_drv(global, ifi->ifi_index, buf, len, NULL);
 
 	if (ifi->ifi_family == AF_BRIDGE && brid && drv) {
 		/* device has been removed from bridge */
@@ -2009,7 +2032,7 @@
 	if (nl80211_register_action_frame(bss, (u8 *) "\x01\x04", 2) < 0)
 		ret = -1;
 #endif /* CONFIG_INTERWORKING */
-#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING)
+#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP)
 	/* GAS Initial Request */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0a", 2) < 0)
 		ret = -1;
@@ -2034,7 +2057,7 @@
 	/* Protected GAS Comeback Response */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0d", 2) < 0)
 		ret = -1;
-#endif /* CONFIG_P2P || CONFIG_INTERWORKING */
+#endif /* CONFIG_P2P || CONFIG_INTERWORKING || CONFIG_DPP */
 #ifdef CONFIG_P2P
 	/* P2P Public Action */
 	if (nl80211_register_action_frame(bss,
@@ -2047,6 +2070,13 @@
 					  5) < 0)
 		ret = -1;
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_DPP
+	/* DPP Public Action */
+	if (nl80211_register_action_frame(bss,
+					  (u8 *) "\x04\x09\x50\x6f\x9a\x1a",
+					  6) < 0)
+		ret = -1;
+#endif /* CONFIG_DPP */
 #ifdef CONFIG_IEEE80211W
 	/* SA Query Response */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0)
@@ -3125,6 +3155,27 @@
 }
 
 
+static enum nl80211_auth_type get_nl_auth_type(int wpa_auth_alg)
+{
+	if (wpa_auth_alg & WPA_AUTH_ALG_OPEN)
+		return NL80211_AUTHTYPE_OPEN_SYSTEM;
+	if (wpa_auth_alg & WPA_AUTH_ALG_SHARED)
+		return NL80211_AUTHTYPE_SHARED_KEY;
+	if (wpa_auth_alg & WPA_AUTH_ALG_LEAP)
+		return NL80211_AUTHTYPE_NETWORK_EAP;
+	if (wpa_auth_alg & WPA_AUTH_ALG_FT)
+		return NL80211_AUTHTYPE_FT;
+	if (wpa_auth_alg & WPA_AUTH_ALG_SAE)
+		return NL80211_AUTHTYPE_SAE;
+	if (wpa_auth_alg & WPA_AUTH_ALG_FILS)
+		return NL80211_AUTHTYPE_FILS_SK;
+	if (wpa_auth_alg & WPA_AUTH_ALG_FILS_SK_PFS)
+		return NL80211_AUTHTYPE_FILS_SK_PFS;
+
+	return NL80211_AUTHTYPE_MAX;
+}
+
+
 static int wpa_driver_nl80211_authenticate(
 	struct i802_bss *bss, struct wpa_driver_auth_params *params)
 {
@@ -3207,22 +3258,10 @@
 			    params->auth_data))
 			goto fail;
 	}
-	if (params->auth_alg & WPA_AUTH_ALG_OPEN)
-		type = NL80211_AUTHTYPE_OPEN_SYSTEM;
-	else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
-		type = NL80211_AUTHTYPE_SHARED_KEY;
-	else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
-		type = NL80211_AUTHTYPE_NETWORK_EAP;
-	else if (params->auth_alg & WPA_AUTH_ALG_FT)
-		type = NL80211_AUTHTYPE_FT;
-	else if (params->auth_alg & WPA_AUTH_ALG_SAE)
-		type = NL80211_AUTHTYPE_SAE;
-	else if (params->auth_alg & WPA_AUTH_ALG_FILS)
-		type = NL80211_AUTHTYPE_FILS_SK;
-	else
-		goto fail;
+	type = get_nl_auth_type(params->auth_alg);
 	wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
-	if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
+	if (type == NL80211_AUTHTYPE_MAX ||
+	    nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
 		goto fail;
 	if (params->local_state_change) {
 		wpa_printf(MSG_DEBUG, "  * Local state change only");
@@ -5065,6 +5104,47 @@
 }
 
 
+static int nl80211_put_fils_connect_params(struct wpa_driver_nl80211_data *drv,
+					   struct wpa_driver_associate_params *params,
+					   struct nl_msg *msg)
+{
+	if (params->fils_erp_username_len) {
+		wpa_hexdump_ascii(MSG_DEBUG, "  * FILS ERP EMSKname/username",
+				  params->fils_erp_username,
+				  params->fils_erp_username_len);
+		if (nla_put(msg, NL80211_ATTR_FILS_ERP_USERNAME,
+			    params->fils_erp_username_len,
+			    params->fils_erp_username))
+			return -1;
+	}
+
+	if (params->fils_erp_realm_len) {
+		wpa_hexdump_ascii(MSG_DEBUG, "  * FILS ERP Realm",
+				  params->fils_erp_realm,
+				  params->fils_erp_realm_len);
+		if (nla_put(msg, NL80211_ATTR_FILS_ERP_REALM,
+			    params->fils_erp_realm_len, params->fils_erp_realm))
+			return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "  * FILS ERP next seq %u",
+		   params->fils_erp_next_seq_num);
+	if (nla_put_u16(msg, NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM,
+			params->fils_erp_next_seq_num))
+		return -1;
+
+	if (params->fils_erp_rrk_len) {
+		wpa_printf(MSG_DEBUG, "  * FILS ERP rRK (len=%lu)",
+			   (unsigned long) params->fils_erp_rrk_len);
+		if (nla_put(msg, NL80211_ATTR_FILS_ERP_RRK,
+			    params->fils_erp_rrk_len, params->fils_erp_rrk))
+			return -1;
+	}
+
+	return 0;
+}
+
+
 static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
 				  struct wpa_driver_associate_params *params,
 				  struct nl_msg *msg)
@@ -5172,7 +5252,11 @@
 	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
-	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA256 ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA384 ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA256 ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA384) {
 		int mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
 
 		switch (params->key_mgmt_suite) {
@@ -5203,6 +5287,18 @@
 		case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
 			mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
 			break;
+		case WPA_KEY_MGMT_FILS_SHA256:
+			mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA256;
+			break;
+		case WPA_KEY_MGMT_FILS_SHA384:
+			mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA384;
+			break;
+		case WPA_KEY_MGMT_FT_FILS_SHA256:
+			mgmt = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256;
+			break;
+		case WPA_KEY_MGMT_FT_FILS_SHA384:
+			mgmt = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
+			break;
 		case WPA_KEY_MGMT_PSK:
 		default:
 			mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
@@ -5260,6 +5356,10 @@
 		drv->connect_reassoc = 1;
 	}
 
+	if ((params->auth_alg & WPA_AUTH_ALG_FILS) &&
+	    nl80211_put_fils_connect_params(drv, params, msg) != 0)
+		return -1;
+
 	return 0;
 }
 
@@ -5301,25 +5401,18 @@
 		algs++;
 	if (params->auth_alg & WPA_AUTH_ALG_LEAP)
 		algs++;
+	if (params->auth_alg & WPA_AUTH_ALG_FILS)
+		algs++;
 	if (algs > 1) {
 		wpa_printf(MSG_DEBUG, "  * Leave out Auth Type for automatic "
 			   "selection");
 		goto skip_auth_type;
 	}
 
-	if (params->auth_alg & WPA_AUTH_ALG_OPEN)
-		type = NL80211_AUTHTYPE_OPEN_SYSTEM;
-	else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
-		type = NL80211_AUTHTYPE_SHARED_KEY;
-	else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
-		type = NL80211_AUTHTYPE_NETWORK_EAP;
-	else if (params->auth_alg & WPA_AUTH_ALG_FT)
-		type = NL80211_AUTHTYPE_FT;
-	else
-		goto fail;
-
+	type = get_nl_auth_type(params->auth_alg);
 	wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
-	if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
+	if (type == NL80211_AUTHTYPE_MAX ||
+	    nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
 		goto fail;
 
 skip_auth_type:
@@ -5844,6 +5937,16 @@
 		[NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
 		[NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 },
 		[NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 },
+		[NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
+	};
+	struct nlattr *rate[NL80211_RATE_INFO_MAX + 1];
+	static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
+		[NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
+		[NL80211_RATE_INFO_BITRATE32] = { .type = NLA_U32 },
+		[NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
+		[NL80211_RATE_INFO_VHT_MCS] = { .type = NLA_U8 },
+		[NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
+		[NL80211_RATE_INFO_VHT_NSS] = { .type = NLA_U8 },
 	};
 
 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
@@ -5895,6 +5998,67 @@
 	if (stats[NL80211_STA_INFO_TX_FAILED])
 		data->tx_retry_failed =
 			nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]);
+	if (stats[NL80211_STA_INFO_SIGNAL])
+		data->signal = nla_get_u8(stats[NL80211_STA_INFO_SIGNAL]);
+
+	if (stats[NL80211_STA_INFO_TX_BITRATE] &&
+	    nla_parse_nested(rate, NL80211_RATE_INFO_MAX,
+			     stats[NL80211_STA_INFO_TX_BITRATE],
+			     rate_policy) == 0) {
+		if (rate[NL80211_RATE_INFO_BITRATE32])
+			data->current_tx_rate =
+				nla_get_u32(rate[NL80211_RATE_INFO_BITRATE32]);
+		else if (rate[NL80211_RATE_INFO_BITRATE])
+			data->current_tx_rate =
+				nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]);
+
+		if (rate[NL80211_RATE_INFO_MCS]) {
+			data->tx_mcs = nla_get_u8(rate[NL80211_RATE_INFO_MCS]);
+			data->flags |= STA_DRV_DATA_TX_MCS;
+		}
+		if (rate[NL80211_RATE_INFO_VHT_MCS]) {
+			data->tx_vhtmcs =
+				nla_get_u8(rate[NL80211_RATE_INFO_VHT_MCS]);
+			data->flags |= STA_DRV_DATA_TX_VHT_MCS;
+		}
+		if (rate[NL80211_RATE_INFO_SHORT_GI])
+			data->flags |= STA_DRV_DATA_TX_SHORT_GI;
+		if (rate[NL80211_RATE_INFO_VHT_NSS]) {
+			data->tx_vht_nss =
+				nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]);
+			data->flags |= STA_DRV_DATA_TX_VHT_NSS;
+		}
+	}
+
+	if (stats[NL80211_STA_INFO_RX_BITRATE] &&
+	    nla_parse_nested(rate, NL80211_RATE_INFO_MAX,
+			     stats[NL80211_STA_INFO_RX_BITRATE],
+			     rate_policy) == 0) {
+		if (rate[NL80211_RATE_INFO_BITRATE32])
+			data->current_rx_rate =
+				nla_get_u32(rate[NL80211_RATE_INFO_BITRATE32]);
+		else if (rate[NL80211_RATE_INFO_BITRATE])
+			data->current_rx_rate =
+				nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]);
+
+		if (rate[NL80211_RATE_INFO_MCS]) {
+			data->rx_mcs =
+				nla_get_u8(rate[NL80211_RATE_INFO_MCS]);
+			data->flags |= STA_DRV_DATA_RX_MCS;
+		}
+		if (rate[NL80211_RATE_INFO_VHT_MCS]) {
+			data->rx_vhtmcs =
+				nla_get_u8(rate[NL80211_RATE_INFO_VHT_MCS]);
+			data->flags |= STA_DRV_DATA_RX_VHT_MCS;
+		}
+		if (rate[NL80211_RATE_INFO_SHORT_GI])
+			data->flags |= STA_DRV_DATA_RX_SHORT_GI;
+		if (rate[NL80211_RATE_INFO_VHT_NSS]) {
+			data->rx_vht_nss =
+				nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]);
+			data->flags |= STA_DRV_DATA_RX_VHT_NSS;
+		}
+	}
 
 	return NL_SKIP;
 }
@@ -6319,7 +6483,7 @@
 			wpa_printf(MSG_ERROR, "nl80211: Failed to "
 				   "remove interface %s from bridge "
 				   "%s: %s",
-				   ifname, brname, strerror(errno));
+				   ifname, in_br, strerror(errno));
 			return -1;
 		}
 	}
@@ -7395,14 +7559,23 @@
 }
 
 
-static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid,
-			 const u8 *pmkid)
+static int nl80211_pmkid(struct i802_bss *bss, int cmd,
+			 struct wpa_pmkid_params *params)
 {
 	struct nl_msg *msg;
 
 	if (!(msg = nl80211_bss_msg(bss, 0, cmd)) ||
-	    (pmkid && nla_put(msg, NL80211_ATTR_PMKID, 16, pmkid)) ||
-	    (bssid && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid))) {
+	    (params->pmkid &&
+	     nla_put(msg, NL80211_ATTR_PMKID, 16, params->pmkid)) ||
+	    (params->bssid &&
+	     nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid)) ||
+	    (params->ssid_len &&
+	     nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) ||
+	    (params->fils_cache_id &&
+	     nla_put(msg, NL80211_ATTR_FILS_CACHE_ID, 2,
+		     params->fils_cache_id)) ||
+	    (params->pmk_len &&
+	     nla_put(msg, NL80211_ATTR_PMK, params->pmk_len, params->pmk))) {
 		nlmsg_free(msg);
 		return -ENOBUFS;
 	}
@@ -7411,28 +7584,52 @@
 }
 
 
-static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
+static int nl80211_add_pmkid(void *priv, struct wpa_pmkid_params *params)
 {
 	struct i802_bss *bss = priv;
-	wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR, MAC2STR(bssid));
-	return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, bssid, pmkid);
+
+	if (params->bssid)
+		wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR,
+			   MAC2STR(params->bssid));
+	else if (params->fils_cache_id && params->ssid_len) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Add PMKSA for cache id %02x%02x SSID %s",
+			   params->fils_cache_id[0], params->fils_cache_id[1],
+			   wpa_ssid_txt(params->ssid, params->ssid_len));
+	}
+
+	return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params);
 }
 
 
-static int nl80211_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
+static int nl80211_remove_pmkid(void *priv, struct wpa_pmkid_params *params)
 {
 	struct i802_bss *bss = priv;
-	wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR,
-		   MAC2STR(bssid));
-	return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, bssid, pmkid);
+
+	if (params->bssid)
+		wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR,
+			   MAC2STR(params->bssid));
+	else if (params->fils_cache_id && params->ssid_len) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Delete PMKSA for cache id %02x%02x SSID %s",
+			   params->fils_cache_id[0], params->fils_cache_id[1],
+			   wpa_ssid_txt(params->ssid, params->ssid_len));
+	}
+
+	return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, params);
 }
 
 
 static int nl80211_flush_pmkid(void *priv)
 {
 	struct i802_bss *bss = priv;
+	struct nl_msg *msg;
+
 	wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs");
-	return nl80211_pmkid(bss, NL80211_CMD_FLUSH_PMKSA, NULL, NULL);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_FLUSH_PMKSA);
+	if (!msg)
+		return -ENOBUFS;
+	return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
 }
 
 
@@ -7627,7 +7824,7 @@
 	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_REKEY_OFFLOAD)) ||
 	    !(replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA)) ||
 	    nla_put(msg, NL80211_REKEY_DATA_KEK, kek_len, kek) ||
-	    nla_put(msg, NL80211_REKEY_DATA_KCK, kck_len, kck) ||
+	    (kck_len && nla_put(msg, NL80211_REKEY_DATA_KCK, kck_len, kck)) ||
 	    nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN,
 		    replay_ctr)) {
 		nl80211_nlmsg_clear(msg);
@@ -8686,6 +8883,67 @@
 
 	return send_and_recv_msgs(drv, msg, NULL, NULL);
 }
+
+
+/* Reserved QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID value for wpa_supplicant */
+#define WPA_SUPPLICANT_CLIENT_ID 1
+
+static int nl80211_set_bssid_blacklist(void *priv, unsigned int num_bssid,
+				       const u8 *bssid)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *params, *nlbssids, *attr;
+	unsigned int i;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Set blacklist BSSID (num=%u)",
+		   num_bssid);
+
+	if (!drv->roam_vendor_cmd_avail)
+		return -1;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_ROAM) ||
+	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD,
+			QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BLACKLIST_BSSID) ||
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID,
+			WPA_SUPPLICANT_CLIENT_ID) ||
+	    nla_put_u32(msg,
+			QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID,
+			num_bssid))
+		goto fail;
+
+	nlbssids = nla_nest_start(
+		msg, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS);
+	if (!nlbssids)
+		goto fail;
+
+	for (i = 0; i < num_bssid; i++) {
+		attr = nla_nest_start(msg, i);
+		if (!attr)
+			goto fail;
+		if (nla_put(msg,
+			    QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID,
+			    ETH_ALEN, &bssid[i * ETH_ALEN]))
+			goto fail;
+		wpa_printf(MSG_DEBUG, "nl80211:   BSSID[%u]: " MACSTR, i,
+			   MAC2STR(&bssid[i * ETH_ALEN]));
+		nla_nest_end(msg, attr);
+	}
+	nla_nest_end(msg, nlbssids);
+	nla_nest_end(msg, params);
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL);
+
+fail:
+	nlmsg_free(msg);
+	return -1;
+}
+
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 
 
@@ -8772,7 +9030,10 @@
 			 params->auto_plinks)) ||
 	    ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS) &&
 	     nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
-			 params->max_peer_links)))
+			 params->max_peer_links)) ||
+	    ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD) &&
+	     nla_put_u32(msg, NL80211_MESHCONF_RSSI_THRESHOLD,
+			 params->rssi_threshold)))
 		return -1;
 
 	/*
@@ -9655,6 +9916,254 @@
 	return -1;
 }
 
+
+#ifdef CONFIG_MBO
+
+static enum mbo_transition_reject_reason
+nl80211_mbo_reject_reason_mapping(enum qca_wlan_btm_candidate_status status)
+{
+	switch (status) {
+	case QCA_STATUS_REJECT_EXCESSIVE_FRAME_LOSS_EXPECTED:
+		return MBO_TRANSITION_REJECT_REASON_FRAME_LOSS;
+	case QCA_STATUS_REJECT_EXCESSIVE_DELAY_EXPECTED:
+		return MBO_TRANSITION_REJECT_REASON_DELAY;
+	case QCA_STATUS_REJECT_INSUFFICIENT_QOS_CAPACITY:
+		return MBO_TRANSITION_REJECT_REASON_QOS_CAPACITY;
+	case QCA_STATUS_REJECT_LOW_RSSI:
+		return MBO_TRANSITION_REJECT_REASON_RSSI;
+	case QCA_STATUS_REJECT_HIGH_INTERFERENCE:
+		return MBO_TRANSITION_REJECT_REASON_INTERFERENCE;
+	case QCA_STATUS_REJECT_UNKNOWN:
+	default:
+		return MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
+	}
+}
+
+
+static void nl80211_parse_btm_candidate_info(struct candidate_list *candidate,
+					     struct nlattr *tb[], int num)
+{
+	enum qca_wlan_btm_candidate_status status;
+	char buf[50];
+
+	os_memcpy(candidate->bssid,
+		  nla_data(tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID]),
+		  ETH_ALEN);
+
+	status = nla_get_u32(
+		tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS]);
+	candidate->is_accept = status == QCA_STATUS_ACCEPT;
+	candidate->reject_reason = nl80211_mbo_reject_reason_mapping(status);
+
+	if (candidate->is_accept)
+		os_snprintf(buf, sizeof(buf), "Accepted");
+	else
+		os_snprintf(buf, sizeof(buf),
+			    "Rejected, Reject_reason: %d",
+			    candidate->reject_reason);
+	wpa_printf(MSG_DEBUG, "nl80211:   BSSID[%d]: " MACSTR " %s",
+		   num, MAC2STR(candidate->bssid), buf);
+}
+
+
+static int
+nl80211_get_bss_transition_status_handler(struct nl_msg *msg, void *arg)
+{
+	struct wpa_bss_candidate_info *info = arg;
+	struct candidate_list *candidate = info->candidates;
+	struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+	struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1];
+	static struct nla_policy policy[
+		QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1] = {
+		[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID] = {
+			.minlen = ETH_ALEN
+		},
+		[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS] = {
+			.type = NLA_U32,
+		},
+	};
+	struct nlattr *attr;
+	int rem;
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	u8 num;
+
+	num = info->num; /* number of candidates sent to driver */
+	info->num = 0;
+	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (!tb_msg[NL80211_ATTR_VENDOR_DATA] ||
+	    nla_parse_nested(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
+			     tb_msg[NL80211_ATTR_VENDOR_DATA], NULL) ||
+	    !tb_vendor[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO])
+		return NL_SKIP;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: WNM Candidate list received from driver");
+	nla_for_each_nested(attr,
+			    tb_vendor[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO],
+			    rem) {
+		if (info->num >= num ||
+		    nla_parse_nested(
+			    tb, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX,
+			    attr, policy) ||
+		    !tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID] ||
+		    !tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS])
+			break;
+
+		nl80211_parse_btm_candidate_info(candidate, tb, info->num);
+
+		candidate++;
+		info->num++;
+	}
+
+	return NL_SKIP;
+}
+
+
+static struct wpa_bss_candidate_info *
+nl80211_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *attr, *attr1, *attr2;
+	struct wpa_bss_candidate_info *info;
+	u8 i;
+	int ret;
+	u8 *pos;
+
+	if (!drv->fetch_bss_trans_status)
+		return NULL;
+
+	info = os_zalloc(sizeof(*info));
+	if (!info)
+		return NULL;
+	/* Allocate memory for number of candidates sent to driver */
+	info->candidates = os_calloc(params->n_candidates,
+				     sizeof(*info->candidates));
+	if (!info->candidates) {
+		os_free(info);
+		return NULL;
+	}
+
+	/* Copy the number of candidates being sent to driver. This is used in
+	 * nl80211_get_bss_transition_status_handler() to limit the number of
+	 * candidates that can be populated in info->candidates and will be
+	 * later overwritten with the actual number of candidates received from
+	 * the driver.
+	 */
+	info->num = params->n_candidates;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS))
+		goto fail;
+
+	attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+	if (!attr)
+		goto fail;
+
+	if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON,
+		       params->mbo_transition_reason))
+		goto fail;
+
+	attr1 = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO);
+	if (!attr1)
+		goto fail;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: WNM Candidate list info sending to driver: mbo_transition_reason: %d n_candidates: %d",
+		   params->mbo_transition_reason, params->n_candidates);
+	pos = params->bssid;
+	for (i = 0; i < params->n_candidates; i++) {
+		wpa_printf(MSG_DEBUG, "nl80211:   BSSID[%d]: " MACSTR, i,
+			   MAC2STR(pos));
+		attr2 = nla_nest_start(msg, i);
+		if (!attr2 ||
+		    nla_put(msg, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID,
+			    ETH_ALEN, pos))
+			goto fail;
+		pos += ETH_ALEN;
+		nla_nest_end(msg, attr2);
+	}
+
+	nla_nest_end(msg, attr1);
+	nla_nest_end(msg, attr);
+
+	ret = send_and_recv_msgs(drv, msg,
+				 nl80211_get_bss_transition_status_handler,
+				 info);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: WNM Get BSS transition status failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+		goto fail;
+	}
+	return info;
+
+fail:
+	nlmsg_free(msg);
+	os_free(info->candidates);
+	os_free(info);
+	return NULL;
+}
+
+
+/**
+ * nl80211_ignore_assoc_disallow - Configure driver to ignore assoc_disallow
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @ignore_assoc_disallow: 0 to not ignore, 1 to ignore
+ * Returns: 0 on success, -1 on failure
+ */
+static int nl80211_ignore_assoc_disallow(void *priv, int ignore_disallow)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *attr;
+	int ret = -1;
+
+	if (!drv->set_wifi_conf_vendor_cmd_avail)
+		return -1;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION))
+		goto fail;
+
+	attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+	if (!attr)
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Set ignore_assoc_disallow %d",
+		   ignore_disallow);
+	if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED,
+		       ignore_disallow))
+		goto fail;
+
+	nla_nest_end(msg, attr);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Set ignore_assoc_disallow failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+		goto fail;
+	}
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+#endif /* CONFIG_MBO */
+
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 
 
@@ -9782,6 +10291,56 @@
 }
 
 
+static int nl80211_update_connection_params(
+	void *priv, struct wpa_driver_associate_params *params,
+	enum wpa_drv_update_connect_params_mask mask)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret = -1;
+	enum nl80211_auth_type type;
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_CONNECT_PARAMS);
+	if (!msg)
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Update connection params (ifindex=%d)",
+		   drv->ifindex);
+
+	if ((mask & WPA_DRV_UPDATE_ASSOC_IES) && params->wpa_ie) {
+		if (nla_put(msg, NL80211_ATTR_IE, params->wpa_ie_len,
+			    params->wpa_ie))
+			goto fail;
+		wpa_hexdump(MSG_DEBUG, "  * IEs", params->wpa_ie,
+			    params->wpa_ie_len);
+	}
+
+	if (mask & WPA_DRV_UPDATE_AUTH_TYPE) {
+		type = get_nl_auth_type(params->auth_alg);
+		if (type == NL80211_AUTHTYPE_MAX ||
+		    nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
+			goto fail;
+		wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
+	}
+
+	if ((mask & WPA_DRV_UPDATE_FILS_ERP_INFO) &&
+	    nl80211_put_fils_connect_params(drv, params, msg))
+		goto fail;
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (ret)
+		wpa_dbg(drv->ctx, MSG_DEBUG,
+			"nl80211: Update connect params command failed: ret=%d (%s)",
+			ret, strerror(-ret));
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 	.name = "nl80211",
 	.desc = "Linux nl80211/cfg80211",
@@ -9899,7 +10458,13 @@
 	.p2p_lo_stop = nl80211_p2p_lo_stop,
 	.set_default_scan_ies = nl80211_set_default_scan_ies,
 	.set_tdls_mode = nl80211_set_tdls_mode,
+#ifdef CONFIG_MBO
+	.get_bss_transition_status = nl80211_get_bss_transition_status,
+	.ignore_assoc_disallow = nl80211_ignore_assoc_disallow,
+#endif /* CONFIG_MBO */
+	.set_bssid_blacklist = nl80211_set_bssid_blacklist,
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 	.configure_data_frame_filters = nl80211_configure_data_frame_filters,
 	.get_ext_capab = nl80211_get_ext_capab,
+	.update_connect_params = nl80211_update_connection_params,
 };
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index bdc79c5..23cf9db 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -162,6 +162,8 @@
 	unsigned int connect_reassoc:1;
 	unsigned int set_wifi_conf_vendor_cmd_avail:1;
 	unsigned int he_capab_vendor_cmd_avail:1;
+	unsigned int fetch_bss_trans_status:1;
+	unsigned int roam_vendor_cmd_avail:1;
 
 	u64 vendor_scan_cookie;
 	u64 remain_on_chan_cookie;
@@ -259,7 +261,8 @@
 
 int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv);
 struct hostapd_hw_modes *
-nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags);
+nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags,
+			    u8 *dfs_domain);
 
 int process_global_event(struct nl_msg *msg, void *arg);
 int process_bss_event(struct nl_msg *msg, void *arg);
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 7064ce1..f11a1d7 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -398,6 +398,9 @@
 	if (ext_feature_isset(ext_features, len,
 			      NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI))
 		capa->flags |= WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI;
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_FILS_SK_OFFLOAD))
+		capa->flags |= WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD;
 }
 
 
@@ -546,23 +549,22 @@
 			   nl80211_iftype_str(capa->iftype));
 
 		len = nla_len(tb1[NL80211_ATTR_EXT_CAPA]);
-		capa->ext_capa = os_malloc(len);
+		capa->ext_capa = os_memdup(nla_data(tb1[NL80211_ATTR_EXT_CAPA]),
+					   len);
 		if (!capa->ext_capa)
 			goto err;
 
-		os_memcpy(capa->ext_capa, nla_data(tb1[NL80211_ATTR_EXT_CAPA]),
-			  len);
 		capa->ext_capa_len = len;
 		wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities",
 			    capa->ext_capa, capa->ext_capa_len);
 
 		len = nla_len(tb1[NL80211_ATTR_EXT_CAPA_MASK]);
-		capa->ext_capa_mask = os_malloc(len);
+		capa->ext_capa_mask =
+			os_memdup(nla_data(tb1[NL80211_ATTR_EXT_CAPA_MASK]),
+				  len);
 		if (!capa->ext_capa_mask)
 			goto err;
 
-		os_memcpy(capa->ext_capa_mask,
-			  nla_data(tb1[NL80211_ATTR_EXT_CAPA_MASK]), len);
 		wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities mask",
 			    capa->ext_capa_mask, capa->ext_capa_len);
 
@@ -747,6 +749,12 @@
 				case QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES:
 					drv->he_capab_vendor_cmd_avail = 1;
 					break;
+				case QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS:
+					drv->fetch_bss_trans_status = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_ROAM:
+					drv->roam_vendor_cmd_avail = 1;
+					break;
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 				}
 			}
@@ -1106,6 +1114,12 @@
 		drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS;
 	if (check_feature(QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD, &info))
 		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD;
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_STA, &info))
+		drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_STA;
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_AP, &info))
+		drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_AP;
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON, &info))
+		drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_STA_CFON;
 	os_free(info.flags);
 }
 
@@ -1127,7 +1141,19 @@
 		WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
 		WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK |
 		WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B |
-		WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
+		WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192 |
+		WPA_DRIVER_CAPA_KEY_MGMT_OWE |
+		WPA_DRIVER_CAPA_KEY_MGMT_DPP;
+
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME)
+		drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
+			WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 |
+			WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 |
+			WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384;
+	else if (drv->capa.flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD)
+		drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
+			WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
+
 	drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
 		WPA_DRIVER_AUTH_SHARED |
 		WPA_DRIVER_AUTH_LEAP;
@@ -1203,6 +1229,7 @@
 	struct hostapd_hw_modes *modes;
 	int last_mode, last_chan_idx;
 	int failed;
+	u8 dfs_domain;
 };
 
 static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa,
@@ -1522,14 +1549,13 @@
 
 	mode11g = &modes[mode11g_idx];
 	mode->num_channels = mode11g->num_channels;
-	mode->channels = os_malloc(mode11g->num_channels *
+	mode->channels = os_memdup(mode11g->channels,
+				   mode11g->num_channels *
 				   sizeof(struct hostapd_channel_data));
 	if (mode->channels == NULL) {
 		(*num_modes)--;
 		return modes; /* Could not add 802.11b mode */
 	}
-	os_memcpy(mode->channels, mode11g->channels,
-		  mode11g->num_channels * sizeof(struct hostapd_channel_data));
 
 	mode->num_rates = 0;
 	mode->rates = os_malloc(4 * sizeof(int));
@@ -1732,6 +1758,20 @@
 }
 
 
+static void nl80211_set_dfs_domain(enum nl80211_dfs_regions region,
+				   u8 *dfs_domain)
+{
+	if (region == NL80211_DFS_FCC)
+		*dfs_domain = HOSTAPD_DFS_REGION_FCC;
+	else if (region == NL80211_DFS_ETSI)
+		*dfs_domain = HOSTAPD_DFS_REGION_ETSI;
+	else if (region == NL80211_DFS_JP)
+		*dfs_domain = HOSTAPD_DFS_REGION_JP;
+	else
+		*dfs_domain = 0;
+}
+
+
 static const char * dfs_domain_name(enum nl80211_dfs_regions region)
 {
 	switch (region) {
@@ -1778,6 +1818,7 @@
 	if (tb_msg[NL80211_ATTR_DFS_REGION]) {
 		enum nl80211_dfs_regions dfs_domain;
 		dfs_domain = nla_get_u8(tb_msg[NL80211_ATTR_DFS_REGION]);
+		nl80211_set_dfs_domain(dfs_domain, &results->dfs_domain);
 		wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s (%s)",
 			   (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]),
 			   dfs_domain_name(dfs_domain));
@@ -1854,7 +1895,8 @@
 
 
 struct hostapd_hw_modes *
-nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
+nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags,
+			    u8 *dfs_domain)
 {
 	u32 feat;
 	struct i802_bss *bss = priv;
@@ -1866,10 +1908,12 @@
 		.modes = NULL,
 		.last_mode = -1,
 		.failed = 0,
+		.dfs_domain = 0,
 	};
 
 	*num_modes = 0;
 	*flags = 0;
+	*dfs_domain = 0;
 
 	feat = get_nl80211_protocol_features(drv);
 	if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
@@ -1893,6 +1937,9 @@
 			*num_modes = 0;
 			return NULL;
 		}
+
+		*dfs_domain = result.dfs_domain;
+
 		return wpa_driver_nl80211_postprocess_modes(result.modes,
 							    num_modes);
 	}
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index ed2cbe4..e6bc254 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -284,7 +284,10 @@
 			       struct nlattr *key_replay_ctr,
 			       struct nlattr *ptk_kck,
 			       struct nlattr *ptk_kek,
-			       struct nlattr *subnet_status)
+			       struct nlattr *subnet_status,
+			       struct nlattr *fils_erp_next_seq_num,
+			       struct nlattr *fils_pmk,
+			       struct nlattr *fils_pmkid)
 {
 	union wpa_event_data event;
 	const u8 *ssid = NULL;
@@ -357,6 +360,9 @@
 				break;
 			}
 		}
+		if (fils_erp_next_seq_num)
+			event.assoc_reject.fils_erp_next_seq_num =
+				nla_get_u16(fils_erp_next_seq_num);
 		wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
 		return;
 	}
@@ -430,6 +436,18 @@
 		event.assoc_info.subnet_status = nla_get_u8(subnet_status);
 	}
 
+	if (fils_erp_next_seq_num)
+		event.assoc_info.fils_erp_next_seq_num =
+			nla_get_u16(fils_erp_next_seq_num);
+
+	if (fils_pmk) {
+		event.assoc_info.fils_pmk = nla_data(fils_pmk);
+		event.assoc_info.fils_pmk_len = nla_len(fils_pmk);
+	}
+
+	if (fils_pmkid)
+		event.assoc_info.fils_pmkid = nla_data(fils_pmkid);
+
 	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
 }
 
@@ -1568,6 +1586,10 @@
 	case NL80211_RADAR_NOP_FINISHED:
 		wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data);
 		break;
+	case NL80211_RADAR_PRE_CAC_EXPIRED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_PRE_CAC_EXPIRED,
+				     &data);
+		break;
 	default:
 		wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d "
 			   "received", event_type);
@@ -1750,7 +1772,10 @@
 			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR],
 			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK],
 			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK],
-			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS]);
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID]);
 }
 
 
@@ -2266,7 +2291,12 @@
 				   tb[NL80211_ATTR_RESP_IE],
 				   tb[NL80211_ATTR_TIMED_OUT],
 				   tb[NL80211_ATTR_TIMEOUT_REASON],
-				   NULL, NULL, NULL, NULL, NULL);
+				   NULL, NULL, NULL,
+				   tb[NL80211_ATTR_FILS_KEK],
+				   NULL,
+				   tb[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM],
+				   tb[NL80211_ATTR_PMK],
+				   tb[NL80211_ATTR_PMKID]);
 		break;
 	case NL80211_CMD_CH_SWITCH_NOTIFY:
 		mlme_event_ch_switch(drv,
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index 4417721..984485b 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -10,6 +10,7 @@
  */
 
 #include "includes.h"
+#include <time.h>
 #include <netlink/genl/genl.h>
 
 #include "utils/common.h"
@@ -595,6 +596,11 @@
 		}
 	}
 
+	if (params->sched_scan_start_delay &&
+	    nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_DELAY,
+			params->sched_scan_start_delay))
+		goto fail;
+
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 
 	/* TODO: if we get an error here, we should fall back to normal scan */
@@ -691,6 +697,7 @@
 		[NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC },
 		[NL80211_BSS_PARENT_TSF] = { .type = NLA_U64 },
 		[NL80211_BSS_PARENT_BSSID] = { .type = NLA_UNSPEC },
+		[NL80211_BSS_LAST_SEEN_BOOTTIME] = { .type = NLA_U64 },
 	};
 	struct wpa_scan_res *r;
 	const u8 *ie, *beacon_ie;
@@ -754,6 +761,23 @@
 	}
 	if (bss[NL80211_BSS_SEEN_MS_AGO])
 		r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
+	if (bss[NL80211_BSS_LAST_SEEN_BOOTTIME]) {
+		u64 boottime;
+		struct timespec ts;
+
+#ifndef CLOCK_BOOTTIME
+#define CLOCK_BOOTTIME 7
+#endif
+		if (clock_gettime(CLOCK_BOOTTIME, &ts) == 0) {
+			/* Use more accurate boottime information to update the
+			 * scan result age since the driver reports this and
+			 * CLOCK_BOOTTIME is available. */
+			boottime = nla_get_u64(
+				bss[NL80211_BSS_LAST_SEEN_BOOTTIME]);
+			r->age = ((u64) ts.tv_sec * 1000000000 +
+				  ts.tv_nsec - boottime) / 1000000;
+		}
+	}
 	r->ie_len = ie_len;
 	pos = (u8 *) (r + 1);
 	if (ie) {
diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c
index 655128a..a3f0837 100644
--- a/src/drivers/driver_privsep.c
+++ b/src/drivers/driver_privsep.c
@@ -184,10 +184,9 @@
 		if (len < 0 || len > 10000 || len > end - pos)
 			break;
 
-		r = os_malloc(len);
+		r = os_memdup(pos, len);
 		if (r == NULL)
 			break;
-		os_memcpy(r, pos, len);
 		pos += len;
 		if (sizeof(*r) + r->ie_len + r->beacon_ie_len > (size_t) len) {
 			wpa_printf(MSG_ERROR,
@@ -484,19 +483,6 @@
 }
 
 
-static void wpa_driver_privsep_event_stkstart(void *ctx, u8 *buf, size_t len)
-{
-	union wpa_event_data data;
-
-	if (len != ETH_ALEN)
-		return;
-
-	os_memset(&data, 0, sizeof(data));
-	os_memcpy(data.stkstart.peer, buf, ETH_ALEN);
-	wpa_supplicant_event(ctx, EVENT_STKSTART, &data);
-}
-
-
 static void wpa_driver_privsep_event_ft_response(void *ctx, u8 *buf,
 						 size_t len)
 {
@@ -590,10 +576,6 @@
 		wpa_driver_privsep_event_pmkid_candidate(drv->ctx, event_buf,
 							 event_len);
 		break;
-	case PRIVSEP_EVENT_STKSTART:
-		wpa_driver_privsep_event_stkstart(drv->ctx, event_buf,
-						  event_len);
-		break;
 	case PRIVSEP_EVENT_FT_RESPONSE:
 		wpa_driver_privsep_event_ft_response(drv->ctx, event_buf,
 						     event_len);
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index 458d458..659eda2 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -290,15 +290,6 @@
 	done:
 		os_free(resp_ies);
 		os_free(req_ies);
-#ifdef CONFIG_PEERKEY
-	} else if (os_strncmp(custom, "STKSTART.request=", 17) == 0) {
-		if (hwaddr_aton(custom + 17, data.stkstart.peer)) {
-			wpa_printf(MSG_DEBUG, "WEXT: unrecognized "
-				   "STKSTART.request '%s'", custom + 17);
-			return;
-		}
-		wpa_supplicant_event(ctx, EVENT_STKSTART, &data);
-#endif /* CONFIG_PEERKEY */
 	}
 }
 
@@ -362,12 +353,11 @@
 	wpa_hexdump(MSG_DEBUG, "AssocReq IE wireless event", (const u8 *) ev,
 		    len);
 	os_free(drv->assoc_req_ies);
-	drv->assoc_req_ies = os_malloc(len);
+	drv->assoc_req_ies = os_memdup(ev, len);
 	if (drv->assoc_req_ies == NULL) {
 		drv->assoc_req_ies_len = 0;
 		return -1;
 	}
-	os_memcpy(drv->assoc_req_ies, ev, len);
 	drv->assoc_req_ies_len = len;
 
 	return 0;
@@ -383,12 +373,11 @@
 	wpa_hexdump(MSG_DEBUG, "AssocResp IE wireless event", (const u8 *) ev,
 		    len);
 	os_free(drv->assoc_resp_ies);
-	drv->assoc_resp_ies = os_malloc(len);
+	drv->assoc_resp_ies = os_memdup(ev, len);
 	if (drv->assoc_resp_ies == NULL) {
 		drv->assoc_resp_ies_len = 0;
 		return -1;
 	}
-	os_memcpy(drv->assoc_resp_ies, ev, len);
 	drv->assoc_resp_ies_len = len;
 
 	return 0;
@@ -472,7 +461,7 @@
 				drv->assoc_resp_ies = NULL;
 				wpa_supplicant_event(drv->ctx, EVENT_DISASSOC,
 						     NULL);
-			
+
 			} else {
 				wpa_driver_wext_event_assoc_ies(drv);
 				wpa_supplicant_event(drv->ctx, EVENT_ASSOC,
@@ -2353,19 +2342,21 @@
 }
 
 
-static int wpa_driver_wext_add_pmkid(void *priv, const u8 *bssid,
-				     const u8 *pmkid)
+static int wpa_driver_wext_add_pmkid(void *priv,
+				     struct wpa_pmkid_params *params)
 {
 	struct wpa_driver_wext_data *drv = priv;
-	return wpa_driver_wext_pmksa(drv, IW_PMKSA_ADD, bssid, pmkid);
+	return wpa_driver_wext_pmksa(drv, IW_PMKSA_ADD, params->bssid,
+				     params->pmkid);
 }
 
 
-static int wpa_driver_wext_remove_pmkid(void *priv, const u8 *bssid,
-		 			const u8 *pmkid)
+static int wpa_driver_wext_remove_pmkid(void *priv,
+					struct wpa_pmkid_params *params)
 {
 	struct wpa_driver_wext_data *drv = priv;
-	return wpa_driver_wext_pmksa(drv, IW_PMKSA_REMOVE, bssid, pmkid);
+	return wpa_driver_wext_pmksa(drv, IW_PMKSA_REMOVE, params->bssid,
+				     params->pmkid);
 }
 
 
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index d6c62ee..6095a6c 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -10,7 +10,7 @@
  * Copyright 2008, 2009 Luis R. Rodriguez <lrodriguez@atheros.com>
  * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
  * Copyright 2008 Colin McCabe <colin@cozybit.com>
- * Copyright 2015	Intel Deutschland GmbH
+ * Copyright 2015-2017	Intel Deutschland GmbH
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -173,6 +173,42 @@
  */
 
 /**
+ * DOC: FILS shared key authentication offload
+ *
+ * FILS shared key authentication offload can be advertized by drivers by
+ * setting @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD flag. The drivers that support
+ * FILS shared key authentication offload should be able to construct the
+ * authentication and association frames for FILS shared key authentication and
+ * eventually do a key derivation as per IEEE 802.11ai. The below additional
+ * parameters should be given to driver in %NL80211_CMD_CONNECT.
+ *	%NL80211_ATTR_FILS_ERP_USERNAME - used to construct keyname_nai
+ *	%NL80211_ATTR_FILS_ERP_REALM - used to construct keyname_nai
+ *	%NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used to construct erp message
+ *	%NL80211_ATTR_FILS_ERP_RRK - used to generate the rIK and rMSK
+ * rIK should be used to generate an authentication tag on the ERP message and
+ * rMSK should be used to derive a PMKSA.
+ * rIK, rMSK should be generated and keyname_nai, sequence number should be used
+ * as specified in IETF RFC 6696.
+ *
+ * When FILS shared key authentication is completed, driver needs to provide the
+ * below additional parameters to userspace.
+ *	%NL80211_ATTR_FILS_KEK - used for key renewal
+ *	%NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used in further EAP-RP exchanges
+ *	%NL80211_ATTR_PMKID - used to identify the PMKSA used/generated
+ *	%Nl80211_ATTR_PMK - used to update PMKSA cache in userspace
+ * The PMKSA can be maintained in userspace persistently so that it can be used
+ * later after reboots or wifi turn off/on also.
+ *
+ * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertized by a FILS
+ * capable AP supporting PMK caching. It specifies the scope within which the
+ * PMKSAs are cached in an ESS. %NL80211_CMD_SET_PMKSA and
+ * %NL80211_CMD_DEL_PMKSA are enhanced to allow support for PMKSA caching based
+ * on FILS cache identifier. Additionally %NL80211_ATTR_PMK is used with
+ * %NL80211_SET_PMKSA to specify the PMK corresponding to a PMKSA for driver to
+ * use in a FILS shared key connection with PMKSA caching.
+ */
+
+/**
  * enum nl80211_commands - supported nl80211 commands
  *
  * @NL80211_CMD_UNSPEC: unspecified command to catch errors
@@ -370,10 +406,18 @@
  * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to
  *	NL80211_CMD_GET_SURVEY and on the "scan" multicast group)
  *
- * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry, using %NL80211_ATTR_MAC
- *	(for the BSSID) and %NL80211_ATTR_PMKID.
+ * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry using %NL80211_ATTR_MAC
+ *	(for the BSSID), %NL80211_ATTR_PMKID, and optionally %NL80211_ATTR_PMK
+ *	(PMK is used for PTKSA derivation in case of FILS shared key offload) or
+ *	using %NL80211_ATTR_SSID, %NL80211_ATTR_FILS_CACHE_ID,
+ *	%NL80211_ATTR_PMKID, and %NL80211_ATTR_PMK in case of FILS
+ *	authentication where %NL80211_ATTR_FILS_CACHE_ID is the identifier
+ *	advertized by a FILS capable AP identifying the scope of PMKSA in an
+ *	ESS.
  * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC
- *	(for the BSSID) and %NL80211_ATTR_PMKID.
+ *	(for the BSSID) and %NL80211_ATTR_PMKID or using %NL80211_ATTR_SSID,
+ *	%NL80211_ATTR_FILS_CACHE_ID, and %NL80211_ATTR_PMKID in case of FILS
+ *	authentication.
  * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries.
  *
  * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain
@@ -854,12 +898,15 @@
  *	cfg80211_scan_done().
  *
  * @NL80211_CMD_START_NAN: Start NAN operation, identified by its
- *	%NL80211_ATTR_WDEV interface. This interface must have been previously
- *	created with %NL80211_CMD_NEW_INTERFACE. After it has been started, the
- *	NAN interface will create or join a cluster. This command must have a
- *	valid %NL80211_ATTR_NAN_MASTER_PREF attribute and optional
- *	%NL80211_ATTR_NAN_DUAL attributes.
- *	After this command NAN functions can be added.
+ *	%NL80211_ATTR_WDEV interface. This interface must have been
+ *	previously created with %NL80211_CMD_NEW_INTERFACE. After it
+ *	has been started, the NAN interface will create or join a
+ *	cluster. This command must have a valid
+ *	%NL80211_ATTR_NAN_MASTER_PREF attribute and optional
+ *	%NL80211_ATTR_BANDS attributes.  If %NL80211_ATTR_BANDS is
+ *	omitted or set to 0, it means don't-care and the device will
+ *	decide what to use.  After this command NAN functions can be
+ *	added.
  * @NL80211_CMD_STOP_NAN: Stop the NAN operation, identified by
  *	its %NL80211_ATTR_WDEV interface.
  * @NL80211_CMD_ADD_NAN_FUNCTION: Add a NAN function. The function is defined
@@ -880,10 +927,14 @@
  *	This command is also used as a notification sent when a NAN function is
  *	terminated. This will contain a %NL80211_ATTR_NAN_FUNC_INST_ID
  *	and %NL80211_ATTR_COOKIE attributes.
- * @NL80211_CMD_CHANGE_NAN_CONFIG: Change current NAN configuration. NAN
- *	must be operational (%NL80211_CMD_START_NAN was executed).
- *	It must contain at least one of the following attributes:
- *	%NL80211_ATTR_NAN_MASTER_PREF, %NL80211_ATTR_NAN_DUAL.
+ * @NL80211_CMD_CHANGE_NAN_CONFIG: Change current NAN
+ *	configuration. NAN must be operational (%NL80211_CMD_START_NAN
+ *	was executed).  It must contain at least one of the following
+ *	attributes: %NL80211_ATTR_NAN_MASTER_PREF,
+ *	%NL80211_ATTR_BANDS.  If %NL80211_ATTR_BANDS is omitted, the
+ *	current configuration is not changed.  If it is present but
+ *	set to zero, the configuration is changed to don't-care
+ *	(i.e. the device can decide what to do).
  * @NL80211_CMD_NAN_FUNC_MATCH: Notification sent when a match is reported.
  *	This will contain a %NL80211_ATTR_NAN_MATCH nested attribute and
  *	%NL80211_ATTR_COOKIE.
@@ -1963,10 +2014,13 @@
  *	%NL80211_CMD_CHANGE_NAN_CONFIG. Its type is u8 and it can't be 0.
  *	Also, values 1 and 255 are reserved for certification purposes and
  *	should not be used during a normal device operation.
- * @NL80211_ATTR_NAN_DUAL: NAN dual band operation config (see
- *	&enum nl80211_nan_dual_band_conf). This attribute is used with
- *	%NL80211_CMD_START_NAN and optionally with
- *	%NL80211_CMD_CHANGE_NAN_CONFIG.
+ * @NL80211_ATTR_BANDS: operating bands configuration.  This is a u32
+ *	bitmask of BIT(NL80211_BAND_*) as described in %enum
+ *	nl80211_band.  For instance, for NL80211_BAND_2GHZ, bit 0
+ *	would be set.  This attribute is used with
+ *	%NL80211_CMD_START_NAN and %NL80211_CMD_CHANGE_NAN_CONFIG, and
+ *	it is optional.  If no bands are set, it means don't-care and
+ *	the device will decide what to use.
  * @NL80211_ATTR_NAN_FUNC: a function that can be added to NAN. See
  *	&enum nl80211_nan_func_attributes for description of this nested
  *	attribute.
@@ -2002,6 +2056,31 @@
  *	u32 attribute with an &enum nl80211_timeout_reason value. This is used,
  *	e.g., with %NL80211_CMD_CONNECT event.
  *
+ * @NL80211_ATTR_FILS_ERP_USERNAME: EAP Re-authentication Protocol (ERP)
+ *	username part of NAI used to refer keys rRK and rIK. This is used with
+ *	%NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_ERP_REALM: EAP Re-authentication Protocol (ERP) realm part
+ *	of NAI specifying the domain name of the ER server. This is used with
+ *	%NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM: Unsigned 16-bit ERP next sequence number
+ *	to use in ERP messages. This is used in generating the FILS wrapped data
+ *	for FILS authentication and is used with %NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_ERP_RRK: ERP re-authentication Root Key (rRK) for the
+ *	NAI specified by %NL80211_ATTR_FILS_ERP_USERNAME and
+ *	%NL80211_ATTR_FILS_ERP_REALM. This is used for generating rIK and rMSK
+ *	from successful FILS authentication and is used with
+ *	%NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertized by a FILS AP
+ *	identifying the scope of PMKSAs. This is used with
+ *	@NL80211_CMD_SET_PMKSA and @NL80211_CMD_DEL_PMKSA.
+ *
+ * @NL80211_ATTR_PMK: PMK for the PMKSA identified by %NL80211_ATTR_PMKID.
+ *	This is used with @NL80211_CMD_SET_PMKSA.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2397,7 +2476,7 @@
 	NL80211_ATTR_MESH_PEER_AID,
 
 	NL80211_ATTR_NAN_MASTER_PREF,
-	NL80211_ATTR_NAN_DUAL,
+	NL80211_ATTR_BANDS,
 	NL80211_ATTR_NAN_FUNC,
 	NL80211_ATTR_NAN_MATCH,
 
@@ -2413,6 +2492,14 @@
 
 	NL80211_ATTR_TIMEOUT_REASON,
 
+	NL80211_ATTR_FILS_ERP_USERNAME,
+	NL80211_ATTR_FILS_ERP_REALM,
+	NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM,
+	NL80211_ATTR_FILS_ERP_RRK,
+	NL80211_ATTR_FILS_CACHE_ID,
+
+	NL80211_ATTR_PMK,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -3932,7 +4019,10 @@
  * @__NL80211_ATTR_CQM_INVALID: invalid
  * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies
  *	the threshold for the RSSI level at which an event will be sent. Zero
- *	to disable.
+ *	to disable.  Alternatively, if %NL80211_EXT_FEATURE_CQM_RSSI_LIST is
+ *	set, multiple values can be supplied as a low-to-high sorted array of
+ *	threshold values in dBm.  Events will be sent when the RSSI value
+ *	crosses any of the thresholds.
  * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies
  *	the minimum amount the RSSI level must change after an event before a
  *	new event may be issued (to reduce effects of RSSI oscillation).
@@ -3952,6 +4042,8 @@
  *	%NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting.
  * @NL80211_ATTR_CQM_BEACON_LOSS_EVENT: flag attribute that's set in a beacon
  *	loss event
+ * @NL80211_ATTR_CQM_RSSI_LEVEL: the RSSI value in dBm that triggered the
+ *	RSSI threshold event.
  * @__NL80211_ATTR_CQM_AFTER_LAST: internal
  * @NL80211_ATTR_CQM_MAX: highest key attribute
  */
@@ -3965,6 +4057,7 @@
 	NL80211_ATTR_CQM_TXE_PKTS,
 	NL80211_ATTR_CQM_TXE_INTVL,
 	NL80211_ATTR_CQM_BEACON_LOSS_EVENT,
+	NL80211_ATTR_CQM_RSSI_LEVEL,
 
 	/* keep last */
 	__NL80211_ATTR_CQM_AFTER_LAST,
@@ -4740,6 +4833,11 @@
  * @NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI: The driver supports sched_scan
  *	for reporting BSSs with better RSSI than the current connected BSS
  *	(%NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI).
+ * @NL80211_EXT_FEATURE_CQM_RSSI_LIST: With this driver the
+ *	%NL80211_ATTR_CQM_RSSI_THOLD attribute accepts a list of zero or more
+ *	RSSI threshold values to monitor rather than exactly one threshold.
+ * @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD: Driver SME supports FILS shared key
+ *	authentication with %NL80211_CMD_CONNECT.
  *
  * @NUM_NL80211_EXT_FEATURES: number of extended features.
  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
@@ -4758,6 +4856,8 @@
 	NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA,
 	NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED,
 	NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI,
+	NL80211_EXT_FEATURE_CQM_RSSI_LIST,
+	NL80211_EXT_FEATURE_FILS_SK_OFFLOAD,
 
 	/* add new features before the definition below */
 	NUM_NL80211_EXT_FEATURES,
@@ -4893,12 +4993,17 @@
  *	change to the channel status.
  * @NL80211_RADAR_NOP_FINISHED: The Non-Occupancy Period for this channel is
  *	over, channel becomes usable.
+ * @NL80211_RADAR_PRE_CAC_EXPIRED: Channel Availability Check done on this
+ *	non-operating channel is expired and no longer valid. New CAC must
+ *	be done on this channel before starting the operation. This is not
+ *	applicable for ETSI dfs domain where pre-CAC is valid for ever.
  */
 enum nl80211_radar_event {
 	NL80211_RADAR_DETECTED,
 	NL80211_RADAR_CAC_FINISHED,
 	NL80211_RADAR_CAC_ABORTED,
 	NL80211_RADAR_NOP_FINISHED,
+	NL80211_RADAR_PRE_CAC_EXPIRED,
 };
 
 /**
@@ -5068,21 +5173,6 @@
 };
 
 /**
- * enum nl80211_nan_dual_band_conf - NAN dual band configuration
- *
- * Defines the NAN dual band mode of operation
- *
- * @NL80211_NAN_BAND_DEFAULT: device default mode
- * @NL80211_NAN_BAND_2GHZ: 2.4GHz mode
- * @NL80211_NAN_BAND_5GHZ: 5GHz mode
-  */
-enum nl80211_nan_dual_band_conf {
-	NL80211_NAN_BAND_DEFAULT	= 1 << 0,
-	NL80211_NAN_BAND_2GHZ		= 1 << 1,
-	NL80211_NAN_BAND_5GHZ		= 1 << 2,
-};
-
-/**
  * enum nl80211_nan_function_type - NAN function type
  *
  * Defines the function type of a NAN function
diff --git a/src/eap_common/eap_sim_common.c b/src/eap_common/eap_sim_common.c
index 2adc3b3..6290c35 100644
--- a/src/eap_common/eap_sim_common.c
+++ b/src/eap_common/eap_sim_common.c
@@ -175,7 +175,7 @@
 	    mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN)
 		return -1;
 
-	tmp = os_malloc(wpabuf_len(req));
+	tmp = os_memdup(wpabuf_head(req), wpabuf_len(req));
 	if (tmp == NULL)
 		return -1;
 
@@ -185,7 +185,6 @@
 	len[1] = extra_len;
 
 	/* HMAC-SHA1-128 */
-	os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req));
 	os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN);
 	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - msg",
 		    tmp, wpabuf_len(req));
@@ -370,7 +369,7 @@
 	    mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN)
 		return -1;
 
-	tmp = os_malloc(wpabuf_len(req));
+	tmp = os_memdup(wpabuf_head(req), wpabuf_len(req));
 	if (tmp == NULL)
 		return -1;
 
@@ -380,7 +379,6 @@
 	len[1] = extra_len;
 
 	/* HMAC-SHA-256-128 */
-	os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req));
 	os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN);
 	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - msg",
 		    tmp, wpabuf_len(req));
@@ -943,10 +941,9 @@
 		return NULL;
 	}
 
-	decrypted = os_malloc(encr_data_len);
+	decrypted = os_memdup(encr_data, encr_data_len);
 	if (decrypted == NULL)
 		return NULL;
-	os_memcpy(decrypted, encr_data, encr_data_len);
 
 	if (aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len)) {
 		os_free(decrypted);
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index bc90c7a..0043707 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -121,15 +121,17 @@
 
 
 /**
- * eap_allowed_method - Check whether EAP method is allowed
+ * eap_config_allowed_method - Check whether EAP method is allowed
  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @config: EAP configuration
  * @vendor: Vendor-Id for expanded types or 0 = IETF for legacy types
  * @method: EAP type
  * Returns: 1 = allowed EAP method, 0 = not allowed
  */
-int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method)
+static int eap_config_allowed_method(struct eap_sm *sm,
+				     struct eap_peer_config *config,
+				     int vendor, u32 method)
 {
-	struct eap_peer_config *config = eap_get_config(sm);
 	int i;
 	struct eap_method_type *m;
 
@@ -146,6 +148,57 @@
 }
 
 
+/**
+ * eap_allowed_method - Check whether EAP method is allowed
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @vendor: Vendor-Id for expanded types or 0 = IETF for legacy types
+ * @method: EAP type
+ * Returns: 1 = allowed EAP method, 0 = not allowed
+ */
+int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method)
+{
+	return eap_config_allowed_method(sm, eap_get_config(sm), vendor,
+					 method);
+}
+
+
+#if defined(PCSC_FUNCS) || defined(CONFIG_EAP_PROXY)
+static int eap_sm_append_3gpp_realm(struct eap_sm *sm, char *imsi,
+				    size_t max_len, size_t *imsi_len,
+				    int mnc_len)
+{
+	char *pos, mnc[4];
+
+	if (*imsi_len + 36 > max_len) {
+		wpa_printf(MSG_WARNING, "No room for realm in IMSI buffer");
+		return -1;
+	}
+
+	if (mnc_len != 2 && mnc_len != 3)
+		mnc_len = 3;
+
+	if (mnc_len == 2) {
+		mnc[0] = '0';
+		mnc[1] = imsi[3];
+		mnc[2] = imsi[4];
+	} else if (mnc_len == 3) {
+		mnc[0] = imsi[3];
+		mnc[1] = imsi[4];
+		mnc[2] = imsi[5];
+	}
+	mnc[3] = '\0';
+
+	pos = imsi + *imsi_len;
+	pos += os_snprintf(pos, imsi + max_len - pos,
+			   "@wlan.mnc%s.mcc%c%c%c.3gppnetwork.org",
+			   mnc, imsi[0], imsi[1], imsi[2]);
+	*imsi_len = pos - imsi;
+
+	return 0;
+}
+#endif /* PCSC_FUNCS || CONFIG_EAP_PROXY */
+
+
 /*
  * This state initializes state machine variables when the machine is
  * activated (portEnabled = TRUE). This is also used when re-starting
@@ -371,9 +424,8 @@
 
 #ifdef CONFIG_ERP
 
-static char * eap_home_realm(struct eap_sm *sm)
+static char * eap_get_realm(struct eap_sm *sm, struct eap_peer_config *config)
 {
-	struct eap_peer_config *config = eap_get_config(sm);
 	char *realm;
 	size_t i, realm_len;
 
@@ -413,7 +465,51 @@
 		}
 	}
 
-	return os_strdup("");
+#ifdef CONFIG_EAP_PROXY
+	/* When identity is not provided in the config, build the realm from
+	 * IMSI for eap_proxy based methods.
+	 */
+	if (!config->identity && !config->anonymous_identity &&
+	    sm->eapol_cb->get_imsi &&
+	    (eap_config_allowed_method(sm, config, EAP_VENDOR_IETF,
+				       EAP_TYPE_SIM) ||
+	     eap_config_allowed_method(sm, config, EAP_VENDOR_IETF,
+				       EAP_TYPE_AKA) ||
+	     eap_config_allowed_method(sm, config, EAP_VENDOR_IETF,
+				       EAP_TYPE_AKA_PRIME))) {
+		char imsi[100];
+		size_t imsi_len;
+		int mnc_len, pos;
+
+		wpa_printf(MSG_DEBUG, "EAP: Build realm from IMSI (eap_proxy)");
+		mnc_len = sm->eapol_cb->get_imsi(sm->eapol_ctx, config->sim_num,
+						 imsi, &imsi_len);
+		if (mnc_len < 0)
+			return NULL;
+
+		pos = imsi_len + 1; /* points to the beginning of the realm */
+		if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len,
+					     mnc_len) < 0) {
+			wpa_printf(MSG_WARNING, "Could not append realm");
+			return NULL;
+		}
+
+		realm = os_strdup(&imsi[pos]);
+		if (!realm)
+			return NULL;
+
+		wpa_printf(MSG_DEBUG, "EAP: Generated realm '%s'", realm);
+		return realm;
+	}
+#endif /* CONFIG_EAP_PROXY */
+
+	return NULL;
+}
+
+
+static char * eap_home_realm(struct eap_sm *sm)
+{
+	return eap_get_realm(sm, eap_get_config(sm));
 }
 
 
@@ -469,6 +565,89 @@
 	}
 }
 
+
+int eap_peer_update_erp_next_seq_num(struct eap_sm *sm, u16 next_seq_num)
+{
+	struct eap_erp_key *erp;
+	char *home_realm;
+
+	home_realm = eap_home_realm(sm);
+	if (!home_realm || os_strlen(home_realm) == 0) {
+		os_free(home_realm);
+		return -1;
+	}
+
+	erp = eap_erp_get_key(sm, home_realm);
+	if (!erp) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Failed to find ERP key for realm: %s",
+			   home_realm);
+		os_free(home_realm);
+		return -1;
+	}
+
+	if ((u32) next_seq_num < erp->next_seq) {
+		/* Sequence number has wrapped around, clear this ERP
+		 * info and do a full auth next time.
+		 */
+		eap_peer_erp_free_key(erp);
+	} else {
+		erp->next_seq = (u32) next_seq_num;
+	}
+
+	os_free(home_realm);
+	return 0;
+}
+
+
+int eap_peer_get_erp_info(struct eap_sm *sm, struct eap_peer_config *config,
+			  const u8 **username, size_t *username_len,
+			  const u8 **realm, size_t *realm_len,
+			  u16 *erp_next_seq_num, const u8 **rrk,
+			  size_t *rrk_len)
+{
+	struct eap_erp_key *erp;
+	char *home_realm;
+	char *pos;
+
+	if (config)
+		home_realm = eap_get_realm(sm, config);
+	else
+		home_realm = eap_home_realm(sm);
+	if (!home_realm || os_strlen(home_realm) == 0) {
+		os_free(home_realm);
+		return -1;
+	}
+
+	erp = eap_erp_get_key(sm, home_realm);
+	os_free(home_realm);
+	if (!erp)
+		return -1;
+
+	if (erp->next_seq >= 65536)
+		return -1; /* SEQ has range of 0..65535 */
+
+	pos = os_strchr(erp->keyname_nai, '@');
+	if (!pos)
+		return -1; /* this cannot really happen */
+	*username_len = pos - erp->keyname_nai;
+	*username = (u8 *) erp->keyname_nai;
+
+	pos++;
+	*realm_len = os_strlen(pos);
+	*realm = (u8 *) pos;
+
+	*erp_next_seq_num = (u16) erp->next_seq;
+
+	*rrk_len = erp->rRK_len;
+	*rrk = erp->rRK;
+
+	if (*username_len == 0 || *realm_len == 0 || *rrk_len == 0)
+		return -1;
+
+	return 0;
+}
+
 #endif /* CONFIG_ERP */
 
 
@@ -483,11 +662,15 @@
 }
 
 
-static void eap_peer_erp_init(struct eap_sm *sm)
+void eap_peer_erp_init(struct eap_sm *sm, u8 *ext_session_id,
+		       size_t ext_session_id_len, u8 *ext_emsk,
+		       size_t ext_emsk_len)
 {
 #ifdef CONFIG_ERP
 	u8 *emsk = NULL;
 	size_t emsk_len = 0;
+	u8 *session_id = NULL;
+	size_t session_id_len = 0;
 	u8 EMSKname[EAP_EMSK_NAME_LEN];
 	u8 len[2], ctx[3];
 	char *realm;
@@ -517,7 +700,13 @@
 	if (erp == NULL)
 		goto fail;
 
-	emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len);
+	if (ext_emsk) {
+		emsk = ext_emsk;
+		emsk_len = ext_emsk_len;
+	} else {
+		emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len);
+	}
+
 	if (!emsk || emsk_len == 0 || emsk_len > ERP_MAX_KEY_LEN) {
 		wpa_printf(MSG_DEBUG,
 			   "EAP: No suitable EMSK available for ERP");
@@ -526,10 +715,23 @@
 
 	wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len);
 
+	if (ext_session_id) {
+		session_id = ext_session_id;
+		session_id_len = ext_session_id_len;
+	} else {
+		session_id = sm->eapSessionId;
+		session_id_len = sm->eapSessionIdLen;
+	}
+
+	if (!session_id || session_id_len == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: No suitable session id available for ERP");
+		goto fail;
+	}
+
 	WPA_PUT_BE16(len, EAP_EMSK_NAME_LEN);
-	if (hmac_sha256_kdf(sm->eapSessionId, sm->eapSessionIdLen, "EMSK",
-			    len, sizeof(len),
-			    EMSKname, EAP_EMSK_NAME_LEN) < 0) {
+	if (hmac_sha256_kdf(session_id, session_id_len, "EMSK", len,
+			    sizeof(len), EMSKname, EAP_EMSK_NAME_LEN) < 0) {
 		wpa_printf(MSG_DEBUG, "EAP: Could not derive EMSKname");
 		goto fail;
 	}
@@ -566,6 +768,7 @@
 	erp = NULL;
 fail:
 	bin_clear_free(emsk, emsk_len);
+	bin_clear_free(ext_session_id, ext_session_id_len);
 	bin_clear_free(erp, sizeof(*erp));
 	os_free(realm);
 #endif /* CONFIG_ERP */
@@ -707,8 +910,6 @@
 
 	if (sm->m->isKeyAvailable && sm->m->getKey &&
 	    sm->m->isKeyAvailable(sm, sm->eap_method_priv)) {
-		struct eap_peer_config *config = eap_get_config(sm);
-
 		eap_sm_free_key(sm);
 		sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv,
 					       &sm->eapKeyDataLen);
@@ -721,8 +922,6 @@
 			wpa_hexdump(MSG_DEBUG, "EAP: Session-Id",
 				    sm->eapSessionId, sm->eapSessionIdLen);
 		}
-		if (config->erp && sm->m->get_emsk && sm->eapSessionId)
-			eap_peer_erp_init(sm);
 	}
 }
 
@@ -820,6 +1019,8 @@
  */
 SM_STATE(EAP, SUCCESS)
 {
+	struct eap_peer_config *config = eap_get_config(sm);
+
 	SM_ENTRY(EAP, SUCCESS);
 	if (sm->eapKeyData != NULL)
 		sm->eapKeyAvailable = TRUE;
@@ -842,6 +1043,11 @@
 
 	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
 		"EAP authentication completed successfully");
+
+	if (config->erp && sm->m->get_emsk && sm->eapSessionId &&
+	    sm->m->isKeyAvailable &&
+	    sm->m->isKeyAvailable(sm, sm->eap_method_priv))
+		eap_peer_erp_init(sm, NULL, 0, NULL, 0);
 }
 
 
@@ -1292,48 +1498,6 @@
 }
 
 
-static int eap_sm_append_3gpp_realm(struct eap_sm *sm, char *imsi,
-				    size_t max_len, size_t *imsi_len)
-{
-	int mnc_len;
-	char *pos, mnc[4];
-
-	if (*imsi_len + 36 > max_len) {
-		wpa_printf(MSG_WARNING, "No room for realm in IMSI buffer");
-		return -1;
-	}
-
-	/* MNC (2 or 3 digits) */
-	mnc_len = scard_get_mnc_len(sm->scard_ctx);
-	if (mnc_len < 0)
-		mnc_len = mnc_len_from_imsi(imsi);
-	if (mnc_len < 0) {
-		wpa_printf(MSG_INFO, "Failed to get MNC length from (U)SIM "
-			   "assuming 3");
-		mnc_len = 3;
-	}
-
-	if (mnc_len == 2) {
-		mnc[0] = '0';
-		mnc[1] = imsi[3];
-		mnc[2] = imsi[4];
-	} else if (mnc_len == 3) {
-		mnc[0] = imsi[3];
-		mnc[1] = imsi[4];
-		mnc[2] = imsi[5];
-	}
-	mnc[3] = '\0';
-
-	pos = imsi + *imsi_len;
-	pos += os_snprintf(pos, imsi + max_len - pos,
-			   "@wlan.mnc%s.mcc%c%c%c.3gppnetwork.org",
-			   mnc, imsi[0], imsi[1], imsi[2]);
-	*imsi_len = pos - imsi;
-
-	return 0;
-}
-
-
 static int eap_sm_imsi_identity(struct eap_sm *sm,
 				struct eap_peer_config *conf)
 {
@@ -1341,7 +1505,7 @@
 	char imsi[100];
 	size_t imsi_len;
 	struct eap_method_type *m = conf->eap_methods;
-	int i;
+	int i, mnc_len;
 
 	imsi_len = sizeof(imsi);
 	if (scard_get_imsi(sm->scard_ctx, imsi, &imsi_len)) {
@@ -1356,7 +1520,18 @@
 		return -1;
 	}
 
-	if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len) < 0) {
+	/* MNC (2 or 3 digits) */
+	mnc_len = scard_get_mnc_len(sm->scard_ctx);
+	if (mnc_len < 0)
+		mnc_len = mnc_len_from_imsi(imsi);
+	if (mnc_len < 0) {
+		wpa_printf(MSG_INFO, "Failed to get MNC length from (U)SIM "
+			   "assuming 3");
+		mnc_len = 3;
+	}
+
+	if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len,
+				     mnc_len) < 0) {
 		wpa_printf(MSG_WARNING, "Could not add realm to SIM identity");
 		return -1;
 	}
diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h
index 932584f..b5591a0 100644
--- a/src/eap_peer/eap.h
+++ b/src/eap_peer/eap.h
@@ -260,6 +260,16 @@
 	 */
 	void (*eap_proxy_notify_sim_status)(void *ctx,
 					    enum eap_proxy_sim_state sim_state);
+
+	/**
+	 * get_imsi - Get the IMSI value from eap_proxy
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * @sim_num: SIM/USIM number to get the IMSI value for
+	 * @imsi: Buffer for IMSI value
+	 * @len: Buffer for returning IMSI length in octets
+	 * Returns: MNC length (2 or 3) or -1 on error
+	 */
+	int (*get_imsi)(void *ctx, int sim_num, char *imsi, size_t *len);
 #endif /* CONFIG_EAP_PROXY */
 
 	/**
@@ -358,6 +368,14 @@
 void eap_peer_erp_free_keys(struct eap_sm *sm);
 struct wpabuf * eap_peer_build_erp_reauth_start(struct eap_sm *sm, u8 eap_id);
 void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr, size_t len);
+int eap_peer_get_erp_info(struct eap_sm *sm, struct eap_peer_config *config,
+			  const u8 **username, size_t *username_len,
+			  const u8 **realm, size_t *realm_len, u16 *erp_seq_num,
+			  const u8 **rrk, size_t *rrk_len);
+int eap_peer_update_erp_next_seq_num(struct eap_sm *sm, u16 seq_num);
+void eap_peer_erp_init(struct eap_sm *sm, u8 *ext_session_id,
+		       size_t ext_session_id_len, u8 *ext_emsk,
+		       size_t ext_emsk_len);
 
 #endif /* IEEE8021X_EAPOL */
 
diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c
index 4188817..f7e3cd6 100644
--- a/src/eap_peer/eap_aka.c
+++ b/src/eap_peer/eap_aka.c
@@ -54,6 +54,8 @@
 	size_t network_name_len;
 	u16 kdf;
 	int kdf_negotiation;
+	u16 last_kdf_attrs[EAP_AKA_PRIME_KDF_MAX];
+	size_t last_kdf_count;
 };
 
 
@@ -415,15 +417,14 @@
 
 	if (attr->next_reauth_id) {
 		os_free(data->reauth_id);
-		data->reauth_id = os_malloc(attr->next_reauth_id_len);
+		data->reauth_id = os_memdup(attr->next_reauth_id,
+					    attr->next_reauth_id_len);
 		if (data->reauth_id == NULL) {
 			wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for "
 				   "next reauth_id");
 			data->reauth_id_len = 0;
 			return -1;
 		}
-		os_memcpy(data->reauth_id, attr->next_reauth_id,
-			  attr->next_reauth_id_len);
 		data->reauth_id_len = attr->next_reauth_id_len;
 		wpa_hexdump_ascii(MSG_DEBUG,
 				  "EAP-AKA: (encr) AT_NEXT_REAUTH_ID",
@@ -575,7 +576,7 @@
 
 
 static struct wpabuf * eap_aka_synchronization_failure(
-	struct eap_aka_data *data, u8 id)
+	struct eap_aka_data *data, u8 id, struct eap_sim_attrs *attr)
 {
 	struct eap_sim_msg *msg;
 
@@ -589,6 +590,15 @@
 	wpa_printf(MSG_DEBUG, "   AT_AUTS");
 	eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts,
 			     EAP_AKA_AUTS_LEN);
+	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+		size_t i;
+
+		for (i = 0; i < attr->kdf_count; i++) {
+			wpa_printf(MSG_DEBUG, "   AT_KDF");
+			eap_sim_msg_add(msg, EAP_SIM_AT_KDF, attr->kdf[i],
+					NULL, 0);
+		}
+	}
 	return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
 }
 
@@ -822,9 +832,13 @@
 	size_t i;
 
 	for (i = 0; i < attr->kdf_count; i++) {
-		if (attr->kdf[i] == EAP_AKA_PRIME_KDF)
+		if (attr->kdf[i] == EAP_AKA_PRIME_KDF) {
+			os_memcpy(data->last_kdf_attrs, attr->kdf,
+				  sizeof(u16) * attr->kdf_count);
+			data->last_kdf_count = attr->kdf_count;
 			return eap_aka_prime_kdf_select(data, id,
 							EAP_AKA_PRIME_KDF);
+		}
 	}
 
 	/* No matching KDF found - fail authentication as if AUTN had been
@@ -845,26 +859,32 @@
 	 * of the selected KDF into the beginning of the list. */
 
 	if (data->kdf_negotiation) {
+		/* When the peer receives the new EAP-Request/AKA'-Challenge
+		 * message, must check only requested change occurred in the
+		 * list of AT_KDF attributes. If there are any other changes,
+		 * the peer must behave like the case that AT_MAC had been
+		 * incorrect and authentication is failed. These are defined in
+		 * EAP-AKA' specification RFC 5448, Section 3.2. */
 		if (attr->kdf[0] != data->kdf) {
 			wpa_printf(MSG_WARNING, "EAP-AKA': The server did not "
 				   "accept the selected KDF");
-			return 0;
+			return -1;
+		}
+
+		if (attr->kdf_count > EAP_AKA_PRIME_KDF_MAX ||
+		    attr->kdf_count != data->last_kdf_count + 1) {
+			wpa_printf(MSG_WARNING,
+				   "EAP-AKA': The length of KDF attributes is wrong");
+			return -1;
 		}
 
 		for (i = 1; i < attr->kdf_count; i++) {
-			if (attr->kdf[i] == data->kdf)
-				break;
+			if (attr->kdf[i] != data->last_kdf_attrs[i - 1]) {
+				wpa_printf(MSG_WARNING,
+					   "EAP-AKA': The KDF attributes except selected KDF are not same as original one");
+				return -1;
+			}
 		}
-		if (i == attr->kdf_count &&
-		    attr->kdf_count < EAP_AKA_PRIME_KDF_MAX) {
-			wpa_printf(MSG_WARNING, "EAP-AKA': The server did not "
-				   "duplicate the selected KDF");
-			return 0;
-		}
-
-		/* TODO: should check that the list is identical to the one
-		 * used in the previous Challenge message apart from the added
-		 * entry in the beginning. */
 	}
 
 	for (i = data->kdf ? 1 : 0; i < attr->kdf_count; i++) {
@@ -913,22 +933,25 @@
 			return eap_aka_authentication_reject(data, id);
 		}
 		os_free(data->network_name);
-		data->network_name = os_malloc(attr->kdf_input_len);
+		data->network_name = os_memdup(attr->kdf_input,
+					       attr->kdf_input_len);
 		if (data->network_name == NULL) {
 			wpa_printf(MSG_WARNING, "EAP-AKA': No memory for "
 				   "storing Network Name");
 			return eap_aka_authentication_reject(data, id);
 		}
-		os_memcpy(data->network_name, attr->kdf_input,
-			  attr->kdf_input_len);
 		data->network_name_len = attr->kdf_input_len;
 		wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': Network Name "
 				  "(AT_KDF_INPUT)",
 				  data->network_name, data->network_name_len);
 		/* TODO: check Network Name per 3GPP.33.402 */
 
-		if (!eap_aka_prime_kdf_valid(data, attr))
+		res = eap_aka_prime_kdf_valid(data, attr);
+		if (res == 0)
 			return eap_aka_authentication_reject(data, id);
+		else if (res == -1)
+			return eap_aka_client_error(
+				data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET);
 
 		if (attr->kdf[0] != EAP_AKA_PRIME_KDF)
 			return eap_aka_prime_kdf_neg(data, id, attr);
@@ -971,7 +994,7 @@
 	} else if (res == -2) {
 		wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication "
 			   "failed (AUTN seq# -> AUTS)");
-		return eap_aka_synchronization_failure(data, id);
+		return eap_aka_synchronization_failure(data, id, attr);
 	} else if (res > 0) {
 		wpa_printf(MSG_DEBUG, "EAP-AKA: Wait for external USIM processing");
 		return NULL;
@@ -1442,12 +1465,11 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+	key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN);
 	if (key == NULL)
 		return NULL;
 
 	*len = EAP_SIM_KEYING_DATA_LEN;
-	os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
 
 	return key;
 }
@@ -1483,12 +1505,11 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
 
 	*len = EAP_EMSK_LEN;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 
 	return key;
 }
diff --git a/src/eap_peer/eap_eke.c b/src/eap_peer/eap_eke.c
index f899f65..0de7d6c 100644
--- a/src/eap_peer/eap_eke.c
+++ b/src/eap_peer/eap_eke.c
@@ -85,12 +85,11 @@
 
 	identity = eap_get_config_identity(sm, &identity_len);
 	if (identity) {
-		data->peerid = os_malloc(identity_len);
+		data->peerid = os_memdup(identity, identity_len);
 		if (data->peerid == NULL) {
 			eap_eke_deinit(sm, data);
 			return NULL;
 		}
-		os_memcpy(data->peerid, identity, identity_len);
 		data->peerid_len = identity_len;
 	}
 
@@ -310,12 +309,11 @@
 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Server Identity",
 			  pos, end - pos);
 	os_free(data->serverid);
-	data->serverid = os_malloc(end - pos);
+	data->serverid = os_memdup(pos, end - pos);
 	if (data->serverid == NULL) {
 		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
-	os_memcpy(data->serverid, pos, end - pos);
 	data->serverid_len = end - pos;
 
 	wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-ID/Response");
@@ -717,10 +715,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_MSK_LEN);
+	key = os_memdup(data->msk, EAP_MSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->msk, EAP_MSK_LEN);
 	*len = EAP_MSK_LEN;
 
 	return key;
@@ -735,10 +732,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 	*len = EAP_EMSK_LEN;
 
 	return key;
diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c
index e4b0c10..74cec7d 100644
--- a/src/eap_peer/eap_fast.c
+++ b/src/eap_peer/eap_fast.c
@@ -1749,12 +1749,11 @@
 	if (!data->success)
 		return NULL;
 
-	key = os_malloc(EAP_FAST_KEY_LEN);
+	key = os_memdup(data->key_data, EAP_FAST_KEY_LEN);
 	if (key == NULL)
 		return NULL;
 
 	*len = EAP_FAST_KEY_LEN;
-	os_memcpy(key, data->key_data, EAP_FAST_KEY_LEN);
 
 	return key;
 }
@@ -1768,12 +1767,11 @@
 	if (!data->success || !data->session_id)
 		return NULL;
 
-	id = os_malloc(data->id_len);
+	id = os_memdup(data->session_id, data->id_len);
 	if (id == NULL)
 		return NULL;
 
 	*len = data->id_len;
-	os_memcpy(id, data->session_id, data->id_len);
 
 	return id;
 }
@@ -1787,12 +1785,11 @@
 	if (!data->success)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
 
 	*len = EAP_EMSK_LEN;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 
 	return key;
 }
diff --git a/src/eap_peer/eap_fast_pac.c b/src/eap_peer/eap_fast_pac.c
index c815860..7d674c8 100644
--- a/src/eap_peer/eap_fast_pac.c
+++ b/src/eap_peer/eap_fast_pac.c
@@ -114,10 +114,9 @@
 			     const u8 *src, size_t src_len)
 {
 	if (src) {
-		*dst = os_malloc(src_len);
+		*dst = os_memdup(src, src_len);
 		if (*dst == NULL)
 			return -1;
-		os_memcpy(*dst, src, src_len);
 		*dst_len = src_len;
 	}
 	return 0;
@@ -720,19 +719,17 @@
 
 		if (type == PAC_TYPE_A_ID) {
 			os_free(pac->a_id);
-			pac->a_id = os_malloc(len);
+			pac->a_id = os_memdup(pos, len);
 			if (pac->a_id == NULL)
 				break;
-			os_memcpy(pac->a_id, pos, len);
 			pac->a_id_len = len;
 		}
 
 		if (type == PAC_TYPE_A_ID_INFO) {
 			os_free(pac->a_id_info);
-			pac->a_id_info = os_malloc(len);
+			pac->a_id_info = os_memdup(pos, len);
 			if (pac->a_id_info == NULL)
 				break;
-			os_memcpy(pac->a_id_info, pos, len);
 			pac->a_id_info_len = len;
 		}
 
@@ -820,10 +817,9 @@
 		if (val > end - pos)
 			goto parse_fail;
 		pac->pac_opaque_len = val;
-		pac->pac_opaque = os_malloc(pac->pac_opaque_len);
+		pac->pac_opaque = os_memdup(pos, pac->pac_opaque_len);
 		if (pac->pac_opaque == NULL)
 			goto parse_fail;
-		os_memcpy(pac->pac_opaque, pos, pac->pac_opaque_len);
 		pos += pac->pac_opaque_len;
 		if (2 > end - pos)
 			goto parse_fail;
@@ -832,10 +828,9 @@
 		if (val > end - pos)
 			goto parse_fail;
 		pac->pac_info_len = val;
-		pac->pac_info = os_malloc(pac->pac_info_len);
+		pac->pac_info = os_memdup(pos, pac->pac_info_len);
 		if (pac->pac_info == NULL)
 			goto parse_fail;
-		os_memcpy(pac->pac_info, pos, pac->pac_info_len);
 		pos += pac->pac_info_len;
 		eap_fast_pac_get_a_id(pac);
 
diff --git a/src/eap_peer/eap_gpsk.c b/src/eap_peer/eap_gpsk.c
index 177cbcc..f9c4d37 100644
--- a/src/eap_peer/eap_gpsk.c
+++ b/src/eap_peer/eap_gpsk.c
@@ -96,12 +96,11 @@
 
 	identity = eap_get_config_identity(sm, &identity_len);
 	if (identity) {
-		data->id_peer = os_malloc(identity_len);
+		data->id_peer = os_memdup(identity, identity_len);
 		if (data->id_peer == NULL) {
 			eap_gpsk_deinit(sm, data);
 			return NULL;
 		}
-		os_memcpy(data->id_peer, identity, identity_len);
 		data->id_peer_len = identity_len;
 	}
 
@@ -117,12 +116,11 @@
 		}
 	}
 
-	data->psk = os_malloc(password_len);
+	data->psk = os_memdup(password, password_len);
 	if (data->psk == NULL) {
 		eap_gpsk_deinit(sm, data);
 		return NULL;
 	}
-	os_memcpy(data->psk, password, password_len);
 	data->psk_len = password_len;
 
 	return data;
@@ -158,12 +156,11 @@
 		return NULL;
 	}
 	os_free(data->id_server);
-	data->id_server = os_malloc(alen);
+	data->id_server = os_memdup(pos, alen);
 	if (data->id_server == NULL) {
 		wpa_printf(MSG_DEBUG, "EAP-GPSK: No memory for ID_Server");
 		return NULL;
 	}
-	os_memcpy(data->id_server, pos, alen);
 	data->id_server_len = alen;
 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server",
 			  data->id_server, data->id_server_len);
@@ -722,10 +719,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_MSK_LEN);
+	key = os_memdup(data->msk, EAP_MSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->msk, EAP_MSK_LEN);
 	*len = EAP_MSK_LEN;
 
 	return key;
@@ -740,10 +736,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 	*len = EAP_EMSK_LEN;
 
 	return key;
@@ -758,10 +753,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	sid = os_malloc(data->id_len);
+	sid = os_memdup(data->session_id, data->id_len);
 	if (sid == NULL)
 		return NULL;
-	os_memcpy(sid, data->session_id, data->id_len);
 	*len = data->id_len;
 
 	return sid;
diff --git a/src/eap_peer/eap_ikev2.c b/src/eap_peer/eap_ikev2.c
index 390f0ec..6ddf508 100644
--- a/src/eap_peer/eap_ikev2.c
+++ b/src/eap_peer/eap_ikev2.c
@@ -83,18 +83,16 @@
 	if (data->ikev2.key_pad == NULL)
 		goto failed;
 	data->ikev2.key_pad_len = 21;
-	data->ikev2.IDr = os_malloc(identity_len);
+	data->ikev2.IDr = os_memdup(identity, identity_len);
 	if (data->ikev2.IDr == NULL)
 		goto failed;
-	os_memcpy(data->ikev2.IDr, identity, identity_len);
 	data->ikev2.IDr_len = identity_len;
 
 	password = eap_get_config_password(sm, &password_len);
 	if (password) {
-		data->ikev2.shared_secret = os_malloc(password_len);
+		data->ikev2.shared_secret = os_memdup(password, password_len);
 		if (data->ikev2.shared_secret == NULL)
 			goto failed;
-		os_memcpy(data->ikev2.shared_secret, password, password_len);
 		data->ikev2.shared_secret_len = password_len;
 	}
 
diff --git a/src/eap_peer/eap_leap.c b/src/eap_peer/eap_leap.c
index ff6fa4a..233b9ee 100644
--- a/src/eap_peer/eap_leap.c
+++ b/src/eap_peer/eap_leap.c
@@ -115,10 +115,14 @@
 	wpabuf_put_u8(resp, 0); /* unused */
 	wpabuf_put_u8(resp, LEAP_RESPONSE_LEN);
 	rpos = wpabuf_put(resp, LEAP_RESPONSE_LEN);
-	if (pwhash)
-		challenge_response(challenge, password, rpos);
-	else
-		nt_challenge_response(challenge, password, password_len, rpos);
+	if ((pwhash && challenge_response(challenge, password, rpos)) ||
+	    (!pwhash &&
+	     nt_challenge_response(challenge, password, password_len, rpos))) {
+		wpa_printf(MSG_DEBUG, "EAP-LEAP: Failed to derive response");
+		ret->ignore = TRUE;
+		wpabuf_free(resp);
+		return NULL;
+	}
 	os_memcpy(data->peer_response, rpos, LEAP_RESPONSE_LEN);
 	wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response",
 		    rpos, LEAP_RESPONSE_LEN);
@@ -239,7 +243,10 @@
 			return NULL;
 		}
 	}
-	challenge_response(data->ap_challenge, pw_hash_hash, expected);
+	if (challenge_response(data->ap_challenge, pw_hash_hash, expected)) {
+		ret->ignore = TRUE;
+		return NULL;
+	}
 
 	ret->methodState = METHOD_DONE;
 	ret->allowNotifications = FALSE;
diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c
index ce2227d..877495c 100644
--- a/src/eap_peer/eap_mschapv2.c
+++ b/src/eap_peer/eap_mschapv2.c
@@ -109,23 +109,21 @@
 		return NULL;
 
 	if (sm->peer_challenge) {
-		data->peer_challenge = os_malloc(MSCHAPV2_CHAL_LEN);
+		data->peer_challenge = os_memdup(sm->peer_challenge,
+						 MSCHAPV2_CHAL_LEN);
 		if (data->peer_challenge == NULL) {
 			eap_mschapv2_deinit(sm, data);
 			return NULL;
 		}
-		os_memcpy(data->peer_challenge, sm->peer_challenge,
-			  MSCHAPV2_CHAL_LEN);
 	}
 
 	if (sm->auth_challenge) {
-		data->auth_challenge = os_malloc(MSCHAPV2_CHAL_LEN);
+		data->auth_challenge = os_memdup(sm->auth_challenge,
+						 MSCHAPV2_CHAL_LEN);
 		if (data->auth_challenge == NULL) {
 			eap_mschapv2_deinit(sm, data);
 			return NULL;
 		}
-		os_memcpy(data->auth_challenge, sm->auth_challenge,
-			  MSCHAPV2_CHAL_LEN);
 	}
 
 	data->phase2 = sm->init_phase2;
@@ -567,11 +565,11 @@
 	if (pwhash) {
 		u8 new_password_hash[16];
 		if (nt_password_hash(new_password, new_password_len,
-				     new_password_hash))
+				     new_password_hash) ||
+		    nt_password_hash_encrypted_with_block(password,
+							  new_password_hash,
+							  cp->encr_hash))
 			goto fail;
-		nt_password_hash_encrypted_with_block(password,
-						      new_password_hash,
-						      cp->encr_hash);
 	} else {
 		if (old_nt_password_hash_encrypted_with_new_nt_password_hash(
 			    new_password, new_password_len,
diff --git a/src/eap_peer/eap_pax.c b/src/eap_peer/eap_pax.c
index a7012d2..3cef1c8 100644
--- a/src/eap_peer/eap_pax.c
+++ b/src/eap_peer/eap_pax.c
@@ -69,12 +69,11 @@
 		return NULL;
 	data->state = PAX_INIT;
 
-	data->cid = os_malloc(identity_len);
+	data->cid = os_memdup(identity, identity_len);
 	if (data->cid == NULL) {
 		eap_pax_deinit(sm, data);
 		return NULL;
 	}
-	os_memcpy(data->cid, identity, identity_len);
 	data->cid_len = identity_len;
 
 	os_memcpy(data->ak, password, EAP_PAX_AK_LEN);
diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c
index 2ff6076..34075b1 100644
--- a/src/eap_peer/eap_peap.c
+++ b/src/eap_peer/eap_peap.c
@@ -1272,12 +1272,11 @@
 	if (data->session_id == NULL || !data->phase2_success)
 		return NULL;
 
-	id = os_malloc(data->id_len);
+	id = os_memdup(data->session_id, data->id_len);
 	if (id == NULL)
 		return NULL;
 
 	*len = data->id_len;
-	os_memcpy(id, data->session_id, data->id_len);
 
 	return id;
 }
diff --git a/src/eap_peer/eap_proxy.h b/src/eap_peer/eap_proxy.h
index 7205fad..9d8e570 100644
--- a/src/eap_peer/eap_proxy.h
+++ b/src/eap_peer/eap_proxy.h
@@ -40,10 +40,16 @@
 int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen,
 			    int verbose);
 
-int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf,
-		       size_t *imsi_len);
+int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, int sim_num,
+		       char *imsi_buf, size_t *imsi_len);
 
 int eap_proxy_notify_config(struct eap_proxy_sm *sm,
 			    struct eap_peer_config *config);
 
+u8 * eap_proxy_get_eap_session_id(struct eap_proxy_sm *sm, size_t *len);
+
+u8 * eap_proxy_get_emsk(struct eap_proxy_sm *sm, size_t *len);
+
+void eap_proxy_sm_abort(struct eap_proxy_sm *sm);
+
 #endif /* EAP_PROXY_H */
diff --git a/src/eap_peer/eap_proxy_dummy.c b/src/eap_peer/eap_proxy_dummy.c
index 08009ca..2cc05c9 100644
--- a/src/eap_peer/eap_proxy_dummy.c
+++ b/src/eap_peer/eap_proxy_dummy.c
@@ -63,8 +63,8 @@
 }
 
 
-int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf,
-		       size_t *imsi_len)
+int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, int sim_num,
+		       char *imsi_buf, size_t *imsi_len)
 {
 	return -1;
 }
@@ -75,3 +75,20 @@
 {
 	return -1;
 }
+
+
+u8 * eap_proxy_get_eap_session_id(struct eap_proxy_sm *sm, size_t *len)
+{
+	return NULL;
+}
+
+
+u8 * eap_proxy_get_emsk(struct eap_proxy_sm *sm, size_t *len)
+{
+	return NULL;
+}
+
+
+void eap_proxy_sm_abort(struct eap_proxy_sm *sm)
+{
+}
diff --git a/src/eap_peer/eap_psk.c b/src/eap_peer/eap_psk.c
index ac18c15..eea9430 100644
--- a/src/eap_peer/eap_psk.c
+++ b/src/eap_peer/eap_psk.c
@@ -116,14 +116,13 @@
 	os_memcpy(data->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN);
 	os_free(data->id_s);
 	data->id_s_len = len - sizeof(*hdr1);
-	data->id_s = os_malloc(data->id_s_len);
+	data->id_s = os_memdup(hdr1 + 1, data->id_s_len);
 	if (data->id_s == NULL) {
 		wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory for "
 			   "ID_S (len=%lu)", (unsigned long) data->id_s_len);
 		ret->ignore = TRUE;
 		return NULL;
 	}
-	os_memcpy(data->id_s, (u8 *) (hdr1 + 1), data->id_s_len);
 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S",
 			  data->id_s, data->id_s_len);
 
@@ -273,13 +272,12 @@
 		    wpabuf_head(reqData), 5);
 	wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - cipher msg", msg, left);
 
-	decrypted = os_malloc(left);
+	decrypted = os_memdup(msg, left);
 	if (decrypted == NULL) {
 		ret->methodState = METHOD_DONE;
 		ret->decision = DECISION_FAIL;
 		return NULL;
 	}
-	os_memcpy(decrypted, msg, left);
 
 	if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce),
 				wpabuf_head(reqData),
@@ -425,12 +423,11 @@
 	if (data->state != PSK_DONE)
 		return NULL;
 
-	key = os_malloc(EAP_MSK_LEN);
+	key = os_memdup(data->msk, EAP_MSK_LEN);
 	if (key == NULL)
 		return NULL;
 
 	*len = EAP_MSK_LEN;
-	os_memcpy(key, data->msk, EAP_MSK_LEN);
 
 	return key;
 }
@@ -466,12 +463,11 @@
 	if (data->state != PSK_DONE)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
 
 	*len = EAP_EMSK_LEN;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 
 	return key;
 }
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
index 662347b..ec277ac 100644
--- a/src/eap_peer/eap_pwd.c
+++ b/src/eap_peer/eap_pwd.c
@@ -183,11 +183,10 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_MSK_LEN);
+	key = os_memdup(data->msk, EAP_MSK_LEN);
 	if (key == NULL)
 		return NULL;
 
-	os_memcpy(key, data->msk, EAP_MSK_LEN);
 	*len = EAP_MSK_LEN;
 
 	return key;
@@ -202,11 +201,10 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	id = os_malloc(1 + SHA256_MAC_LEN);
+	id = os_memdup(data->session_id, 1 + SHA256_MAC_LEN);
 	if (id == NULL)
 		return NULL;
 
-	os_memcpy(id, data->session_id, 1 + SHA256_MAC_LEN);
 	*len = 1 + SHA256_MAC_LEN;
 
 	return id;
diff --git a/src/eap_peer/eap_sake.c b/src/eap_peer/eap_sake.c
index 330febb..0a6ce25 100644
--- a/src/eap_peer/eap_sake.c
+++ b/src/eap_peer/eap_sake.c
@@ -85,12 +85,11 @@
 
 	identity = eap_get_config_identity(sm, &identity_len);
 	if (identity) {
-		data->peerid = os_malloc(identity_len);
+		data->peerid = os_memdup(identity, identity_len);
 		if (data->peerid == NULL) {
 			eap_sake_deinit(sm, data);
 			return NULL;
 		}
-		os_memcpy(data->peerid, identity, identity_len);
 		data->peerid_len = identity_len;
 	}
 
@@ -230,10 +229,9 @@
 	if (attr.serverid) {
 		wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-SAKE: SERVERID",
 				  attr.serverid, attr.serverid_len);
-		data->serverid = os_malloc(attr.serverid_len);
+		data->serverid = os_memdup(attr.serverid, attr.serverid_len);
 		if (data->serverid == NULL)
 			return NULL;
-		os_memcpy(data->serverid, attr.serverid, attr.serverid_len);
 		data->serverid_len = attr.serverid_len;
 	}
 
@@ -450,10 +448,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_MSK_LEN);
+	key = os_memdup(data->msk, EAP_MSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->msk, EAP_MSK_LEN);
 	*len = EAP_MSK_LEN;
 
 	return key;
@@ -490,10 +487,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 	*len = EAP_EMSK_LEN;
 
 	return key;
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
index 95ecdf7..25f592c 100644
--- a/src/eap_peer/eap_sim.c
+++ b/src/eap_peer/eap_sim.c
@@ -437,15 +437,14 @@
 
 	if (attr->next_reauth_id) {
 		os_free(data->reauth_id);
-		data->reauth_id = os_malloc(attr->next_reauth_id_len);
+		data->reauth_id = os_memdup(attr->next_reauth_id,
+					    attr->next_reauth_id_len);
 		if (data->reauth_id == NULL) {
 			wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for "
 				   "next reauth_id");
 			data->reauth_id_len = 0;
 			return -1;
 		}
-		os_memcpy(data->reauth_id, attr->next_reauth_id,
-			  attr->next_reauth_id_len);
 		data->reauth_id_len = attr->next_reauth_id_len;
 		wpa_hexdump_ascii(MSG_DEBUG,
 				  "EAP-SIM: (encr) AT_NEXT_REAUTH_ID",
@@ -640,14 +639,13 @@
 	}
 
 	os_free(data->ver_list);
-	data->ver_list = os_malloc(attr->version_list_len);
+	data->ver_list = os_memdup(attr->version_list, attr->version_list_len);
 	if (data->ver_list == NULL) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to allocate "
 			   "memory for version list");
 		return eap_sim_client_error(data, id,
 					    EAP_SIM_UNABLE_TO_PROCESS_PACKET);
 	}
-	os_memcpy(data->ver_list, attr->version_list, attr->version_list_len);
 	data->ver_list_len = attr->version_list_len;
 	pos = data->ver_list;
 	for (i = 0; i < data->ver_list_len / 2; i++) {
@@ -1186,12 +1184,11 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+	key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN);
 	if (key == NULL)
 		return NULL;
 
 	*len = EAP_SIM_KEYING_DATA_LEN;
-	os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
 
 	return key;
 }
@@ -1228,12 +1225,11 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
 
 	*len = EAP_EMSK_LEN;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 
 	return key;
 }
diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c
index ca2354f..c1820a4 100644
--- a/src/eap_peer/eap_tls.c
+++ b/src/eap_peer/eap_tls.c
@@ -338,12 +338,11 @@
 	if (data->key_data == NULL)
 		return NULL;
 
-	key = os_malloc(EAP_TLS_KEY_LEN);
+	key = os_memdup(data->key_data, EAP_TLS_KEY_LEN);
 	if (key == NULL)
 		return NULL;
 
 	*len = EAP_TLS_KEY_LEN;
-	os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
 
 	return key;
 }
@@ -357,12 +356,11 @@
 	if (data->key_data == NULL)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
 
 	*len = EAP_EMSK_LEN;
-	os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
 
 	return key;
 }
@@ -376,12 +374,11 @@
 	if (data->session_id == NULL)
 		return NULL;
 
-	id = os_malloc(data->id_len);
+	id = os_memdup(data->session_id, data->id_len);
 	if (id == NULL)
 		return NULL;
 
 	*len = data->id_len;
-	os_memcpy(id, data->session_id, data->id_len);
 
 	return id;
 }
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
index 0dcb9c1..b3d4aba 100644
--- a/src/eap_peer/eap_tls_common.c
+++ b/src/eap_peer/eap_tls_common.c
@@ -84,6 +84,14 @@
 		params->flags |= TLS_CONN_EXT_CERT_CHECK;
 	if (os_strstr(txt, "tls_ext_cert_check=0"))
 		params->flags &= ~TLS_CONN_EXT_CERT_CHECK;
+	if (os_strstr(txt, "tls_suiteb=1"))
+		params->flags |= TLS_CONN_SUITEB;
+	if (os_strstr(txt, "tls_suiteb=0"))
+		params->flags &= ~TLS_CONN_SUITEB;
+	if (os_strstr(txt, "tls_suiteb_no_ecdh=1"))
+		params->flags |= TLS_CONN_SUITEB_NO_ECDH;
+	if (os_strstr(txt, "tls_suiteb_no_ecdh=0"))
+		params->flags &= ~TLS_CONN_SUITEB_NO_ECDH;
 }
 
 
diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c
index 3354b2d..f18788c 100644
--- a/src/eap_peer/eap_ttls.c
+++ b/src/eap_peer/eap_ttls.c
@@ -624,12 +624,28 @@
 	os_memset(pos, 0, 24); /* LM-Response */
 	pos += 24;
 	if (pwhash) {
-		challenge_response(challenge, password, pos); /* NT-Response */
+		/* NT-Response */
+		if (challenge_response(challenge, password, pos)) {
+			wpa_printf(MSG_ERROR,
+				   "EAP-TTLS/MSCHAP: Failed derive password hash");
+			wpabuf_free(msg);
+			os_free(challenge);
+			return -1;
+		}
+
 		wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password hash",
 				password, 16);
 	} else {
-		nt_challenge_response(challenge, password, password_len,
-				      pos); /* NT-Response */
+		/* NT-Response */
+		if (nt_challenge_response(challenge, password, password_len,
+					  pos)) {
+			wpa_printf(MSG_ERROR,
+				   "EAP-TTLS/MSCHAP: Failed derive password");
+			wpabuf_free(msg);
+			os_free(challenge);
+			return -1;
+		}
+
 		wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password",
 				      password, password_len);
 	}
@@ -870,13 +886,12 @@
 {
 	wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message");
 	if (parse->eapdata == NULL) {
-		parse->eapdata = os_malloc(dlen);
+		parse->eapdata = os_memdup(dpos, dlen);
 		if (parse->eapdata == NULL) {
 			wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate "
 				   "memory for Phase 2 EAP data");
 			return -1;
 		}
-		os_memcpy(parse->eapdata, dpos, dlen);
 		parse->eap_len = dlen;
 	} else {
 		u8 *neweap = os_realloc(parse->eapdata, parse->eap_len + dlen);
@@ -1745,12 +1760,11 @@
 	if (data->key_data == NULL || !data->phase2_success)
 		return NULL;
 
-	key = os_malloc(EAP_TLS_KEY_LEN);
+	key = os_memdup(data->key_data, EAP_TLS_KEY_LEN);
 	if (key == NULL)
 		return NULL;
 
 	*len = EAP_TLS_KEY_LEN;
-	os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
 
 	return key;
 }
@@ -1764,12 +1778,11 @@
 	if (data->session_id == NULL || !data->phase2_success)
 		return NULL;
 
-	id = os_malloc(data->id_len);
+	id = os_memdup(data->session_id, data->id_len);
 	if (id == NULL)
 		return NULL;
 
 	*len = data->id_len;
-	os_memcpy(id, data->session_id, data->id_len);
 
 	return id;
 }
@@ -1783,12 +1796,11 @@
 	if (data->key_data == NULL)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
 
 	*len = EAP_EMSK_LEN;
-	os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
 
 	return key;
 }
diff --git a/src/eap_peer/ikev2.c b/src/eap_peer/ikev2.c
index ca6502e..7bd97b1 100644
--- a/src/eap_peer/ikev2.c
+++ b/src/eap_peer/ikev2.c
@@ -476,10 +476,9 @@
 	wpa_printf(MSG_DEBUG, "IKEV2: IDi ID Type %d", id_type);
 	wpa_hexdump_ascii(MSG_DEBUG, "IKEV2: IDi", idi, idi_len);
 	os_free(data->IDi);
-	data->IDi = os_malloc(idi_len);
+	data->IDi = os_memdup(idi, idi_len);
 	if (data->IDi == NULL)
 		return -1;
-	os_memcpy(data->IDi, idi, idi_len);
 	data->IDi_len = idi_len;
 	data->IDi_type = id_type;
 
diff --git a/src/eap_peer/tncc.c b/src/eap_peer/tncc.c
index 0c5caa7..a9bafe2 100644
--- a/src/eap_peer/tncc.c
+++ b/src/eap_peer/tncc.c
@@ -126,12 +126,10 @@
 
 	imc = tnc_imc[imcID];
 	os_free(imc->supported_types);
-	imc->supported_types =
-		os_malloc(typeCount * sizeof(TNC_MessageType));
+	imc->supported_types = os_memdup(supportedTypes,
+					 typeCount * sizeof(TNC_MessageType));
 	if (imc->supported_types == NULL)
 		return TNC_RESULT_FATAL;
-	os_memcpy(imc->supported_types, supportedTypes,
-		  typeCount * sizeof(TNC_MessageType));
 	imc->num_supported_types = typeCount;
 
 	return TNC_RESULT_SUCCESS;
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index 93eab62..c67fa82 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -132,6 +132,7 @@
 	size_t server_id_len;
 	int erp;
 	unsigned int tls_session_lifetime;
+	unsigned int tls_flags;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	u32 tls_test_flags;
diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h
index c90443d..3d6f8d5 100644
--- a/src/eap_server/eap_i.h
+++ b/src/eap_server/eap_i.h
@@ -211,6 +211,7 @@
 	Boolean try_initiate_reauth;
 	int erp;
 	unsigned int tls_session_lifetime;
+	unsigned int tls_flags;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	u32 tls_test_flags;
diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c
index 1b571cf..9706e25 100644
--- a/src/eap_server/eap_server.c
+++ b/src/eap_server/eap_server.c
@@ -1868,6 +1868,7 @@
 	sm->server_id_len = conf->server_id_len;
 	sm->erp = conf->erp;
 	sm->tls_session_lifetime = conf->tls_session_lifetime;
+	sm->tls_flags = conf->tls_flags;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	sm->tls_test_flags = conf->tls_test_flags;
diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c
index a8bb5ea..1750211 100644
--- a/src/eap_server/eap_server_aka.c
+++ b/src/eap_server/eap_server_aka.c
@@ -1261,10 +1261,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+	key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
 	*len = EAP_SIM_KEYING_DATA_LEN;
 	return key;
 }
@@ -1278,10 +1277,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 	*len = EAP_EMSK_LEN;
 	return key;
 }
diff --git a/src/eap_server/eap_server_eke.c b/src/eap_server/eap_server_eke.c
index 1eba8f5..71580bf 100644
--- a/src/eap_server/eap_server_eke.c
+++ b/src/eap_server/eap_server_eke.c
@@ -467,13 +467,12 @@
 
 	data->peerid_type = *pos++;
 	os_free(data->peerid);
-	data->peerid = os_malloc(end - pos);
+	data->peerid = os_memdup(pos, end - pos);
 	if (data->peerid == NULL) {
 		wpa_printf(MSG_INFO, "EAP-EKE: Failed to allocate memory for peerid");
 		eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 		return;
 	}
-	os_memcpy(data->peerid, pos, end - pos);
 	data->peerid_len = end - pos;
 	wpa_printf(MSG_DEBUG, "EAP-EKE: Peer IDType %u", data->peerid_type);
 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Peer Identity",
@@ -731,10 +730,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_MSK_LEN);
+	key = os_memdup(data->msk, EAP_MSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->msk, EAP_MSK_LEN);
 	*len = EAP_MSK_LEN;
 
 	return key;
@@ -749,10 +747,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 	*len = EAP_EMSK_LEN;
 
 	return key;
diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c
index f6d7e32..fa0342f 100644
--- a/src/eap_server/eap_server_fast.c
+++ b/src/eap_server/eap_server_fast.c
@@ -471,12 +471,11 @@
 		eap_fast_reset(sm, data);
 		return NULL;
 	}
-	data->srv_id = os_malloc(sm->eap_fast_a_id_len);
+	data->srv_id = os_memdup(sm->eap_fast_a_id, sm->eap_fast_a_id_len);
 	if (data->srv_id == NULL) {
 		eap_fast_reset(sm, data);
 		return NULL;
 	}
-	os_memcpy(data->srv_id, sm->eap_fast_a_id, sm->eap_fast_a_id_len);
 	data->srv_id_len = sm->eap_fast_a_id_len;
 
 	if (sm->eap_fast_a_id_info == NULL) {
diff --git a/src/eap_server/eap_server_gpsk.c b/src/eap_server/eap_server_gpsk.c
index 94e74ec..fb3d117 100644
--- a/src/eap_server/eap_server_gpsk.c
+++ b/src/eap_server/eap_server_gpsk.c
@@ -269,13 +269,12 @@
 		return;
 	}
 	os_free(data->id_peer);
-	data->id_peer = os_malloc(alen);
+	data->id_peer = os_memdup(pos, alen);
 	if (data->id_peer == NULL) {
 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Not enough memory to store "
 			   "%d-octet ID_Peer", alen);
 		return;
 	}
-	os_memcpy(data->id_peer, pos, alen);
 	data->id_peer_len = alen;
 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer",
 			  data->id_peer, data->id_peer_len);
@@ -575,10 +574,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_MSK_LEN);
+	key = os_memdup(data->msk, EAP_MSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->msk, EAP_MSK_LEN);
 	*len = EAP_MSK_LEN;
 
 	return key;
@@ -593,10 +591,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 	*len = EAP_EMSK_LEN;
 
 	return key;
@@ -618,10 +615,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	sid = os_malloc(data->id_len);
+	sid = os_memdup(data->session_id, data->id_len);
 	if (sid == NULL)
 		return NULL;
-	os_memcpy(sid, data->session_id, data->id_len);
 	*len = data->id_len;
 
 	return sid;
diff --git a/src/eap_server/eap_server_gtc.c b/src/eap_server/eap_server_gtc.c
index 193a851..fcccbcb 100644
--- a/src/eap_server/eap_server_gtc.c
+++ b/src/eap_server/eap_server_gtc.c
@@ -141,12 +141,11 @@
 		} else {
 			os_free(sm->identity);
 			sm->identity_len = pos2 - pos;
-			sm->identity = os_malloc(sm->identity_len);
+			sm->identity = os_memdup(pos, sm->identity_len);
 			if (sm->identity == NULL) {
 				data->state = FAILURE;
 				return;
 			}
-			os_memcpy(sm->identity, pos, sm->identity_len);
 		}
 
 		if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
diff --git a/src/eap_server/eap_server_ikev2.c b/src/eap_server/eap_server_ikev2.c
index 3a249d1..1833419 100644
--- a/src/eap_server/eap_server_ikev2.c
+++ b/src/eap_server/eap_server_ikev2.c
@@ -103,10 +103,9 @@
 	data->ikev2.proposal.encr = ENCR_AES_CBC;
 	data->ikev2.proposal.dh = DH_GROUP2_1024BIT_MODP;
 
-	data->ikev2.IDi = os_malloc(sm->server_id_len);
+	data->ikev2.IDi = os_memdup(sm->server_id, sm->server_id_len);
 	if (data->ikev2.IDi == NULL)
 		goto failed;
-	os_memcpy(data->ikev2.IDi, sm->server_id, sm->server_id_len);
 	data->ikev2.IDi_len = sm->server_id_len;
 
 	data->ikev2.get_shared_secret = eap_ikev2_get_shared_secret;
diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c
index 460cd9c..6c47bb6 100644
--- a/src/eap_server/eap_server_mschapv2.c
+++ b/src/eap_server/eap_server_mschapv2.c
@@ -71,13 +71,12 @@
 	}
 
 	if (sm->peer_challenge) {
-		data->peer_challenge = os_malloc(CHALLENGE_LEN);
+		data->peer_challenge = os_memdup(sm->peer_challenge,
+						 CHALLENGE_LEN);
 		if (data->peer_challenge == NULL) {
 			os_free(data);
 			return NULL;
 		}
-		os_memcpy(data->peer_challenge, sm->peer_challenge,
-			  CHALLENGE_LEN);
 	}
 
 	return data;
diff --git a/src/eap_server/eap_server_pax.c b/src/eap_server/eap_server_pax.c
index 782b8c3..3257789 100644
--- a/src/eap_server/eap_server_pax.c
+++ b/src/eap_server/eap_server_pax.c
@@ -327,13 +327,12 @@
 	}
 	data->cid_len = cid_len;
 	os_free(data->cid);
-	data->cid = os_malloc(data->cid_len);
+	data->cid = os_memdup(pos + 2, data->cid_len);
 	if (data->cid == NULL) {
 		wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for "
 			   "CID");
 		return;
 	}
-	os_memcpy(data->cid, pos + 2, data->cid_len);
 	pos += 2 + data->cid_len;
 	left -= 2 + data->cid_len;
 	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID",
diff --git a/src/eap_server/eap_server_psk.c b/src/eap_server/eap_server_psk.c
index 857d421..0eab893 100644
--- a/src/eap_server/eap_server_psk.c
+++ b/src/eap_server/eap_server_psk.c
@@ -236,13 +236,12 @@
 	left -= sizeof(*resp);
 
 	os_free(data->id_p);
-	data->id_p = os_malloc(left);
+	data->id_p = os_memdup(cpos, left);
 	if (data->id_p == NULL) {
 		wpa_printf(MSG_INFO, "EAP-PSK: Failed to allocate memory for "
 			   "ID_P");
 		return;
 	}
-	os_memcpy(data->id_p, cpos, left);
 	data->id_p_len = left;
 	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PSK: ID_P",
 			  data->id_p, data->id_p_len);
@@ -371,10 +370,9 @@
 	pos += 16;
 	left -= 16;
 
-	decrypted = os_malloc(left);
+	decrypted = os_memdup(pos, left);
 	if (decrypted == NULL)
 		return;
-	os_memcpy(decrypted, pos, left);
 
 	if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce),
 				wpabuf_head(respData), 22, decrypted, left,
@@ -450,10 +448,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_MSK_LEN);
+	key = os_memdup(data->msk, EAP_MSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->msk, EAP_MSK_LEN);
 	*len = EAP_MSK_LEN;
 
 	return key;
@@ -468,10 +465,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 	*len = EAP_EMSK_LEN;
 
 	return key;
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
index c60539f..68f0af9 100644
--- a/src/eap_server/eap_server_pwd.c
+++ b/src/eap_server/eap_server_pwd.c
@@ -1035,11 +1035,10 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_MSK_LEN);
+	key = os_memdup(data->msk, EAP_MSK_LEN);
 	if (key == NULL)
 		return NULL;
 
-	os_memcpy(key, data->msk, EAP_MSK_LEN);
 	*len = EAP_MSK_LEN;
 
 	return key;
@@ -1054,11 +1053,10 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
 
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 	*len = EAP_EMSK_LEN;
 
 	return key;
@@ -1087,11 +1085,10 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	id = os_malloc(1 + SHA256_MAC_LEN);
+	id = os_memdup(data->session_id, 1 + SHA256_MAC_LEN);
 	if (id == NULL)
 		return NULL;
 
-	os_memcpy(id, data->session_id, 1 + SHA256_MAC_LEN);
 	*len = 1 + SHA256_MAC_LEN;
 
 	return id;
diff --git a/src/eap_server/eap_server_sake.c b/src/eap_server/eap_server_sake.c
index 84d0e0b..66183f5 100644
--- a/src/eap_server/eap_server_sake.c
+++ b/src/eap_server/eap_server_sake.c
@@ -326,10 +326,9 @@
 	data->peerid = NULL;
 	data->peerid_len = 0;
 	if (attr.peerid) {
-		data->peerid = os_malloc(attr.peerid_len);
+		data->peerid = os_memdup(attr.peerid, attr.peerid_len);
 		if (data->peerid == NULL)
 			return;
-		os_memcpy(data->peerid, attr.peerid, attr.peerid_len);
 		data->peerid_len = attr.peerid_len;
 	}
 
@@ -460,10 +459,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_MSK_LEN);
+	key = os_memdup(data->msk, EAP_MSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->msk, EAP_MSK_LEN);
 	*len = EAP_MSK_LEN;
 
 	return key;
@@ -478,10 +476,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 	*len = EAP_EMSK_LEN;
 
 	return key;
diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c
index 3a6ed79..10637d4 100644
--- a/src/eap_server/eap_server_sim.c
+++ b/src/eap_server/eap_server_sim.c
@@ -787,10 +787,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+	key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
 	*len = EAP_SIM_KEYING_DATA_LEN;
 	return key;
 }
@@ -804,10 +803,9 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	key = os_malloc(EAP_EMSK_LEN);
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
 	if (key == NULL)
 		return NULL;
-	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 	*len = EAP_EMSK_LEN;
 	return key;
 }
diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c
index 6909695..3c9027b 100644
--- a/src/eap_server/eap_server_tls_common.c
+++ b/src/eap_server/eap_server_tls_common.c
@@ -47,7 +47,7 @@
 			    int verify_peer, int eap_type)
 {
 	u8 session_ctx[8];
-	unsigned int flags = 0;
+	unsigned int flags = sm->tls_flags;
 
 	if (sm->ssl_ctx == NULL) {
 		wpa_printf(MSG_ERROR, "TLS context not initialized - cannot use TLS-based EAP method");
diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c
index a53633f..b14996b 100644
--- a/src/eap_server/eap_server_ttls.c
+++ b/src/eap_server/eap_server_ttls.c
@@ -228,14 +228,13 @@
 		if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) {
 			wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message");
 			if (parse->eap == NULL) {
-				parse->eap = os_malloc(dlen);
+				parse->eap = os_memdup(dpos, dlen);
 				if (parse->eap == NULL) {
 					wpa_printf(MSG_WARNING, "EAP-TTLS: "
 						   "failed to allocate memory "
 						   "for Phase 2 EAP data");
 					goto fail;
 				}
-				os_memcpy(parse->eap, dpos, dlen);
 				parse->eap_len = dlen;
 			} else {
 				u8 *neweap = os_realloc(parse->eap,
@@ -372,7 +371,7 @@
 
 static struct wpabuf * eap_ttls_build_start(struct eap_sm *sm,
 					    struct eap_ttls_data *data, u8 id)
-{	
+{
 	struct wpabuf *req;
 
 	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, 1,
@@ -666,11 +665,14 @@
 	}
 	os_free(chal);
 
-	if (sm->user->password_hash)
-		challenge_response(challenge, sm->user->password, nt_response);
-	else
-		nt_challenge_response(challenge, sm->user->password,
-				      sm->user->password_len, nt_response);
+	if ((sm->user->password_hash &&
+	     challenge_response(challenge, sm->user->password, nt_response)) ||
+	    (!sm->user->password_hash &&
+	     nt_challenge_response(challenge, sm->user->password,
+				   sm->user->password_len, nt_response))) {
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
 
 	if (os_memcmp_const(nt_response, response + 2 + 24, 24) == 0) {
 		wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response");
@@ -1051,12 +1053,11 @@
 		}
 
 		os_free(sm->identity);
-		sm->identity = os_malloc(parse.user_name_len);
+		sm->identity = os_memdup(parse.user_name, parse.user_name_len);
 		if (sm->identity == NULL) {
 			eap_ttls_state(data, FAILURE);
 			goto done;
 		}
-		os_memcpy(sm->identity, parse.user_name, parse.user_name_len);
 		sm->identity_len = parse.user_name_len;
 		if (eap_user_get(sm, parse.user_name, parse.user_name_len, 1)
 		    != 0) {
diff --git a/src/eap_server/ikev2.c b/src/eap_server/ikev2.c
index 5385cd8..0e9210e 100644
--- a/src/eap_server/ikev2.c
+++ b/src/eap_server/ikev2.c
@@ -544,10 +544,9 @@
 		}
 		os_free(data->IDr);
 	}
-	data->IDr = os_malloc(idr_len);
+	data->IDr = os_memdup(idr, idr_len);
 	if (data->IDr == NULL)
 		return -1;
-	os_memcpy(data->IDr, idr, idr_len);
 	data->IDr_len = idr_len;
 	data->IDr_type = id_type;
 
@@ -1147,10 +1146,9 @@
 			return NULL;
 	} else {
 		os_free(data->shared_secret);
-		data->shared_secret = os_malloc(secret_len);
+		data->shared_secret = os_memdup(secret, secret_len);
 		if (data->shared_secret == NULL)
 			return NULL;
-		os_memcpy(data->shared_secret, secret, secret_len);
 		data->shared_secret_len = secret_len;
 	}
 
diff --git a/src/eap_server/tncs.c b/src/eap_server/tncs.c
index cfcbd3e..942a195 100644
--- a/src/eap_server/tncs.c
+++ b/src/eap_server/tncs.c
@@ -161,12 +161,10 @@
 	if (imv == NULL)
 		return TNC_RESULT_INVALID_PARAMETER;
 	os_free(imv->supported_types);
-	imv->supported_types =
-		os_malloc(typeCount * sizeof(TNC_MessageType));
+	imv->supported_types = os_memdup(supportedTypes,
+					 typeCount * sizeof(TNC_MessageType));
 	if (imv->supported_types == NULL)
 		return TNC_RESULT_FATAL;
-	os_memcpy(imv->supported_types, supportedTypes,
-		  typeCount * sizeof(TNC_MessageType));
 	imv->num_supported_types = typeCount;
 
 	return TNC_RESULT_SUCCESS;
diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c
index ff673bb..36074d3 100644
--- a/src/eapol_auth/eapol_auth_sm.c
+++ b/src/eapol_auth/eapol_auth_sm.c
@@ -848,6 +848,7 @@
 	eap_conf.server_id_len = eapol->conf.server_id_len;
 	eap_conf.erp = eapol->conf.erp;
 	eap_conf.tls_session_lifetime = eapol->conf.tls_session_lifetime;
+	eap_conf.tls_flags = eapol->conf.tls_flags;
 	sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf);
 	if (sm->eap == NULL) {
 		eapol_auth_free(sm);
@@ -1197,30 +1198,27 @@
 	dst->server_id = src->server_id;
 	dst->server_id_len = src->server_id_len;
 	if (src->eap_req_id_text) {
-		dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len);
+		dst->eap_req_id_text = os_memdup(src->eap_req_id_text,
+						 src->eap_req_id_text_len);
 		if (dst->eap_req_id_text == NULL)
 			return -1;
-		os_memcpy(dst->eap_req_id_text, src->eap_req_id_text,
-			  src->eap_req_id_text_len);
 		dst->eap_req_id_text_len = src->eap_req_id_text_len;
 	} else {
 		dst->eap_req_id_text = NULL;
 		dst->eap_req_id_text_len = 0;
 	}
 	if (src->pac_opaque_encr_key) {
-		dst->pac_opaque_encr_key = os_malloc(16);
+		dst->pac_opaque_encr_key = os_memdup(src->pac_opaque_encr_key,
+						     16);
 		if (dst->pac_opaque_encr_key == NULL)
 			goto fail;
-		os_memcpy(dst->pac_opaque_encr_key, src->pac_opaque_encr_key,
-			  16);
 	} else
 		dst->pac_opaque_encr_key = NULL;
 	if (src->eap_fast_a_id) {
-		dst->eap_fast_a_id = os_malloc(src->eap_fast_a_id_len);
+		dst->eap_fast_a_id = os_memdup(src->eap_fast_a_id,
+					       src->eap_fast_a_id_len);
 		if (dst->eap_fast_a_id == NULL)
 			goto fail;
-		os_memcpy(dst->eap_fast_a_id, src->eap_fast_a_id,
-			  src->eap_fast_a_id_len);
 		dst->eap_fast_a_id_len = src->eap_fast_a_id_len;
 	} else
 		dst->eap_fast_a_id = NULL;
@@ -1249,6 +1247,7 @@
 	dst->erp_send_reauth_start = src->erp_send_reauth_start;
 	dst->erp = src->erp;
 	dst->tls_session_lifetime = src->tls_session_lifetime;
+	dst->tls_flags = src->tls_flags;
 
 	return 0;
 
diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h
index e1974e4..44f3f31 100644
--- a/src/eapol_auth/eapol_auth_sm.h
+++ b/src/eapol_auth/eapol_auth_sm.h
@@ -28,6 +28,7 @@
 	char *erp_domain; /* a copy of this will be allocated */
 	int erp; /* Whether ERP is enabled on authentication server */
 	unsigned int tls_session_lifetime;
+	unsigned int tls_flags;
 	u8 *pac_opaque_encr_key;
 	u8 *eap_fast_a_id;
 	size_t eap_fast_a_id_len;
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index e727005..8e4f0e4 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -16,6 +16,7 @@
 #include "crypto/md5.h"
 #include "common/eapol_common.h"
 #include "eap_peer/eap.h"
+#include "eap_peer/eap_config.h"
 #include "eap_peer/eap_proxy.h"
 #include "eapol_supp_sm.h"
 
@@ -492,9 +493,20 @@
 #ifdef CONFIG_EAP_PROXY
 	if (sm->use_eap_proxy) {
 		if (eap_proxy_key_available(sm->eap_proxy)) {
+			u8 *session_id, *emsk;
+			size_t session_id_len, emsk_len;
+
 			/* New key received - clear IEEE 802.1X EAPOL-Key replay
 			 * counter */
 			sm->replay_counter_valid = FALSE;
+
+			session_id = eap_proxy_get_eap_session_id(
+				sm->eap_proxy, &session_id_len);
+			emsk = eap_proxy_get_emsk(sm->eap_proxy, &emsk_len);
+			if (sm->config->erp && session_id && emsk)
+				eap_peer_erp_init(sm->eap, session_id,
+						  session_id_len, emsk,
+						  emsk_len);
 		}
 		return;
 	}
@@ -899,6 +911,9 @@
 	wpabuf_free(sm->eapReqData);
 	sm->eapReqData = NULL;
 	eap_sm_abort(sm->eap);
+#ifdef CONFIG_EAP_PROXY
+	eap_proxy_sm_abort(sm->eap_proxy);
+#endif /* CONFIG_EAP_PROXY */
 }
 
 
@@ -2050,6 +2065,7 @@
 #ifdef CONFIG_EAP_PROXY
 	eapol_sm_eap_proxy_cb,
 	eapol_sm_eap_proxy_notify_sim_status,
+	eapol_sm_get_eap_proxy_imsi,
 #endif /* CONFIG_EAP_PROXY */
 	eapol_sm_set_anon_id
 };
@@ -2158,11 +2174,13 @@
 
 
 #ifdef CONFIG_EAP_PROXY
-int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len)
+int eapol_sm_get_eap_proxy_imsi(void *ctx, int sim_num, char *imsi, size_t *len)
 {
+	struct eapol_sm *sm = ctx;
+
 	if (sm->eap_proxy == NULL)
 		return -1;
-	return eap_proxy_get_imsi(sm->eap_proxy, imsi, len);
+	return eap_proxy_get_imsi(sm->eap_proxy, sim_num, imsi, len);
 }
 #endif /* CONFIG_EAP_PROXY */
 
@@ -2195,3 +2213,33 @@
 	eap_peer_finish(sm->eap, (const struct eap_hdr *) buf, len);
 #endif /* CONFIG_ERP */
 }
+
+
+int eapol_sm_update_erp_next_seq_num(struct eapol_sm *sm, u16 next_seq_num)
+{
+#ifdef CONFIG_ERP
+	if (!sm)
+		return -1;
+	return eap_peer_update_erp_next_seq_num(sm->eap, next_seq_num);
+#else /* CONFIG_ERP */
+	return -1;
+#endif /* CONFIG_ERP */
+}
+
+
+int eapol_sm_get_erp_info(struct eapol_sm *sm, struct eap_peer_config *config,
+			  const u8 **username, size_t *username_len,
+			  const u8 **realm, size_t *realm_len,
+			  u16 *erp_next_seq_num, const u8 **rrk,
+			  size_t *rrk_len)
+{
+#ifdef CONFIG_ERP
+	if (!sm)
+		return -1;
+	return eap_peer_get_erp_info(sm->eap, config, username, username_len,
+				     realm, realm_len, erp_next_seq_num, rrk,
+				     rrk_len);
+#else /* CONFIG_ERP */
+	return -1;
+#endif /* CONFIG_ERP */
+}
diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h
index cb06e9a..a25c799 100644
--- a/src/eapol_supp/eapol_supp_sm.h
+++ b/src/eapol_supp/eapol_supp_sm.h
@@ -339,7 +339,15 @@
 struct wpabuf * eapol_sm_build_erp_reauth_start(struct eapol_sm *sm);
 void eapol_sm_process_erp_finish(struct eapol_sm *sm, const u8 *buf,
 				 size_t len);
-int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len);
+int eapol_sm_get_eap_proxy_imsi(void *ctx, int sim_num, char *imsi,
+				size_t *len);
+int eapol_sm_update_erp_next_seq_num(struct eapol_sm *sm, u16 next_seq_num);
+int eapol_sm_get_erp_info(struct eapol_sm *sm, struct eap_peer_config *config,
+			  const u8 **username, size_t *username_len,
+			  const u8 **realm, size_t *realm_len,
+			  u16 *erp_next_seq_num, const u8 **rrk,
+			  size_t *rrk_len);
+
 #else /* IEEE8021X_EAPOL */
 static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
 {
@@ -458,6 +466,19 @@
 					       const u8 *buf, size_t len)
 {
 }
+static inline int eapol_sm_update_erp_next_seq_num(struct eapol_sm *sm,
+						   u16 next_seq_num)
+{
+	return -1;
+}
+static inline int
+eapol_sm_get_erp_info(struct eapol_sm *sm, struct eap_peer_config *config,
+		      const u8 **username, size_t *username_len,
+		      const u8 **realm, size_t *realm_len,
+		      u16 *erp_next_seq_num, const u8 **rrk, size_t *rrk_len)
+{
+	return -1;
+}
 #endif /* IEEE8021X_EAPOL */
 
 #endif /* EAPOL_SUPP_SM_H */
diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c
index a7a300e..65b4906 100644
--- a/src/l2_packet/l2_packet_linux.c
+++ b/src/l2_packet/l2_packet_linux.c
@@ -96,6 +96,9 @@
 		   const u8 *buf, size_t len)
 {
 	int ret;
+
+	if (TEST_FAIL())
+		return -1;
 	if (l2 == NULL)
 		return -1;
 	if (l2->l2_hdr) {
@@ -458,6 +461,9 @@
 {
 	const struct sock_fprog *sock_filter;
 
+	if (TEST_FAIL())
+		return -1;
+
 	switch (type) {
 	case L2_PACKET_FILTER_DHCP:
 		sock_filter = &dhcp_sock_filter;
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index 14d6279..1a31ef2 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -1010,8 +1010,16 @@
 	}
 	p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
 
-	if (p2p->find_type == P2P_FIND_PROGRESSIVE &&
-	    (freq = p2p_get_next_prog_freq(p2p)) > 0) {
+	if (p2p->find_pending_full &&
+	    (p2p->find_type == P2P_FIND_PROGRESSIVE ||
+	     p2p->find_type == P2P_FIND_START_WITH_FULL)) {
+		type = P2P_SCAN_FULL;
+		p2p_dbg(p2p, "Starting search (pending full scan)");
+		p2p->find_pending_full = 0;
+	} else if ((p2p->find_type == P2P_FIND_PROGRESSIVE &&
+	    (freq = p2p_get_next_prog_freq(p2p)) > 0) ||
+	    (p2p->find_type == P2P_FIND_START_WITH_FULL &&
+	     (freq = p2p->find_specified_freq) > 0)) {
 		type = P2P_SCAN_SOCIAL_PLUS_ONE;
 		p2p_dbg(p2p, "Starting search (+ freq %u)", freq);
 	} else {
@@ -1173,12 +1181,11 @@
 
 	p2p_free_req_dev_types(p2p);
 	if (req_dev_types && num_req_dev_types) {
-		p2p->req_dev_types = os_malloc(num_req_dev_types *
+		p2p->req_dev_types = os_memdup(req_dev_types,
+					       num_req_dev_types *
 					       WPS_DEV_TYPE_LEN);
 		if (p2p->req_dev_types == NULL)
 			return -1;
-		os_memcpy(p2p->req_dev_types, req_dev_types,
-			  num_req_dev_types * WPS_DEV_TYPE_LEN);
 		p2p->num_req_dev_types = num_req_dev_types;
 	}
 
@@ -1235,7 +1242,12 @@
 		p2p->pending_listen_freq = 0;
 	}
 	p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+	p2p->find_pending_full = 0;
 	p2p->find_type = type;
+	if (freq != 2412 && freq != 2437 && freq != 2462 && freq != 60480)
+		p2p->find_specified_freq = freq;
+	else
+		p2p->find_specified_freq = 0;
 	p2p_device_clear_reported(p2p);
 	os_memset(p2p->sd_query_no_ack, 0, ETH_ALEN);
 	p2p_set_state(p2p, P2P_SEARCH);
@@ -1251,7 +1263,7 @@
 		if (freq > 0) {
 			/*
 			 * Start with the specified channel and then move to
-			 * social channels only scans.
+			 * scans for social channels and this specific channel.
 			 */
 			res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx,
 						 P2P_SCAN_SPECIFIC, freq,
@@ -1280,6 +1292,9 @@
 	if (res != 0 && p2p->p2p_scan_running) {
 		p2p_dbg(p2p, "Failed to start p2p_scan - another p2p_scan was already running");
 		/* wait for the previous p2p_scan to complete */
+		if (type == P2P_FIND_PROGRESSIVE ||
+		    (type == P2P_FIND_START_WITH_FULL && freq == 0))
+			p2p->find_pending_full = 1;
 		res = 0; /* do not report failure */
 	} else if (res != 0) {
 		p2p_dbg(p2p, "Failed to start p2p_scan");
@@ -2993,6 +3008,7 @@
 	wpabuf_free(p2p->wfd_dev_info);
 	wpabuf_free(p2p->wfd_assoc_bssid);
 	wpabuf_free(p2p->wfd_coupled_sink_info);
+	wpabuf_free(p2p->wfd_r2_dev_info);
 #endif /* CONFIG_WIFI_DISPLAY */
 
 	eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
@@ -4817,11 +4833,10 @@
 	struct p2p_channel *n;
 
 	if (pref_chan) {
-		n = os_malloc(num_pref_chan * sizeof(struct p2p_channel));
+		n = os_memdup(pref_chan,
+			      num_pref_chan * sizeof(struct p2p_channel));
 		if (n == NULL)
 			return -1;
-		os_memcpy(n, pref_chan,
-			  num_pref_chan * sizeof(struct p2p_channel));
 	} else
 		n = NULL;
 
@@ -5143,6 +5158,20 @@
 }
 
 
+int p2p_set_wfd_r2_dev_info(struct p2p_data *p2p, const struct wpabuf *elem)
+{
+	wpabuf_free(p2p->wfd_r2_dev_info);
+	if (elem) {
+		p2p->wfd_r2_dev_info = wpabuf_dup(elem);
+		if (p2p->wfd_r2_dev_info == NULL)
+			return -1;
+	} else
+		p2p->wfd_r2_dev_info = NULL;
+
+	return 0;
+}
+
+
 int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem)
 {
 	wpabuf_free(p2p->wfd_assoc_bssid);
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 70d3a90..fac5ce0 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -2266,6 +2266,7 @@
 int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie);
 int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie);
 int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem);
+int p2p_set_wfd_r2_dev_info(struct p2p_data *p2p, const struct wpabuf *elem);
 int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem);
 int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p,
 				  const struct wpabuf *elem);
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index 051b4e3..16c28a0 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -367,6 +367,8 @@
 		return NULL;
 	if (group->p2p->wfd_dev_info)
 		wpabuf_put_buf(wfd_subelems, group->p2p->wfd_dev_info);
+	if (group->p2p->wfd_r2_dev_info)
+		wpabuf_put_buf(wfd_subelems, group->p2p->wfd_r2_dev_info);
 	if (group->p2p->wfd_assoc_bssid)
 		wpabuf_put_buf(wfd_subelems,
 			       group->p2p->wfd_assoc_bssid);
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index ce69932..6a4d751 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -437,9 +437,11 @@
 	int inv_persistent;
 
 	enum p2p_discovery_type find_type;
+	int find_specified_freq;
 	unsigned int last_p2p_find_timeout;
 	u8 last_prog_scan_class;
 	u8 last_prog_scan_chan;
+	unsigned int find_pending_full:1;
 	int p2p_scan_running;
 	enum p2p_after_scan {
 		P2P_AFTER_SCAN_NOTHING,
@@ -545,6 +547,7 @@
 	struct wpabuf *wfd_dev_info;
 	struct wpabuf *wfd_assoc_bssid;
 	struct wpabuf *wfd_coupled_sink_info;
+	struct wpabuf *wfd_r2_dev_info;
 #endif /* CONFIG_WIFI_DISPLAY */
 
 	u16 authorized_oob_dev_pw_id;
diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c
index d2fb4b5..b9e753f 100644
--- a/src/p2p/p2p_sd.c
+++ b/src/p2p/p2p_sd.c
@@ -625,6 +625,7 @@
 	u8 dialog_token;
 	size_t frag_len, max_len;
 	int more = 0;
+	unsigned int wait_time = 200;
 
 	wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Request", data, len);
 	if (len < 1)
@@ -677,12 +678,13 @@
 		p2p_dbg(p2p, "All fragments of SD response sent");
 		wpabuf_free(p2p->sd_resp);
 		p2p->sd_resp = NULL;
+		wait_time = 0; /* no more SD frames in the sequence */
 	}
 
 	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
 	if (p2p_send_action(p2p, rx_freq, sa, p2p->cfg->dev_addr,
 			    p2p->cfg->dev_addr,
-			    wpabuf_head(resp), wpabuf_len(resp), 200) < 0)
+			    wpabuf_head(resp), wpabuf_len(resp), wait_time) < 0)
 		p2p_dbg(p2p, "Failed to send Action frame");
 
 	wpabuf_free(resp);
diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c
index 3f9e53d..cad0292 100644
--- a/src/pae/ieee802_1x_kay.c
+++ b/src/pae/ieee802_1x_kay.c
@@ -532,6 +532,7 @@
 		ieee802_1x_delete_receive_sa(participant->kay, psa);
 
 	dl_list_del(&psc->list);
+	secy_delete_receive_sc(participant->kay, psc);
 	os_free(psc);
 }
 
@@ -632,6 +633,8 @@
 	struct receive_sc *rxsc;
 
 	peer = ieee802_1x_kay_get_potential_peer(participant, mi);
+	if (!peer)
+		return NULL;
 
 	rxsc = ieee802_1x_kay_init_receive_sc(&participant->current_peer_sci);
 	if (!rxsc)
@@ -961,21 +964,19 @@
 		body_len = get_mka_param_body_len(hdr);
 		body_type = get_mka_param_body_type(hdr);
 
-		if (body_type != MKA_LIVE_PEER_LIST &&
-		    body_type != MKA_POTENTIAL_PEER_LIST)
-			continue;
-
-		ieee802_1x_mka_dump_peer_body(
-			(struct ieee802_1x_mka_peer_body *)pos);
-
-		if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) {
+		if (left_len < (MKA_HDR_LEN + MKA_ALIGN_LENGTH(body_len) + DEFAULT_ICV_LEN)) {
 			wpa_printf(MSG_ERROR,
 				   "KaY: MKA Peer Packet Body Length (%zu bytes) is less than the Parameter Set Header Length (%zu bytes) + the Parameter Set Body Length (%zu bytes) + %d bytes of ICV",
 				   left_len, MKA_HDR_LEN,
-				   body_len, DEFAULT_ICV_LEN);
-			continue;
+				   MKA_ALIGN_LENGTH(body_len),
+				   DEFAULT_ICV_LEN);
+			return FALSE;
 		}
 
+		if (body_type != MKA_LIVE_PEER_LIST &&
+		    body_type != MKA_POTENTIAL_PEER_LIST)
+			continue;
+
 		if ((body_len % 16) != 0) {
 			wpa_printf(MSG_ERROR,
 				   "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets",
@@ -983,6 +984,9 @@
 			continue;
 		}
 
+		ieee802_1x_mka_dump_peer_body(
+			(struct ieee802_1x_mka_peer_body *)pos);
+
 		for (i = 0; i < body_len;
 		     i += sizeof(struct ieee802_1x_mka_peer_id)) {
 			const struct ieee802_1x_mka_peer_id *peer_mi;
@@ -2363,7 +2367,6 @@
 				if (sci_equal(&rxsc->sci, &peer->sci)) {
 					ieee802_1x_kay_deinit_receive_sc(
 						participant, rxsc);
-					secy_delete_receive_sc(kay, rxsc);
 				}
 			}
 			dl_list_del(&peer->list);
@@ -2546,6 +2549,7 @@
 	dl_list_for_each_safe(psa, tmp, &psc->sa_list, struct transmit_sa, list)
 		ieee802_1x_delete_transmit_sa(participant->kay, psa);
 
+	secy_delete_transmit_sc(participant->kay, psc);
 	os_free(psc);
 }
 
@@ -3015,7 +3019,7 @@
 				   "KaY: MKA Peer Packet Body Length (%zu bytes) is less than the Parameter Set Header Length (%zu bytes) + the Parameter Set Body Length (%zu bytes) + %d bytes of ICV",
 				   left_len, MKA_HDR_LEN,
 				   body_len, DEFAULT_ICV_LEN);
-			continue;
+			return -1;
 		}
 
 		if (handled[body_type])
@@ -3097,6 +3101,7 @@
 	kay = os_zalloc(sizeof(*kay));
 	if (!kay) {
 		wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
+		os_free(ctx);
 		return NULL;
 	}
 
@@ -3131,10 +3136,8 @@
 	dl_list_init(&kay->participant_list);
 
 	if (policy != DO_NOT_SECURE &&
-	    secy_get_capability(kay, &kay->macsec_capable) < 0) {
-		os_free(kay);
-		return NULL;
-	}
+	    secy_get_capability(kay, &kay->macsec_capable) < 0)
+		goto error;
 
 	if (policy == DO_NOT_SECURE ||
 	    kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) {
@@ -3161,16 +3164,17 @@
 	wpa_printf(MSG_DEBUG, "KaY: state machine created");
 
 	/* Initialize the SecY must be prio to CP, as CP will control SecY */
-	secy_init_macsec(kay);
+	if (secy_init_macsec(kay) < 0) {
+		wpa_printf(MSG_DEBUG, "KaY: Could not initialize MACsec");
+		goto error;
+	}
 
 	wpa_printf(MSG_DEBUG, "KaY: secy init macsec done");
 
 	/* init CP */
 	kay->cp = ieee802_1x_cp_sm_init(kay);
-	if (kay->cp == NULL) {
-		ieee802_1x_kay_deinit(kay);
-		return NULL;
-	}
+	if (kay->cp == NULL)
+		goto error;
 
 	if (policy == DO_NOT_SECURE) {
 		ieee802_1x_cp_connect_authenticated(kay->cp);
@@ -3181,12 +3185,15 @@
 		if (kay->l2_mka == NULL) {
 			wpa_printf(MSG_WARNING,
 				   "KaY: Failed to initialize L2 packet processing for MKA packet");
-			ieee802_1x_kay_deinit(kay);
-			return NULL;
+			goto error;
 		}
 	}
 
 	return kay;
+
+error:
+	ieee802_1x_kay_deinit(kay);
+	return NULL;
 }
 
 
@@ -3433,10 +3440,8 @@
 		rxsc = dl_list_entry(participant->rxsc_list.next,
 				     struct receive_sc, list);
 		ieee802_1x_kay_deinit_receive_sc(participant, rxsc);
-		secy_delete_receive_sc(kay, rxsc);
 	}
 	ieee802_1x_kay_deinit_transmit_sc(participant, participant->txsc);
-	secy_delete_transmit_sc(kay, participant->txsc);
 
 	os_memset(&participant->cak, 0, sizeof(participant->cak));
 	os_memset(&participant->kek, 0, sizeof(participant->kek));
diff --git a/src/radius/radius.c b/src/radius/radius.c
index 9ddc19b..fc98ad6 100644
--- a/src/radius/radius.c
+++ b/src/radius/radius.c
@@ -631,6 +631,9 @@
 	size_t buf_needed;
 	struct radius_attr_hdr *attr;
 
+	if (TEST_FAIL())
+		return NULL;
+
 	if (data_len > RADIUS_MAX_ATTR_LEN) {
 		wpa_printf(MSG_ERROR, "radius_msg_add_attr: too long attribute (%lu bytes)",
 		       (unsigned long) data_len);
@@ -960,10 +963,9 @@
 			}
 
 			len = vhdr->vendor_length - sizeof(*vhdr);
-			data = os_malloc(len);
+			data = os_memdup(pos + sizeof(*vhdr), len);
 			if (data == NULL)
 				return NULL;
-			os_memcpy(data, pos + sizeof(*vhdr), len);
 			if (alen)
 				*alen = len;
 			return data;
@@ -1040,12 +1042,11 @@
 		return NULL;
 	}
 
-	res = os_malloc(plain[0]);
+	res = os_memdup(plain + 1, plain[0]);
 	if (res == NULL) {
 		os_free(plain);
 		return NULL;
 	}
-	os_memcpy(res, plain + 1, plain[0]);
 	if (reslen)
 		*reslen = plain[0];
 	os_free(plain);
@@ -1594,10 +1595,9 @@
 		goto out;
 
 	/* alloc writable memory for decryption */
-	buf = os_malloc(fdlen);
+	buf = os_memdup(fdata, fdlen);
 	if (buf == NULL)
 		goto out;
-	os_memcpy(buf, fdata, fdlen);
 	buflen = fdlen;
 
 	/* init pointers */
@@ -1684,12 +1684,11 @@
 	dst->count = 0;
 
 	for (i = 0; i < src->count; i++) {
-		dst->attr[i].data = os_malloc(src->attr[i].len);
+		dst->attr[i].data = os_memdup(src->attr[i].data,
+					      src->attr[i].len);
 		if (dst->attr[i].data == NULL)
 			break;
 		dst->count++;
-		os_memcpy(dst->attr[i].data, src->attr[i].data,
-			  src->attr[i].len);
 		dst->attr[i].len = src->attr[i].len;
 	}
 
diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c
index 8a3d7e0..ed24c19 100644
--- a/src/radius/radius_das.c
+++ b/src/radius/radius_das.c
@@ -373,13 +373,12 @@
 	os_memcpy(&das->client_addr, conf->client_addr,
 		  sizeof(das->client_addr));
 
-	das->shared_secret = os_malloc(conf->shared_secret_len);
+	das->shared_secret = os_memdup(conf->shared_secret,
+				       conf->shared_secret_len);
 	if (das->shared_secret == NULL) {
 		radius_das_deinit(das);
 		return NULL;
 	}
-	os_memcpy(das->shared_secret, conf->shared_secret,
-		  conf->shared_secret_len);
 	das->shared_secret_len = conf->shared_secret_len;
 
 	das->sock = radius_das_open_socket(conf->port);
diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
index e8bef45..c76bb22 100644
--- a/src/radius/radius_server.c
+++ b/src/radius/radius_server.c
@@ -272,6 +272,8 @@
 
 	unsigned int tls_session_lifetime;
 
+	unsigned int tls_flags;
+
 	/**
 	 * wps - Wi-Fi Protected Setup context
 	 *
@@ -662,14 +664,14 @@
 
 	sess->username = os_malloc(user_len * 4 + 1);
 	if (sess->username == NULL) {
-		radius_server_session_free(data, sess);
+		radius_server_session_remove(data, sess);
 		return NULL;
 	}
 	printf_encode(sess->username, user_len * 4 + 1, user, user_len);
 
 	sess->nas_ip = os_strdup(from_addr);
 	if (sess->nas_ip == NULL) {
-		radius_server_session_free(data, sess);
+		radius_server_session_remove(data, sess);
 		return NULL;
 	}
 
@@ -696,13 +698,14 @@
 	eap_conf.server_id_len = os_strlen(data->server_id);
 	eap_conf.erp = data->erp;
 	eap_conf.tls_session_lifetime = data->tls_session_lifetime;
+	eap_conf.tls_flags = data->tls_flags;
 	radius_server_testing_options(sess, &eap_conf);
 	sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb,
 				       &eap_conf);
 	if (sess->eap == NULL) {
 		RADIUS_DEBUG("Failed to initialize EAP state machine for the "
 			     "new session");
-		radius_server_session_free(data, sess);
+		radius_server_session_remove(data, sess);
 		return NULL;
 	}
 	sess->eap_if = eap_get_interface(sess->eap);
@@ -1754,6 +1757,7 @@
 	data->erp = conf->erp;
 	data->erp_domain = conf->erp_domain;
 	data->tls_session_lifetime = conf->tls_session_lifetime;
+	data->tls_flags = conf->tls_flags;
 
 	if (conf->subscr_remediation_url) {
 		data->subscr_remediation_url =
diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h
index 7a25802..996f00e 100644
--- a/src/radius/radius_server.h
+++ b/src/radius/radius_server.h
@@ -172,6 +172,8 @@
 
 	unsigned int tls_session_lifetime;
 
+	unsigned int tls_flags;
+
 	/**
 	 * wps - Wi-Fi Protected Setup context
 	 *
diff --git a/src/rsn_supp/Makefile b/src/rsn_supp/Makefile
index d5e61fe..c2d81f2 100644
--- a/src/rsn_supp/Makefile
+++ b/src/rsn_supp/Makefile
@@ -10,7 +10,6 @@
 
 CFLAGS += -DCONFIG_IEEE80211W
 CFLAGS += -DCONFIG_IEEE80211R
-CFLAGS += -DCONFIG_PEERKEY
 CFLAGS += -DCONFIG_TDLS
 CFLAGS += -DCONFIG_WNM
 CFLAGS += -DIEEE8021X_EAPOL
@@ -18,7 +17,6 @@
 LIB_OBJS= \
 	pmksa_cache.o \
 	wpa_ft.o \
-	peerkey.o \
 	tdls.o \
 	preauth.o \
 	wpa.o \
diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c
deleted file mode 100644
index ce338f8..0000000
--- a/src/rsn_supp/peerkey.c
+++ /dev/null
@@ -1,1166 +0,0 @@
-/*
- * WPA Supplicant - PeerKey for Direct Link Setup (DLS)
- * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-
-#ifdef CONFIG_PEERKEY
-
-#include "common.h"
-#include "eloop.h"
-#include "crypto/sha1.h"
-#include "crypto/sha256.h"
-#include "crypto/random.h"
-#include "common/ieee802_11_defs.h"
-#include "wpa.h"
-#include "wpa_i.h"
-#include "wpa_ie.h"
-#include "peerkey.h"
-
-
-static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len)
-{
-	os_memcpy(pos, ie, ie_len);
-	return pos + ie_len;
-}
-
-
-static u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len)
-{
-	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
-	*pos++ = RSN_SELECTOR_LEN + data_len;
-	RSN_SELECTOR_PUT(pos, kde);
-	pos += RSN_SELECTOR_LEN;
-	os_memcpy(pos, data, data_len);
-	pos += data_len;
-	return pos;
-}
-
-
-static void wpa_supplicant_smk_timeout(void *eloop_ctx, void *timeout_ctx)
-{
-#if 0
-	struct wpa_sm *sm = eloop_ctx;
-	struct wpa_peerkey *peerkey = timeout_ctx;
-#endif
-	/* TODO: time out SMK and any STK that was generated using this SMK */
-}
-
-
-static void wpa_supplicant_peerkey_free(struct wpa_sm *sm,
-					struct wpa_peerkey *peerkey)
-{
-	eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey);
-	os_free(peerkey);
-}
-
-
-static int wpa_supplicant_send_smk_error(struct wpa_sm *sm, const u8 *dst,
-					 const u8 *peer,
-					 u16 mui, u16 error_type, int ver)
-{
-	size_t rlen;
-	struct wpa_eapol_key *err;
-	struct rsn_error_kde error;
-	u8 *rbuf, *pos, *mic;
-	size_t kde_len, mic_len = 16;
-	u16 key_info;
-
-	kde_len = 2 + RSN_SELECTOR_LEN + sizeof(error);
-	if (peer)
-		kde_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN;
-
-	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
-				  NULL, sizeof(*err) + mic_len + 2 + kde_len,
-				  &rlen, (void *) &err);
-	if (rbuf == NULL)
-		return -1;
-	mic = (u8 *) (err + 1);
-
-	err->type = EAPOL_KEY_TYPE_RSN;
-	key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
-		WPA_KEY_INFO_SECURE | WPA_KEY_INFO_ERROR |
-		WPA_KEY_INFO_REQUEST;
-	WPA_PUT_BE16(err->key_info, key_info);
-	WPA_PUT_BE16(err->key_length, 0);
-	os_memcpy(err->replay_counter, sm->request_counter,
-		  WPA_REPLAY_COUNTER_LEN);
-	inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
-
-	WPA_PUT_BE16(mic + mic_len, (u16) kde_len);
-	pos = mic + mic_len + 2;
-
-	if (peer) {
-		/* Peer MAC Address KDE */
-		pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN);
-	}
-
-	/* Error KDE */
-	error.mui = host_to_be16(mui);
-	error.error_type = host_to_be16(error_type);
-	wpa_add_kde(pos, RSN_KEY_DATA_ERROR, (u8 *) &error, sizeof(error));
-
-	if (peer) {
-		wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error (peer "
-			   MACSTR " mui %d error_type %d)",
-			   MAC2STR(peer), mui, error_type);
-	} else {
-		wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error "
-			   "(mui %d error_type %d)", mui, error_type);
-	}
-
-	wpa_eapol_key_send(sm, &sm->ptk, ver, dst, ETH_P_EAPOL, rbuf, rlen,
-			   mic);
-
-	return 0;
-}
-
-
-static int wpa_supplicant_send_smk_m3(struct wpa_sm *sm,
-				      const unsigned char *src_addr,
-				      const struct wpa_eapol_key *key,
-				      int ver, struct wpa_peerkey *peerkey)
-{
-	size_t rlen;
-	struct wpa_eapol_key *reply;
-	u8 *rbuf, *pos, *mic;
-	size_t kde_len, mic_len = 16;
-	u16 key_info;
-
-	/* KDEs: Peer RSN IE, Initiator MAC Address, Initiator Nonce */
-	kde_len = peerkey->rsnie_p_len +
-		2 + RSN_SELECTOR_LEN + ETH_ALEN +
-		2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN;
-
-	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
-				  NULL, sizeof(*reply) + mic_len + 2 + kde_len,
-				  &rlen, (void *) &reply);
-	if (rbuf == NULL)
-		return -1;
-
-	reply->type = EAPOL_KEY_TYPE_RSN;
-	key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
-		WPA_KEY_INFO_SECURE;
-	WPA_PUT_BE16(reply->key_info, key_info);
-	WPA_PUT_BE16(reply->key_length, 0);
-	os_memcpy(reply->replay_counter, key->replay_counter,
-		  WPA_REPLAY_COUNTER_LEN);
-
-	os_memcpy(reply->key_nonce, peerkey->pnonce, WPA_NONCE_LEN);
-
-	mic = (u8 *) (reply + 1);
-	WPA_PUT_BE16(mic + mic_len, (u16) kde_len);
-	pos = mic + mic_len + 2;
-
-	/* Peer RSN IE */
-	pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len);
-
-	/* Initiator MAC Address KDE */
-	pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peerkey->addr, ETH_ALEN);
-
-	/* Initiator Nonce */
-	wpa_add_kde(pos, RSN_KEY_DATA_NONCE, peerkey->inonce, WPA_NONCE_LEN);
-
-	wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK M3");
-	wpa_eapol_key_send(sm, &sm->ptk, ver, src_addr, ETH_P_EAPOL, rbuf, rlen,
-			   mic);
-
-	return 0;
-}
-
-
-static int wpa_supplicant_process_smk_m2(
-	struct wpa_sm *sm, const unsigned char *src_addr,
-	const struct wpa_eapol_key *key, const u8 *key_data,
-	size_t key_data_len, int ver)
-{
-	struct wpa_peerkey *peerkey;
-	struct wpa_eapol_ie_parse kde;
-	struct wpa_ie_data ie;
-	int cipher;
-	struct rsn_ie_hdr *hdr;
-	u8 *pos;
-
-	wpa_printf(MSG_DEBUG, "RSN: Received SMK M2");
-
-	if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
-		wpa_printf(MSG_INFO, "RSN: SMK handshake not allowed for "
-			   "the current network");
-		return -1;
-	}
-
-	if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0) {
-		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M2");
-		return -1;
-	}
-
-	if (kde.rsn_ie == NULL || kde.mac_addr == NULL ||
-	    kde.mac_addr_len < ETH_ALEN) {
-		wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in "
-			   "SMK M2");
-		return -1;
-	}
-
-	wpa_printf(MSG_DEBUG, "RSN: SMK M2 - SMK initiator " MACSTR,
-		   MAC2STR(kde.mac_addr));
-
-	if (kde.rsn_ie_len > PEERKEY_MAX_IE_LEN) {
-		wpa_printf(MSG_INFO, "RSN: Too long Initiator RSN IE in SMK "
-			   "M2");
-		return -1;
-	}
-
-	if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
-		wpa_printf(MSG_INFO, "RSN: Failed to parse RSN IE in SMK M2");
-		return -1;
-	}
-
-	cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher &
-					  sm->allowed_pairwise_cipher, 0);
-	if (cipher < 0) {
-		wpa_printf(MSG_INFO, "RSN: No acceptable cipher in SMK M2");
-		wpa_supplicant_send_smk_error(sm, src_addr, kde.mac_addr,
-					      STK_MUI_SMK, STK_ERR_CPHR_NS,
-					      ver);
-		return -1;
-	}
-	wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey",
-		   wpa_cipher_txt(cipher));
-
-	/* TODO: find existing entry and if found, use that instead of adding
-	 * a new one; how to handle the case where both ends initiate at the
-	 * same time? */
-	peerkey = os_zalloc(sizeof(*peerkey));
-	if (peerkey == NULL)
-		return -1;
-	os_memcpy(peerkey->addr, kde.mac_addr, ETH_ALEN);
-	os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN);
-	os_memcpy(peerkey->rsnie_i, kde.rsn_ie, kde.rsn_ie_len);
-	peerkey->rsnie_i_len = kde.rsn_ie_len;
-	peerkey->cipher = cipher;
-	peerkey->akmp = ie.key_mgmt;
-
-	if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-			"WPA: Failed to get random data for PNonce");
-		wpa_supplicant_peerkey_free(sm, peerkey);
-		return -1;
-	}
-
-	hdr = (struct rsn_ie_hdr *) peerkey->rsnie_p;
-	hdr->elem_id = WLAN_EID_RSN;
-	WPA_PUT_LE16(hdr->version, RSN_VERSION);
-	pos = (u8 *) (hdr + 1);
-	/* Group Suite can be anything for SMK RSN IE; receiver will just
-	 * ignore it. */
-	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
-	pos += RSN_SELECTOR_LEN;
-	/* Include only the selected cipher in pairwise cipher suite */
-	WPA_PUT_LE16(pos, 1);
-	pos += 2;
-	RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, cipher));
-	pos += RSN_SELECTOR_LEN;
-
-	hdr->len = (pos - peerkey->rsnie_p) - 2;
-	peerkey->rsnie_p_len = pos - peerkey->rsnie_p;
-	wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake",
-		    peerkey->rsnie_p, peerkey->rsnie_p_len);
-
-	wpa_supplicant_send_smk_m3(sm, src_addr, key, ver, peerkey);
-
-	peerkey->next = sm->peerkey;
-	sm->peerkey = peerkey;
-
-	return 0;
-}
-
-
-/**
- * rsn_smkid - Derive SMK identifier
- * @smk: Station master key (32 bytes)
- * @pnonce: Peer Nonce
- * @mac_p: Peer MAC address
- * @inonce: Initiator Nonce
- * @mac_i: Initiator MAC address
- * @akmp: Negotiated AKM
- *
- * 8.5.1.4 Station to station (STK) key hierarchy
- * SMKID = HMAC-SHA1-128(SMK, "SMK Name" || PNonce || MAC_P || INonce || MAC_I)
- */
-static void rsn_smkid(const u8 *smk, const u8 *pnonce, const u8 *mac_p,
-		      const u8 *inonce, const u8 *mac_i, u8 *smkid,
-		      int akmp)
-{
-	char *title = "SMK Name";
-	const u8 *addr[5];
-	const size_t len[5] = { 8, WPA_NONCE_LEN, ETH_ALEN, WPA_NONCE_LEN,
-				ETH_ALEN };
-	unsigned char hash[SHA256_MAC_LEN];
-
-	addr[0] = (u8 *) title;
-	addr[1] = pnonce;
-	addr[2] = mac_p;
-	addr[3] = inonce;
-	addr[4] = mac_i;
-
-#ifdef CONFIG_IEEE80211W
-	if (wpa_key_mgmt_sha256(akmp))
-		hmac_sha256_vector(smk, PMK_LEN, 5, addr, len, hash);
-	else
-#endif /* CONFIG_IEEE80211W */
-		hmac_sha1_vector(smk, PMK_LEN, 5, addr, len, hash);
-	os_memcpy(smkid, hash, PMKID_LEN);
-}
-
-
-static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm,
-					   struct wpa_peerkey *peerkey)
-{
-	size_t mlen;
-	struct wpa_eapol_key *msg;
-	u8 *mbuf, *mic;
-	size_t kde_len, mic_len = 16;
-	u16 key_info, ver;
-
-	kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
-
-	mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
-				  sizeof(*msg) + mic_len + 2 + kde_len, &mlen,
-				  (void *) &msg);
-	if (mbuf == NULL)
-		return;
-
-	mic = (u8 *) (msg + 1);
-	msg->type = EAPOL_KEY_TYPE_RSN;
-
-	if (peerkey->cipher != WPA_CIPHER_TKIP)
-		ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
-	else
-		ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
-
-	key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK;
-	WPA_PUT_BE16(msg->key_info, key_info);
-
-	if (peerkey->cipher != WPA_CIPHER_TKIP)
-		WPA_PUT_BE16(msg->key_length, 16);
-	else
-		WPA_PUT_BE16(msg->key_length, 32);
-
-	os_memcpy(msg->replay_counter, peerkey->replay_counter,
-		  WPA_REPLAY_COUNTER_LEN);
-	inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN);
-
-	WPA_PUT_BE16(mic + mic_len, kde_len);
-	wpa_add_kde(mic + mic_len + 2, RSN_KEY_DATA_PMKID,
-		    peerkey->smkid, PMKID_LEN);
-
-	if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-			"RSN: Failed to get random data for INonce (STK)");
-		os_free(mbuf);
-		return;
-	}
-	wpa_hexdump(MSG_DEBUG, "RSN: INonce for STK 4-Way Handshake",
-		    peerkey->inonce, WPA_NONCE_LEN);
-	os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN);
-
-	wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 1/4 to " MACSTR,
-		   MAC2STR(peerkey->addr));
-	wpa_eapol_key_send(sm, NULL, ver, peerkey->addr, ETH_P_EAPOL,
-			   mbuf, mlen, NULL);
-}
-
-
-static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm,
-					   struct wpa_peerkey *peerkey)
-{
-	size_t mlen;
-	struct wpa_eapol_key *msg;
-	u8 *mbuf, *pos, *mic;
-	size_t kde_len, mic_len = 16;
-	u16 key_info, ver;
-	be32 lifetime;
-
-	kde_len = peerkey->rsnie_i_len +
-		2 + RSN_SELECTOR_LEN + sizeof(lifetime);
-
-	mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
-				  sizeof(*msg) + mic_len + 2 + kde_len, &mlen,
-				  (void *) &msg);
-	if (mbuf == NULL)
-		return;
-
-	mic = (u8 *) (msg + 1);
-	msg->type = EAPOL_KEY_TYPE_RSN;
-
-	if (peerkey->cipher != WPA_CIPHER_TKIP)
-		ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
-	else
-		ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
-
-	key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK |
-		WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
-	WPA_PUT_BE16(msg->key_info, key_info);
-
-	if (peerkey->cipher != WPA_CIPHER_TKIP)
-		WPA_PUT_BE16(msg->key_length, 16);
-	else
-		WPA_PUT_BE16(msg->key_length, 32);
-
-	os_memcpy(msg->replay_counter, peerkey->replay_counter,
-		  WPA_REPLAY_COUNTER_LEN);
-	inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN);
-
-	WPA_PUT_BE16(mic + mic_len, kde_len);
-	pos = mic + mic_len + 2;
-	pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len);
-	lifetime = host_to_be32(peerkey->lifetime);
-	wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
-		    (u8 *) &lifetime, sizeof(lifetime));
-
-	os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN);
-
-	wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 3/4 to " MACSTR,
-		   MAC2STR(peerkey->addr));
-	wpa_eapol_key_send(sm, &peerkey->stk, ver, peerkey->addr, ETH_P_EAPOL,
-			   mbuf, mlen, mic);
-}
-
-
-static int wpa_supplicant_process_smk_m4(struct wpa_peerkey *peerkey,
-					 struct wpa_eapol_ie_parse *kde)
-{
-	wpa_printf(MSG_DEBUG, "RSN: Received SMK M4 (Initiator " MACSTR ")",
-		   MAC2STR(kde->mac_addr));
-
-	if (os_memcmp(kde->smk + PMK_LEN, peerkey->pnonce, WPA_NONCE_LEN) != 0)
-	{
-		wpa_printf(MSG_INFO, "RSN: PNonce in SMK KDE does not "
-			   "match with the one used in SMK M3");
-		return -1;
-	}
-
-	if (os_memcmp(kde->nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) {
-		wpa_printf(MSG_INFO, "RSN: INonce in SMK M4 did not "
-			   "match with the one received in SMK M2");
-		return -1;
-	}
-
-	return 0;
-}
-
-
-static int wpa_supplicant_process_smk_m5(struct wpa_sm *sm,
-					 const unsigned char *src_addr,
-					 const struct wpa_eapol_key *key,
-					 int ver,
-					 struct wpa_peerkey *peerkey,
-					 struct wpa_eapol_ie_parse *kde)
-{
-	int cipher;
-	struct wpa_ie_data ie;
-
-	wpa_printf(MSG_DEBUG, "RSN: Received SMK M5 (Peer " MACSTR ")",
-		   MAC2STR(kde->mac_addr));
-	if (kde->rsn_ie == NULL || kde->rsn_ie_len > PEERKEY_MAX_IE_LEN ||
-	    wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0) {
-		wpa_printf(MSG_INFO, "RSN: No RSN IE in SMK M5");
-		/* TODO: abort negotiation */
-		return -1;
-	}
-
-	if (os_memcmp(key->key_nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) {
-		wpa_printf(MSG_INFO, "RSN: Key Nonce in SMK M5 does "
-			   "not match with INonce used in SMK M1");
-		return -1;
-	}
-
-	if (os_memcmp(kde->smk + PMK_LEN, peerkey->inonce, WPA_NONCE_LEN) != 0)
-	{
-		wpa_printf(MSG_INFO, "RSN: INonce in SMK KDE does not "
-			   "match with the one used in SMK M1");
-		return -1;
-	}
-
-	os_memcpy(peerkey->rsnie_p, kde->rsn_ie, kde->rsn_ie_len);
-	peerkey->rsnie_p_len = kde->rsn_ie_len;
-	os_memcpy(peerkey->pnonce, kde->nonce, WPA_NONCE_LEN);
-
-	cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher &
-					  sm->allowed_pairwise_cipher, 0);
-	if (cipher < 0) {
-		wpa_printf(MSG_INFO, "RSN: SMK Peer STA " MACSTR " selected "
-			   "unacceptable cipher", MAC2STR(kde->mac_addr));
-		wpa_supplicant_send_smk_error(sm, src_addr, kde->mac_addr,
-					      STK_MUI_SMK, STK_ERR_CPHR_NS,
-					      ver);
-		/* TODO: abort negotiation */
-		return -1;
-	}
-	wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey",
-		   wpa_cipher_txt(cipher));
-	peerkey->cipher = cipher;
-
-	return 0;
-}
-
-
-static int wpa_supplicant_process_smk_m45(
-	struct wpa_sm *sm, const unsigned char *src_addr,
-	const struct wpa_eapol_key *key, const u8 *key_data,
-	size_t key_data_len, int ver)
-{
-	struct wpa_peerkey *peerkey;
-	struct wpa_eapol_ie_parse kde;
-	u32 lifetime;
-
-	if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
-		wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for "
-			   "the current network");
-		return -1;
-	}
-
-	if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0) {
-		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M4/M5");
-		return -1;
-	}
-
-	if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN ||
-	    kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN ||
-	    kde.smk == NULL || kde.smk_len < PMK_LEN + WPA_NONCE_LEN ||
-	    kde.lifetime == NULL || kde.lifetime_len < 4) {
-		wpa_printf(MSG_INFO, "RSN: No MAC Address, Nonce, SMK, or "
-			   "Lifetime KDE in SMK M4/M5");
-		return -1;
-	}
-
-	for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
-		if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) == 0 &&
-		    os_memcmp(peerkey->initiator ? peerkey->inonce :
-			   peerkey->pnonce,
-			   key->key_nonce, WPA_NONCE_LEN) == 0)
-			break;
-	}
-	if (peerkey == NULL) {
-		wpa_printf(MSG_INFO, "RSN: No matching SMK handshake found "
-			   "for SMK M4/M5: peer " MACSTR,
-			   MAC2STR(kde.mac_addr));
-		return -1;
-	}
-
-	if (peerkey->initiator) {
-		if (wpa_supplicant_process_smk_m5(sm, src_addr, key, ver,
-						  peerkey, &kde) < 0)
-			return -1;
-	} else {
-		if (wpa_supplicant_process_smk_m4(peerkey, &kde) < 0)
-			return -1;
-	}
-
-	os_memcpy(peerkey->smk, kde.smk, PMK_LEN);
-	peerkey->smk_complete = 1;
-	wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", peerkey->smk, PMK_LEN);
-	lifetime = WPA_GET_BE32(kde.lifetime);
-	wpa_printf(MSG_DEBUG, "RSN: SMK lifetime %u seconds", lifetime);
-	if (lifetime > 1000000000)
-		lifetime = 1000000000; /* avoid overflowing eloop time */
-	peerkey->lifetime = lifetime;
-	eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout,
-			       sm, peerkey);
-
-	if (peerkey->initiator) {
-		rsn_smkid(peerkey->smk, peerkey->pnonce, peerkey->addr,
-			  peerkey->inonce, sm->own_addr, peerkey->smkid,
-			  peerkey->akmp);
-		wpa_supplicant_send_stk_1_of_4(sm, peerkey);
-	} else {
-		rsn_smkid(peerkey->smk, peerkey->pnonce, sm->own_addr,
-			  peerkey->inonce, peerkey->addr, peerkey->smkid,
-			  peerkey->akmp);
-	}
-	wpa_hexdump(MSG_DEBUG, "RSN: SMKID", peerkey->smkid, PMKID_LEN);
-
-	return 0;
-}
-
-
-static int wpa_supplicant_process_smk_error(
-	struct wpa_sm *sm, const unsigned char *src_addr,
-	const u8 *key_data, size_t key_data_len)
-{
-	struct wpa_eapol_ie_parse kde;
-	struct rsn_error_kde error;
-	u8 peer[ETH_ALEN];
-	u16 error_type;
-
-	wpa_printf(MSG_DEBUG, "RSN: Received SMK Error");
-
-	if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
-		wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for "
-			   "the current network");
-		return -1;
-	}
-
-	if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0) {
-		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error");
-		return -1;
-	}
-
-	if (kde.error == NULL || kde.error_len < sizeof(error)) {
-		wpa_printf(MSG_INFO, "RSN: No Error KDE in SMK Error");
-		return -1;
-	}
-
-	if (kde.mac_addr && kde.mac_addr_len >= ETH_ALEN)
-		os_memcpy(peer, kde.mac_addr, ETH_ALEN);
-	else
-		os_memset(peer, 0, ETH_ALEN);
-	os_memcpy(&error, kde.error, sizeof(error));
-	error_type = be_to_host16(error.error_type);
-	wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-		"RSN: SMK Error KDE received: MUI %d error_type %d peer "
-		MACSTR,
-		be_to_host16(error.mui), error_type,
-		MAC2STR(peer));
-
-	if (kde.mac_addr &&
-	    (error_type == STK_ERR_STA_NR || error_type == STK_ERR_STA_NRSN ||
-	     error_type == STK_ERR_CPHR_NS)) {
-		struct wpa_peerkey *peerkey;
-
-		for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
-			if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) ==
-			    0)
-				break;
-		}
-		if (peerkey == NULL) {
-			wpa_printf(MSG_DEBUG, "RSN: No matching SMK handshake "
-				   "found for SMK Error");
-			return -1;
-		}
-		/* TODO: abort SMK/STK handshake and remove all related keys */
-	}
-
-	return 0;
-}
-
-
-static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm,
-					      struct wpa_peerkey *peerkey,
-					      const struct wpa_eapol_key *key,
-					      u16 ver, const u8 *key_data,
-					      size_t key_data_len)
-{
-	struct wpa_eapol_ie_parse ie;
-	size_t kde_buf_len;
-	struct wpa_ptk *stk;
-	u8 buf[8], *kde_buf, *pos;
-	be32 lifetime;
-
-	wpa_printf(MSG_DEBUG, "RSN: RX message 1 of STK 4-Way Handshake from "
-		   MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
-
-	os_memset(&ie, 0, sizeof(ie));
-
-	/* RSN: msg 1/4 should contain SMKID for the selected SMK */
-	wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", key_data, key_data_len);
-	if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0 ||
-	    ie.pmkid == NULL) {
-		wpa_printf(MSG_DEBUG, "RSN: No SMKID in STK 1/4");
-		return;
-	}
-	if (os_memcmp_const(ie.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
-		wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 1/4",
-			    ie.pmkid, PMKID_LEN);
-		return;
-	}
-
-	if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-			"RSN: Failed to get random data for PNonce");
-		return;
-	}
-	wpa_hexdump(MSG_DEBUG, "WPA: Renewed PNonce",
-		    peerkey->pnonce, WPA_NONCE_LEN);
-
-	/* Calculate STK which will be stored as a temporary STK until it has
-	 * been verified when processing message 3/4. */
-	stk = &peerkey->tstk;
-	wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion",
-		       sm->own_addr, peerkey->addr,
-		       peerkey->pnonce, key->key_nonce,
-		       stk, peerkey->akmp, peerkey->cipher);
-	/* Supplicant: swap tx/rx Mic keys */
-	os_memcpy(buf, &stk->tk[16], 8);
-	os_memcpy(&stk->tk[16], &stk->tk[24], 8);
-	os_memcpy(&stk->tk[24], buf, 8);
-	peerkey->tstk_set = 1;
-
-	kde_buf_len = peerkey->rsnie_p_len +
-		2 + RSN_SELECTOR_LEN + sizeof(lifetime) +
-		2 + RSN_SELECTOR_LEN + PMKID_LEN;
-	kde_buf = os_malloc(kde_buf_len);
-	if (kde_buf == NULL)
-		return;
-	pos = kde_buf;
-	pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len);
-	lifetime = host_to_be32(peerkey->lifetime);
-	pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
-			  (u8 *) &lifetime, sizeof(lifetime));
-	wpa_add_kde(pos, RSN_KEY_DATA_PMKID, peerkey->smkid, PMKID_LEN);
-
-	if (wpa_supplicant_send_2_of_4(sm, peerkey->addr, key, ver,
-				       peerkey->pnonce, kde_buf, kde_buf_len,
-				       stk) < 0) {
-		wpa_printf(MSG_INFO, "RSN: Failed to send STK message 2/4");
-		os_free(kde_buf);
-		return;
-	}
-	os_free(kde_buf);
-
-	os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN);
-}
-
-
-static void wpa_supplicant_update_smk_lifetime(struct wpa_sm *sm,
-					       struct wpa_peerkey *peerkey,
-					       struct wpa_eapol_ie_parse *kde)
-{
-	u32 lifetime;
-
-	if (kde->lifetime == NULL || kde->lifetime_len < sizeof(lifetime))
-		return;
-
-	lifetime = WPA_GET_BE32(kde->lifetime);
-
-	if (lifetime >= peerkey->lifetime) {
-		wpa_printf(MSG_DEBUG, "RSN: Peer used SMK lifetime %u seconds "
-			   "which is larger than or equal to own value %u "
-			   "seconds - ignored", lifetime, peerkey->lifetime);
-		return;
-	}
-
-	wpa_printf(MSG_DEBUG, "RSN: Peer used shorter SMK lifetime %u seconds "
-		   "(own was %u seconds) - updated",
-		   lifetime, peerkey->lifetime);
-	peerkey->lifetime = lifetime;
-
-	eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey);
-	eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout,
-			       sm, peerkey);
-}
-
-
-static void wpa_supplicant_process_stk_2_of_4(struct wpa_sm *sm,
-					      struct wpa_peerkey *peerkey,
-					      const struct wpa_eapol_key *key,
-					      u16 ver, const u8 *key_data,
-					      size_t key_data_len)
-{
-	struct wpa_eapol_ie_parse kde;
-
-	wpa_printf(MSG_DEBUG, "RSN: RX message 2 of STK 4-Way Handshake from "
-		   MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
-
-	os_memset(&kde, 0, sizeof(kde));
-
-	/* RSN: msg 2/4 should contain SMKID for the selected SMK and RSN IE
-	 * from the peer. It may also include Lifetime KDE. */
-	wpa_hexdump(MSG_DEBUG, "RSN: msg 2/4 key data", key_data, key_data_len);
-	if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0 ||
-	    kde.pmkid == NULL || kde.rsn_ie == NULL) {
-		wpa_printf(MSG_DEBUG, "RSN: No SMKID or RSN IE in STK 2/4");
-		return;
-	}
-
-	if (os_memcmp_const(kde.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
-		wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 2/4",
-			    kde.pmkid, PMKID_LEN);
-		return;
-	}
-
-	if (kde.rsn_ie_len != peerkey->rsnie_p_len ||
-	    os_memcmp(kde.rsn_ie, peerkey->rsnie_p, kde.rsn_ie_len) != 0) {
-		wpa_printf(MSG_INFO, "RSN: Peer RSN IE in SMK and STK "
-			   "handshakes did not match");
-		wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in SMK handshake",
-			    peerkey->rsnie_p, peerkey->rsnie_p_len);
-		wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in STK handshake",
-			    kde.rsn_ie, kde.rsn_ie_len);
-		return;
-	}
-
-	wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde);
-
-	wpa_supplicant_send_stk_3_of_4(sm, peerkey);
-	os_memcpy(peerkey->pnonce, key->key_nonce, WPA_NONCE_LEN);
-}
-
-
-static void wpa_supplicant_process_stk_3_of_4(struct wpa_sm *sm,
-					      struct wpa_peerkey *peerkey,
-					      const struct wpa_eapol_key *key,
-					      u16 ver, const u8 *key_data,
-					      size_t key_data_len)
-{
-	struct wpa_eapol_ie_parse kde;
-	size_t key_len;
-	const u8 *_key;
-	u8 key_buf[32], rsc[6];
-
-	wpa_printf(MSG_DEBUG, "RSN: RX message 3 of STK 4-Way Handshake from "
-		   MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
-
-	os_memset(&kde, 0, sizeof(kde));
-
-	/* RSN: msg 3/4 should contain Initiator RSN IE. It may also include
-	 * Lifetime KDE. */
-	wpa_hexdump(MSG_DEBUG, "RSN: msg 3/4 key data", key_data, key_data_len);
-	if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0) {
-		wpa_printf(MSG_DEBUG, "RSN: Failed to parse key data in "
-			   "STK 3/4");
-		return;
-	}
-
-	if (kde.rsn_ie_len != peerkey->rsnie_i_len ||
-	    os_memcmp(kde.rsn_ie, peerkey->rsnie_i, kde.rsn_ie_len) != 0) {
-		wpa_printf(MSG_INFO, "RSN: Initiator RSN IE in SMK and STK "
-			   "handshakes did not match");
-		wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in SMK "
-			    "handshake",
-			    peerkey->rsnie_i, peerkey->rsnie_i_len);
-		wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in STK "
-			    "handshake",
-			    kde.rsn_ie, kde.rsn_ie_len);
-		return;
-	}
-
-	if (os_memcmp(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
-		wpa_printf(MSG_WARNING, "RSN: INonce from message 1 of STK "
-			   "4-Way Handshake differs from 3 of STK 4-Way "
-			   "Handshake - drop packet (src=" MACSTR ")",
-			   MAC2STR(peerkey->addr));
-		wpa_hexdump(MSG_DEBUG, "RSN: INonce from message 1",
-			    peerkey->inonce, WPA_NONCE_LEN);
-		wpa_hexdump(MSG_DEBUG, "RSN: INonce from message 3",
-			    key->key_nonce, WPA_NONCE_LEN);
-		return;
-	}
-
-	wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde);
-
-	if (wpa_supplicant_send_4_of_4(sm, peerkey->addr, key, ver,
-				       WPA_GET_BE16(key->key_info),
-				       &peerkey->stk) < 0) {
-		wpa_printf(MSG_INFO, "RSN: Failed to send STK message 4/4");
-		return;
-	}
-
-	_key = peerkey->stk.tk;
-	if (peerkey->cipher == WPA_CIPHER_TKIP) {
-		/* Swap Tx/Rx keys for Michael MIC */
-		os_memcpy(key_buf, _key, 16);
-		os_memcpy(key_buf + 16, _key + 24, 8);
-		os_memcpy(key_buf + 24, _key + 16, 8);
-		_key = key_buf;
-		key_len = 32;
-	} else
-		key_len = 16;
-
-	os_memset(rsc, 0, 6);
-	if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1,
-			   rsc, sizeof(rsc), _key, key_len) < 0) {
-		os_memset(key_buf, 0, sizeof(key_buf));
-		wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the "
-			   "driver.");
-		return;
-	}
-	os_memset(key_buf, 0, sizeof(key_buf));
-}
-
-
-static void wpa_supplicant_process_stk_4_of_4(struct wpa_sm *sm,
-					      struct wpa_peerkey *peerkey,
-					      const struct wpa_eapol_key *key,
-					      u16 ver)
-{
-	u8 rsc[6];
-
-	wpa_printf(MSG_DEBUG, "RSN: RX message 4 of STK 4-Way Handshake from "
-		   MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
-
-	os_memset(rsc, 0, 6);
-	if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1,
-			   rsc, sizeof(rsc), peerkey->stk.tk,
-			   peerkey->cipher == WPA_CIPHER_TKIP ? 32 : 16) < 0) {
-		wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the "
-			   "driver.");
-		return;
-	}
-}
-
-
-/**
- * peerkey_verify_eapol_key_mic - Verify PeerKey MIC
- * @sm: Pointer to WPA state machine data from wpa_sm_init()
- * @peerkey: Pointer to the PeerKey data for the peer
- * @key: Pointer to the EAPOL-Key frame header
- * @ver: Version bits from EAPOL-Key Key Info
- * @buf: Pointer to the beginning of EAPOL-Key frame
- * @len: Length of the EAPOL-Key frame
- * Returns: 0 on success, -1 on failure
- */
-int peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
-				 struct wpa_peerkey *peerkey,
-				 struct wpa_eapol_key *key, u16 ver,
-				 const u8 *buf, size_t len)
-{
-	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN], *mic_pos;
-	size_t mic_len = 16;
-	int ok = 0;
-
-	if (peerkey->initiator && !peerkey->stk_set) {
-		wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion",
-			       sm->own_addr, peerkey->addr,
-			       peerkey->inonce, key->key_nonce,
-			       &peerkey->stk, peerkey->akmp, peerkey->cipher);
-		peerkey->stk_set = 1;
-	}
-
-	mic_pos = (u8 *) (key + 1);
-	os_memcpy(mic, mic_pos, mic_len);
-	if (peerkey->tstk_set) {
-		os_memset(mic_pos, 0, mic_len);
-		wpa_eapol_key_mic(peerkey->tstk.kck, peerkey->tstk.kck_len,
-				  sm->key_mgmt, ver, buf, len, mic_pos);
-		if (os_memcmp_const(mic, mic_pos, mic_len) != 0) {
-			wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
-				   "when using TSTK - ignoring TSTK");
-		} else {
-			ok = 1;
-			peerkey->tstk_set = 0;
-			peerkey->stk_set = 1;
-			os_memcpy(&peerkey->stk, &peerkey->tstk,
-				  sizeof(peerkey->stk));
-			os_memset(&peerkey->tstk, 0, sizeof(peerkey->tstk));
-		}
-	}
-
-	if (!ok && peerkey->stk_set) {
-		os_memset(mic_pos, 0, mic_len);
-		wpa_eapol_key_mic(peerkey->stk.kck, peerkey->stk.kck_len,
-				  sm->key_mgmt, ver, buf, len, mic_pos);
-		if (os_memcmp_const(mic, mic_pos, mic_len) != 0) {
-			wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
-				   "- dropping packet");
-			return -1;
-		}
-		ok = 1;
-	}
-
-	if (!ok) {
-		wpa_printf(MSG_WARNING, "RSN: Could not verify EAPOL-Key MIC "
-			   "- dropping packet");
-		return -1;
-	}
-
-	os_memcpy(peerkey->replay_counter, key->replay_counter,
-		  WPA_REPLAY_COUNTER_LEN);
-	peerkey->replay_counter_set = 1;
-	return 0;
-}
-
-
-/**
- * wpa_sm_stkstart - Send EAPOL-Key Request for STK handshake (STK M1)
- * @sm: Pointer to WPA state machine data from wpa_sm_init()
- * @peer: MAC address of the peer STA
- * Returns: 0 on success, or -1 on failure
- *
- * Send an EAPOL-Key Request to the current authenticator to start STK
- * handshake with the peer.
- */
-int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer)
-{
-	size_t rlen, kde_len, mic_len;
-	struct wpa_eapol_key *req;
-	int key_info, ver;
-	u8 bssid[ETH_ALEN], *rbuf, *pos, *count_pos, *mic;
-	u16 count;
-	struct rsn_ie_hdr *hdr;
-	struct wpa_peerkey *peerkey;
-	struct wpa_ie_data ie;
-
-	if (sm->proto != WPA_PROTO_RSN || !sm->ptk_set || !sm->peerkey_enabled)
-		return -1;
-
-	if (sm->ap_rsn_ie &&
-	    wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &ie) == 0 &&
-	    !(ie.capabilities & WPA_CAPABILITY_PEERKEY_ENABLED)) {
-		wpa_printf(MSG_DEBUG, "RSN: Current AP does not support STK");
-		return -1;
-	}
-
-	mic_len = wpa_mic_len(sm->key_mgmt);
-	if (sm->pairwise_cipher != WPA_CIPHER_TKIP)
-		ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
-	else
-		ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
-
-	if (wpa_sm_get_bssid(sm, bssid) < 0) {
-		wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key "
-			   "SMK M1");
-		return -1;
-	}
-
-	/* TODO: find existing entry and if found, use that instead of adding
-	 * a new one */
-	peerkey = os_zalloc(sizeof(*peerkey));
-	if (peerkey == NULL)
-		return -1;
-	peerkey->initiator = 1;
-	os_memcpy(peerkey->addr, peer, ETH_ALEN);
-	peerkey->akmp = sm->key_mgmt;
-
-	/* SMK M1:
-	 * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce,
-	 *           MIC=MIC, DataKDs=(RSNIE_I, MAC_P KDE))
-	 */
-
-	hdr = (struct rsn_ie_hdr *) peerkey->rsnie_i;
-	hdr->elem_id = WLAN_EID_RSN;
-	WPA_PUT_LE16(hdr->version, RSN_VERSION);
-	pos = (u8 *) (hdr + 1);
-	/* Group Suite can be anything for SMK RSN IE; receiver will just
-	 * ignore it. */
-	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
-	pos += RSN_SELECTOR_LEN;
-	count_pos = pos;
-	pos += 2;
-
-	count = rsn_cipher_put_suites(pos, sm->allowed_pairwise_cipher);
-	pos += count * RSN_SELECTOR_LEN;
-	WPA_PUT_LE16(count_pos, count);
-
-	hdr->len = (pos - peerkey->rsnie_i) - 2;
-	peerkey->rsnie_i_len = pos - peerkey->rsnie_i;
-	wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake",
-		    peerkey->rsnie_i, peerkey->rsnie_i_len);
-
-	kde_len = peerkey->rsnie_i_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN;
-
-	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
-				  sizeof(*req) + mic_len + 2 + kde_len, &rlen,
-				  (void *) &req);
-	if (rbuf == NULL) {
-		wpa_supplicant_peerkey_free(sm, peerkey);
-		return -1;
-	}
-
-	req->type = EAPOL_KEY_TYPE_RSN;
-	key_info = WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
-		WPA_KEY_INFO_SECURE | WPA_KEY_INFO_REQUEST | ver;
-	WPA_PUT_BE16(req->key_info, key_info);
-	WPA_PUT_BE16(req->key_length, 0);
-	os_memcpy(req->replay_counter, sm->request_counter,
-		  WPA_REPLAY_COUNTER_LEN);
-	inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
-
-	if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-			"WPA: Failed to get random data for INonce");
-		os_free(rbuf);
-		wpa_supplicant_peerkey_free(sm, peerkey);
-		return -1;
-	}
-	os_memcpy(req->key_nonce, peerkey->inonce, WPA_NONCE_LEN);
-	wpa_hexdump(MSG_DEBUG, "WPA: INonce for SMK handshake",
-		    req->key_nonce, WPA_NONCE_LEN);
-
-	mic = pos = (u8 *) (req + 1);
-	pos += mic_len;
-	WPA_PUT_BE16(pos, (u16) kde_len);
-	pos += 2;
-
-	/* Initiator RSN IE */
-	pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len);
-	/* Peer MAC address KDE */
-	wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN);
-
-	wpa_printf(MSG_INFO, "RSN: Sending EAPOL-Key SMK M1 Request (peer "
-		   MACSTR ")", MAC2STR(peer));
-	wpa_eapol_key_send(sm, &sm->ptk, ver, bssid, ETH_P_EAPOL, rbuf, rlen,
-			   mic);
-
-	peerkey->next = sm->peerkey;
-	sm->peerkey = peerkey;
-
-	return 0;
-}
-
-
-/**
- * peerkey_deinit - Free PeerKey values
- * @sm: Pointer to WPA state machine data from wpa_sm_init()
- */
-void peerkey_deinit(struct wpa_sm *sm)
-{
-	struct wpa_peerkey *prev, *peerkey = sm->peerkey;
-	while (peerkey) {
-		prev = peerkey;
-		peerkey = peerkey->next;
-		wpa_supplicant_peerkey_free(sm, prev);
-	}
-	sm->peerkey = NULL;
-}
-
-
-void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
-			   struct wpa_eapol_key *key, u16 key_info, u16 ver,
-			   const u8 *key_data, size_t key_data_len)
-{
-	if ((key_info & (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) ==
-	    (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) {
-		/* 3/4 STK 4-Way Handshake */
-		wpa_supplicant_process_stk_3_of_4(sm, peerkey, key, ver,
-						  key_data, key_data_len);
-	} else if (key_info & WPA_KEY_INFO_ACK) {
-		/* 1/4 STK 4-Way Handshake */
-		wpa_supplicant_process_stk_1_of_4(sm, peerkey, key, ver,
-						  key_data, key_data_len);
-	} else if (key_info & WPA_KEY_INFO_SECURE) {
-		/* 4/4 STK 4-Way Handshake */
-		wpa_supplicant_process_stk_4_of_4(sm, peerkey, key, ver);
-	} else {
-		/* 2/4 STK 4-Way Handshake */
-		wpa_supplicant_process_stk_2_of_4(sm, peerkey, key, ver,
-						  key_data, key_data_len);
-	}
-}
-
-
-void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
-			  struct wpa_eapol_key *key, const u8 *key_data,
-			  size_t key_data_len,
-			  u16 key_info, u16 ver)
-{
-	if (key_info & WPA_KEY_INFO_ERROR) {
-		/* SMK Error */
-		wpa_supplicant_process_smk_error(sm, src_addr, key_data,
-						 key_data_len);
-	} else if (key_info & WPA_KEY_INFO_ACK) {
-		/* SMK M2 */
-		wpa_supplicant_process_smk_m2(sm, src_addr, key, key_data,
-					      key_data_len, ver);
-	} else {
-		/* SMK M4 or M5 */
-		wpa_supplicant_process_smk_m45(sm, src_addr, key, key_data,
-					       key_data_len, ver);
-	}
-}
-
-#endif /* CONFIG_PEERKEY */
diff --git a/src/rsn_supp/peerkey.h b/src/rsn_supp/peerkey.h
deleted file mode 100644
index 02e12e9..0000000
--- a/src/rsn_supp/peerkey.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * WPA Supplicant - PeerKey for Direct Link Setup (DLS)
- * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#ifndef PEERKEY_H
-#define PEERKEY_H
-
-#define PEERKEY_MAX_IE_LEN 80
-struct wpa_peerkey {
-	struct wpa_peerkey *next;
-	int initiator; /* whether this end was initator for SMK handshake */
-	u8 addr[ETH_ALEN]; /* other end MAC address */
-	u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */
-	u8 pnonce[WPA_NONCE_LEN]; /* Peer Nonce */
-	u8 rsnie_i[PEERKEY_MAX_IE_LEN]; /* Initiator RSN IE */
-	size_t rsnie_i_len;
-	u8 rsnie_p[PEERKEY_MAX_IE_LEN]; /* Peer RSN IE */
-	size_t rsnie_p_len;
-	u8 smk[PMK_LEN];
-	int smk_complete;
-	u8 smkid[PMKID_LEN];
-	u32 lifetime;
-	int cipher; /* Selected cipher (WPA_CIPHER_*) */
-	u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
-	int replay_counter_set;
-	int akmp;
-
-	struct wpa_ptk stk, tstk;
-	int stk_set, tstk_set;
-};
-
-
-#ifdef CONFIG_PEERKEY
-
-int peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
-				 struct wpa_peerkey *peerkey,
-				 struct wpa_eapol_key *key, u16 ver,
-				 const u8 *buf, size_t len);
-void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
-			   struct wpa_eapol_key *key, u16 key_info, u16 ver,
-			   const u8 *key_data, size_t key_data_len);
-void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
-			  struct wpa_eapol_key *key, const u8 *key_data,
-			  size_t key_data_len, u16 key_info, u16 ver);
-void peerkey_deinit(struct wpa_sm *sm);
-
-#else /* CONFIG_PEERKEY */
-
-static inline int
-peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
-			     struct wpa_peerkey *peerkey,
-			     struct wpa_eapol_key *key, u16 ver,
-			     const u8 *buf, size_t len)
-{
-	return -1;
-}
-
-static inline void
-peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
-		      struct wpa_eapol_key *key, u16 key_info, u16 ver,
-		      const u8 *key_data, size_t key_data_len)
-{
-}
-
-static inline void
-peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
-		     struct wpa_eapol_key *key, const u8 *key_data,
-		     size_t key_data_len, u16 key_info, u16 ver)
-{
-}
-
-static inline void peerkey_deinit(struct wpa_sm *sm)
-{
-}
-
-#endif /* CONFIG_PEERKEY */
-
-#endif /* PEERKEY_H */
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index f723bb0..f5024f2 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -44,7 +44,9 @@
 				   enum pmksa_free_reason reason)
 {
 	wpa_sm_remove_pmkid(pmksa->sm, entry->network_ctx, entry->aa,
-			    entry->pmkid);
+			    entry->pmkid,
+			    entry->fils_cache_id_set ? entry->fils_cache_id :
+			    NULL);
 	pmksa->pmksa_count--;
 	pmksa->free_cb(entry, pmksa->ctx, reason);
 	_pmksa_cache_free_entry(entry);
@@ -117,6 +119,7 @@
  * @spa: Supplicant address
  * @network_ctx: Network configuration context for this PMK
  * @akmp: WPA_KEY_MGMT_* used in key derivation
+ * @cache_id: Pointer to FILS Cache Identifier or %NULL if not advertised
  * Returns: Pointer to the added PMKSA cache entry or %NULL on error
  *
  * This function create a PMKSA entry for a new PMK and adds it to the PMKSA
@@ -127,7 +130,8 @@
 struct rsn_pmksa_cache_entry *
 pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
 		const u8 *pmkid, const u8 *kck, size_t kck_len,
-		const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
+		const u8 *aa, const u8 *spa, void *network_ctx, int akmp,
+		const u8 *cache_id)
 {
 	struct rsn_pmksa_cache_entry *entry;
 	struct os_reltime now;
@@ -150,13 +154,16 @@
 	else if (wpa_key_mgmt_suite_b(akmp))
 		rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
 	else
-		rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
-			  wpa_key_mgmt_sha256(akmp));
+		rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp);
 	os_get_reltime(&now);
 	entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime;
 	entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime *
 		pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100;
 	entry->akmp = akmp;
+	if (cache_id) {
+		entry->fils_cache_id_set = 1;
+		os_memcpy(entry->fils_cache_id, cache_id, FILS_CACHE_ID_LEN);
+	}
 	os_memcpy(entry->aa, aa, ETH_ALEN);
 	entry->network_ctx = network_ctx;
 
@@ -257,8 +264,9 @@
 	pmksa->pmksa_count++;
 	wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR
 		   " network_ctx=%p", MAC2STR(entry->aa), entry->network_ctx);
-	wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa,
-			 entry->pmkid);
+	wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa, entry->pmkid,
+			 entry->fils_cache_id_set ? entry->fils_cache_id : NULL,
+			 entry->pmk, entry->pmk_len);
 
 	return entry;
 }
@@ -358,16 +366,19 @@
 			const u8 *aa)
 {
 	struct rsn_pmksa_cache_entry *new_entry;
+	os_time_t old_expiration = old_entry->expiration;
 
 	new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len,
 				    NULL, NULL, 0,
 				    aa, pmksa->sm->own_addr,
-				    old_entry->network_ctx, old_entry->akmp);
+				    old_entry->network_ctx, old_entry->akmp,
+				    old_entry->fils_cache_id_set ?
+				    old_entry->fils_cache_id : NULL);
 	if (new_entry == NULL)
 		return NULL;
 
 	/* TODO: reorder entries based on expiration time? */
-	new_entry->expiration = old_entry->expiration;
+	new_entry->expiration = old_expiration;
 	new_entry->opportunistic = 1;
 
 	return new_entry;
@@ -410,6 +421,24 @@
 }
 
 
+static struct rsn_pmksa_cache_entry *
+pmksa_cache_get_fils_cache_id(struct rsn_pmksa_cache *pmksa,
+			      const void *network_ctx, const u8 *cache_id)
+{
+	struct rsn_pmksa_cache_entry *entry;
+
+	for (entry = pmksa->pmksa; entry; entry = entry->next) {
+		if (network_ctx == entry->network_ctx &&
+		    entry->fils_cache_id_set &&
+		    os_memcmp(cache_id, entry->fils_cache_id,
+			      FILS_CACHE_ID_LEN) == 0)
+			return entry;
+	}
+
+	return NULL;
+}
+
+
 /**
  * pmksa_cache_get_current - Get the current used PMKSA entry
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -442,11 +471,12 @@
  * @bssid: BSSID for PMKSA or %NULL if not used
  * @network_ctx: Network configuration context
  * @try_opportunistic: Whether to allow opportunistic PMKSA caching
+ * @fils_cache_id: Pointer to FILS Cache Identifier or %NULL if not used
  * Returns: 0 if PMKSA was found or -1 if no matching entry was found
  */
 int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
 			    const u8 *bssid, void *network_ctx,
-			    int try_opportunistic)
+			    int try_opportunistic, const u8 *fils_cache_id)
 {
 	struct rsn_pmksa_cache *pmksa = sm->pmksa;
 	wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p "
@@ -457,6 +487,10 @@
 	if (bssid)
 		wpa_printf(MSG_DEBUG, "RSN: Search for BSSID " MACSTR,
 			   MAC2STR(bssid));
+	if (fils_cache_id)
+		wpa_printf(MSG_DEBUG,
+			   "RSN: Search for FILS Cache Identifier %02x%02x",
+			   fils_cache_id[0], fils_cache_id[1]);
 
 	sm->cur_pmksa = NULL;
 	if (pmkid)
@@ -469,6 +503,10 @@
 		sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa,
 							      network_ctx,
 							      bssid);
+	if (sm->cur_pmksa == NULL && fils_cache_id)
+		sm->cur_pmksa = pmksa_cache_get_fils_cache_id(pmksa,
+							      network_ctx,
+							      fils_cache_id);
 	if (sm->cur_pmksa) {
 		wpa_hexdump(MSG_DEBUG, "RSN: PMKSA cache entry found - PMKID",
 			    sm->cur_pmksa->pmkid, PMKID_LEN);
@@ -495,11 +533,20 @@
 	char *pos = buf;
 	struct rsn_pmksa_cache_entry *entry;
 	struct os_reltime now;
+	int cache_id_used = 0;
+
+	for (entry = pmksa->pmksa; entry; entry = entry->next) {
+		if (entry->fils_cache_id_set) {
+			cache_id_used = 1;
+			break;
+		}
+	}
 
 	os_get_reltime(&now);
 	ret = os_snprintf(pos, buf + len - pos,
 			  "Index / AA / PMKID / expiration (in seconds) / "
-			  "opportunistic\n");
+			  "opportunistic%s\n",
+			  cache_id_used ? " / FILS Cache Identifier" : "");
 	if (os_snprintf_error(buf + len - pos, ret))
 		return pos - buf;
 	pos += ret;
@@ -514,12 +561,24 @@
 		pos += ret;
 		pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
 					PMKID_LEN);
-		ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
+		ret = os_snprintf(pos, buf + len - pos, " %d %d",
 				  (int) (entry->expiration - now.sec),
 				  entry->opportunistic);
 		if (os_snprintf_error(buf + len - pos, ret))
 			return pos - buf;
 		pos += ret;
+		if (entry->fils_cache_id_set) {
+			ret = os_snprintf(pos, buf + len - pos, " %02x%02x",
+					  entry->fils_cache_id[0],
+					  entry->fils_cache_id[1]);
+			if (os_snprintf_error(buf + len - pos, ret))
+				return pos - buf;
+			pos += ret;
+		}
+		ret = os_snprintf(pos, buf + len - pos, "\n");
+		if (os_snprintf_error(buf + len - pos, ret))
+			return pos - buf;
+		pos += ret;
 		entry = entry->next;
 	}
 	return pos - buf;
diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h
index adc251a..f9a72a6 100644
--- a/src/rsn_supp/pmksa_cache.h
+++ b/src/rsn_supp/pmksa_cache.h
@@ -21,6 +21,14 @@
 	int akmp; /* WPA_KEY_MGMT_* */
 	u8 aa[ETH_ALEN];
 
+	/*
+	 * If FILS Cache Identifier is included (fils_cache_id_set), this PMKSA
+	 * cache entry is applicable to all BSSs (any BSSID/aa[]) that
+	 * advertise the same FILS Cache Identifier within the same ESS.
+	 */
+	u8 fils_cache_id[2];
+	unsigned int fils_cache_id_set:1;
+
 	os_time_t reauth_time;
 
 	/**
@@ -59,7 +67,8 @@
 struct rsn_pmksa_cache_entry *
 pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
 		const u8 *pmkid, const u8 *kck, size_t kck_len,
-		const u8 *aa, const u8 *spa, void *network_ctx, int akmp);
+		const u8 *aa, const u8 *spa, void *network_ctx, int akmp,
+		const u8 *cache_id);
 struct rsn_pmksa_cache_entry *
 pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa,
 		      struct rsn_pmksa_cache_entry *entry);
@@ -67,7 +76,7 @@
 void pmksa_cache_clear_current(struct wpa_sm *sm);
 int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
 			    const u8 *bssid, void *network_ctx,
-			    int try_opportunistic);
+			    int try_opportunistic, const u8 *fils_cache_id);
 struct rsn_pmksa_cache_entry *
 pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa,
 			      void *network_ctx, const u8 *aa);
@@ -123,7 +132,8 @@
 static inline struct rsn_pmksa_cache_entry *
 pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
 		const u8 *pmkid, const u8 *kck, size_t kck_len,
-		const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
+		const u8 *aa, const u8 *spa, void *network_ctx, int akmp,
+		const u8 *cache_id)
 {
 	return NULL;
 }
@@ -135,7 +145,8 @@
 static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
 					  const u8 *bssid,
 					  void *network_ctx,
-					  int try_opportunistic)
+					  int try_opportunistic,
+					  const u8 *fils_cache_id)
 {
 	return -1;
 }
diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c
index e83d073..d4276b9 100644
--- a/src/rsn_supp/preauth.c
+++ b/src/rsn_supp/preauth.c
@@ -97,7 +97,7 @@
 					NULL, 0,
 					sm->preauth_bssid, sm->own_addr,
 					sm->network_ctx,
-					WPA_KEY_MGMT_IEEE8021X);
+					WPA_KEY_MGMT_IEEE8021X, NULL);
 		} else {
 			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 				"RSN: failed to get master session key from "
@@ -342,7 +342,8 @@
 		/* Some drivers (e.g., NDIS) expect to get notified about the
 		 * PMKIDs again, so report the existing data now. */
 		if (p) {
-			wpa_sm_add_pmkid(sm, NULL, candidate->bssid, p->pmkid);
+			wpa_sm_add_pmkid(sm, NULL, candidate->bssid, p->pmkid,
+					 NULL, p->pmk, p->pmk_len);
 		}
 
 		dl_list_del(&candidate->list);
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index c2ed952..5e350ed 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -303,10 +303,9 @@
 	peer->sm_tmr.peer_capab = peer_capab;
 	peer->sm_tmr.buf_len = msg_len;
 	os_free(peer->sm_tmr.buf);
-	peer->sm_tmr.buf = os_malloc(msg_len);
+	peer->sm_tmr.buf = os_memdup(msg, msg_len);
 	if (peer->sm_tmr.buf == NULL)
 		return -1;
-	os_memcpy(peer->sm_tmr.buf, msg, msg_len);
 
 	wpa_printf(MSG_DEBUG, "TDLS: Retry timeout registered "
 		   "(action_code=%u)", action_code);
@@ -413,8 +412,9 @@
 	size_t len[2];
 	u8 data[3 * ETH_ALEN];
 
-	/* IEEE Std 802.11z-2010 8.5.9.1:
-	 * TPK-Key-Input = SHA-256(min(SNonce, ANonce) || max(SNonce, ANonce))
+	/* IEEE Std 802.11-2016 12.7.9.2:
+	 * TPK-Key-Input = Hash(min(SNonce, ANonce) || max(SNonce, ANonce))
+	 * Hash = SHA-256 for TDLS
 	 */
 	len[0] = WPA_NONCE_LEN;
 	len[1] = WPA_NONCE_LEN;
@@ -432,11 +432,8 @@
 			key_input, SHA256_MAC_LEN);
 
 	/*
-	 * TPK-Key-Data = KDF-N_KEY(TPK-Key-Input, "TDLS PMK",
-	 *	min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID || N_KEY)
-	 * TODO: is N_KEY really included in KDF Context and if so, in which
-	 * presentation format (little endian 16-bit?) is it used? It gets
-	 * added by the KDF anyway..
+	 * TPK = KDF-Hash-Length(TPK-Key-Input, "TDLS PMK",
+	 *	min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID)
 	 */
 
 	if (os_memcmp(own_addr, peer->addr, ETH_ALEN) < 0) {
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index af72298..15b0e60 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - WPA state machine and EAPOL-Key processing
- * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
  * Copyright(c) 2015 Intel Deutschland GmbH
  *
  * This software may be distributed under the terms of the BSD license.
@@ -15,6 +15,9 @@
 #include "crypto/crypto.h"
 #include "crypto/random.h"
 #include "crypto/aes_siv.h"
+#include "crypto/sha256.h"
+#include "crypto/sha384.h"
+#include "crypto/sha512.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "eap_common/eap_defs.h"
@@ -25,7 +28,6 @@
 #include "pmksa_cache.h"
 #include "wpa_i.h"
 #include "wpa_ie.h"
-#include "peerkey.h"
 
 
 static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
@@ -48,8 +50,11 @@
 		       u8 *msg, size_t msg_len, u8 *key_mic)
 {
 	int ret = -1;
-	size_t mic_len = wpa_mic_len(sm->key_mgmt);
+	size_t mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
 
+	wpa_printf(MSG_DEBUG, "WPA: Send EAPOL-Key frame to " MACSTR
+		   " ver=%d mic_len=%d key_mgmt=0x%x",
+		   MAC2STR(dest), ver, (int) mic_len, sm->key_mgmt);
 	if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) {
 		/*
 		 * Association event was not yet received; try to fetch
@@ -193,7 +198,7 @@
 		return;
 	}
 
-	mic_len = wpa_mic_len(sm->key_mgmt);
+	mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
 	hdrlen = sizeof(*reply) + mic_len + 2;
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
 				  hdrlen, &rlen, (void *) &reply);
@@ -315,6 +320,13 @@
 		}
 		if (res == 0) {
 			struct rsn_pmksa_cache_entry *sa = NULL;
+			const u8 *fils_cache_id = NULL;
+
+#ifdef CONFIG_FILS
+			if (sm->fils_cache_id_set)
+				fils_cache_id = sm->fils_cache_id;
+#endif /* CONFIG_FILS */
+
 			wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state "
 					"machines", sm->pmk, pmk_len);
 			sm->pmk_len = pmk_len;
@@ -327,7 +339,8 @@
 						     NULL, 0,
 						     src_addr, sm->own_addr,
 						     sm->network_ctx,
-						     sm->key_mgmt);
+						     sm->key_mgmt,
+						     fils_cache_id);
 			}
 			if (!sm->cur_pmksa && pmkid &&
 			    pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL))
@@ -457,7 +470,7 @@
 
 	wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len);
 
-	mic_len = wpa_mic_len(sm->key_mgmt);
+	mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
 	hdrlen = sizeof(*reply) + mic_len + 2;
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
 				  NULL, hdrlen + wpa_ie_len,
@@ -572,7 +585,8 @@
 	/* Calculate PTK which will be stored as a temporary PTK until it has
 	 * been verified when processing message 3/4. */
 	ptk = &sm->tptk;
-	wpa_derive_ptk(sm, src_addr, key, ptk);
+	if (wpa_derive_ptk(sm, src_addr, key, ptk) < 0)
+		goto failed;
 	if (sm->pairwise_cipher == WPA_CIPHER_TKIP) {
 		u8 buf[8];
 		/* Supplicant: swap tx/rx Mic keys */
@@ -643,7 +657,9 @@
 			sm, addr, MLME_SETPROTECTION_PROTECT_TYPE_RX_TX,
 			MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
 		eapol_sm_notify_portValid(sm->eapol, TRUE);
-		if (wpa_key_mgmt_wpa_psk(sm->key_mgmt))
+		if (wpa_key_mgmt_wpa_psk(sm->key_mgmt) ||
+		    sm->key_mgmt == WPA_KEY_MGMT_DPP ||
+		    sm->key_mgmt == WPA_KEY_MGMT_OWE)
 			eapol_sm_notify_eap_success(sm->eapol, TRUE);
 		/*
 		 * Start preauthentication after a short wait to avoid a
@@ -710,6 +726,11 @@
 
 	alg = wpa_cipher_to_alg(sm->pairwise_cipher);
 	keylen = wpa_cipher_key_len(sm->pairwise_cipher);
+	if (keylen <= 0 || (unsigned int) keylen != sm->ptk.tk_len) {
+		wpa_printf(MSG_DEBUG, "WPA: TK length mismatch: %d != %lu",
+			   keylen, (long unsigned int) sm->ptk.tk_len);
+		return -1;
+	}
 	rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
 
 	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
@@ -730,6 +751,7 @@
 
 	/* TK is not needed anymore in supplicant */
 	os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
+	sm->ptk.tk_len = 0;
 	sm->ptk.installed = 1;
 
 	if (sm->wpa_ptk_rekey) {
@@ -1277,7 +1299,7 @@
 	struct wpa_eapol_key *reply;
 	u8 *rbuf, *key_mic;
 
-	mic_len = wpa_mic_len(sm->key_mgmt);
+	mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
 	hdrlen = sizeof(*reply) + mic_len + 2;
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
 				  hdrlen, &rlen, (void *) &reply);
@@ -1427,7 +1449,7 @@
 		sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, NULL,
 				     sm->ptk.kck, sm->ptk.kck_len,
 				     sm->bssid, sm->own_addr,
-				     sm->network_ctx, sm->key_mgmt);
+				     sm->network_ctx, sm->key_mgmt, NULL);
 		if (!sm->cur_pmksa)
 			sm->cur_pmksa = sa;
 	}
@@ -1586,7 +1608,7 @@
 	struct wpa_eapol_key *reply;
 	u8 *rbuf, *key_mic;
 
-	mic_len = wpa_mic_len(sm->key_mgmt);
+	mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
 	hdrlen = sizeof(*reply) + mic_len + 2;
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
 				  hdrlen, &rlen, (void *) &reply);
@@ -1600,6 +1622,8 @@
 	key_info |= ver | WPA_KEY_INFO_SECURE;
 	if (mic_len)
 		key_info |= WPA_KEY_INFO_MIC;
+	else
+		key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
 	WPA_PUT_BE16(reply->key_info, key_info);
 	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
 		WPA_PUT_BE16(reply->key_length, 0);
@@ -1628,7 +1652,7 @@
 	struct wpa_gtk_data gd;
 	const u8 *key_rsc;
 
-	if (!sm->msg_3_of_4_ok) {
+	if (!sm->msg_3_of_4_ok && !wpa_fils_is_completed(sm)) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"WPA: Group Key Handshake started prior to completion of 4-way handshake");
 		goto failed;
@@ -1695,14 +1719,15 @@
 {
 	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
 	int ok = 0;
-	size_t mic_len = wpa_mic_len(sm->key_mgmt);
+	size_t mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
 
 	os_memcpy(mic, key + 1, mic_len);
 	if (sm->tptk_set) {
 		os_memset(key + 1, 0, mic_len);
-		wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len, sm->key_mgmt,
-				  ver, buf, len, (u8 *) (key + 1));
-		if (os_memcmp_const(mic, key + 1, mic_len) != 0) {
+		if (wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len,
+				      sm->key_mgmt,
+				      ver, buf, len, (u8 *) (key + 1)) < 0 ||
+		    os_memcmp_const(mic, key + 1, mic_len) != 0) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: Invalid EAPOL-Key MIC "
 				"when using TPTK - ignoring TPTK");
@@ -1712,14 +1737,23 @@
 			sm->ptk_set = 1;
 			os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk));
 			os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+			/*
+			 * This assures the same TPTK in sm->tptk can never be
+			 * copied twice to sm->pkt as the new PTK. In
+			 * combination with the installed flag in the wpa_ptk
+			 * struct, this assures the same PTK is only installed
+			 * once.
+			 */
+			sm->renew_snonce = 1;
 		}
 	}
 
 	if (!ok && sm->ptk_set) {
 		os_memset(key + 1, 0, mic_len);
-		wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len, sm->key_mgmt,
-				  ver, buf, len, (u8 *) (key + 1));
-		if (os_memcmp_const(mic, key + 1, mic_len) != 0) {
+		if (wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len,
+				      sm->key_mgmt,
+				      ver, buf, len, (u8 *) (key + 1)) < 0 ||
+		    os_memcmp_const(mic, key + 1, mic_len) != 0) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: Invalid EAPOL-Key MIC - "
 				"dropping packet");
@@ -1766,6 +1800,8 @@
 		return -1;
 #else /* CONFIG_NO_RC4 */
 		u8 ek[32];
+
+		wpa_printf(MSG_DEBUG, "WPA: Decrypt Key Data using RC4");
 		os_memcpy(ek, key->key_iv, 16);
 		os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len);
 		if (rc4_skip(ek, 32, 256, key_data, *key_data_len)) {
@@ -1778,9 +1814,15 @@
 #endif /* CONFIG_NO_RC4 */
 	} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
 		   ver == WPA_KEY_INFO_TYPE_AES_128_CMAC ||
+		   sm->key_mgmt == WPA_KEY_MGMT_OWE ||
+		   sm->key_mgmt == WPA_KEY_MGMT_DPP ||
 		   sm->key_mgmt == WPA_KEY_MGMT_OSEN ||
 		   wpa_key_mgmt_suite_b(sm->key_mgmt)) {
 		u8 *buf;
+
+		wpa_printf(MSG_DEBUG,
+			   "WPA: Decrypt Key Data using AES-UNWRAP (KEK length %u)",
+			   (unsigned int) sm->ptk.kek_len);
 		if (*key_data_len < 8 || *key_data_len % 8) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: Unsupported AES-WRAP len %u",
@@ -1962,7 +2004,6 @@
 	u16 key_info, ver;
 	u8 *tmp = NULL;
 	int ret = -1;
-	struct wpa_peerkey *peerkey = NULL;
 	u8 *mic, *key_data;
 	size_t mic_len, keyhdrlen;
 
@@ -1970,7 +2011,7 @@
 	sm->ft_completed = 0;
 #endif /* CONFIG_IEEE80211R */
 
-	mic_len = wpa_mic_len(sm->key_mgmt);
+	mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
 	keyhdrlen = sizeof(*key) + mic_len + 2;
 
 	if (len < sizeof(*hdr) + keyhdrlen) {
@@ -2018,10 +2059,9 @@
 	 * Make a copy of the frame since we need to modify the buffer during
 	 * MAC validation and Key Data decryption.
 	 */
-	tmp = os_malloc(data_len);
+	tmp = os_memdup(buf, data_len);
 	if (tmp == NULL)
 		goto out;
-	os_memcpy(tmp, buf, data_len);
 	key = (struct wpa_eapol_key *) (tmp + sizeof(struct ieee802_1x_hdr));
 	mic = (u8 *) (key + 1);
 	key_data = mic + mic_len + 2;
@@ -2056,6 +2096,8 @@
 	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
 	    !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
 	    !wpa_key_mgmt_fils(sm->key_mgmt) &&
+	    sm->key_mgmt != WPA_KEY_MGMT_OWE &&
+	    sm->key_mgmt != WPA_KEY_MGMT_DPP &&
 	    sm->key_mgmt != WPA_KEY_MGMT_OSEN) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"WPA: Unsupported EAPOL-Key descriptor version %d",
@@ -2072,7 +2114,9 @@
 	}
 
 	if ((wpa_key_mgmt_suite_b(sm->key_mgmt) ||
-	     wpa_key_mgmt_fils(sm->key_mgmt)) &&
+	     wpa_key_mgmt_fils(sm->key_mgmt) ||
+	     sm->key_mgmt == WPA_KEY_MGMT_DPP ||
+	     sm->key_mgmt == WPA_KEY_MGMT_OWE) &&
 	    ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"RSN: Unsupported EAPOL-Key descriptor version %d (expected AKM defined = 0)",
@@ -2106,6 +2150,8 @@
 	if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
 	    !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
 	    !wpa_key_mgmt_fils(sm->key_mgmt) &&
+	    sm->key_mgmt != WPA_KEY_MGMT_OWE &&
+	    sm->key_mgmt != WPA_KEY_MGMT_DPP &&
 	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"WPA: CCMP is used, but EAPOL-Key "
@@ -2133,44 +2179,7 @@
 		goto out;
 	}
 
-#ifdef CONFIG_PEERKEY
-	for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
-		if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0)
-			break;
-	}
-
-	if (!(key_info & WPA_KEY_INFO_SMK_MESSAGE) && peerkey) {
-		if (!peerkey->initiator && peerkey->replay_counter_set &&
-		    os_memcmp(key->replay_counter, peerkey->replay_counter,
-			      WPA_REPLAY_COUNTER_LEN) <= 0) {
-			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-				"RSN: EAPOL-Key Replay Counter did not "
-				"increase (STK) - dropping packet");
-			goto out;
-		} else if (peerkey->initiator) {
-			u8 _tmp[WPA_REPLAY_COUNTER_LEN];
-			os_memcpy(_tmp, key->replay_counter,
-				  WPA_REPLAY_COUNTER_LEN);
-			inc_byte_array(_tmp, WPA_REPLAY_COUNTER_LEN);
-			if (os_memcmp(_tmp, peerkey->replay_counter,
-				      WPA_REPLAY_COUNTER_LEN) != 0) {
-				wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
-					"RSN: EAPOL-Key Replay "
-					"Counter did not match (STK) - "
-					"dropping packet");
-				goto out;
-			}
-		}
-	}
-
-	if (peerkey && peerkey->initiator && (key_info & WPA_KEY_INFO_ACK)) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-			"RSN: Ack bit in key_info from STK peer");
-		goto out;
-	}
-#endif /* CONFIG_PEERKEY */
-
-	if (!peerkey && sm->rx_replay_counter_set &&
+	if (sm->rx_replay_counter_set &&
 	    os_memcmp(key->replay_counter, sm->rx_replay_counter,
 		      WPA_REPLAY_COUNTER_LEN) <= 0) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
@@ -2179,11 +2188,13 @@
 		goto out;
 	}
 
-	if (!(key_info & (WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE))
-#ifdef CONFIG_PEERKEY
-	    && (peerkey == NULL || !peerkey->initiator)
-#endif /* CONFIG_PEERKEY */
-		) {
+	if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"WPA: Unsupported SMK bit in key_info");
+		goto out;
+	}
+
+	if (!(key_info & WPA_KEY_INFO_ACK)) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"WPA: No Ack bit in key_info");
 		goto out;
@@ -2195,17 +2206,10 @@
 		goto out;
 	}
 
-	if ((key_info & WPA_KEY_INFO_MIC) && !peerkey &&
+	if ((key_info & WPA_KEY_INFO_MIC) &&
 	    wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len))
 		goto out;
 
-#ifdef CONFIG_PEERKEY
-	if ((key_info & WPA_KEY_INFO_MIC) && peerkey &&
-	    peerkey_verify_eapol_key_mic(sm, peerkey, key, ver, tmp,
-					 data_len))
-		goto out;
-#endif /* CONFIG_PEERKEY */
-
 #ifdef CONFIG_FILS
 	if (!mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
 		if (wpa_supp_aead_decrypt(sm, tmp, data_len, &key_data_len))
@@ -2228,12 +2232,8 @@
 				"non-zero key index");
 			goto out;
 		}
-		if (peerkey) {
-			/* PeerKey 4-Way Handshake */
-			peerkey_rx_eapol_4way(sm, peerkey, key, key_info, ver,
-					      key_data, key_data_len);
-		} else if (key_info & (WPA_KEY_INFO_MIC |
-				       WPA_KEY_INFO_ENCR_KEY_DATA)) {
+		if (key_info & (WPA_KEY_INFO_MIC |
+				WPA_KEY_INFO_ENCR_KEY_DATA)) {
 			/* 3/4 4-Way Handshake */
 			wpa_supplicant_process_3_of_4(sm, key, ver, key_data,
 						      key_data_len);
@@ -2243,10 +2243,6 @@
 						      ver, key_data,
 						      key_data_len);
 		}
-	} else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
-		/* PeerKey SMK Handshake */
-		peerkey_rx_eapol_smk(sm, src_addr, key, key_data, key_data_len,
-				     key_info, ver);
 	} else {
 		if ((mic_len && (key_info & WPA_KEY_INFO_MIC)) ||
 		    (!mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA))) {
@@ -2488,13 +2484,21 @@
 	os_free(sm->ap_rsn_ie);
 	wpa_sm_drop_sa(sm);
 	os_free(sm->ctx);
-	peerkey_deinit(sm);
 #ifdef CONFIG_IEEE80211R
 	os_free(sm->assoc_resp_ies);
 #endif /* CONFIG_IEEE80211R */
 #ifdef CONFIG_TESTING_OPTIONS
 	wpabuf_free(sm->test_assoc_ie);
 #endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_FILS_SK_PFS
+	crypto_ecdh_deinit(sm->fils_ecdh);
+#endif /* CONFIG_FILS_SK_PFS */
+#ifdef CONFIG_FILS
+	wpabuf_free(sm->fils_ft_ies);
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+	crypto_ecdh_deinit(sm->owe_ecdh);
+#endif /* CONFIG_OWE */
 	os_free(sm);
 }
 
@@ -2588,7 +2592,6 @@
 {
 	eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL);
 	eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
-	peerkey_deinit(sm);
 	rsn_preauth_deinit(sm);
 	pmksa_cache_clear_current(sm);
 	if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE)
@@ -2607,6 +2610,7 @@
 	wpa_sm_drop_sa(sm);
 
 	sm->msg_3_of_4_ok = 0;
+	os_memset(sm->bssid, 0, ETH_ALEN);
 }
 
 
@@ -2626,6 +2630,8 @@
 	if (sm == NULL)
 		return;
 
+	wpa_hexdump_key(MSG_DEBUG, "WPA: Set PMK based on external data",
+			pmk, pmk_len);
 	sm->pmk_len = pmk_len;
 	os_memcpy(sm->pmk, pmk, pmk_len);
 
@@ -2638,7 +2644,7 @@
 	if (bssid) {
 		pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0,
 				bssid, sm->own_addr,
-				sm->network_ctx, sm->key_mgmt);
+				sm->network_ctx, sm->key_mgmt, NULL);
 	}
 }
 
@@ -2656,11 +2662,15 @@
 		return;
 
 	if (sm->cur_pmksa) {
+		wpa_hexdump_key(MSG_DEBUG,
+				"WPA: Set PMK based on current PMKSA",
+				sm->cur_pmksa->pmk, sm->cur_pmksa->pmk_len);
 		sm->pmk_len = sm->cur_pmksa->pmk_len;
 		os_memcpy(sm->pmk, sm->cur_pmksa->pmk, sm->pmk_len);
 	} else {
-		sm->pmk_len = PMK_LEN;
-		os_memset(sm->pmk, 0, PMK_LEN);
+		wpa_printf(MSG_DEBUG, "WPA: No current PMKSA - clear PMK");
+		sm->pmk_len = 0;
+		os_memset(sm->pmk, 0, PMK_LEN_MAX);
 	}
 }
 
@@ -2708,7 +2718,6 @@
 
 	if (config) {
 		sm->network_ctx = config->network_ctx;
-		sm->peerkey_enabled = config->peerkey_enabled;
 		sm->allowed_pairwise_cipher = config->allowed_pairwise_cipher;
 		sm->proactive_key_caching = config->proactive_key_caching;
 		sm->eap_workaround = config->eap_workaround;
@@ -2721,9 +2730,17 @@
 		sm->wpa_ptk_rekey = config->wpa_ptk_rekey;
 		sm->p2p = config->p2p;
 		sm->wpa_rsc_relaxation = config->wpa_rsc_relaxation;
+#ifdef CONFIG_FILS
+		if (config->fils_cache_id) {
+			sm->fils_cache_id_set = 1;
+			os_memcpy(sm->fils_cache_id, config->fils_cache_id,
+				  FILS_CACHE_ID_LEN);
+		} else {
+			sm->fils_cache_id_set = 0;
+		}
+#endif /* CONFIG_FILS */
 	} else {
 		sm->network_ctx = NULL;
-		sm->peerkey_enabled = 0;
 		sm->allowed_pairwise_cipher = 0;
 		sm->proactive_key_caching = 0;
 		sm->eap_workaround = 0;
@@ -2876,9 +2893,12 @@
 		    >= 0 &&
 		    rsn.capabilities & (WPA_CAPABILITY_MFPR |
 					WPA_CAPABILITY_MFPC)) {
-			ret = os_snprintf(pos, end - pos, "pmf=%d\n",
+			ret = os_snprintf(pos, end - pos, "pmf=%d\n"
+					  "mgmt_group_cipher=%s\n",
 					  (rsn.capabilities &
-					   WPA_CAPABILITY_MFPR) ? 2 : 1);
+					   WPA_CAPABILITY_MFPR) ? 2 : 1,
+					  wpa_cipher_txt(
+						  sm->mgmt_group_cipher));
 			if (os_snprintf_error(end - pos, ret))
 				return pos - buf;
 			pos += ret;
@@ -2944,11 +2964,10 @@
 		 * the correct version of the IE even if PMKSA caching is
 		 * aborted (which would remove PMKID from IE generation).
 		 */
-		sm->assoc_wpa_ie = os_malloc(*wpa_ie_len);
+		sm->assoc_wpa_ie = os_memdup(wpa_ie, *wpa_ie_len);
 		if (sm->assoc_wpa_ie == NULL)
 			return -1;
 
-		os_memcpy(sm->assoc_wpa_ie, wpa_ie, *wpa_ie_len);
 		sm->assoc_wpa_ie_len = *wpa_ie_len;
 	} else {
 		wpa_hexdump(MSG_DEBUG,
@@ -2984,11 +3003,10 @@
 		sm->assoc_wpa_ie_len = 0;
 	} else {
 		wpa_hexdump(MSG_DEBUG, "WPA: set own WPA/RSN IE", ie, len);
-		sm->assoc_wpa_ie = os_malloc(len);
+		sm->assoc_wpa_ie = os_memdup(ie, len);
 		if (sm->assoc_wpa_ie == NULL)
 			return -1;
 
-		os_memcpy(sm->assoc_wpa_ie, ie, len);
 		sm->assoc_wpa_ie_len = len;
 	}
 
@@ -3019,11 +3037,10 @@
 		sm->ap_wpa_ie_len = 0;
 	} else {
 		wpa_hexdump(MSG_DEBUG, "WPA: set AP WPA IE", ie, len);
-		sm->ap_wpa_ie = os_malloc(len);
+		sm->ap_wpa_ie = os_memdup(ie, len);
 		if (sm->ap_wpa_ie == NULL)
 			return -1;
 
-		os_memcpy(sm->ap_wpa_ie, ie, len);
 		sm->ap_wpa_ie_len = len;
 	}
 
@@ -3054,11 +3071,10 @@
 		sm->ap_rsn_ie_len = 0;
 	} else {
 		wpa_hexdump(MSG_DEBUG, "WPA: set AP RSN IE", ie, len);
-		sm->ap_rsn_ie = os_malloc(len);
+		sm->ap_rsn_ie = os_memdup(ie, len);
 		if (sm->ap_rsn_ie == NULL)
 			return -1;
 
-		os_memcpy(sm->ap_rsn_ie, ie, len);
 		sm->ap_rsn_ie_len = len;
 	}
 
@@ -3111,6 +3127,23 @@
 }
 
 
+void wpa_sm_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
+			    const u8 *pmkid, const u8 *bssid,
+			    const u8 *fils_cache_id)
+{
+	sm->cur_pmksa = pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0,
+					bssid, sm->own_addr, sm->network_ctx,
+					sm->key_mgmt, fils_cache_id);
+}
+
+
+int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid,
+			const void *network_ctx)
+{
+	return pmksa_cache_get(sm->pmksa, bssid, NULL, network_ctx) != NULL;
+}
+
+
 void wpa_sm_drop_sa(struct wpa_sm *sm)
 {
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK");
@@ -3213,27 +3246,6 @@
 #endif /* CONFIG_WNM */
 
 
-#ifdef CONFIG_PEERKEY
-int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr,
-			    const u8 *buf, size_t len)
-{
-	struct wpa_peerkey *peerkey;
-
-	for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
-		if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0)
-			break;
-	}
-
-	if (!peerkey)
-		return 0;
-
-	wpa_sm_rx_eapol(sm, src_addr, buf, len);
-
-	return 1;
-}
-#endif /* CONFIG_PEERKEY */
-
-
 #ifdef CONFIG_P2P
 
 int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf)
@@ -3278,20 +3290,29 @@
 
 
 #ifdef CONFIG_TESTING_OPTIONS
+
 void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf)
 {
 	wpabuf_free(sm->test_assoc_ie);
 	sm->test_assoc_ie = buf;
 }
+
+
+const u8 * wpa_sm_get_anonce(struct wpa_sm *sm)
+{
+	return sm->anonce;
+}
+
 #endif /* CONFIG_TESTING_OPTIONS */
 
 
 #ifdef CONFIG_FILS
 
-struct wpabuf * fils_build_auth(struct wpa_sm *sm)
+struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md)
 {
 	struct wpabuf *buf = NULL;
 	struct wpabuf *erp_msg;
+	struct wpabuf *pub = NULL;
 
 	erp_msg = eapol_sm_build_erp_reauth_start(sm->eapol);
 	if (!erp_msg && !sm->cur_pmksa) {
@@ -3319,7 +3340,28 @@
 	wpa_hexdump(MSG_DEBUG, "FILS: Generated FILS Session",
 		    sm->fils_session, FILS_SESSION_LEN);
 
-	buf = wpabuf_alloc(1000 + sm->assoc_wpa_ie_len);
+#ifdef CONFIG_FILS_SK_PFS
+	sm->fils_dh_group = dh_group;
+	if (dh_group) {
+		crypto_ecdh_deinit(sm->fils_ecdh);
+		sm->fils_ecdh = crypto_ecdh_init(dh_group);
+		if (!sm->fils_ecdh) {
+			wpa_printf(MSG_INFO,
+				   "FILS: Could not initialize ECDH with group %d",
+				   dh_group);
+			goto fail;
+		}
+		pub = crypto_ecdh_get_pubkey(sm->fils_ecdh, 1);
+		if (!pub)
+			goto fail;
+		wpa_hexdump_buf(MSG_DEBUG, "FILS: Element (DH public key)",
+				pub);
+		sm->fils_dh_elem_len = wpabuf_len(pub);
+	}
+#endif /* CONFIG_FILS_SK_PFS */
+
+	buf = wpabuf_alloc(1000 + sm->assoc_wpa_ie_len +
+			   (pub ? wpabuf_len(pub) : 0));
 	if (!buf)
 		goto fail;
 
@@ -3331,16 +3373,31 @@
 	/* Status Code */
 	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
 
-	/* TODO: Finite Cyclic Group when using PK or PFS */
-	/* TODO: Element when using PK or PFS */
+	/* TODO: FILS PK */
+#ifdef CONFIG_FILS_SK_PFS
+	if (dh_group) {
+		/* Finite Cyclic Group */
+		wpabuf_put_le16(buf, dh_group);
+		/* Element */
+		wpabuf_put_buf(buf, pub);
+	}
+#endif /* CONFIG_FILS_SK_PFS */
 
 	/* RSNE */
 	wpa_hexdump(MSG_DEBUG, "FILS: RSNE in FILS Authentication frame",
 		    sm->assoc_wpa_ie, sm->assoc_wpa_ie_len);
 	wpabuf_put_data(buf, sm->assoc_wpa_ie, sm->assoc_wpa_ie_len);
 
-	/* TODO: MDE when using FILS for FT initial association */
-	/* TODO: FTE when using FILS for FT initial association */
+	if (md) {
+		/* MDE when using FILS for FT initial association */
+		struct rsn_mdie *mdie;
+
+		wpabuf_put_u8(buf, WLAN_EID_MOBILITY_DOMAIN);
+		wpabuf_put_u8(buf, sizeof(*mdie));
+		mdie = wpabuf_put(buf, sizeof(*mdie));
+		os_memcpy(mdie->mobility_domain, md, MOBILITY_DOMAIN_ID_LEN);
+		mdie->ft_capab = 0;
+	}
 
 	/* FILS Nonce */
 	wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
@@ -3377,11 +3434,13 @@
 
 fail:
 	wpabuf_free(erp_msg);
+	wpabuf_free(pub);
 	return buf;
 }
 
 
-int fils_process_auth(struct wpa_sm *sm, const u8 *data, size_t len)
+int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
+		      size_t len)
 {
 	const u8 *pos, *end;
 	struct ieee802_11_elems elems;
@@ -3390,19 +3449,69 @@
 	u8 ick[FILS_ICK_MAX_LEN];
 	size_t ick_len;
 	int res;
+	struct wpabuf *dh_ss = NULL;
+	const u8 *g_sta = NULL;
+	size_t g_sta_len = 0;
+	const u8 *g_ap = NULL;
+	size_t g_ap_len = 0;
+	struct wpabuf *pub = NULL;
+
+	os_memcpy(sm->bssid, bssid, ETH_ALEN);
 
 	wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields",
 		    data, len);
 	pos = data;
 	end = data + len;
 
-	/* TODO: Finite Cyclic Group when using PK or PFS */
-	/* TODO: Element when using PK or PFS */
+	/* TODO: FILS PK */
+#ifdef CONFIG_FILS_SK_PFS
+	if (sm->fils_dh_group) {
+		u16 group;
+
+		/* Using FILS PFS */
+
+		/* Finite Cyclic Group */
+		if (end - pos < 2) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: No room for Finite Cyclic Group");
+			goto fail;
+		}
+		group = WPA_GET_LE16(pos);
+		pos += 2;
+		if (group != sm->fils_dh_group) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Unexpected change in Finite Cyclic Group: %u (expected %u)",
+				   group, sm->fils_dh_group);
+			goto fail;
+		}
+
+		/* Element */
+		if ((size_t) (end - pos) < sm->fils_dh_elem_len) {
+			wpa_printf(MSG_DEBUG, "FILS: No room for Element");
+			goto fail;
+		}
+
+		if (!sm->fils_ecdh) {
+			wpa_printf(MSG_DEBUG, "FILS: No ECDH state available");
+			goto fail;
+		}
+		dh_ss = crypto_ecdh_set_peerkey(sm->fils_ecdh, 1, pos,
+						sm->fils_dh_elem_len);
+		if (!dh_ss) {
+			wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed");
+			goto fail;
+		}
+		wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", dh_ss);
+		g_ap = pos;
+		g_ap_len = sm->fils_dh_elem_len;
+		pos += sm->fils_dh_elem_len;
+	}
+#endif /* CONFIG_FILS_SK_PFS */
 
 	wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos);
 	if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) {
 		wpa_printf(MSG_DEBUG, "FILS: Could not parse elements");
-		return -1;
+		goto fail;
 	}
 
 	/* RSNE */
@@ -3412,18 +3521,63 @@
 	    wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
 				 &rsn) < 0) {
 		wpa_printf(MSG_DEBUG, "FILS: No RSN element");
-		return -1;
+		goto fail;
 	}
 
 	if (!elems.fils_nonce) {
 		wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
-		return -1;
+		goto fail;
 	}
 	os_memcpy(sm->fils_anonce, elems.fils_nonce, FILS_NONCE_LEN);
 	wpa_hexdump(MSG_DEBUG, "FILS: ANonce", sm->fils_anonce, FILS_NONCE_LEN);
 
-	/* TODO: MDE when using FILS+FT */
-	/* TODO: FTE when using FILS+FT */
+	if (wpa_key_mgmt_ft(sm->key_mgmt)) {
+		struct wpa_ft_ies parse;
+
+		if (!elems.mdie || !elems.ftie) {
+			wpa_printf(MSG_DEBUG, "FILS+FT: No MDE or FTE");
+			goto fail;
+		}
+
+		if (wpa_ft_parse_ies(pos, end - pos, &parse) < 0) {
+			wpa_printf(MSG_DEBUG, "FILS+FT: Failed to parse IEs");
+			goto fail;
+		}
+
+		if (!parse.r0kh_id) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS+FT: No R0KH-ID subelem in FTE");
+			goto fail;
+		}
+		os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len);
+		sm->r0kh_id_len = parse.r0kh_id_len;
+		wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: R0KH-ID",
+				  sm->r0kh_id, sm->r0kh_id_len);
+
+		if (!parse.r1kh_id) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS+FT: No R1KH-ID subelem in FTE");
+			goto fail;
+		}
+		os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
+		wpa_hexdump(MSG_DEBUG, "FILS+FT: R1KH-ID",
+			    sm->r1kh_id, FT_R1KH_ID_LEN);
+
+		/* TODO: Check MDE and FTE payload */
+
+		wpabuf_free(sm->fils_ft_ies);
+		sm->fils_ft_ies = wpabuf_alloc(2 + elems.mdie_len +
+					       2 + elems.ftie_len);
+		if (!sm->fils_ft_ies)
+			goto fail;
+		wpabuf_put_data(sm->fils_ft_ies, elems.mdie - 2,
+				2 + elems.mdie_len);
+		wpabuf_put_data(sm->fils_ft_ies, elems.ftie - 2,
+				2 + elems.ftie_len);
+	} else {
+		wpabuf_free(sm->fils_ft_ies);
+		sm->fils_ft_ies = NULL;
+	}
 
 	/* PMKID List */
 	if (rsn.pmkid && rsn.num_pmkid > 0) {
@@ -3432,7 +3586,7 @@
 
 		if (rsn.num_pmkid != 1) {
 			wpa_printf(MSG_DEBUG, "FILS: Invalid PMKID selection");
-			return -1;
+			goto fail;
 		}
 		wpa_hexdump(MSG_DEBUG, "FILS: PMKID", rsn.pmkid, PMKID_LEN);
 		if (os_memcmp(sm->cur_pmksa->pmkid, rsn.pmkid, PMKID_LEN) != 0)
@@ -3440,7 +3594,7 @@
 			wpa_printf(MSG_DEBUG, "FILS: PMKID mismatch");
 			wpa_hexdump(MSG_DEBUG, "FILS: Expected PMKID",
 				    sm->cur_pmksa->pmkid, PMKID_LEN);
-			return -1;
+			goto fail;
 		}
 		wpa_printf(MSG_DEBUG,
 			   "FILS: Matching PMKID - continue using PMKSA caching");
@@ -3455,7 +3609,7 @@
 	/* FILS Session */
 	if (!elems.fils_session) {
 		wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
-		return -1;
+		goto fail;
 	}
 	wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session,
 		    FILS_SESSION_LEN);
@@ -3464,7 +3618,7 @@
 		wpa_printf(MSG_DEBUG, "FILS: Session mismatch");
 		wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
 			    sm->fils_session, FILS_SESSION_LEN);
-		return -1;
+		goto fail;
 	}
 
 	/* FILS Wrapped Data */
@@ -3478,7 +3632,7 @@
 		eapol_sm_process_erp_finish(sm->eapol, elems.fils_wrapped_data,
 					    elems.fils_wrapped_data_len);
 		if (eapol_sm_failed(sm->eapol))
-			return -1;
+			goto fail;
 
 		rmsk_len = ERP_MAX_KEY_LEN;
 		res = eapol_sm_get_key(sm->eapol, rmsk, rmsk_len);
@@ -3487,18 +3641,26 @@
 			res = eapol_sm_get_key(sm->eapol, rmsk, rmsk_len);
 		}
 		if (res)
-			return -1;
+			goto fail;
 
 		res = fils_rmsk_to_pmk(sm->key_mgmt, rmsk, rmsk_len,
-				       sm->fils_nonce, sm->fils_anonce, NULL, 0,
+				       sm->fils_nonce, sm->fils_anonce,
+				       dh_ss ? wpabuf_head(dh_ss) : NULL,
+				       dh_ss ? wpabuf_len(dh_ss) : 0,
 				       sm->pmk, &sm->pmk_len);
 		os_memset(rmsk, 0, sizeof(rmsk));
+
+		/* Don't use DHss in PTK derivation if PMKSA caching is not
+		 * used. */
+		wpabuf_clear_free(dh_ss);
+		dh_ss = NULL;
+
 		if (res)
-			return -1;
+			goto fail;
 
 		if (!sm->fils_erp_pmkid_set) {
 			wpa_printf(MSG_DEBUG, "FILS: PMKID not available");
-			return -1;
+			goto fail;
 		}
 		wpa_hexdump(MSG_DEBUG, "FILS: PMKID", sm->fils_erp_pmkid,
 			    PMKID_LEN);
@@ -3506,37 +3668,174 @@
 		sm->cur_pmksa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len,
 						sm->fils_erp_pmkid, NULL, 0,
 						sm->bssid, sm->own_addr,
-						sm->network_ctx, sm->key_mgmt);
+						sm->network_ctx, sm->key_mgmt,
+						NULL);
 	}
 
 	if (!sm->cur_pmksa) {
 		wpa_printf(MSG_DEBUG,
 			   "FILS: No remaining options to continue FILS authentication");
-		return -1;
+		goto fail;
 	}
 
 	if (fils_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr, sm->bssid,
-			    sm->fils_nonce, sm->fils_anonce, &sm->ptk,
-			    ick, &ick_len, sm->key_mgmt, sm->pairwise_cipher) <
-	    0) {
+			    sm->fils_nonce, sm->fils_anonce,
+			    dh_ss ? wpabuf_head(dh_ss) : NULL,
+			    dh_ss ? wpabuf_len(dh_ss) : 0,
+			    &sm->ptk, ick, &ick_len,
+			    sm->key_mgmt, sm->pairwise_cipher,
+			    sm->fils_ft, &sm->fils_ft_len) < 0) {
 		wpa_printf(MSG_DEBUG, "FILS: Failed to derive PTK");
-		return -1;
+		goto fail;
 	}
+
+	wpabuf_clear_free(dh_ss);
+	dh_ss = NULL;
+
 	sm->ptk_set = 1;
 	sm->tptk_set = 0;
 	os_memset(&sm->tptk, 0, sizeof(sm->tptk));
 
+#ifdef CONFIG_FILS_SK_PFS
+	if (sm->fils_dh_group) {
+		if (!sm->fils_ecdh) {
+			wpa_printf(MSG_INFO, "FILS: ECDH not initialized");
+			goto fail;
+		}
+		pub = crypto_ecdh_get_pubkey(sm->fils_ecdh, 1);
+		if (!pub)
+			goto fail;
+		wpa_hexdump_buf(MSG_DEBUG, "FILS: gSTA", pub);
+		g_sta = wpabuf_head(pub);
+		g_sta_len = wpabuf_len(pub);
+		if (!g_ap) {
+			wpa_printf(MSG_INFO, "FILS: gAP not available");
+			goto fail;
+		}
+		wpa_hexdump(MSG_DEBUG, "FILS: gAP", g_ap, g_ap_len);
+	}
+#endif /* CONFIG_FILS_SK_PFS */
+
 	res = fils_key_auth_sk(ick, ick_len, sm->fils_nonce,
 			       sm->fils_anonce, sm->own_addr, sm->bssid,
-			       NULL, 0, NULL, 0, /* TODO: SK+PFS */
+			       g_sta, g_sta_len, g_ap, g_ap_len,
 			       sm->key_mgmt, sm->fils_key_auth_sta,
 			       sm->fils_key_auth_ap,
 			       &sm->fils_key_auth_len);
+	wpabuf_free(pub);
 	os_memset(ick, 0, sizeof(ick));
 	return res;
+fail:
+	wpabuf_free(pub);
+	wpabuf_clear_free(dh_ss);
+	return -1;
 }
 
 
+#ifdef CONFIG_IEEE80211R
+static int fils_ft_build_assoc_req_rsne(struct wpa_sm *sm, struct wpabuf *buf)
+{
+	struct rsn_ie_hdr *rsnie;
+	u16 capab;
+	u8 *pos;
+
+	/* RSNIE[PMKR0Name/PMKR1Name] */
+	rsnie = wpabuf_put(buf, sizeof(*rsnie));
+	rsnie->elem_id = WLAN_EID_RSN;
+	WPA_PUT_LE16(rsnie->version, RSN_VERSION);
+
+	/* Group Suite Selector */
+	if (!wpa_cipher_valid_group(sm->group_cipher)) {
+		wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)",
+			   sm->group_cipher);
+		return -1;
+	}
+	pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
+	RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
+						  sm->group_cipher));
+
+	/* Pairwise Suite Count */
+	wpabuf_put_le16(buf, 1);
+
+	/* Pairwise Suite List */
+	if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
+		wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)",
+			   sm->pairwise_cipher);
+		return -1;
+	}
+	pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
+	RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
+						  sm->pairwise_cipher));
+
+	/* Authenticated Key Management Suite Count */
+	wpabuf_put_le16(buf, 1);
+
+	/* Authenticated Key Management Suite List */
+	pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
+	if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256)
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
+	else if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384)
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
+	else {
+		wpa_printf(MSG_WARNING,
+			   "FILS+FT: Invalid key management type (%d)",
+			   sm->key_mgmt);
+		return -1;
+	}
+
+	/* RSN Capabilities */
+	capab = 0;
+#ifdef CONFIG_IEEE80211W
+	if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC)
+		capab |= WPA_CAPABILITY_MFPC;
+#endif /* CONFIG_IEEE80211W */
+	wpabuf_put_le16(buf, capab);
+
+	/* PMKID Count */
+	wpabuf_put_le16(buf, 1);
+
+	/* PMKID List [PMKR1Name] */
+	wpa_hexdump_key(MSG_DEBUG, "FILS+FT: XXKey (FILS-FT)",
+			sm->fils_ft, sm->fils_ft_len);
+	wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: SSID", sm->ssid, sm->ssid_len);
+	wpa_hexdump(MSG_DEBUG, "FILS+FT: MDID",
+		    sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN);
+	wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: R0KH-ID",
+			  sm->r0kh_id, sm->r0kh_id_len);
+	if (wpa_derive_pmk_r0(sm->fils_ft, sm->fils_ft_len, sm->ssid,
+			      sm->ssid_len, sm->mobility_domain,
+			      sm->r0kh_id, sm->r0kh_id_len, sm->own_addr,
+			      sm->pmk_r0, sm->pmk_r0_name) < 0) {
+		wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMK-R0");
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0", sm->pmk_r0, PMK_LEN);
+	wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name",
+		    sm->pmk_r0_name, WPA_PMK_NAME_LEN);
+	wpa_printf(MSG_DEBUG, "FILS+FT: R1KH-ID: " MACSTR,
+		   MAC2STR(sm->r1kh_id));
+	pos = wpabuf_put(buf, WPA_PMK_NAME_LEN);
+	if (wpa_derive_pmk_r1_name(sm->pmk_r0_name, sm->r1kh_id, sm->own_addr,
+				   pos) < 0) {
+		wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMKR1Name");
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", pos, WPA_PMK_NAME_LEN);
+
+#ifdef CONFIG_IEEE80211W
+	if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
+		/* Management Group Cipher Suite */
+		pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+	}
+#endif /* CONFIG_IEEE80211W */
+
+	rsnie->len = ((u8 *) wpabuf_put(buf, 0) - (u8 *) rsnie) - 2;
+	return 0;
+}
+#endif /* CONFIG_IEEE80211R */
+
+
 struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek,
 				     size_t *kek_len, const u8 **snonce,
 				     const u8 **anonce,
@@ -3548,12 +3847,30 @@
 	unsigned int i;
 
 	len = 1000;
+#ifdef CONFIG_IEEE80211R
+	if (sm->fils_ft_ies)
+		len += wpabuf_len(sm->fils_ft_ies);
+	if (wpa_key_mgmt_ft(sm->key_mgmt))
+		len += 256;
+#endif /* CONFIG_IEEE80211R */
 	for (i = 0; hlp && i < num_hlp; i++)
 		len += 10 + wpabuf_len(hlp[i]);
 	buf = wpabuf_alloc(len);
 	if (!buf)
 		return NULL;
 
+#ifdef CONFIG_IEEE80211R
+	if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->fils_ft_ies) {
+		/* MDE and FTE when using FILS+FT */
+		wpabuf_put_buf(buf, sm->fils_ft_ies);
+		/* RSNE with PMKR1Name in PMKID field */
+		if (fils_ft_build_assoc_req_rsne(sm, buf) < 0) {
+			wpabuf_free(buf);
+			return NULL;
+		}
+	}
+#endif /* CONFIG_IEEE80211R */
+
 	/* FILS Session */
 	wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
 	wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); /* Length */
@@ -3706,6 +4023,12 @@
 		return -1;
 	}
 
+	if (sm->fils_completed) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Association has already been completed for this FILS authentication - ignore unexpected retransmission");
+		return -1;
+	}
+
 	wpa_hexdump(MSG_DEBUG, "FILS: (Re)Association Response frame",
 		    resp, len);
 
@@ -3797,7 +4120,7 @@
 	os_memcpy(gd.gtk, kde.gtk + 2, kde.gtk_len - 2);
 
 	wpa_printf(MSG_DEBUG, "FILS: Set GTK to driver");
-	if (wpa_supplicant_install_gtk(sm, &gd, elems.key_delivery) < 0) {
+	if (wpa_supplicant_install_gtk(sm, &gd, elems.key_delivery, 0) < 0) {
 		wpa_printf(MSG_DEBUG, "FILS: Failed to set GTK");
 		goto fail;
 	}
@@ -3809,6 +4132,11 @@
 
 	alg = wpa_cipher_to_alg(sm->pairwise_cipher);
 	keylen = wpa_cipher_key_len(sm->pairwise_cipher);
+	if (keylen <= 0 || (unsigned int) keylen != sm->ptk.tk_len) {
+		wpa_printf(MSG_DEBUG, "FILS: TK length mismatch: %u != %lu",
+			   keylen, (long unsigned int) sm->ptk.tk_len);
+		goto fail;
+	}
 	rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
 	wpa_hexdump_key(MSG_DEBUG, "FILS: Set TK to driver",
 			sm->ptk.tk, keylen);
@@ -3825,6 +4153,7 @@
 	 * takes care of association frame encryption/decryption. */
 	/* TK is not needed anymore in supplicant */
 	os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
+	sm->ptk.tk_len = 0;
 	sm->ptk.installed = 1;
 
 	/* FILS HLP Container */
@@ -3840,6 +4169,13 @@
 	return -1;
 }
 
+
+void wpa_sm_set_reset_fils_completed(struct wpa_sm *sm, int set)
+{
+	if (sm)
+		sm->fils_completed = !!set;
+}
+
 #endif /* CONFIG_FILS */
 
 
@@ -3851,3 +4187,219 @@
 	return 0;
 #endif /* CONFIG_FILS */
 }
+
+
+#ifdef CONFIG_OWE
+
+struct wpabuf * owe_build_assoc_req(struct wpa_sm *sm, u16 group)
+{
+	struct wpabuf *ie = NULL, *pub = NULL;
+	size_t prime_len;
+
+	if (group == 19)
+		prime_len = 32;
+	else if (group == 20)
+		prime_len = 48;
+	else if (group == 21)
+		prime_len = 66;
+	else
+		return NULL;
+
+	crypto_ecdh_deinit(sm->owe_ecdh);
+	sm->owe_ecdh = crypto_ecdh_init(group);
+	if (!sm->owe_ecdh)
+		goto fail;
+	sm->owe_group = group;
+	pub = crypto_ecdh_get_pubkey(sm->owe_ecdh, 0);
+	pub = wpabuf_zeropad(pub, prime_len);
+	if (!pub)
+		goto fail;
+
+	ie = wpabuf_alloc(5 + wpabuf_len(pub));
+	if (!ie)
+		goto fail;
+	wpabuf_put_u8(ie, WLAN_EID_EXTENSION);
+	wpabuf_put_u8(ie, 1 + 2 + wpabuf_len(pub));
+	wpabuf_put_u8(ie, WLAN_EID_EXT_OWE_DH_PARAM);
+	wpabuf_put_le16(ie, group);
+	wpabuf_put_buf(ie, pub);
+	wpabuf_free(pub);
+	wpa_hexdump_buf(MSG_DEBUG, "OWE: Diffie-Hellman Parameter element",
+			ie);
+
+	return ie;
+fail:
+	wpabuf_free(pub);
+	crypto_ecdh_deinit(sm->owe_ecdh);
+	sm->owe_ecdh = NULL;
+	return NULL;
+}
+
+
+int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid,
+			   const u8 *resp_ies, size_t resp_ies_len)
+{
+	struct ieee802_11_elems elems;
+	u16 group;
+	struct wpabuf *secret, *pub, *hkey;
+	int res;
+	u8 prk[SHA512_MAC_LEN], pmkid[SHA512_MAC_LEN];
+	const char *info = "OWE Key Generation";
+	const u8 *addr[2];
+	size_t len[2];
+	size_t hash_len, prime_len;
+	struct wpa_ie_data data;
+
+	if (!resp_ies ||
+	    ieee802_11_parse_elems(resp_ies, resp_ies_len, &elems, 1) ==
+	    ParseFailed) {
+		wpa_printf(MSG_INFO,
+			   "OWE: Could not parse Association Response frame elements");
+		return -1;
+	}
+
+	if (sm->cur_pmksa && elems.rsn_ie &&
+	    wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, 2 + elems.rsn_ie_len,
+				 &data) == 0 &&
+	    data.num_pmkid == 1 && data.pmkid &&
+	    os_memcmp(sm->cur_pmksa->pmkid, data.pmkid, PMKID_LEN) == 0) {
+		wpa_printf(MSG_DEBUG, "OWE: Use PMKSA caching");
+		wpa_sm_set_pmk_from_pmksa(sm);
+		return 0;
+	}
+
+	if (!elems.owe_dh) {
+		wpa_printf(MSG_INFO,
+			   "OWE: No Diffie-Hellman Parameter element found in Association Response frame");
+		return -1;
+	}
+
+	group = WPA_GET_LE16(elems.owe_dh);
+	if (group != sm->owe_group) {
+		wpa_printf(MSG_INFO,
+			   "OWE: Unexpected Diffie-Hellman group in response: %u",
+			   group);
+		return -1;
+	}
+
+	if (!sm->owe_ecdh) {
+		wpa_printf(MSG_INFO, "OWE: No ECDH state available");
+		return -1;
+	}
+
+	if (group == 19)
+		prime_len = 32;
+	else if (group == 20)
+		prime_len = 48;
+	else if (group == 21)
+		prime_len = 66;
+	else
+		return -1;
+
+	secret = crypto_ecdh_set_peerkey(sm->owe_ecdh, 0,
+					 elems.owe_dh + 2,
+					 elems.owe_dh_len - 2);
+	secret = wpabuf_zeropad(secret, prime_len);
+	if (!secret) {
+		wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key");
+		return -1;
+	}
+	wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret);
+
+	/* prk = HKDF-extract(C | A | group, z) */
+
+	pub = crypto_ecdh_get_pubkey(sm->owe_ecdh, 0);
+	if (!pub) {
+		wpabuf_clear_free(secret);
+		return -1;
+	}
+
+	/* PMKID = Truncate-128(Hash(C | A)) */
+	addr[0] = wpabuf_head(pub);
+	len[0] = wpabuf_len(pub);
+	addr[1] = elems.owe_dh + 2;
+	len[1] = elems.owe_dh_len - 2;
+	if (group == 19) {
+		res = sha256_vector(2, addr, len, pmkid);
+		hash_len = SHA256_MAC_LEN;
+	} else if (group == 20) {
+		res = sha384_vector(2, addr, len, pmkid);
+		hash_len = SHA384_MAC_LEN;
+	} else if (group == 21) {
+		res = sha512_vector(2, addr, len, pmkid);
+		hash_len = SHA512_MAC_LEN;
+	} else {
+		res = -1;
+		hash_len = 0;
+	}
+	pub = wpabuf_zeropad(pub, prime_len);
+	if (res < 0 || !pub) {
+		wpabuf_free(pub);
+		wpabuf_clear_free(secret);
+		return -1;
+	}
+
+	hkey = wpabuf_alloc(wpabuf_len(pub) + elems.owe_dh_len - 2 + 2);
+	if (!hkey) {
+		wpabuf_free(pub);
+		wpabuf_clear_free(secret);
+		return -1;
+	}
+
+	wpabuf_put_buf(hkey, pub); /* C */
+	wpabuf_free(pub);
+	wpabuf_put_data(hkey, elems.owe_dh + 2, elems.owe_dh_len - 2); /* A */
+	wpabuf_put_le16(hkey, sm->owe_group); /* group */
+	if (group == 19)
+		res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey),
+				  wpabuf_head(secret), wpabuf_len(secret), prk);
+	else if (group == 20)
+		res = hmac_sha384(wpabuf_head(hkey), wpabuf_len(hkey),
+				  wpabuf_head(secret), wpabuf_len(secret), prk);
+	else if (group == 21)
+		res = hmac_sha512(wpabuf_head(hkey), wpabuf_len(hkey),
+				  wpabuf_head(secret), wpabuf_len(secret), prk);
+	wpabuf_clear_free(hkey);
+	wpabuf_clear_free(secret);
+	if (res < 0)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, hash_len);
+
+	/* PMK = HKDF-expand(prk, "OWE Key Generation", n) */
+
+	if (group == 19)
+		res = hmac_sha256_kdf(prk, hash_len, NULL, (const u8 *) info,
+				      os_strlen(info), sm->pmk, hash_len);
+	else if (group == 20)
+		res = hmac_sha384_kdf(prk, hash_len, NULL, (const u8 *) info,
+				      os_strlen(info), sm->pmk, hash_len);
+	else if (group == 21)
+		res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info,
+				      os_strlen(info), sm->pmk, hash_len);
+	os_memset(prk, 0, SHA512_MAC_LEN);
+	if (res < 0)
+		return -1;
+	sm->pmk_len = hash_len;
+
+	wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sm->pmk, sm->pmk_len);
+	wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN);
+	pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, pmkid, NULL, 0,
+			bssid, sm->own_addr, sm->network_ctx, sm->key_mgmt,
+			NULL);
+
+	return 0;
+}
+
+#endif /* CONFIG_OWE */
+
+
+void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id)
+{
+#ifdef CONFIG_FILS
+	if (sm && fils_cache_id) {
+		sm->fils_cache_id_set = 1;
+		os_memcpy(sm->fils_cache_id, fils_cache_id, FILS_CACHE_ID_LEN);
+	}
+#endif /* CONFIG_FILS */
+}
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index bde8c78..81c0171 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -39,9 +39,10 @@
 	u8 * (*alloc_eapol)(void *ctx, u8 type, const void *data, u16 data_len,
 			    size_t *msg_len, void **data_pos);
 	int (*add_pmkid)(void *ctx, void *network_ctx, const u8 *bssid,
-			 const u8 *pmkid);
+			 const u8 *pmkid, const u8 *fils_cache_id,
+			 const u8 *pmk, size_t pmk_len);
 	int (*remove_pmkid)(void *ctx, void *network_ctx, const u8 *bssid,
-			    const u8 *pmkid);
+			    const u8 *pmkid, const u8 *fils_cache_id);
 	void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob);
 	const struct wpa_config_blob * (*get_config_blob)(void *ctx,
 							  const char *name);
@@ -99,7 +100,6 @@
 
 struct rsn_supp_config {
 	void *network_ctx;
-	int peerkey_enabled;
 	int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */
 	int proactive_key_caching;
 	int eap_workaround;
@@ -109,6 +109,7 @@
 	int wpa_ptk_rekey;
 	int p2p;
 	int wpa_rsc_relaxation;
+	const u8 *fils_cache_id;
 };
 
 #ifndef CONFIG_NO_WPA
@@ -155,6 +156,11 @@
 struct rsn_pmksa_cache_entry *
 wpa_sm_pmksa_cache_add_entry(struct wpa_sm *sm,
 			     struct rsn_pmksa_cache_entry * entry);
+void wpa_sm_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
+			    const u8 *pmkid, const u8 *bssid,
+			    const u8 *fils_cache_id);
+int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid,
+			const void *network_ctx);
 void wpa_sm_drop_sa(struct wpa_sm *sm);
 int wpa_sm_has_ptk(struct wpa_sm *sm);
 
@@ -168,6 +174,7 @@
 void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm,
 			    const u8 *ptk_kck, size_t ptk_kck_len,
 			    const u8 *ptk_kek, size_t ptk_kek_len);
+int wpa_fils_is_completed(struct wpa_sm *sm);
 
 #else /* CONFIG_NO_WPA */
 
@@ -335,24 +342,12 @@
 {
 }
 
-#endif /* CONFIG_NO_WPA */
-
-#ifdef CONFIG_PEERKEY
-int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer);
-int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr,
-			    const u8 *buf, size_t len);
-#else /* CONFIG_PEERKEY */
-static inline int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer)
-{
-	return -1;
-}
-
-static inline int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr,
-					  const u8 *buf, size_t len)
+static inline int wpa_fils_is_completed(struct wpa_sm *sm)
 {
 	return 0;
 }
-#endif /* CONFIG_PEERKEY */
+
+#endif /* CONFIG_NO_WPA */
 
 #ifdef CONFIG_IEEE80211R
 
@@ -433,15 +428,23 @@
 
 int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf);
 void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf);
+const u8 * wpa_sm_get_anonce(struct wpa_sm *sm);
 
-struct wpabuf * fils_build_auth(struct wpa_sm *sm);
-int fils_process_auth(struct wpa_sm *sm, const u8 *data, size_t len);
+struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md);
+int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
+		      size_t len);
 struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek,
 				     size_t *kek_len, const u8 **snonce,
 				     const u8 **anonce,
 				     const struct wpabuf **hlp,
 				     unsigned int num_hlp);
 int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len);
-int wpa_fils_is_completed(struct wpa_sm *sm);
+
+struct wpabuf * owe_build_assoc_req(struct wpa_sm *sm, u16 group);
+int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid,
+			   const u8 *resp_ies, size_t resp_ies_len);
+
+void wpa_sm_set_reset_fils_completed(struct wpa_sm *sm, int set);
+void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id);
 
 #endif /* WPA_H */
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index d45bb45..1ff7afe 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -205,6 +205,12 @@
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
 	else if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE)
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
+#ifdef CONFIG_FILS
+	else if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256)
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
+	else if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384)
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
+#endif /* CONFIG_FILS */
 	else {
 		wpa_printf(MSG_WARNING, "FT: Invalid key management type (%d)",
 			   sm->key_mgmt);
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index b061638..e8da194 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -1,6 +1,6 @@
 /*
  * Internal WPA/RSN supplicant state machine definitions
- * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -11,7 +11,6 @@
 
 #include "utils/list.h"
 
-struct wpa_peerkey;
 struct wpa_tdls_peer;
 struct wpa_eapol_key;
 
@@ -57,7 +56,6 @@
 	int fast_reauth; /* whether EAP fast re-authentication is enabled */
 
 	void *network_ctx;
-	int peerkey_enabled;
 	int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */
 	int proactive_key_caching;
 	int eap_workaround;
@@ -94,9 +92,6 @@
 	u8 *ap_wpa_ie, *ap_rsn_ie;
 	size_t ap_wpa_ie_len, ap_rsn_ie_len;
 
-#ifdef CONFIG_PEERKEY
-	struct wpa_peerkey *peerkey;
-#endif /* CONFIG_PEERKEY */
 #ifdef CONFIG_TDLS
 	struct wpa_tdls_peer *tdls;
 	int tdls_prohibited;
@@ -154,8 +149,21 @@
 	size_t fils_key_auth_len;
 	unsigned int fils_completed:1;
 	unsigned int fils_erp_pmkid_set:1;
+	unsigned int fils_cache_id_set:1;
 	u8 fils_erp_pmkid[PMKID_LEN];
+	u8 fils_cache_id[FILS_CACHE_ID_LEN];
+	struct crypto_ecdh *fils_ecdh;
+	int fils_dh_group;
+	size_t fils_dh_elem_len;
+	struct wpabuf *fils_ft_ies;
+	u8 fils_ft[FILS_FT_MAX_LEN];
+	size_t fils_ft_len;
 #endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+	struct crypto_ecdh *owe_ecdh;
+	u16 owe_group;
+#endif /* CONFIG_OWE */
 };
 
 
@@ -228,17 +236,22 @@
 }
 
 static inline int wpa_sm_add_pmkid(struct wpa_sm *sm, void *network_ctx,
-				   const u8 *bssid, const u8 *pmkid)
+				   const u8 *bssid, const u8 *pmkid,
+				   const u8 *cache_id, const u8 *pmk,
+				   size_t pmk_len)
 {
 	WPA_ASSERT(sm->ctx->add_pmkid);
-	return sm->ctx->add_pmkid(sm->ctx->ctx, network_ctx, bssid, pmkid);
+	return sm->ctx->add_pmkid(sm->ctx->ctx, network_ctx, bssid, pmkid,
+				  cache_id, pmk, pmk_len);
 }
 
 static inline int wpa_sm_remove_pmkid(struct wpa_sm *sm, void *network_ctx,
-				      const u8 *bssid, const u8 *pmkid)
+				      const u8 *bssid, const u8 *pmkid,
+				      const u8 *cache_id)
 {
 	WPA_ASSERT(sm->ctx->remove_pmkid);
-	return sm->ctx->remove_pmkid(sm->ctx->ctx, network_ctx, bssid, pmkid);
+	return sm->ctx->remove_pmkid(sm->ctx->ctx, network_ctx, bssid, pmkid,
+				     cache_id);
 }
 
 static inline int wpa_sm_mlme_setprotection(struct wpa_sm *sm, const u8 *addr,
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index 3be3087..d649058 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -192,6 +192,14 @@
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
 #endif /* CONFIG_IEEE80211R */
 #endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+	} else if (key_mgmt & WPA_KEY_MGMT_OWE) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OWE);
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+	} else if (key_mgmt & WPA_KEY_MGMT_DPP) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP);
+#endif /* CONFIG_DPP */
 	} else {
 		wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
 			   key_mgmt);
@@ -417,44 +425,6 @@
 		return 0;
 	}
 
-#ifdef CONFIG_PEERKEY
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) {
-		ie->smk = pos + 2 + RSN_SELECTOR_LEN;
-		ie->smk_len = pos[1] - RSN_SELECTOR_LEN;
-		wpa_hexdump_key(MSG_DEBUG, "WPA: SMK in EAPOL-Key",
-				pos, pos[1] + 2);
-		return 0;
-	}
-
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) {
-		ie->nonce = pos + 2 + RSN_SELECTOR_LEN;
-		ie->nonce_len = pos[1] - RSN_SELECTOR_LEN;
-		wpa_hexdump(MSG_DEBUG, "WPA: Nonce in EAPOL-Key",
-			    pos, pos[1] + 2);
-		return 0;
-	}
-
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) {
-		ie->lifetime = pos + 2 + RSN_SELECTOR_LEN;
-		ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN;
-		wpa_hexdump(MSG_DEBUG, "WPA: Lifetime in EAPOL-Key",
-			    pos, pos[1] + 2);
-		return 0;
-	}
-
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) {
-		ie->error = pos + 2 + RSN_SELECTOR_LEN;
-		ie->error_len = pos[1] - RSN_SELECTOR_LEN;
-		wpa_hexdump(MSG_DEBUG, "WPA: Error in EAPOL-Key",
-			    pos, pos[1] + 2);
-		return 0;
-	}
-#endif /* CONFIG_PEERKEY */
-
 #ifdef CONFIG_IEEE80211W
 	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
 	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h
index fe95af0..0e72af5 100644
--- a/src/rsn_supp/wpa_ie.h
+++ b/src/rsn_supp/wpa_ie.h
@@ -21,16 +21,6 @@
 	size_t gtk_len;
 	const u8 *mac_addr;
 	size_t mac_addr_len;
-#ifdef CONFIG_PEERKEY
-	const u8 *smk;
-	size_t smk_len;
-	const u8 *nonce;
-	size_t nonce_len;
-	const u8 *lifetime;
-	size_t lifetime_len;
-	const u8 *error;
-	size_t error_len;
-#endif /* CONFIG_PEERKEY */
 #ifdef CONFIG_IEEE80211W
 	const u8 *igtk;
 	size_t igtk_len;
diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c
index 244c3cb..e66f1a9 100644
--- a/src/tls/tlsv1_client_read.c
+++ b/src/tls/tlsv1_client_read.c
@@ -685,10 +685,9 @@
 			    pos, conn->dh_p_len);
 		goto fail;
 	}
-	conn->dh_p = os_malloc(conn->dh_p_len);
+	conn->dh_p = os_memdup(pos, conn->dh_p_len);
 	if (conn->dh_p == NULL)
 		goto fail;
-	os_memcpy(conn->dh_p, pos, conn->dh_p_len);
 	pos += conn->dh_p_len;
 	wpa_hexdump(MSG_DEBUG, "TLSv1: DH p (prime)",
 		    conn->dh_p, conn->dh_p_len);
@@ -700,10 +699,9 @@
 	if (val == 0 || val > (size_t) (end - pos))
 		goto fail;
 	conn->dh_g_len = val;
-	conn->dh_g = os_malloc(conn->dh_g_len);
+	conn->dh_g = os_memdup(pos, conn->dh_g_len);
 	if (conn->dh_g == NULL)
 		goto fail;
-	os_memcpy(conn->dh_g, pos, conn->dh_g_len);
 	pos += conn->dh_g_len;
 	wpa_hexdump(MSG_DEBUG, "TLSv1: DH g (generator)",
 		    conn->dh_g, conn->dh_g_len);
@@ -717,10 +715,9 @@
 	if (val == 0 || val > (size_t) (end - pos))
 		goto fail;
 	conn->dh_ys_len = val;
-	conn->dh_ys = os_malloc(conn->dh_ys_len);
+	conn->dh_ys = os_memdup(pos, conn->dh_ys_len);
 	if (conn->dh_ys == NULL)
 		goto fail;
-	os_memcpy(conn->dh_ys, pos, conn->dh_ys_len);
 	pos += conn->dh_ys_len;
 	wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)",
 		    conn->dh_ys, conn->dh_ys_len);
diff --git a/src/tls/tlsv1_cred.c b/src/tls/tlsv1_cred.c
index 52c1ae0..842e5dd 100644
--- a/src/tls/tlsv1_cred.c
+++ b/src/tls/tlsv1_cred.c
@@ -1166,10 +1166,9 @@
 	if (hdr.length == 0)
 		return -1;
 	os_free(cred->dh_p);
-	cred->dh_p = os_malloc(hdr.length);
+	cred->dh_p = os_memdup(hdr.payload, hdr.length);
 	if (cred->dh_p == NULL)
 		return -1;
-	os_memcpy(cred->dh_p, hdr.payload, hdr.length);
 	cred->dh_p_len = hdr.length;
 	pos = hdr.payload + hdr.length;
 
@@ -1188,10 +1187,9 @@
 	if (hdr.length == 0)
 		return -1;
 	os_free(cred->dh_g);
-	cred->dh_g = os_malloc(hdr.length);
+	cred->dh_g = os_memdup(hdr.payload, hdr.length);
 	if (cred->dh_g == NULL)
 		return -1;
-	os_memcpy(cred->dh_g, hdr.payload, hdr.length);
 	cred->dh_g_len = hdr.length;
 
 	return 0;
diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c
index 0d6ee67..f80c9a3 100644
--- a/src/tls/x509v3.c
+++ b/src/tls/x509v3.c
@@ -274,13 +274,12 @@
 		 */
 	}
 	os_free(cert->public_key);
-	cert->public_key = os_malloc(hdr.length - 1);
+	cert->public_key = os_memdup(pos + 1, hdr.length - 1);
 	if (cert->public_key == NULL) {
 		wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for "
 			   "public key");
 		return -1;
 	}
-	os_memcpy(cert->public_key, pos + 1, hdr.length - 1);
 	cert->public_key_len = hdr.length - 1;
 	wpa_hexdump(MSG_MSGDUMP, "X509: subjectPublicKey",
 		    cert->public_key, cert->public_key_len);
@@ -925,10 +924,9 @@
 	/* iPAddress OCTET STRING */
 	wpa_hexdump(MSG_MSGDUMP, "X509: altName - iPAddress", pos, len);
 	os_free(name->ip);
-	name->ip = os_malloc(len);
+	name->ip = os_memdup(pos, len);
 	if (name->ip == NULL)
 		return -1;
-	os_memcpy(name->ip, pos, len);
 	name->ip_len = len;
 	return 0;
 }
@@ -1700,14 +1698,13 @@
 		return NULL;
 	}
 	os_free(cert->sign_value);
-	cert->sign_value = os_malloc(hdr.length - 1);
+	cert->sign_value = os_memdup(pos + 1, hdr.length - 1);
 	if (cert->sign_value == NULL) {
 		wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for "
 			   "signatureValue");
 		x509_certificate_free(cert);
 		return NULL;
 	}
-	os_memcpy(cert->sign_value, pos + 1, hdr.length - 1);
 	cert->sign_value_len = hdr.length - 1;
 	wpa_hexdump(MSG_MSGDUMP, "X509: signature",
 		    cert->sign_value, cert->sign_value_len);
diff --git a/src/utils/base64.c b/src/utils/base64.c
index d44f290..8eb4ba1 100644
--- a/src/utils/base64.c
+++ b/src/utils/base64.c
@@ -13,21 +13,14 @@
 
 static const unsigned char base64_table[65] =
 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const unsigned char base64_url_table[65] =
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
 
-/**
- * base64_encode - Base64 encode
- * @src: Data to be encoded
- * @len: Length of the data to be encoded
- * @out_len: Pointer to output length variable, or %NULL if not used
- * Returns: Allocated buffer of out_len bytes of encoded data,
- * or %NULL on failure
- *
- * Caller is responsible for freeing the returned buffer. Returned buffer is
- * nul terminated to make it easier to use as a C string. The nul terminator is
- * not included in out_len.
- */
-unsigned char * base64_encode(const unsigned char *src, size_t len,
-			      size_t *out_len)
+
+static unsigned char * base64_gen_encode(const unsigned char *src, size_t len,
+					 size_t *out_len,
+					 const unsigned char *table,
+					 int add_pad)
 {
 	unsigned char *out, *pos;
 	const unsigned char *end, *in;
@@ -35,7 +28,8 @@
 	int line_len;
 
 	olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
-	olen += olen / 72; /* line feeds */
+	if (add_pad)
+		olen += olen / 72; /* line feeds */
 	olen++; /* nul termination */
 	if (olen < len)
 		return NULL; /* integer overflow */
@@ -48,35 +42,35 @@
 	pos = out;
 	line_len = 0;
 	while (end - in >= 3) {
-		*pos++ = base64_table[(in[0] >> 2) & 0x3f];
-		*pos++ = base64_table[(((in[0] & 0x03) << 4) |
-				       (in[1] >> 4)) & 0x3f];
-		*pos++ = base64_table[(((in[1] & 0x0f) << 2) |
-				       (in[2] >> 6)) & 0x3f];
-		*pos++ = base64_table[in[2] & 0x3f];
+		*pos++ = table[(in[0] >> 2) & 0x3f];
+		*pos++ = table[(((in[0] & 0x03) << 4) | (in[1] >> 4)) & 0x3f];
+		*pos++ = table[(((in[1] & 0x0f) << 2) | (in[2] >> 6)) & 0x3f];
+		*pos++ = table[in[2] & 0x3f];
 		in += 3;
 		line_len += 4;
-		if (line_len >= 72) {
+		if (add_pad && line_len >= 72) {
 			*pos++ = '\n';
 			line_len = 0;
 		}
 	}
 
 	if (end - in) {
-		*pos++ = base64_table[(in[0] >> 2) & 0x3f];
+		*pos++ = table[(in[0] >> 2) & 0x3f];
 		if (end - in == 1) {
-			*pos++ = base64_table[((in[0] & 0x03) << 4) & 0x3f];
-			*pos++ = '=';
+			*pos++ = table[((in[0] & 0x03) << 4) & 0x3f];
+			if (add_pad)
+				*pos++ = '=';
 		} else {
-			*pos++ = base64_table[(((in[0] & 0x03) << 4) |
-					       (in[1] >> 4)) & 0x3f];
-			*pos++ = base64_table[((in[1] & 0x0f) << 2) & 0x3f];
+			*pos++ = table[(((in[0] & 0x03) << 4) |
+					(in[1] >> 4)) & 0x3f];
+			*pos++ = table[((in[1] & 0x0f) << 2) & 0x3f];
 		}
-		*pos++ = '=';
+		if (add_pad)
+			*pos++ = '=';
 		line_len += 4;
 	}
 
-	if (line_len)
+	if (add_pad && line_len)
 		*pos++ = '\n';
 
 	*pos = '\0';
@@ -86,26 +80,18 @@
 }
 
 
-/**
- * base64_decode - Base64 decode
- * @src: Data to be decoded
- * @len: Length of the data to be decoded
- * @out_len: Pointer to output length variable
- * Returns: Allocated buffer of out_len bytes of decoded data,
- * or %NULL on failure
- *
- * Caller is responsible for freeing the returned buffer.
- */
-unsigned char * base64_decode(const unsigned char *src, size_t len,
-			      size_t *out_len)
+static unsigned char * base64_gen_decode(const unsigned char *src, size_t len,
+					 size_t *out_len,
+					 const unsigned char *table)
 {
 	unsigned char dtable[256], *out, *pos, block[4], tmp;
 	size_t i, count, olen;
 	int pad = 0;
+	size_t extra_pad;
 
 	os_memset(dtable, 0x80, 256);
 	for (i = 0; i < sizeof(base64_table) - 1; i++)
-		dtable[base64_table[i]] = (unsigned char) i;
+		dtable[table[i]] = (unsigned char) i;
 	dtable['='] = 0;
 
 	count = 0;
@@ -114,21 +100,28 @@
 			count++;
 	}
 
-	if (count == 0 || count % 4)
+	if (count == 0)
 		return NULL;
+	extra_pad = (4 - count % 4) % 4;
 
-	olen = count / 4 * 3;
+	olen = (count + extra_pad) / 4 * 3;
 	pos = out = os_malloc(olen);
 	if (out == NULL)
 		return NULL;
 
 	count = 0;
-	for (i = 0; i < len; i++) {
-		tmp = dtable[src[i]];
+	for (i = 0; i < len + extra_pad; i++) {
+		unsigned char val;
+
+		if (i >= len)
+			val = '=';
+		else
+			val = src[i];
+		tmp = dtable[val];
 		if (tmp == 0x80)
 			continue;
 
-		if (src[i] == '=')
+		if (val == '=')
 			pad++;
 		block[count] = tmp;
 		count++;
@@ -155,3 +148,53 @@
 	*out_len = pos - out;
 	return out;
 }
+
+
+/**
+ * base64_encode - Base64 encode
+ * @src: Data to be encoded
+ * @len: Length of the data to be encoded
+ * @out_len: Pointer to output length variable, or %NULL if not used
+ * Returns: Allocated buffer of out_len bytes of encoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer. Returned buffer is
+ * nul terminated to make it easier to use as a C string. The nul terminator is
+ * not included in out_len.
+ */
+unsigned char * base64_encode(const unsigned char *src, size_t len,
+			      size_t *out_len)
+{
+	return base64_gen_encode(src, len, out_len, base64_table, 1);
+}
+
+
+unsigned char * base64_url_encode(const unsigned char *src, size_t len,
+				  size_t *out_len, int add_pad)
+{
+	return base64_gen_encode(src, len, out_len, base64_url_table, add_pad);
+}
+
+
+/**
+ * base64_decode - Base64 decode
+ * @src: Data to be decoded
+ * @len: Length of the data to be decoded
+ * @out_len: Pointer to output length variable
+ * Returns: Allocated buffer of out_len bytes of decoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer.
+ */
+unsigned char * base64_decode(const unsigned char *src, size_t len,
+			      size_t *out_len)
+{
+	return base64_gen_decode(src, len, out_len, base64_table);
+}
+
+
+unsigned char * base64_url_decode(const unsigned char *src, size_t len,
+				  size_t *out_len)
+{
+	return base64_gen_decode(src, len, out_len, base64_url_table);
+}
diff --git a/src/utils/base64.h b/src/utils/base64.h
index aa21fd0..5a72c3e 100644
--- a/src/utils/base64.h
+++ b/src/utils/base64.h
@@ -13,5 +13,9 @@
 			      size_t *out_len);
 unsigned char * base64_decode(const unsigned char *src, size_t len,
 			      size_t *out_len);
+unsigned char * base64_url_encode(const unsigned char *src, size_t len,
+				  size_t *out_len, int add_pad);
+unsigned char * base64_url_decode(const unsigned char *src, size_t len,
+				  size_t *out_len);
 
 #endif /* BASE64_H */
diff --git a/src/utils/common.h b/src/utils/common.h
index 8842864..46e96a6 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -53,6 +53,15 @@
 }
 #endif /* __APPLE__ */
 
+#ifdef __rtems__
+#include <rtems/endian.h>
+#define __BYTE_ORDER BYTE_ORDER
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#define __BIG_ENDIAN BIG_ENDIAN
+#define bswap_16 CPU_swap_u16
+#define bswap_32 CPU_swap_u32
+#endif /* __rtems__ */
+
 #ifdef CONFIG_NATIVE_WINDOWS
 #include <winsock.h>
 
@@ -331,6 +340,9 @@
 #ifndef ETH_P_RRB
 #define ETH_P_RRB 0x890D
 #endif /* ETH_P_RRB */
+#ifndef ETH_P_OUI
+#define ETH_P_OUI 0x88B7
+#endif /* ETH_P_OUI */
 
 
 #ifdef __GNUC__
diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c
index a06aae8..58519ea 100644
--- a/src/utils/http_curl.c
+++ b/src/utils/http_curl.c
@@ -486,12 +486,11 @@
 		return;
 
 	n->hash_len = ASN1_STRING_length(hash->hashValue);
-	n->hash = os_malloc(n->hash_len);
+	n->hash = os_memdup(ASN1_STRING_data(hash->hashValue), n->hash_len);
 	if (n->hash == NULL) {
 		os_free(n->alg_oid);
 		return;
 	}
-	os_memcpy(n->hash, ASN1_STRING_data(hash->hashValue), n->hash_len);
 
 	len = ASN1_STRING_length(uri);
 	n->uri = os_malloc(len + 1);
@@ -987,7 +986,7 @@
 
 	ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
 					 SSL_get_ex_data_X509_STORE_CTX_idx());
-	ssl_ctx = ssl->ctx;
+	ssl_ctx = SSL_get_SSL_CTX(ssl);
 	ctx = SSL_CTX_get_app_data(ssl_ctx);
 
 	wpa_printf(MSG_DEBUG, "curl_cb_ssl_verify, preverify_ok: %d",
@@ -1095,7 +1094,7 @@
 {
 	struct http_ctx *ctx = arg;
 	const unsigned char *p;
-	int len, status, reason;
+	int len, status, reason, res;
 	OCSP_RESPONSE *rsp;
 	OCSP_BASICRESP *basic;
 	OCSP_CERTID *id;
@@ -1200,17 +1199,36 @@
 		return 0;
 	}
 
-	id = OCSP_cert_to_id(NULL, ctx->peer_cert, ctx->peer_issuer);
+	id = OCSP_cert_to_id(EVP_sha256(), ctx->peer_cert, ctx->peer_issuer);
 	if (!id) {
-		wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier");
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Could not create OCSP certificate identifier (SHA256)");
 		OCSP_BASICRESP_free(basic);
 		OCSP_RESPONSE_free(rsp);
 		ctx->last_err = "Could not create OCSP certificate identifier";
 		return 0;
 	}
 
-	if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at,
-				   &this_update, &next_update)) {
+	res = OCSP_resp_find_status(basic, id, &status, &reason, &produced_at,
+				    &this_update, &next_update);
+	if (!res) {
+		id = OCSP_cert_to_id(NULL, ctx->peer_cert, ctx->peer_issuer);
+		if (!id) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: Could not create OCSP certificate identifier (SHA1)");
+			OCSP_BASICRESP_free(basic);
+			OCSP_RESPONSE_free(rsp);
+			ctx->last_err =
+				"Could not create OCSP certificate identifier";
+			return 0;
+		}
+
+		res = OCSP_resp_find_status(basic, id, &status, &reason,
+					    &produced_at, &this_update,
+					    &next_update);
+	}
+
+	if (!res) {
 		wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s",
 			   (ctx->ocsp == MANDATORY_OCSP) ? "" :
 			   " (OCSP not required)");
diff --git a/src/utils/json.c b/src/utils/json.c
new file mode 100644
index 0000000..eae627d
--- /dev/null
+++ b/src/utils/json.c
@@ -0,0 +1,567 @@
+/*
+ * JavaScript Object Notation (JSON) parser (RFC7159)
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "base64.h"
+#include "json.h"
+
+#define JSON_MAX_DEPTH 10
+#define JSON_MAX_TOKENS 500
+
+
+void json_escape_string(char *txt, size_t maxlen, const char *data, size_t len)
+{
+	char *end = txt + maxlen;
+	size_t i;
+
+	for (i = 0; i < len; i++) {
+		if (txt + 4 >= end)
+			break;
+
+		switch (data[i]) {
+		case '\"':
+			*txt++ = '\\';
+			*txt++ = '\"';
+			break;
+		case '\\':
+			*txt++ = '\\';
+			*txt++ = '\\';
+			break;
+		case '\n':
+			*txt++ = '\\';
+			*txt++ = 'n';
+			break;
+		case '\r':
+			*txt++ = '\\';
+			*txt++ = 'r';
+			break;
+		case '\t':
+			*txt++ = '\\';
+			*txt++ = 't';
+			break;
+		default:
+			if (data[i] >= 32 && data[i] <= 126) {
+				*txt++ = data[i];
+			} else {
+				txt += os_snprintf(txt, end - txt, "\\u%04x",
+						   data[i]);
+			}
+			break;
+		}
+	}
+
+	*txt = '\0';
+}
+
+
+static char * json_parse_string(const char **json_pos, const char *end)
+{
+	const char *pos = *json_pos;
+	char *str, *spos, *s_end;
+	size_t max_len, buf_len;
+	u8 bin[2];
+
+	pos++; /* skip starting quote */
+
+	max_len = end - pos + 1;
+	buf_len = max_len > 10 ? 10 : max_len;
+	str = os_malloc(buf_len);
+	if (!str)
+		return NULL;
+	spos = str;
+	s_end = str + buf_len;
+
+	for (; pos < end; pos++) {
+		if (buf_len < max_len && s_end - spos < 3) {
+			char *tmp;
+			int idx;
+
+			idx = spos - str;
+			buf_len *= 2;
+			if (buf_len > max_len)
+				buf_len = max_len;
+			tmp = os_realloc(str, buf_len);
+			if (!tmp)
+				goto fail;
+			str = tmp;
+			spos = str + idx;
+			s_end = str + buf_len;
+		}
+
+		switch (*pos) {
+		case '\"': /* end string */
+			*spos = '\0';
+			/* caller will move to the next position */
+			*json_pos = pos;
+			return str;
+		case '\\':
+			pos++;
+			switch (*pos) {
+			case '"':
+			case '\\':
+			case '/':
+				*spos++ = *pos;
+				break;
+			case 'n':
+				*spos++ = '\n';
+				break;
+			case 'r':
+				*spos++ = '\r';
+				break;
+			case 't':
+				*spos++ = '\t';
+				break;
+			case 'u':
+				if (end - pos < 5 ||
+				    hexstr2bin(pos + 1, bin, 2) < 0 ||
+				    bin[1] == 0x00) {
+					wpa_printf(MSG_DEBUG,
+						   "JSON: Invalid \\u escape");
+					goto fail;
+				}
+				if (bin[0] == 0x00) {
+					*spos++ = bin[1];
+				} else {
+					*spos++ = bin[0];
+					*spos++ = bin[1];
+				}
+				pos += 4;
+				break;
+			default:
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Unknown escape '%c'", *pos);
+				goto fail;
+			}
+			break;
+		default:
+			*spos++ = *pos;
+			break;
+		}
+	}
+
+fail:
+	os_free(str);
+	return NULL;
+}
+
+
+static int json_parse_number(const char **json_pos, const char *end,
+			     int *ret_val)
+{
+	const char *pos = *json_pos;
+	size_t len;
+	char *str;
+
+	for (; pos < end; pos++) {
+		if (*pos != '-' && (*pos < '0' || *pos > '9')) {
+			pos--;
+			break;
+		}
+	}
+	if (pos < *json_pos)
+		return -1;
+	len = pos - *json_pos + 1;
+	str = os_malloc(len + 1);
+	if (!str)
+		return -1;
+	os_memcpy(str, *json_pos, len);
+	str[len] = '\0';
+
+	*ret_val = atoi(str);
+	os_free(str);
+	*json_pos = pos;
+	return 0;
+}
+
+
+static int json_check_tree_state(struct json_token *token)
+{
+	if (!token)
+		return 0;
+	if (json_check_tree_state(token->child) < 0 ||
+	    json_check_tree_state(token->sibling) < 0)
+		return -1;
+	if (token->state != JSON_COMPLETED) {
+		wpa_printf(MSG_DEBUG,
+			   "JSON: Unexpected token state %d (name=%s type=%d)",
+			   token->state, token->name ? token->name : "N/A",
+			   token->type);
+		return -1;
+	}
+	return 0;
+}
+
+
+static struct json_token * json_alloc_token(unsigned int *tokens)
+{
+	(*tokens)++;
+	if (*tokens > JSON_MAX_TOKENS) {
+		wpa_printf(MSG_DEBUG, "JSON: Maximum token limit exceeded");
+		return NULL;
+	}
+	return os_zalloc(sizeof(struct json_token));
+}
+
+
+struct json_token * json_parse(const char *data, size_t data_len)
+{
+	struct json_token *root = NULL, *curr_token = NULL, *token = NULL;
+	const char *pos, *end;
+	char *str;
+	int num;
+	unsigned int depth = 0;
+	unsigned int tokens = 0;
+
+	pos = data;
+	end = data + data_len;
+
+	for (; pos < end; pos++) {
+		switch (*pos) {
+		case '[': /* start array */
+		case '{': /* start object */
+			if (!curr_token) {
+				token = json_alloc_token(&tokens);
+				if (!token)
+					goto fail;
+			} else if (curr_token->state == JSON_WAITING_VALUE) {
+				token = curr_token;
+			} else if (curr_token->parent &&
+				   curr_token->parent->type == JSON_ARRAY &&
+				   curr_token->parent->state == JSON_STARTED &&
+				   curr_token->state == JSON_EMPTY) {
+				token = curr_token;
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Invalid state for start array/object");
+				goto fail;
+			}
+			depth++;
+			if (depth > JSON_MAX_DEPTH) {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Max depth exceeded");
+				goto fail;
+			}
+			token->type = *pos == '[' ? JSON_ARRAY : JSON_OBJECT;
+			token->state = JSON_STARTED;
+			token->child = json_alloc_token(&tokens);
+			if (!token->child)
+				goto fail;
+			curr_token = token->child;
+			curr_token->parent = token;
+			curr_token->state = JSON_EMPTY;
+			break;
+		case ']': /* end array */
+		case '}': /* end object */
+			if (!curr_token || !curr_token->parent ||
+			    curr_token->parent->state != JSON_STARTED) {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Invalid state for end array/object");
+				goto fail;
+			}
+			depth--;
+			curr_token = curr_token->parent;
+			if ((*pos == ']' &&
+			     curr_token->type != JSON_ARRAY) ||
+			    (*pos == '}' &&
+			     curr_token->type != JSON_OBJECT)) {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Array/Object mismatch");
+				goto fail;
+			}
+			if (curr_token->child->state == JSON_EMPTY &&
+			    !curr_token->child->child &&
+			    !curr_token->child->sibling) {
+				/* Remove pending child token since the
+				 * array/object was empty. */
+				json_free(curr_token->child);
+				curr_token->child = NULL;
+			}
+			curr_token->state = JSON_COMPLETED;
+			break;
+		case '\"': /* string */
+			str = json_parse_string(&pos, end);
+			if (!str)
+				goto fail;
+			if (!curr_token) {
+				token = json_alloc_token(&tokens);
+				if (!token)
+					goto fail;
+				token->type = JSON_STRING;
+				token->string = str;
+				token->state = JSON_COMPLETED;
+			} else if (curr_token->parent &&
+				   curr_token->parent->type == JSON_ARRAY &&
+				   curr_token->parent->state == JSON_STARTED &&
+				   curr_token->state == JSON_EMPTY) {
+				curr_token->string = str;
+				curr_token->state = JSON_COMPLETED;
+				curr_token->type = JSON_STRING;
+				wpa_printf(MSG_MSGDUMP,
+					   "JSON: String value: '%s'",
+					   curr_token->string);
+			} else if (curr_token->state == JSON_EMPTY) {
+				curr_token->type = JSON_VALUE;
+				curr_token->name = str;
+				curr_token->state = JSON_STARTED;
+			} else if (curr_token->state == JSON_WAITING_VALUE) {
+				curr_token->string = str;
+				curr_token->state = JSON_COMPLETED;
+				curr_token->type = JSON_STRING;
+				wpa_printf(MSG_MSGDUMP,
+					   "JSON: String value: '%s' = '%s'",
+					   curr_token->name,
+					   curr_token->string);
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Invalid state for a string");
+				os_free(str);
+				goto fail;
+			}
+			break;
+		case ' ':
+		case '\t':
+		case '\r':
+		case '\n':
+			/* ignore whitespace */
+			break;
+		case ':': /* name/value separator */
+			if (!curr_token || curr_token->state != JSON_STARTED)
+				goto fail;
+			curr_token->state = JSON_WAITING_VALUE;
+			break;
+		case ',': /* member separator */
+			if (!curr_token)
+				goto fail;
+			curr_token->sibling = json_alloc_token(&tokens);
+			if (!curr_token->sibling)
+				goto fail;
+			curr_token->sibling->parent = curr_token->parent;
+			curr_token = curr_token->sibling;
+			curr_token->state = JSON_EMPTY;
+			break;
+		case 't': /* true */
+		case 'f': /* false */
+		case 'n': /* null */
+			if (!((end - pos >= 4 &&
+			       os_strncmp(pos, "true", 4) == 0) ||
+			      (end - pos >= 5 &&
+			       os_strncmp(pos, "false", 5) == 0) ||
+			      (end - pos >= 4 &&
+			       os_strncmp(pos, "null", 4) == 0))) {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Invalid literal name");
+				goto fail;
+			}
+			if (!curr_token) {
+				token = json_alloc_token(&tokens);
+				if (!token)
+					goto fail;
+				curr_token = token;
+			} else if (curr_token->state == JSON_WAITING_VALUE) {
+				wpa_printf(MSG_MSGDUMP,
+					   "JSON: Literal name: '%s' = %c",
+					   curr_token->name, *pos);
+			} else if (curr_token->parent &&
+				   curr_token->parent->type == JSON_ARRAY &&
+				   curr_token->parent->state == JSON_STARTED &&
+				   curr_token->state == JSON_EMPTY) {
+				wpa_printf(MSG_MSGDUMP,
+					   "JSON: Literal name: %c", *pos);
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Invalid state for a literal name");
+				goto fail;
+			}
+			switch (*pos) {
+			case 't':
+				curr_token->type = JSON_BOOLEAN;
+				curr_token->number = 1;
+				pos += 3;
+				break;
+			case 'f':
+				curr_token->type = JSON_BOOLEAN;
+				curr_token->number = 0;
+				pos += 4;
+				break;
+			case 'n':
+				curr_token->type = JSON_NULL;
+				pos += 3;
+				break;
+			}
+			curr_token->state = JSON_COMPLETED;
+			break;
+		case '-':
+		case '0':
+		case '1':
+		case '2':
+		case '3':
+		case '4':
+		case '5':
+		case '6':
+		case '7':
+		case '8':
+		case '9':
+			/* number */
+			if (json_parse_number(&pos, end, &num) < 0)
+				goto fail;
+			if (!curr_token) {
+				token = json_alloc_token(&tokens);
+				if (!token)
+					goto fail;
+				token->type = JSON_NUMBER;
+				token->number = num;
+				token->state = JSON_COMPLETED;
+			} else if (curr_token->state == JSON_WAITING_VALUE) {
+				curr_token->number = num;
+				curr_token->state = JSON_COMPLETED;
+				curr_token->type = JSON_NUMBER;
+				wpa_printf(MSG_MSGDUMP,
+					   "JSON: Number value: '%s' = '%d'",
+					   curr_token->name,
+					   curr_token->number);
+			} else if (curr_token->parent &&
+				   curr_token->parent->type == JSON_ARRAY &&
+				   curr_token->parent->state == JSON_STARTED &&
+				   curr_token->state == JSON_EMPTY) {
+				curr_token->number = num;
+				curr_token->state = JSON_COMPLETED;
+				curr_token->type = JSON_NUMBER;
+				wpa_printf(MSG_MSGDUMP,
+					   "JSON: Number value: %d",
+					   curr_token->number);
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Invalid state for a number");
+				goto fail;
+			}
+			break;
+		default:
+			wpa_printf(MSG_DEBUG,
+				   "JSON: Unexpected JSON character: %c", *pos);
+			goto fail;
+		}
+
+		if (!root)
+			root = token;
+		if (!curr_token)
+			curr_token = token;
+	}
+
+	if (json_check_tree_state(root) < 0) {
+		wpa_printf(MSG_DEBUG, "JSON: Incomplete token in the tree");
+		goto fail;
+	}
+
+	return root;
+fail:
+	wpa_printf(MSG_DEBUG, "JSON: Parsing failed");
+	json_free(root);
+	return NULL;
+}
+
+
+void json_free(struct json_token *json)
+{
+	if (!json)
+		return;
+	json_free(json->child);
+	json_free(json->sibling);
+	os_free(json->name);
+	os_free(json->string);
+	os_free(json);
+}
+
+
+struct json_token * json_get_member(struct json_token *json, const char *name)
+{
+	struct json_token *token, *ret = NULL;
+
+	if (!json || json->type != JSON_OBJECT)
+		return NULL;
+	/* Return last matching entry */
+	for (token = json->child; token; token = token->sibling) {
+		if (token->name && os_strcmp(token->name, name) == 0)
+			ret = token;
+	}
+	return ret;
+}
+
+
+struct wpabuf * json_get_member_base64url(struct json_token *json,
+					  const char *name)
+{
+	struct json_token *token;
+	unsigned char *buf;
+	size_t buflen;
+	struct wpabuf *ret;
+
+	token = json_get_member(json, name);
+	if (!token || token->type != JSON_STRING)
+		return NULL;
+	buf = base64_url_decode((const unsigned char *) token->string,
+				os_strlen(token->string), &buflen);
+	if (!buf)
+		return NULL;
+	ret = wpabuf_alloc_ext_data(buf, buflen);
+	if (!ret)
+		os_free(buf);
+
+	return ret;
+}
+
+
+static const char * json_type_str(enum json_type type)
+{
+	switch (type) {
+	case JSON_VALUE:
+		return "VALUE";
+	case JSON_OBJECT:
+		return "OBJECT";
+	case JSON_ARRAY:
+		return "ARRAY";
+	case JSON_STRING:
+		return "STRING";
+	case JSON_NUMBER:
+		return "NUMBER";
+	case JSON_BOOLEAN:
+		return "BOOLEAN";
+	case JSON_NULL:
+		return "NULL";
+	}
+	return "??";
+}
+
+
+static void json_print_token(struct json_token *token, int depth,
+			     char *buf, size_t buflen)
+{
+	size_t len;
+	int ret;
+
+	if (!token)
+		return;
+	len = os_strlen(buf);
+	ret = os_snprintf(buf + len, buflen - len, "[%d:%s:%s]",
+			  depth, json_type_str(token->type),
+			  token->name ? token->name : "");
+	if (os_snprintf_error(buflen - len, ret)) {
+		buf[len] = '\0';
+		return;
+	}
+	json_print_token(token->child, depth + 1, buf, buflen);
+	json_print_token(token->sibling, depth, buf, buflen);
+}
+
+
+void json_print_tree(struct json_token *root, char *buf, size_t buflen)
+{
+	buf[0] = '\0';
+	json_print_token(root, 1, buf, buflen);
+}
diff --git a/src/utils/json.h b/src/utils/json.h
new file mode 100644
index 0000000..8faa95d
--- /dev/null
+++ b/src/utils/json.h
@@ -0,0 +1,42 @@
+/*
+ * JavaScript Object Notation (JSON) parser (RFC7159)
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef JSON_H
+#define JSON_H
+
+struct json_token {
+	enum json_type {
+		JSON_VALUE,
+		JSON_OBJECT,
+		JSON_ARRAY,
+		JSON_STRING,
+		JSON_NUMBER,
+		JSON_BOOLEAN,
+		JSON_NULL,
+	} type;
+	enum json_parsing_state {
+		JSON_EMPTY,
+		JSON_STARTED,
+		JSON_WAITING_VALUE,
+		JSON_COMPLETED,
+	} state;
+	char *name;
+	char *string;
+	int number;
+	struct json_token *parent, *child, *sibling;
+};
+
+void json_escape_string(char *txt, size_t maxlen, const char *data, size_t len);
+struct json_token * json_parse(const char *data, size_t data_len);
+void json_free(struct json_token *json);
+struct json_token * json_get_member(struct json_token *json, const char *name);
+struct wpabuf * json_get_member_base64url(struct json_token *json,
+					  const char *name);
+void json_print_tree(struct json_token *root, char *buf, size_t buflen);
+
+#endif /* JSON_H */
diff --git a/src/utils/os.h b/src/utils/os.h
index e8f0b79..21ba5c3 100644
--- a/src/utils/os.h
+++ b/src/utils/os.h
@@ -614,6 +614,18 @@
  */
 int os_memcmp_const(const void *a, const void *b, size_t len);
 
+
+/**
+ * os_memdup - Allocate duplicate of passed memory chunk
+ * @src: Source buffer to duplicate
+ * @len: Length of source buffer
+ * Returns: %NULL if allocation failed, copy of src buffer otherwise
+ *
+ * This function allocates a memory block like os_malloc() would, and
+ * copies the given source buffer into it.
+ */
+void * os_memdup(const void *src, size_t len);
+
 /**
  * os_exec - Execute an external program
  * @program: Path to the program
diff --git a/src/utils/os_none.c b/src/utils/os_none.c
index 0c3214d..e74f206 100644
--- a/src/utils/os_none.c
+++ b/src/utils/os_none.c
@@ -114,6 +114,12 @@
 }
 
 
+void * os_memdup(const void *src, size_t n)
+{
+	return NULL;
+}
+
+
 #ifdef OS_NO_C_LIB_DEFINES
 void * os_malloc(size_t size)
 {
diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c
index b516cc4..3f6388d 100644
--- a/src/utils/os_unix.c
+++ b/src/utils/os_unix.c
@@ -528,6 +528,16 @@
 }
 
 
+void * os_memdup(const void *src, size_t len)
+{
+	void *r = os_malloc(len);
+
+	if (r)
+		os_memcpy(r, src, len);
+	return r;
+}
+
+
 #ifdef WPA_TRACE
 
 #if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
@@ -560,6 +570,8 @@
 		i++;
 	if (i < res && os_strcmp(func[i], "os_strdup") == 0)
 		i++;
+	if (i < res && os_strcmp(func[i], "os_memdup") == 0)
+		i++;
 
 	pos = wpa_trace_fail_func;
 
diff --git a/src/utils/os_win32.c b/src/utils/os_win32.c
index dea27b9..f9e4b30 100644
--- a/src/utils/os_win32.c
+++ b/src/utils/os_win32.c
@@ -283,3 +283,13 @@
 {
 	return -1;
 }
+
+
+void * os_memdup(const void *src, size_t len)
+{
+	void *r = os_malloc(len);
+
+	if (r)
+		os_memcpy(r, src, len);
+	return r;
+}
diff --git a/src/utils/trace.c b/src/utils/trace.c
index d72cf60..e0b5b0b 100644
--- a/src/utils/trace.c
+++ b/src/utils/trace.c
@@ -6,6 +6,10 @@
  * See README for more details.
  */
 
+#ifdef WPA_TRACE_BFD
+#define _GNU_SOURCE
+#include <link.h>
+#endif /* WPA_TRACE_BCD */
 #include "includes.h"
 
 #include "common.h"
@@ -25,6 +29,28 @@
 static char *prg_fname = NULL;
 static bfd *cached_abfd = NULL;
 static asymbol **syms = NULL;
+static unsigned long start_offset;
+static int start_offset_looked_up;
+
+
+static int callback(struct dl_phdr_info *info, size_t size, void *data)
+{
+	/*
+	 * dl_iterate_phdr(3):
+	 * "The first object visited by callback is the main program."
+	 */
+	start_offset = info->dlpi_addr;
+
+	/*
+	 * dl_iterate_phdr(3):
+	 * "The dl_iterate_phdr() function walks through the list of an
+	 *  application's shared objects and calls the function callback
+	 *  once for each object, until either all shared objects have
+	 *  been processed or callback returns a nonzero value."
+	 */
+	return 1;
+}
+
 
 static void get_prg_fname(void)
 {
@@ -160,7 +186,7 @@
 	if (abfd == NULL)
 		return;
 
-	data.pc = (bfd_hostptr_t) pc;
+	data.pc = (bfd_hostptr_t) (pc - start_offset);
 	data.found = FALSE;
 	bfd_map_over_sections(abfd, find_addr_sect, &data);
 
@@ -201,7 +227,7 @@
 	if (abfd == NULL)
 		return NULL;
 
-	data.pc = (bfd_hostptr_t) pc;
+	data.pc = (bfd_hostptr_t) (pc - start_offset);
 	data.found = FALSE;
 	bfd_map_over_sections(abfd, find_addr_sect, &data);
 
@@ -233,6 +259,11 @@
 		wpa_printf(MSG_INFO, "Failed to read symbols");
 		return;
 	}
+
+	if (!start_offset_looked_up) {
+		dl_iterate_phdr(callback, NULL);
+		start_offset_looked_up = 1;
+	}
 }
 
 
@@ -268,7 +299,7 @@
 	for (i = 0; i < btrace_num; i++) {
 		struct bfd_data data;
 
-		data.pc = (bfd_hostptr_t) btrace_res[i];
+		data.pc = (bfd_hostptr_t) (btrace_res[i] - start_offset);
 		data.found = FALSE;
 		bfd_map_over_sections(abfd, find_addr_sect, &data);
 
diff --git a/src/utils/utils_module_tests.c b/src/utils/utils_module_tests.c
index abdb79c..1b8ff82 100644
--- a/src/utils/utils_module_tests.c
+++ b/src/utils/utils_module_tests.c
@@ -16,6 +16,7 @@
 #include "utils/base64.h"
 #include "utils/ip_addr.h"
 #include "utils/eloop.h"
+#include "utils/json.h"
 #include "utils/module_tests.h"
 
 
@@ -839,6 +840,85 @@
 }
 
 
+#ifdef CONFIG_JSON
+struct json_test_data {
+	const char *json;
+	const char *tree;
+};
+
+static const struct json_test_data json_test_cases[] = {
+	{ "{}", "[1:OBJECT:]" },
+	{ "[]", "[1:ARRAY:]" },
+	{ "{", NULL },
+	{ "[", NULL },
+	{ "}", NULL },
+	{ "]", NULL },
+	{ "[[]]", "[1:ARRAY:][2:ARRAY:]" },
+	{ "{\"t\":\"test\"}", "[1:OBJECT:][2:STRING:t]" },
+	{ "{\"t\":123}", "[1:OBJECT:][2:NUMBER:t]" },
+	{ "{\"t\":true}", "[1:OBJECT:][2:BOOLEAN:t]" },
+	{ "{\"t\":false}", "[1:OBJECT:][2:BOOLEAN:t]" },
+	{ "{\"t\":null}", "[1:OBJECT:][2:NULL:t]" },
+	{ "{\"t\":truetrue}", NULL },
+	{ "\"test\"", "[1:STRING:]" },
+	{ "123", "[1:NUMBER:]" },
+	{ "true", "[1:BOOLEAN:]" },
+	{ "false", "[1:BOOLEAN:]" },
+	{ "null", "[1:NULL:]" },
+	{ "truetrue", NULL },
+	{ " {\t\n\r\"a\"\n:\r1\n,\n\"b\":3\n}\n",
+	  "[1:OBJECT:][2:NUMBER:a][2:NUMBER:b]" },
+	{ ",", NULL },
+	{ "{,}", NULL },
+	{ "[,]", NULL },
+	{ ":", NULL },
+	{ "{:}", NULL },
+	{ "[:]", NULL },
+	{ "{ \"\\u005c\" : \"\\u005c\" }", "[1:OBJECT:][2:STRING:\\]" },
+	{ "[{},{}]", "[1:ARRAY:][2:OBJECT:][2:OBJECT:]" },
+	{ "[1,2]", "[1:ARRAY:][2:NUMBER:][2:NUMBER:]" },
+	{ "[\"1\",\"2\"]", "[1:ARRAY:][2:STRING:][2:STRING:]" },
+	{ "[true,false]", "[1:ARRAY:][2:BOOLEAN:][2:BOOLEAN:]" },
+};
+#endif /* CONFIG_JSON */
+
+
+static int json_tests(void)
+{
+#ifdef CONFIG_JSON
+	unsigned int i;
+	struct json_token *root;
+	char buf[1000];
+
+	wpa_printf(MSG_INFO, "JSON tests");
+
+	for (i = 0; i < ARRAY_SIZE(json_test_cases); i++) {
+		const struct json_test_data *test = &json_test_cases[i];
+		int res = 0;
+
+		root = json_parse(test->json, os_strlen(test->json));
+		if ((root && !test->tree) || (!root && test->tree)) {
+			wpa_printf(MSG_INFO, "JSON test %u failed", i);
+			res = -1;
+		} else if (root) {
+			json_print_tree(root, buf, sizeof(buf));
+			if (os_strcmp(buf, test->tree) != 0) {
+				wpa_printf(MSG_INFO,
+					   "JSON test %u tree mismatch: %s %s",
+					   i, buf, test->tree);
+				res = -1;
+			}
+		}
+		json_free(root);
+		if (res < 0)
+			return -1;
+
+	}
+#endif /* CONFIG_JSON */
+	return 0;
+}
+
+
 int utils_module_tests(void)
 {
 	int ret = 0;
@@ -855,6 +935,7 @@
 	    wpabuf_tests() < 0 ||
 	    ip_addr_tests() < 0 ||
 	    eloop_tests() < 0 ||
+	    json_tests() < 0 ||
 	    int_array_tests() < 0)
 		ret = -1;
 
diff --git a/src/utils/uuid.c b/src/utils/uuid.c
index 0f224f9..98e43d0 100644
--- a/src/utils/uuid.c
+++ b/src/utils/uuid.c
@@ -9,6 +9,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "crypto/sha256.h"
 #include "uuid.h"
 
 int uuid_str2bin(const char *str, u8 *bin)
@@ -69,3 +70,27 @@
 			return 0;
 	return 1;
 }
+
+
+int uuid_random(u8 *uuid)
+{
+	struct os_time t;
+	u8 hash[SHA256_MAC_LEN];
+
+	/* Use HMAC-SHA256 and timestamp as context to avoid exposing direct
+	 * os_get_random() output in the UUID field. */
+	os_get_time(&t);
+	if (os_get_random(uuid, UUID_LEN) < 0 ||
+	    hmac_sha256(uuid, UUID_LEN, (const u8 *) &t, sizeof(t), hash) < 0)
+		return -1;
+
+	os_memcpy(uuid, hash, UUID_LEN);
+
+	/* Version: 4 = random */
+	uuid[6] = (4 << 4) | (uuid[6] & 0x0f);
+
+	/* Variant specified in RFC 4122 */
+	uuid[8] = 0x80 | (uuid[8] & 0x3f);
+
+	return 0;
+}
diff --git a/src/utils/uuid.h b/src/utils/uuid.h
index 5e860cb..6e20210 100644
--- a/src/utils/uuid.h
+++ b/src/utils/uuid.h
@@ -14,5 +14,6 @@
 int uuid_str2bin(const char *str, u8 *bin);
 int uuid_bin2str(const u8 *bin, char *str, size_t max_len);
 int is_nil_uuid(const u8 *uuid);
+int uuid_random(u8 *uuid);
 
 #endif /* UUID_H */
diff --git a/src/utils/xml-utils.c b/src/utils/xml-utils.c
index 4916d29..a37a92d 100644
--- a/src/utils/xml-utils.c
+++ b/src/utils/xml-utils.c
@@ -246,10 +246,8 @@
 			xml_node_create_text(ctx, tnds, NULL, "Path", uri);
 
 		val = get_val(ctx, node);
-		if (val) {
-			xml_node_create_text(ctx, tnds, NULL, "Value", val);
-			xml_node_get_text_free(ctx, val);
-		}
+		xml_node_create_text(ctx, tnds, NULL, "Value", val ? val : "");
+		xml_node_get_text_free(ctx, val);
 
 		new_uri = add_path(uri, name);
 		node_to_tnds(ctx, new_uri ? out : tnds, node, new_uri);
diff --git a/src/wps/wps.c b/src/wps/wps.c
index fade6b6..8d22827 100644
--- a/src/wps/wps.c
+++ b/src/wps/wps.c
@@ -51,12 +51,11 @@
 	}
 	if (cfg->pin) {
 		data->dev_pw_id = cfg->dev_pw_id;
-		data->dev_password = os_malloc(cfg->pin_len);
+		data->dev_password = os_memdup(cfg->pin, cfg->pin_len);
 		if (data->dev_password == NULL) {
 			os_free(data);
 			return NULL;
 		}
-		os_memcpy(data->dev_password, cfg->pin, cfg->pin_len);
 		data->dev_password_len = cfg->pin_len;
 		wpa_hexdump_key(MSG_DEBUG, "WPS: AP PIN dev_password",
 				data->dev_password, data->dev_password_len);
@@ -75,14 +74,12 @@
 
 		data->dev_pw_id = cfg->wps->ap_nfc_dev_pw_id;
 		data->dev_password =
-			os_malloc(wpabuf_len(cfg->wps->ap_nfc_dev_pw));
+			os_memdup(wpabuf_head(cfg->wps->ap_nfc_dev_pw),
+				  wpabuf_len(cfg->wps->ap_nfc_dev_pw));
 		if (data->dev_password == NULL) {
 			os_free(data);
 			return NULL;
 		}
-		os_memcpy(data->dev_password,
-			  wpabuf_head(cfg->wps->ap_nfc_dev_pw),
-			  wpabuf_len(cfg->wps->ap_nfc_dev_pw));
 		data->dev_password_len = wpabuf_len(cfg->wps->ap_nfc_dev_pw);
 		wpa_hexdump_key(MSG_DEBUG, "WPS: NFC dev_password",
 			    data->dev_password, data->dev_password_len);
@@ -124,15 +121,14 @@
 
 	if (cfg->new_ap_settings) {
 		data->new_ap_settings =
-			os_malloc(sizeof(*data->new_ap_settings));
+			os_memdup(cfg->new_ap_settings,
+				  sizeof(*data->new_ap_settings));
 		if (data->new_ap_settings == NULL) {
 			bin_clear_free(data->dev_password,
 				       data->dev_password_len);
 			os_free(data);
 			return NULL;
 		}
-		os_memcpy(data->new_ap_settings, cfg->new_ap_settings,
-			  sizeof(*data->new_ap_settings));
 	}
 
 	if (cfg->peer_addr)
diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c
index b840acd..affd6a4 100644
--- a/src/wps/wps_er.c
+++ b/src/wps/wps_er.c
@@ -322,11 +322,10 @@
 	if (!s)
 		return -1;
 
-	ap->ap_settings = os_malloc(sizeof(*ap->ap_settings));
+	ap->ap_settings = os_memdup(&s->ap_settings, sizeof(*ap->ap_settings));
 	if (ap->ap_settings == NULL)
 		return -1;
 
-	os_memcpy(ap->ap_settings, &s->ap_settings, sizeof(*ap->ap_settings));
 	wpa_printf(MSG_DEBUG, "WPS ER: Use cached AP settings");
 	return 0;
 }
@@ -1958,10 +1957,9 @@
 	}
 
 	os_free(ap->ap_settings);
-	ap->ap_settings = os_malloc(sizeof(*cred));
+	ap->ap_settings = os_memdup(cred, sizeof(*cred));
 	if (ap->ap_settings == NULL)
 		return -1;
-	os_memcpy(ap->ap_settings, cred, sizeof(*cred));
 	ap->ap_settings->cred_attr = NULL;
 	wpa_printf(MSG_DEBUG, "WPS ER: Updated local AP settings based set "
 		   "config request");
@@ -2018,10 +2016,9 @@
 	}
 
 	os_free(ap->ap_settings);
-	ap->ap_settings = os_malloc(sizeof(*cred));
+	ap->ap_settings = os_memdup(cred, sizeof(*cred));
 	if (ap->ap_settings == NULL)
 		return -1;
-	os_memcpy(ap->ap_settings, cred, sizeof(*cred));
 	ap->ap_settings->cred_attr = NULL;
 
 	if (wps_er_send_get_device_info(ap, wps_er_ap_config_m1) < 0)
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index fac8bd8..379925e 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -748,12 +748,11 @@
 		p->wildcard_uuid = 1;
 	else
 		os_memcpy(p->uuid, uuid, WPS_UUID_LEN);
-	p->pin = os_malloc(pin_len);
+	p->pin = os_memdup(pin, pin_len);
 	if (p->pin == NULL) {
 		os_free(p);
 		return -1;
 	}
-	os_memcpy(p->pin, pin, pin_len);
 	p->pin_len = pin_len;
 
 	if (timeout) {
@@ -881,6 +880,7 @@
 					const u8 *uuid, size_t *pin_len)
 {
 	struct wps_uuid_pin *pin, *found = NULL;
+	int wildcard = 0;
 
 	wps_registrar_expire_pins(reg);
 
@@ -900,7 +900,7 @@
 			    pin->wildcard_uuid == 2) {
 				wpa_printf(MSG_DEBUG, "WPS: Found a wildcard "
 					   "PIN. Assigned it for this UUID-E");
-				pin->wildcard_uuid++;
+				wildcard = 1;
 				os_memcpy(pin->uuid, uuid, WPS_UUID_LEN);
 				found = pin;
 				break;
@@ -922,6 +922,8 @@
 	}
 	*pin_len = found->pin_len;
 	found->flags |= PIN_LOCKED;
+	if (wildcard)
+		found->wildcard_uuid++;
 	return found->pin;
 }
 
@@ -1404,10 +1406,9 @@
 		return -1;
 	}
 
-	wps->dev_password = os_malloc(pin_len);
+	wps->dev_password = os_memdup(pin, pin_len);
 	if (wps->dev_password == NULL)
 		return -1;
-	os_memcpy(wps->dev_password, pin, pin_len);
 	wps->dev_password_len = pin_len;
 
 	return 0;
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 867af85..41ba954 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -231,8 +231,6 @@
 NEED_80211_COMMON=y
 NEED_SHA256=y
 NEED_AES_SIV=y
-NEED_AES_OMAC1=y
-NEED_AES_CTR=y
 CONFIG_SAE=y
 CONFIG_AP=y
 L_CFLAGS += -DCONFIG_MESH
@@ -248,10 +246,45 @@
 NEED_DH_GROUPS=y
 endif
 
+ifdef CONFIG_DPP
+L_CFLAGS += -DCONFIG_DPP
+OBJS += src/common/dpp.c
+OBJS += dpp_supplicant.c
+NEED_AES_SIV=y
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_HMAC_SHA512_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+NEED_SHA512=y
+NEED_JSON=y
+NEED_GAS_SERVER=y
+NEED_BASE64=y
+endif
+
+ifdef CONFIG_OWE
+L_CFLAGS += -DCONFIG_OWE
+NEED_ECC=y
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_HMAC_SHA512_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+NEED_SHA512=y
+endif
+
 ifdef CONFIG_FILS
 L_CFLAGS += -DCONFIG_FILS
 NEED_SHA384=y
 NEED_AES_SIV=y
+ifdef CONFIG_FILS_SK_PFS
+L_CFLAGS += -DCONFIG_FILS_SK_PFS
+NEED_ECC=y
+endif
+endif
+
+ifdef CONFIG_MBO
+CONFIG_WNM=y
 endif
 
 ifdef CONFIG_WNM
@@ -270,10 +303,6 @@
 L_CFLAGS += -DCONFIG_TDLS_TESTING
 endif
 
-ifdef CONFIG_PEERKEY
-L_CFLAGS += -DCONFIG_PEERKEY
-endif
-
 ifdef CONFIG_PMKSA_CACHE_EXTERNAL
 L_CFLAGS += -DCONFIG_PMKSA_CACHE_EXTERNAL
 endif
@@ -282,7 +311,6 @@
 OBJS += src/rsn_supp/wpa.c
 OBJS += src/rsn_supp/preauth.c
 OBJS += src/rsn_supp/pmksa_cache.c
-OBJS += src/rsn_supp/peerkey.c
 OBJS += src/rsn_supp/wpa_ie.c
 OBJS += src/common/wpa_common.c
 NEED_AES=y
@@ -834,7 +862,8 @@
 OBJS += src/ap/ieee802_11_he.c
 endif
 endif
-ifdef CONFIG_WNM
+ifdef CONFIG_WNM_AP
+L_CFLAGS += -DCONFIG_WNM_AP
 OBJS += src/ap/wnm_ap.c
 endif
 ifdef CONFIG_MBO
@@ -875,6 +904,10 @@
 OBJS += src/ap/wps_hostapd.c
 OBJS += src/eap_server/eap_server_wsc.c
 endif
+ifdef CONFIG_DPP
+OBJS += src/ap/dpp_hostapd.c
+OBJS += src/ap/gas_query_ap.c
+endif
 ifdef CONFIG_INTERWORKING
 OBJS += src/ap/gas_serv.c
 endif
@@ -888,6 +921,10 @@
 L_CFLAGS += -DCONFIG_MBO
 endif
 
+ifdef CONFIG_TESTING_OPTIONS
+L_CFLAGS += -DCONFIG_TESTING_OPTIONS
+endif
+
 ifdef NEED_RSN_AUTHENTICATOR
 L_CFLAGS += -DCONFIG_NO_RADIUS
 NEED_AES_WRAP=y
@@ -898,9 +935,6 @@
 L_CFLAGS += -DCONFIG_IEEE80211R_AP
 OBJS += src/ap/wpa_auth_ft.c
 endif
-ifdef CONFIG_PEERKEY
-OBJS += src/ap/peerkey_auth.c
-endif
 endif
 
 ifdef CONFIG_ACS
@@ -1000,6 +1034,10 @@
 LIBS += -ldl
 LIBS_p += -ldl
 endif
+ifndef CONFIG_TLS_DEFAULT_CIPHERS
+CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW"
+endif
+L_CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
 endif
 
 ifeq ($(CONFIG_TLS), gnutls)
@@ -1160,6 +1198,12 @@
 ifdef NEED_AES_EAX
 AESOBJS += src/crypto/aes-eax.c
 NEED_AES_CTR=y
+NEED_AES_OMAC1=y
+endif
+ifdef NEED_AES_SIV
+AESOBJS += src/crypto/aes-siv.c
+NEED_AES_CTR=y
+NEED_AES_OMAC1=y
 endif
 ifdef NEED_AES_CTR
 AESOBJS += src/crypto/aes-ctr.c
@@ -1192,9 +1236,6 @@
 AESOBJS += src/crypto/aes-internal-enc.c
 endif
 endif
-ifdef NEED_AES_SIV
-AESOBJS += src/crypto/aes-siv.c
-endif
 ifdef NEED_AES
 OBJS += $(AESOBJS)
 endif
@@ -1290,6 +1331,14 @@
 L_CFLAGS += -DCONFIG_HMAC_SHA256_KDF
 SHA256OBJS += src/crypto/sha256-kdf.c
 endif
+ifdef NEED_HMAC_SHA384_KDF
+L_CFLAGS += -DCONFIG_HMAC_SHA384_KDF
+SHA256OBJS += src/crypto/sha384-kdf.c
+endif
+ifdef NEED_HMAC_SHA512_KDF
+L_CFLAGS += -DCONFIG_HMAC_SHA512_KDF
+SHA256OBJS += src/crypto/sha512-kdf.c
+endif
 OBJS += $(SHA256OBJS)
 endif
 ifdef NEED_SHA384
@@ -1299,6 +1348,13 @@
 endif
 OBJS += src/crypto/sha384-prf.c
 endif
+ifdef NEED_SHA512
+L_CFLAGS += -DCONFIG_SHA512
+ifneq ($(CONFIG_TLS), openssl)
+OBJS += src/crypto/sha512.c
+endif
+OBJS += src/crypto/sha512-prf.c
+endif
 
 ifdef NEED_DH_GROUPS
 OBJS += src/crypto/dh_groups.c
@@ -1522,6 +1578,12 @@
 L_CFLAGS += -DCONFIG_EXT_PASSWORD
 endif
 
+ifdef NEED_GAS_SERVER
+OBJS += src/common/gas_server.c
+L_CFLAGS += -DCONFIG_GAS_SERVER
+NEED_GAS=y
+endif
+
 ifdef NEED_GAS
 OBJS += src/common/gas.c
 OBJS += gas_query.c
@@ -1534,6 +1596,11 @@
 L_CFLAGS += -DCONFIG_OFFCHANNEL
 endif
 
+ifdef NEED_JSON
+OBJS += src/utils/json.c
+L_CFLAGS += -DCONFIG_JSON
+endif
+
 OBJS += src/drivers/driver_common.c
 
 OBJS += wpa_supplicant.c events.c blacklist.c wpas_glue.c scan.c
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 7bcb7e4..65205d8 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -261,8 +261,6 @@
 NEED_80211_COMMON=y
 NEED_SHA256=y
 NEED_AES_SIV=y
-NEED_AES_OMAC1=y
-NEED_AES_CTR=y
 CONFIG_SAE=y
 CONFIG_AP=y
 CFLAGS += -DCONFIG_MESH
@@ -278,10 +276,45 @@
 NEED_DH_GROUPS=y
 endif
 
+ifdef CONFIG_DPP
+CFLAGS += -DCONFIG_DPP
+OBJS += ../src/common/dpp.o
+OBJS += dpp_supplicant.o
+NEED_AES_SIV=y
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_HMAC_SHA512_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+NEED_SHA512=y
+NEED_JSON=y
+NEED_GAS_SERVER=y
+NEED_BASE64=y
+endif
+
+ifdef CONFIG_OWE
+CFLAGS += -DCONFIG_OWE
+NEED_ECC=y
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_HMAC_SHA512_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+NEED_SHA512=y
+endif
+
 ifdef CONFIG_FILS
 CFLAGS += -DCONFIG_FILS
 NEED_SHA384=y
 NEED_AES_SIV=y
+ifdef CONFIG_FILS_SK_PFS
+CFLAGS += -DCONFIG_FILS_SK_PFS
+NEED_ECC=y
+endif
+endif
+
+ifdef CONFIG_MBO
+CONFIG_WNM=y
 endif
 
 ifdef CONFIG_WNM
@@ -300,10 +333,6 @@
 CFLAGS += -DCONFIG_TDLS_TESTING
 endif
 
-ifdef CONFIG_PEERKEY
-CFLAGS += -DCONFIG_PEERKEY
-endif
-
 ifdef CONFIG_PMKSA_CACHE_EXTERNAL
 CFLAGS += -DCONFIG_PMKSA_CACHE_EXTERNAL
 endif
@@ -312,7 +341,6 @@
 OBJS += ../src/rsn_supp/wpa.o
 OBJS += ../src/rsn_supp/preauth.o
 OBJS += ../src/rsn_supp/pmksa_cache.o
-OBJS += ../src/rsn_supp/peerkey.o
 OBJS += ../src/rsn_supp/wpa_ie.o
 OBJS += ../src/common/wpa_common.o
 NEED_AES=y
@@ -810,6 +838,20 @@
 endif
 endif
 
+ifdef CONFIG_MACSEC
+CFLAGS += -DCONFIG_MACSEC
+CONFIG_IEEE8021X_EAPOL=y
+NEED_AES_ENCBLOCK=y
+NEED_AES_UNWRAP=y
+NEED_AES_WRAP=y
+NEED_AES_OMAC1=y
+OBJS += wpas_kay.o
+OBJS += ../src/pae/ieee802_1x_cp.o
+OBJS += ../src/pae/ieee802_1x_kay.o
+OBJS += ../src/pae/ieee802_1x_key.o
+OBJS += ../src/pae/ieee802_1x_secy_ops.o
+endif
+
 ifdef CONFIG_IEEE8021X_EAPOL
 # IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication)
 CFLAGS += -DIEEE8021X_EAPOL
@@ -822,19 +864,6 @@
 endif
 endif
 
-ifdef CONFIG_MACSEC
-CFLAGS += -DCONFIG_MACSEC
-NEED_AES_ENCBLOCK=y
-NEED_AES_UNWRAP=y
-NEED_AES_WRAP=y
-NEED_AES_OMAC1=y
-OBJS += wpas_kay.o
-OBJS += ../src/pae/ieee802_1x_cp.o
-OBJS += ../src/pae/ieee802_1x_kay.o
-OBJS += ../src/pae/ieee802_1x_key.o
-OBJS += ../src/pae/ieee802_1x_secy_ops.o
-endif
-
 ifdef CONFIG_AP
 NEED_EAP_COMMON=y
 NEED_RSN_AUTHENTICATOR=y
@@ -872,7 +901,8 @@
 OBJS += ../src/ap/ieee802_11_he.o
 endif
 endif
-ifdef CONFIG_WNM
+ifdef CONFIG_WNM_AP
+CFLAGS += -DCONFIG_WNM_AP
 OBJS += ../src/ap/wnm_ap.o
 endif
 ifdef CONFIG_MBO
@@ -913,6 +943,10 @@
 OBJS += ../src/ap/wps_hostapd.o
 OBJS += ../src/eap_server/eap_server_wsc.o
 endif
+ifdef CONFIG_DPP
+OBJS += ../src/ap/dpp_hostapd.o
+OBJS += ../src/ap/gas_query_ap.o
+endif
 ifdef CONFIG_INTERWORKING
 OBJS += ../src/ap/gas_serv.o
 endif
@@ -936,9 +970,6 @@
 CFLAGS += -DCONFIG_IEEE80211R_AP
 OBJS += ../src/ap/wpa_auth_ft.o
 endif
-ifdef CONFIG_PEERKEY
-OBJS += ../src/ap/peerkey_auth.o
-endif
 endif
 
 ifdef CONFIG_ACS
@@ -1043,6 +1074,10 @@
 LIBS += -ldl
 LIBS_p += -ldl
 endif
+ifndef CONFIG_TLS_DEFAULT_CIPHERS
+CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW"
+endif
+CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
 endif
 
 ifeq ($(CONFIG_TLS), gnutls)
@@ -1145,6 +1180,48 @@
 endif
 endif
 
+ifeq ($(CONFIG_TLS), linux)
+OBJS += ../src/crypto/crypto_linux.o
+OBJS_p += ../src/crypto/crypto_linux.o
+ifdef TLS_FUNCS
+OBJS += ../src/crypto/crypto_internal-rsa.o
+OBJS += ../src/crypto/tls_internal.o
+OBJS += ../src/tls/tlsv1_common.o
+OBJS += ../src/tls/tlsv1_record.o
+OBJS += ../src/tls/tlsv1_cred.o
+OBJS += ../src/tls/tlsv1_client.o
+OBJS += ../src/tls/tlsv1_client_write.o
+OBJS += ../src/tls/tlsv1_client_read.o
+OBJS += ../src/tls/tlsv1_client_ocsp.o
+OBJS += ../src/tls/asn1.o
+OBJS += ../src/tls/rsa.o
+OBJS += ../src/tls/x509v3.o
+OBJS += ../src/tls/pkcs1.o
+OBJS += ../src/tls/pkcs5.o
+OBJS += ../src/tls/pkcs8.o
+NEED_SHA256=y
+NEED_BASE64=y
+NEED_TLS_PRF=y
+ifdef CONFIG_TLSV12
+NEED_TLS_PRF_SHA256=y
+endif
+NEED_MODEXP=y
+NEED_CIPHER=y
+CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
+endif
+ifdef NEED_MODEXP
+OBJS += ../src/crypto/crypto_internal-modexp.o
+OBJS += ../src/tls/bignum.o
+CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+CFLAGS += -DLTM_FAST
+endif
+CONFIG_INTERNAL_DH_GROUP5=y
+ifdef NEED_FIPS186_2_PRF
+OBJS += ../src/crypto/fips_prf_internal.o
+OBJS += ../src/crypto/sha1-internal.o
+endif
+endif
+
 ifeq ($(CONFIG_TLS), none)
 ifdef TLS_FUNCS
 OBJS += ../src/crypto/tls_none.o
@@ -1199,11 +1276,19 @@
 endif
 
 ifdef NEED_INTERNAL_AES_WRAP
+ifneq ($(CONFIG_TLS), linux)
 AESOBJS += ../src/crypto/aes-unwrap.o
 endif
+endif
 ifdef NEED_AES_EAX
 AESOBJS += ../src/crypto/aes-eax.o
 NEED_AES_CTR=y
+NEED_AES_OMAC1=y
+endif
+ifdef NEED_AES_SIV
+AESOBJS += ../src/crypto/aes-siv.o
+NEED_AES_CTR=y
+NEED_AES_OMAC1=y
 endif
 ifdef NEED_AES_CTR
 AESOBJS += ../src/crypto/aes-ctr.o
@@ -1216,11 +1301,10 @@
 ifdef CONFIG_OPENSSL_CMAC
 CFLAGS += -DCONFIG_OPENSSL_CMAC
 else
+ifneq ($(CONFIG_TLS), linux)
 AESOBJS += ../src/crypto/aes-omac1.o
 endif
 endif
-ifdef NEED_AES_SIV
-AESOBJS += ../src/crypto/aes-siv.o
 endif
 ifdef NEED_AES_WRAP
 NEED_AES_ENC=y
@@ -1231,9 +1315,11 @@
 ifdef NEED_AES_CBC
 NEED_AES_ENC=y
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 AESOBJS += ../src/crypto/aes-cbc.o
 endif
 endif
+endif
 ifdef NEED_AES_ENC
 ifdef CONFIG_INTERNAL_AES
 AESOBJS += ../src/crypto/aes-internal-enc.o
@@ -1245,8 +1331,10 @@
 
 ifdef NEED_SHA1
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 SHA1OBJS += ../src/crypto/sha1.o
 endif
+endif
 SHA1OBJS += ../src/crypto/sha1-prf.o
 ifdef CONFIG_INTERNAL_SHA1
 SHA1OBJS += ../src/crypto/sha1-internal.o
@@ -1271,9 +1359,11 @@
 
 ifndef CONFIG_FIPS
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 MD5OBJS += ../src/crypto/md5.o
 endif
 endif
+endif
 ifdef NEED_MD5
 ifdef CONFIG_INTERNAL_MD5
 MD5OBJS += ../src/crypto/md5-internal.o
@@ -1312,8 +1402,10 @@
 ifdef NEED_SHA256
 CFLAGS += -DCONFIG_SHA256
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 SHA256OBJS += ../src/crypto/sha256.o
 endif
+endif
 SHA256OBJS += ../src/crypto/sha256-prf.o
 ifdef CONFIG_INTERNAL_SHA256
 SHA256OBJS += ../src/crypto/sha256-internal.o
@@ -1333,15 +1425,34 @@
 CFLAGS += -DCONFIG_HMAC_SHA256_KDF
 OBJS += ../src/crypto/sha256-kdf.o
 endif
+ifdef NEED_HMAC_SHA384_KDF
+CFLAGS += -DCONFIG_HMAC_SHA384_KDF
+OBJS += ../src/crypto/sha384-kdf.o
+endif
+ifdef NEED_HMAC_SHA512_KDF
+CFLAGS += -DCONFIG_HMAC_SHA512_KDF
+OBJS += ../src/crypto/sha512-kdf.o
+endif
 OBJS += $(SHA256OBJS)
 endif
 ifdef NEED_SHA384
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 OBJS += ../src/crypto/sha384.o
 endif
+endif
 CFLAGS += -DCONFIG_SHA384
 OBJS += ../src/crypto/sha384-prf.o
 endif
+ifdef NEED_SHA512
+ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
+OBJS += ../src/crypto/sha512.o
+endif
+endif
+CFLAGS += -DCONFIG_SHA512
+OBJS += ../src/crypto/sha512-prf.o
+endif
 
 ifdef NEED_DH_GROUPS
 OBJS += ../src/crypto/dh_groups.o
@@ -1590,6 +1701,12 @@
 CFLAGS += -DCONFIG_EXT_PASSWORD
 endif
 
+ifdef NEED_GAS_SERVER
+OBJS += ../src/common/gas_server.o
+CFLAGS += -DCONFIG_GAS_SERVER
+NEED_GAS=y
+endif
+
 ifdef NEED_GAS
 OBJS += ../src/common/gas.o
 OBJS += gas_query.o
@@ -1602,6 +1719,11 @@
 CFLAGS += -DCONFIG_OFFCHANNEL
 endif
 
+ifdef NEED_JSON
+OBJS += ../src/utils/json.o
+CFLAGS += -DCONFIG_JSON
+endif
+
 ifdef CONFIG_MODULE_TESTS
 CFLAGS += -DCONFIG_MODULE_TESTS
 OBJS += wpas_module_tests.o
diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config
index 1679347..70450ac 100644
--- a/wpa_supplicant/android.config
+++ b/wpa_supplicant/android.config
@@ -276,9 +276,6 @@
 # bridge interfaces (commit 'bridge: respect RFC2863 operational state')').
 #CONFIG_NO_LINUX_PACKET_SOCKET_WAR=y
 
-# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
-CONFIG_PEERKEY=y
-
 # IEEE 802.11w (management frame protection), also known as PMF
 # Driver support is also needed for IEEE 802.11w.
 CONFIG_IEEE80211W=y
@@ -304,6 +301,10 @@
 # will be used)
 #CONFIG_TLSV12=y
 
+# Select which ciphers to use by default with OpenSSL if the user does not
+# specify them.
+#CONFIG_TLS_DEFAULT_CIPHERS="DEFAULT:!EXP:!LOW"
+
 # If CONFIG_TLS=internal is used, additional library and include paths are
 # needed for LibTomMath. Alternatively, an integrated, minimal version of
 # LibTomMath can be used. See beginning of libtommath.c for details on benefits
@@ -541,4 +542,8 @@
 # channels (experimental)
 #CONFIG_BGSCAN_LEARN=y
 
+# Opportunistic Wireless Encryption (OWE)
+# Experimental implementation of draft-harkins-owe-07.txt
+#CONFIG_OWE=y
+
 include $(wildcard $(LOCAL_PATH)/android_config_*.inc)
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 1721d6a..6668d58 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -46,16 +46,35 @@
 
 #ifdef CONFIG_IEEE80211N
 static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s,
+			     struct wpa_ssid *ssid,
 			     struct hostapd_config *conf,
 			     struct hostapd_hw_modes *mode)
 {
 #ifdef CONFIG_P2P
 	u8 center_chan = 0;
 	u8 channel = conf->channel;
+#endif /* CONFIG_P2P */
 
 	if (!conf->secondary_channel)
 		goto no_vht;
 
+	/* Use the maximum oper channel width if it's given. */
+	if (ssid->max_oper_chwidth)
+		conf->vht_oper_chwidth = ssid->max_oper_chwidth;
+
+	ieee80211_freq_to_chan(ssid->vht_center_freq2,
+			       &conf->vht_oper_centr_freq_seg1_idx);
+
+	if (!ssid->p2p_group) {
+		if (!ssid->vht_center_freq1 ||
+		    conf->vht_oper_chwidth == VHT_CHANWIDTH_USE_HT)
+			goto no_vht;
+		ieee80211_freq_to_chan(ssid->vht_center_freq1,
+				       &conf->vht_oper_centr_freq_seg0_idx);
+		return;
+	}
+
+#ifdef CONFIG_P2P
 	switch (conf->vht_oper_chwidth) {
 	case VHT_CHANWIDTH_80MHZ:
 	case VHT_CHANWIDTH_80P80MHZ:
@@ -84,14 +103,11 @@
 
 	conf->vht_oper_centr_freq_seg0_idx = center_chan;
 	return;
+#endif /* CONFIG_P2P */
 
 no_vht:
 	conf->vht_oper_centr_freq_seg0_idx =
-		channel + conf->secondary_channel * 2;
-#else /* CONFIG_P2P */
-	conf->vht_oper_centr_freq_seg0_idx =
 		conf->channel + conf->secondary_channel * 2;
-#endif /* CONFIG_P2P */
 	conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT;
 }
 #endif /* CONFIG_IEEE80211N */
@@ -131,27 +147,37 @@
 		}
 
 #ifdef CONFIG_HT_OVERRIDES
-		if (ssid->disable_ht) {
+		if (ssid->disable_ht)
+			ssid->ht = 0;
+#endif /* CONFIG_HT_OVERRIDES */
+
+		if (!ssid->ht) {
 			conf->ieee80211n = 0;
 			conf->ht_capab = 0;
 			no_ht = 1;
 		}
-#endif /* CONFIG_HT_OVERRIDES */
 
 		if (!no_ht && mode && mode->ht_capab) {
 			conf->ieee80211n = 1;
 #ifdef CONFIG_P2P
-			if (conf->hw_mode == HOSTAPD_MODE_IEEE80211A &&
+			if (ssid->p2p_group &&
+			    conf->hw_mode == HOSTAPD_MODE_IEEE80211A &&
 			    (mode->ht_capab &
 			     HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
 			    ssid->ht40)
 				conf->secondary_channel =
 					wpas_p2p_get_ht40_mode(wpa_s, mode,
 							       conf->channel);
+#endif /* CONFIG_P2P */
+
+			if (!ssid->p2p_group &&
+			    (mode->ht_capab &
+			     HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+				conf->secondary_channel = ssid->ht40;
+
 			if (conf->secondary_channel)
 				conf->ht_capab |=
 					HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
-#endif /* CONFIG_P2P */
 
 			/*
 			 * white-list capabilities that won't cause issues
@@ -169,7 +195,7 @@
 			if (mode->vht_capab && ssid->vht) {
 				conf->ieee80211ac = 1;
 				conf->vht_capab |= mode->vht_capab;
-				wpas_conf_ap_vht(wpa_s, conf, mode);
+				wpas_conf_ap_vht(wpa_s, ssid, conf, mode);
 			}
 		}
 	}
@@ -318,17 +344,34 @@
 		for (i = 0; i < NUM_WEP_KEYS; i++) {
 			if (ssid->wep_key_len[i] == 0)
 				continue;
-			wep->key[i] = os_malloc(ssid->wep_key_len[i]);
+			wep->key[i] = os_memdup(ssid->wep_key[i],
+						ssid->wep_key_len[i]);
 			if (wep->key[i] == NULL)
 				return -1;
-			os_memcpy(wep->key[i], ssid->wep_key[i],
-				  ssid->wep_key_len[i]);
 			wep->len[i] = ssid->wep_key_len[i];
 		}
 		wep->idx = ssid->wep_tx_keyidx;
 		wep->keys_set = 1;
 	}
 
+	if (wpa_s->conf->go_interworking) {
+		wpa_printf(MSG_DEBUG,
+			   "P2P: Enable Interworking with access_network_type: %d",
+			   wpa_s->conf->go_access_network_type);
+		bss->interworking = wpa_s->conf->go_interworking;
+		bss->access_network_type = wpa_s->conf->go_access_network_type;
+		bss->internet = wpa_s->conf->go_internet;
+		if (wpa_s->conf->go_venue_group) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Venue group: %d  Venue type: %d",
+				   wpa_s->conf->go_venue_group,
+				   wpa_s->conf->go_venue_type);
+			bss->venue_group = wpa_s->conf->go_venue_group;
+			bss->venue_type = wpa_s->conf->go_venue_type;
+			bss->venue_info_set = 1;
+		}
+	}
+
 	if (ssid->ap_max_inactivity)
 		bss->ap_max_inactivity = ssid->ap_max_inactivity;
 
@@ -694,13 +737,6 @@
 		return -1;
 	}
 
-	/* Use the maximum oper channel width if it's given. */
-	if (ssid->max_oper_chwidth)
-		conf->vht_oper_chwidth = ssid->max_oper_chwidth;
-
-	ieee80211_freq_to_chan(ssid->vht_center_freq2,
-			       &conf->vht_oper_centr_freq_seg1_idx);
-
 	os_memcpy(wpa_s->ap_iface->conf->wmm_ac_params,
 		  wpa_s->conf->wmm_ac_params,
 		  sizeof(wpa_s->conf->wmm_ac_params));
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 914bd5d..708b58a 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -1317,3 +1317,19 @@
 	*rates = r;
 	return len;
 }
+
+
+#ifdef CONFIG_FILS
+const u8 * wpa_bss_get_fils_cache_id(struct wpa_bss *bss)
+{
+	const u8 *ie;
+
+	if (bss) {
+		ie = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION);
+		if (ie && ie[1] >= 4 && WPA_GET_LE16(ie + 2) & BIT(7))
+			return ie + 4;
+	}
+
+	return NULL;
+}
+#endif /* CONFIG_FILS */
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index 84505fa..37d9fb6 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -145,6 +145,7 @@
 int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates);
 struct wpa_bss_anqp * wpa_bss_anqp_alloc(void);
 int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss);
+const u8 * wpa_bss_get_fils_cache_id(struct wpa_bss *bss);
 
 static inline int bss_is_dmg(const struct wpa_bss *bss)
 {
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 58ecf19..a0d480e 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -397,6 +397,50 @@
 #endif /* NO_CONFIG_WRITE */
 
 
+static int wpa_config_parse_bssid_hint(const struct parse_data *data,
+				       struct wpa_ssid *ssid, int line,
+				       const char *value)
+{
+	if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 ||
+	    os_strcmp(value, "any") == 0) {
+		ssid->bssid_hint_set = 0;
+		wpa_printf(MSG_MSGDUMP, "BSSID hint any");
+		return 0;
+	}
+	if (hwaddr_aton(value, ssid->bssid_hint)) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid BSSID hint '%s'.",
+			   line, value);
+		return -1;
+	}
+	ssid->bssid_hint_set = 1;
+	wpa_hexdump(MSG_MSGDUMP, "BSSID hint", ssid->bssid_hint, ETH_ALEN);
+	return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_bssid_hint(const struct parse_data *data,
+					  struct wpa_ssid *ssid)
+{
+	char *value;
+	int res;
+
+	if (!ssid->bssid_hint_set)
+		return NULL;
+
+	value = os_malloc(20);
+	if (!value)
+		return NULL;
+	res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->bssid_hint));
+	if (os_snprintf_error(20, res)) {
+		os_free(value);
+		return NULL;
+	}
+	return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
 static int wpa_config_parse_bssid_blacklist(const struct parse_data *data,
 					    struct wpa_ssid *ssid, int line,
 					    const char *value)
@@ -732,6 +776,14 @@
 			val |= WPA_KEY_MGMT_FT_FILS_SHA384;
 #endif /* CONFIG_IEEE80211R */
 #endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+		else if (os_strcmp(start, "OWE") == 0)
+			val |= WPA_KEY_MGMT_OWE;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+		else if (os_strcmp(start, "DPP") == 0)
+			val |= WPA_KEY_MGMT_DPP;
+#endif /* CONFIG_DPP */
 		else {
 			wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
 				   line, start);
@@ -934,6 +986,47 @@
 	}
 #endif /* CONFIG_SUITEB192 */
 
+#ifdef CONFIG_FILS
+	if (ssid->key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
+		ret = os_snprintf(pos, end - pos, "%sFILS-SHA256",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+	if (ssid->key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
+		ret = os_snprintf(pos, end - pos, "%sFILS-SHA384",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+#ifdef CONFIG_IEEE80211R
+	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
+		ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA256",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
+		ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA384",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_FILS */
+
 	if (pos == buf) {
 		os_free(buf);
 		buf = NULL;
@@ -1055,6 +1148,40 @@
 #endif /* NO_CONFIG_WRITE */
 
 
+static int wpa_config_parse_group_mgmt(const struct parse_data *data,
+				       struct wpa_ssid *ssid, int line,
+				       const char *value)
+{
+	int val;
+
+	val = wpa_config_parse_cipher(line, value);
+	if (val == -1)
+		return -1;
+
+	if (val & ~WPA_ALLOWED_GROUP_MGMT_CIPHERS) {
+		wpa_printf(MSG_ERROR,
+			   "Line %d: not allowed group management cipher (0x%x).",
+			   line, val);
+		return -1;
+	}
+
+	if (ssid->group_mgmt_cipher == val)
+		return 1;
+	wpa_printf(MSG_MSGDUMP, "group_mgmt: 0x%x", val);
+	ssid->group_mgmt_cipher = val;
+	return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_group_mgmt(const struct parse_data *data,
+					  struct wpa_ssid *ssid)
+{
+	return wpa_config_write_cipher(ssid->group_mgmt_cipher);
+}
+#endif /* NO_CONFIG_WRITE */
+
+
 static int wpa_config_parse_auth_alg(const struct parse_data *data,
 				     struct wpa_ssid *ssid, int line,
 				     const char *value)
@@ -1892,6 +2019,24 @@
 #endif /* CONFIG_MACSEC */
 
 
+static int wpa_config_parse_peerkey(const struct parse_data *data,
+				    struct wpa_ssid *ssid, int line,
+				    const char *value)
+{
+	wpa_printf(MSG_INFO, "NOTE: Obsolete peerkey parameter ignored");
+	return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_peerkey(const struct parse_data *data,
+				       struct wpa_ssid *ssid)
+{
+	return NULL;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
 /* Helper macros for network block parser */
 
 #ifdef OFFSET
@@ -1983,20 +2128,28 @@
 	{ STR_RANGE(ssid, 0, SSID_MAX_LEN) },
 	{ INT_RANGE(scan_ssid, 0, 1) },
 	{ FUNC(bssid) },
+	{ FUNC(bssid_hint) },
 	{ FUNC(bssid_blacklist) },
 	{ FUNC(bssid_whitelist) },
 	{ FUNC_KEY(psk) },
 	{ INT(mem_only_psk) },
+	{ STR_KEY(sae_password) },
 	{ FUNC(proto) },
 	{ FUNC(key_mgmt) },
 	{ INT(bg_scan_period) },
 	{ FUNC(pairwise) },
 	{ FUNC(group) },
+	{ FUNC(group_mgmt) },
 	{ FUNC(auth_alg) },
 	{ FUNC(scan_freq) },
 	{ FUNC(freq_list) },
+	{ INT_RANGE(ht, 0, 1) },
+	{ INT_RANGE(vht, 0, 1) },
+	{ INT_RANGE(ht40, -1, 1) },
 	{ INT_RANGE(max_oper_chwidth, VHT_CHANWIDTH_USE_HT,
 		    VHT_CHANWIDTH_80P80MHZ) },
+	{ INT(vht_center_freq1) },
+	{ INT(vht_center_freq2) },
 #ifdef IEEE8021X_EAPOL
 	{ FUNC(eap) },
 	{ STR_LENe(identity) },
@@ -2057,6 +2210,7 @@
 #ifdef CONFIG_MESH
 	{ INT_RANGE(mode, 0, 5) },
 	{ INT_RANGE(no_auto_peer, 0, 1) },
+	{ INT_RANGE(mesh_rssi_threshold, -255, 1) },
 #else /* CONFIG_MESH */
 	{ INT_RANGE(mode, 0, 4) },
 #endif /* CONFIG_MESH */
@@ -2066,7 +2220,7 @@
 #ifdef CONFIG_IEEE80211W
 	{ INT_RANGE(ieee80211w, 0, 2) },
 #endif /* CONFIG_IEEE80211W */
-	{ INT_RANGE(peerkey, 0, 1) },
+	{ FUNC(peerkey) /* obsolete - removed */ },
 	{ INT_RANGE(mixed_cell, 0, 1) },
 	{ INT_RANGE(frequency, 0, 65000) },
 	{ INT_RANGE(fixed_freq, 0, 1) },
@@ -2138,6 +2292,14 @@
 	{ INT_RANGE(mac_addr, 0, 2) },
 	{ INT_RANGE(pbss, 0, 2) },
 	{ INT_RANGE(wps_disabled, 0, 1) },
+	{ INT_RANGE(fils_dh_group, 0, 65535) },
+#ifdef CONFIG_DPP
+	{ STR(dpp_connector) },
+	{ STR_LEN(dpp_netaccesskey) },
+	{ INT(dpp_netaccesskey_expiry) },
+	{ STR_LEN(dpp_csign) },
+#endif /* CONFIG_DPP */
+	{ INT_RANGE(owe_group, 0, 65535) },
 };
 
 #undef OFFSET
@@ -2307,6 +2469,7 @@
 	os_free(ssid->ssid);
 	str_clear_free(ssid->passphrase);
 	os_free(ssid->ext_psk);
+	str_clear_free(ssid->sae_password);
 #ifdef IEEE8021X_EAPOL
 	eap_peer_config_free(&ssid->eap);
 #endif /* IEEE8021X_EAPOL */
@@ -2323,6 +2486,9 @@
 #ifdef CONFIG_MESH
 	os_free(ssid->mesh_basic_rates);
 #endif /* CONFIG_MESH */
+	os_free(ssid->dpp_connector);
+	bin_clear_free(ssid->dpp_netaccesskey, ssid->dpp_netaccesskey_len);
+	os_free(ssid->dpp_csign);
 	while ((psk = dl_list_first(&ssid->psk_list, struct psk_list_entry,
 				    list))) {
 		dl_list_del(&psk->list);
@@ -2576,6 +2742,7 @@
 	ssid->group_cipher = DEFAULT_GROUP;
 	ssid->key_mgmt = DEFAULT_KEY_MGMT;
 	ssid->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
+	ssid->ht = 1;
 #ifdef IEEE8021X_EAPOL
 	ssid->eapol_flags = DEFAULT_EAPOL_FLAGS;
 	ssid->eap_workaround = DEFAULT_EAP_WORKAROUND;
@@ -2587,6 +2754,7 @@
 	ssid->dot11MeshRetryTimeout = DEFAULT_MESH_RETRY_TIMEOUT;
 	ssid->dot11MeshConfirmTimeout = DEFAULT_MESH_CONFIRM_TIMEOUT;
 	ssid->dot11MeshHoldingTimeout = DEFAULT_MESH_HOLDING_TIMEOUT;
+	ssid->mesh_rssi_threshold = DEFAULT_MESH_RSSI_THRESHOLD;
 #endif /* CONFIG_MESH */
 #ifdef CONFIG_HT_OVERRIDES
 	ssid->disable_ht = DEFAULT_DISABLE_HT;
@@ -3742,6 +3910,9 @@
 
 #ifdef CONFIG_MBO
 	config->mbo_cell_capa = DEFAULT_MBO_CELL_CAPA;
+	config->disassoc_imminent_rssi_threshold =
+		DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD;
+	config->oce = DEFAULT_OCE_SUPPORT;
 #endif /* CONFIG_MBO */
 
 	if (ctrl_interface)
@@ -4354,6 +4525,7 @@
 	{ FUNC_NO_VAR(load_dynamic_eap), 0 },
 #ifdef CONFIG_WPS
 	{ FUNC(uuid), CFG_CHANGED_UUID },
+	{ INT_RANGE(auto_uuid, 0, 1), 0 },
 	{ STR_RANGE(device_name, 0, WPS_DEV_NAME_MAX_LEN),
 	  CFG_CHANGED_DEVICE_NAME },
 	{ STR_RANGE(manufacturer, 0, 64), CFG_CHANGED_WPS_STRING },
@@ -4410,6 +4582,11 @@
 	{ INT_RANGE(interworking, 0, 1), 0 },
 	{ FUNC(hessid), 0 },
 	{ INT_RANGE(access_network_type, 0, 15), 0 },
+	{ INT_RANGE(go_interworking, 0, 1), 0 },
+	{ INT_RANGE(go_access_network_type, 0, 15), 0 },
+	{ INT_RANGE(go_internet, 0, 1), 0 },
+	{ INT_RANGE(go_venue_group, 0, 255), 0 },
+	{ INT_RANGE(go_venue_type, 0, 255), 0 },
 	{ INT_RANGE(pbc_in_m1, 0, 1), 0 },
 	{ STR(autoscan), 0 },
 	{ INT_RANGE(wps_nfc_dev_pw_id, 0x10, 0xffff),
@@ -4430,9 +4607,10 @@
 	{ FUNC(freq_list), 0 },
 	{ INT(scan_cur_freq), 0 },
 	{ INT(sched_scan_interval), 0 },
+	{ INT(sched_scan_start_delay), 0 },
 	{ INT(tdls_external_control), 0},
 	{ STR(osu_dir), 0 },
-	{ STR(wowlan_triggers), 0 },
+	{ STR(wowlan_triggers), CFG_CHANGED_WOWLAN_TRIGGERS },
 	{ INT(p2p_search_delay), 0},
 	{ INT(mac_addr), 0 },
 	{ INT(rand_addr_lifetime), 0 },
@@ -4453,12 +4631,15 @@
 	{ STR(non_pref_chan), 0 },
 	{ INT_RANGE(mbo_cell_capa, MBO_CELL_CAPA_AVAILABLE,
 		    MBO_CELL_CAPA_NOT_SUPPORTED), 0 },
-#endif /*CONFIG_MBO */
+	{ INT_RANGE(disassoc_imminent_rssi_threshold, -120, 0), 0 },
+	{ INT_RANGE(oce, 0, 3), 0 },
+#endif /* CONFIG_MBO */
 	{ INT(gas_address3), 0 },
 	{ INT_RANGE(ftm_responder, 0, 1), 0 },
 	{ INT_RANGE(ftm_initiator, 0, 1), 0 },
 	{ INT(gas_rand_addr_lifetime), 0 },
 	{ INT_RANGE(gas_rand_mac_addr, 0, 2), 0 },
+	{ INT_RANGE(dpp_config_processing, 0, 2), 0 },
 };
 
 #undef FUNC
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 2f2bb01..07b67e6 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -41,6 +41,8 @@
 #define DEFAULT_P2P_GO_CTWINDOW 0
 #define DEFAULT_WPA_RSC_RELAXATION 1
 #define DEFAULT_MBO_CELL_CAPA MBO_CELL_CAPA_NOT_SUPPORTED
+#define DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD -75
+#define DEFAULT_OCE_SUPPORT OCE_STA
 
 #include "config_ssid.h"
 #include "wps/wps.h"
@@ -334,6 +336,7 @@
 #define CFG_CHANGED_NFC_PASSWORD_TOKEN BIT(15)
 #define CFG_CHANGED_P2P_PASSPHRASE_LEN BIT(16)
 #define CFG_CHANGED_SCHED_SCAN_PLANS BIT(17)
+#define CFG_CHANGED_WOWLAN_TRIGGERS BIT(18)
 
 /**
  * struct wpa_config - wpa_supplicant configuration data
@@ -625,6 +628,13 @@
 	u8 uuid[16];
 
 	/**
+	 * auto_uuid - Automatic UUID behavior
+	 * 0 = generate static value based on the local MAC address (default)
+	 * 1 = generate a random UUID every time wpa_supplicant starts
+	 */
+	int auto_uuid;
+
+	/**
 	 * device_name - Device Name (WPS)
 	 * User-friendly description of device; up to 32 octets encoded in
 	 * UTF-8
@@ -872,6 +882,34 @@
 	 */
 	int access_network_type;
 
+	 /**
+	  * go_interworking - Whether Interworking for P2P GO is enabled
+	  */
+	int go_interworking;
+
+	/**
+	 * go_access_network_type - P2P GO Access Network Type
+	 *
+	 * This indicates which access network type to advertise if Interworking
+	 * is enabled for P2P GO.
+	 */
+	int go_access_network_type;
+
+	/**
+	 * go_internet - Interworking: Internet connectivity (0 or 1)
+	 */
+	int go_internet;
+
+	/**
+	 * go_venue_group - Interworking: Venue group
+	 */
+	int go_venue_group;
+
+	/**
+	 * go_venue_type: Interworking: Venue type
+	 */
+	int go_venue_type;
+
 	/**
 	 * hessid - Homogenous ESS identifier
 	 *
@@ -1096,6 +1134,15 @@
 	unsigned int sched_scan_interval;
 
 	/**
+	 * sched_scan_start_delay - Schedule scan start delay before first scan
+	 *
+	 * Delay (in seconds) before scheduling first scan plan cycle. The
+	 * driver may ignore this parameter and start immediately (or at any
+	 * other time), if this feature is not supported.
+	 */
+	unsigned int sched_scan_start_delay;
+
+	/**
 	 * tdls_external_control - External control for TDLS setup requests
 	 *
 	 * Enable TDLS mode where external programs are given the control
@@ -1291,6 +1338,19 @@
 	 * mbo_cell_capa - Cellular capabilities for MBO
 	 */
 	enum mbo_cellular_capa mbo_cell_capa;
+
+	/**
+	 * disassoc_imminent_rssi_threshold - RSSI threshold of candidate AP
+	 * when disassociation imminent is set.
+	 */
+	int disassoc_imminent_rssi_threshold;
+
+	/**
+	 * oce - Enable OCE in STA and/or STA-CFON mode
+	 *  - Set BIT(0) to enable OCE in non-AP STA mode
+	 *  - Set BIT(1) to enable OCE in STA-CFON mode
+	 */
+	unsigned int oce;
 #endif /* CONFIG_MBO */
 
 	/**
@@ -1343,6 +1403,21 @@
 	 * 2 = like 1, but maintain OUI (with local admin bit set)
 	 */
 	int gas_rand_mac_addr;
+
+	/**
+	 * dpp_config_processing - How to process DPP configuration
+	 *
+	 * 0 = report received configuration to an external program for
+	 *	processing; do not generate any network profile internally
+	 * 1 = report received configuration to an external program and generate
+	 *	a network profile internally, but do not automatically connect
+	 *	to the created (disabled) profile; the network profile id is
+	 *	reported to external programs
+	 * 2 = report received configuration to an external program, generate
+	 *	a network profile internally, try to connect to the created
+	 *	profile automatically
+	 */
+	int dpp_config_processing;
 };
 
 
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 84a1ee9..6b7abe2 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -312,7 +312,7 @@
 		encoded_len += len;
 	}
 
-	if (!end) {
+	if (!end || !encoded) {
 		wpa_printf(MSG_ERROR, "Line %d: blob was not terminated "
 			   "properly", *line);
 		os_free(encoded);
@@ -503,6 +503,17 @@
 }
 
 
+static void write_bssid_hint(FILE *f, struct wpa_ssid *ssid)
+{
+	char *value = wpa_config_get(ssid, "bssid_hint");
+
+	if (!value)
+		return;
+	fprintf(f, "\tbssid_hint=%s\n", value);
+	os_free(value);
+}
+
+
 static void write_psk(FILE *f, struct wpa_ssid *ssid)
 {
 	char *value;
@@ -582,6 +593,22 @@
 }
 
 
+static void write_group_mgmt(FILE *f, struct wpa_ssid *ssid)
+{
+	char *value;
+
+	if (!ssid->group_mgmt_cipher)
+		return;
+
+	value = wpa_config_get(ssid, "group_mgmt");
+	if (!value)
+		return;
+	if (value[0])
+		fprintf(f, "\tgroup_mgmt=%s\n", value);
+	os_free(value);
+}
+
+
 static void write_auth_alg(FILE *f, struct wpa_ssid *ssid)
 {
 	char *value;
@@ -713,15 +740,18 @@
 	STR(ssid);
 	INT(scan_ssid);
 	write_bssid(f, ssid);
+	write_bssid_hint(f, ssid);
 	write_str(f, "bssid_blacklist", ssid);
 	write_str(f, "bssid_whitelist", ssid);
 	write_psk(f, ssid);
 	INT(mem_only_psk);
+	STR(sae_password);
 	write_proto(f, ssid);
 	write_key_mgmt(f, ssid);
 	INT_DEF(bg_scan_period, DEFAULT_BG_SCAN_PERIOD);
 	write_pairwise(f, ssid);
 	write_group(f, ssid);
+	write_group_mgmt(f, ssid);
 	write_auth_alg(f, ssid);
 	STR(bgscan);
 	STR(autoscan);
@@ -790,11 +820,16 @@
 #endif /* CONFIG_ACS */
 	write_int(f, "proactive_key_caching", ssid->proactive_key_caching, -1);
 	INT(disabled);
-	INT(peerkey);
 	INT(mixed_cell);
+	INT(vht);
+	INT_DEF(ht, 1);
+	INT(ht40);
 	INT(max_oper_chwidth);
+	INT(vht_center_freq1);
+	INT(vht_center_freq2);
 	INT(pbss);
 	INT(wps_disabled);
+	INT(fils_dh_group);
 #ifdef CONFIG_IEEE80211W
 	write_int(f, "ieee80211w", ssid->ieee80211w,
 		  MGMT_FRAME_PROTECTION_DEFAULT);
@@ -826,10 +861,18 @@
 	INT_DEF(dot11MeshRetryTimeout, DEFAULT_MESH_RETRY_TIMEOUT);
 	INT_DEF(dot11MeshConfirmTimeout, DEFAULT_MESH_CONFIRM_TIMEOUT);
 	INT_DEF(dot11MeshHoldingTimeout, DEFAULT_MESH_HOLDING_TIMEOUT);
+	INT_DEF(mesh_rssi_threshold, DEFAULT_MESH_RSSI_THRESHOLD);
 #endif /* CONFIG_MESH */
 	INT(wpa_ptk_rekey);
 	INT(group_rekey);
 	INT(ignore_broadcast_ssid);
+#ifdef CONFIG_DPP
+	STR(dpp_connector);
+	STR(dpp_netaccesskey);
+	INT(dpp_netaccesskey_expiry);
+	STR(dpp_csign);
+#endif /* CONFIG_DPP */
+	INT(owe_group);
 #ifdef CONFIG_HT_OVERRIDES
 	INT_DEF(disable_ht, DEFAULT_DISABLE_HT);
 	INT_DEF(disable_ht40, DEFAULT_DISABLE_HT40);
@@ -1082,6 +1125,8 @@
 		uuid_bin2str(config->uuid, buf, sizeof(buf));
 		fprintf(f, "uuid=%s\n", buf);
 	}
+	if (config->auto_uuid)
+		fprintf(f, "auto_uuid=%d\n", config->auto_uuid);
 	if (config->device_name)
 		fprintf(f, "device_name=%s\n", config->device_name);
 	if (config->manufacturer)
@@ -1247,6 +1292,17 @@
 	if (config->access_network_type != DEFAULT_ACCESS_NETWORK_TYPE)
 		fprintf(f, "access_network_type=%d\n",
 			config->access_network_type);
+	if (config->go_interworking)
+		fprintf(f, "go_interworking=%d\n", config->go_interworking);
+	if (config->go_access_network_type)
+		fprintf(f, "go_access_network_type=%d\n",
+			config->go_access_network_type);
+	if (config->go_internet)
+		fprintf(f, "go_internet=%d\n", config->go_internet);
+	if (config->go_venue_group)
+		fprintf(f, "go_venue_group=%d\n", config->go_venue_group);
+	if (config->go_venue_type)
+		fprintf(f, "go_venue_type=%d\n", config->go_venue_type);
 #endif /* CONFIG_INTERWORKING */
 	if (config->pbc_in_m1)
 		fprintf(f, "pbc_in_m1=%d\n", config->pbc_in_m1);
@@ -1320,6 +1376,10 @@
 		fprintf(f, "sched_scan_interval=%u\n",
 			config->sched_scan_interval);
 
+	if (config->sched_scan_start_delay)
+		fprintf(f, "sched_scan_start_delay=%u\n",
+			config->sched_scan_start_delay);
+
 	if (config->external_sim)
 		fprintf(f, "external_sim=%d\n", config->external_sim);
 
@@ -1394,6 +1454,12 @@
 		fprintf(f, "non_pref_chan=%s\n", config->non_pref_chan);
 	if (config->mbo_cell_capa != DEFAULT_MBO_CELL_CAPA)
 		fprintf(f, "mbo_cell_capa=%u\n", config->mbo_cell_capa);
+	if (config->disassoc_imminent_rssi_threshold !=
+	    DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD)
+		fprintf(f, "disassoc_imminent_rssi_threshold=%d\n",
+			config->disassoc_imminent_rssi_threshold);
+	if (config->oce != DEFAULT_OCE_SUPPORT)
+		fprintf(f, "oce=%u\n", config->oce);
 #endif /* CONFIG_MBO */
 
 	if (config->gas_address3)
@@ -1419,6 +1485,9 @@
 			config->gas_rand_addr_lifetime);
 	if (config->gas_rand_mac_addr)
 		fprintf(f, "gas_rand_mac_addr=%d\n", config->gas_rand_mac_addr);
+	if (config->dpp_config_processing)
+		fprintf(f, "dpp_config_processing=%d\n",
+			config->dpp_config_processing);
 
 }
 
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 69ace37..83d657d 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -28,6 +28,7 @@
 #define DEFAULT_MESH_RETRY_TIMEOUT 40
 #define DEFAULT_MESH_CONFIRM_TIMEOUT 40
 #define DEFAULT_MESH_HOLDING_TIMEOUT 40
+#define DEFAULT_MESH_RSSI_THRESHOLD 1 /* no change */
 #define DEFAULT_DISABLE_HT 0
 #define DEFAULT_DISABLE_HT40 0
 #define DEFAULT_DISABLE_SGI 0
@@ -146,6 +147,19 @@
 	int bssid_set;
 
 	/**
+	 * bssid_hint - BSSID hint
+	 *
+	 * If set, this is configured to the driver as a preferred initial BSSID
+	 * while connecting to this network.
+	 */
+	u8 bssid_hint[ETH_ALEN];
+
+	/**
+	 * bssid_hint_set - Whether BSSID hint is configured for this network
+	 */
+	int bssid_hint_set;
+
+	/**
 	 * go_p2p_dev_addr - GO's P2P Device Address or all zeros if not set
 	 */
 	u8 go_p2p_dev_addr[ETH_ALEN];
@@ -170,6 +184,16 @@
 	char *passphrase;
 
 	/**
+	 * sae_password - SAE password
+	 *
+	 * This parameter can be used to set a password for SAE. By default, the
+	 * passphrase value is used if this separate parameter is not used, but
+	 * passphrase follows the WPA-PSK constraints (8..63 characters) even
+	 * though SAE passwords do not have such constraints.
+	 */
+	char *sae_password;
+
+	/**
 	 * ext_psk - PSK/passphrase name in external storage
 	 *
 	 * If this is set, PSK/passphrase will be fetched from external storage
@@ -196,6 +220,15 @@
 	int group_cipher;
 
 	/**
+	 * group_mgmt_cipher - Bitfield of allowed group management ciphers
+	 *
+	 * This is a bitfield of WPA_CIPHER_AES_128_CMAC and WPA_CIPHER_BIP_*
+	 * values. If 0, no constraint is used for the cipher, i.e., whatever
+	 * the AP uses is accepted.
+	 */
+	int group_mgmt_cipher;
+
+	/**
 	 * key_mgmt - Bitfield of allowed key management protocols
 	 *
 	 * WPA_KEY_MGMT_*
@@ -392,17 +425,6 @@
 	int disabled_for_connect;
 
 	/**
-	 * peerkey -  Whether PeerKey handshake for direct links is allowed
-	 *
-	 * This is only used when both RSN/WPA2 and IEEE 802.11e (QoS) are
-	 * enabled.
-	 *
-	 * 0 = disabled (default)
-	 * 1 = enabled
-	 */
-	int peerkey;
-
-	/**
 	 * id_str - Network identifier string for external scripts
 	 *
 	 * This value is passed to external ctrl_iface monitors in
@@ -470,12 +492,14 @@
 	int dot11MeshConfirmTimeout; /* msec */
 	int dot11MeshHoldingTimeout; /* msec */
 
+	int ht;
 	int ht40;
 
 	int vht;
 
 	u8 max_oper_chwidth;
 
+	unsigned int vht_center_freq1;
 	unsigned int vht_center_freq2;
 
 	/**
@@ -806,12 +830,72 @@
 	int no_auto_peer;
 
 	/**
+	 * mesh_rssi_threshold - Set mesh parameter mesh_rssi_threshold (dBm)
+	 *
+	 * -255..-1 = threshold value in dBm
+	 * 0 = not using RSSI threshold
+	 * 1 = do not change driver default
+	 */
+	int mesh_rssi_threshold;
+
+	/**
 	 * wps_disabled - WPS disabled in AP mode
 	 *
 	 * 0 = WPS enabled and configured (default)
 	 * 1 = WPS disabled
 	 */
 	int wps_disabled;
+
+	/**
+	 * fils_dh_group - FILS DH Group
+	 *
+	 * 0 = PFS disabled with FILS shared key authentication
+	 * 1-65535 DH Group to use for FILS PFS
+	 */
+	int fils_dh_group;
+
+	/**
+	 * dpp_connector - DPP Connector (signedConnector as string)
+	 */
+	char *dpp_connector;
+
+	/**
+	 * dpp_netaccesskey - DPP netAccessKey (own private key)
+	 */
+	u8 *dpp_netaccesskey;
+
+	/**
+	 * dpp_netaccesskey_len - DPP netAccessKey length in octets
+	 */
+	size_t dpp_netaccesskey_len;
+
+	/**
+	 * net_access_key_expiry - DPP netAccessKey expiry in UNIX time stamp
+	 *
+	 * 0 indicates no expiration.
+	 */
+	unsigned int dpp_netaccesskey_expiry;
+
+	/**
+	 * dpp_csign - C-sign-key (Configurator public key)
+	 */
+	u8 *dpp_csign;
+
+	/**
+	 * dpp_csign_len - C-sign-key length in octets
+	 */
+	size_t dpp_csign_len;
+
+	/**
+	 * owe_group - OWE DH Group
+	 *
+	 * 0 = use default (19)
+	 * 1-65535 DH Group to use for OWE
+	 *
+	 * Groups 19 (NIST P-256), 20 (NIST P-384), and 21 (NIST P-521) are
+	 * currently supported.
+	 */
+	int owe_group;
 };
 
 #endif /* CONFIG_SSID_H */
diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c
index 82ba3b0..24f496b 100644
--- a/wpa_supplicant/config_winreg.c
+++ b/wpa_supplicant/config_winreg.c
@@ -99,13 +99,12 @@
 			break;
 		}
 		blob->name = os_strdup((char *) name);
-		blob->data = os_malloc(datalen);
+		blob->data = os_memdup(data, datalen);
 		if (blob->name == NULL || blob->data == NULL) {
 			wpa_config_free_blob(blob);
 			errors++;
 			break;
 		}
-		os_memcpy(blob->data, data, datalen);
 		blob->len = datalen;
 
 		wpa_config_set_blob(config, blob);
@@ -234,6 +233,7 @@
 #ifdef CONFIG_WPS
 	if (wpa_config_read_global_uuid(config, hk))
 		errors++;
+	wpa_config_read_reg_dword(hk, TEXT("auto_uuid"), &config->auto_uuid);
 	config->device_name = wpa_config_read_reg_string(
 		hk, TEXT("device_name"));
 	config->manufacturer = wpa_config_read_reg_string(
@@ -580,6 +580,8 @@
 		uuid_bin2str(config->uuid, buf, sizeof(buf));
 		wpa_config_write_reg_string(hk, "uuid", buf);
 	}
+	wpa_config_write_reg_dword(hk, TEXT("auto_uuid"), config->auto_uuid,
+				   0);
 	wpa_config_write_reg_string(hk, "device_name", config->device_name);
 	wpa_config_write_reg_string(hk, "manufacturer", config->manufacturer);
 	wpa_config_write_reg_string(hk, "model_name", config->model_name);
@@ -868,6 +870,7 @@
 	INT(scan_ssid);
 	write_bssid(netw, ssid);
 	write_psk(netw, ssid);
+	STR(sae_password);
 	write_proto(netw, ssid);
 	write_key_mgmt(netw, ssid);
 	write_pairwise(netw, ssid);
@@ -924,7 +927,6 @@
 	write_int(netw, "proactive_key_caching", ssid->proactive_key_caching,
 		  -1);
 	INT(disabled);
-	INT(peerkey);
 #ifdef CONFIG_IEEE80211W
 	write_int(netw, "ieee80211w", ssid->ieee80211w,
 		  MGMT_FRAME_PROTECTION_DEFAULT);
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 761d917..c4d8dfe 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -52,6 +52,7 @@
 #include "offchannel.h"
 #include "drivers/driver.h"
 #include "mesh.h"
+#include "dpp_supplicant.h"
 
 static int wpa_supplicant_global_iface_list(struct wpa_global *global,
 					    char *buf, int len);
@@ -61,6 +62,29 @@
 static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s,
 					char *val);
 
+
+#ifdef CONFIG_FILS
+
+static int wpa_is_fils_supported(struct wpa_supplicant *wpa_s)
+{
+	return (((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+		 (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_FILS)) ||
+		(!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+		 (wpa_s->drv_flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD)));
+}
+
+
+#ifdef CONFIG_FILS_SK_PFS
+static int wpa_is_fils_sk_pfs_supported(struct wpa_supplicant *wpa_s)
+{
+	return (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+		(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_FILS);
+}
+#endif /* CONFIG_FILS_SK_PFS */
+
+#endif /* CONFIG_FILS */
+
+
 static int set_bssid_filter(struct wpa_supplicant *wpa_s, char *val)
 {
 	char *pos;
@@ -576,6 +600,11 @@
 		ret = set_disallow_aps(wpa_s, value);
 	} else if (os_strcasecmp(cmd, "no_keep_alive") == 0) {
 		wpa_s->no_keep_alive = !!atoi(value);
+#ifdef CONFIG_DPP
+	} else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) {
+		os_free(wpa_s->dpp_configurator_params);
+		wpa_s->dpp_configurator_params = os_strdup(value);
+#endif /* CONFIG_DPP */
 #ifdef CONFIG_TESTING_OPTIONS
 	} else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) {
 		wpa_s->ext_mgmt_frame_handling = !!atoi(value);
@@ -597,6 +626,8 @@
 		wpa_s->ignore_auth_resp = !!atoi(value);
 	} else if (os_strcasecmp(cmd, "ignore_assoc_disallow") == 0) {
 		wpa_s->ignore_assoc_disallow = !!atoi(value);
+		wpa_drv_ignore_assoc_disallow(wpa_s,
+					      wpa_s->ignore_assoc_disallow);
 	} else if (os_strcasecmp(cmd, "reject_btm_req_reason") == 0) {
 		wpa_s->reject_btm_req_reason = atoi(value);
 	} else if (os_strcasecmp(cmd, "get_pref_freq_list_override") == 0) {
@@ -605,6 +636,26 @@
 			wpa_s->get_pref_freq_list_override = NULL;
 		else
 			wpa_s->get_pref_freq_list_override = os_strdup(value);
+	} else if (os_strcasecmp(cmd, "sae_commit_override") == 0) {
+		wpabuf_free(wpa_s->sae_commit_override);
+		if (value[0] == '\0')
+			wpa_s->sae_commit_override = NULL;
+		else
+			wpa_s->sae_commit_override = wpabuf_parse_bin(value);
+#ifdef CONFIG_DPP
+	} else if (os_strcasecmp(cmd, "dpp_config_obj_override") == 0) {
+		os_free(wpa_s->dpp_config_obj_override);
+		wpa_s->dpp_config_obj_override = os_strdup(value);
+	} else if (os_strcasecmp(cmd, "dpp_discovery_override") == 0) {
+		os_free(wpa_s->dpp_discovery_override);
+		wpa_s->dpp_discovery_override = os_strdup(value);
+	} else if (os_strcasecmp(cmd, "dpp_groups_override") == 0) {
+		os_free(wpa_s->dpp_groups_override);
+		wpa_s->dpp_groups_override = os_strdup(value);
+	} else if (os_strcasecmp(cmd,
+				 "dpp_ignore_netaccesskey_mismatch") == 0) {
+		wpa_s->dpp_ignore_netaccesskey_mismatch = atoi(value);
+#endif /* CONFIG_DPP */
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifndef CONFIG_NO_CONFIG_BLOBS
 	} else if (os_strcmp(cmd, "blob") == 0) {
@@ -621,6 +672,25 @@
 		}
 	} else if (os_strcasecmp(cmd, "mbo_cell_capa") == 0) {
 		wpas_mbo_update_cell_capa(wpa_s, atoi(value));
+	} else if (os_strcasecmp(cmd, "oce") == 0) {
+		wpa_s->conf->oce = atoi(value);
+		if (wpa_s->conf->oce) {
+			if ((wpa_s->conf->oce & OCE_STA) &&
+			    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA))
+				wpa_s->enable_oce = OCE_STA;
+
+			if ((wpa_s->conf->oce & OCE_STA_CFON) &&
+			    (wpa_s->drv_flags &
+			     WPA_DRIVER_FLAGS_OCE_STA_CFON)) {
+				/* TODO: Need to add STA-CFON support */
+				wpa_printf(MSG_ERROR,
+					   "OCE STA-CFON feature is not yet supported");
+				return -1;
+			}
+		} else {
+			wpa_s->enable_oce = 0;
+		}
+		wpa_supplicant_set_default_scan_ies(wpa_s);
 #endif /* CONFIG_MBO */
 	} else if (os_strcasecmp(cmd, "lci") == 0) {
 		ret = wpas_ctrl_iface_set_lci(wpa_s, value);
@@ -632,6 +702,8 @@
 		ret = wpas_ctrl_set_relative_band_adjust(wpa_s, value);
 	} else if (os_strcasecmp(cmd, "ric_ies") == 0) {
 		ret = wpas_ctrl_iface_set_ric_ies(wpa_s, value);
+	} else if (os_strcasecmp(cmd, "roaming") == 0) {
+		ret = wpa_drv_roaming(wpa_s, atoi(value), NULL);
 	} else {
 		value[-1] = '=';
 		ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
@@ -677,6 +749,12 @@
 #endif /* CONFIG_TESTING_GET_GTK */
 	} else if (os_strcmp(cmd, "tls_library") == 0) {
 		res = tls_get_library_version(buf, buflen);
+#ifdef CONFIG_TESTING_OPTIONS
+	} else if (os_strcmp(cmd, "anonce") == 0) {
+		return wpa_snprintf_hex(buf, buflen,
+					wpa_sm_get_anonce(wpa_s->wpa),
+					WPA_NONCE_LEN);
+#endif /* CONFIG_TESTING_OPTIONS */
 	} else {
 		res = wpa_config_get_value(cmd, wpa_s->conf, buf, buflen);
 	}
@@ -710,27 +788,6 @@
 #endif /* IEEE8021X_EAPOL */
 
 
-#ifdef CONFIG_PEERKEY
-/* MLME-STKSTART.request(peer) */
-static int wpa_supplicant_ctrl_iface_stkstart(
-	struct wpa_supplicant *wpa_s, char *addr)
-{
-	u8 peer[ETH_ALEN];
-
-	if (hwaddr_aton(addr, peer)) {
-		wpa_printf(MSG_DEBUG, "CTRL_IFACE STKSTART: invalid "
-			   "address '%s'", addr);
-		return -1;
-	}
-
-	wpa_printf(MSG_DEBUG, "CTRL_IFACE STKSTART " MACSTR,
-		   MAC2STR(peer));
-
-	return wpa_sm_stkstart(wpa_s->wpa, peer);
-}
-#endif /* CONFIG_PEERKEY */
-
-
 #ifdef CONFIG_TDLS
 
 static int wpa_supplicant_ctrl_iface_tdls_discover(
@@ -2578,6 +2635,26 @@
 #endif /* CONFIG_IEEE80211R */
 #endif /* CONFIG_FILS */
 
+#ifdef CONFIG_OWE
+	if (data.key_mgmt & WPA_KEY_MGMT_OWE) {
+		ret = os_snprintf(pos, end - pos, "%sOWE",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+#endif /* CONFIG_OWE */
+
+#ifdef CONFIG_DPP
+	if (data.key_mgmt & WPA_KEY_MGMT_DPP) {
+		ret = os_snprintf(pos, end - pos, "%sDPP",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+#endif /* CONFIG_DPP */
+
 	if (data.key_mgmt & WPA_KEY_MGMT_OSEN) {
 		ret = os_snprintf(pos, end - pos, "%sOSEN",
 				  pos == start ? "" : "+");
@@ -2653,7 +2730,7 @@
 {
 	char *pos, *end;
 	int ret;
-	const u8 *ie, *ie2, *osen_ie, *p2p, *mesh;
+	const u8 *ie, *ie2, *osen_ie, *p2p, *mesh, *owe;
 
 	mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID);
 	p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
@@ -2684,6 +2761,14 @@
 	if (osen_ie)
 		pos = wpa_supplicant_ie_txt(pos, end, "OSEN",
 					    osen_ie, 2 + osen_ie[1]);
+	owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
+	if (owe) {
+		ret = os_snprintf(pos, end - pos,
+				  ie2 ? "[OWE-TRANS]" : "[OWE-TRANS-OPEN]");
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
 	pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
 	if (!ie && !ie2 && !osen_ie && (bss->caps & IEEE80211_CAP_PRIVACY)) {
 		ret = os_snprintf(pos, end - pos, "[WEP]");
@@ -2984,9 +3069,8 @@
 	if (pos) {
 		int *freqs = freq_range_to_channel_list(wpa_s, pos + 6);
 		if (freqs) {
-			wpa_s->scan_req = MANUAL_SCAN_REQ;
-			os_free(wpa_s->manual_scan_freqs);
-			wpa_s->manual_scan_freqs = freqs;
+			os_free(wpa_s->select_network_scan_freqs);
+			wpa_s->select_network_scan_freqs = freqs;
 		}
 	}
 
@@ -3161,6 +3245,7 @@
 		return 0; /* No change to the previously configured value */
 
 	if (os_strcmp(name, "bssid") != 0 &&
+	    os_strcmp(name, "bssid_hint") != 0 &&
 	    os_strcmp(name, "priority") != 0) {
 		wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
 
@@ -3796,6 +3881,50 @@
 		pos += ret;
 	}
 #endif /* CONFIG_SUITEB192 */
+#ifdef CONFIG_OWE
+	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_OWE) {
+		ret = os_snprintf(pos, end - pos, " OWE");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_DPP) {
+		ret = os_snprintf(pos, end - pos, " DPP");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_DPP */
+#ifdef CONFIG_FILS
+	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256) {
+		ret = os_snprintf(pos, end - pos, " FILS-SHA256");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384) {
+		ret = os_snprintf(pos, end - pos, " FILS-SHA384");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#ifdef CONFIG_IEEE80211R
+	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256) {
+		ret = os_snprintf(pos, end - pos, " FT-FILS-SHA256");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384) {
+		ret = os_snprintf(pos, end - pos, " FT-FILS-SHA384");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_FILS */
 
 	return pos - buf;
 }
@@ -3898,6 +4027,26 @@
 	}
 #endif /* CONFIG_SAE */
 
+#ifdef CONFIG_FILS
+	if (wpa_is_fils_supported(wpa_s)) {
+		ret = os_snprintf(pos, end - pos, "%sFILS_SK_WITHOUT_PFS",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+#ifdef CONFIG_FILS_SK_PFS
+	if (wpa_is_fils_sk_pfs_supported(wpa_s)) {
+		ret = os_snprintf(pos, end - pos, "%sFILS_SK_WITH_PFS",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_FILS_SK_PFS */
+#endif /* CONFIG_FILS */
+
 	return pos - buf;
 }
 
@@ -4156,12 +4305,23 @@
 #endif /* CONFIG_ACS */
 
 #ifdef CONFIG_FILS
-	if (os_strcmp(field, "fils") == 0 &&
-	    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_FILS)) {
-		res = os_snprintf(buf, buflen, "FILS");
-		if (os_snprintf_error(buflen, res))
-			return -1;
-		return res;
+	if (os_strcmp(field, "fils") == 0) {
+#ifdef CONFIG_FILS_SK_PFS
+		if (wpa_is_fils_supported(wpa_s) &&
+		    wpa_is_fils_sk_pfs_supported(wpa_s)) {
+			res = os_snprintf(buf, buflen, "FILS FILS-SK-PFS");
+			if (os_snprintf_error(buflen, res))
+				return -1;
+			return res;
+		}
+#endif /* CONFIG_FILS_SK_PFS */
+
+		if (wpa_is_fils_supported(wpa_s)) {
+			res = os_snprintf(buf, buflen, "FILS");
+			if (os_snprintf_error(buflen, res))
+				return -1;
+			return res;
+		}
 	}
 #endif /* CONFIG_FILS */
 
@@ -4285,7 +4445,7 @@
 	size_t i;
 	int ret;
 	char *pos, *end;
-	const u8 *ie, *ie2, *osen_ie, *mesh;
+	const u8 *ie, *ie2, *osen_ie, *mesh, *owe;
 
 	pos = buf;
 	end = buf + buflen;
@@ -4409,6 +4569,15 @@
 		if (osen_ie)
 			pos = wpa_supplicant_ie_txt(pos, end, "OSEN",
 						    osen_ie, 2 + osen_ie[1]);
+		owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
+		if (owe) {
+			ret = os_snprintf(
+				pos, end - pos,
+				ie2 ? "[OWE-TRANS]" : "[OWE-TRANS-OPEN]");
+			if (os_snprintf_error(end - pos, ret))
+				return 0;
+			pos += ret;
+		}
 		pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
 		if (!ie && !ie2 && !osen_ie &&
 		    (bss->caps & IEEE80211_CAP_PRIVACY)) {
@@ -6506,6 +6675,12 @@
 {
 	os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
 	wpa_s->force_long_sd = 0;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	os_free(wpa_s->get_pref_freq_list_override);
+	wpa_s->get_pref_freq_list_override = NULL;
+#endif /* CONFIG_TESTING_OPTIONS */
+
 	wpas_p2p_stop_find(wpa_s);
 	wpa_s->parent->p2ps_method_config_any = 0;
 	if (wpa_s->global->p2p)
@@ -6713,7 +6888,7 @@
 	u16 id[MAX_ANQP_INFO_ID];
 	size_t num_id = 0;
 	u32 subtypes = 0;
-	int get_cell_pref = 0;
+	u32 mbo_subtypes = 0;
 
 	used = hwaddr_aton2(dst, dst_addr);
 	if (used < 0)
@@ -6734,9 +6909,10 @@
 		} else if (os_strncmp(pos, "mbo:", 4) == 0) {
 #ifdef CONFIG_MBO
 			int num = atoi(pos + 4);
-			if (num != MBO_ANQP_SUBTYPE_CELL_CONN_PREF)
+
+			if (num <= 0 || num > MAX_MBO_ANQP_SUBTYPE)
 				return -1;
-			get_cell_pref = 1;
+			mbo_subtypes |= BIT(num);
 #else /* CONFIG_MBO */
 			return -1;
 #endif /* CONFIG_MBO */
@@ -6751,11 +6927,11 @@
 		pos++;
 	}
 
-	if (num_id == 0)
+	if (num_id == 0 && !subtypes && !mbo_subtypes)
 		return -1;
 
 	return anqp_send_req(wpa_s, dst_addr, id, num_id, subtypes,
-			     get_cell_pref);
+			     mbo_subtypes);
 }
 
 
@@ -7157,26 +7333,25 @@
 static int wpas_ctrl_iface_wnm_bss_query(struct wpa_supplicant *wpa_s, char *cmd)
 {
 	int query_reason, list = 0;
+	char *btm_candidates = NULL;
 
 	query_reason = atoi(cmd);
 
 	cmd = os_strchr(cmd, ' ');
 	if (cmd) {
-		cmd++;
-		if (os_strncmp(cmd, "list", 4) == 0) {
+		if (os_strncmp(cmd, " list", 5) == 0)
 			list = 1;
-		} else {
-			wpa_printf(MSG_DEBUG, "WNM Query: Invalid option %s",
-				   cmd);
-			return -1;
-		}
+		else
+			btm_candidates = cmd;
 	}
 
 	wpa_printf(MSG_DEBUG,
 		   "CTRL_IFACE: WNM_BSS_QUERY query_reason=%d%s",
 		   query_reason, list ? " candidate list" : "");
 
-	return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason, list);
+	return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason,
+						  btm_candidates,
+						  list);
 }
 
 #endif /* CONFIG_WNM */
@@ -7531,6 +7706,10 @@
 	wpa_s->after_wps = 0;
 	wpa_s->known_wps_freq = 0;
 
+#ifdef CONFIG_DPP
+	wpas_dpp_deinit(wpa_s);
+#endif /* CONFIG_DPP */
+
 #ifdef CONFIG_TDLS
 #ifdef CONFIG_TDLS_TESTING
 	tdls_testing = 0;
@@ -7592,15 +7771,20 @@
 	wpa_s->p2p_go_csa_on_inv = 0;
 	wpa_s->ignore_auth_resp = 0;
 	wpa_s->ignore_assoc_disallow = 0;
+	wpa_s->testing_resend_assoc = 0;
 	wpa_s->reject_btm_req_reason = 0;
 	wpa_sm_set_test_assoc_ie(wpa_s->wpa, NULL);
 	os_free(wpa_s->get_pref_freq_list_override);
 	wpa_s->get_pref_freq_list_override = NULL;
+	wpabuf_free(wpa_s->sae_commit_override);
+	wpa_s->sae_commit_override = NULL;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	wpa_s->disconnected = 0;
 	os_free(wpa_s->next_scan_freqs);
 	wpa_s->next_scan_freqs = NULL;
+	os_free(wpa_s->select_network_scan_freqs);
+	wpa_s->select_network_scan_freqs = NULL;
 
 	wpa_bss_flush(wpa_s);
 	if (!dl_list_empty(&wpa_s->bss)) {
@@ -7918,6 +8102,19 @@
 			goto done;
 		}
 
+		pos = os_strstr(params, "bssid=");
+		if (pos) {
+			u8 bssid[ETH_ALEN];
+
+			pos += 6;
+			if (hwaddr_aton(pos, bssid)) {
+				wpa_printf(MSG_ERROR, "Invalid BSSID %s", pos);
+				*reply_len = -1;
+				goto done;
+			}
+			os_memcpy(wpa_s->next_scan_bssid, bssid, ETH_ALEN);
+		}
+
 		pos = params;
 		while (pos && *pos != '\0') {
 			if (os_strncmp(pos, "ssid ", 5) == 0) {
@@ -8717,6 +8914,79 @@
 	return 0;
 }
 
+
+static int wpas_ctrl_reset_pn(struct wpa_supplicant *wpa_s)
+{
+	u8 zero[WPA_TK_MAX_LEN];
+
+	if (wpa_s->last_tk_alg == WPA_ALG_NONE)
+		return -1;
+
+	wpa_printf(MSG_INFO, "TESTING: Reset PN");
+	os_memset(zero, 0, sizeof(zero));
+
+	/* First, use a zero key to avoid any possible duplicate key avoidance
+	 * in the driver. */
+	if (wpa_drv_set_key(wpa_s, wpa_s->last_tk_alg, wpa_s->last_tk_addr,
+			    wpa_s->last_tk_key_idx, 1, zero, 6,
+			    zero, wpa_s->last_tk_len) < 0)
+		return -1;
+
+	/* Set the previously configured key to reset its TSC/RSC */
+	return wpa_drv_set_key(wpa_s, wpa_s->last_tk_alg, wpa_s->last_tk_addr,
+			       wpa_s->last_tk_key_idx, 1, zero, 6,
+			       wpa_s->last_tk, wpa_s->last_tk_len);
+}
+
+
+static int wpas_ctrl_key_request(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	const char *pos = cmd;
+	int error, pairwise;
+
+	error = atoi(pos);
+	pos = os_strchr(pos, ' ');
+	if (!pos)
+		return -1;
+	pairwise = atoi(pos);
+	wpa_sm_key_request(wpa_s->wpa, error, pairwise);
+	return 0;
+}
+
+
+static int wpas_ctrl_resend_assoc(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_SME
+	struct wpa_driver_associate_params params;
+	int ret;
+
+	os_memset(&params, 0, sizeof(params));
+	params.bssid = wpa_s->bssid;
+	params.ssid = wpa_s->sme.ssid;
+	params.ssid_len = wpa_s->sme.ssid_len;
+	params.freq.freq = wpa_s->sme.freq;
+	if (wpa_s->last_assoc_req_wpa_ie) {
+		params.wpa_ie = wpabuf_head(wpa_s->last_assoc_req_wpa_ie);
+		params.wpa_ie_len = wpabuf_len(wpa_s->last_assoc_req_wpa_ie);
+	}
+	params.pairwise_suite = wpa_s->pairwise_cipher;
+	params.group_suite = wpa_s->group_cipher;
+	params.mgmt_group_suite = wpa_s->mgmt_group_cipher;
+	params.key_mgmt_suite = wpa_s->key_mgmt;
+	params.wpa_proto = wpa_s->wpa_proto;
+	params.mgmt_frame_protection = wpa_s->sme.mfp;
+	params.rrm_used = wpa_s->rrm.rrm_used;
+	if (wpa_s->sme.prev_bssid_set)
+		params.prev_bssid = wpa_s->sme.prev_bssid;
+	wpa_printf(MSG_INFO, "TESTING: Resend association request");
+	ret = wpa_drv_associate(wpa_s, &params);
+	wpa_s->testing_resend_assoc = 1;
+	return ret;
+#else /* CONFIG_SME */
+	return -1;
+#endif /* CONFIG_SME */
+}
+
 #endif /* CONFIG_TESTING_OPTIONS */
 
 
@@ -9164,6 +9434,7 @@
 	 * Entry format:
 	 * <BSSID> <PMKID> <PMK> <reauth_time in seconds>
 	 * <expiration in seconds> <akmp> <opportunistic>
+	 * [FILS Cache Identifier]
 	 */
 
 	for (entry = wpa_sm_pmksa_cache_head(wpa_s->wpa); entry;
@@ -9198,6 +9469,15 @@
 			break;
 		pos2 += ret;
 
+		if (entry->fils_cache_id_set) {
+			ret = os_snprintf(pos2, end - pos2, " %02x%02x",
+					  entry->fils_cache_id[0],
+					  entry->fils_cache_id[1]);
+			if (os_snprintf_error(end - pos2, ret))
+				break;
+			pos2 += ret;
+		}
+
 		ret = os_snprintf(pos2, end - pos2, "\n");
 		if (os_snprintf_error(end - pos2, ret))
 			break;
@@ -9218,12 +9498,13 @@
 	char *pos, *pos2;
 	int ret = -1;
 	struct os_reltime now;
-	int reauth_time = 0, expiration = 0;
+	int reauth_time = 0, expiration = 0, i;
 
 	/*
 	 * Entry format:
 	 * <network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds>
 	 * <expiration in seconds> <akmp> <opportunistic>
+	 * [FILS Cache Identifier]
 	 */
 
 	ssid = wpa_config_get_network(wpa_s->conf, atoi(cmd));
@@ -9271,6 +9552,21 @@
 	if (sscanf(pos, "%d %d %d %d", &reauth_time, &expiration,
 		   &entry->akmp, &entry->opportunistic) != 4)
 		goto fail;
+	for (i = 0; i < 4; i++) {
+		pos = os_strchr(pos, ' ');
+		if (!pos) {
+			if (i < 3)
+				goto fail;
+			break;
+		}
+		pos++;
+	}
+	if (pos) {
+		if (hexstr2bin(pos, entry->fils_cache_id,
+			       FILS_CACHE_ID_LEN) < 0)
+			goto fail;
+		entry->fils_cache_id_set = 1;
+	}
 	os_get_reltime(&now);
 	entry->expiration = now.sec + expiration;
 	entry->reauth_time = now.sec + reauth_time;
@@ -9377,7 +9673,9 @@
 	int reply_len;
 
 	if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 ||
-	    os_strncmp(buf, "SET_NETWORK ", 12) == 0) {
+	    os_strncmp(buf, "SET_NETWORK ", 12) == 0 ||
+	    os_strncmp(buf, "PMKSA_ADD ", 10) == 0 ||
+	    os_strncmp(buf, "MESH_PMKSA_ADD ", 15) == 0) {
 		if (wpa_debug_show_keys)
 			wpa_dbg(wpa_s, MSG_DEBUG,
 				"Control interface command '%s'", buf);
@@ -9386,7 +9684,9 @@
 				"Control interface command '%s [REMOVED]'",
 				os_strncmp(buf, WPA_CTRL_RSP,
 					   os_strlen(WPA_CTRL_RSP)) == 0 ?
-				WPA_CTRL_RSP : "SET_NETWORK");
+				WPA_CTRL_RSP :
+				(os_strncmp(buf, "SET_NETWORK ", 12) == 0 ?
+				 "SET_NETWORK" : "key-add"));
 	} else if (os_strncmp(buf, "WPS_NFC_TAG_READ", 16) == 0 ||
 		   os_strncmp(buf, "NFC_REPORT_HANDOVER", 19) == 0) {
 		wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface",
@@ -9482,11 +9782,6 @@
 		if (wpa_supplicant_ctrl_iface_preauth(wpa_s, buf + 8))
 			reply_len = -1;
 #endif /* IEEE8021X_EAPOL */
-#ifdef CONFIG_PEERKEY
-	} else if (os_strncmp(buf, "STKSTART ", 9) == 0) {
-		if (wpa_supplicant_ctrl_iface_stkstart(wpa_s, buf + 9))
-			reply_len = -1;
-#endif /* CONFIG_PEERKEY */
 #ifdef CONFIG_IEEE80211R
 	} else if (os_strncmp(buf, "FT_DS ", 6) == 0) {
 		if (wpa_supplicant_ctrl_iface_ft_ds(wpa_s, buf + 6))
@@ -10063,6 +10358,15 @@
 	} else if (os_strncmp(buf, "TEST_ASSOC_IE ", 14) == 0) {
 		if (wpas_ctrl_test_assoc_ie(wpa_s, buf + 14) < 0)
 			reply_len = -1;
+	} else if (os_strcmp(buf, "RESET_PN") == 0) {
+		if (wpas_ctrl_reset_pn(wpa_s) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "KEY_REQUEST ", 12) == 0) {
+		if (wpas_ctrl_key_request(wpa_s, buf + 12) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "RESEND_ASSOC") == 0) {
+		if (wpas_ctrl_resend_assoc(wpa_s) < 0)
+			reply_len = -1;
 #endif /* CONFIG_TESTING_OPTIONS */
 	} else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) {
 		if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0)
@@ -10091,6 +10395,86 @@
 	} else if (os_strcmp(buf, "FILS_HLP_REQ_FLUSH") == 0) {
 		wpas_flush_fils_hlp_req(wpa_s);
 #endif /* CONFIG_FILS */
+#ifdef CONFIG_DPP
+	} else if (os_strncmp(buf, "DPP_QR_CODE ", 12) == 0) {
+		int res;
+
+		res = wpas_dpp_qr_code(wpa_s, buf + 12);
+		if (res < 0) {
+			reply_len = -1;
+		} else {
+			reply_len = os_snprintf(reply, reply_size, "%d", res);
+			if (os_snprintf_error(reply_size, reply_len))
+				reply_len = -1;
+		}
+	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_GEN ", 18) == 0) {
+		int res;
+
+		res = wpas_dpp_bootstrap_gen(wpa_s, buf + 18);
+		if (res < 0) {
+			reply_len = -1;
+		} else {
+			reply_len = os_snprintf(reply, reply_size, "%d", res);
+			if (os_snprintf_error(reply_size, reply_len))
+				reply_len = -1;
+		}
+	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_REMOVE ", 21) == 0) {
+		if (wpas_dpp_bootstrap_remove(wpa_s, buf + 21) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_GET_URI ", 22) == 0) {
+		const char *uri;
+
+		uri = wpas_dpp_bootstrap_get_uri(wpa_s, atoi(buf + 22));
+		if (!uri) {
+			reply_len = -1;
+		} else {
+			reply_len = os_snprintf(reply, reply_size, "%s", uri);
+			if (os_snprintf_error(reply_size, reply_len))
+				reply_len = -1;
+		}
+	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_INFO ", 19) == 0) {
+		reply_len = wpas_dpp_bootstrap_info(wpa_s, atoi(buf + 19),
+						    reply, reply_size);
+	} else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) {
+		if (wpas_dpp_auth_init(wpa_s, buf + 13) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DPP_LISTEN ", 11) == 0) {
+		if (wpas_dpp_listen(wpa_s, buf + 11) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "DPP_STOP_LISTEN") == 0) {
+		wpas_dpp_listen_stop(wpa_s);
+	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_ADD", 20) == 0) {
+		int res;
+
+		res = wpas_dpp_configurator_add(wpa_s, buf + 20);
+		if (res < 0) {
+			reply_len = -1;
+		} else {
+			reply_len = os_snprintf(reply, reply_size, "%d", res);
+			if (os_snprintf_error(reply_size, reply_len))
+				reply_len = -1;
+		}
+	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_REMOVE ", 24) == 0) {
+		if (wpas_dpp_configurator_remove(wpa_s, buf + 24) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_SIGN ", 22) == 0) {
+		if (wpas_dpp_configurator_sign(wpa_s, buf + 22) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) {
+		int res;
+
+		res = wpas_dpp_pkex_add(wpa_s, buf + 12);
+		if (res < 0) {
+			reply_len = -1;
+		} else {
+			reply_len = os_snprintf(reply, reply_size, "%d", res);
+			if (os_snprintf_error(reply_size, reply_len))
+				reply_len = -1;
+		}
+	} else if (os_strncmp(buf, "DPP_PKEX_REMOVE ", 16) == 0) {
+		if (wpas_dpp_pkex_remove(wpa_s, buf + 16) < 0)
+			reply_len = -1;
+#endif /* CONFIG_DPP */
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
@@ -10409,6 +10793,7 @@
 #ifdef ANDROID
 		"DRIVER ",
 #endif /* ANDROID */
+		"GET_CAPABILITY ",
 		"GET_NETWORK ",
 		"REMOVE_NETWORK ",
 		"P2P_FIND ",
diff --git a/wpa_supplicant/ctrl_iface_named_pipe.c b/wpa_supplicant/ctrl_iface_named_pipe.c
index 54e0e2f..9c0a47e 100644
--- a/wpa_supplicant/ctrl_iface_named_pipe.c
+++ b/wpa_supplicant/ctrl_iface_named_pipe.c
@@ -319,13 +319,12 @@
 	}
 
 	os_free(dst->rsp_buf);
-	dst->rsp_buf = os_malloc(send_len);
+	dst->rsp_buf = os_memdup(send_buf, send_len);
 	if (dst->rsp_buf == NULL) {
 		ctrl_close_pipe(dst);
 		os_free(reply);
 		return;
 	}
-	os_memcpy(dst->rsp_buf, send_buf, send_len);
 	os_free(reply);
 
 	if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap,
@@ -739,13 +738,12 @@
 	}
 
 	os_free(dst->rsp_buf);
-	dst->rsp_buf = os_malloc(send_len);
+	dst->rsp_buf = os_memdup(send_buf, send_len);
 	if (dst->rsp_buf == NULL) {
 		global_close_pipe(dst);
 		os_free(reply);
 		return;
 	}
-	os_memcpy(dst->rsp_buf, send_buf, send_len);
 	os_free(reply);
 
 	if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap,
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index 0c355f7..8115f77 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -793,6 +793,144 @@
 
 #endif /* CONFIG_WPS */
 
+
+#ifdef CONFIG_MESH
+
+void wpas_dbus_signal_mesh_group_started(struct wpa_supplicant *wpa_s,
+					 struct wpa_ssid *ssid)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (!iface || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_MESH,
+				      "MeshGroupStarted");
+	if (!msg)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_byte_array(&dict_iter, "SSID",
+					     (const char *) ssid->ssid,
+					     ssid->ssid_len) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
+void wpas_dbus_signal_mesh_group_removed(struct wpa_supplicant *wpa_s,
+					 const u8 *meshid, u8 meshid_len,
+					 int reason)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (!iface || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_MESH,
+				      "MeshGroupRemoved");
+	if (!msg)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_byte_array(&dict_iter, "SSID",
+					     (const char *) meshid,
+					     meshid_len) ||
+	    !wpa_dbus_dict_append_int32(&dict_iter, "DisconnectReason",
+					reason) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
+void wpas_dbus_signal_mesh_peer_connected(struct wpa_supplicant *wpa_s,
+					  const u8 *peer_addr)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (!iface || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_MESH,
+				      "MeshPeerConnected");
+	if (!msg)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_byte_array(&dict_iter, "PeerAddress",
+					     (const char *) peer_addr,
+					     ETH_ALEN) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
+void wpas_dbus_signal_mesh_peer_disconnected(struct wpa_supplicant *wpa_s,
+					     const u8 *peer_addr, int reason)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (!iface || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_MESH,
+				      "MeshPeerDisconnected");
+	if (!msg)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_byte_array(&dict_iter, "PeerAddress",
+					     (const char *) peer_addr,
+					     ETH_ALEN) ||
+	    !wpa_dbus_dict_append_int32(&dict_iter, "DisconnectReason",
+					reason) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+#endif /* CONFIG_MESH */
+
+
 void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s,
 				    int depth, const char *subject,
 				    const char *altsubject[],
@@ -3086,6 +3224,20 @@
 		  END_ARGS
 	  }
 	},
+	{ "TDLSChannelSwitch", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_tdls_channel_switch,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "TDLSCancelChannelSwitch", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_tdls_cancel_channel_switch,
+	  {
+		  { "peer_address", "s", ARG_IN },
+		  END_ARGS
+	  }
+	},
 #endif /* CONFIG_TDLS */
 	{ "VendorElemAdd", WPAS_DBUS_NEW_IFACE_INTERFACE,
 	  (WPADBusMethodHandler) wpas_dbus_handler_vendor_elem_add,
@@ -3119,6 +3271,12 @@
 	  }
 	},
 #endif /* CONFIG_NO_CONFIG_WRITE */
+	{ "AbortScan", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_abort_scan,
+	  {
+		  END_ARGS
+	  }
+	},
 	{ NULL, NULL, NULL, { END_ARGS } }
 };
 
@@ -3325,6 +3483,18 @@
 	  NULL,
 	  NULL
 	},
+#ifdef CONFIG_MESH
+	{ "MeshPeers", WPAS_DBUS_NEW_IFACE_MESH, "aay",
+	  wpas_dbus_getter_mesh_peers,
+	  NULL,
+	  NULL
+	},
+	{ "MeshGroup", WPAS_DBUS_NEW_IFACE_MESH, "ay",
+	  wpas_dbus_getter_mesh_group,
+	  NULL,
+	  NULL
+	},
+#endif /* CONFIG_MESH */
 	{ NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
@@ -3602,6 +3772,32 @@
 		  END_ARGS
 	  }
 	},
+#ifdef CONFIG_MESH
+	{ "MeshGroupStarted", WPAS_DBUS_NEW_IFACE_MESH,
+	  {
+		  { "args", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "MeshGroupRemoved", WPAS_DBUS_NEW_IFACE_MESH,
+	  {
+		  { "args", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "MeshPeerConnected", WPAS_DBUS_NEW_IFACE_MESH,
+	  {
+		  { "args", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "MeshPeerDisconnected", WPAS_DBUS_NEW_IFACE_MESH,
+	  {
+		  { "args", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+#endif /* CONFIG_MESH */
 	{ NULL, NULL, { END_ARGS } }
 };
 
diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h
index bd0e074..e68acb7 100644
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -65,6 +65,8 @@
 #define WPAS_DBUS_NEW_IFACE_P2PDEVICE	\
 		WPAS_DBUS_NEW_IFACE_INTERFACE ".P2PDevice"
 
+#define WPAS_DBUS_NEW_IFACE_MESH WPAS_DBUS_NEW_IFACE_INTERFACE ".Mesh"
+
 /*
  * Groups correspond to P2P groups where this device is a GO (owner)
  */
@@ -239,6 +241,15 @@
 					      const u8 *sa, const u8 *dev_addr,
 					      const u8 *bssid, int id,
 					      int op_freq);
+void wpas_dbus_signal_mesh_group_started(struct wpa_supplicant *wpa_s,
+					 struct wpa_ssid *ssid);
+void wpas_dbus_signal_mesh_group_removed(struct wpa_supplicant *wpa_s,
+					 const u8 *meshid, u8 meshid_len,
+					 int reason);
+void wpas_dbus_signal_mesh_peer_connected(struct wpa_supplicant *wpa_s,
+					  const u8 *peer_addr);
+void wpas_dbus_signal_mesh_peer_disconnected(struct wpa_supplicant *wpa_s,
+					     const u8 *peer_addr, int reason);
 
 #else /* CONFIG_CTRL_IFACE_DBUS_NEW */
 
@@ -554,6 +565,31 @@
 {
 }
 
+static inline
+void wpas_dbus_signal_mesh_group_started(struct wpa_supplicant *wpa_s,
+					 struct wpa_ssid *ssid)
+{
+}
+
+static inline
+void wpas_dbus_signal_mesh_group_removed(struct wpa_supplicant *wpa_s,
+					 const u8 *meshid, u8 meshid_len,
+					 int reason)
+{
+}
+
+static inline
+void wpas_dbus_signal_mesh_peer_connected(struct wpa_supplicant *wpa_s,
+					  const u8 *peer_addr)
+{
+}
+
+static inline
+void wpas_dbus_signal_mesh_peer_disconnected(struct wpa_supplicant *wpa_s,
+					     const u8 *peer_addr, int reason)
+{
+}
+
 #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
 
 #endif /* CTRL_IFACE_DBUS_H_NEW */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index e6f356b..564c868 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -28,6 +28,10 @@
 #include "dbus_dict_helpers.h"
 #include "dbus_common_i.h"
 #include "drivers/driver.h"
+#ifdef CONFIG_MESH
+#include "ap/hostapd.h"
+#include "ap/sta_info.h"
+#endif /* CONFIG_MESH */
 
 static const char * const debug_strings[] = {
 	"excessive", "msgdump", "debug", "info", "warning", "error", NULL
@@ -1076,12 +1080,11 @@
 		}
 
 		if (len != 0) {
-			ssid = os_malloc(len);
+			ssid = os_memdup(val, len);
 			if (ssid == NULL) {
 				*reply = wpas_dbus_error_no_memory(message);
 				return -1;
 			}
-			os_memcpy(ssid, val, len);
 		} else {
 			/* Allow zero-length SSIDs */
 			ssid = NULL;
@@ -1420,6 +1423,27 @@
 }
 
 
+/*
+ * wpas_dbus_handler_abort_scan - Request an ongoing scan to be aborted
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: Abort failed or no scan in progress DBus error message on failure
+ * or NULL otherwise.
+ *
+ * Handler function for "AbortScan" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_abort_scan(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s)
+{
+	if (wpas_abort_ongoing_scan(wpa_s) < 0)
+		return dbus_message_new_error(
+			message, WPAS_DBUS_ERROR_IFACE_SCAN_ERROR,
+			"Abort failed or no scan in progress");
+
+	return NULL;
+}
+
+
 /**
  * wpas_dbus_handler_signal_poll - Request immediate signal properties
  * @message: Pointer to incoming dbus message
@@ -1927,13 +1951,12 @@
 		goto err;
 	}
 
-	blob->data = os_malloc(blob_len);
+	blob->data = os_memdup(blob_data, blob_len);
 	blob->name = os_strdup(blob_name);
 	if (!blob->data || !blob->name) {
 		reply = wpas_dbus_error_no_memory(message);
 		goto err;
 	}
-	os_memcpy(blob->data, blob_data, blob_len);
 	blob->len = blob_len;
 
 	wpa_config_set_blob(wpa_s->conf, blob);
@@ -2294,6 +2317,156 @@
 	return NULL;
 }
 
+/*
+ * wpas_dbus_handler_tdls_channel_switch - Enable channel switching with TDLS peer
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "TDLSChannelSwitch" method call of network interface.
+ */
+DBusMessage *
+wpas_dbus_handler_tdls_channel_switch(DBusMessage *message,
+				      struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter	iter, iter_dict;
+	struct wpa_dbus_dict_entry entry;
+	u8 peer[ETH_ALEN];
+	struct hostapd_freq_params freq_params;
+	u8 oper_class = 0;
+	int ret;
+	int is_peer_present = 0;
+
+	if (!wpa_tdls_is_external_setup(wpa_s->wpa)) {
+		wpa_printf(MSG_INFO,
+			   "tdls_chanswitch: Only supported with external setup");
+		return wpas_dbus_error_unknown_error(message, "TDLS is not using external setup");
+	}
+
+	os_memset(&freq_params, 0, sizeof(freq_params));
+
+	dbus_message_iter_init(message, &iter);
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		return wpas_dbus_error_invalid_args(message, NULL);
+
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			return wpas_dbus_error_invalid_args(message, NULL);
+
+		if (os_strcmp(entry.key, "PeerAddress") == 0 &&
+		    entry.type == DBUS_TYPE_STRING) {
+			if (hwaddr_aton(entry.str_value, peer)) {
+				wpa_printf(MSG_DEBUG,
+					   "tdls_chanswitch: Invalid address '%s'",
+					   entry.str_value);
+				wpa_dbus_dict_entry_clear(&entry);
+				return wpas_dbus_error_invalid_args(message,
+								    NULL);
+			}
+
+			is_peer_present = 1;
+		} else if (os_strcmp(entry.key, "OperClass") == 0 &&
+			   entry.type == DBUS_TYPE_BYTE) {
+			oper_class = entry.byte_value;
+		} else if (os_strcmp(entry.key, "Frequency") == 0 &&
+			   entry.type == DBUS_TYPE_UINT32) {
+			freq_params.freq = entry.uint32_value;
+		} else if (os_strcmp(entry.key, "SecChannelOffset") == 0 &&
+			   entry.type == DBUS_TYPE_UINT32) {
+			freq_params.sec_channel_offset = entry.uint32_value;
+		} else if (os_strcmp(entry.key, "CenterFrequency1") == 0 &&
+			   entry.type == DBUS_TYPE_UINT32) {
+			freq_params.center_freq1 = entry.uint32_value;
+		} else if (os_strcmp(entry.key, "CenterFrequency2") == 0 &&
+			   entry.type == DBUS_TYPE_UINT32) {
+			freq_params.center_freq2 = entry.uint32_value;
+		} else if (os_strcmp(entry.key, "Bandwidth") == 0 &&
+			   entry.type == DBUS_TYPE_UINT32) {
+			freq_params.bandwidth = entry.uint32_value;
+		} else if (os_strcmp(entry.key, "HT") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			freq_params.ht_enabled = entry.bool_value;
+		} else if (os_strcmp(entry.key, "VHT") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			freq_params.vht_enabled = entry.bool_value;
+		} else {
+			wpa_dbus_dict_entry_clear(&entry);
+			return wpas_dbus_error_invalid_args(message, NULL);
+		}
+
+		wpa_dbus_dict_entry_clear(&entry);
+	}
+
+	if (oper_class == 0) {
+		wpa_printf(MSG_INFO,
+			   "tdls_chanswitch: Invalid op class provided");
+		return wpas_dbus_error_invalid_args(
+			message, "Invalid op class provided");
+	}
+
+	if (freq_params.freq == 0) {
+		wpa_printf(MSG_INFO,
+			   "tdls_chanswitch: Invalid freq provided");
+		return wpas_dbus_error_invalid_args(message,
+						    "Invalid freq provided");
+	}
+
+	if (is_peer_present == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "tdls_chanswitch: peer address not provided");
+		return wpas_dbus_error_invalid_args(
+			message, "peer address not provided");
+	}
+
+	wpa_printf(MSG_DEBUG, "dbus: TDLS_CHAN_SWITCH " MACSTR
+		   " OP CLASS %d FREQ %d CENTER1 %d CENTER2 %d BW %d SEC_OFFSET %d%s%s",
+		   MAC2STR(peer), oper_class, freq_params.freq,
+		   freq_params.center_freq1, freq_params.center_freq2,
+		   freq_params.bandwidth, freq_params.sec_channel_offset,
+		   freq_params.ht_enabled ? " HT" : "",
+		   freq_params.vht_enabled ? " VHT" : "");
+
+	ret = wpa_tdls_enable_chan_switch(wpa_s->wpa, peer, oper_class,
+					  &freq_params);
+	if (ret)
+		return wpas_dbus_error_unknown_error(
+			message, "error processing TDLS channel switch");
+
+	return NULL;
+}
+
+/*
+ * wpas_dbus_handler_tdls_cancel_channel_switch - Disable channel switching with TDLS peer
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "TDLSCancelChannelSwitch" method call of network
+ * interface.
+ */
+DBusMessage *
+wpas_dbus_handler_tdls_cancel_channel_switch(DBusMessage *message,
+					     struct wpa_supplicant *wpa_s)
+{
+	u8 peer[ETH_ALEN];
+	DBusMessage *error_reply;
+	int ret;
+
+	if (get_peer_hwaddr_helper(message, __func__, peer, &error_reply) < 0)
+		return error_reply;
+
+	wpa_printf(MSG_DEBUG, "dbus: TDLS_CANCEL_CHAN_SWITCH " MACSTR,
+		   MAC2STR(peer));
+
+	ret = wpa_tdls_disable_chan_switch(wpa_s->wpa, peer);
+	if (ret)
+		return wpas_dbus_error_unknown_error(
+			message, "error canceling TDLS channel switch");
+
+	return NULL;
+}
+
 #endif /* CONFIG_TDLS */
 
 
@@ -2673,6 +2846,11 @@
 	     !wpa_s->conf->p2p_disabled &&
 	     !wpa_dbus_dict_string_array_add_element(
 		     &iter_array, "p2p")) ||
+#ifdef CONFIG_MESH
+	    (res >= 0 && (capa.flags & WPA_DRIVER_FLAGS_MESH) &&
+	     !wpa_dbus_dict_string_array_add_element(
+		     &iter_array, "mesh")) ||
+#endif /* CONFIG_MESH */
 	    !wpa_dbus_dict_end_string_array(&iter_dict,
 					    &iter_dict_entry,
 					    &iter_dict_val,
@@ -4624,3 +4802,100 @@
 	return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
 				      "Not found");
 }
+
+
+#ifdef CONFIG_MESH
+
+/**
+ * wpas_dbus_getter_mesh_peers - Get connected mesh peers
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "MeshPeers" property.
+ */
+dbus_bool_t wpas_dbus_getter_mesh_peers(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	struct hostapd_data *hapd;
+	struct sta_info *sta;
+	DBusMessageIter variant_iter, array_iter;
+	int i;
+	DBusMessageIter inner_array_iter;
+
+	if (!wpa_s->ifmsh)
+		return FALSE;
+	hapd = wpa_s->ifmsh->bss[0];
+
+	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+					      DBUS_TYPE_ARRAY_AS_STRING
+					      DBUS_TYPE_ARRAY_AS_STRING
+					      DBUS_TYPE_BYTE_AS_STRING,
+					      &variant_iter) ||
+	    !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+					      DBUS_TYPE_ARRAY_AS_STRING
+					      DBUS_TYPE_BYTE_AS_STRING,
+					      &array_iter))
+		return FALSE;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (!dbus_message_iter_open_container(
+			    &array_iter, DBUS_TYPE_ARRAY,
+			    DBUS_TYPE_BYTE_AS_STRING,
+			    &inner_array_iter))
+			return FALSE;
+
+		for (i = 0; i < ETH_ALEN; i++) {
+			if (!dbus_message_iter_append_basic(&inner_array_iter,
+							    DBUS_TYPE_BYTE,
+							    &(sta->addr[i])))
+				return FALSE;
+		}
+
+		if (!dbus_message_iter_close_container(
+			    &array_iter, &inner_array_iter))
+			return FALSE;
+	}
+
+	if (!dbus_message_iter_close_container(&variant_iter, &array_iter) ||
+	    !dbus_message_iter_close_container(iter, &variant_iter))
+		return FALSE;
+
+	return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_mesh_group - Get mesh group
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "MeshGroup" property.
+ */
+dbus_bool_t wpas_dbus_getter_mesh_group(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+	if (!wpa_s->ifmsh || !ssid)
+		return FALSE;
+
+	if (!wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+						    (char *) ssid->ssid,
+						    ssid->ssid_len, error)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: error constructing reply", __func__);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+#endif /* CONFIG_MESH */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index 3b8f096..26652ad 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -74,6 +74,9 @@
 DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
 				     struct wpa_supplicant *wpa_s);
 
+DBusMessage * wpas_dbus_handler_abort_scan(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s);
+
 DBusMessage * wpas_dbus_handler_signal_poll(DBusMessage *message,
 					    struct wpa_supplicant *wpa_s);
 
@@ -205,6 +208,9 @@
 DECLARE_ACCESSOR(wpas_dbus_getter_wps_device_device_type);
 DECLARE_ACCESSOR(wpas_dbus_setter_wps_device_device_type);
 
+DECLARE_ACCESSOR(wpas_dbus_getter_mesh_peers);
+DECLARE_ACCESSOR(wpas_dbus_getter_mesh_group);
+
 DBusMessage * wpas_dbus_handler_tdls_discover(DBusMessage *message,
 					      struct wpa_supplicant *wpa_s);
 DBusMessage * wpas_dbus_handler_tdls_setup(DBusMessage *message,
@@ -213,6 +219,12 @@
 					    struct wpa_supplicant *wpa_s);
 DBusMessage * wpas_dbus_handler_tdls_teardown(DBusMessage *message,
 					      struct wpa_supplicant *wpa_s);
+DBusMessage *
+wpas_dbus_handler_tdls_channel_switch(DBusMessage *message,
+				      struct wpa_supplicant *wpa_s);
+DBusMessage *
+wpas_dbus_handler_tdls_cancel_channel_switch(DBusMessage *message,
+					     struct wpa_supplicant *wpa_s);
 
 DBusMessage * wpas_dbus_handler_vendor_elem_add(DBusMessage *message,
 						struct wpa_supplicant *wpa_s);
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index 1e37e27..450023e 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -288,9 +288,6 @@
 # bridge interfaces (commit 'bridge: respect RFC2863 operational state')').
 #CONFIG_NO_LINUX_PACKET_SOCKET_WAR=y
 
-# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
-CONFIG_PEERKEY=y
-
 # IEEE 802.11w (management frame protection), also known as PMF
 # Driver support is also needed for IEEE 802.11w.
 #CONFIG_IEEE80211W=y
@@ -299,6 +296,7 @@
 # openssl = OpenSSL (default)
 # gnutls = GnuTLS
 # internal = Internal TLSv1 implementation (experimental)
+# linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
 # none = Empty template
 #CONFIG_TLS=openssl
 
@@ -316,6 +314,10 @@
 # will be used)
 #CONFIG_TLSV12=y
 
+# Select which ciphers to use by default with OpenSSL if the user does not
+# specify them.
+#CONFIG_TLS_DEFAULT_CIPHERS="DEFAULT:!EXP:!LOW"
+
 # If CONFIG_TLS=internal is used, additional library and include paths are
 # needed for LibTomMath. Alternatively, an integrated, minimal version of
 # LibTomMath can be used. See beginning of libtommath.c for details on benefits
@@ -557,6 +559,8 @@
 # Note: This is an experimental and not yet complete implementation. This
 # should not be enabled for production use.
 #CONFIG_FILS=y
+# FILS shared key authentication with PFS
+#CONFIG_FILS_SK_PFS=y
 
 # Support RSN on IBSS networks
 # This is needed to be able to use mode=1 network profile with proto=RSN and
@@ -580,3 +584,7 @@
 # Learn channels used by the network and try to avoid bgscans on other
 # channels (experimental)
 #CONFIG_BGSCAN_LEARN=y
+
+# Opportunistic Wireless Encryption (OWE)
+# Experimental implementation of draft-harkins-owe-07.txt
+#CONFIG_OWE=y
diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c
new file mode 100644
index 0000000..bf29f19
--- /dev/null
+++ b/wpa_supplicant/dpp_supplicant.c
@@ -0,0 +1,2025 @@
+/*
+ * wpa_supplicant - DPP
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/dpp.h"
+#include "common/gas.h"
+#include "common/gas_server.h"
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "wpa_supplicant_i.h"
+#include "config.h"
+#include "driver_i.h"
+#include "offchannel.h"
+#include "gas_query.h"
+#include "bss.h"
+#include "scan.h"
+#include "notify.h"
+#include "dpp_supplicant.h"
+
+
+static int wpas_dpp_listen_start(struct wpa_supplicant *wpa_s,
+				 unsigned int freq);
+static void wpas_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx);
+static void wpas_dpp_auth_success(struct wpa_supplicant *wpa_s, int initiator);
+static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s,
+			       unsigned int freq, const u8 *dst,
+			       const u8 *src, const u8 *bssid,
+			       const u8 *data, size_t data_len,
+			       enum offchannel_send_action_result result);
+
+static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+/* Use a hardcoded Transaction ID 1 in Peer Discovery frames since there is only
+ * a single transaction in progress at any point in time. */
+static const u8 TRANSACTION_ID = 1;
+
+
+static struct dpp_configurator *
+dpp_configurator_get_id(struct wpa_supplicant *wpa_s, unsigned int id)
+{
+	struct dpp_configurator *conf;
+
+	dl_list_for_each(conf, &wpa_s->dpp_configurator,
+			 struct dpp_configurator, list) {
+		if (conf->id == id)
+			return conf;
+	}
+	return NULL;
+}
+
+
+static unsigned int wpas_dpp_next_id(struct wpa_supplicant *wpa_s)
+{
+	struct dpp_bootstrap_info *bi;
+	unsigned int max_id = 0;
+
+	dl_list_for_each(bi, &wpa_s->dpp_bootstrap, struct dpp_bootstrap_info,
+			 list) {
+		if (bi->id > max_id)
+			max_id = bi->id;
+	}
+	return max_id + 1;
+}
+
+
+/**
+ * wpas_dpp_qr_code - Parse and add DPP bootstrapping info from a QR Code
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @cmd: DPP URI read from a QR Code
+ * Returns: Identifier of the stored info or -1 on failure
+ */
+int wpas_dpp_qr_code(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	struct dpp_bootstrap_info *bi;
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+	bi = dpp_parse_qr_code(cmd);
+	if (!bi)
+		return -1;
+
+	bi->id = wpas_dpp_next_id(wpa_s);
+	dl_list_add(&wpa_s->dpp_bootstrap, &bi->list);
+
+	if (auth && auth->response_pending &&
+	    dpp_notify_new_qr_code(auth, bi) == 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Sending out pending authentication response");
+		offchannel_send_action(wpa_s, auth->curr_freq,
+				       auth->peer_mac_addr, wpa_s->own_addr,
+				       broadcast,
+				       wpabuf_head(auth->resp_msg),
+				       wpabuf_len(auth->resp_msg),
+				       500, wpas_dpp_tx_status, 0);
+	}
+
+	return bi->id;
+}
+
+
+static char * get_param(const char *cmd, const char *param)
+{
+	const char *pos, *end;
+	char *val;
+	size_t len;
+
+	pos = os_strstr(cmd, param);
+	if (!pos)
+		return NULL;
+
+	pos += os_strlen(param);
+	end = os_strchr(pos, ' ');
+	if (end)
+		len = end - pos;
+	else
+		len = os_strlen(pos);
+	val = os_malloc(len + 1);
+	if (!val)
+		return NULL;
+	os_memcpy(val, pos, len);
+	val[len] = '\0';
+	return val;
+}
+
+
+int wpas_dpp_bootstrap_gen(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	char *chan = NULL, *mac = NULL, *info = NULL, *pk = NULL, *curve = NULL;
+	char *key = NULL;
+	u8 *privkey = NULL;
+	size_t privkey_len = 0;
+	size_t len;
+	int ret = -1;
+	struct dpp_bootstrap_info *bi;
+
+	bi = os_zalloc(sizeof(*bi));
+	if (!bi)
+		goto fail;
+
+	if (os_strstr(cmd, "type=qrcode"))
+		bi->type = DPP_BOOTSTRAP_QR_CODE;
+	else if (os_strstr(cmd, "type=pkex"))
+		bi->type = DPP_BOOTSTRAP_PKEX;
+	else
+		goto fail;
+
+	chan = get_param(cmd, " chan=");
+	mac = get_param(cmd, " mac=");
+	info = get_param(cmd, " info=");
+	curve = get_param(cmd, " curve=");
+	key = get_param(cmd, " key=");
+
+	if (key) {
+		privkey_len = os_strlen(key) / 2;
+		privkey = os_malloc(privkey_len);
+		if (!privkey ||
+		    hexstr2bin(key, privkey, privkey_len) < 0)
+			goto fail;
+	}
+
+	pk = dpp_keygen(bi, curve, privkey, privkey_len);
+	if (!pk)
+		goto fail;
+
+	len = 4; /* "DPP:" */
+	if (chan) {
+		if (dpp_parse_uri_chan_list(bi, chan) < 0)
+			goto fail;
+		len += 3 + os_strlen(chan); /* C:...; */
+	}
+	if (mac) {
+		if (dpp_parse_uri_mac(bi, mac) < 0)
+			goto fail;
+		len += 3 + os_strlen(mac); /* M:...; */
+	}
+	if (info) {
+		if (dpp_parse_uri_info(bi, info) < 0)
+			goto fail;
+		len += 3 + os_strlen(info); /* I:...; */
+	}
+	len += 4 + os_strlen(pk);
+	bi->uri = os_malloc(len + 1);
+	if (!bi->uri)
+		goto fail;
+	os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%sK:%s;;",
+		    chan ? "C:" : "", chan ? chan : "", chan ? ";" : "",
+		    mac ? "M:" : "", mac ? mac : "", mac ? ";" : "",
+		    info ? "I:" : "", info ? info : "", info ? ";" : "",
+		    pk);
+	bi->id = wpas_dpp_next_id(wpa_s);
+	dl_list_add(&wpa_s->dpp_bootstrap, &bi->list);
+	ret = bi->id;
+	bi = NULL;
+fail:
+	os_free(curve);
+	os_free(pk);
+	os_free(chan);
+	os_free(mac);
+	os_free(info);
+	str_clear_free(key);
+	bin_clear_free(privkey, privkey_len);
+	dpp_bootstrap_info_free(bi);
+	return ret;
+}
+
+
+static struct dpp_bootstrap_info *
+dpp_bootstrap_get_id(struct wpa_supplicant *wpa_s, unsigned int id)
+{
+	struct dpp_bootstrap_info *bi;
+
+	dl_list_for_each(bi, &wpa_s->dpp_bootstrap, struct dpp_bootstrap_info,
+			 list) {
+		if (bi->id == id)
+			return bi;
+	}
+	return NULL;
+}
+
+
+static int dpp_bootstrap_del(struct wpa_supplicant *wpa_s, unsigned int id)
+{
+	struct dpp_bootstrap_info *bi, *tmp;
+	int found = 0;
+
+	dl_list_for_each_safe(bi, tmp, &wpa_s->dpp_bootstrap,
+			      struct dpp_bootstrap_info, list) {
+		if (id && bi->id != id)
+			continue;
+		found = 1;
+		dl_list_del(&bi->list);
+		dpp_bootstrap_info_free(bi);
+	}
+
+	if (id == 0)
+		return 0; /* flush succeeds regardless of entries found */
+	return found ? 0 : -1;
+}
+
+
+int wpas_dpp_bootstrap_remove(struct wpa_supplicant *wpa_s, const char *id)
+{
+	unsigned int id_val;
+
+	if (os_strcmp(id, "*") == 0) {
+		id_val = 0;
+	} else {
+		id_val = atoi(id);
+		if (id_val == 0)
+			return -1;
+	}
+
+	return dpp_bootstrap_del(wpa_s, id_val);
+}
+
+
+const char * wpas_dpp_bootstrap_get_uri(struct wpa_supplicant *wpa_s,
+					unsigned int id)
+{
+	struct dpp_bootstrap_info *bi;
+
+	bi = dpp_bootstrap_get_id(wpa_s, id);
+	if (!bi)
+		return NULL;
+	return bi->uri;
+}
+
+
+int wpas_dpp_bootstrap_info(struct wpa_supplicant *wpa_s, int id,
+			    char *reply, int reply_size)
+{
+	struct dpp_bootstrap_info *bi;
+
+	bi = dpp_bootstrap_get_id(wpa_s, id);
+	if (!bi)
+		return -1;
+	return os_snprintf(reply, reply_size, "type=%s\n"
+			   "mac_addr=" MACSTR "\n"
+			   "info=%s\n"
+			   "num_freq=%u\n"
+			   "curve=%s\n",
+			   dpp_bootstrap_type_txt(bi->type),
+			   MAC2STR(bi->mac_addr),
+			   bi->info ? bi->info : "",
+			   bi->num_freq,
+			   bi->curve->name);
+}
+
+
+static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s,
+			       unsigned int freq, const u8 *dst,
+			       const u8 *src, const u8 *bssid,
+			       const u8 *data, size_t data_len,
+			       enum offchannel_send_action_result result)
+{
+	wpa_printf(MSG_DEBUG, "DPP: TX status: freq=%u dst=" MACSTR
+		   " result=%s",
+		   freq, MAC2STR(dst),
+		   result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" :
+		   (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" :
+		    "FAILED"));
+
+	if (!wpa_s->dpp_auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore TX status since there is no ongoing authentication exchange");
+		return;
+	}
+
+	if (wpa_s->dpp_auth->remove_on_tx_status) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Terminate authentication exchange due to an earlier error");
+		eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
+		offchannel_send_action_done(wpa_s);
+		dpp_auth_deinit(wpa_s->dpp_auth);
+		wpa_s->dpp_auth = NULL;
+		return;
+	}
+
+	if (wpa_s->dpp_auth_ok_on_ack)
+		wpas_dpp_auth_success(wpa_s, 1);
+
+	if (!is_broadcast_ether_addr(dst) &&
+	    result != OFFCHANNEL_SEND_ACTION_SUCCESS) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unicast DPP Action frame was not ACKed");
+		/* TODO: In case of DPP Authentication Request frame, move to
+		 * the next channel immediately */
+	}
+}
+
+
+static void wpas_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	if (!wpa_s->dpp_auth)
+		return;
+	wpa_printf(MSG_DEBUG, "DPP: Continue reply wait on channel %u MHz",
+		   wpa_s->dpp_auth->curr_freq);
+	wpas_dpp_listen_start(wpa_s, wpa_s->dpp_auth->curr_freq);
+}
+
+
+static void wpas_dpp_set_testing_options(struct wpa_supplicant *wpa_s,
+					 struct dpp_authentication *auth)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	if (wpa_s->dpp_config_obj_override)
+		auth->config_obj_override =
+			os_strdup(wpa_s->dpp_config_obj_override);
+	if (wpa_s->dpp_discovery_override)
+		auth->discovery_override =
+			os_strdup(wpa_s->dpp_discovery_override);
+	if (wpa_s->dpp_groups_override)
+		auth->groups_override =
+			os_strdup(wpa_s->dpp_groups_override);
+	auth->ignore_netaccesskey_mismatch =
+		wpa_s->dpp_ignore_netaccesskey_mismatch;
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
+static void wpas_dpp_set_configurator(struct wpa_supplicant *wpa_s,
+				      struct dpp_authentication *auth,
+				      const char *cmd)
+{
+	const char *pos, *end;
+	struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL;
+	struct dpp_configurator *conf = NULL;
+	u8 ssid[32] = { "test" };
+	size_t ssid_len = 4;
+	char pass[64] = { };
+	size_t pass_len = 0;
+	u8 psk[PMK_LEN];
+	int psk_set = 0;
+
+	if (!cmd)
+		return;
+
+	wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd);
+	pos = os_strstr(cmd, " ssid=");
+	if (pos) {
+		pos += 6;
+		end = os_strchr(pos, ' ');
+		ssid_len = end ? (size_t) (end - pos) : os_strlen(pos);
+		ssid_len /= 2;
+		if (ssid_len > sizeof(ssid) ||
+		    hexstr2bin(pos, ssid, ssid_len) < 0)
+			goto fail;
+	}
+
+	pos = os_strstr(cmd, " pass=");
+	if (pos) {
+		pos += 6;
+		end = os_strchr(pos, ' ');
+		pass_len = end ? (size_t) (end - pos) : os_strlen(pos);
+		pass_len /= 2;
+		if (pass_len > sizeof(pass) - 1 || pass_len < 8 ||
+		    hexstr2bin(pos, (u8 *) pass, pass_len) < 0)
+			goto fail;
+	}
+
+	pos = os_strstr(cmd, " psk=");
+	if (pos) {
+		pos += 5;
+		if (hexstr2bin(pos, psk, PMK_LEN) < 0)
+			goto fail;
+		psk_set = 1;
+	}
+
+	if (os_strstr(cmd, " conf=sta-")) {
+		conf_sta = os_zalloc(sizeof(struct dpp_configuration));
+		if (!conf_sta)
+			goto fail;
+		os_memcpy(conf_sta->ssid, ssid, ssid_len);
+		conf_sta->ssid_len = ssid_len;
+		if (os_strstr(cmd, " conf=sta-psk")) {
+			conf_sta->dpp = 0;
+			if (psk_set) {
+				os_memcpy(conf_sta->psk, psk, PMK_LEN);
+			} else {
+				conf_sta->passphrase = os_strdup(pass);
+				if (!conf_sta->passphrase)
+					goto fail;
+			}
+		} else if (os_strstr(cmd, " conf=sta-dpp")) {
+			conf_sta->dpp = 1;
+		} else {
+			goto fail;
+		}
+	}
+
+	if (os_strstr(cmd, " conf=ap-")) {
+		conf_ap = os_zalloc(sizeof(struct dpp_configuration));
+		if (!conf_ap)
+			goto fail;
+		os_memcpy(conf_ap->ssid, ssid, ssid_len);
+		conf_ap->ssid_len = ssid_len;
+		if (os_strstr(cmd, " conf=ap-psk")) {
+			conf_ap->dpp = 0;
+			if (psk_set) {
+				os_memcpy(conf_ap->psk, psk, PMK_LEN);
+			} else {
+				conf_ap->passphrase = os_strdup(pass);
+				if (!conf_ap->passphrase)
+					goto fail;
+			}
+		} else if (os_strstr(cmd, " conf=ap-dpp")) {
+			conf_ap->dpp = 1;
+		} else {
+			goto fail;
+		}
+	}
+
+	pos = os_strstr(cmd, " expiry=");
+	if (pos) {
+		long int val;
+
+		pos += 8;
+		val = strtol(pos, NULL, 0);
+		if (val <= 0)
+			goto fail;
+		if (conf_sta)
+			conf_sta->netaccesskey_expiry = val;
+		if (conf_ap)
+			conf_ap->netaccesskey_expiry = val;
+	}
+
+	pos = os_strstr(cmd, " configurator=");
+	if (pos) {
+		pos += 14;
+		conf = dpp_configurator_get_id(wpa_s, atoi(pos));
+		if (!conf) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Could not find the specified configurator");
+			goto fail;
+		}
+	}
+	auth->conf_sta = conf_sta;
+	auth->conf_ap = conf_ap;
+	auth->conf = conf;
+	return;
+
+fail:
+	wpa_printf(MSG_DEBUG, "DPP: Failed to set configurator parameters");
+	dpp_configuration_free(conf_sta);
+	dpp_configuration_free(conf_ap);
+}
+
+
+int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	const char *pos;
+	struct dpp_bootstrap_info *peer_bi, *own_bi = NULL;
+	const u8 *dst;
+	int res;
+	int configurator = 1;
+	unsigned int wait_time;
+
+	wpa_s->dpp_gas_client = 0;
+
+	pos = os_strstr(cmd, " peer=");
+	if (!pos)
+		return -1;
+	pos += 6;
+	peer_bi = dpp_bootstrap_get_id(wpa_s, atoi(pos));
+	if (!peer_bi) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Could not find bootstrapping info for the identified peer");
+		return -1;
+	}
+
+	pos = os_strstr(cmd, " own=");
+	if (pos) {
+		pos += 5;
+		own_bi = dpp_bootstrap_get_id(wpa_s, atoi(pos));
+		if (!own_bi) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Could not find bootstrapping info for the identified local entry");
+			return -1;
+		}
+
+		if (peer_bi->curve != own_bi->curve) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Mismatching curves in bootstrapping info (peer=%s own=%s)",
+				   peer_bi->curve->name, own_bi->curve->name);
+			return -1;
+		}
+	}
+
+	pos = os_strstr(cmd, " role=");
+	if (pos) {
+		pos += 6;
+		if (os_strncmp(pos, "configurator", 12) == 0)
+			configurator = 1;
+		else if (os_strncmp(pos, "enrollee", 8) == 0)
+			configurator = 0;
+		else
+			goto fail;
+	}
+
+	pos = os_strstr(cmd, " netrole=");
+	if (pos) {
+		pos += 9;
+		wpa_s->dpp_netrole_ap = os_strncmp(pos, "ap", 2) == 0;
+	}
+
+	if (wpa_s->dpp_auth) {
+		eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
+		offchannel_send_action_done(wpa_s);
+		dpp_auth_deinit(wpa_s->dpp_auth);
+	}
+	wpa_s->dpp_auth = dpp_auth_init(wpa_s, peer_bi, own_bi, configurator);
+	if (!wpa_s->dpp_auth)
+		goto fail;
+	wpas_dpp_set_testing_options(wpa_s, wpa_s->dpp_auth);
+	wpas_dpp_set_configurator(wpa_s, wpa_s->dpp_auth, cmd);
+
+	/* TODO: Support iteration over all frequencies and filtering of
+	 * frequencies based on locally enabled channels that allow initiation
+	 * of transmission. */
+	if (peer_bi->num_freq > 0)
+		wpa_s->dpp_auth->curr_freq = peer_bi->freq[0];
+	else
+		wpa_s->dpp_auth->curr_freq = 2412;
+
+	if (is_zero_ether_addr(peer_bi->mac_addr)) {
+		dst = broadcast;
+	} else {
+		dst = peer_bi->mac_addr;
+		os_memcpy(wpa_s->dpp_auth->peer_mac_addr, peer_bi->mac_addr,
+			  ETH_ALEN);
+	}
+	wpa_s->dpp_auth_ok_on_ack = 0;
+	eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
+	wait_time = wpa_s->max_remain_on_chan;
+	if (wait_time > 2000)
+		wait_time = 2000;
+	eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
+			       wpas_dpp_reply_wait_timeout,
+			       wpa_s, NULL);
+	res = offchannel_send_action(wpa_s, wpa_s->dpp_auth->curr_freq,
+				     dst, wpa_s->own_addr, broadcast,
+				     wpabuf_head(wpa_s->dpp_auth->req_msg),
+				     wpabuf_len(wpa_s->dpp_auth->req_msg),
+				     wait_time, wpas_dpp_tx_status, 0);
+
+	return res;
+fail:
+	return -1;
+}
+
+
+struct wpas_dpp_listen_work {
+	unsigned int freq;
+	unsigned int duration;
+	struct wpabuf *probe_resp_ie;
+};
+
+
+static void wpas_dpp_listen_work_free(struct wpas_dpp_listen_work *lwork)
+{
+	if (!lwork)
+		return;
+	os_free(lwork);
+}
+
+
+static void wpas_dpp_listen_work_done(struct wpa_supplicant *wpa_s)
+{
+	struct wpas_dpp_listen_work *lwork;
+
+	if (!wpa_s->dpp_listen_work)
+		return;
+
+	lwork = wpa_s->dpp_listen_work->ctx;
+	wpas_dpp_listen_work_free(lwork);
+	radio_work_done(wpa_s->dpp_listen_work);
+	wpa_s->dpp_listen_work = NULL;
+}
+
+
+static void dpp_start_listen_cb(struct wpa_radio_work *work, int deinit)
+{
+	struct wpa_supplicant *wpa_s = work->wpa_s;
+	struct wpas_dpp_listen_work *lwork = work->ctx;
+
+	if (deinit) {
+		if (work->started) {
+			wpa_s->dpp_listen_work = NULL;
+			wpas_dpp_listen_stop(wpa_s);
+		}
+		wpas_dpp_listen_work_free(lwork);
+		return;
+	}
+
+	wpa_s->dpp_listen_work = work;
+
+	wpa_s->dpp_pending_listen_freq = lwork->freq;
+
+	if (wpa_drv_remain_on_channel(wpa_s, lwork->freq,
+				      wpa_s->max_remain_on_chan) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to request the driver to remain on channel (%u MHz) for listen",
+			   lwork->freq);
+		wpas_dpp_listen_work_done(wpa_s);
+		wpa_s->dpp_pending_listen_freq = 0;
+		return;
+	}
+	wpa_s->off_channel_freq = 0;
+	wpa_s->roc_waiting_drv_freq = lwork->freq;
+}
+
+
+static int wpas_dpp_listen_start(struct wpa_supplicant *wpa_s,
+				 unsigned int freq)
+{
+	struct wpas_dpp_listen_work *lwork;
+
+	if (wpa_s->dpp_listen_work) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Reject start_listen since dpp_listen_work already exists");
+		return -1;
+	}
+
+	if (wpa_s->dpp_listen_freq)
+		wpas_dpp_listen_stop(wpa_s);
+	wpa_s->dpp_listen_freq = freq;
+
+	lwork = os_zalloc(sizeof(*lwork));
+	if (!lwork)
+		return -1;
+	lwork->freq = freq;
+
+	if (radio_add_work(wpa_s, freq, "dpp-listen", 0, dpp_start_listen_cb,
+			   lwork) < 0) {
+		wpas_dpp_listen_work_free(lwork);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int wpas_dpp_listen(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	int freq;
+
+	freq = atoi(cmd);
+	if (freq <= 0)
+		return -1;
+
+	if (os_strstr(cmd, " role=configurator"))
+		wpa_s->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR;
+	else if (os_strstr(cmd, " role=enrollee"))
+		wpa_s->dpp_allowed_roles = DPP_CAPAB_ENROLLEE;
+	else
+		wpa_s->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR |
+			DPP_CAPAB_ENROLLEE;
+	wpa_s->dpp_qr_mutual = os_strstr(cmd, " qr=mutual") != NULL;
+	wpa_s->dpp_netrole_ap = os_strstr(cmd, " netrole=ap") != NULL;
+	if (wpa_s->dpp_listen_freq == (unsigned int) freq) {
+		wpa_printf(MSG_DEBUG, "DPP: Already listening on %u MHz",
+			   freq);
+		return 0;
+	}
+
+	return wpas_dpp_listen_start(wpa_s, freq);
+}
+
+
+void wpas_dpp_listen_stop(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->dpp_listen_freq)
+		return;
+
+	wpa_printf(MSG_DEBUG, "DPP: Stop listen on %u MHz",
+		   wpa_s->dpp_listen_freq);
+	wpa_drv_cancel_remain_on_channel(wpa_s);
+	wpa_s->dpp_listen_freq = 0;
+	wpas_dpp_listen_work_done(wpa_s);
+}
+
+
+void wpas_dpp_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+				   unsigned int freq)
+{
+	if (!wpa_s->dpp_listen_freq && !wpa_s->dpp_pending_listen_freq)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: remain-on-channel callback (off_channel_freq=%u dpp_pending_listen_freq=%d roc_waiting_drv_freq=%d freq=%u)",
+		   wpa_s->off_channel_freq, wpa_s->dpp_pending_listen_freq,
+		   wpa_s->roc_waiting_drv_freq, freq);
+	if (wpa_s->off_channel_freq &&
+	    wpa_s->off_channel_freq == wpa_s->dpp_pending_listen_freq) {
+		wpa_printf(MSG_DEBUG, "DPP: Listen on %u MHz started", freq);
+		wpa_s->dpp_pending_listen_freq = 0;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore remain-on-channel callback (off_channel_freq=%u dpp_pending_listen_freq=%d freq=%u)",
+			   wpa_s->off_channel_freq,
+			   wpa_s->dpp_pending_listen_freq, freq);
+	}
+}
+
+
+void wpas_dpp_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+					  unsigned int freq)
+{
+	wpas_dpp_listen_work_done(wpa_s);
+
+	if (wpa_s->dpp_auth && !wpa_s->dpp_gas_client) {
+		/* Continue listen with a new remain-on-channel */
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Continue wait on %u MHz for the ongoing DPP provisioning session",
+			   wpa_s->dpp_auth->curr_freq);
+		wpas_dpp_listen_start(wpa_s, wpa_s->dpp_auth->curr_freq);
+		return;
+	}
+
+	if (wpa_s->dpp_listen_freq) {
+		/* Continue listen with a new remain-on-channel */
+		wpas_dpp_listen_start(wpa_s, wpa_s->dpp_listen_freq);
+	}
+}
+
+
+static void wpas_dpp_rx_auth_req(struct wpa_supplicant *wpa_s, const u8 *src,
+				 const u8 *hdr, const u8 *buf, size_t len,
+				 unsigned int freq)
+{
+	const u8 *r_bootstrap, *i_bootstrap, *wrapped_data;
+	u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len;
+	struct dpp_bootstrap_info *bi, *own_bi = NULL, *peer_bi = NULL;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR,
+		   MAC2STR(src));
+
+	wrapped_data = dpp_get_attr(buf, len, DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing required Wrapped data attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped data",
+		    wrapped_data, wrapped_data_len);
+
+	r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+				   &r_bootstrap_len);
+	if (!r_bootstrap || r_bootstrap > wrapped_data ||
+	    r_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+		    r_bootstrap, r_bootstrap_len);
+
+	i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+				   &i_bootstrap_len);
+	if (!i_bootstrap || i_bootstrap > wrapped_data ||
+	    i_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Initiator Bootstrapping Key Hash attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash",
+		    i_bootstrap, i_bootstrap_len);
+
+	/* Try to find own and peer bootstrapping key matches based on the
+	 * received hash values */
+	dl_list_for_each(bi, &wpa_s->dpp_bootstrap, struct dpp_bootstrap_info,
+			 list) {
+		if (!own_bi && bi->own &&
+		    os_memcmp(bi->pubkey_hash, r_bootstrap,
+			      SHA256_MAC_LEN) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Found matching own bootstrapping information");
+			own_bi = bi;
+		}
+
+		if (!peer_bi && !bi->own &&
+		    os_memcmp(bi->pubkey_hash, i_bootstrap,
+			      SHA256_MAC_LEN) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Found matching peer bootstrapping information");
+			peer_bi = bi;
+		}
+
+		if (own_bi && peer_bi)
+			break;
+	}
+
+	if (!own_bi) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No matching own bootstrapping key found - ignore message");
+		return;
+	}
+
+	if (wpa_s->dpp_auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Already in DPP authentication exchange - ignore new one");
+		return;
+	}
+
+	wpa_s->dpp_gas_client = 0;
+	wpa_s->dpp_auth_ok_on_ack = 0;
+	wpa_s->dpp_auth = dpp_auth_req_rx(wpa_s, wpa_s->dpp_allowed_roles,
+					  wpa_s->dpp_qr_mutual,
+					  peer_bi, own_bi, freq, hdr, buf,
+					  wrapped_data, wrapped_data_len);
+	if (!wpa_s->dpp_auth) {
+		wpa_printf(MSG_DEBUG, "DPP: No response generated");
+		return;
+	}
+	wpas_dpp_set_testing_options(wpa_s, wpa_s->dpp_auth);
+	wpas_dpp_set_configurator(wpa_s, wpa_s->dpp_auth,
+				  wpa_s->dpp_configurator_params);
+	os_memcpy(wpa_s->dpp_auth->peer_mac_addr, src, ETH_ALEN);
+
+	offchannel_send_action(wpa_s, wpa_s->dpp_auth->curr_freq,
+			       src, wpa_s->own_addr, broadcast,
+			       wpabuf_head(wpa_s->dpp_auth->resp_msg),
+			       wpabuf_len(wpa_s->dpp_auth->resp_msg),
+			       500, wpas_dpp_tx_status, 0);
+}
+
+
+static void wpas_dpp_start_gas_server(struct wpa_supplicant *wpa_s)
+{
+	/* TODO: stop wait and start ROC */
+}
+
+
+static struct wpa_ssid * wpas_dpp_add_network(struct wpa_supplicant *wpa_s,
+					      struct dpp_authentication *auth)
+{
+	struct wpa_ssid *ssid;
+
+	ssid = wpa_config_add_network(wpa_s->conf);
+	if (!ssid)
+		return NULL;
+	wpas_notify_network_added(wpa_s, ssid);
+	wpa_config_set_network_defaults(ssid);
+	ssid->disabled = 1;
+
+	ssid->ssid = os_malloc(auth->ssid_len);
+	if (!ssid->ssid)
+		goto fail;
+	os_memcpy(ssid->ssid, auth->ssid, auth->ssid_len);
+	ssid->ssid_len = auth->ssid_len;
+
+	if (auth->connector) {
+		ssid->key_mgmt = WPA_KEY_MGMT_DPP;
+		ssid->dpp_connector = os_strdup(auth->connector);
+		if (!ssid->dpp_connector)
+			goto fail;
+	}
+
+	if (auth->c_sign_key) {
+		ssid->dpp_csign = os_malloc(wpabuf_len(auth->c_sign_key));
+		if (!ssid->dpp_csign)
+			goto fail;
+		os_memcpy(ssid->dpp_csign, wpabuf_head(auth->c_sign_key),
+			  wpabuf_len(auth->c_sign_key));
+		ssid->dpp_csign_len = wpabuf_len(auth->c_sign_key);
+	}
+
+	if (auth->net_access_key) {
+		ssid->dpp_netaccesskey =
+			os_malloc(wpabuf_len(auth->net_access_key));
+		if (!ssid->dpp_netaccesskey)
+			goto fail;
+		os_memcpy(ssid->dpp_netaccesskey,
+			  wpabuf_head(auth->net_access_key),
+			  wpabuf_len(auth->net_access_key));
+		ssid->dpp_netaccesskey_len = wpabuf_len(auth->net_access_key);
+		ssid->dpp_netaccesskey_expiry = auth->net_access_key_expiry;
+	}
+
+	if (!auth->connector) {
+		ssid->key_mgmt = WPA_KEY_MGMT_PSK;
+		if (auth->passphrase[0]) {
+			if (wpa_config_set_quoted(ssid, "psk",
+						  auth->passphrase) < 0)
+				goto fail;
+			wpa_config_update_psk(ssid);
+			ssid->export_keys = 1;
+		} else {
+			ssid->psk_set = auth->psk_set;
+			os_memcpy(ssid->psk, auth->psk, PMK_LEN);
+		}
+	}
+
+	return ssid;
+fail:
+	wpas_notify_network_removed(wpa_s, ssid);
+	wpa_config_remove_network(wpa_s->conf, ssid->id);
+	return NULL;
+}
+
+
+static void wpas_dpp_process_config(struct wpa_supplicant *wpa_s,
+				    struct dpp_authentication *auth)
+{
+	struct wpa_ssid *ssid;
+
+	if (wpa_s->conf->dpp_config_processing < 1)
+		return;
+
+	ssid = wpas_dpp_add_network(wpa_s, auth);
+	if (!ssid)
+		return;
+
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_NETWORK_ID "%d", ssid->id);
+	if (wpa_s->conf->dpp_config_processing < 2)
+		return;
+
+	wpa_printf(MSG_DEBUG, "DPP: Trying to connect to the new network");
+	ssid->disabled = 0;
+	wpa_s->disconnected = 0;
+	wpa_s->reassociate = 1;
+	wpa_s->scan_runs = 0;
+	wpa_s->normal_scans = 0;
+	wpa_supplicant_cancel_sched_scan(wpa_s);
+	wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static void wpas_dpp_handle_config_obj(struct wpa_supplicant *wpa_s,
+				       struct dpp_authentication *auth)
+{
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_RECEIVED);
+	if (auth->ssid_len)
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONFOBJ_SSID "%s",
+			wpa_ssid_txt(auth->ssid, auth->ssid_len));
+	if (auth->connector) {
+		/* TODO: Save the Connector and consider using a command
+		 * to fetch the value instead of sending an event with
+		 * it. The Connector could end up being larger than what
+		 * most clients are ready to receive as an event
+		 * message. */
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONNECTOR "%s",
+			auth->connector);
+	}
+	if (auth->c_sign_key) {
+		char *hex;
+		size_t hexlen;
+
+		hexlen = 2 * wpabuf_len(auth->c_sign_key) + 1;
+		hex = os_malloc(hexlen);
+		if (hex) {
+			wpa_snprintf_hex(hex, hexlen,
+					 wpabuf_head(auth->c_sign_key),
+					 wpabuf_len(auth->c_sign_key));
+			wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_C_SIGN_KEY "%s",
+				hex);
+			os_free(hex);
+		}
+	}
+	if (auth->net_access_key) {
+		char *hex;
+		size_t hexlen;
+
+		hexlen = 2 * wpabuf_len(auth->net_access_key) + 1;
+		hex = os_malloc(hexlen);
+		if (hex) {
+			wpa_snprintf_hex(hex, hexlen,
+					 wpabuf_head(auth->net_access_key),
+					 wpabuf_len(auth->net_access_key));
+			if (auth->net_access_key_expiry)
+				wpa_msg(wpa_s, MSG_INFO,
+					DPP_EVENT_NET_ACCESS_KEY "%s %lu", hex,
+					(long unsigned)
+					auth->net_access_key_expiry);
+			else
+				wpa_msg(wpa_s, MSG_INFO,
+					DPP_EVENT_NET_ACCESS_KEY "%s", hex);
+			os_free(hex);
+		}
+	}
+
+	wpas_dpp_process_config(wpa_s, auth);
+}
+
+
+static void wpas_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
+				 enum gas_query_result result,
+				 const struct wpabuf *adv_proto,
+				 const struct wpabuf *resp, u16 status_code)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	const u8 *pos;
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+	if (!auth || !auth->auth_success) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+		return;
+	}
+	if (!resp || status_code != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "DPP: GAS query did not succeed");
+		goto fail;
+	}
+
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response adv_proto",
+			adv_proto);
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response (GAS response)",
+			resp);
+
+	if (wpabuf_len(adv_proto) != 10 ||
+	    !(pos = wpabuf_head(adv_proto)) ||
+	    pos[0] != WLAN_EID_ADV_PROTO ||
+	    pos[1] != 8 ||
+	    pos[3] != WLAN_EID_VENDOR_SPECIFIC ||
+	    pos[4] != 5 ||
+	    WPA_GET_BE24(&pos[5]) != OUI_WFA ||
+	    pos[8] != 0x1a ||
+	    pos[9] != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Not a DPP Advertisement Protocol ID");
+		goto fail;
+	}
+
+	if (dpp_conf_resp_rx(auth, resp) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
+		goto fail;
+	}
+
+	wpas_dpp_handle_config_obj(wpa_s, auth);
+	dpp_auth_deinit(wpa_s->dpp_auth);
+	wpa_s->dpp_auth = NULL;
+	return;
+
+fail:
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED);
+	dpp_auth_deinit(wpa_s->dpp_auth);
+	wpa_s->dpp_auth = NULL;
+}
+
+
+static void wpas_dpp_start_gas_client(struct wpa_supplicant *wpa_s)
+{
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+	struct wpabuf *buf, *conf_req;
+	char json[100];
+	int res;
+
+	wpa_s->dpp_gas_client = 1;
+	os_snprintf(json, sizeof(json),
+		    "{\"name\":\"Test\","
+		    "\"wi-fi_tech\":\"infra\","
+		    "\"netRole\":\"%s\"}",
+		    wpa_s->dpp_netrole_ap ? "ap" : "sta");
+	wpa_printf(MSG_DEBUG, "DPP: GAS Config Attributes: %s", json);
+
+	offchannel_send_action_done(wpa_s);
+	wpas_dpp_listen_stop(wpa_s);
+
+	conf_req = dpp_build_conf_req(auth, json);
+	if (!conf_req) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No configuration request data available");
+		return;
+	}
+
+	buf = gas_build_initial_req(0, 10 + 2 + wpabuf_len(conf_req));
+	if (!buf) {
+		wpabuf_free(conf_req);
+		return;
+	}
+
+	/* Advertisement Protocol IE */
+	wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
+	wpabuf_put_u8(buf, 8); /* Length */
+	wpabuf_put_u8(buf, 0x7f);
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+	wpabuf_put_u8(buf, 5);
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, DPP_OUI_TYPE);
+	wpabuf_put_u8(buf, 0x01);
+
+	/* GAS Query */
+	wpabuf_put_le16(buf, wpabuf_len(conf_req));
+	wpabuf_put_buf(buf, conf_req);
+	wpabuf_free(conf_req);
+
+	wpa_printf(MSG_DEBUG, "DPP: GAS request to " MACSTR " (freq %u MHz)",
+		   MAC2STR(auth->peer_mac_addr), auth->curr_freq);
+
+	res = gas_query_req(wpa_s->gas, auth->peer_mac_addr, auth->curr_freq,
+			    buf, wpas_dpp_gas_resp_cb, wpa_s);
+	if (res < 0) {
+		wpa_msg(wpa_s, MSG_DEBUG, "GAS: Failed to send Query Request");
+		wpabuf_free(buf);
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: GAS query started with dialog token %u", res);
+	}
+}
+
+
+static void wpas_dpp_auth_success(struct wpa_supplicant *wpa_s, int initiator)
+{
+	wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=%d", initiator);
+
+	if (wpa_s->dpp_auth->configurator)
+		wpas_dpp_start_gas_server(wpa_s);
+	else
+		wpas_dpp_start_gas_client(wpa_s);
+}
+
+
+static void wpas_dpp_rx_auth_resp(struct wpa_supplicant *wpa_s, const u8 *src,
+				  const u8 *hdr, const u8 *buf, size_t len)
+{
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+	struct wpabuf *msg;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication Response from " MACSTR,
+		   MAC2STR(src));
+
+	if (!auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Authentication in progress - drop");
+		return;
+	}
+
+	if (!is_zero_ether_addr(auth->peer_mac_addr) &&
+	    os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+			   MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+		return;
+	}
+
+	eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
+
+	msg = dpp_auth_resp_rx(auth, hdr, buf, len);
+	if (!msg) {
+		if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Start wait for full response");
+			offchannel_send_action_done(wpa_s);
+			wpas_dpp_listen_start(wpa_s, auth->curr_freq);
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "DPP: No confirm generated");
+		return;
+	}
+	os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
+
+	offchannel_send_action(wpa_s, auth->curr_freq,
+			       src, wpa_s->own_addr, broadcast,
+			       wpabuf_head(msg), wpabuf_len(msg),
+			       500, wpas_dpp_tx_status, 0);
+	wpabuf_free(msg);
+	wpa_s->dpp_auth_ok_on_ack = 1;
+}
+
+
+static void wpas_dpp_rx_auth_conf(struct wpa_supplicant *wpa_s, const u8 *src,
+				  const u8 *hdr, const u8 *buf, size_t len)
+{
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation from " MACSTR,
+		   MAC2STR(src));
+
+	if (!auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Authentication in progress - drop");
+		return;
+	}
+
+	if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+			   MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+		return;
+	}
+
+	if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
+		return;
+	}
+
+	wpas_dpp_auth_success(wpa_s, 0);
+}
+
+
+static void wpas_dpp_rx_peer_disc_resp(struct wpa_supplicant *wpa_s,
+				       const u8 *src,
+				       const u8 *buf, size_t len)
+{
+	struct wpa_ssid *ssid;
+	const u8 *connector, *trans_id;
+	u16 connector_len, trans_id_len;
+	struct dpp_introduction intro;
+	struct rsn_pmksa_cache_entry *entry;
+	struct os_time now;
+	struct os_reltime rnow;
+	os_time_t expiry;
+	unsigned int seconds;
+
+	wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Response from " MACSTR,
+		   MAC2STR(src));
+	if (is_zero_ether_addr(wpa_s->dpp_intro_bssid) ||
+	    os_memcmp(src, wpa_s->dpp_intro_bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Not waiting for response from "
+			   MACSTR " - drop", MAC2STR(src));
+		return;
+	}
+	offchannel_send_action_done(wpa_s);
+
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+		if (ssid == wpa_s->dpp_intro_network)
+			break;
+	}
+	if (!ssid || !ssid->dpp_connector || !ssid->dpp_netaccesskey ||
+	    !ssid->dpp_csign) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Profile not found for network introduction");
+		return;
+	}
+
+	trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
+			       &trans_id_len);
+	if (!trans_id || trans_id_len != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include Transaction ID");
+		goto fail;
+	}
+	if (trans_id[0] != TRANSACTION_ID) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore frame with unexpected Transaction ID %u",
+			   trans_id[0]);
+		goto fail;
+	}
+
+	connector = dpp_get_attr(buf, len, DPP_ATTR_CONNECTOR, &connector_len);
+	if (!connector) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include its Connector");
+		return;
+	}
+
+	if (dpp_peer_intro(&intro, ssid->dpp_connector,
+			   ssid->dpp_netaccesskey,
+			   ssid->dpp_netaccesskey_len,
+			   ssid->dpp_csign,
+			   ssid->dpp_csign_len,
+			   connector, connector_len, &expiry) < 0) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Network Introduction protocol resulted in failure");
+		goto fail;
+	}
+
+	entry = os_zalloc(sizeof(*entry));
+	if (!entry)
+		goto fail;
+	os_memcpy(entry->aa, src, ETH_ALEN);
+	os_memcpy(entry->pmkid, intro.pmkid, PMKID_LEN);
+	os_memcpy(entry->pmk, intro.pmk, intro.pmk_len);
+	entry->pmk_len = intro.pmk_len;
+	entry->akmp = WPA_KEY_MGMT_DPP;
+	if (expiry) {
+		os_get_time(&now);
+		seconds = expiry - now.sec;
+	} else {
+		seconds = 86400 * 7;
+	}
+	os_get_reltime(&rnow);
+	entry->expiration = rnow.sec + seconds;
+	entry->reauth_time = rnow.sec + seconds;
+	entry->network_ctx = ssid;
+	wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry);
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Try connection again after successful network introduction");
+	if (wpa_supplicant_fast_associate(wpa_s) != 1) {
+		wpa_supplicant_cancel_sched_scan(wpa_s);
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+	}
+fail:
+	os_memset(&intro, 0, sizeof(intro));
+}
+
+
+static void
+wpas_dpp_tx_pkex_status(struct wpa_supplicant *wpa_s,
+			unsigned int freq, const u8 *dst,
+			const u8 *src, const u8 *bssid,
+			const u8 *data, size_t data_len,
+			enum offchannel_send_action_result result)
+{
+	wpa_printf(MSG_DEBUG, "DPP: TX status: freq=%u dst=" MACSTR
+		   " result=%s (PKEX)",
+		   freq, MAC2STR(dst),
+		   result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" :
+		   (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" :
+		    "FAILED"));
+	/* TODO: Time out wait for response more quickly in error cases? */
+}
+
+
+static void
+wpas_dpp_rx_pkex_exchange_req(struct wpa_supplicant *wpa_s, const u8 *src,
+			      const u8 *buf, size_t len, unsigned int freq)
+{
+	struct wpabuf *msg;
+	unsigned int wait_time;
+
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request from " MACSTR,
+		   MAC2STR(src));
+
+	/* TODO: Support multiple PKEX codes by iterating over all the enabled
+	 * values here */
+
+	if (!wpa_s->dpp_pkex_code || !wpa_s->dpp_pkex_bi) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No PKEX code configured - ignore request");
+		return;
+	}
+
+	if (wpa_s->dpp_pkex) {
+		/* TODO: Support parallel operations */
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Already in PKEX session - ignore new request");
+		return;
+	}
+
+	wpa_s->dpp_pkex = dpp_pkex_rx_exchange_req(wpa_s->dpp_pkex_bi,
+						   wpa_s->own_addr, src,
+						   wpa_s->dpp_pkex_identifier,
+						   wpa_s->dpp_pkex_code,
+						   buf, len);
+	if (!wpa_s->dpp_pkex) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to process the request - ignore it");
+		return;
+	}
+
+	msg = wpa_s->dpp_pkex->exchange_resp;
+	wait_time = wpa_s->max_remain_on_chan;
+	if (wait_time > 2000)
+		wait_time = 2000;
+	offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr,
+			       broadcast,
+			       wpabuf_head(msg), wpabuf_len(msg),
+			       wait_time, wpas_dpp_tx_pkex_status, 0);
+}
+
+
+static void
+wpas_dpp_rx_pkex_exchange_resp(struct wpa_supplicant *wpa_s, const u8 *src,
+			       const u8 *buf, size_t len, unsigned int freq)
+{
+	struct wpabuf *msg;
+	unsigned int wait_time;
+
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response from " MACSTR,
+		   MAC2STR(src));
+
+	/* TODO: Support multiple PKEX codes by iterating over all the enabled
+	 * values here */
+
+	if (!wpa_s->dpp_pkex || !wpa_s->dpp_pkex->initiator ||
+	    wpa_s->dpp_pkex->exchange_done) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
+		return;
+	}
+
+	os_memcpy(wpa_s->dpp_pkex->peer_mac, src, ETH_ALEN);
+	msg = dpp_pkex_rx_exchange_resp(wpa_s->dpp_pkex, buf, len);
+	if (!msg) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Request to " MACSTR,
+		   MAC2STR(src));
+
+	wait_time = wpa_s->max_remain_on_chan;
+	if (wait_time > 2000)
+		wait_time = 2000;
+	offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr,
+			       broadcast,
+			       wpabuf_head(msg), wpabuf_len(msg),
+			       wait_time, wpas_dpp_tx_pkex_status, 0);
+	wpabuf_free(msg);
+}
+
+
+static void
+wpas_dpp_rx_pkex_commit_reveal_req(struct wpa_supplicant *wpa_s, const u8 *src,
+				   const u8 *hdr, const u8 *buf, size_t len,
+				   unsigned int freq)
+{
+	struct wpabuf *msg;
+	unsigned int wait_time;
+	struct dpp_pkex *pkex = wpa_s->dpp_pkex;
+	struct dpp_bootstrap_info *bi;
+
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Request from " MACSTR,
+		   MAC2STR(src));
+
+	if (!pkex || pkex->initiator || !pkex->exchange_done) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
+		return;
+	}
+
+	msg = dpp_pkex_rx_commit_reveal_req(pkex, hdr, buf, len);
+	if (!msg) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to process the request");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Response to "
+		   MACSTR, MAC2STR(src));
+
+	wait_time = wpa_s->max_remain_on_chan;
+	if (wait_time > 2000)
+		wait_time = 2000;
+	offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr,
+			       broadcast,
+			       wpabuf_head(msg), wpabuf_len(msg),
+			       wait_time, wpas_dpp_tx_pkex_status, 0);
+	wpabuf_free(msg);
+
+	bi = os_zalloc(sizeof(*bi));
+	if (!bi)
+		return;
+	bi->id = wpas_dpp_next_id(wpa_s);
+	bi->type = DPP_BOOTSTRAP_PKEX;
+	os_memcpy(bi->mac_addr, src, ETH_ALEN);
+	bi->num_freq = 1;
+	bi->freq[0] = freq;
+	bi->curve = pkex->own_bi->curve;
+	bi->pubkey = pkex->peer_bootstrap_key;
+	pkex->peer_bootstrap_key = NULL;
+	dpp_pkex_free(pkex);
+	wpa_s->dpp_pkex = NULL;
+	if (dpp_bootstrap_key_hash(bi) < 0) {
+		dpp_bootstrap_info_free(bi);
+		return;
+	}
+	dl_list_add(&wpa_s->dpp_bootstrap, &bi->list);
+}
+
+
+static void
+wpas_dpp_rx_pkex_commit_reveal_resp(struct wpa_supplicant *wpa_s, const u8 *src,
+				    const u8 *hdr, const u8 *buf, size_t len,
+				    unsigned int freq)
+{
+	int res;
+	struct dpp_bootstrap_info *bi, *own_bi;
+	struct dpp_pkex *pkex = wpa_s->dpp_pkex;
+	char cmd[500];
+
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Response from " MACSTR,
+		   MAC2STR(src));
+
+	if (!pkex || !pkex->initiator || !pkex->exchange_done) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
+		return;
+	}
+
+	res = dpp_pkex_rx_commit_reveal_resp(pkex, hdr, buf, len);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
+		return;
+	}
+
+	own_bi = pkex->own_bi;
+
+	bi = os_zalloc(sizeof(*bi));
+	if (!bi)
+		return;
+	bi->id = wpas_dpp_next_id(wpa_s);
+	bi->type = DPP_BOOTSTRAP_PKEX;
+	os_memcpy(bi->mac_addr, src, ETH_ALEN);
+	bi->num_freq = 1;
+	bi->freq[0] = freq;
+	bi->curve = own_bi->curve;
+	bi->pubkey = pkex->peer_bootstrap_key;
+	pkex->peer_bootstrap_key = NULL;
+	dpp_pkex_free(pkex);
+	wpa_s->dpp_pkex = NULL;
+	if (dpp_bootstrap_key_hash(bi) < 0) {
+		dpp_bootstrap_info_free(bi);
+		return;
+	}
+	dl_list_add(&wpa_s->dpp_bootstrap, &bi->list);
+
+	os_snprintf(cmd, sizeof(cmd), " peer=%u %s",
+		    bi->id,
+		    wpa_s->dpp_pkex_auth_cmd ? wpa_s->dpp_pkex_auth_cmd : "");
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Start authentication after PKEX with parameters: %s",
+		   cmd);
+	if (wpas_dpp_auth_init(wpa_s, cmd) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Authentication initialization failed");
+		return;
+	}
+}
+
+
+void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src,
+			const u8 *buf, size_t len, unsigned int freq)
+{
+	u8 crypto_suite;
+	enum dpp_public_action_frame_type type;
+	const u8 *hdr;
+
+	if (len < DPP_HDR_LEN)
+		return;
+	if (WPA_GET_BE24(buf) != OUI_WFA || buf[3] != DPP_OUI_TYPE)
+		return;
+	hdr = buf;
+	buf += 4;
+	len -= 4;
+	crypto_suite = *buf++;
+	type = *buf++;
+	len -= 2;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Received DPP Public Action frame crypto suite %u type %d from "
+		   MACSTR " freq=%u",
+		   crypto_suite, type, MAC2STR(src), freq);
+	if (crypto_suite != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: Unsupported crypto suite %u",
+			   crypto_suite);
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes", buf, len);
+	if (dpp_check_attrs(buf, len) < 0)
+		return;
+
+	switch (type) {
+	case DPP_PA_AUTHENTICATION_REQ:
+		wpas_dpp_rx_auth_req(wpa_s, src, hdr, buf, len, freq);
+		break;
+	case DPP_PA_AUTHENTICATION_RESP:
+		wpas_dpp_rx_auth_resp(wpa_s, src, hdr, buf, len);
+		break;
+	case DPP_PA_AUTHENTICATION_CONF:
+		wpas_dpp_rx_auth_conf(wpa_s, src, hdr, buf, len);
+		break;
+	case DPP_PA_PEER_DISCOVERY_RESP:
+		wpas_dpp_rx_peer_disc_resp(wpa_s, src, buf, len);
+		break;
+	case DPP_PA_PKEX_EXCHANGE_REQ:
+		wpas_dpp_rx_pkex_exchange_req(wpa_s, src, buf, len, freq);
+		break;
+	case DPP_PA_PKEX_EXCHANGE_RESP:
+		wpas_dpp_rx_pkex_exchange_resp(wpa_s, src, buf, len, freq);
+		break;
+	case DPP_PA_PKEX_COMMIT_REVEAL_REQ:
+		wpas_dpp_rx_pkex_commit_reveal_req(wpa_s, src, hdr, buf, len,
+						   freq);
+		break;
+	case DPP_PA_PKEX_COMMIT_REVEAL_RESP:
+		wpas_dpp_rx_pkex_commit_reveal_resp(wpa_s, src, hdr, buf, len,
+						    freq);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignored unsupported frame subtype %d", type);
+		break;
+	}
+}
+
+
+static struct wpabuf *
+wpas_dpp_gas_req_handler(void *ctx, const u8 *sa, const u8 *query,
+			 size_t query_len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+	struct wpabuf *resp;
+
+	wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR,
+		   MAC2STR(sa));
+	if (!auth || !auth->auth_success ||
+	    os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+		return NULL;
+	}
+	wpa_hexdump(MSG_DEBUG,
+		    "DPP: Received Configuration Request (GAS Query Request)",
+		    query, query_len);
+	resp = dpp_conf_req_rx(auth, query, query_len);
+	if (!resp)
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED);
+	return resp;
+}
+
+
+static void
+wpas_dpp_gas_status_handler(void *ctx, struct wpabuf *resp, int ok)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+	if (!auth) {
+		wpabuf_free(resp);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Configuration exchange completed (ok=%d)",
+		   ok);
+	eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
+	offchannel_send_action_done(wpa_s);
+	wpas_dpp_listen_stop(wpa_s);
+	if (ok)
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_SENT);
+	else
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED);
+	dpp_auth_deinit(wpa_s->dpp_auth);
+	wpa_s->dpp_auth = NULL;
+	wpabuf_free(resp);
+}
+
+
+static unsigned int wpas_dpp_next_configurator_id(struct wpa_supplicant *wpa_s)
+{
+	struct dpp_configurator *conf;
+	unsigned int max_id = 0;
+
+	dl_list_for_each(conf, &wpa_s->dpp_configurator,
+			 struct dpp_configurator, list) {
+		if (conf->id > max_id)
+			max_id = conf->id;
+	}
+	return max_id + 1;
+}
+
+
+int wpas_dpp_configurator_add(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	char *curve = NULL;
+	char *key = NULL;
+	u8 *privkey = NULL;
+	size_t privkey_len = 0;
+	int ret = -1;
+	struct dpp_configurator *conf = NULL;
+
+	curve = get_param(cmd, " curve=");
+	key = get_param(cmd, " key=");
+
+	if (key) {
+		privkey_len = os_strlen(key) / 2;
+		privkey = os_malloc(privkey_len);
+		if (!privkey ||
+		    hexstr2bin(key, privkey, privkey_len) < 0)
+			goto fail;
+	}
+
+	conf = dpp_keygen_configurator(curve, privkey, privkey_len);
+	if (!conf)
+		goto fail;
+
+	conf->id = wpas_dpp_next_configurator_id(wpa_s);
+	dl_list_add(&wpa_s->dpp_configurator, &conf->list);
+	ret = conf->id;
+	conf = NULL;
+fail:
+	os_free(curve);
+	str_clear_free(key);
+	bin_clear_free(privkey, privkey_len);
+	dpp_configurator_free(conf);
+	return ret;
+}
+
+
+static int dpp_configurator_del(struct wpa_supplicant *wpa_s, unsigned int id)
+{
+	struct dpp_configurator *conf, *tmp;
+	int found = 0;
+
+	dl_list_for_each_safe(conf, tmp, &wpa_s->dpp_configurator,
+			      struct dpp_configurator, list) {
+		if (id && conf->id != id)
+			continue;
+		found = 1;
+		dl_list_del(&conf->list);
+		dpp_configurator_free(conf);
+	}
+
+	if (id == 0)
+		return 0; /* flush succeeds regardless of entries found */
+	return found ? 0 : -1;
+}
+
+
+int wpas_dpp_configurator_remove(struct wpa_supplicant *wpa_s, const char *id)
+{
+	unsigned int id_val;
+
+	if (os_strcmp(id, "*") == 0) {
+		id_val = 0;
+	} else {
+		id_val = atoi(id);
+		if (id_val == 0)
+			return -1;
+	}
+
+	return dpp_configurator_del(wpa_s, id_val);
+}
+
+
+int wpas_dpp_configurator_sign(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	struct dpp_authentication *auth;
+	int ret = -1;
+	char *curve = NULL;
+
+	auth = os_zalloc(sizeof(*auth));
+	if (!auth)
+		return -1;
+
+	curve = get_param(cmd, " curve=");
+	wpas_dpp_set_configurator(wpa_s, auth, cmd);
+
+	if (dpp_configurator_own_config(auth, curve) == 0) {
+		wpas_dpp_handle_config_obj(wpa_s, auth);
+		ret = 0;
+	}
+
+	dpp_auth_deinit(auth);
+	os_free(curve);
+
+	return ret;
+}
+
+
+static void
+wpas_dpp_tx_introduction_status(struct wpa_supplicant *wpa_s,
+				unsigned int freq, const u8 *dst,
+				const u8 *src, const u8 *bssid,
+				const u8 *data, size_t data_len,
+				enum offchannel_send_action_result result)
+{
+	wpa_printf(MSG_DEBUG, "DPP: TX status: freq=%u dst=" MACSTR
+		   " result=%s (DPP Peer Discovery Request)",
+		   freq, MAC2STR(dst),
+		   result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" :
+		   (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" :
+		    "FAILED"));
+	/* TODO: Time out wait for response more quickly in error cases? */
+}
+
+
+int wpas_dpp_check_connect(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+			   struct wpa_bss *bss)
+{
+	struct os_time now;
+	struct wpabuf *msg;
+	unsigned int wait_time;
+
+	if (!(ssid->key_mgmt & WPA_KEY_MGMT_DPP) || !bss)
+		return 0; /* Not using DPP AKM - continue */
+	if (wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid))
+		return 0; /* PMKSA exists for DPP AKM - continue */
+
+	if (!ssid->dpp_connector || !ssid->dpp_netaccesskey ||
+	    !ssid->dpp_csign) {
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_MISSING_CONNECTOR
+			"missing %s",
+			!ssid->dpp_connector ? "Connector" :
+			(!ssid->dpp_netaccesskey ? "netAccessKey" :
+			 "C-sign-key"));
+		return -1;
+	}
+
+	os_get_time(&now);
+
+	if (ssid->dpp_netaccesskey_expiry &&
+	    ssid->dpp_netaccesskey_expiry < now.sec) {
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_MISSING_CONNECTOR
+			"netAccessKey expired");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Starting network introduction protocol to derive PMKSA for "
+		   MACSTR, MAC2STR(bss->bssid));
+
+	msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_REQ,
+			    5 + 4 + os_strlen(ssid->dpp_connector));
+	if (!msg)
+		return -1;
+
+	/* Transaction ID */
+	wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, TRANSACTION_ID);
+
+	/* DPP Connector */
+	wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+	wpabuf_put_le16(msg, os_strlen(ssid->dpp_connector));
+	wpabuf_put_str(msg, ssid->dpp_connector);
+
+	/* TODO: Timeout on AP response */
+	wait_time = wpa_s->max_remain_on_chan;
+	if (wait_time > 2000)
+		wait_time = 2000;
+	offchannel_send_action(wpa_s, bss->freq, bss->bssid, wpa_s->own_addr,
+			       broadcast,
+			       wpabuf_head(msg), wpabuf_len(msg),
+			       wait_time, wpas_dpp_tx_introduction_status, 0);
+	wpabuf_free(msg);
+
+	/* Request this connection attempt to terminate - new one will be
+	 * started when network introduction protocol completes */
+	os_memcpy(wpa_s->dpp_intro_bssid, bss->bssid, ETH_ALEN);
+	wpa_s->dpp_intro_network = ssid;
+	return 1;
+}
+
+
+int wpas_dpp_pkex_add(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	struct dpp_bootstrap_info *own_bi;
+	const char *pos, *end;
+	unsigned int wait_time;
+
+	pos = os_strstr(cmd, " own=");
+	if (!pos)
+		return -1;
+	pos += 5;
+	own_bi = dpp_bootstrap_get_id(wpa_s, atoi(pos));
+	if (!own_bi) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Identified bootstrap info not found");
+		return -1;
+	}
+	if (own_bi->type != DPP_BOOTSTRAP_PKEX) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Identified bootstrap info not for PKEX");
+		return -1;
+	}
+	wpa_s->dpp_pkex_bi = own_bi;
+
+	os_free(wpa_s->dpp_pkex_identifier);
+	wpa_s->dpp_pkex_identifier = NULL;
+	pos = os_strstr(cmd, " identifier=");
+	if (pos) {
+		pos += 12;
+		end = os_strchr(pos, ' ');
+		if (!end)
+			return -1;
+		wpa_s->dpp_pkex_identifier = os_malloc(end - pos + 1);
+		if (!wpa_s->dpp_pkex_identifier)
+			return -1;
+		os_memcpy(wpa_s->dpp_pkex_identifier, pos, end - pos);
+		wpa_s->dpp_pkex_identifier[end - pos] = '\0';
+	}
+
+	pos = os_strstr(cmd, " code=");
+	if (!pos)
+		return -1;
+	os_free(wpa_s->dpp_pkex_code);
+	wpa_s->dpp_pkex_code = os_strdup(pos + 6);
+	if (!wpa_s->dpp_pkex_code)
+		return -1;
+
+	if (os_strstr(cmd, " init=1")) {
+		struct wpabuf *msg;
+
+		wpa_printf(MSG_DEBUG, "DPP: Initiating PKEX");
+		dpp_pkex_free(wpa_s->dpp_pkex);
+		wpa_s->dpp_pkex = dpp_pkex_init(own_bi, wpa_s->own_addr,
+						wpa_s->dpp_pkex_identifier,
+						wpa_s->dpp_pkex_code);
+		if (!wpa_s->dpp_pkex)
+			return -1;
+
+		msg = wpa_s->dpp_pkex->exchange_req;
+		wait_time = wpa_s->max_remain_on_chan;
+		if (wait_time > 2000)
+			wait_time = 2000;
+		/* TODO: Which channel to use? */
+		offchannel_send_action(wpa_s, 2437, broadcast, wpa_s->own_addr,
+				       broadcast,
+				       wpabuf_head(msg), wpabuf_len(msg),
+				       wait_time, wpas_dpp_tx_pkex_status, 0);
+	}
+
+	/* TODO: Support multiple PKEX info entries */
+
+	os_free(wpa_s->dpp_pkex_auth_cmd);
+	wpa_s->dpp_pkex_auth_cmd = os_strdup(cmd);
+
+	return 1;
+}
+
+
+int wpas_dpp_pkex_remove(struct wpa_supplicant *wpa_s, const char *id)
+{
+	unsigned int id_val;
+
+	if (os_strcmp(id, "*") == 0) {
+		id_val = 0;
+	} else {
+		id_val = atoi(id);
+		if (id_val == 0)
+			return -1;
+	}
+
+	if ((id_val != 0 && id_val != 1) || !wpa_s->dpp_pkex_code)
+		return -1;
+
+	/* TODO: Support multiple PKEX entries */
+	os_free(wpa_s->dpp_pkex_code);
+	wpa_s->dpp_pkex_code = NULL;
+	os_free(wpa_s->dpp_pkex_identifier);
+	wpa_s->dpp_pkex_identifier = NULL;
+	os_free(wpa_s->dpp_pkex_auth_cmd);
+	wpa_s->dpp_pkex_auth_cmd = NULL;
+	wpa_s->dpp_pkex_bi = NULL;
+	/* TODO: Remove dpp_pkex only if it is for the identified PKEX code */
+	dpp_pkex_free(wpa_s->dpp_pkex);
+	wpa_s->dpp_pkex = NULL;
+	return 0;
+}
+
+
+int wpas_dpp_init(struct wpa_supplicant *wpa_s)
+{
+	u8 adv_proto_id[7];
+
+	adv_proto_id[0] = WLAN_EID_VENDOR_SPECIFIC;
+	adv_proto_id[1] = 5;
+	WPA_PUT_BE24(&adv_proto_id[2], OUI_WFA);
+	adv_proto_id[5] = DPP_OUI_TYPE;
+	adv_proto_id[6] = 0x01;
+
+	if (gas_server_register(wpa_s->gas_server, adv_proto_id,
+				sizeof(adv_proto_id), wpas_dpp_gas_req_handler,
+				wpas_dpp_gas_status_handler, wpa_s) < 0)
+		return -1;
+	dl_list_init(&wpa_s->dpp_bootstrap);
+	dl_list_init(&wpa_s->dpp_configurator);
+	wpa_s->dpp_init_done = 1;
+	return 0;
+}
+
+
+void wpas_dpp_deinit(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	os_free(wpa_s->dpp_config_obj_override);
+	wpa_s->dpp_config_obj_override = NULL;
+	os_free(wpa_s->dpp_discovery_override);
+	wpa_s->dpp_discovery_override = NULL;
+	os_free(wpa_s->dpp_groups_override);
+	wpa_s->dpp_groups_override = NULL;
+	wpa_s->dpp_ignore_netaccesskey_mismatch = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (!wpa_s->dpp_init_done)
+		return;
+	eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
+	offchannel_send_action_done(wpa_s);
+	wpas_dpp_listen_stop(wpa_s);
+	dpp_bootstrap_del(wpa_s, 0);
+	dpp_configurator_del(wpa_s, 0);
+	dpp_auth_deinit(wpa_s->dpp_auth);
+	wpa_s->dpp_auth = NULL;
+	wpas_dpp_pkex_remove(wpa_s, "*");
+	wpa_s->dpp_pkex = NULL;
+	os_memset(wpa_s->dpp_intro_bssid, 0, ETH_ALEN);
+	os_free(wpa_s->dpp_configurator_params);
+	wpa_s->dpp_configurator_params = NULL;
+}
diff --git a/wpa_supplicant/dpp_supplicant.h b/wpa_supplicant/dpp_supplicant.h
new file mode 100644
index 0000000..05a466d
--- /dev/null
+++ b/wpa_supplicant/dpp_supplicant.h
@@ -0,0 +1,38 @@
+/*
+ * wpa_supplicant - DPP
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DPP_SUPPLICANT_H
+#define DPP_SUPPLICANT_H
+
+int wpas_dpp_qr_code(struct wpa_supplicant *wpa_s, const char *cmd);
+int wpas_dpp_bootstrap_gen(struct wpa_supplicant *wpa_s, const char *cmd);
+int wpas_dpp_bootstrap_remove(struct wpa_supplicant *wpa_s, const char *id);
+const char * wpas_dpp_bootstrap_get_uri(struct wpa_supplicant *wpa_s,
+					unsigned int id);
+int wpas_dpp_bootstrap_info(struct wpa_supplicant *wpa_s, int id,
+			    char *reply, int reply_size);
+int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd);
+int wpas_dpp_listen(struct wpa_supplicant *wpa_s, const char *cmd);
+void wpas_dpp_listen_stop(struct wpa_supplicant *wpa_s);
+void wpas_dpp_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+				   unsigned int freq);
+void wpas_dpp_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+					  unsigned int freq);
+void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src,
+			const u8 *buf, size_t len, unsigned int freq);
+int wpas_dpp_configurator_add(struct wpa_supplicant *wpa_s, const char *cmd);
+int wpas_dpp_configurator_remove(struct wpa_supplicant *wpa_s, const char *id);
+int wpas_dpp_configurator_sign(struct wpa_supplicant *wpa_s, const char *cmd);
+int wpas_dpp_pkex_add(struct wpa_supplicant *wpa_s, const char *cmd);
+int wpas_dpp_pkex_remove(struct wpa_supplicant *wpa_s, const char *id);
+int wpas_dpp_init(struct wpa_supplicant *wpa_s);
+void wpas_dpp_deinit(struct wpa_supplicant *wpa_s);
+int wpas_dpp_check_connect(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+			   struct wpa_bss *bss);
+
+#endif /* DPP_SUPPLICANT_H */
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 0af63c9..e131653 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -189,20 +189,19 @@
 }
 
 static inline int wpa_drv_add_pmkid(struct wpa_supplicant *wpa_s,
-				    const u8 *bssid, const u8 *pmkid)
+				    struct wpa_pmkid_params *params)
 {
 	if (wpa_s->driver->add_pmkid) {
-		return wpa_s->driver->add_pmkid(wpa_s->drv_priv, bssid, pmkid);
+		return wpa_s->driver->add_pmkid(wpa_s->drv_priv, params);
 	}
 	return -1;
 }
 
 static inline int wpa_drv_remove_pmkid(struct wpa_supplicant *wpa_s,
-				       const u8 *bssid, const u8 *pmkid)
+				       struct wpa_pmkid_params *params)
 {
 	if (wpa_s->driver->remove_pmkid) {
-		return wpa_s->driver->remove_pmkid(wpa_s->drv_priv, bssid,
-						   pmkid);
+		return wpa_s->driver->remove_pmkid(wpa_s->drv_priv, params);
 	}
 	return -1;
 }
@@ -276,11 +275,12 @@
 
 static inline struct hostapd_hw_modes *
 wpa_drv_get_hw_feature_data(struct wpa_supplicant *wpa_s, u16 *num_modes,
-			    u16 *flags)
+			    u16 *flags, u8 *dfs_domain)
 {
 	if (wpa_s->driver->get_hw_feature_data)
 		return wpa_s->driver->get_hw_feature_data(wpa_s->drv_priv,
-							  num_modes, flags);
+							  num_modes, flags,
+							  dfs_domain);
 	return NULL;
 }
 
@@ -989,4 +989,43 @@
 					    tdls_external_control);
 }
 
+static inline struct wpa_bss_candidate_info *
+wpa_drv_get_bss_trans_status(struct wpa_supplicant *wpa_s,
+			     struct wpa_bss_trans_info *params)
+{
+	if (!wpa_s->driver->get_bss_transition_status)
+		return NULL;
+	return wpa_s->driver->get_bss_transition_status(wpa_s->drv_priv,
+							params);
+}
+
+static inline int wpa_drv_ignore_assoc_disallow(struct wpa_supplicant *wpa_s,
+						int val)
+{
+	if (!wpa_s->driver->ignore_assoc_disallow)
+		return -1;
+	return wpa_s->driver->ignore_assoc_disallow(wpa_s->drv_priv, val);
+}
+
+static inline int wpa_drv_set_bssid_blacklist(struct wpa_supplicant *wpa_s,
+					      unsigned int num_bssid,
+					      const u8 *bssids)
+{
+	if (!wpa_s->driver->set_bssid_blacklist)
+		return -1;
+	return wpa_s->driver->set_bssid_blacklist(wpa_s->drv_priv, num_bssid,
+						  bssids);
+}
+
+static inline int wpa_drv_update_connect_params(
+	struct wpa_supplicant *wpa_s,
+	struct wpa_driver_associate_params *params,
+	enum wpa_drv_update_connect_params_mask mask)
+{
+	if (!wpa_s->driver->update_connect_params)
+		return -1;
+	return wpa_s->driver->update_connect_params(wpa_s->drv_priv, params,
+						    mask);
+}
+
 #endif /* DRIVER_I_H */
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index bc3c90e..63cf773 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -28,6 +28,7 @@
 #include "notify.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
+#include "common/gas_server.h"
 #include "crypto/random.h"
 #include "blacklist.h"
 #include "wpas_glue.h"
@@ -46,6 +47,7 @@
 #include "mesh.h"
 #include "mesh_mpm.h"
 #include "wmm_ac.h"
+#include "dpp_supplicant.h"
 
 
 #ifndef CONFIG_NO_SCAN_PROCESSING
@@ -54,8 +56,7 @@
 #endif /* CONFIG_NO_SCAN_PROCESSING */
 
 
-static int wpas_temp_disabled(struct wpa_supplicant *wpa_s,
-			      struct wpa_ssid *ssid)
+int wpas_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
 {
 	struct os_reltime now;
 
@@ -302,7 +303,9 @@
 
 	eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
 	eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
-	if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt))
+	if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_OWE ||
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_DPP)
 		eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
 	wpa_s->ap_ies_from_associnfo = 0;
 	wpa_s->current_ssid = NULL;
@@ -311,6 +314,11 @@
 
 	wpas_rrm_reset(wpa_s);
 	wpa_s->wnmsleep_used = 0;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	wpa_s->last_tk_alg = WPA_ALG_NONE;
+	os_memset(wpa_s->last_tk, 0, sizeof(wpa_s->last_tk));
+#endif /* CONFIG_TESTING_OPTIONS */
 }
 
 
@@ -327,7 +335,7 @@
 	for (i = 0; i < ie.num_pmkid; i++) {
 		pmksa_set = pmksa_cache_set_current(wpa_s->wpa,
 						    ie.pmkid + i * PMKID_LEN,
-						    NULL, NULL, 0);
+						    NULL, NULL, 0, NULL);
 		if (pmksa_set == 0) {
 			eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
 			break;
@@ -562,6 +570,14 @@
 			break;
 		}
 
+		if (ssid->group_mgmt_cipher &&
+		    !(ie.mgmt_group_cipher & ssid->group_mgmt_cipher)) {
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip RSN IE - group mgmt cipher mismatch");
+			break;
+		}
+
 		if (!(ie.key_mgmt & ssid->key_mgmt)) {
 			if (debug_print)
 				wpa_dbg(wpa_s, MSG_DEBUG,
@@ -868,6 +884,80 @@
 }
 
 
+static void owe_trans_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+			   const u8 **ret_ssid, size_t *ret_ssid_len)
+{
+#ifdef CONFIG_OWE
+	const u8 *owe, *pos, *end, *bssid;
+	u8 ssid_len;
+	struct wpa_bss *open_bss;
+
+	owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
+	if (!owe || !wpa_bss_get_ie(bss, WLAN_EID_RSN))
+		return;
+
+	pos = owe + 6;
+	end = owe + 2 + owe[1];
+
+	if (end - pos < ETH_ALEN + 1)
+		return;
+	bssid = pos;
+	pos += ETH_ALEN;
+	ssid_len = *pos++;
+	if (end - pos < ssid_len || ssid_len > SSID_MAX_LEN)
+		return;
+
+	/* Match the profile SSID against the OWE transition mode SSID on the
+	 * open network. */
+	wpa_dbg(wpa_s, MSG_DEBUG, "OWE: transition mode BSSID: " MACSTR
+		" SSID: %s", MAC2STR(bssid), wpa_ssid_txt(pos, ssid_len));
+	*ret_ssid = pos;
+	*ret_ssid_len = ssid_len;
+
+	if (bss->ssid_len > 0)
+		return;
+
+	open_bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
+	if (!open_bss)
+		return;
+	if (ssid_len != open_bss->ssid_len ||
+	    os_memcmp(pos, open_bss->ssid, ssid_len) != 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"OWE: transition mode SSID mismatch: %s",
+			wpa_ssid_txt(open_bss->ssid, open_bss->ssid_len));
+		return;
+	}
+
+	owe = wpa_bss_get_vendor_ie(open_bss, OWE_IE_VENDOR_TYPE);
+	if (!owe || wpa_bss_get_ie(open_bss, WLAN_EID_RSN)) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"OWE: transition mode open BSS unexpected info");
+		return;
+	}
+
+	pos = owe + 6;
+	end = owe + 2 + owe[1];
+
+	if (end - pos < ETH_ALEN + 1)
+		return;
+	if (os_memcmp(pos, bss->bssid, ETH_ALEN) != 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"OWE: transition mode BSSID mismatch: " MACSTR,
+			MAC2STR(pos));
+		return;
+	}
+	pos += ETH_ALEN;
+	ssid_len = *pos++;
+	if (end - pos < ssid_len || ssid_len > SSID_MAX_LEN)
+		return;
+	wpa_dbg(wpa_s, MSG_DEBUG, "OWE: learned transition mode OWE SSID: %s",
+		wpa_ssid_txt(pos, ssid_len));
+	os_memcpy(bss->ssid, pos, ssid_len);
+	bss->ssid_len = ssid_len;
+#endif /* CONFIG_OWE */
+}
+
+
 struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
 				     int i, struct wpa_bss *bss,
 				     struct wpa_ssid *group,
@@ -882,6 +972,8 @@
 #ifdef CONFIG_MBO
 	const u8 *assoc_disallow;
 #endif /* CONFIG_MBO */
+	const u8 *match_ssid;
+	size_t match_ssid_len;
 
 	ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
 	wpa_ie_len = ie ? ie[1] : 0;
@@ -931,7 +1023,11 @@
 		}
 	}
 
-	if (bss->ssid_len == 0) {
+	match_ssid = bss->ssid;
+	match_ssid_len = bss->ssid_len;
+	owe_trans_ssid(wpa_s, bss, &match_ssid, &match_ssid_len);
+
+	if (match_ssid_len == 0) {
 		if (debug_print)
 			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - SSID not known");
 		return NULL;
@@ -943,7 +1039,7 @@
 		return NULL;
 	}
 
-	if (disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
+	if (disallowed_ssid(wpa_s, match_ssid, match_ssid_len)) {
 		if (debug_print)
 			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - SSID disallowed");
 		return NULL;
@@ -998,8 +1094,8 @@
 			check_ssid = 0;
 
 		if (check_ssid &&
-		    (bss->ssid_len != ssid->ssid_len ||
-		     os_memcmp(bss->ssid, ssid->ssid, bss->ssid_len) != 0)) {
+		    (match_ssid_len != ssid->ssid_len ||
+		     os_memcmp(match_ssid, ssid->ssid, match_ssid_len) != 0)) {
 			if (debug_print)
 				wpa_dbg(wpa_s, MSG_DEBUG,
 					"   skip - SSID mismatch");
@@ -1209,6 +1305,19 @@
 #endif /* CONFIG_TESTING_OPTIONS */
 #endif /* CONFIG_MBO */
 
+#ifdef CONFIG_DPP
+		if ((ssid->key_mgmt & WPA_KEY_MGMT_DPP) &&
+		    !wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid) &&
+		    (!ssid->dpp_connector ||
+		     !ssid->dpp_netaccesskey ||
+		     !ssid->dpp_csign)) {
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - no PMKSA entry for DPP");
+			continue;
+		}
+#endif /* CONFIG_DPP */
+
 		/* Matching configuration found */
 		return ssid;
 	}
@@ -2213,7 +2322,8 @@
 
 #ifdef CONFIG_FILS
 #ifdef CONFIG_SME
-	if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS &&
+	if ((wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS ||
+	     wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS_SK_PFS) &&
 	    (!data->assoc_info.resp_frame ||
 	     fils_process_assoc_resp(wpa_s->wpa,
 				     data->assoc_info.resp_frame,
@@ -2222,8 +2332,24 @@
 		return -1;
 	}
 #endif /* CONFIG_SME */
+
+	/* Additional processing for FILS when SME is in driver */
+	if (wpa_s->auth_alg == WPA_AUTH_ALG_FILS &&
+	    !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))
+		wpa_sm_set_reset_fils_completed(wpa_s->wpa, 1);
 #endif /* CONFIG_FILS */
 
+#ifdef CONFIG_OWE
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_OWE &&
+	    (wpa_drv_get_bssid(wpa_s, bssid) < 0 ||
+	     owe_process_assoc_resp(wpa_s->wpa, bssid,
+				    data->assoc_info.resp_ies,
+				    data->assoc_info.resp_ies_len) < 0)) {
+		wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_UNSPECIFIED);
+		return -1;
+	}
+#endif /* CONFIG_OWE */
+
 #ifdef CONFIG_IEEE80211R
 #ifdef CONFIG_SME
 	if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FT) {
@@ -2521,7 +2647,9 @@
 		eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
 		eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
 	}
-	if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || ft_completed ||
+	if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_DPP ||
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_OWE || ft_completed ||
 	    already_authorized)
 		eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
 	/* 802.1X::portControl = Auto */
@@ -2636,6 +2764,16 @@
 		if (wpa_s->reassoc_same_bss)
 			wmm_ac_restore_tspecs(wpa_s);
 	}
+
+#ifdef CONFIG_FILS
+	if (wpa_key_mgmt_fils(wpa_s->key_mgmt)) {
+		struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, bssid);
+		const u8 *fils_cache_id = wpa_bss_get_fils_cache_id(bss);
+
+		if (fils_cache_id)
+			wpa_sm_set_fils_cache_id(wpa_s->wpa, fils_cache_id);
+	}
+#endif /* CONFIG_FILS */
 }
 
 
@@ -3027,18 +3165,6 @@
 }
 
 
-#ifdef CONFIG_PEERKEY
-static void
-wpa_supplicant_event_stkstart(struct wpa_supplicant *wpa_s,
-			      union wpa_event_data *data)
-{
-	if (data == NULL)
-		return;
-	wpa_sm_stkstart(wpa_s->wpa, data->stkstart.peer);
-}
-#endif /* CONFIG_PEERKEY */
-
-
 #ifdef CONFIG_TDLS
 static void wpa_supplicant_event_tdls(struct wpa_supplicant *wpa_s,
 				      union wpa_event_data *data)
@@ -3401,6 +3527,7 @@
 	struct wpa_supplicant *wpa_s, struct channel_list_changed *info)
 {
 	struct wpa_supplicant *ifs;
+	u8 dfs_domain;
 
 	/*
 	 * To allow backwards compatibility with higher level layers that
@@ -3425,7 +3552,7 @@
 			   ifs->ifname);
 		free_hw_features(ifs);
 		ifs->hw.modes = wpa_drv_get_hw_feature_data(
-			ifs, &ifs->hw.num_modes, &ifs->hw.flags);
+			ifs, &ifs->hw.num_modes, &ifs->hw.flags, &dfs_domain);
 
 		/* Restart PNO/sched_scan with updated channel list */
 		if (ifs->pno) {
@@ -3500,6 +3627,15 @@
 		return;
 #endif /* CONFIG_GAS */
 
+#ifdef CONFIG_GAS_SERVER
+	if ((mgmt->u.action.category == WLAN_ACTION_PUBLIC ||
+	     mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL) &&
+	    gas_server_rx(wpa_s->gas_server, mgmt->da, mgmt->sa, mgmt->bssid,
+			  mgmt->u.action.category,
+			  payload, plen, freq) == 0)
+		return;
+#endif /* CONFIG_GAS_SERVER */
+
 #ifdef CONFIG_TDLS
 	if (category == WLAN_ACTION_PUBLIC && plen >= 4 &&
 	    payload[0] == WLAN_TDLS_DISCOVERY_RESPONSE) {
@@ -3528,6 +3664,7 @@
 	if (category == WLAN_ACTION_RADIO_MEASUREMENT &&
 	    payload[0] == WLAN_RRM_RADIO_MEASUREMENT_REQUEST) {
 		wpas_rrm_handle_radio_measurement_request(wpa_s, mgmt->sa,
+							  mgmt->da,
 							  payload + 1,
 							  plen - 1);
 		return;
@@ -3554,6 +3691,18 @@
 	}
 #endif /* CONFIG_FST */
 
+#ifdef CONFIG_DPP
+	if (category == WLAN_ACTION_PUBLIC && plen >= 5 &&
+	    payload[0] == WLAN_PA_VENDOR_SPECIFIC &&
+	    WPA_GET_BE24(&payload[1]) == OUI_WFA &&
+	    payload[4] == DPP_OUI_TYPE) {
+		payload++;
+		plen--;
+		wpas_dpp_rx_action(wpa_s, mgmt->sa, payload, plen, freq);
+		return;
+	}
+#endif /* CONFIG_DPP */
+
 	wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
 			   category, payload, plen, freq);
 	if (wpa_s->ifmsh)
@@ -3611,6 +3760,30 @@
 			       data->assoc_info.ptk_kck_len,
 			       data->assoc_info.ptk_kek,
 			       data->assoc_info.ptk_kek_len);
+#ifdef CONFIG_FILS
+	if (wpa_s->auth_alg == WPA_AUTH_ALG_FILS) {
+		struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, wpa_s->bssid);
+		const u8 *fils_cache_id = wpa_bss_get_fils_cache_id(bss);
+
+		/* Update ERP next sequence number */
+		eapol_sm_update_erp_next_seq_num(
+			wpa_s->eapol, data->assoc_info.fils_erp_next_seq_num);
+
+		if (data->assoc_info.fils_pmk && data->assoc_info.fils_pmkid) {
+			/* Add the new PMK and PMKID to the PMKSA cache */
+			wpa_sm_pmksa_cache_add(wpa_s->wpa,
+					       data->assoc_info.fils_pmk,
+					       data->assoc_info.fils_pmk_len,
+					       data->assoc_info.fils_pmkid,
+					       wpa_s->bssid, fils_cache_id);
+		} else if (data->assoc_info.fils_pmkid) {
+			/* Update the current PMKSA used for this connection */
+			pmksa_cache_set_current(wpa_s->wpa,
+						data->assoc_info.fils_pmkid,
+						NULL, NULL, 0, NULL);
+		}
+	}
+#endif /* CONFIG_FILS */
 }
 
 
@@ -3618,7 +3791,6 @@
 			  union wpa_event_data *data)
 {
 	struct wpa_supplicant *wpa_s = ctx;
-	char buf[100];
 	int resched;
 
 	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED &&
@@ -3668,9 +3840,17 @@
 				   "EVENT_ASSOC - ignore_auth_resp active!");
 			break;
 		}
+		if (wpa_s->testing_resend_assoc) {
+			wpa_printf(MSG_INFO,
+				   "EVENT_DEAUTH - testing_resend_assoc");
+			break;
+		}
 #endif /* CONFIG_TESTING_OPTIONS */
 		wpa_supplicant_event_assoc(wpa_s, data);
-		if (data && data->assoc_info.authorized)
+		if (data &&
+		    (data->assoc_info.authorized ||
+		     (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+		      wpa_fils_is_completed(wpa_s->wpa))))
 			wpa_supplicant_event_assoc_auth(wpa_s, data);
 		if (data) {
 			wpa_msg(wpa_s, MSG_INFO,
@@ -3689,6 +3869,11 @@
 				   "EVENT_DEAUTH - ignore_auth_resp active!");
 			break;
 		}
+		if (wpa_s->testing_resend_assoc) {
+			wpa_printf(MSG_INFO,
+				   "EVENT_DEAUTH - testing_resend_assoc");
+			break;
+		}
 #endif /* CONFIG_TESTING_OPTIONS */
 		wpas_event_deauth(wpa_s,
 				  data ? &data->deauth_info : NULL);
@@ -3761,11 +3946,6 @@
 	case EVENT_PMKID_CANDIDATE:
 		wpa_supplicant_event_pmkid_candidate(wpa_s, data);
 		break;
-#ifdef CONFIG_PEERKEY
-	case EVENT_STKSTART:
-		wpa_supplicant_event_stkstart(wpa_s, data);
-		break;
-#endif /* CONFIG_PEERKEY */
 #ifdef CONFIG_TDLS
 	case EVENT_TDLS:
 		wpa_supplicant_event_tdls(wpa_s, data);
@@ -3787,24 +3967,23 @@
 		break;
 #endif /* CONFIG_IBSS_RSN */
 	case EVENT_ASSOC_REJECT:
-		if (data->assoc_reject.timeout_reason)
-			os_snprintf(buf, sizeof(buf), "=%s",
-				    data->assoc_reject.timeout_reason);
-		else
-			buf[0] = '\0';
 		if (data->assoc_reject.bssid)
 			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
-				"bssid=" MACSTR	" status_code=%u%s%s",
+				"bssid=" MACSTR	" status_code=%u%s%s%s",
 				MAC2STR(data->assoc_reject.bssid),
 				data->assoc_reject.status_code,
 				data->assoc_reject.timed_out ? " timeout" : "",
-				buf);
+				data->assoc_reject.timeout_reason ? "=" : "",
+				data->assoc_reject.timeout_reason ?
+				data->assoc_reject.timeout_reason : "");
 		else
 			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
-				"status_code=%u%s%s",
+				"status_code=%u%s%s%s",
 				data->assoc_reject.status_code,
 				data->assoc_reject.timed_out ? " timeout" : "",
-				buf);
+				data->assoc_reject.timeout_reason ? "=" : "",
+				data->assoc_reject.timeout_reason ?
+				data->assoc_reject.timeout_reason : "");
 		wpa_s->assoc_status_code = data->assoc_reject.status_code;
 		wpa_s->assoc_timed_out = data->assoc_reject.timed_out;
 		wpas_notify_assoc_status_code(wpa_s);
@@ -3812,6 +3991,15 @@
 			sme_event_assoc_reject(wpa_s, data);
 		else {
 			const u8 *bssid = data->assoc_reject.bssid;
+
+#ifdef CONFIG_FILS
+			/* Update ERP next sequence number */
+			if (wpa_s->auth_alg == WPA_AUTH_ALG_FILS)
+				eapol_sm_update_erp_next_seq_num(
+				      wpa_s->eapol,
+				      data->assoc_reject.fils_erp_next_seq_num);
+#endif /* CONFIG_FILS */
+
 			if (bssid == NULL || is_zero_ether_addr(bssid))
 				bssid = wpa_s->pending_bssid;
 			wpas_connection_failed(wpa_s, bssid);
@@ -4107,6 +4295,10 @@
 #endif /* CONFIG_OFFCHANNEL */
 		wpas_p2p_cancel_remain_on_channel_cb(
 			wpa_s, data->remain_on_channel.freq);
+#ifdef CONFIG_DPP
+		wpas_dpp_cancel_remain_on_channel_cb(
+			wpa_s, data->remain_on_channel.freq);
+#endif /* CONFIG_DPP */
 		break;
 	case EVENT_EAPOL_RX:
 		wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src,
diff --git a/wpa_supplicant/examples/dpp-qrcode.py b/wpa_supplicant/examples/dpp-qrcode.py
new file mode 100644
index 0000000..e2a00c9
--- /dev/null
+++ b/wpa_supplicant/examples/dpp-qrcode.py
@@ -0,0 +1,130 @@
+#!/usr/bin/python
+#
+# Example Android logcat to wpa_supplicant wrapper for QR Code scans
+# Copyright (c) 2017, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import os
+import sys
+import argparse
+import logging
+import qrcode
+
+scriptsdir = os.path.dirname(os.path.realpath(sys.modules[__name__].__file__))
+sys.path.append(os.path.join(scriptsdir, '..', '..', 'wpaspy'))
+
+import wpaspy
+
+wpas_ctrl = '/var/run/wpa_supplicant'
+
+def wpas_connect():
+    ifaces = []
+    if os.path.isdir(wpas_ctrl):
+        try:
+            ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
+        except OSError, error:
+            print "Could not find wpa_supplicant: ", error
+            return None
+
+    if len(ifaces) < 1:
+        print "No wpa_supplicant control interface found"
+        return None
+
+    for ctrl in ifaces:
+        try:
+            wpas = wpaspy.Ctrl(ctrl)
+            return wpas
+        except Exception, e:
+            pass
+    return None
+
+def dpp_logcat():
+    for line in iter(sys.stdin.readline, ''):
+        if "ResultHandler: Launching intent: Intent" not in line:
+            continue
+        if "act=android.intent.action.VIEW" not in line:
+            continue
+        uri = None
+        for val in line.split(' '):
+            if val.startswith('dat='):
+                uri = val.split('=', 1)[1]
+                break
+        if not uri:
+            continue
+        if not uri.startswith('DPP:'):
+            continue
+        print "Found DPP bootstrap info URI:"
+        print uri
+        wpas = wpas_connect()
+        if not wpas:
+            print "Could not connect to wpa_supplicant"
+            print
+            continue
+        res = wpas.request("DPP_QR_CODE " + uri);
+        try:
+            id = int(res)
+        except ValueError:
+            print "QR Code URI rejected"
+            continue
+        print "QR Code URI accepted - ID=%d" % id
+        print wpas.request("DPP_BOOTSTRAP_INFO %d" % id)
+        del wpas
+
+def dpp_display(curve):
+        wpas = wpas_connect()
+        if not wpas:
+            print "Could not connect to wpa_supplicant"
+            return
+        res = wpas.request("STATUS")
+        addr = None
+        for line in res.splitlines():
+            if line.startswith("address="):
+                addr = line.split('=')[1]
+                break
+        cmd = "DPP_BOOTSTRAP_GEN type=qrcode"
+        cmd += " chan=81/1"
+        if addr:
+            cmd += " mac=" + addr.replace(':','')
+        if curve:
+            cmd += " curve=" + curve
+        res = wpas.request(cmd)
+        try:
+            id = int(res)
+        except ValueError:
+            print "Failed to generate bootstrap info URI"
+            return
+        print "Bootstrap information - ID=%d" % id
+        print wpas.request("DPP_BOOTSTRAP_INFO %d" % id)
+        uri = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % id)
+        print uri
+        print "ID=%d" % id
+        qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_M,
+                           border=3)
+        qr.add_data(uri, optimize=5)
+        qr.print_ascii(tty=True)
+        print "ID=%d" % id
+        del wpas
+
+def main():
+    parser = argparse.ArgumentParser(description='Android logcat to wpa_supplicant integration for DPP QR Code operations')
+    parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
+                        action='store_const', dest='loglevel',
+                        help='verbose debug output')
+    parser.add_argument('--curve', '-c',
+                        help='set a specific curve (P-256, P-384, P-521, BP-256R1, BP-384R1, BP-512R1) for key generation')
+    parser.add_argument('command', choices=['logcat',
+                                            'display'],
+                        nargs='?')
+    args = parser.parse_args()
+
+    logging.basicConfig(level=args.loglevel)
+
+    if args.command == "logcat":
+        dpp_logcat()
+    elif args.command == "display":
+        dpp_display(args.curve)
+
+if __name__ == '__main__':
+    main()
diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c
index db481a5..91cf19a 100644
--- a/wpa_supplicant/gas_query.c
+++ b/wpa_supplicant/gas_query.c
@@ -243,10 +243,17 @@
 	}
 	os_get_reltime(&query->last_oper);
 
-	if (result == OFFCHANNEL_SEND_ACTION_SUCCESS) {
+	if (result == OFFCHANNEL_SEND_ACTION_SUCCESS ||
+	    result == OFFCHANNEL_SEND_ACTION_NO_ACK) {
 		eloop_cancel_timeout(gas_query_timeout, gas, query);
-		eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
-				       gas_query_timeout, gas, query);
+		if (result == OFFCHANNEL_SEND_ACTION_NO_ACK) {
+			wpa_printf(MSG_DEBUG, "GAS: No ACK to GAS request");
+			eloop_register_timeout(0, 250000,
+					       gas_query_timeout, gas, query);
+		} else {
+			eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
+					       gas_query_timeout, gas, query);
+		}
 		if (query->wait_comeback && !query->retry) {
 			eloop_cancel_timeout(gas_query_rx_comeback_timeout,
 					     gas, query);
diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c
index 858636f..f9b7e4c 100644
--- a/wpa_supplicant/hs20_supplicant.c
+++ b/wpa_supplicant/hs20_supplicant.c
@@ -430,10 +430,9 @@
 	dl_list_for_each(icon, &wpa_s->icon_head, struct icon_entry, list) {
 		if (icon->dialog_token == dialog_token && !icon->image &&
 		    os_memcmp(icon->bssid, sa, ETH_ALEN) == 0) {
-			icon->image = os_malloc(slen);
+			icon->image = os_memdup(pos, slen);
 			if (!icon->image)
 				return -1;
-			os_memcpy(icon->image, pos, slen);
 			icon->image_len = slen;
 			hs20_remove_duplicate_icons(wpa_s, icon);
 			wpa_msg(wpa_s, MSG_INFO,
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index 954061a..00919d1 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -259,9 +259,13 @@
 
 
 static const u8 * auth_get_psk(void *ctx, const u8 *addr,
-			       const u8 *p2p_dev_addr, const u8 *prev_psk)
+			       const u8 *p2p_dev_addr, const u8 *prev_psk,
+			       size_t *psk_len)
 {
 	struct ibss_rsn *ibss_rsn = ctx;
+
+	if (psk_len)
+		*psk_len = PMK_LEN;
 	wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)",
 		   __func__, MAC2STR(addr), prev_psk);
 	if (prev_psk)
@@ -458,7 +462,7 @@
 				"\x00\x0f\xac\x04"
 				"\x01\x00\x00\x0f\xac\x04"
 				"\x01\x00\x00\x0f\xac\x02"
-				"\x00\x00", 22, NULL, 0) !=
+				"\x00\x00", 22, NULL, 0, NULL, 0) !=
 	    WPA_IE_OK) {
 		wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
 		return -1;
@@ -760,10 +764,9 @@
 	if (supp < 0)
 		return -1;
 
-	tmp = os_malloc(len);
+	tmp = os_memdup(buf, len);
 	if (tmp == NULL)
 		return -1;
-	os_memcpy(tmp, buf, len);
 	if (supp) {
 		peer->authentication_status |= IBSS_RSN_AUTH_EAPOL_BY_PEER;
 		wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Supplicant from "
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index fd2b02e..4c79356 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -106,10 +106,12 @@
 	if (buf == NULL)
 		return NULL;
 
-	len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST);
-	for (i = 0; i < num_ids; i++)
-		wpabuf_put_le16(buf, info_ids[i]);
-	gas_anqp_set_element_len(buf, len_pos);
+	if (num_ids > 0) {
+		len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST);
+		for (i = 0; i < num_ids; i++)
+			wpabuf_put_le16(buf, info_ids[i]);
+		gas_anqp_set_element_len(buf, len_pos);
+	}
 	if (extra)
 		wpabuf_put_buf(buf, extra);
 
@@ -1769,9 +1771,10 @@
 	switch (eap->method) {
 	case EAP_TYPE_TTLS:
 		if (eap->inner_method) {
-			os_snprintf(buf, sizeof(buf), "\"autheap=%s\"",
-				    eap_get_name(EAP_VENDOR_IETF,
-						 eap->inner_method));
+			name = eap_get_name(EAP_VENDOR_IETF, eap->inner_method);
+			if (!name)
+				goto fail;
+			os_snprintf(buf, sizeof(buf), "\"autheap=%s\"", name);
 			if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
 				goto fail;
 			break;
@@ -1894,7 +1897,7 @@
 		size_t len;
 		wpa_msg(wpa_s, MSG_DEBUG,
 			"Interworking: IMSI not available - try to read again through eap_proxy");
-		wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol,
+		wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, -1,
 							     wpa_s->imsi,
 							     &len);
 		if (wpa_s->mnc_len > 0) {
@@ -2694,7 +2697,7 @@
 
 int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
 		  u16 info_ids[], size_t num_ids, u32 subtypes,
-		  int get_cell_pref)
+		  u32 mbo_subtypes)
 {
 	struct wpabuf *buf;
 	struct wpabuf *extra_buf = NULL;
@@ -2728,13 +2731,14 @@
 #endif /* CONFIG_HS20 */
 
 #ifdef CONFIG_MBO
-	if (get_cell_pref) {
+	if (mbo_subtypes) {
 		struct wpabuf *mbo;
 
-		mbo = mbo_build_anqp_buf(wpa_s, bss);
+		mbo = mbo_build_anqp_buf(wpa_s, bss, mbo_subtypes);
 		if (mbo) {
 			if (wpabuf_resize(&extra_buf, wpabuf_len(mbo))) {
 				wpabuf_free(extra_buf);
+				wpabuf_free(mbo);
 				return -1;
 			}
 			wpabuf_put_buf(extra_buf, mbo);
@@ -2805,9 +2809,7 @@
 {
 	const u8 *pos = data;
 	struct wpa_bss_anqp *anqp = NULL;
-#ifdef CONFIG_HS20
 	u8 type;
-#endif /* CONFIG_HS20 */
 
 	if (bss)
 		anqp = bss->anqp;
@@ -2910,7 +2912,6 @@
 			return;
 
 		switch (WPA_GET_BE24(pos)) {
-#ifdef CONFIG_HS20
 		case OUI_WFA:
 			pos += 3;
 			slen -= 3;
@@ -2921,19 +2922,26 @@
 			slen--;
 
 			switch (type) {
+#ifdef CONFIG_HS20
 			case HS20_ANQP_OUI_TYPE:
 				hs20_parse_rx_hs20_anqp_resp(wpa_s, bss, sa,
 							     pos, slen,
 							     dialog_token);
 				break;
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+			case MBO_ANQP_OUI_TYPE:
+				mbo_parse_rx_anqp_resp(wpa_s, bss, sa,
+						       pos, slen);
+				break;
+#endif /* CONFIG_MBO */
 			default:
 				wpa_msg(wpa_s, MSG_DEBUG,
-					"HS20: Unsupported ANQP vendor type %u",
+					"ANQP: Unsupported ANQP vendor type %u",
 					type);
 				break;
 			}
 			break;
-#endif /* CONFIG_HS20 */
 		default:
 			wpa_msg(wpa_s, MSG_DEBUG,
 				"Interworking: Unsupported vendor-specific ANQP OUI %06x",
diff --git a/wpa_supplicant/interworking.h b/wpa_supplicant/interworking.h
index 3d22292..37ee2e9 100644
--- a/wpa_supplicant/interworking.h
+++ b/wpa_supplicant/interworking.h
@@ -13,7 +13,7 @@
 
 int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
 		  u16 info_ids[], size_t num_ids, u32 subtypes,
-		  int get_cell_pref);
+		  u32 mbo_subtypes);
 void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
 		  enum gas_query_result result,
 		  const struct wpabuf *adv_proto,
diff --git a/wpa_supplicant/mbo.c b/wpa_supplicant/mbo.c
index d20ae5e..129d205 100644
--- a/wpa_supplicant/mbo.c
+++ b/wpa_supplicant/mbo.c
@@ -154,7 +154,8 @@
 	struct wpabuf *mbo;
 	int res;
 
-	if (len < MBO_IE_HEADER + 3 + 7)
+	if (len < MBO_IE_HEADER + 3 + 7 +
+	    ((wpa_s->enable_oce & OCE_STA) ? 3 : 0))
 		return 0;
 
 	/* Leave room for the MBO IE header */
@@ -173,9 +174,16 @@
 	wpabuf_put_u8(mbo, 1);
 	wpabuf_put_u8(mbo, wpa_s->conf->mbo_cell_capa);
 
+	/* Add OCE capability indication attribute if OCE is enabled */
+	if (wpa_s->enable_oce & OCE_STA) {
+		wpabuf_put_u8(mbo, OCE_ATTR_ID_CAPA_IND);
+		wpabuf_put_u8(mbo, 1);
+		wpabuf_put_u8(mbo, OCE_RELEASE);
+	}
+
 	res = mbo_add_ie(buf, len, wpabuf_head_u8(mbo), wpabuf_len(mbo));
 	if (!res)
-		wpa_printf(MSG_ERROR, "Failed to add MBO IE");
+		wpa_printf(MSG_ERROR, "Failed to add MBO/OCE IE");
 
 	wpabuf_free(mbo);
 	return res;
@@ -277,11 +285,10 @@
 		   non_pref_chan ? non_pref_chan : "N/A");
 
 	/*
-	 * The shortest channel configuration is 10 characters - commas, 3
-	 * colons, and 4 values that one of them (oper_class) is 2 digits or
-	 * more.
+	 * The shortest channel configuration is 7 characters - 3 colons and
+	 * 4 values.
 	 */
-	if (!non_pref_chan || os_strlen(non_pref_chan) < 10)
+	if (!non_pref_chan || os_strlen(non_pref_chan) < 7)
 		goto update;
 
 	cmd = os_strdup(non_pref_chan);
@@ -369,21 +376,30 @@
 
 void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie)
 {
+	u8 *len;
+
 	wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
-	wpabuf_put_u8(ie, 7);
+	len = wpabuf_put(ie, 1);
+
 	wpabuf_put_be24(ie, OUI_WFA);
 	wpabuf_put_u8(ie, MBO_OUI_TYPE);
 
 	wpabuf_put_u8(ie, MBO_ATTR_ID_CELL_DATA_CAPA);
 	wpabuf_put_u8(ie, 1);
 	wpabuf_put_u8(ie, wpa_s->conf->mbo_cell_capa);
+	if (wpa_s->enable_oce & OCE_STA) {
+		wpabuf_put_u8(ie, OCE_ATTR_ID_CAPA_IND);
+		wpabuf_put_u8(ie, 1);
+		wpabuf_put_u8(ie, OCE_RELEASE);
+	}
+	*len = (u8 *) wpabuf_put(ie, 0) - len - 1;
 }
 
 
 void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie,
 			   size_t len)
 {
-	const u8 *pos, *cell_pref = NULL, *reason = NULL;
+	const u8 *pos, *cell_pref = NULL;
 	u8 id, elen;
 	u16 disallowed_sec = 0;
 
@@ -418,7 +434,8 @@
 			if (elen != 1)
 				goto fail;
 
-			reason = pos;
+			wpa_s->wnm_mbo_trans_reason_present = 1;
+			wpa_s->wnm_mbo_transition_reason = *pos;
 			break;
 		case MBO_ATTR_ID_ASSOC_RETRY_DELAY:
 			if (elen != 2)
@@ -432,6 +449,9 @@
 			} else if (wpa_s->wnm_mode &
 				   WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
 				disallowed_sec = WPA_GET_LE16(pos);
+				wpa_printf(MSG_DEBUG,
+					   "MBO: Association retry delay: %u",
+					   disallowed_sec);
 			} else {
 				wpa_printf(MSG_DEBUG,
 					   "MBO: Association retry delay attribute not in disassoc imminent mode");
@@ -461,9 +481,9 @@
 		wpa_msg(wpa_s, MSG_INFO, MBO_CELL_PREFERENCE "preference=%u",
 			*cell_pref);
 
-	if (reason)
+	if (wpa_s->wnm_mbo_trans_reason_present)
 		wpa_msg(wpa_s, MSG_INFO, MBO_TRANSITION_REASON "reason=%u",
-			*reason);
+			wpa_s->wnm_mbo_transition_reason);
 
 	if (disallowed_sec && wpa_s->current_bss)
 		wpa_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid,
@@ -515,10 +535,11 @@
 
 
 struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s,
-				   struct wpa_bss *bss)
+				   struct wpa_bss *bss, u32 mbo_subtypes)
 {
 	struct wpabuf *anqp_buf;
 	u8 *len_pos;
+	u8 i;
 
 	if (!wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE)) {
 		wpa_printf(MSG_INFO, "MBO: " MACSTR
@@ -527,7 +548,8 @@
 		return NULL;
 	}
 
-	anqp_buf = wpabuf_alloc(10);
+	/* Allocate size for the maximum case - all MBO subtypes are set */
+	anqp_buf = wpabuf_alloc(9 + MAX_MBO_ANQP_SUBTYPE);
 	if (!anqp_buf)
 		return NULL;
 
@@ -535,8 +557,43 @@
 	wpabuf_put_be24(anqp_buf, OUI_WFA);
 	wpabuf_put_u8(anqp_buf, MBO_ANQP_OUI_TYPE);
 
-	wpabuf_put_u8(anqp_buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF);
+	wpabuf_put_u8(anqp_buf, MBO_ANQP_SUBTYPE_QUERY_LIST);
+
+	/* The first valid MBO subtype is 1 */
+	for (i = 1; i <= MAX_MBO_ANQP_SUBTYPE; i++) {
+		if (mbo_subtypes & BIT(i))
+			wpabuf_put_u8(anqp_buf, i);
+	}
+
 	gas_anqp_set_element_len(anqp_buf, len_pos);
 
 	return anqp_buf;
 }
+
+
+void mbo_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
+			    struct wpa_bss *bss, const u8 *sa,
+			    const u8 *data, size_t slen)
+{
+	const u8 *pos = data;
+	u8 subtype;
+
+	if (slen < 1)
+		return;
+
+	subtype = *pos++;
+	slen--;
+
+	switch (subtype) {
+	case MBO_ANQP_SUBTYPE_CELL_CONN_PREF:
+		if (slen < 1)
+			break;
+		wpa_msg(wpa_s, MSG_INFO, RX_MBO_ANQP MACSTR
+			" cell_conn_pref=%u", MAC2STR(sa), *pos);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "MBO: Unsupported ANQP subtype %u",
+			   subtype);
+		break;
+	}
+}
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
index 2ca81a3..c0c8f91 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -84,6 +84,7 @@
 			MESH_CONF_SEC_AMPE;
 	else
 		conf->security |= MESH_CONF_SEC_NONE;
+#ifdef CONFIG_IEEE80211W
 	conf->ieee80211w = ssid->ieee80211w;
 	if (conf->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT) {
 		if (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP)
@@ -91,6 +92,7 @@
 		else
 			conf->ieee80211w = NO_MGMT_FRAME_PROTECTION;
 	}
+#endif /* CONFIG_IEEE80211W */
 
 	cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher, 0);
 	if (cipher < 0 || cipher == WPA_CIPHER_TKIP) {
@@ -257,11 +259,10 @@
 		 * advertised in beacons match the one in peering frames, sigh.
 		 */
 		if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G) {
-			conf->basic_rates = os_malloc(sizeof(basic_rates_erp));
+			conf->basic_rates = os_memdup(basic_rates_erp,
+						      sizeof(basic_rates_erp));
 			if (!conf->basic_rates)
 				goto out_free;
-			os_memcpy(conf->basic_rates, basic_rates_erp,
-				  sizeof(basic_rates_erp));
 		}
 	} else {
 		rate_len = 0;
@@ -304,11 +305,10 @@
 			wpas_mesh_copy_groups(bss, wpa_s);
 		} else {
 			bss->conf->sae_groups =
-				os_malloc(sizeof(default_groups));
+				os_memdup(default_groups,
+					  sizeof(default_groups));
 			if (!bss->conf->sae_groups)
 				goto out_free;
-			os_memcpy(bss->conf->sae_groups, default_groups,
-				  sizeof(default_groups));
 		}
 
 		len = os_strlen(ssid->passphrase);
@@ -413,6 +413,10 @@
 	else if (wpa_s->conf->dtim_period > 0)
 		params.dtim_period = wpa_s->conf->dtim_period;
 	params.conf.max_peer_links = wpa_s->conf->max_peer_links;
+	if (ssid->mesh_rssi_threshold < DEFAULT_MESH_RSSI_THRESHOLD) {
+		params.conf.rssi_threshold = ssid->mesh_rssi_threshold;
+		params.conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD;
+	}
 
 	if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
 		params.flags |= WPA_DRIVER_MESH_FLAG_SAE_AUTH;
diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c
index f152044..bc3cc5e 100644
--- a/wpa_supplicant/mesh_mpm.c
+++ b/wpa_supplicant/mesh_mpm.c
@@ -20,6 +20,7 @@
 #include "driver_i.h"
 #include "mesh_mpm.h"
 #include "mesh_rsn.h"
+#include "notify.h"
 
 struct mesh_peer_mgmt_ie {
 	const u8 *proto_id; /* Mesh Peering Protocol Identifier (2 octets) */
@@ -857,6 +858,9 @@
 	/* Send ctrl event */
 	wpa_msg(wpa_s, MSG_INFO, MESH_PEER_CONNECTED MACSTR,
 		MAC2STR(sta->addr));
+
+	/* Send D-Bus event */
+	wpas_notify_mesh_peer_connected(wpa_s, sta->addr);
 }
 
 
@@ -1009,6 +1013,10 @@
 			wpa_msg(wpa_s, MSG_INFO, MESH_PEER_DISCONNECTED MACSTR,
 				MAC2STR(sta->addr));
 
+			/* Send D-Bus event */
+			wpas_notify_mesh_peer_disconnected(wpa_s, sta->addr,
+							   reason);
+
 			hapd->num_plinks--;
 
 			mesh_mpm_send_plink_action(wpa_s, sta,
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index 628382c..25dcde5 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -75,12 +75,15 @@
 
 
 static const u8 *auth_get_psk(void *ctx, const u8 *addr,
-			      const u8 *p2p_dev_addr, const u8 *prev_psk)
+			      const u8 *p2p_dev_addr, const u8 *prev_psk,
+			      size_t *psk_len)
 {
 	struct mesh_rsn *mesh_rsn = ctx;
 	struct hostapd_data *hapd = mesh_rsn->wpa_s->ifmsh->bss[0];
 	struct sta_info *sta = ap_get_sta(hapd, addr);
 
+	if (psk_len)
+		*psk_len = PMK_LEN;
 	wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)",
 		   __func__, MAC2STR(addr), prev_psk);
 
@@ -314,7 +317,12 @@
 				     struct wpa_ssid *ssid,
 				     struct sta_info *sta)
 {
-	if (ssid->passphrase == NULL) {
+	const char *password;
+
+	password = ssid->sae_password;
+	if (!password)
+		password = ssid->passphrase;
+	if (!password) {
 		wpa_msg(wpa_s, MSG_DEBUG, "SAE: No password available");
 		return -1;
 	}
@@ -325,8 +333,8 @@
 	}
 
 	return sae_prepare_commit(wpa_s->own_addr, sta->addr,
-				  (u8 *) ssid->passphrase,
-				  os_strlen(ssid->passphrase), sta->sae);
+				  (u8 *) password, os_strlen(password),
+				  sta->sae);
 }
 
 
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 9464c4b..a5db82c 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -972,3 +972,49 @@
 							url);
 #endif /* CONFIG_HS20 */
 }
+
+
+#ifdef CONFIG_MESH
+
+void wpas_notify_mesh_group_started(struct wpa_supplicant *wpa_s,
+				    struct wpa_ssid *ssid)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_signal_mesh_group_started(wpa_s, ssid);
+}
+
+
+void wpas_notify_mesh_group_removed(struct wpa_supplicant *wpa_s,
+				    const u8 *meshid, u8 meshid_len,
+				    int reason_code)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_signal_mesh_group_removed(wpa_s, meshid, meshid_len,
+					    reason_code);
+}
+
+
+void wpas_notify_mesh_peer_connected(struct wpa_supplicant *wpa_s,
+				     const u8 *peer_addr)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_signal_mesh_peer_connected(wpa_s, peer_addr);
+}
+
+
+void wpas_notify_mesh_peer_disconnected(struct wpa_supplicant *wpa_s,
+					const u8 *peer_addr, int reason_code)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_signal_mesh_peer_disconnected(wpa_s, peer_addr, reason_code);
+}
+
+#endif /* CONFIG_MESH */
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index 9c98524..26b07f5 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -145,6 +145,15 @@
 void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s,
 					 const u8 *sa, const u8 *go_dev_addr,
 					 const u8 *bssid, int id, int op_freq);
+void wpas_notify_mesh_group_started(struct wpa_supplicant *wpa_s,
+				    struct wpa_ssid *ssid);
+void wpas_notify_mesh_group_removed(struct wpa_supplicant *wpa_s,
+				    const u8 *meshid, u8 meshid_len,
+				    int reason_code);
+void wpas_notify_mesh_peer_connected(struct wpa_supplicant *wpa_s,
+				     const u8 *peer_addr);
+void wpas_notify_mesh_peer_disconnected(struct wpa_supplicant *wpa_s,
+					const u8 *peer_addr, int reason_code);
 void wpas_notify_anqp_query_done(struct wpa_supplicant *wpa_s, const u8* bssid,
 				 const char* result,
 				 const struct wpa_bss_anqp *anqp);
diff --git a/wpa_supplicant/offchannel.c b/wpa_supplicant/offchannel.c
index 26d41a4..b74be7d 100644
--- a/wpa_supplicant/offchannel.c
+++ b/wpa_supplicant/offchannel.c
@@ -310,6 +310,8 @@
 
 		iface = wpas_get_tx_interface(wpa_s, src);
 		wpa_s->action_tx_wait_time = wait_time;
+		if (wait_time)
+			wpa_s->action_tx_wait_time_used = 1;
 
 		ret = wpa_drv_send_action(
 			iface, wpa_s->pending_action_freq,
@@ -398,13 +400,14 @@
 	wpabuf_free(wpa_s->pending_action_tx);
 	wpa_s->pending_action_tx = NULL;
 	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX &&
-	    wpa_s->action_tx_wait_time)
+	    (wpa_s->action_tx_wait_time || wpa_s->action_tx_wait_time_used))
 		wpa_drv_send_action_cancel_wait(wpa_s);
 	else if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
 		wpa_drv_cancel_remain_on_channel(wpa_s);
 		wpa_s->off_channel_freq = 0;
 		wpa_s->roc_waiting_drv_freq = 0;
 	}
+	wpa_s->action_tx_wait_time_used = 0;
 }
 
 
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index a341549..0eccd52 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -1320,6 +1320,10 @@
 	if (wpa_s->p2p_go_group_formation_completed) {
 		wpa_s->global->p2p_group_formation = NULL;
 		wpa_s->p2p_in_provisioning = 0;
+	} else if (wpa_s->p2p_in_provisioning && !success) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"P2P: Stop provisioning state due to failure");
+		wpa_s->p2p_in_provisioning = 0;
 	}
 	wpa_s->p2p_in_invitation = 0;
 	wpa_s->group_formation_reported = 1;
@@ -1998,6 +2002,11 @@
 		d->wps_nfc_dh_pubkey = wpabuf_dup(s->wps_nfc_dh_pubkey);
 	}
 	d->p2p_cli_probe = s->p2p_cli_probe;
+	d->go_interworking = s->go_interworking;
+	d->go_access_network_type = s->go_access_network_type;
+	d->go_internet = s->go_internet;
+	d->go_venue_group = s->go_venue_group;
+	d->go_venue_type = s->go_venue_type;
 }
 
 
@@ -5626,8 +5635,10 @@
 		if (!res && size > 0) {
 			i = 0;
 			while (i < size &&
-			       wpas_p2p_disallowed_freq(wpa_s->global,
-							pref_freq_list[i])) {
+			       (!p2p_supported_freq(wpa_s->global->p2p,
+						    pref_freq_list[i]) ||
+				wpas_p2p_disallowed_freq(wpa_s->global,
+							 pref_freq_list[i]))) {
 				wpa_printf(MSG_DEBUG,
 					   "P2P: preferred_freq_list[%d]=%d is disallowed",
 					   i, pref_freq_list[i]);
@@ -5729,30 +5740,6 @@
 {
 	unsigned int i, r;
 
-	/* first try some random selection of the social channels */
-	if (os_get_random((u8 *) &r, sizeof(r)) < 0)
-		return;
-
-	for (i = 0; i < 3; i++) {
-		params->freq = 2412 + ((r + i) % 3) * 25;
-		if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
-			goto out;
-	}
-
-	/* try all other channels in operating class 81 */
-	for (i = 0; i < 11; i++) {
-		params->freq = 2412 + i * 5;
-
-		/* skip social channels; covered in the previous loop */
-		if (params->freq == 2412 ||
-		    params->freq == 2437 ||
-		    params->freq == 2462)
-			continue;
-
-		if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
-			goto out;
-	}
-
 	/* try all channels in operating class 115 */
 	for (i = 0; i < 4; i++) {
 		params->freq = 5180 + i * 20;
@@ -5787,6 +5774,30 @@
 			goto out;
 	}
 
+	/* try some random selection of the social channels */
+	if (os_get_random((u8 *) &r, sizeof(r)) < 0)
+		return;
+
+	for (i = 0; i < 3; i++) {
+		params->freq = 2412 + ((r + i) % 3) * 25;
+		if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
+			goto out;
+	}
+
+	/* try all other channels in operating class 81 */
+	for (i = 0; i < 11; i++) {
+		params->freq = 2412 + i * 5;
+
+		/* skip social channels; covered in the previous loop */
+		if (params->freq == 2412 ||
+		    params->freq == 2437 ||
+		    params->freq == 2462)
+			continue;
+
+		if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
+			goto out;
+	}
+
 	params->freq = 0;
 	wpa_printf(MSG_DEBUG, "P2P: No 2.4, 5, or 60 GHz channel allowed");
 	return;
@@ -5846,12 +5857,30 @@
 
 	/* try using the forced freq */
 	if (freq) {
-		if (!wpas_p2p_supported_freq_go(wpa_s, channels, freq)) {
+		if (wpas_p2p_disallowed_freq(wpa_s->global, freq) ||
+		    !freq_included(wpa_s, channels, freq)) {
 			wpa_printf(MSG_DEBUG,
-				   "P2P: Forced GO freq %d MHz not accepted",
+				   "P2P: Forced GO freq %d MHz disallowed",
 				   freq);
 			goto fail;
 		}
+		if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
+			if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+			    ieee80211_is_dfs(freq)) {
+				/*
+				 * If freq is a DFS channel and DFS is offloaded
+				 * to the driver, allow P2P GO to use it.
+				 */
+				wpa_printf(MSG_DEBUG,
+					   "P2P: %s: The forced channel for GO (%u MHz) requires DFS and DFS is offloaded",
+					   __func__, freq);
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "P2P: The forced channel for GO (%u MHz) is not supported for P2P uses",
+					   freq);
+				goto fail;
+			}
+		}
 
 		for (i = 0; i < num; i++) {
 			if (freqs[i].freq == freq) {
@@ -6090,24 +6119,7 @@
 	if (wpas_p2p_init_go_params(wpa_s, &params, freq, vht_center_freq2,
 				    ht40, vht, max_oper_chwidth, NULL))
 		return -1;
-	if (params.freq &&
-	    !p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) {
-		if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
-		    ieee80211_is_dfs(params.freq)) {
-			/*
-			 * If freq is a DFS channel and DFS is offloaded to the
-			 * driver, allow P2P GO to use it.
-			 */
-			wpa_printf(MSG_DEBUG,
-				   "P2P: %s: The forced channel for GO (%u MHz) is DFS, and DFS is offloaded to driver",
-				__func__, params.freq);
-		} else {
-			wpa_printf(MSG_DEBUG,
-				   "P2P: The selected channel for GO (%u MHz) is not supported for P2P uses",
-				   params.freq);
-			return -1;
-		}
-	}
+
 	p2p_go_params(wpa_s->global->p2p, &params);
 	params.persistent_group = persistent_group;
 
@@ -6583,8 +6595,14 @@
 	wpa_s->p2p_long_listen = 0;
 
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL ||
-	    wpa_s->p2p_in_provisioning)
+	    wpa_s->p2p_in_provisioning) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Reject p2p_find operation%s%s",
+			(wpa_s->global->p2p_disabled || !wpa_s->global->p2p) ?
+			" (P2P disabled)" : "",
+			wpa_s->p2p_in_provisioning ?
+			" (p2p_in_provisioning)" : "");
 		return -1;
+	}
 
 	wpa_supplicant_cancel_sched_scan(wpa_s);
 
@@ -9065,16 +9083,20 @@
 	unsigned int i, invalid_freq = 0, policy_move = 0, flags = 0;
 	unsigned int timeout;
 	int freq;
+	int dfs_offload;
 
 	wpas_p2p_go_update_common_freqs(wpa_s);
 
 	freq = wpa_s->current_ssid->frequency;
+	dfs_offload = (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+		ieee80211_is_dfs(freq);
 	for (i = 0, invalid_freq = 0; i < num; i++) {
 		if (freqs[i].freq == freq) {
 			flags = freqs[i].flags;
 
 			/* The channel is invalid, must change it */
-			if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
+			if (!p2p_supported_freq_go(wpa_s->global->p2p, freq) &&
+			    !dfs_offload) {
 				wpa_dbg(wpa_s, MSG_DEBUG,
 					"P2P: Freq=%d MHz no longer valid for GO",
 					freq);
@@ -9084,7 +9106,7 @@
 			/* Freq is not used by any other station interface */
 			continue;
 		} else if (!p2p_supported_freq(wpa_s->global->p2p,
-					       freqs[i].freq)) {
+					       freqs[i].freq) && !dfs_offload) {
 			/* Freq is not valid for P2P use cases */
 			continue;
 		} else if (wpa_s->conf->p2p_go_freq_change_policy ==
diff --git a/wpa_supplicant/preauth_test.c b/wpa_supplicant/preauth_test.c
index 6ae239f..a213a30 100644
--- a/wpa_supplicant/preauth_test.c
+++ b/wpa_supplicant/preauth_test.c
@@ -144,7 +144,9 @@
 
 
 static int wpa_supplicant_add_pmkid(void *wpa_s, void *network_ctx,
-				    const u8 *bssid, const u8 *pmkid)
+				    const u8 *bssid, const u8 *pmkid,
+				    const u8 *fils_cache_id,
+				    const u8 *pmk, size_t pmk_len)
 {
 	printf("%s - not implemented\n", __func__);
 	return -1;
@@ -152,7 +154,8 @@
 
 
 static int wpa_supplicant_remove_pmkid(void *wpa_s, void *network_ctx,
-				       const u8 *bssid, const u8 *pmkid)
+				       const u8 *bssid, const u8 *pmkid,
+				       const u8 *fils_cache_id)
 {
 	printf("%s - not implemented\n", __func__);
 	return -1;
@@ -344,8 +347,8 @@
 	if (preauth_test.auth_timed_out)
 		ret = -2;
 	else {
-		ret = pmksa_cache_set_current(wpa_s.wpa, NULL, bssid, NULL, 0)
-			? 0 : -3;
+		ret = pmksa_cache_set_current(wpa_s.wpa, NULL, bssid, NULL, 0,
+					      NULL) ? 0 : -3;
 	}
 
 	test_eapol_clean(&wpa_s);
diff --git a/wpa_supplicant/rrm.c b/wpa_supplicant/rrm.c
index 5be917c..f4fbfa7 100644
--- a/wpa_supplicant/rrm.c
+++ b/wpa_supplicant/rrm.c
@@ -355,7 +355,8 @@
 	return 0;
 
 reject:
-	if (wpas_rrm_report_elem(buf, req->token,
+	if (!is_multicast_ether_addr(wpa_s->rrm.dst_addr) &&
+	    wpas_rrm_report_elem(buf, req->token,
 				 MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE,
 				 req->type, NULL, 0) < 0) {
 		wpa_printf(MSG_DEBUG, "RRM: Failed to add report element");
@@ -409,7 +410,8 @@
 			pos = next;
 		}
 
-		next += next[1] + 2;
+		if (len)
+			next += next[1] + 2;
 	}
 #undef MPDU_REPORT_LEN
 }
@@ -627,6 +629,7 @@
 	if (ext_freqs) {
 		int_array_concat(&freqs, ext_freqs);
 		os_free(ext_freqs);
+		int_array_sort_unique(freqs);
 	}
 
 	return freqs;
@@ -793,7 +796,7 @@
 	u8 *ie = (u8 *) (bss + 1);
 	size_t ie_len = bss->ie_len + bss->beacon_ie_len;
 	int ret;
-	u8 buf[2000];
+	u8 *buf;
 	struct rrm_measurement_beacon_report *rep;
 
 	if (os_memcmp(data->bssid, broadcast_ether_addr, ETH_ALEN) != 0 &&
@@ -805,10 +808,18 @@
 	     os_memcmp(data->ssid, bss->ssid, bss->ssid_len) != 0))
 		return 0;
 
+	/* Maximum element length: beacon report element + reported frame body
+	 * subelement + all IEs of the reported beacon */
+	buf = os_malloc(sizeof(*rep) + 14 + ie_len);
+	if (!buf)
+		return -1;
+
 	rep = (struct rrm_measurement_beacon_report *) buf;
 	if (wpas_get_op_chan_phy(bss->freq, ie, ie_len, &rep->op_class,
-				 &rep->channel, &rep->report_info) < 0)
-		return 0;
+				 &rep->channel, &rep->report_info) < 0) {
+		ret = 0;
+		goto out;
+	}
 
 	rep->start_time = host_to_le64(start);
 	rep->duration = host_to_le16(data->scan_params.duration);
@@ -819,15 +830,17 @@
 	rep->parent_tsf = host_to_le32(parent_tsf);
 
 	ret = wpas_beacon_rep_add_frame_body(data->eids, data->report_detail,
-					     bss, rep->variable,
-					     sizeof(buf) - sizeof(*rep));
+					     bss, rep->variable, 14 + ie_len);
 	if (ret < 0)
-		return -1;
+		goto out;
 
-	return wpas_rrm_report_elem(wpa_buf, wpa_s->beacon_rep_data.token,
-				    MEASUREMENT_REPORT_MODE_ACCEPT,
-				    MEASURE_TYPE_BEACON, buf,
-				    ret + sizeof(*rep));
+	ret = wpas_rrm_report_elem(wpa_buf, wpa_s->beacon_rep_data.token,
+				   MEASUREMENT_REPORT_MODE_ACCEPT,
+				   MEASURE_TYPE_BEACON, buf,
+				   ret + sizeof(*rep));
+out:
+	os_free(buf);
+	return ret;
 }
 
 
@@ -858,21 +871,24 @@
 }
 
 
-static void wpas_rrm_refuse_request(struct wpa_supplicant *wpa_s)
+void wpas_rrm_refuse_request(struct wpa_supplicant *wpa_s)
 {
-	struct wpabuf *buf = NULL;
+	if (!is_multicast_ether_addr(wpa_s->rrm.dst_addr)) {
+		struct wpabuf *buf = NULL;
 
-	if (wpas_rrm_report_elem(&buf, wpa_s->beacon_rep_data.token,
-				 MEASUREMENT_REPORT_MODE_REJECT_REFUSED,
-				 MEASURE_TYPE_BEACON, NULL, 0)) {
-		wpa_printf(MSG_ERROR, "RRM: Memory allocation failed");
+		if (wpas_rrm_report_elem(&buf, wpa_s->beacon_rep_data.token,
+					 MEASUREMENT_REPORT_MODE_REJECT_REFUSED,
+					 MEASURE_TYPE_BEACON, NULL, 0)) {
+			wpa_printf(MSG_ERROR, "RRM: Memory allocation failed");
+			wpabuf_free(buf);
+			return;
+		}
+
+		wpas_rrm_send_msr_report(wpa_s, buf);
 		wpabuf_free(buf);
-		return;
 	}
 
-	wpas_rrm_send_msr_report(wpa_s, buf);
 	wpas_clear_beacon_rep_data(wpa_s);
-	wpabuf_free(buf);
 }
 
 
@@ -952,7 +968,7 @@
 		    BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS) {
 			wpa_printf(MSG_DEBUG, "Invalid reporting detail: %u",
 				   subelem[0]);
-			return 0;
+			return -1;
 		}
 
 		break;
@@ -1019,6 +1035,7 @@
 	u32 interval_usec;
 	u32 _rand;
 	int ret = 0, res;
+	u8 reject_mode;
 
 	if (len < sizeof(*req))
 		return -1;
@@ -1052,9 +1069,12 @@
 
 		res = wpas_rm_handle_beacon_req_subelem(
 			wpa_s, data, subelems[0], subelems[1], &subelems[2]);
-		if (res != 1) {
+		if (res < 0) {
 			ret = res;
 			goto out;
+		} else if (!res) {
+			reject_mode = MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE;
+			goto out_reject;
 		}
 
 		elems_len -= 2 + subelems[1];
@@ -1072,7 +1092,8 @@
 		req->variable, len - sizeof(*req));
 	if (!params->freqs) {
 		wpa_printf(MSG_DEBUG, "Beacon request: No valid channels");
-		goto out;
+		reject_mode = MEASUREMENT_REPORT_MODE_REJECT_REFUSED;
+		goto out_reject;
 	}
 
 	params->duration = le_to_host16(req->duration);
@@ -1096,6 +1117,13 @@
 	eloop_register_timeout(0, interval_usec, wpas_rrm_scan_timeout, wpa_s,
 			       NULL);
 	return 1;
+out_reject:
+	if (!is_multicast_ether_addr(wpa_s->rrm.dst_addr) &&
+	    wpas_rrm_report_elem(buf, elem_token, reject_mode,
+				 MEASURE_TYPE_BEACON, NULL, 0) < 0) {
+		wpa_printf(MSG_DEBUG, "RRM: Failed to add report element");
+		ret = -1;
+	}
 out:
 	wpas_clear_beacon_rep_data(wpa_s);
 	return ret;
@@ -1153,7 +1181,8 @@
 	}
 
 reject:
-	if (wpas_rrm_report_elem(buf, req->token,
+	if (!is_multicast_ether_addr(wpa_s->rrm.dst_addr) &&
+	    wpas_rrm_report_elem(buf, req->token,
 				 MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE,
 				 req->type, NULL, 0) < 0) {
 		wpa_printf(MSG_DEBUG, "RRM: Failed to add report element");
@@ -1214,7 +1243,7 @@
 
 
 void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s,
-					       const u8 *src,
+					       const u8 *src, const u8 *dst,
 					       const u8 *frame, size_t len)
 {
 	struct wpabuf *report;
@@ -1238,6 +1267,7 @@
 	}
 
 	wpa_s->rrm.token = *frame;
+	os_memcpy(wpa_s->rrm.dst_addr, dst, ETH_ALEN);
 
 	/* Number of repetitions is not supported */
 
@@ -1365,6 +1395,10 @@
 			continue;
 		}
 
+		/*
+		 * Don't report results that were not received during the
+		 * current measurement.
+		 */
 		if (!(wpa_s->drv_rrm_flags &
 		      WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT)) {
 			struct os_reltime update_time, diff;
@@ -1391,14 +1425,10 @@
 					   (unsigned int) diff.usec);
 				continue;
 			}
-		}
-
-		/*
-		 * Don't report results that were not received during the
-		 * current measurement.
-		 */
-		if (info->scan_start_tsf > scan_res->res[i]->parent_tsf)
+		} else if (info->scan_start_tsf >
+			   scan_res->res[i]->parent_tsf) {
 			continue;
+		}
 
 		if (wpas_add_beacon_rep(wpa_s, &buf, bss, info->scan_start_tsf,
 					scan_res->res[i]->parent_tsf) < 0)
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index 3a100cd..104b258 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -117,9 +117,19 @@
 static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s,
 				     struct wpa_ssid *ssid)
 {
+	int min_temp_disabled = 0;
+
 	while (ssid) {
-		if (!wpas_network_disabled(wpa_s, ssid))
-			break;
+		if (!wpas_network_disabled(wpa_s, ssid)) {
+			int temp_disabled = wpas_temp_disabled(wpa_s, ssid);
+
+			if (temp_disabled <= 0)
+				break;
+
+			if (!min_temp_disabled ||
+			    temp_disabled < min_temp_disabled)
+				min_temp_disabled = temp_disabled;
+		}
 		ssid = ssid->next;
 	}
 
@@ -128,7 +138,7 @@
 		wpa_dbg(wpa_s, MSG_DEBUG, "wpa_supplicant_assoc_try: Reached "
 			"end of scan list - go back to beginning");
 		wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
-		wpa_supplicant_req_scan(wpa_s, 0, 0);
+		wpa_supplicant_req_scan(wpa_s, min_temp_disabled, 0);
 		return;
 	}
 	if (ssid->next) {
@@ -190,7 +200,8 @@
 	wpa_scan_free_params(params);
 	work->ctx = NULL;
 	if (ret) {
-		int retry = wpa_s->last_scan_req != MANUAL_SCAN_REQ;
+		int retry = wpa_s->last_scan_req != MANUAL_SCAN_REQ &&
+			!wpa_s->beacon_rep_data.token;
 
 		if (wpa_s->disconnected)
 			retry = 0;
@@ -208,7 +219,14 @@
 			/* Restore scan_req since we will try to scan again */
 			wpa_s->scan_req = wpa_s->last_scan_req;
 			wpa_supplicant_req_scan(wpa_s, 1, 0);
+		} else if (wpa_s->scan_res_handler) {
+			/* Clear the scan_res_handler */
+			wpa_s->scan_res_handler = NULL;
 		}
+
+		if (wpa_s->beacon_rep_data.token)
+			wpas_rrm_refuse_request(wpa_s);
+
 		return;
 	}
 
@@ -458,8 +476,8 @@
 		wpabuf_put_data(default_ies, ext_capab, ext_capab_len);
 
 #ifdef CONFIG_MBO
-	/* Send cellular capabilities for potential MBO STAs */
-	if (wpabuf_resize(&default_ies, 9) == 0)
+	/* Send MBO and OCE capabilities */
+	if (wpabuf_resize(&default_ies, 12) == 0)
 		wpas_mbo_scan_ie(wpa_s, default_ies);
 #endif /* CONFIG_MBO */
 
@@ -540,8 +558,8 @@
 #endif /* CONFIG_FST */
 
 #ifdef CONFIG_MBO
-	/* Send cellular capabilities for potential MBO STAs */
-	if (wpabuf_resize(&extra_ie, 9) == 0)
+	/* Send MBO and OCE capabilities */
+	if (wpabuf_resize(&extra_ie, 12) == 0)
 		wpas_mbo_scan_ie(wpa_s, extra_ie);
 #endif /* CONFIG_MBO */
 
@@ -1018,6 +1036,13 @@
 		wpa_s->manual_scan_freqs = NULL;
 	}
 
+	if (params.freqs == NULL && wpa_s->select_network_scan_freqs) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Limit select_network scan to specified channels");
+		params.freqs = wpa_s->select_network_scan_freqs;
+		wpa_s->select_network_scan_freqs = NULL;
+	}
+
 	if (params.freqs == NULL && wpa_s->next_scan_freqs) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on previously "
 			"generated frequency list");
@@ -1502,13 +1527,18 @@
 		params.sched_scan_plans_num = 1;
 	}
 
+	params.sched_scan_start_delay = wpa_s->conf->sched_scan_start_delay;
+
 	if (ssid || !wpa_s->first_sched_scan) {
 		wpa_dbg(wpa_s, MSG_DEBUG,
-			"Starting sched scan: interval %u timeout %d",
+			"Starting sched scan after %u seconds: interval %u timeout %d",
+			params.sched_scan_start_delay,
 			params.sched_scan_plans[0].interval,
 			wpa_s->sched_scan_timeout);
 	} else {
-		wpa_dbg(wpa_s, MSG_DEBUG, "Starting sched scan (no timeout)");
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Starting sched scan after %u seconds (no timeout)",
+			params.sched_scan_start_delay);
 	}
 
 	wpa_setband_scan_freqs(wpa_s, scan_params);
@@ -2331,11 +2361,10 @@
 
 	for (i = 0; i < src->num_ssids; i++) {
 		if (src->ssids[i].ssid) {
-			n = os_malloc(src->ssids[i].ssid_len);
+			n = os_memdup(src->ssids[i].ssid,
+				      src->ssids[i].ssid_len);
 			if (n == NULL)
 				goto failed;
-			os_memcpy(n, src->ssids[i].ssid,
-				  src->ssids[i].ssid_len);
 			params->ssids[i].ssid = n;
 			params->ssids[i].ssid_len = src->ssids[i].ssid_len;
 		}
@@ -2343,30 +2372,26 @@
 	params->num_ssids = src->num_ssids;
 
 	if (src->extra_ies) {
-		n = os_malloc(src->extra_ies_len);
+		n = os_memdup(src->extra_ies, src->extra_ies_len);
 		if (n == NULL)
 			goto failed;
-		os_memcpy(n, src->extra_ies, src->extra_ies_len);
 		params->extra_ies = n;
 		params->extra_ies_len = src->extra_ies_len;
 	}
 
 	if (src->freqs) {
 		int len = int_array_len(src->freqs);
-		params->freqs = os_malloc((len + 1) * sizeof(int));
+		params->freqs = os_memdup(src->freqs, (len + 1) * sizeof(int));
 		if (params->freqs == NULL)
 			goto failed;
-		os_memcpy(params->freqs, src->freqs, (len + 1) * sizeof(int));
 	}
 
 	if (src->filter_ssids) {
-		params->filter_ssids = os_malloc(sizeof(*params->filter_ssids) *
+		params->filter_ssids = os_memdup(src->filter_ssids,
+						 sizeof(*params->filter_ssids) *
 						 src->num_filter_ssids);
 		if (params->filter_ssids == NULL)
 			goto failed;
-		os_memcpy(params->filter_ssids, src->filter_ssids,
-			  sizeof(*params->filter_ssids) *
-			  src->num_filter_ssids);
 		params->num_filter_ssids = src->num_filter_ssids;
 	}
 
@@ -2379,14 +2404,12 @@
 
 	if (src->sched_scan_plans_num > 0) {
 		params->sched_scan_plans =
-			os_malloc(sizeof(*src->sched_scan_plans) *
+			os_memdup(src->sched_scan_plans,
+				  sizeof(*src->sched_scan_plans) *
 				  src->sched_scan_plans_num);
 		if (!params->sched_scan_plans)
 			goto failed;
 
-		os_memcpy(params->sched_scan_plans, src->sched_scan_plans,
-			  sizeof(*src->sched_scan_plans) *
-			  src->sched_scan_plans_num);
 		params->sched_scan_plans_num = src->sched_scan_plans_num;
 	}
 
@@ -2411,10 +2434,9 @@
 	if (src->bssid) {
 		u8 *bssid;
 
-		bssid = os_malloc(ETH_ALEN);
+		bssid = os_memdup(src->bssid, ETH_ALEN);
 		if (!bssid)
 			goto failed;
-		os_memcpy(bssid, src->bssid, ETH_ALEN);
 		params->bssid = bssid;
 	}
 
@@ -2586,6 +2608,8 @@
 		params.sched_scan_plans_num = 1;
 	}
 
+	params.sched_scan_start_delay = wpa_s->conf->sched_scan_start_delay;
+
 	if (params.freqs == NULL && wpa_s->manual_sched_scan_freqs) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Limit sched scan to specified channels");
 		params.freqs = wpa_s->manual_sched_scan_freqs;
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index beb9d6e..da0e8eb 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -87,8 +87,25 @@
 {
 	struct wpabuf *buf;
 	size_t len;
+	const char *password;
 
-	if (ssid->passphrase == NULL) {
+#ifdef CONFIG_TESTING_OPTIONS
+	if (wpa_s->sae_commit_override) {
+		wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override");
+		buf = wpabuf_alloc(4 + wpabuf_len(wpa_s->sae_commit_override));
+		if (!buf)
+			return NULL;
+		wpabuf_put_le16(buf, 1); /* Transaction seq# */
+		wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+		wpabuf_put_buf(buf, wpa_s->sae_commit_override);
+		return buf;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	password = ssid->sae_password;
+	if (!password)
+		password = ssid->passphrase;
+	if (!password) {
 		wpa_printf(MSG_DEBUG, "SAE: No password available");
 		return NULL;
 	}
@@ -99,8 +116,7 @@
 	}
 
 	if (sae_prepare_commit(wpa_s->own_addr, bssid,
-			       (u8 *) ssid->passphrase,
-			       os_strlen(ssid->passphrase),
+			       (u8 *) password, os_strlen(password),
 			       &wpa_s->sme.sae) < 0) {
 		wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
 		return NULL;
@@ -208,9 +224,9 @@
 #ifdef CONFIG_IEEE80211R
 	const u8 *ie;
 #endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211R
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS)
 	const u8 *md = NULL;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R || CONFIG_FILS */
 	int i, bssid_changed;
 	struct wpabuf *resp = NULL;
 	u8 ext_capab[18];
@@ -301,13 +317,19 @@
 	     wpa_bss_get_ie(bss, WLAN_EID_RSN)) &&
 	    wpa_key_mgmt_wpa(ssid->key_mgmt)) {
 		int try_opportunistic;
+		const u8 *cache_id = NULL;
+
 		try_opportunistic = (ssid->proactive_key_caching < 0 ?
 				     wpa_s->conf->okc :
 				     ssid->proactive_key_caching) &&
 			(ssid->proto & WPA_PROTO_RSN);
+#ifdef CONFIG_FILS
+		if (wpa_key_mgmt_fils(ssid->key_mgmt))
+			cache_id = wpa_bss_get_fils_cache_id(bss);
+#endif /* CONFIG_FILS */
 		if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
 					    wpa_s->current_ssid,
-					    try_opportunistic) == 0)
+					    try_opportunistic, cache_id) == 0)
 			eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
 		wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
 		if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
@@ -367,7 +389,12 @@
 		wpa_ft_prepare_auth_request(wpa_s->wpa, ie);
 	}
 
-	if (md && wpa_key_mgmt_ft(ssid->key_mgmt)) {
+	if (md && !wpa_key_mgmt_ft(ssid->key_mgmt))
+		md = NULL;
+	if (md) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "SME: FT mobility domain %02x%02x",
+			md[0], md[1]);
+
 		if (wpa_s->sme.assoc_req_ie_len + 5 <
 		    sizeof(wpa_s->sme.assoc_req_ie)) {
 			struct rsn_mdie *mdie;
@@ -516,8 +543,8 @@
 
 #ifdef CONFIG_SAE
 	if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE &&
-	    pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0) == 0)
-	{
+	    pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0,
+				    NULL) == 0) {
 		wpa_dbg(wpa_s, MSG_DEBUG,
 			"PMKSA cache entry found - try to use PMKSA caching instead of new SAE authentication");
 		params.auth_alg = WPA_AUTH_ALG_OPEN;
@@ -551,18 +578,63 @@
 	 * networks). */
 	if (params.auth_alg == WPA_AUTH_ALG_OPEN &&
 	    wpa_key_mgmt_fils(ssid->key_mgmt)) {
+		const u8 *indic;
+		u16 fils_info;
+
+		/*
+		 * Check FILS Indication element (FILS Information field) bits
+		 * indicating supported authentication algorithms against local
+		 * configuration (ssid->fils_dh_group). Try to use FILS
+		 * authentication only if the AP supports the combination in the
+		 * network profile. */
+		indic = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION);
+		if (!indic || indic[1] < 2) {
+			wpa_printf(MSG_DEBUG, "SME: " MACSTR
+				   " does not include FILS Indication element - cannot use FILS authentication with it",
+				   MAC2STR(bss->bssid));
+			goto no_fils;
+		}
+
+		fils_info = WPA_GET_LE16(indic + 2);
+		if (ssid->fils_dh_group == 0 && !(fils_info & BIT(9))) {
+			wpa_printf(MSG_DEBUG, "SME: " MACSTR
+				   " does not support FILS SK without PFS - cannot use FILS authentication with it",
+				   MAC2STR(bss->bssid));
+			goto no_fils;
+		}
+		if (ssid->fils_dh_group != 0 && !(fils_info & BIT(10))) {
+			wpa_printf(MSG_DEBUG, "SME: " MACSTR
+				   " does not support FILS SK with PFS - cannot use FILS authentication with it",
+				   MAC2STR(bss->bssid));
+			goto no_fils;
+		}
+
 		if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
-					    ssid, 0) == 0)
+					    ssid, 0,
+					    wpa_bss_get_fils_cache_id(bss)) ==
+		    0)
 			wpa_printf(MSG_DEBUG,
 				   "SME: Try to use FILS with PMKSA caching");
-		resp = fils_build_auth(wpa_s->wpa);
+		resp = fils_build_auth(wpa_s->wpa, ssid->fils_dh_group, md);
 		if (resp) {
-			params.auth_alg = WPA_AUTH_ALG_FILS;
+			int auth_alg;
+
+			if (ssid->fils_dh_group)
+				wpa_printf(MSG_DEBUG,
+					   "SME: Try to use FILS SK authentication with PFS (DH Group %u)",
+					   ssid->fils_dh_group);
+			else
+				wpa_printf(MSG_DEBUG,
+					   "SME: Try to use FILS SK authentication without PFS");
+			auth_alg = ssid->fils_dh_group ?
+				WPA_AUTH_ALG_FILS_SK_PFS : WPA_AUTH_ALG_FILS;
+			params.auth_alg = auth_alg;
 			params.auth_data = wpabuf_head(resp);
 			params.auth_data_len = wpabuf_len(resp);
-			wpa_s->sme.auth_alg = WPA_AUTH_ALG_FILS;
+			wpa_s->sme.auth_alg = auth_alg;
 		}
 	}
+no_fils:
 #endif /* CONFIG_FILS */
 
 	wpa_supplicant_cancel_sched_scan(wpa_s);
@@ -572,6 +644,7 @@
 		" (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid),
 		wpa_ssid_txt(params.ssid, params.ssid_len), params.freq);
 
+	eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
 	wpa_clear_keys(wpa_s, bss->bssid);
 	wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);
 	if (old_ssid != wpa_s->current_ssid)
@@ -959,9 +1032,29 @@
 #endif /* CONFIG_IEEE80211R */
 
 #ifdef CONFIG_FILS
-	if (data->auth.auth_type == WLAN_AUTH_FILS_SK) {
-		if (fils_process_auth(wpa_s->wpa, data->auth.ies,
-				      data->auth.ies_len) < 0) {
+	if (data->auth.auth_type == WLAN_AUTH_FILS_SK ||
+	    data->auth.auth_type == WLAN_AUTH_FILS_SK_PFS) {
+		u16 expect_auth_type;
+
+		expect_auth_type = wpa_s->sme.auth_alg ==
+			WPA_AUTH_ALG_FILS_SK_PFS ? WLAN_AUTH_FILS_SK_PFS :
+			WLAN_AUTH_FILS_SK;
+		if (data->auth.auth_type != expect_auth_type) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"SME: FILS Authentication response used different auth alg (%u; expected %u)",
+				data->auth.auth_type, expect_auth_type);
+			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid="
+				MACSTR
+				" reason=%d locally_generated=1",
+				MAC2STR(wpa_s->pending_bssid),
+				WLAN_REASON_DEAUTH_LEAVING);
+			wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+			wpa_supplicant_mark_disassoc(wpa_s);
+			return;
+		}
+
+		if (fils_process_auth(wpa_s->wpa, wpa_s->pending_bssid,
+				      data->auth.ies, data->auth.ies_len) < 0) {
 			wpa_dbg(wpa_s, MSG_DEBUG,
 				"SME: FILS Authentication response processing failed");
 			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid="
@@ -981,6 +1074,24 @@
 }
 
 
+#ifdef CONFIG_FILS
+#ifdef CONFIG_IEEE80211R
+static void remove_ie(u8 *buf, size_t *len, u8 eid)
+{
+	u8 *pos, *next, *end;
+
+	pos = (u8 *) get_ie(buf, *len, eid);
+	if (pos) {
+		next = pos + 2 + pos[1];
+		end = buf + *len;
+		*len -= 2 + pos[1];
+		os_memmove(pos, next, end - next);
+	}
+}
+#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_FILS */
+
+
 void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
 		   const u8 *bssid, u16 auth_type)
 {
@@ -1001,7 +1112,8 @@
 	os_memset(&params, 0, sizeof(params));
 
 #ifdef CONFIG_FILS
-	if (auth_type == WLAN_AUTH_FILS_SK) {
+	if (auth_type == WLAN_AUTH_FILS_SK ||
+	    auth_type == WLAN_AUTH_FILS_SK_PFS) {
 		struct wpabuf *buf;
 		const u8 *snonce, *anonce;
 		const unsigned int max_hlp = 20;
@@ -1035,6 +1147,30 @@
 			wpabuf_free(hlp[i]);
 		if (!buf)
 			return;
+		wpa_hexdump(MSG_DEBUG, "FILS: assoc_req before FILS elements",
+			    wpa_s->sme.assoc_req_ie,
+			    wpa_s->sme.assoc_req_ie_len);
+#ifdef CONFIG_IEEE80211R
+		if (wpa_key_mgmt_ft(wpa_s->key_mgmt)) {
+			/* Remove RSNE and MDE to allow them to be overridden
+			 * with FILS+FT specific values from
+			 * fils_build_assoc_req(). */
+			remove_ie(wpa_s->sme.assoc_req_ie,
+				  &wpa_s->sme.assoc_req_ie_len,
+				  WLAN_EID_RSN);
+			wpa_hexdump(MSG_DEBUG,
+				    "FILS: assoc_req after RSNE removal",
+				    wpa_s->sme.assoc_req_ie,
+				    wpa_s->sme.assoc_req_ie_len);
+			remove_ie(wpa_s->sme.assoc_req_ie,
+				  &wpa_s->sme.assoc_req_ie_len,
+				  WLAN_EID_MOBILITY_DOMAIN);
+			wpa_hexdump(MSG_DEBUG,
+				    "FILS: assoc_req after MDE removal",
+				    wpa_s->sme.assoc_req_ie,
+				    wpa_s->sme.assoc_req_ie_len);
+		}
+#endif /* CONFIG_IEEE80211R */
 		/* TODO: Make wpa_s->sme.assoc_req_ie use dynamic allocation */
 		if (wpa_s->sme.assoc_req_ie_len + wpabuf_len(buf) >
 		    sizeof(wpa_s->sme.assoc_req_ie)) {
@@ -1047,6 +1183,9 @@
 			  wpabuf_head(buf), wpabuf_len(buf));
 		wpa_s->sme.assoc_req_ie_len += wpabuf_len(buf);
 		wpabuf_free(buf);
+		wpa_hexdump(MSG_DEBUG, "FILS: assoc_req after FILS elements",
+			    wpa_s->sme.assoc_req_ie,
+			    wpa_s->sme.assoc_req_ie_len);
 
 		os_memcpy(nonces, snonce, FILS_NONCE_LEN);
 		os_memcpy(nonces + FILS_NONCE_LEN, anonce, FILS_NONCE_LEN);
@@ -1055,6 +1194,40 @@
 	}
 #endif /* CONFIG_FILS */
 
+#ifdef CONFIG_OWE
+#ifdef CONFIG_TESTING_OPTIONS
+	if (get_ie_ext(wpa_s->sme.assoc_req_ie, wpa_s->sme.assoc_req_ie_len,
+		       WLAN_EID_EXT_OWE_DH_PARAM)) {
+		wpa_printf(MSG_INFO, "TESTING: Override OWE DH element");
+	} else
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (auth_type == WLAN_AUTH_OPEN &&
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_OWE) {
+		struct wpabuf *owe_ie;
+		u16 group = OWE_DH_GROUP;
+
+		if (wpa_s->current_ssid && wpa_s->current_ssid->owe_group)
+			group = wpa_s->current_ssid->owe_group;
+		owe_ie = owe_build_assoc_req(wpa_s->wpa, group);
+		if (!owe_ie) {
+			wpa_printf(MSG_ERROR,
+				   "OWE: Failed to build IE for Association Request frame");
+			return;
+		}
+		if (wpa_s->sme.assoc_req_ie_len + wpabuf_len(owe_ie) >
+		    sizeof(wpa_s->sme.assoc_req_ie)) {
+			wpa_printf(MSG_ERROR,
+				   "OWE: Not enough buffer room for own Association Request frame elements");
+			wpabuf_free(owe_ie);
+			return;
+		}
+		os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
+			  wpabuf_head(owe_ie), wpabuf_len(owe_ie));
+		wpa_s->sme.assoc_req_ie_len += wpabuf_len(owe_ie);
+		wpabuf_free(owe_ie);
+	}
+#endif /* CONFIG_OWE */
+
 	params.bssid = bssid;
 	params.ssid = wpa_s->sme.ssid;
 	params.ssid_len = wpa_s->sme.ssid_len;
@@ -1066,6 +1239,7 @@
 	params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
 	params.pairwise_suite = wpa_s->pairwise_cipher;
 	params.group_suite = wpa_s->group_cipher;
+	params.mgmt_group_suite = wpa_s->mgmt_group_cipher;
 	params.key_mgmt_suite = wpa_s->key_mgmt;
 	params.wpa_proto = wpa_s->wpa_proto;
 #ifdef CONFIG_HT_OVERRIDES
@@ -1140,6 +1314,14 @@
 
 	eloop_register_timeout(SME_ASSOC_TIMEOUT, 0, sme_assoc_timer, wpa_s,
 			       NULL);
+
+#ifdef CONFIG_TESTING_OPTIONS
+	wpabuf_free(wpa_s->last_assoc_req_wpa_ie);
+	wpa_s->last_assoc_req_wpa_ie = NULL;
+	if (params.wpa_ie)
+		wpa_s->last_assoc_req_wpa_ie =
+			wpabuf_alloc_copy(params.wpa_ie, params.wpa_ie_len);
+#endif /* CONFIG_TESTING_OPTIONS */
 }
 
 
@@ -1158,10 +1340,9 @@
 	os_memcpy(wpa_s->sme.mobility_domain, md, MOBILITY_DOMAIN_ID_LEN);
 	wpa_hexdump(MSG_DEBUG, "SME: FT IEs", ies, ies_len);
 	os_free(wpa_s->sme.ft_ies);
-	wpa_s->sme.ft_ies = os_malloc(ies_len);
+	wpa_s->sme.ft_ies = os_memdup(ies, ies_len);
 	if (wpa_s->sme.ft_ies == NULL)
 		return -1;
-	os_memcpy(wpa_s->sme.ft_ies, ies, ies_len);
 	wpa_s->sme.ft_ies_len = ies_len;
 	return 0;
 }
diff --git a/wpa_supplicant/wifi_display.c b/wpa_supplicant/wifi_display.c
index c363b21..c94e461 100644
--- a/wpa_supplicant/wifi_display.c
+++ b/wpa_supplicant/wifi_display.c
@@ -86,6 +86,7 @@
 		p2p_set_wfd_ie_prov_disc_resp(global->p2p, NULL);
 		p2p_set_wfd_ie_go_neg(global->p2p, NULL);
 		p2p_set_wfd_dev_info(global->p2p, NULL);
+		p2p_set_wfd_r2_dev_info(global->p2p, NULL);
 		p2p_set_wfd_assoc_bssid(global->p2p, NULL);
 		p2p_set_wfd_coupled_sink_info(global->p2p, NULL);
 		return 0;
@@ -93,6 +94,8 @@
 
 	p2p_set_wfd_dev_info(global->p2p,
 			     global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
+	p2p_set_wfd_r2_dev_info(
+		global->p2p, global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO]);
 	p2p_set_wfd_assoc_bssid(
 		global->p2p,
 		global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]);
@@ -133,6 +136,11 @@
 	if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
 		len += wpabuf_len(global->wfd_subelem[
 					  WFD_SUBELEM_DEVICE_INFO]);
+
+	if (global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO])
+		len += wpabuf_len(global->wfd_subelem[
+					  WFD_SUBELEM_R2_DEVICE_INFO]);
+
 	if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
 		len += wpabuf_len(global->wfd_subelem[
 					  WFD_SUBELEM_ASSOCIATED_BSSID]);
@@ -151,6 +159,11 @@
 	if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
 		wpabuf_put_buf(buf,
 			       global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
+
+	if (global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO])
+		wpabuf_put_buf(buf,
+			       global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO]);
+
 	if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
 		wpabuf_put_buf(buf, global->wfd_subelem[
 				       WFD_SUBELEM_ASSOCIATED_BSSID]);
diff --git a/wpa_supplicant/wmm_ac.c b/wpa_supplicant/wmm_ac.c
index 5625d36..a88cc46 100644
--- a/wpa_supplicant/wmm_ac.c
+++ b/wpa_supplicant/wmm_ac.c
@@ -87,13 +87,10 @@
 	}
 
 	/* copy tspec */
-	_tspec = os_malloc(sizeof(*_tspec));
+	_tspec = os_memdup(tspec, sizeof(*_tspec));
 	if (!_tspec)
 		return -1;
 
-	/* store the admitted TSPEC */
-	os_memcpy(_tspec, tspec, sizeof(*_tspec));
-
 	if (dir != WMM_AC_DIR_DOWNLINK) {
 		ret = wpa_drv_add_ts(wpa_s, tsid, addr, up, admitted_time);
 		wpa_printf(MSG_DEBUG,
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index bd0b517..28346ea 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -13,6 +13,7 @@
 #include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
 #include "rsn_supp/wpa.h"
+#include "config.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
 #include "scan.h"
@@ -84,12 +85,11 @@
 	/* TFS IE(s) */
 	if (tfs_req) {
 		wnmtfs_ie_len = wpabuf_len(tfs_req);
-		wnmtfs_ie = os_malloc(wnmtfs_ie_len);
+		wnmtfs_ie = os_memdup(wpabuf_head(tfs_req), wnmtfs_ie_len);
 		if (wnmtfs_ie == NULL) {
 			os_free(wnmsleep_ie);
 			return -1;
 		}
-		os_memcpy(wnmtfs_ie, wpabuf_head(tfs_req), wnmtfs_ie_len);
 	} else {
 		wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
 		if (wnmtfs_ie == NULL) {
@@ -501,10 +501,128 @@
 }
 
 
-static struct wpa_bss *
-compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs)
+static void wnm_clear_acceptable(struct wpa_supplicant *wpa_s)
 {
+	unsigned int i;
 
+	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++)
+		wpa_s->wnm_neighbor_report_elements[i].acceptable = 0;
+}
+
+
+static struct wpa_bss * get_first_acceptable(struct wpa_supplicant *wpa_s)
+{
+	unsigned int i;
+	struct neighbor_report *nei;
+
+	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+		nei = &wpa_s->wnm_neighbor_report_elements[i];
+		if (nei->acceptable)
+			return wpa_bss_get_bssid(wpa_s, nei->bssid);
+	}
+
+	return NULL;
+}
+
+
+#ifdef CONFIG_MBO
+static struct wpa_bss *
+get_mbo_transition_candidate(struct wpa_supplicant *wpa_s,
+			     enum mbo_transition_reject_reason *reason)
+{
+	struct wpa_bss *target = NULL;
+	struct wpa_bss_trans_info params;
+	struct wpa_bss_candidate_info *info = NULL;
+	struct neighbor_report *nei = wpa_s->wnm_neighbor_report_elements;
+	u8 *first_candidate_bssid = NULL, *pos;
+	unsigned int i;
+
+	params.mbo_transition_reason = wpa_s->wnm_mbo_transition_reason;
+	params.n_candidates = 0;
+	params.bssid = os_calloc(wpa_s->wnm_num_neighbor_report, ETH_ALEN);
+	if (!params.bssid)
+		return NULL;
+
+	pos = params.bssid;
+	for (i = 0; i < wpa_s->wnm_num_neighbor_report; nei++, i++) {
+		if (nei->is_first)
+			first_candidate_bssid = nei->bssid;
+		if (!nei->acceptable)
+			continue;
+		os_memcpy(pos, nei->bssid, ETH_ALEN);
+		pos += ETH_ALEN;
+		params.n_candidates++;
+	}
+
+	if (!params.n_candidates)
+		goto end;
+
+	info = wpa_drv_get_bss_trans_status(wpa_s, &params);
+	if (!info) {
+		/* If failed to get candidate BSS transition status from driver,
+		 * get the first acceptable candidate from wpa_supplicant.
+		 */
+		target = wpa_bss_get_bssid(wpa_s, params.bssid);
+		goto end;
+	}
+
+	/* Get the first acceptable candidate from driver */
+	for (i = 0; i < info->num; i++) {
+		if (info->candidates[i].is_accept) {
+			target = wpa_bss_get_bssid(wpa_s,
+						   info->candidates[i].bssid);
+			goto end;
+		}
+	}
+
+	/* If Disassociation Imminent is set and driver rejects all the
+	 * candidate select first acceptable candidate which has
+	 * rssi > disassoc_imminent_rssi_threshold
+	 */
+	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
+		for (i = 0; i < info->num; i++) {
+			target = wpa_bss_get_bssid(wpa_s,
+						   info->candidates[i].bssid);
+			if (target &&
+			    (target->level <
+			     wpa_s->conf->disassoc_imminent_rssi_threshold))
+				continue;
+			goto end;
+		}
+	}
+
+	/* While sending BTM reject use reason code of the first candidate
+	 * received in BTM request frame
+	 */
+	if (reason) {
+		for (i = 0; i < info->num; i++) {
+			if (first_candidate_bssid &&
+			    os_memcmp(first_candidate_bssid,
+				      info->candidates[i].bssid, ETH_ALEN) == 0)
+			{
+				*reason = info->candidates[i].reject_reason;
+				break;
+			}
+		}
+	}
+
+	target = NULL;
+
+end:
+	os_free(params.bssid);
+	if (info) {
+		os_free(info->candidates);
+		os_free(info);
+	}
+	return target;
+}
+#endif /* CONFIG_MBO */
+
+
+static struct wpa_bss *
+compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs,
+			      enum mbo_transition_reject_reason *reason)
+{
 	u8 i;
 	struct wpa_bss *bss = wpa_s->current_bss;
 	struct wpa_bss *target;
@@ -515,6 +633,8 @@
 	wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d",
 		   MAC2STR(wpa_s->bssid), bss->level);
 
+	wnm_clear_acceptable(wpa_s);
+
 	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
 		struct neighbor_report *nei;
 
@@ -591,14 +711,26 @@
 			continue;
 		}
 
+		nei->acceptable = 1;
+	}
+
+#ifdef CONFIG_MBO
+	if (wpa_s->wnm_mbo_trans_reason_present)
+		target = get_mbo_transition_candidate(wpa_s, reason);
+	else
+		target = get_first_acceptable(wpa_s);
+#else /* CONFIG_MBO */
+	target = get_first_acceptable(wpa_s);
+#endif /* CONFIG_MBO */
+
+	if (target) {
 		wpa_printf(MSG_DEBUG,
 			   "WNM: Found an acceptable preferred transition candidate BSS "
 			   MACSTR " (RSSI %d)",
-			   MAC2STR(nei->bssid), target->level);
-		return target;
+			   MAC2STR(target->bssid), target->level);
 	}
 
-	return NULL;
+	return target;
 }
 
 
@@ -651,36 +783,40 @@
 }
 
 
-static int wnm_add_nei_rep(u8 *buf, size_t len, const u8 *bssid, u32 bss_info,
-			   u8 op_class, u8 chan, u8 phy_type, u8 pref)
+static int wnm_add_nei_rep(struct wpabuf **buf, const u8 *bssid,
+			   u32 bss_info, u8 op_class, u8 chan, u8 phy_type,
+			   u8 pref)
 {
-	u8 *pos = buf;
-
-	if (len < 18) {
+	if (wpabuf_len(*buf) + 18 >
+	    IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN) {
 		wpa_printf(MSG_DEBUG,
-			   "WNM: Not enough room for Neighbor Report element");
+			   "WNM: No room in frame for Neighbor Report element");
 		return -1;
 	}
 
-	*pos++ = WLAN_EID_NEIGHBOR_REPORT;
+	if (wpabuf_resize(buf, 18) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Failed to allocate memory for Neighbor Report element");
+		return -1;
+	}
+
+	wpabuf_put_u8(*buf, WLAN_EID_NEIGHBOR_REPORT);
 	/* length: 13 for basic neighbor report + 3 for preference subelement */
-	*pos++ = 16;
-	os_memcpy(pos, bssid, ETH_ALEN);
-	pos += ETH_ALEN;
-	WPA_PUT_LE32(pos, bss_info);
-	pos += 4;
-	*pos++ = op_class;
-	*pos++ = chan;
-	*pos++ = phy_type;
-	*pos++ = WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE;
-	*pos++ = 1;
-	*pos++ = pref;
-	return pos - buf;
+	wpabuf_put_u8(*buf, 16);
+	wpabuf_put_data(*buf, bssid, ETH_ALEN);
+	wpabuf_put_le32(*buf, bss_info);
+	wpabuf_put_u8(*buf, op_class);
+	wpabuf_put_u8(*buf, chan);
+	wpabuf_put_u8(*buf, phy_type);
+	wpabuf_put_u8(*buf, WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE);
+	wpabuf_put_u8(*buf, 1);
+	wpabuf_put_u8(*buf, pref);
+	return 0;
 }
 
 
 static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s,
-			       struct wpa_bss *bss, u8 *buf, size_t len,
+			       struct wpa_bss *bss, struct wpabuf **buf,
 			       u8 pref)
 {
 	const u8 *ie;
@@ -729,20 +865,19 @@
 
 	info = wnm_get_bss_info(wpa_s, bss);
 
-	return wnm_add_nei_rep(buf, len, bss->bssid, info, op_class, chan,
-			       phy_type, pref);
+	return wnm_add_nei_rep(buf, bss->bssid, info, op_class, chan, phy_type,
+			       pref);
 }
 
 
-static int wnm_add_cand_list(struct wpa_supplicant *wpa_s, u8 *buf, size_t len)
+static void wnm_add_cand_list(struct wpa_supplicant *wpa_s, struct wpabuf **buf)
 {
-	u8 *pos = buf;
 	unsigned int i, pref = 255;
 	struct os_reltime now;
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
 
 	if (!ssid)
-		return 0;
+		return;
 
 	/*
 	 * TODO: Define when scan results are no longer valid for the candidate
@@ -750,7 +885,7 @@
 	 */
 	os_get_reltime(&now);
 	if (os_reltime_expired(&now, &wpa_s->last_scan, 10))
-		return 0;
+		return;
 
 	wpa_printf(MSG_DEBUG,
 		   "WNM: Add candidate list to BSS Transition Management Response frame");
@@ -759,92 +894,99 @@
 		int res;
 
 		if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1, 0)) {
-			res = wnm_nei_rep_add_bss(wpa_s, bss, pos, len, pref--);
+			res = wnm_nei_rep_add_bss(wpa_s, bss, buf, pref--);
 			if (res == -2)
 				continue; /* could not build entry for BSS */
 			if (res < 0)
 				break; /* no more room for candidates */
 			if (pref == 1)
 				break;
-
-			pos += res;
-			len -= res;
 		}
 	}
 
-	wpa_hexdump(MSG_DEBUG,
-		    "WNM: BSS Transition Management Response candidate list",
-		    buf, pos - buf);
-
-	return pos - buf;
+	wpa_hexdump_buf(MSG_DEBUG,
+			"WNM: BSS Transition Management Response candidate list",
+			*buf);
 }
 
 
+#define BTM_RESP_MIN_SIZE	5 + ETH_ALEN
+
 static void wnm_send_bss_transition_mgmt_resp(
 	struct wpa_supplicant *wpa_s, u8 dialog_token,
-	enum bss_trans_mgmt_status_code status, u8 delay,
-	const u8 *target_bssid)
+	enum bss_trans_mgmt_status_code status,
+	enum mbo_transition_reject_reason reason,
+	u8 delay, const u8 *target_bssid)
 {
-	u8 buf[2000], *pos;
-	struct ieee80211_mgmt *mgmt;
-	size_t len;
+	struct wpabuf *buf;
 	int res;
 
-	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response "
-		   "to " MACSTR " dialog_token=%u status=%u delay=%d",
-		   MAC2STR(wpa_s->bssid), dialog_token, status, delay);
+	wpa_printf(MSG_DEBUG,
+		   "WNM: Send BSS Transition Management Response to " MACSTR
+		   " dialog_token=%u status=%u reason=%u delay=%d",
+		   MAC2STR(wpa_s->bssid), dialog_token, status, reason, delay);
 	if (!wpa_s->current_bss) {
 		wpa_printf(MSG_DEBUG,
 			   "WNM: Current BSS not known - drop response");
 		return;
 	}
 
-	mgmt = (struct ieee80211_mgmt *) buf;
-	os_memset(&buf, 0, sizeof(buf));
-	os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
-	os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
-	os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
-	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-					   WLAN_FC_STYPE_ACTION);
-	mgmt->u.action.category = WLAN_ACTION_WNM;
-	mgmt->u.action.u.bss_tm_resp.action = WNM_BSS_TRANS_MGMT_RESP;
-	mgmt->u.action.u.bss_tm_resp.dialog_token = dialog_token;
-	mgmt->u.action.u.bss_tm_resp.status_code = status;
-	mgmt->u.action.u.bss_tm_resp.bss_termination_delay = delay;
-	pos = mgmt->u.action.u.bss_tm_resp.variable;
+	buf = wpabuf_alloc(BTM_RESP_MIN_SIZE);
+	if (!buf) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Failed to allocate memory for BTM response");
+		return;
+	}
+
+	wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+	wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_RESP);
+	wpabuf_put_u8(buf, dialog_token);
+	wpabuf_put_u8(buf, status);
+	wpabuf_put_u8(buf, delay);
 	if (target_bssid) {
-		os_memcpy(pos, target_bssid, ETH_ALEN);
-		pos += ETH_ALEN;
+		wpabuf_put_data(buf, target_bssid, ETH_ALEN);
 	} else if (status == WNM_BSS_TM_ACCEPT) {
 		/*
 		 * P802.11-REVmc clarifies that the Target BSSID field is always
 		 * present when status code is zero, so use a fake value here if
 		 * no BSSID is yet known.
 		 */
-		os_memset(pos, 0, ETH_ALEN);
-		pos += ETH_ALEN;
+		wpabuf_put_data(buf, "\0\0\0\0\0\0", ETH_ALEN);
 	}
 
 	if (status == WNM_BSS_TM_ACCEPT)
-		pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos);
+		wnm_add_cand_list(wpa_s, &buf);
 
 #ifdef CONFIG_MBO
-	if (status != WNM_BSS_TM_ACCEPT) {
-		pos += wpas_mbo_ie_bss_trans_reject(
-			wpa_s, pos, buf + sizeof(buf) - pos,
-			MBO_TRANSITION_REJECT_REASON_UNSPECIFIED);
+	if (status != WNM_BSS_TM_ACCEPT &&
+	    wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) {
+		u8 mbo[10];
+		size_t ret;
+
+		ret = wpas_mbo_ie_bss_trans_reject(wpa_s, mbo, sizeof(mbo),
+						   reason);
+		if (ret) {
+			if (wpabuf_resize(&buf, ret) < 0) {
+				wpabuf_free(buf);
+				wpa_printf(MSG_DEBUG,
+					   "WNM: Failed to allocate memory for MBO IE");
+				return;
+			}
+
+			wpabuf_put_data(buf, mbo, ret);
+		}
 	}
 #endif /* CONFIG_MBO */
 
-	len = pos - (u8 *) &mgmt->u.action.category;
-
 	res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
 				  wpa_s->own_addr, wpa_s->bssid,
-				  &mgmt->u.action.category, len, 0);
+				  wpabuf_head_u8(buf), wpabuf_len(buf), 0);
 	if (res < 0) {
 		wpa_printf(MSG_DEBUG,
 			   "WNM: Failed to send BSS Transition Management Response");
 	}
+
+	wpabuf_free(buf);
 }
 
 
@@ -863,10 +1005,10 @@
 		wpa_s->wnm_reply = 0;
 		wpa_printf(MSG_DEBUG,
 			   "WNM: Sending successful BSS Transition Management Response");
-		wnm_send_bss_transition_mgmt_resp(wpa_s,
-						  wpa_s->wnm_dialog_token,
-						  WNM_BSS_TM_ACCEPT,
-						  0, bss->bssid);
+		wnm_send_bss_transition_mgmt_resp(
+			wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_ACCEPT,
+			MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
+			bss->bssid);
 	}
 
 	if (bss == wpa_s->current_bss) {
@@ -888,6 +1030,8 @@
 	struct wpa_bss *bss;
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
 	enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED;
+	enum mbo_transition_reject_reason reason =
+		MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
 
 	if (!wpa_s->wnm_neighbor_report_elements)
 		return 0;
@@ -909,7 +1053,7 @@
 	}
 
 	/* Compare the Neighbor Report and scan results */
-	bss = compare_scan_neighbor_results(wpa_s, 0);
+	bss = compare_scan_neighbor_results(wpa_s, 0, &reason);
 	if (!bss) {
 		wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found");
 		status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
@@ -930,7 +1074,7 @@
 		wpa_s->wnm_reply = 0;
 		wnm_send_bss_transition_mgmt_resp(wpa_s,
 						  wpa_s->wnm_dialog_token,
-						  status, 0, NULL);
+						  status, reason, 0, NULL);
 	}
 	wnm_deallocate_memory(wpa_s);
 
@@ -1118,7 +1262,7 @@
 		return 0;
 	}
 
-	bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE);
+	bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE, NULL);
 	if (!bss) {
 		wpa_dbg(wpa_s, MSG_DEBUG,
 			"WNM: Comparison of scan results against transition candidates did not find matches");
@@ -1144,6 +1288,11 @@
 	if (end - pos < 5)
 		return;
 
+#ifdef CONFIG_MBO
+	wpa_s->wnm_mbo_trans_reason_present = 0;
+	wpa_s->wnm_mbo_transition_reason = 0;
+#endif /* CONFIG_MBO */
+
 	if (wpa_s->current_bss)
 		beacon_int = wpa_s->current_bss->beacon_int;
 	else
@@ -1166,10 +1315,10 @@
 		wpa_printf(MSG_INFO,
 			   "WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d",
 			   wpa_s->reject_btm_req_reason);
-		wnm_send_bss_transition_mgmt_resp(wpa_s,
-						  wpa_s->wnm_dialog_token,
-						  wpa_s->reject_btm_req_reason,
-						  0, NULL);
+		wnm_send_bss_transition_mgmt_resp(
+			wpa_s, wpa_s->wnm_dialog_token,
+			wpa_s->reject_btm_req_reason,
+			MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
 		return;
 	}
 #endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */
@@ -1248,6 +1397,15 @@
 					wpa_s->wnm_num_neighbor_report];
 				wnm_parse_neighbor_report(wpa_s, pos, len, rep);
 				wpa_s->wnm_num_neighbor_report++;
+#ifdef CONFIG_MBO
+				if (wpa_s->wnm_mbo_trans_reason_present &&
+				    wpa_s->wnm_num_neighbor_report == 1) {
+					rep->is_first = 1;
+					wpa_printf(MSG_DEBUG,
+						   "WNM: First transition candidate is "
+						   MACSTR, MAC2STR(rep->bssid));
+				}
+#endif /* CONFIG_MBO */
 			}
 
 			pos += len;
@@ -1259,7 +1417,8 @@
 			wnm_send_bss_transition_mgmt_resp(
 				wpa_s, wpa_s->wnm_dialog_token,
 				WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
-				0, NULL);
+				MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
+				NULL);
 			return;
 		}
 
@@ -1322,19 +1481,21 @@
 			wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates");
 			status = WNM_BSS_TM_REJECT_UNSPECIFIED;
 		}
-		wnm_send_bss_transition_mgmt_resp(wpa_s,
-						  wpa_s->wnm_dialog_token,
-						  status, 0, NULL);
+		wnm_send_bss_transition_mgmt_resp(
+			wpa_s, wpa_s->wnm_dialog_token, status,
+			MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
 	}
 }
 
 
+#define BTM_QUERY_MIN_SIZE	4
+
 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
-				       u8 query_reason, int cand_list)
+				       u8 query_reason,
+				       const char *btm_candidates,
+				       int cand_list)
 {
-	u8 buf[2000], *pos;
-	struct ieee80211_mgmt *mgmt;
-	size_t len;
+	struct wpabuf *buf;
 	int ret;
 
 	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to "
@@ -1342,28 +1503,43 @@
 		   MAC2STR(wpa_s->bssid), query_reason,
 		   cand_list ? " candidate list" : "");
 
-	mgmt = (struct ieee80211_mgmt *) buf;
-	os_memset(&buf, 0, sizeof(buf));
-	os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
-	os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
-	os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
-	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-					   WLAN_FC_STYPE_ACTION);
-	mgmt->u.action.category = WLAN_ACTION_WNM;
-	mgmt->u.action.u.bss_tm_query.action = WNM_BSS_TRANS_MGMT_QUERY;
-	mgmt->u.action.u.bss_tm_query.dialog_token = 1;
-	mgmt->u.action.u.bss_tm_query.query_reason = query_reason;
-	pos = mgmt->u.action.u.bss_tm_query.variable;
+	buf = wpabuf_alloc(BTM_QUERY_MIN_SIZE);
+	if (!buf)
+		return -1;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+	wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_QUERY);
+	wpabuf_put_u8(buf, 1);
+	wpabuf_put_u8(buf, query_reason);
 
 	if (cand_list)
-		pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos);
+		wnm_add_cand_list(wpa_s, &buf);
 
-	len = pos - (u8 *) &mgmt->u.action.category;
+	if (btm_candidates) {
+		const size_t max_len = 1000;
+
+		ret = wpabuf_resize(&buf, max_len);
+		if (ret < 0) {
+			wpabuf_free(buf);
+			return ret;
+		}
+
+		ret = ieee802_11_parse_candidate_list(btm_candidates,
+						      wpabuf_put(buf, 0),
+						      max_len);
+		if (ret < 0) {
+			wpabuf_free(buf);
+			return ret;
+		}
+
+		wpabuf_put(buf, ret);
+	}
 
 	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
 				  wpa_s->own_addr, wpa_s->bssid,
-				  &mgmt->u.action.category, len, 0);
+				  wpabuf_head_u8(buf), wpabuf_len(buf), 0);
 
+	wpabuf_free(buf);
 	return ret;
 }
 
diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h
index 81d8153..02cd1cd 100644
--- a/wpa_supplicant/wnm_sta.h
+++ b/wpa_supplicant/wnm_sta.h
@@ -43,6 +43,10 @@
 	unsigned int rm_capab_present:1;
 	unsigned int bearing_present:1;
 	unsigned int bss_term_present:1;
+	unsigned int acceptable:1;
+#ifdef CONFIG_MBO
+	unsigned int is_first:1;
+#endif /* CONFIG_MBO */
 	struct measurement_pilot *meas_pilot;
 	struct multiple_bssid *mul_bssid;
 	int freq;
@@ -56,7 +60,10 @@
 			      const struct ieee80211_mgmt *mgmt, size_t len);
 
 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
-				       u8 query_reason, int cand_list);
+				       u8 query_reason,
+				       const char *btm_candidates,
+				       int cand_list);
+
 void wnm_deallocate_memory(struct wpa_supplicant *wpa_s);
 
 
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 964311c..6b345af 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -479,6 +479,8 @@
 		"hs20",
 #endif /* CONFIG_HS20 */
 		"interworking", "hessid", "access_network_type", "pbc_in_m1",
+		"go_interworking", "go_access_network_type", "go_internet",
+		"go_venue_group", "go_venue_type",
 		"autoscan", "wps_nfc_dev_pw_id", "wps_nfc_dh_pubkey",
 		"wps_nfc_dh_privkey", "wps_nfc_dev_pw", "ext_password_backend",
 		"p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
@@ -574,10 +576,13 @@
 		"hs20",
 #endif /* CONFIG_HS20 */
 		"interworking", "access_network_type", "pbc_in_m1", "autoscan",
+		"go_interworking", "go_access_network_type", "go_internet",
+		"go_venue_group", "go_venue_type",
 		"wps_nfc_dev_pw_id", "ext_password_backend",
 		"p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
 		"dtim_period", "beacon_int", "ignore_old_scan_res",
 		"scan_cur_freq", "sched_scan_interval",
+		"sched_scan_start_delay",
 		"tdls_external_control", "osu_dir", "wowlan_triggers",
 		"p2p_search_delay", "mac_addr", "rand_addr_lifetime",
 		"preassoc_mac_addr", "key_mgmt_offload", "passive_scan",
@@ -677,13 +682,6 @@
 }
 
 
-static int wpa_cli_cmd_stkstart(struct wpa_ctrl *ctrl, int argc,
-				char *argv[])
-{
-	return wpa_cli_cmd(ctrl, "STKSTART", 1, argc, argv);
-}
-
-
 static int wpa_cli_cmd_ft_ds(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	return wpa_cli_cmd(ctrl, "FT_DS", 1, argc, argv);
@@ -1370,7 +1368,8 @@
 	"ssid", "scan_ssid", "bssid", "bssid_blacklist",
 	"bssid_whitelist", "psk", "proto", "key_mgmt",
 	"bg_scan_period", "pairwise", "group", "auth_alg", "scan_freq",
-	"freq_list", "max_oper_chwidth",
+	"freq_list", "max_oper_chwidth", "ht40", "vht", "vht_center_freq1",
+	"vht_center_freq2", "ht",
 #ifdef IEEE8021X_EAPOL
 	"eap", "identity", "anonymous_identity", "password", "ca_cert",
 	"ca_path", "client_cert", "private_key", "private_key_passwd",
@@ -1390,7 +1389,7 @@
 	"eap_workaround", "pac_file", "fragment_size", "ocsp",
 #endif /* IEEE8021X_EAPOL */
 #ifdef CONFIG_MESH
-	"mode", "no_auto_peer",
+	"mode", "no_auto_peer", "mesh_rssi_threshold",
 #else /* CONFIG_MESH */
 	"mode",
 #endif /* CONFIG_MESH */
@@ -1398,7 +1397,7 @@
 #ifdef CONFIG_IEEE80211W
 	"ieee80211w",
 #endif /* CONFIG_IEEE80211W */
-	"peerkey", "mixed_cell", "frequency", "fixed_freq",
+	"mixed_cell", "frequency", "fixed_freq",
 #ifdef CONFIG_MESH
 	"mesh_basic_rates", "dot11MeshMaxRetries",
 	"dot11MeshRetryTimeout", "dot11MeshConfirmTimeout",
@@ -1821,7 +1820,8 @@
 	}
 
 	buf[len] = '\0';
-	if (os_memcmp(buf, "FAIL", 4) == 0)
+	if (os_memcmp(buf, "FAIL", 4) == 0 ||
+	    os_memcmp(buf, "UNKNOWN COMMAND", 15) == 0)
 		return -1;
 	if (print)
 		printf("%s", buf);
@@ -2831,6 +2831,94 @@
 }
 
 
+#ifdef CONFIG_DPP
+
+static int wpa_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_QR_CODE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_bootstrap_gen(struct wpa_ctrl *ctrl, int argc,
+					 char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_BOOTSTRAP_GEN", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_bootstrap_remove(struct wpa_ctrl *ctrl, int argc,
+					    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_BOOTSTRAP_REMOVE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_bootstrap_get_uri(struct wpa_ctrl *ctrl, int argc,
+					     char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_BOOTSTRAP_GET_URI", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_bootstrap_info(struct wpa_ctrl *ctrl, int argc,
+					  char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_BOOTSTRAP_INFO", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_auth_init(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_AUTH_INIT", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_listen(struct wpa_ctrl *ctrl, int argc,
+				  char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_LISTEN", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_stop_listen(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "DPP_STOP_LISTEN");
+}
+
+
+static int wpa_cli_cmd_dpp_configurator_add(struct wpa_ctrl *ctrl, int argc,
+					    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_CONFIGURATOR_ADD", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_configurator_remove(struct wpa_ctrl *ctrl, int argc,
+					       char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_CONFIGURATOR_REMOVE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_pkex_add(struct wpa_ctrl *ctrl, int argc,
+				    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_PKEX_ADD", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_pkex_remove(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_PKEX_REMOVE", 1, argc, argv);
+}
+
+#endif /* CONFIG_DPP */
+
+
 enum wpa_cli_cmd_flags {
 	cli_cmd_flag_none		= 0x00,
 	cli_cmd_flag_sensitive		= 0x01
@@ -3072,9 +3160,6 @@
 	{ "bss_flush", wpa_cli_cmd_bss_flush, NULL,
 	  cli_cmd_flag_none,
 	  "<value> = set BSS flush age (0 by default)" },
-	{ "stkstart", wpa_cli_cmd_stkstart, NULL,
-	  cli_cmd_flag_none,
-	  "<addr> = request STK negotiation with <addr>" },
 	{ "ft_ds", wpa_cli_cmd_ft_ds, wpa_cli_complete_bss,
 	  cli_cmd_flag_none,
 	  "<addr> = request over-the-DS FT with <addr>" },
@@ -3409,7 +3494,9 @@
 	{ "wnm_sleep", wpa_cli_cmd_wnm_sleep, NULL, cli_cmd_flag_none,
 	  "<enter/exit> [interval=#] = enter/exit WNM-Sleep mode" },
 	{ "wnm_bss_query", wpa_cli_cmd_wnm_bss_query, NULL, cli_cmd_flag_none,
-	  "<query reason> [list] = Send BSS Transition Management Query" },
+	  "<query reason> [list]"
+	  " [neighbor=<BSSID>,<BSSID information>,<operating class>,<channel number>,<PHY type>[,<hexdump of optional subelements>]"
+	  " = Send BSS Transition Management Query" },
 #endif /* CONFIG_WNM */
 	{ "raw", wpa_cli_cmd_raw, NULL, cli_cmd_flag_sensitive,
 	  "<params..> = Sent unprocessed command" },
@@ -3444,6 +3531,41 @@
 	{ "p2p_lo_stop", wpa_cli_cmd_p2p_lo_stop, NULL,
 	  cli_cmd_flag_none,
 	  "= stop P2P listen offload" },
+#ifdef CONFIG_DPP
+	{ "dpp_qr_code", wpa_cli_cmd_dpp_qr_code, NULL, cli_cmd_flag_none,
+	  "report a scanned DPP URI from a QR Code" },
+	{ "dpp_bootstrap_gen", wpa_cli_cmd_dpp_bootstrap_gen, NULL,
+	  cli_cmd_flag_sensitive,
+	  "type=<qrcode> [chan=..] [mac=..] [info=..] [curve=..] [key=..] = generate DPP bootstrap information" },
+	{ "dpp_bootstrap_remove", wpa_cli_cmd_dpp_bootstrap_remove, NULL,
+	  cli_cmd_flag_none,
+	  "*|<id> = remove DPP bootstrap information" },
+	{ "dpp_bootstrap_get_uri", wpa_cli_cmd_dpp_bootstrap_get_uri, NULL,
+	  cli_cmd_flag_none,
+	  "<id> = get DPP bootstrap URI" },
+	{ "dpp_bootstrap_info", wpa_cli_cmd_dpp_bootstrap_info, NULL,
+	  cli_cmd_flag_none,
+	  "<id> = show DPP bootstrap information" },
+	{ "dpp_auth_init", wpa_cli_cmd_dpp_auth_init, NULL, cli_cmd_flag_none,
+	  "peer=<id> [own=<id>] = initiate DPP bootstrapping" },
+	{ "dpp_listen", wpa_cli_cmd_dpp_listen, NULL, cli_cmd_flag_none,
+	  "<freq in MHz> = start DPP listen" },
+	{ "dpp_stop_listen", wpa_cli_cmd_dpp_stop_listen, NULL,
+	  cli_cmd_flag_none,
+	  "= stop DPP listen" },
+	{ "dpp_configurator_add", wpa_cli_cmd_dpp_configurator_add, NULL,
+	  cli_cmd_flag_sensitive,
+	  "[curve=..] [key=..] = add DPP configurator" },
+	{ "dpp_configurator_remove", wpa_cli_cmd_dpp_configurator_remove, NULL,
+	  cli_cmd_flag_none,
+	  "*|<id> = remove DPP configurator" },
+	{ "dpp_pkex_add", wpa_cli_cmd_dpp_pkex_add, NULL,
+	  cli_cmd_flag_sensitive,
+	  "add PKEX code" },
+	{ "dpp_pkex_remove", wpa_cli_cmd_dpp_pkex_remove, NULL,
+	  cli_cmd_flag_none,
+	  "*|<id> = remove DPP pkex information" },
+#endif /* CONFIG_DPP */
 	{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
 };
 
@@ -3762,6 +3884,10 @@
 		wpa_cli_exec(action_file, ifname, pos);
 	} else if (str_starts(pos, WPS_EVENT_SUCCESS)) {
 		wpa_cli_exec(action_file, ifname, pos);
+	} else if (str_starts(pos, WPS_EVENT_ACTIVE)) {
+		wpa_cli_exec(action_file, ifname, pos);
+	} else if (str_starts(pos, WPS_EVENT_TIMEOUT)) {
+		wpa_cli_exec(action_file, ifname, pos);
 	} else if (str_starts(pos, WPS_EVENT_FAIL)) {
 		wpa_cli_exec(action_file, ifname, pos);
 	} else if (str_starts(pos, AP_STA_CONNECTED)) {
diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
index 46cb95e..b3ad45e 100644
--- a/wpa_supplicant/wpa_priv.c
+++ b/wpa_supplicant/wpa_priv.c
@@ -1081,12 +1081,6 @@
 				    &data->pmkid_candidate,
 				    sizeof(struct pmkid_candidate));
 		break;
-	case EVENT_STKSTART:
-		if (data == NULL)
-			return;
-		wpa_priv_send_event(iface, PRIVSEP_EVENT_STKSTART,
-				    &data->stkstart.peer, ETH_ALEN);
-		break;
 	case EVENT_FT_RESPONSE:
 		wpa_priv_send_ft_response(iface, data);
 		break;
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 9aaedb3..f8e5bf7 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -38,6 +38,7 @@
 #include "common/wpa_ctrl.h"
 #include "common/ieee802_11_defs.h"
 #include "common/hw_features_common.h"
+#include "common/gas_server.h"
 #include "p2p/p2p.h"
 #include "fst/fst.h"
 #include "blacklist.h"
@@ -59,6 +60,11 @@
 #include "wnm_sta.h"
 #include "wpas_kay.h"
 #include "mesh.h"
+#include "dpp_supplicant.h"
+#ifdef CONFIG_MESH
+#include "ap/ap_config.h"
+#include "ap/hostapd.h"
+#endif /* CONFIG_MESH */
 
 const char *const wpa_supplicant_version =
 "wpa_supplicant v" VERSION_STR "\n"
@@ -112,6 +118,13 @@
 "\n";
 #endif /* CONFIG_NO_STDOUT_DEBUG */
 
+
+static void wpa_bss_tmp_disallow_timeout(void *eloop_ctx, void *timeout_ctx);
+#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL)
+static void wpas_update_fils_connect_params(struct wpa_supplicant *wpa_s);
+#endif /* CONFIG_FILS && IEEE8021X_EAPOL */
+
+
 /* Configure default/group WEP keys for static WEP */
 int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
 {
@@ -415,6 +428,7 @@
 
 	dl_list_for_each_safe(bss, prev, &wpa_s->bss_tmp_disallowed,
 			      struct wpa_bss_tmp_disallowed, list) {
+		eloop_cancel_timeout(wpa_bss_tmp_disallow_timeout, wpa_s, bss);
 		dl_list_del(&bss->list);
 		os_free(bss);
 	}
@@ -455,6 +469,8 @@
 	wpa_s->l2_test = NULL;
 	os_free(wpa_s->get_pref_freq_list_override);
 	wpa_s->get_pref_freq_list_override = NULL;
+	wpabuf_free(wpa_s->last_assoc_req_wpa_ie);
+	wpa_s->last_assoc_req_wpa_ie = NULL;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	if (wpa_s->conf != NULL) {
@@ -527,6 +543,8 @@
 
 	os_free(wpa_s->manual_scan_freqs);
 	wpa_s->manual_scan_freqs = NULL;
+	os_free(wpa_s->select_network_scan_freqs);
+	wpa_s->select_network_scan_freqs = NULL;
 
 	os_free(wpa_s->manual_sched_scan_freqs);
 	wpa_s->manual_sched_scan_freqs = NULL;
@@ -545,6 +563,8 @@
 		radio_remove_works(wpa_s, "gas-query", 0);
 	gas_query_deinit(wpa_s->gas);
 	wpa_s->gas = NULL;
+	gas_server_deinit(wpa_s->gas_server);
+	wpa_s->gas_server = NULL;
 
 	free_hw_features(wpa_s);
 
@@ -623,6 +643,10 @@
 
 	wpabuf_free(wpa_s->ric_ies);
 	wpa_s->ric_ies = NULL;
+
+#ifdef CONFIG_DPP
+	wpas_dpp_deinit(wpa_s);
+#endif /* CONFIG_DPP */
 }
 
 
@@ -836,12 +860,24 @@
 
 	if (state == WPA_COMPLETED && wpa_s->new_connection) {
 		struct wpa_ssid *ssid = wpa_s->current_ssid;
+		int fils_hlp_sent = 0;
+
+#ifdef CONFIG_SME
+		if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+		    wpa_auth_alg_fils(wpa_s->sme.auth_alg))
+			fils_hlp_sent = 1;
+#endif /* CONFIG_SME */
+		if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+		    wpa_auth_alg_fils(wpa_s->auth_alg))
+			fils_hlp_sent = 1;
+
 #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
 		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to "
-			MACSTR " completed [id=%d id_str=%s]",
+			MACSTR " completed [id=%d id_str=%s%s]",
 			MAC2STR(wpa_s->bssid),
 			ssid ? ssid->id : -1,
-			ssid && ssid->id_str ? ssid->id_str : "");
+			ssid && ssid->id_str ? ssid->id_str : "",
+			fils_hlp_sent ? " FILS_HLP_SENT" : "");
 #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 		wpas_clear_temp_disabled(wpa_s, ssid, 1);
 		wpa_blacklist_clear(wpa_s);
@@ -856,6 +892,11 @@
 		wpas_p2p_completed(wpa_s);
 
 		sme_sched_obss_scan(wpa_s, 1);
+
+#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL)
+		if (!fils_hlp_sent && ssid && ssid->eap.erp)
+			wpas_update_fils_connect_params(wpa_s);
+#endif /* CONFIG_FILS && IEEE8021X_EAPOL */
 	} else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING ||
 		   state == WPA_ASSOCIATED) {
 		wpa_s->new_connection = 1;
@@ -996,7 +1037,9 @@
 	 * TODO: should notify EAPOL SM about changes in opensc_engine_path,
 	 * pkcs11_engine_path, pkcs11_module_path, openssl_ciphers.
 	 */
-	if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) {
+	if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_OWE ||
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_DPP) {
 		/*
 		 * Clear forced success to clear EAP state for next
 		 * authentication.
@@ -1200,9 +1243,24 @@
 			ie.pairwise_cipher = ssid->pairwise_cipher;
 			ie.key_mgmt = ssid->key_mgmt;
 #ifdef CONFIG_IEEE80211W
-			ie.mgmt_group_cipher =
-				ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION ?
-				WPA_CIPHER_AES_128_CMAC : 0;
+			ie.mgmt_group_cipher = 0;
+			if (ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+				if (ssid->group_mgmt_cipher &
+				    WPA_CIPHER_BIP_GMAC_256)
+					ie.mgmt_group_cipher =
+						WPA_CIPHER_BIP_GMAC_256;
+				else if (ssid->group_mgmt_cipher &
+					 WPA_CIPHER_BIP_CMAC_256)
+					ie.mgmt_group_cipher =
+						WPA_CIPHER_BIP_CMAC_256;
+				else if (ssid->group_mgmt_cipher &
+					 WPA_CIPHER_BIP_GMAC_128)
+					ie.mgmt_group_cipher =
+						WPA_CIPHER_BIP_GMAC_128;
+				else
+					ie.mgmt_group_cipher =
+						WPA_CIPHER_AES_128_CMAC;
+			}
 #endif /* CONFIG_IEEE80211W */
 			wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Set cipher suites "
 				"based on configuration");
@@ -1332,6 +1390,16 @@
 		wpa_s->key_mgmt = WPA_KEY_MGMT_OSEN;
 		wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using KEY_MGMT OSEN");
 #endif /* CONFIG_HS20 */
+#ifdef CONFIG_OWE
+	} else if (sel & WPA_KEY_MGMT_OWE) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_OWE;
+		wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT OWE");
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+	} else if (sel & WPA_KEY_MGMT_DPP) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_DPP;
+		wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT DPP");
+#endif /* CONFIG_DPP */
 	} else {
 		wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select "
 			"authenticated key management type");
@@ -1345,6 +1413,8 @@
 
 #ifdef CONFIG_IEEE80211W
 	sel = ie.mgmt_group_cipher;
+	if (ssid->group_mgmt_cipher)
+		sel &= ssid->group_mgmt_cipher;
 	if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION ||
 	    !(ie.capabilities & WPA_CAPABILITY_MFPC))
 		sel = 0;
@@ -1387,6 +1457,10 @@
 				       NULL);
 			psk_set = 1;
 		}
+
+		if (wpa_key_mgmt_sae(ssid->key_mgmt) && ssid->sae_password)
+			psk_set = 1;
+
 #ifndef CONFIG_NO_PBKDF2
 		if (bss && ssid->bssid_set && ssid->ssid_len == 0 &&
 		    ssid->passphrase) {
@@ -1469,6 +1543,12 @@
 				"No PSK available for association");
 			return -1;
 		}
+#ifdef CONFIG_OWE
+	} else if (wpa_s->key_mgmt == WPA_KEY_MGMT_OWE) {
+		/* OWE Diffie-Hellman exchange in (Re)Association
+		 * Request/Response frames set the PMK, so do not override it
+		 * here. */
+#endif /* CONFIG_OWE */
 	} else
 		wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
 
@@ -1726,6 +1806,9 @@
 	wmm_ac_clear_saved_tspecs(wpa_s);
 	wpa_s->reassoc_same_bss = 0;
 	wpa_s->reassoc_same_ess = 0;
+#ifdef CONFIG_TESTING_OPTIONS
+	wpa_s->testing_resend_assoc = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
 
 	if (wpa_s->last_ssid == ssid) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS");
@@ -1807,6 +1890,7 @@
 		wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_STARTED "ssid=\"%s\" id=%d",
 			wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
 			ssid->id);
+		wpas_notify_mesh_group_started(wpa_s, ssid);
 #else /* CONFIG_MESH */
 		wpa_msg(wpa_s, MSG_ERROR,
 			"mesh mode support not included in the build");
@@ -1814,6 +1898,20 @@
 		return;
 	}
 
+	/*
+	 * Set WPA state machine configuration to match the selected network now
+	 * so that the information is available before wpas_start_assoc_cb()
+	 * gets called. This is needed at least for RSN pre-authentication where
+	 * candidate APs are added to a list based on scan result processing
+	 * before completion of the first association.
+	 */
+	wpa_supplicant_rsn_supp_set_config(wpa_s, ssid);
+
+#ifdef CONFIG_DPP
+	if (wpas_dpp_check_connect(wpa_s, ssid, bss) != 0)
+		return;
+#endif /* CONFIG_DPP */
+
 #ifdef CONFIG_TDLS
 	if (bss)
 		wpa_tdls_ap_ies(wpa_s->wpa, (const u8 *) (bss + 1),
@@ -2163,17 +2261,419 @@
 }
 
 
+#ifdef CONFIG_FILS
+static size_t wpas_add_fils_hlp_req(struct wpa_supplicant *wpa_s, u8 *ie_buf,
+				    size_t ie_buf_len)
+{
+	struct fils_hlp_req *req;
+	size_t rem_len, hdr_len, hlp_len, len, ie_len = 0;
+	const u8 *pos;
+	u8 *buf = ie_buf;
+
+	dl_list_for_each(req, &wpa_s->fils_hlp_req, struct fils_hlp_req,
+			 list) {
+		rem_len = ie_buf_len - ie_len;
+		pos = wpabuf_head(req->pkt);
+		hdr_len = 1 + 2 * ETH_ALEN + 6;
+		hlp_len = wpabuf_len(req->pkt);
+
+		if (rem_len < 2 + hdr_len + hlp_len) {
+			wpa_printf(MSG_ERROR,
+				   "FILS: Cannot fit HLP - rem_len=%lu to_fill=%lu",
+				   (unsigned long) rem_len,
+				   (unsigned long) (2 + hdr_len + hlp_len));
+			break;
+		}
+
+		len = (hdr_len + hlp_len) > 255 ? 255 : hdr_len + hlp_len;
+		/* Element ID */
+		*buf++ = WLAN_EID_EXTENSION;
+		/* Length */
+		*buf++ = len;
+		/* Element ID Extension */
+		*buf++ = WLAN_EID_EXT_FILS_HLP_CONTAINER;
+		/* Destination MAC address */
+		os_memcpy(buf, req->dst, ETH_ALEN);
+		buf += ETH_ALEN;
+		/* Source MAC address */
+		os_memcpy(buf, wpa_s->own_addr, ETH_ALEN);
+		buf += ETH_ALEN;
+		/* LLC/SNAP Header */
+		os_memcpy(buf, "\xaa\xaa\x03\x00\x00\x00", 6);
+		buf += 6;
+		/* HLP Packet */
+		os_memcpy(buf, pos, len - hdr_len);
+		buf += len - hdr_len;
+		pos += len - hdr_len;
+
+		hlp_len -= len - hdr_len;
+		ie_len += 2 + len;
+		rem_len -= 2 + len;
+
+		while (hlp_len) {
+			len = (hlp_len > 255) ? 255 : hlp_len;
+			if (rem_len < 2 + len)
+				break;
+			*buf++ = WLAN_EID_FRAGMENT;
+			*buf++ = len;
+			os_memcpy(buf, pos, len);
+			buf += len;
+			pos += len;
+
+			hlp_len -= len;
+			ie_len += 2 + len;
+			rem_len -= 2 + len;
+		}
+	}
+
+	return ie_len;
+}
+#endif /* CONFIG_FILS */
+
+
+static u8 * wpas_populate_assoc_ies(
+	struct wpa_supplicant *wpa_s,
+	struct wpa_bss *bss, struct wpa_ssid *ssid,
+	struct wpa_driver_associate_params *params,
+	enum wpa_drv_update_connect_params_mask *mask)
+{
+	u8 *wpa_ie;
+	size_t max_wpa_ie_len = 500;
+	size_t wpa_ie_len;
+	int algs = WPA_AUTH_ALG_OPEN;
+#ifdef CONFIG_FILS
+	const u8 *realm, *username, *rrk;
+	size_t realm_len, username_len, rrk_len;
+	u16 next_seq_num;
+	struct fils_hlp_req *req;
+
+	dl_list_for_each(req, &wpa_s->fils_hlp_req, struct fils_hlp_req,
+			 list) {
+		max_wpa_ie_len += 3 + 2 * ETH_ALEN + 6 + wpabuf_len(req->pkt) +
+				  2 + 2 * wpabuf_len(req->pkt) / 255;
+	}
+#endif /* CONFIG_FILS */
+
+	wpa_ie = os_malloc(max_wpa_ie_len);
+	if (!wpa_ie) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to allocate connect IE buffer for %lu bytes",
+			   (unsigned long) max_wpa_ie_len);
+		return NULL;
+	}
+
+	if (bss && (wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) ||
+		    wpa_bss_get_ie(bss, WLAN_EID_RSN)) &&
+	    wpa_key_mgmt_wpa(ssid->key_mgmt)) {
+		int try_opportunistic;
+		const u8 *cache_id = NULL;
+
+		try_opportunistic = (ssid->proactive_key_caching < 0 ?
+				     wpa_s->conf->okc :
+				     ssid->proactive_key_caching) &&
+			(ssid->proto & WPA_PROTO_RSN);
+#ifdef CONFIG_FILS
+		if (wpa_key_mgmt_fils(ssid->key_mgmt))
+			cache_id = wpa_bss_get_fils_cache_id(bss);
+#endif /* CONFIG_FILS */
+		if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
+					    ssid, try_opportunistic,
+					    cache_id) == 0)
+			eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
+		wpa_ie_len = max_wpa_ie_len;
+		if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
+					      wpa_ie, &wpa_ie_len)) {
+			wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
+				"key management and encryption suites");
+			os_free(wpa_ie);
+			return NULL;
+		}
+	} else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && bss &&
+		   wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) {
+		/*
+		 * Both WPA and non-WPA IEEE 802.1X enabled in configuration -
+		 * use non-WPA since the scan results did not indicate that the
+		 * AP is using WPA or WPA2.
+		 */
+		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+		wpa_ie_len = 0;
+		wpa_s->wpa_proto = 0;
+	} else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
+		wpa_ie_len = max_wpa_ie_len;
+		if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
+					      wpa_ie, &wpa_ie_len)) {
+			wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
+				"key management and encryption suites (no "
+				"scan results)");
+			os_free(wpa_ie);
+			return NULL;
+		}
+#ifdef CONFIG_WPS
+	} else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
+		struct wpabuf *wps_ie;
+		wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid));
+		if (wps_ie && wpabuf_len(wps_ie) <= max_wpa_ie_len) {
+			wpa_ie_len = wpabuf_len(wps_ie);
+			os_memcpy(wpa_ie, wpabuf_head(wps_ie), wpa_ie_len);
+		} else
+			wpa_ie_len = 0;
+		wpabuf_free(wps_ie);
+		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+		if (!bss || (bss->caps & IEEE80211_CAP_PRIVACY))
+			params->wps = WPS_MODE_PRIVACY;
+		else
+			params->wps = WPS_MODE_OPEN;
+		wpa_s->wpa_proto = 0;
+#endif /* CONFIG_WPS */
+	} else {
+		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+		wpa_ie_len = 0;
+		wpa_s->wpa_proto = 0;
+	}
+
+#ifdef IEEE8021X_EAPOL
+	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+		if (ssid->leap) {
+			if (ssid->non_leap == 0)
+				algs = WPA_AUTH_ALG_LEAP;
+			else
+				algs |= WPA_AUTH_ALG_LEAP;
+		}
+	}
+
+#ifdef CONFIG_FILS
+	/* Clear FILS association */
+	wpa_sm_set_reset_fils_completed(wpa_s->wpa, 0);
+
+	if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD) &&
+	    ssid->eap.erp && wpa_key_mgmt_fils(wpa_s->key_mgmt) &&
+	    eapol_sm_get_erp_info(wpa_s->eapol, &ssid->eap, &username,
+				  &username_len, &realm, &realm_len,
+				  &next_seq_num, &rrk, &rrk_len) == 0) {
+		algs = WPA_AUTH_ALG_FILS;
+		params->fils_erp_username = username;
+		params->fils_erp_username_len = username_len;
+		params->fils_erp_realm = realm;
+		params->fils_erp_realm_len = realm_len;
+		params->fils_erp_next_seq_num = next_seq_num;
+		params->fils_erp_rrk = rrk;
+		params->fils_erp_rrk_len = rrk_len;
+
+		if (mask)
+			*mask |= WPA_DRV_UPDATE_FILS_ERP_INFO;
+	}
+#endif /* CONFIG_FILS */
+#endif /* IEEE8021X_EAPOL */
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs);
+	if (ssid->auth_alg) {
+		algs = ssid->auth_alg;
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Overriding auth_alg selection: 0x%x", algs);
+	}
+
+#ifdef CONFIG_P2P
+	if (wpa_s->global->p2p) {
+		u8 *pos;
+		size_t len;
+		int res;
+		pos = wpa_ie + wpa_ie_len;
+		len = max_wpa_ie_len - wpa_ie_len;
+		res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len,
+					    ssid->p2p_group);
+		if (res >= 0)
+			wpa_ie_len += res;
+	}
+
+	wpa_s->cross_connect_disallowed = 0;
+	if (bss) {
+		struct wpabuf *p2p;
+		p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
+		if (p2p) {
+			wpa_s->cross_connect_disallowed =
+				p2p_get_cross_connect_disallowed(p2p);
+			wpabuf_free(p2p);
+			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: WLAN AP %s cross "
+				"connection",
+				wpa_s->cross_connect_disallowed ?
+				"disallows" : "allows");
+		}
+	}
+
+	os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info));
+#endif /* CONFIG_P2P */
+
+	if (bss) {
+		wpa_ie_len += wpas_supp_op_class_ie(wpa_s, bss->freq,
+						    wpa_ie + wpa_ie_len,
+						    max_wpa_ie_len -
+						    wpa_ie_len);
+	}
+
+	/*
+	 * Workaround: Add Extended Capabilities element only if the AP
+	 * included this element in Beacon/Probe Response frames. Some older
+	 * APs seem to have interoperability issues if this element is
+	 * included, so while the standard may require us to include the
+	 * element in all cases, it is justifiable to skip it to avoid
+	 * interoperability issues.
+	 */
+	if (ssid->p2p_group)
+		wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT);
+	else
+		wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION);
+
+	if (!bss || wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB)) {
+		u8 ext_capab[18];
+		int ext_capab_len;
+		ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
+						     sizeof(ext_capab));
+		if (ext_capab_len > 0 &&
+		    wpa_ie_len + ext_capab_len <= max_wpa_ie_len) {
+			u8 *pos = wpa_ie;
+			if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN)
+				pos += 2 + pos[1];
+			os_memmove(pos + ext_capab_len, pos,
+				   wpa_ie_len - (pos - wpa_ie));
+			wpa_ie_len += ext_capab_len;
+			os_memcpy(pos, ext_capab, ext_capab_len);
+		}
+	}
+
+#ifdef CONFIG_HS20
+	if (is_hs20_network(wpa_s, ssid, bss)) {
+		struct wpabuf *hs20;
+
+		hs20 = wpabuf_alloc(20);
+		if (hs20) {
+			int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
+			size_t len;
+
+			wpas_hs20_add_indication(hs20, pps_mo_id);
+			len = max_wpa_ie_len - wpa_ie_len;
+			if (wpabuf_len(hs20) <= len) {
+				os_memcpy(wpa_ie + wpa_ie_len,
+					  wpabuf_head(hs20), wpabuf_len(hs20));
+				wpa_ie_len += wpabuf_len(hs20);
+			}
+			wpabuf_free(hs20);
+
+			hs20_configure_frame_filters(wpa_s);
+		}
+	}
+#endif /* CONFIG_HS20 */
+
+	if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) {
+		struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
+		size_t len;
+
+		len = max_wpa_ie_len - wpa_ie_len;
+		if (wpabuf_len(buf) <= len) {
+			os_memcpy(wpa_ie + wpa_ie_len,
+				  wpabuf_head(buf), wpabuf_len(buf));
+			wpa_ie_len += wpabuf_len(buf);
+		}
+	}
+
+#ifdef CONFIG_FST
+	if (wpa_s->fst_ies) {
+		int fst_ies_len = wpabuf_len(wpa_s->fst_ies);
+
+		if (wpa_ie_len + fst_ies_len <= max_wpa_ie_len) {
+			os_memcpy(wpa_ie + wpa_ie_len,
+				  wpabuf_head(wpa_s->fst_ies), fst_ies_len);
+			wpa_ie_len += fst_ies_len;
+		}
+	}
+#endif /* CONFIG_FST */
+
+#ifdef CONFIG_MBO
+	if (bss && wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE)) {
+		int len;
+
+		len = wpas_mbo_ie(wpa_s, wpa_ie + wpa_ie_len,
+				  max_wpa_ie_len - wpa_ie_len);
+		if (len >= 0)
+			wpa_ie_len += len;
+	}
+#endif /* CONFIG_MBO */
+
+#ifdef CONFIG_FILS
+	if (algs == WPA_AUTH_ALG_FILS) {
+		size_t len;
+
+		len = wpas_add_fils_hlp_req(wpa_s, wpa_ie + wpa_ie_len,
+					    max_wpa_ie_len - wpa_ie_len);
+		wpa_ie_len += len;
+	}
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+	if (algs == WPA_AUTH_ALG_OPEN &&
+	    ssid->key_mgmt == WPA_KEY_MGMT_OWE) {
+		struct wpabuf *owe_ie;
+		u16 group = OWE_DH_GROUP;
+
+		if (ssid->owe_group)
+			group = ssid->owe_group;
+		owe_ie = owe_build_assoc_req(wpa_s->wpa, group);
+		if (owe_ie &&
+		    wpabuf_len(owe_ie) <= max_wpa_ie_len - wpa_ie_len) {
+			os_memcpy(wpa_ie + wpa_ie_len,
+				  wpabuf_head(owe_ie), wpabuf_len(owe_ie));
+			wpa_ie_len += wpabuf_len(owe_ie);
+			wpabuf_free(owe_ie);
+		}
+	}
+#endif /* CONFIG_OWE */
+
+	params->wpa_ie = wpa_ie;
+	params->wpa_ie_len = wpa_ie_len;
+	params->auth_alg = algs;
+	if (mask)
+		*mask |= WPA_DRV_UPDATE_ASSOC_IES | WPA_DRV_UPDATE_AUTH_TYPE;
+
+	return wpa_ie;
+}
+
+
+#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL)
+static void wpas_update_fils_connect_params(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_driver_associate_params params;
+	enum wpa_drv_update_connect_params_mask mask = 0;
+	u8 *wpa_ie;
+
+	if (wpa_s->auth_alg != WPA_AUTH_ALG_OPEN)
+		return; /* nothing to do */
+
+	os_memset(&params, 0, sizeof(params));
+	wpa_ie = wpas_populate_assoc_ies(wpa_s, wpa_s->current_bss,
+					 wpa_s->current_ssid, &params, &mask);
+	if (!wpa_ie)
+		return;
+
+	if (params.auth_alg != WPA_AUTH_ALG_FILS) {
+		os_free(wpa_ie);
+		return;
+	}
+
+	wpa_s->auth_alg = params.auth_alg;
+	wpa_drv_update_connect_params(wpa_s, &params, mask);
+	os_free(wpa_ie);
+}
+#endif /* CONFIG_FILS && IEEE8021X_EAPOL */
+
+
 static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
 {
 	struct wpa_connect_work *cwork = work->ctx;
 	struct wpa_bss *bss = cwork->bss;
 	struct wpa_ssid *ssid = cwork->ssid;
 	struct wpa_supplicant *wpa_s = work->wpa_s;
-	u8 wpa_ie[200];
-	size_t wpa_ie_len;
+	u8 *wpa_ie;
 	int use_crypt, ret, i, bssid_changed;
-	int algs = WPA_AUTH_ALG_OPEN;
-	unsigned int cipher_pairwise, cipher_group;
+	unsigned int cipher_pairwise, cipher_group, cipher_group_mgmt;
 	struct wpa_driver_associate_params params;
 	int wep_keys_set = 0;
 	int assoc_failed = 0;
@@ -2265,214 +2765,17 @@
 	 * previous association. */
 	wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
 
-#ifdef IEEE8021X_EAPOL
-	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
-		if (ssid->leap) {
-			if (ssid->non_leap == 0)
-				algs = WPA_AUTH_ALG_LEAP;
-			else
-				algs |= WPA_AUTH_ALG_LEAP;
-		}
+	wpa_ie = wpas_populate_assoc_ies(wpa_s, bss, ssid, &params, NULL);
+	if (!wpa_ie) {
+		wpas_connect_work_done(wpa_s);
+		return;
 	}
-#endif /* IEEE8021X_EAPOL */
-	wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs);
-	if (ssid->auth_alg) {
-		algs = ssid->auth_alg;
-		wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: "
-			"0x%x", algs);
-	}
-
-	if (bss && (wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) ||
-		    wpa_bss_get_ie(bss, WLAN_EID_RSN)) &&
-	    wpa_key_mgmt_wpa(ssid->key_mgmt)) {
-		int try_opportunistic;
-		try_opportunistic = (ssid->proactive_key_caching < 0 ?
-				     wpa_s->conf->okc :
-				     ssid->proactive_key_caching) &&
-			(ssid->proto & WPA_PROTO_RSN);
-		if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
-					    ssid, try_opportunistic) == 0)
-			eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
-		wpa_ie_len = sizeof(wpa_ie);
-		if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
-					      wpa_ie, &wpa_ie_len)) {
-			wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
-				"key management and encryption suites");
-			wpas_connect_work_done(wpa_s);
-			return;
-		}
-	} else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && bss &&
-		   wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) {
-		/*
-		 * Both WPA and non-WPA IEEE 802.1X enabled in configuration -
-		 * use non-WPA since the scan results did not indicate that the
-		 * AP is using WPA or WPA2.
-		 */
-		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
-		wpa_ie_len = 0;
-		wpa_s->wpa_proto = 0;
-	} else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
-		wpa_ie_len = sizeof(wpa_ie);
-		if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
-					      wpa_ie, &wpa_ie_len)) {
-			wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
-				"key management and encryption suites (no "
-				"scan results)");
-			wpas_connect_work_done(wpa_s);
-			return;
-		}
-#ifdef CONFIG_WPS
-	} else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
-		struct wpabuf *wps_ie;
-		wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid));
-		if (wps_ie && wpabuf_len(wps_ie) <= sizeof(wpa_ie)) {
-			wpa_ie_len = wpabuf_len(wps_ie);
-			os_memcpy(wpa_ie, wpabuf_head(wps_ie), wpa_ie_len);
-		} else
-			wpa_ie_len = 0;
-		wpabuf_free(wps_ie);
-		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
-		if (!bss || (bss->caps & IEEE80211_CAP_PRIVACY))
-			params.wps = WPS_MODE_PRIVACY;
-		else
-			params.wps = WPS_MODE_OPEN;
-		wpa_s->wpa_proto = 0;
-#endif /* CONFIG_WPS */
-	} else {
-		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
-		wpa_ie_len = 0;
-		wpa_s->wpa_proto = 0;
-	}
-
-#ifdef CONFIG_P2P
-	if (wpa_s->global->p2p) {
-		u8 *pos;
-		size_t len;
-		int res;
-		pos = wpa_ie + wpa_ie_len;
-		len = sizeof(wpa_ie) - wpa_ie_len;
-		res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len,
-					    ssid->p2p_group);
-		if (res >= 0)
-			wpa_ie_len += res;
-	}
-
-	wpa_s->cross_connect_disallowed = 0;
-	if (bss) {
-		struct wpabuf *p2p;
-		p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
-		if (p2p) {
-			wpa_s->cross_connect_disallowed =
-				p2p_get_cross_connect_disallowed(p2p);
-			wpabuf_free(p2p);
-			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: WLAN AP %s cross "
-				"connection",
-				wpa_s->cross_connect_disallowed ?
-				"disallows" : "allows");
-		}
-	}
-
-	os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info));
-#endif /* CONFIG_P2P */
-
-	if (bss) {
-		wpa_ie_len += wpas_supp_op_class_ie(wpa_s, bss->freq,
-						    wpa_ie + wpa_ie_len,
-						    sizeof(wpa_ie) -
-						    wpa_ie_len);
-	}
-
-	/*
-	 * Workaround: Add Extended Capabilities element only if the AP
-	 * included this element in Beacon/Probe Response frames. Some older
-	 * APs seem to have interoperability issues if this element is
-	 * included, so while the standard may require us to include the
-	 * element in all cases, it is justifiable to skip it to avoid
-	 * interoperability issues.
-	 */
-	if (ssid->p2p_group)
-		wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT);
-	else
-		wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION);
-
-	if (!bss || wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB)) {
-		u8 ext_capab[18];
-		int ext_capab_len;
-		ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
-						     sizeof(ext_capab));
-		if (ext_capab_len > 0) {
-			u8 *pos = wpa_ie;
-			if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN)
-				pos += 2 + pos[1];
-			os_memmove(pos + ext_capab_len, pos,
-				   wpa_ie_len - (pos - wpa_ie));
-			wpa_ie_len += ext_capab_len;
-			os_memcpy(pos, ext_capab, ext_capab_len);
-		}
-	}
-
-#ifdef CONFIG_HS20
-	if (is_hs20_network(wpa_s, ssid, bss)) {
-		struct wpabuf *hs20;
-
-		hs20 = wpabuf_alloc(20);
-		if (hs20) {
-			int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
-			size_t len;
-
-			wpas_hs20_add_indication(hs20, pps_mo_id);
-			len = sizeof(wpa_ie) - wpa_ie_len;
-			if (wpabuf_len(hs20) <= len) {
-				os_memcpy(wpa_ie + wpa_ie_len,
-					  wpabuf_head(hs20), wpabuf_len(hs20));
-				wpa_ie_len += wpabuf_len(hs20);
-			}
-			wpabuf_free(hs20);
-
-			hs20_configure_frame_filters(wpa_s);
-		}
-	}
-#endif /* CONFIG_HS20 */
-
-	if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) {
-		struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
-		size_t len;
-
-		len = sizeof(wpa_ie) - wpa_ie_len;
-		if (wpabuf_len(buf) <= len) {
-			os_memcpy(wpa_ie + wpa_ie_len,
-				  wpabuf_head(buf), wpabuf_len(buf));
-			wpa_ie_len += wpabuf_len(buf);
-		}
-	}
-
-#ifdef CONFIG_FST
-	if (wpa_s->fst_ies) {
-		int fst_ies_len = wpabuf_len(wpa_s->fst_ies);
-
-		if (wpa_ie_len + fst_ies_len <= sizeof(wpa_ie)) {
-			os_memcpy(wpa_ie + wpa_ie_len,
-				  wpabuf_head(wpa_s->fst_ies), fst_ies_len);
-			wpa_ie_len += fst_ies_len;
-		}
-	}
-#endif /* CONFIG_FST */
-
-#ifdef CONFIG_MBO
-	if (bss && wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE)) {
-		int len;
-
-		len = wpas_mbo_ie(wpa_s, wpa_ie + wpa_ie_len,
-				  sizeof(wpa_ie) - wpa_ie_len);
-		if (len >= 0)
-			wpa_ie_len += len;
-	}
-#endif /* CONFIG_MBO */
 
 	wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
 	use_crypt = 1;
 	cipher_pairwise = wpa_s->pairwise_cipher;
 	cipher_group = wpa_s->group_cipher;
+	cipher_group_mgmt = wpa_s->mgmt_group_cipher;
 	if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
 	    wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
 		if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE)
@@ -2525,6 +2828,9 @@
 		params.freq_hint = bss->freq;
 		params.pbss = bss_is_pbss(bss);
 	} else {
+		if (ssid->bssid_hint_set)
+			params.bssid_hint = ssid->bssid_hint;
+
 		params.ssid = ssid->ssid;
 		params.ssid_len = ssid->ssid_len;
 		params.pbss = (ssid->pbss != 2) ? ssid->pbss : 0;
@@ -2549,13 +2855,12 @@
 			params.beacon_int = wpa_s->conf->beacon_int;
 	}
 
-	params.wpa_ie = wpa_ie;
-	params.wpa_ie_len = wpa_ie_len;
 	params.pairwise_suite = cipher_pairwise;
 	params.group_suite = cipher_group;
+	params.mgmt_group_suite = cipher_group_mgmt;
 	params.key_mgmt_suite = wpa_s->key_mgmt;
 	params.wpa_proto = wpa_s->wpa_proto;
-	params.auth_alg = algs;
+	wpa_s->auth_alg = params.auth_alg;
 	params.mode = ssid->mode;
 	params.bg_scan_period = ssid->bg_scan_period;
 	for (i = 0; i < NUM_WEP_KEYS; i++) {
@@ -2647,6 +2952,7 @@
 			if (wpas_p2p_handle_frequency_conflicts(
 				    wpa_s, params.freq.freq, ssid) < 0) {
 				wpas_connect_work_done(wpa_s);
+				os_free(wpa_ie);
 				return;
 			}
 		}
@@ -2658,6 +2964,7 @@
 		params.prev_bssid = prev_bssid;
 
 	ret = wpa_drv_associate(wpa_s, &params);
+	os_free(wpa_ie);
 	if (ret < 0) {
 		wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
 			"failed");
@@ -2799,8 +3106,13 @@
 
 #ifdef CONFIG_MESH
 	if (wpa_s->ifmsh) {
+		struct mesh_conf *mconf;
+
+		mconf = wpa_s->ifmsh->mconf;
 		wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_REMOVED "%s",
 			wpa_s->ifname);
+		wpas_notify_mesh_group_removed(wpa_s, mconf->meshid,
+					       mconf->meshid_len, reason_code);
 		wpa_supplicant_leave_mesh(wpa_s);
 	}
 #endif /* CONFIG_MESH */
@@ -2836,8 +3148,9 @@
 		wpa_s->reassociate = 1;
 }
 
+
 /**
- * wpa_supplicant_add_network - Add a new network.
+ * wpa_supplicant_add_network - Add a new network
  * @wpa_s: wpa_supplicant structure for a network interface
  * Returns: The new network configuration or %NULL if operation failed
  *
@@ -2852,9 +3165,8 @@
 	struct wpa_ssid *ssid;
 
 	ssid = wpa_config_add_network(wpa_s->conf);
-	if (!ssid) {
+	if (!ssid)
 		return NULL;
-	}
 	wpas_notify_network_added(wpa_s, ssid);
 	ssid->disabled = 1;
 	wpa_config_set_network_defaults(ssid);
@@ -2862,6 +3174,7 @@
 	return ssid;
 }
 
+
 /**
  * wpa_supplicant_remove_network - Remove a configured network based on id
  * @wpa_s: wpa_supplicant structure for a network interface
@@ -2881,16 +3194,14 @@
 	int was_disabled;
 
 	ssid = wpa_config_get_network(wpa_s->conf, id);
-	if (ssid)
-		wpas_notify_network_removed(wpa_s, ssid);
-	if (ssid == NULL) {
+	if (!ssid)
 		return -1;
-	}
+	wpas_notify_network_removed(wpa_s, ssid);
 
 	if (wpa_s->last_ssid == ssid)
 		wpa_s->last_ssid = NULL;
 
-	if (ssid == wpa_s->current_ssid || wpa_s->current_ssid == NULL) {
+	if (ssid == wpa_s->current_ssid || !wpa_s->current_ssid) {
 #ifdef CONFIG_SME
 		wpa_s->sme.prev_bssid_set = 0;
 #endif /* CONFIG_SME */
@@ -2913,19 +3224,20 @@
 
 	was_disabled = ssid->disabled;
 
-	if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
+	if (wpa_config_remove_network(wpa_s->conf, id) < 0)
 		return -2;
-	}
 
 	if (!was_disabled && wpa_s->sched_scanning) {
-		wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to remove "
-			   "network from filters");
+		wpa_printf(MSG_DEBUG,
+			   "Stop ongoing sched_scan to remove network from filters");
 		wpa_supplicant_cancel_sched_scan(wpa_s);
 		wpa_supplicant_req_scan(wpa_s, 0, 0);
 	}
+
 	return 0;
 }
 
+
 /**
  * wpa_supplicant_enable_network - Mark a configured network as enabled
  * @wpa_s: wpa_supplicant structure for a network interface
@@ -2990,13 +3302,19 @@
 				wpas_notify_network_enabled_changed(
 					wpa_s, other_ssid);
 		}
-		if (wpa_s->current_ssid)
+		if (wpa_s->current_ssid) {
+			if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+				wpa_s->own_disconnect_req = 1;
 			wpa_supplicant_deauthenticate(
 				wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+		}
 	} else if (ssid->disabled != 2) {
-		if (ssid == wpa_s->current_ssid)
+		if (ssid == wpa_s->current_ssid) {
+			if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+				wpa_s->own_disconnect_req = 1;
 			wpa_supplicant_deauthenticate(
 				wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+		}
 
 		was_disabled = ssid->disabled;
 
@@ -3299,6 +3617,41 @@
 }
 
 
+#ifdef CONFIG_OWE
+static int owe_trans_ssid_match(struct wpa_supplicant *wpa_s, const u8 *bssid,
+				const u8 *entry_ssid, size_t entry_ssid_len)
+{
+	const u8 *owe, *pos, *end;
+	u8 ssid_len;
+	struct wpa_bss *bss;
+
+	/* Check network profile SSID aganst the SSID in the
+	 * OWE Transition Mode element. */
+
+	bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
+	if (!bss)
+		return 0;
+
+	owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
+	if (!owe)
+		return 0;
+
+	pos = owe + 6;
+	end = owe + 2 + owe[1];
+
+	if (end - pos < ETH_ALEN + 1)
+		return 0;
+	pos += ETH_ALEN;
+	ssid_len = *pos++;
+	if (end - pos < ssid_len || ssid_len > SSID_MAX_LEN)
+		return 0;
+
+	return entry_ssid_len == ssid_len &&
+		os_memcmp(pos, entry_ssid, ssid_len) == 0;
+}
+#endif /* CONFIG_OWE */
+
+
 /**
  * wpa_supplicant_get_ssid - Get a pointer to the current network structure
  * @wpa_s: Pointer to wpa_supplicant data
@@ -3347,6 +3700,15 @@
 			return entry;
 #endif /* CONFIG_WPS */
 
+#ifdef CONFIG_OWE
+		if (!wpas_network_disabled(wpa_s, entry) &&
+		    owe_trans_ssid_match(wpa_s, bssid, entry->ssid,
+		    entry->ssid_len) &&
+		    (!entry->bssid_set ||
+		     os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
+			return entry;
+#endif /* CONFIG_OWE */
+
 		if (!wpas_network_disabled(wpa_s, entry) && entry->bssid_set &&
 		    entry->ssid_len == 0 &&
 		    os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)
@@ -3454,16 +3816,6 @@
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
 
-#ifdef CONFIG_PEERKEY
-	if (wpa_s->wpa_state > WPA_ASSOCIATED && wpa_s->current_ssid &&
-	    wpa_s->current_ssid->peerkey &&
-	    !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
-	    wpa_sm_rx_eapol_peerkey(wpa_s->wpa, src_addr, buf, len) == 1) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "RSN: Processed PeerKey EAPOL-Key");
-		return;
-	}
-#endif /* CONFIG_PEERKEY */
-
 	if (wpa_s->wpa_state < WPA_ASSOCIATED ||
 	    (wpa_s->last_eapol_matches_bssid &&
 #ifdef CONFIG_AP
@@ -3574,6 +3926,8 @@
 
 	os_memcpy(wpa_s->last_eapol_src, src_addr, ETH_ALEN);
 	if (!wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) &&
+	    wpa_s->key_mgmt != WPA_KEY_MGMT_OWE &&
+	    wpa_s->key_mgmt != WPA_KEY_MGMT_DPP &&
 	    eapol_sm_rx_eapol(wpa_s->eapol, src_addr, buf, len) > 0)
 		return;
 	wpa_drv_poll(wpa_s);
@@ -4765,11 +5119,47 @@
 }
 
 
+#ifdef CONFIG_GAS_SERVER
+
+static void wpas_gas_server_tx_status(struct wpa_supplicant *wpa_s,
+				      unsigned int freq, const u8 *dst,
+				      const u8 *src, const u8 *bssid,
+				      const u8 *data, size_t data_len,
+				      enum offchannel_send_action_result result)
+{
+	wpa_printf(MSG_DEBUG, "GAS: TX status: freq=%u dst=" MACSTR
+		   " result=%s",
+		   freq, MAC2STR(dst),
+		   result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" :
+		   (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" :
+		    "FAILED"));
+	gas_server_tx_status(wpa_s->gas_server, dst, data, data_len,
+			     result == OFFCHANNEL_SEND_ACTION_SUCCESS);
+}
+
+
+static void wpas_gas_server_tx(void *ctx, int freq, const u8 *da,
+			       struct wpabuf *buf, unsigned int wait_time)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+	if (wait_time > wpa_s->max_remain_on_chan)
+		wait_time = wpa_s->max_remain_on_chan;
+
+	offchannel_send_action(wpa_s, freq, da, wpa_s->own_addr, broadcast,
+			       wpabuf_head(buf), wpabuf_len(buf),
+			       wait_time, wpas_gas_server_tx_status, 0);
+}
+
+#endif /* CONFIG_GAS_SERVER */
+
 static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
 				     struct wpa_interface *iface)
 {
 	struct wpa_driver_capa capa;
 	int capa_res;
+	u8 dfs_domain;
 
 	wpa_printf(MSG_DEBUG, "Initializing interface '%s' conf '%s' driver "
 		   "'%s' ctrl_interface '%s' bridge '%s'", iface->ifname,
@@ -4897,7 +5287,8 @@
 
 	wpa_s->hw.modes = wpa_drv_get_hw_feature_data(wpa_s,
 						      &wpa_s->hw.num_modes,
-						      &wpa_s->hw.flags);
+						      &wpa_s->hw.flags,
+						      &dfs_domain);
 	if (wpa_s->hw.modes) {
 		u16 i;
 
@@ -5007,6 +5398,19 @@
 	if (wpas_wps_init(wpa_s))
 		return -1;
 
+#ifdef CONFIG_GAS_SERVER
+	wpa_s->gas_server = gas_server_init(wpa_s, wpas_gas_server_tx);
+	if (!wpa_s->gas_server) {
+		wpa_printf(MSG_ERROR, "Failed to initialize GAS server");
+		return -1;
+	}
+#endif /* CONFIG_GAS_SERVER */
+
+#ifdef CONFIG_DPP
+	if (wpas_dpp_init(wpa_s) < 0)
+		return -1;
+#endif /* CONFIG_DPP */
+
 	if (wpa_supplicant_init_eapol(wpa_s) < 0)
 		return -1;
 	wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol);
@@ -5056,8 +5460,8 @@
 #ifdef CONFIG_EAP_PROXY
 {
 	size_t len;
-	wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, wpa_s->imsi,
-						     &len);
+	wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, -1,
+						     wpa_s->imsi, &len);
 	if (wpa_s->mnc_len > 0) {
 		wpa_s->imsi[len] = '\0';
 		wpa_printf(MSG_DEBUG, "eap_proxy: IMSI %s (MNC length %d)",
@@ -5082,6 +5486,17 @@
 	hs20_init(wpa_s);
 #endif /* CONFIG_HS20 */
 #ifdef CONFIG_MBO
+	if (wpa_s->conf->oce) {
+		if ((wpa_s->conf->oce & OCE_STA) &&
+		    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA))
+			wpa_s->enable_oce = OCE_STA;
+		if ((wpa_s->conf->oce & OCE_STA_CFON) &&
+		    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA_CFON)) {
+			/* TODO: Need to add STA-CFON support */
+			wpa_printf(MSG_ERROR,
+				   "OCE STA-CFON feature is not yet supported");
+		}
+	}
 	wpas_mbo_update_non_pref_chan(wpa_s, wpa_s->conf->non_pref_chan);
 #endif /* CONFIG_MBO */
 
@@ -5748,6 +6163,16 @@
 	if (wpa_s->conf->changed_parameters & CFG_CHANGED_SCHED_SCAN_PLANS)
 		wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans);
 
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_WOWLAN_TRIGGERS) {
+		struct wpa_driver_capa capa;
+		int res = wpa_drv_get_capa(wpa_s, &capa);
+
+		if (res == 0 && wpas_set_wowlan_triggers(wpa_s, &capa) < 0)
+			wpa_printf(MSG_ERROR,
+				   "Failed to update wowlan_triggers to '%s'",
+				   wpa_s->conf->wowlan_triggers);
+	}
+
 #ifdef CONFIG_WPS
 	wpas_wps_update_config(wpa_s);
 #endif /* CONFIG_WPS */
@@ -6062,6 +6487,7 @@
 
 	if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set &&
 	    (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk &&
+	    !(wpa_key_mgmt_sae(ssid->key_mgmt) && ssid->sae_password) &&
 	    !ssid->mem_only_psk)
 		return 1;
 
@@ -6253,6 +6679,7 @@
 		wpa_s->reattach = 0;
 }
 
+
 /**
  * wpas_request_disconnection - Request disconnection
  * @wpa_s: Pointer to the network interface
@@ -6484,18 +6911,56 @@
 }
 
 
+static int wpa_set_driver_tmp_disallow_list(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_bss_tmp_disallowed *tmp;
+	unsigned int num_bssid = 0;
+	u8 *bssids;
+	int ret;
+
+	bssids = os_malloc(dl_list_len(&wpa_s->bss_tmp_disallowed) * ETH_ALEN);
+	if (!bssids)
+		return -1;
+	dl_list_for_each(tmp, &wpa_s->bss_tmp_disallowed,
+			 struct wpa_bss_tmp_disallowed, list) {
+		os_memcpy(&bssids[num_bssid * ETH_ALEN], tmp->bssid,
+			  ETH_ALEN);
+		num_bssid++;
+	}
+	ret = wpa_drv_set_bssid_blacklist(wpa_s, num_bssid, bssids);
+	os_free(bssids);
+	return ret;
+}
+
+
+static void wpa_bss_tmp_disallow_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct wpa_bss_tmp_disallowed *tmp, *bss = timeout_ctx;
+
+	/* Make sure the bss is not already freed */
+	dl_list_for_each(tmp, &wpa_s->bss_tmp_disallowed,
+			 struct wpa_bss_tmp_disallowed, list) {
+		if (bss == tmp) {
+			dl_list_del(&tmp->list);
+			os_free(tmp);
+			wpa_set_driver_tmp_disallow_list(wpa_s);
+			break;
+		}
+	}
+}
+
+
 void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid,
 			  unsigned int sec)
 {
 	struct wpa_bss_tmp_disallowed *bss;
-	struct os_reltime until;
-
-	os_get_reltime(&until);
-	until.sec += sec;
 
 	bss = wpas_get_disallowed_bss(wpa_s, bssid);
 	if (bss) {
-		bss->disallowed_until = until;
+		eloop_cancel_timeout(wpa_bss_tmp_disallow_timeout, wpa_s, bss);
+		eloop_register_timeout(sec, 0, wpa_bss_tmp_disallow_timeout,
+				       wpa_s, bss);
 		return;
 	}
 
@@ -6506,27 +6971,20 @@
 		return;
 	}
 
-	bss->disallowed_until = until;
 	os_memcpy(bss->bssid, bssid, ETH_ALEN);
 	dl_list_add(&wpa_s->bss_tmp_disallowed, &bss->list);
+	wpa_set_driver_tmp_disallow_list(wpa_s);
+	eloop_register_timeout(sec, 0, wpa_bss_tmp_disallow_timeout,
+			       wpa_s, bss);
 }
 
 
 int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid)
 {
 	struct wpa_bss_tmp_disallowed *bss = NULL, *tmp, *prev;
-	struct os_reltime now, age;
-
-	os_get_reltime(&now);
 
 	dl_list_for_each_safe(tmp, prev, &wpa_s->bss_tmp_disallowed,
 			 struct wpa_bss_tmp_disallowed, list) {
-		if (!os_reltime_before(&now, &tmp->disallowed_until)) {
-			/* This BSS is not disallowed anymore */
-			dl_list_del(&tmp->list);
-			os_free(tmp);
-			continue;
-		}
 		if (os_memcmp(bssid, tmp->bssid, ETH_ALEN) == 0) {
 			bss = tmp;
 			break;
@@ -6535,9 +6993,5 @@
 	if (!bss)
 		return 0;
 
-	os_reltime_sub(&bss->disallowed_until, &now, &age);
-	wpa_printf(MSG_DEBUG,
-		   "BSS " MACSTR " disabled for %ld.%0ld seconds",
-		   MAC2STR(bss->bssid), age.sec, age.usec);
 	return 1;
 }
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 6faa7af..61eb38f 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -183,13 +183,13 @@
 # OpenSSL cipher string
 #
 # This is an OpenSSL specific configuration option for configuring the default
-# ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the default.
+# ciphers. If not set, the value configured at build time ("DEFAULT:!EXP:!LOW"
+# by default) is used.
 # See https://www.openssl.org/docs/apps/ciphers.html for OpenSSL documentation
 # on cipher suite configuration. This is applicable only if wpa_supplicant is
 # built to use OpenSSL.
 #openssl_ciphers=DEFAULT:!EXP:!LOW
 
-
 # Dynamic EAP methods
 # If EAP methods were built dynamically as shared object files, they need to be
 # loaded here before being used in the network blocks. By default, EAP methods
@@ -218,9 +218,15 @@
 # Wi-Fi Protected Setup (WPS) parameters
 
 # Universally Unique IDentifier (UUID; see RFC 4122) of the device
-# If not configured, UUID will be generated based on the local MAC address.
+# If not configured, UUID will be generated based on the mechanism selected with
+# the auto_uuid parameter.
 #uuid=12345678-9abc-def0-1234-56789abcdef0
 
+# Automatic UUID behavior
+# 0 = generate static value based on the local MAC address (default)
+# 1 = generate a random UUID every time wpa_supplicant starts
+#auto_uuid=0
+
 # Device Name
 # User-friendly description of device; up to 32 octets encoded in UTF-8
 #device_name=Wireless Client
@@ -436,6 +442,36 @@
 # Enable Interworking
 # interworking=1
 
+# Enable P2P GO advertisement of Interworking
+# go_interworking=1
+
+# P2P GO Interworking: Access Network Type
+# 0 = Private network
+# 1 = Private network with guest access
+# 2 = Chargeable public network
+# 3 = Free public network
+# 4 = Personal device network
+# 5 = Emergency services only network
+# 14 = Test or experimental
+# 15 = Wildcard
+#go_access_network_type=0
+
+# P2P GO Interworking: Whether the network provides connectivity to the Internet
+# 0 = Unspecified
+# 1 = Network provides connectivity to the Internet
+#go_internet=1
+
+# P2P GO Interworking: Group Venue Info (optional)
+# The available values are defined in IEEE Std 802.11-2016, 9.4.1.35.
+# Example values (group,type):
+# 0,0 = Unspecified
+# 1,7 = Convention Center
+# 1,13 = Coffee Shop
+# 2,0 = Unspecified Business
+# 7,1  Private Residence
+#go_venue_group=7
+#go_venue_type=1
+
 # Homogenous ESS identifier
 # If this is set, scans will be used to request response only from BSSes
 # belonging to the specified Homogeneous ESS. This is used only if interworking
@@ -688,7 +724,7 @@
 # Format:
 # non_pref_chan=<oper_class>:<chan>:<preference>:<reason>
 # Example:
-# non_pref_chan="81:5:10:2 81:1:0:2 81:9:0:2"
+# non_pref_chan=81:5:10:2 81:1:0:2 81:9:0:2
 
 # MBO Cellular Data Capabilities
 # 1 = Cellular data connection available
@@ -696,6 +732,13 @@
 # 3 = Not cellular capable (default)
 #mbo_cell_capa=3
 
+# Optimized Connectivity Experience (OCE)
+# oce: Enable OCE features (bitmap)
+# Set BIT(0) to Enable OCE in non-AP STA mode (default; disabled if the driver
+#	does not indicate support for OCE in STA mode)
+# Set BIT(1) to Enable OCE in STA-CFON mode
+#oce=1
+
 # network block
 #
 # Each network (usually AP's sharing the same SSID) is configured as a separate
@@ -866,6 +909,14 @@
 # WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key [IEEE 802.11]
 # If not set, this defaults to: CCMP TKIP WEP104 WEP40
 #
+# group_mgmt: list of accepted group management ciphers for RSN (PMF)
+# AES-128-CMAC = BIP-CMAC-128
+# BIP-GMAC-128
+# BIP-GMAC-256
+# BIP-CMAC-256
+# If not set, no constraint on the cipher, i.e., accept whichever cipher the AP
+# indicates.
+#
 # psk: WPA preshared key; 256-bit pre-shared key
 # The key used in WPA-PSK mode can be entered either as 64 hex-digits, i.e.,
 # 32 bytes or as an ASCII passphrase (in which case, the real PSK will be
@@ -883,6 +934,12 @@
 # 1 = do not store psk/passphrase to the configuration file
 #mem_only_psk=0
 #
+# sae_password: SAE password
+# This parameter can be used to set a password for SAE. By default, the
+# passphrase value is used if this separate parameter is not used, but
+# passphrase follows the WPA-PSK constraints (8..63 characters) even
+# though SAE passwords do not have such constraints.
+#
 # eapol_flags: IEEE 802.1X/EAPOL options (bit field)
 # Dynamic WEP key required for non-WPA mode
 # bit0 (1): require dynamically generated unicast WEP key
@@ -935,12 +992,6 @@
 # hex without quotation, e.g., 0102030405)
 # wep_tx_keyidx: Default WEP key index (TX) (0..3)
 #
-# peerkey: Whether PeerKey negotiation for direct links (IEEE 802.11e DLS) is
-# allowed. This is only used with RSN/WPA2.
-# 0 = disabled (default)
-# 1 = enabled
-#peerkey=1
-#
 # wpa_ptk_rekey: Maximum lifetime for PTK in seconds. This can be used to
 # enforce rekeying of PTK to mitigate some attacks against TKIP deficiencies.
 #
@@ -1151,6 +1202,9 @@
 #	chain when receiving CTRL-RSP-EXT_CERT_CHECK event from the control
 #	interface and report the result of the validation with
 #	CTRL-RSP_EXT_CERT_CHECK.
+# tls_suiteb=0 - do not apply Suite B 192-bit constraints on TLS (default)
+# tls_suiteb=1 - apply Suite B 192-bit constraints on TLS; this is used in
+#	particular when using Suite B with RSA keys of >= 3K (3072) bits
 #
 # Following certificate/private key fields are used in inner Phase2
 # authentication when using EAP-TTLS or EAP-PEAP.
@@ -1248,6 +1302,11 @@
 # 1 = WPS disabled
 #wps_disabled=0
 
+# FILS DH Group
+# 0 = PFS disabled with FILS shared key authentication (default)
+# 1-65535 = DH Group to use for FILS PFS
+#fils_dh_group=0
+
 # MAC address policy
 # 0 = use permanent MAC address
 # 1 = use random MAC address for each ESS connection
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 395be3d..d0172fa 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -296,7 +296,7 @@
 
 #ifdef CONFIG_WIFI_DISPLAY
 	int wifi_display;
-#define MAX_WFD_SUBELEMS 10
+#define MAX_WFD_SUBELEMS 12
 	struct wpabuf *wfd_subelem[MAX_WFD_SUBELEMS];
 #endif /* CONFIG_WIFI_DISPLAY */
 
@@ -428,6 +428,9 @@
 
 	/* token - Dialog token of the current radio measurement */
 	u8 token;
+
+	/* destination address of the current radio measurement request */
+	u8 dst_addr[ETH_ALEN];
 };
 
 enum wpa_supplicant_test_failure {
@@ -447,7 +450,6 @@
 struct wpa_bss_tmp_disallowed {
 	struct dl_list list;
 	u8 bssid[ETH_ALEN];
-	struct os_reltime disallowed_until;
 };
 
 struct beacon_rep_data {
@@ -665,6 +667,7 @@
 	struct os_reltime scan_min_time;
 	int scan_runs; /* number of scan runs since WPS was started */
 	int *next_scan_freqs;
+	int *select_network_scan_freqs;
 	int *manual_scan_freqs;
 	int *manual_sched_scan_freqs;
 	unsigned int manual_scan_passive:1;
@@ -747,6 +750,7 @@
 	int sta_uapsd;
 	int set_ap_uapsd;
 	int ap_uapsd;
+	int auth_alg;
 
 #ifdef CONFIG_SME
 	struct {
@@ -825,6 +829,7 @@
 					    result);
 	unsigned int roc_waiting_drv_freq;
 	int action_tx_wait_time;
+	int action_tx_wait_time_used;
 
 	int p2p_mgmt;
 
@@ -959,6 +964,7 @@
 	int best_overall_freq;
 
 	struct gas_query *gas;
+	struct gas_server *gas_server;
 
 #ifdef CONFIG_INTERWORKING
 	unsigned int fetch_anqp_in_progress:1;
@@ -1045,6 +1051,10 @@
 	struct neighbor_report *wnm_neighbor_report_elements;
 	struct os_reltime wnm_cand_valid_until;
 	u8 wnm_cand_from_bss[ETH_ALEN];
+#ifdef CONFIG_MBO
+	unsigned int wnm_mbo_trans_reason_present:1;
+	u8 wnm_mbo_transition_reason;
+#endif /* CONFIG_MBO */
 #endif /* CONFIG_WNM */
 
 #ifdef CONFIG_TESTING_GET_GTK
@@ -1068,6 +1078,14 @@
 	unsigned int p2p_go_csa_on_inv:1;
 	unsigned int ignore_auth_resp:1;
 	unsigned int ignore_assoc_disallow:1;
+	unsigned int testing_resend_assoc:1;
+	struct wpabuf *sae_commit_override;
+	enum wpa_alg last_tk_alg;
+	u8 last_tk_addr[ETH_ALEN];
+	int last_tk_key_idx;
+	u8 last_tk[WPA_TK_MAX_LEN];
+	size_t last_tk_len;
+	struct wpabuf *last_assoc_req_wpa_ie;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	struct wmm_ac_assoc_data *wmm_ac_assoc_info;
@@ -1096,6 +1114,14 @@
 	} *non_pref_chan;
 	size_t non_pref_chan_num;
 	u8 mbo_wnm_token;
+	/**
+	 * enable_oce - Enable OCE if it is enabled by user and device also
+	 *		supports OCE.
+	 * User can enable OCE with wpa_config's 'oce' parameter as follows -
+	 *  - Set BIT(0) to enable OCE in non-AP STA mode.
+	 *  - Set BIT(1) to enable OCE in STA-CFON mode.
+	 */
+	u8 enable_oce;
 #endif /* CONFIG_MBO */
 
 	/*
@@ -1151,6 +1177,35 @@
 
 	/* RIC elements for FT protocol */
 	struct wpabuf *ric_ies;
+
+#ifdef CONFIG_DPP
+	struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */
+	struct dl_list dpp_configurator; /* struct dpp_configurator */
+	int dpp_init_done;
+	struct dpp_authentication *dpp_auth;
+	struct wpa_radio_work *dpp_listen_work;
+	unsigned int dpp_pending_listen_freq;
+	unsigned int dpp_listen_freq;
+	u8 dpp_allowed_roles;
+	int dpp_qr_mutual;
+	int dpp_netrole_ap;
+	int dpp_auth_ok_on_ack;
+	int dpp_gas_client;
+	u8 dpp_intro_bssid[ETH_ALEN];
+	void *dpp_intro_network;
+	struct dpp_pkex *dpp_pkex;
+	struct dpp_bootstrap_info *dpp_pkex_bi;
+	char *dpp_pkex_code;
+	char *dpp_pkex_identifier;
+	char *dpp_pkex_auth_cmd;
+	char *dpp_configurator_params;
+#ifdef CONFIG_TESTING_OPTIONS
+	char *dpp_config_obj_override;
+	char *dpp_discovery_override;
+	char *dpp_groups_override;
+	unsigned int dpp_ignore_netaccesskey_mismatch:1;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_DPP */
 };
 
 
@@ -1265,12 +1320,13 @@
 						  struct wpabuf *neighbor_rep),
 				       void *cb_ctx);
 void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s,
-					       const u8 *src,
+					       const u8 *src, const u8 *dst,
 					       const u8 *frame, size_t len);
 void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
 					      const u8 *src,
 					      const u8 *frame, size_t len,
 					      int rssi);
+void wpas_rrm_refuse_request(struct wpa_supplicant *wpa_s);
 int wpas_beacon_rep_scan_process(struct wpa_supplicant *wpa_s,
 				 struct wpa_scan_results *scan_res,
 				 struct scan_info *info);
@@ -1291,7 +1347,10 @@
 				    enum mbo_transition_reject_reason reason);
 void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa);
 struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s,
-				   struct wpa_bss *bss);
+				   struct wpa_bss *bss, u32 mbo_subtypes);
+void mbo_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
+			    struct wpa_bss *bss, const u8 *sa,
+			    const u8 *data, size_t slen);
 
 /* op_classes.c */
 enum chan_allowed {
@@ -1347,6 +1406,7 @@
 int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s);
 struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
 					     struct wpa_ssid **selected_ssid);
+int wpas_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
 
 /* eap_register.c */
 int eap_register_methods(void);
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 0d753a9..e44f6af 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -146,6 +146,8 @@
 	 * extra copy here */
 
 	if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_OWE ||
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_DPP ||
 	    wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) {
 		/* Current SSID is not using IEEE 802.1X/EAP, so drop possible
 		 * EAPOL frames (mainly, EAPOL-Start) from EAPOL state
@@ -500,6 +502,16 @@
 		wpa_s->last_gtk_len = key_len;
 	}
 #endif /* CONFIG_TESTING_GET_GTK */
+#ifdef CONFIG_TESTING_OPTIONS
+	if (addr && !is_broadcast_ether_addr(addr)) {
+		wpa_s->last_tk_alg = alg;
+		os_memcpy(wpa_s->last_tk_addr, addr, ETH_ALEN);
+		wpa_s->last_tk_key_idx = key_idx;
+		if (key)
+			os_memcpy(wpa_s->last_tk, key, key_len);
+		wpa_s->last_tk_len = key_len;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 	return wpa_drv_set_key(wpa_s, alg, addr, key_idx, set_tx, seq, seq_len,
 			       key, key_len);
 }
@@ -529,30 +541,59 @@
 
 
 static int wpa_supplicant_add_pmkid(void *_wpa_s, void *network_ctx,
-				    const u8 *bssid, const u8 *pmkid)
+				    const u8 *bssid, const u8 *pmkid,
+				    const u8 *fils_cache_id,
+				    const u8 *pmk, size_t pmk_len)
 {
 	struct wpa_supplicant *wpa_s = _wpa_s;
 	struct wpa_ssid *ssid;
+	struct wpa_pmkid_params params;
 
+	os_memset(&params, 0, sizeof(params));
 	ssid = wpas_get_network_ctx(wpa_s, network_ctx);
 	if (ssid)
 		wpa_msg(wpa_s, MSG_INFO, PMKSA_CACHE_ADDED MACSTR " %d",
 			MAC2STR(bssid), ssid->id);
-	return wpa_drv_add_pmkid(wpa_s, bssid, pmkid);
+	if (ssid && fils_cache_id) {
+		params.ssid = ssid->ssid;
+		params.ssid_len = ssid->ssid_len;
+		params.fils_cache_id = fils_cache_id;
+	} else {
+		params.bssid = bssid;
+	}
+
+	params.pmkid = pmkid;
+	params.pmk = pmk;
+	params.pmk_len = pmk_len;
+
+	return wpa_drv_add_pmkid(wpa_s, &params);
 }
 
 
 static int wpa_supplicant_remove_pmkid(void *_wpa_s, void *network_ctx,
-				       const u8 *bssid, const u8 *pmkid)
+				       const u8 *bssid, const u8 *pmkid,
+				       const u8 *fils_cache_id)
 {
 	struct wpa_supplicant *wpa_s = _wpa_s;
 	struct wpa_ssid *ssid;
+	struct wpa_pmkid_params params;
 
+	os_memset(&params, 0, sizeof(params));
 	ssid = wpas_get_network_ctx(wpa_s, network_ctx);
 	if (ssid)
 		wpa_msg(wpa_s, MSG_INFO, PMKSA_CACHE_REMOVED MACSTR " %d",
 			MAC2STR(bssid), ssid->id);
-	return wpa_drv_remove_pmkid(wpa_s, bssid, pmkid);
+	if (ssid && fils_cache_id) {
+		params.ssid = ssid->ssid;
+		params.ssid_len = ssid->ssid_len;
+		params.fils_cache_id = fils_cache_id;
+	} else {
+		params.bssid = bssid;
+	}
+
+	params.pmkid = pmkid;
+
+	return wpa_drv_remove_pmkid(wpa_s, &params);
 }
 
 
@@ -900,7 +941,7 @@
 	struct wpa_supplicant *wpa_s = ctx;
 	size_t len;
 
-	wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol,
+	wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, -1,
 						     wpa_s->imsi, &len);
 	if (wpa_s->mnc_len > 0) {
 		wpa_s->imsi[len] = '\0';
@@ -1204,7 +1245,6 @@
 	if (ssid) {
 		os_memset(&conf, 0, sizeof(conf));
 		conf.network_ctx = ssid;
-		conf.peerkey_enabled = ssid->peerkey;
 		conf.allowed_pairwise_cipher = ssid->pairwise_cipher;
 #ifdef IEEE8021X_EAPOL
 		conf.proactive_key_caching = ssid->proactive_key_caching < 0 ?
@@ -1232,6 +1272,11 @@
 		}
 #endif /* CONFIG_P2P */
 		conf.wpa_rsc_relaxation = wpa_s->conf->wpa_rsc_relaxation;
+#ifdef CONFIG_FILS
+		if (wpa_key_mgmt_fils(wpa_s->key_mgmt))
+			conf.fils_cache_id =
+				wpa_bss_get_fils_cache_id(wpa_s->current_bss);
+#endif /* CONFIG_FILS */
 	}
 	wpa_sm_set_config(wpa_s->wpa, ssid ? &conf : NULL);
 }
diff --git a/wpa_supplicant/wpas_kay.c b/wpa_supplicant/wpas_kay.c
index d087e00..587e5c3 100644
--- a/wpa_supplicant/wpas_kay.c
+++ b/wpa_supplicant/wpas_kay.c
@@ -235,10 +235,9 @@
 	res = ieee802_1x_kay_init(kay_ctx, policy, ssid->macsec_port,
 				  ssid->mka_priority, wpa_s->ifname,
 				  wpa_s->own_addr);
-	if (res == NULL) {
-		os_free(kay_ctx);
+	/* ieee802_1x_kay_init() frees kay_ctx on failure */
+	if (res == NULL)
 		return -1;
-	}
 
 	wpa_s->kay = res;
 
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 228269e..bd8af83 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -1027,10 +1027,9 @@
 				continue;
 
 			os_free(ssid->ssid);
-			ssid->ssid = os_malloc(bss->ssid_len);
+			ssid->ssid = os_memdup(bss->ssid, bss->ssid_len);
 			if (ssid->ssid == NULL)
 				break;
-			os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
 			ssid->ssid_len = bss->ssid_len;
 			wpa_hexdump_ascii(MSG_DEBUG, "WPS: Picked SSID from "
 					  "scan results",
@@ -1169,6 +1168,7 @@
 		return -1;
 	if (wpa_s->wps_fragment_size)
 		ssid->eap.fragment_size = wpa_s->wps_fragment_size;
+	wpa_supplicant_wps_event(wpa_s, WPS_EV_PBC_ACTIVE, NULL);
 	eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
 			       wpa_s, NULL);
 	wpas_wps_reassoc(wpa_s, ssid, bssid, 0);
@@ -1481,6 +1481,9 @@
 					  wpa_s->global->ifaces->wps->uuid,
 					  WPS_UUID_LEN);
 			src = "from the first interface";
+		} else if (wpa_s->conf->auto_uuid == 1) {
+			uuid_random(wps->uuid);
+			src = "based on random data";
 		} else {
 			uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid);
 			src = "based on MAC address";