Cumulative patch from commit 4ec1fd8e42bad9390f14a58225b6e5f6fb691950

4ec1fd8 FT: Differentiate between FT for station and for AP in build
f0259c3 hostapd: Fix own wide bandwidth subelement generation (neighbor report)
0a63635 AP: Use valid status code in wpa_ft_send_rrb_auth_resp()
e4b48b7 Extend ieee80211_freq_to_channel_ext() to cover channels 52-64
c433c50 wpa_supplicant: Make CONFIG_MBO independent of CONFIG_AP
d044d2f wpa_supplicant: Get scan_result IE also from Beacon frames
451a27b hostapd: Add a configuration to set an AP as stationary
5cb5937 hostapd: Clear location configuration when it is reset
f5ec346 hostapd: Fix adding neighbor entry
99b82bf mka: Implement reference counting on data_key
23c3528 mka: Add support for removing SAs
6b6175b mka: Sync structs definitions with IEEE Std 802.1X-2010
6f551ab mka: Remove "channel" hacks from the stack and the macsec_qca driver
7d8f795 Fix typo in DigestAlgorithn
f5c15dc Fix typo in eap_example_server.c
59d7cff AP: Disable VHT in TKIP-only configuration
847ee1a wpa_supplicant: Use correct interface type when creating P2P interface
78a3b23 P2P: Clear old P2PS provision data
f69939e P2P: Clear listen state during PD-in-FIND
4cc0f90 P2P: Clear P2PS provision state on P2P flush
a818425 hostapd: Added signal level to STA tracking
a1047f5 Remove duplicate dl_list_init() for global_ctrl_dst
3765c97 hostapd_cli: Remove duplicate const in hostapd_cli_cmd()
8c88922 TDLS: Fix checks on prohibit bits
c2ad5b9 nl80211: Update channel information after channel switch notification
913c3e1 Add CONFIG_IBSS_RSN=y into wpa_supplicant defconfig
81a10a9 Do not try to start/join RSN IBSS without CONFIG_IBSS_RSN=y
ea06a08 HS 2.0 server: Remove redundant NULL check
5f99d96 Removed redundant NULL check for sta in hostapd_event_sta_low_ack()
230b2b2 Removed redundant NULL check for b in wpabuf_concat()
641c73f driver.h: Fix a typo in a comment
2e4e4fb nl80211: Allow TDLS trigger modes to be configured to the host driver
14cd203 QCA vendor command to configure the TDLS behavior in the host driver
a18563d Extend QCA vendor attribute link layer statistics attribute
95f3703 Add more QCA vendor attribute definitions into qca-vendor.h
87416ea QCA vendor attribute to report frame aggregation failure
befdb2d nl80211: Check driver FILS capability
40a4572 nl80211: FILS KEK and nonces for NL80211_CMD_ASSOCIATE
d8f9342 nl80211: Add support for setting FILS authentication algorithm
e76e950 Sync with mac80211-next.git include/uapi/linux/nl80211.h
2a0b86d Note set_key(WPA_ALG_NONE) failure in debug log
061dac1 FILS: Claim FILS capability only if driver supports it
ff338fa FILS: Setup EAPOL state machines properly after FILS association (AP)
da24c5a FILS: Set TK after association (AP)
07e0117 FILS: Mark connection fully authorized after FILS Association (AP)
706df42 FILS: Association Response processing (STA)
e73ffa0 FILS: Add Association Response frame elements and encrypt them (AP)
78815f3 FILS: Decrypt Association Request elements and check Key-Auth (AP)
86cd692 FILS: Add elements to FILS Association Request frame
ac56c39 driver: Add option to pass FILS KEK/AAD to the driver for association
783c292 P2P: Check if the pref_freq reported by the driver supports P2P
a660993 FILS: Authentication frame processing (STA)
c4fd6d8 FILS: Process FILS Authentication frame (AP)
ffb62f2 FILS: Add a helper function for status code conversion
c1bd4ba FILS: Extend wpa_auth_pmksa_get() to support PMKID matching
c30bd28 FILS: Export IEEE 802.1X helper functions
a6228b8 ERP: Update client identity based on EAP-Initiate/Re-auth
f00b9b8 FILS: Try to use FILS authentication if PMKSA or ERP entry is available
2c2c557 SME: Clear possibly used WPA/RSN IE for new connection
0866ed0 WPA: Add debug print for not-update-own-IEs case
14de9e3 FILS: Include wpa_insert_pmkid() in non-FT builds
de57d87 ERP: Make eap_peer_finish() callable
c28767e ERP: Make eap_peer_erp_reauth_start() available
5b092fb nl80211: Make full (Re)Association Response frame available
2aa1e48 FILS: Do not clear PTK on FILS Auth/Assoc (AP)
a852ab4 FILS: Key-Auth derivation function for FILS SK
c089bc5 FILS: PMK-to-PTK key derivation for FILS authentication
ce16c48 Rename sae_data to more generic auth_data
6eb1a56 Add QCA vendor command/attr for low level DMG(11ad) RF sector control
a2675b3 wpa_cli: Mark number of char *cmd constant
e097556 hostapd_cli: Mark number of char *cmd constant
c43cf33 wpa_cli: Add completion for ssid config commands
624259d wpa_cli: Add completion for sta, deauthenticate and disassociate
4c43f44 cli: Add list_sta command
85bab32 hostapd_cli: Process events received following control iface commands
e054a43 hostapd_cli: Refactor control iface reconnects with common helper
aa2ab91 hostapd_cli: Refresh stations list on control interface reconnect
839e4a8 hostapd_cli: Add completion for sta command
bc4b680 hostapd_cli: Enable command completion and history for Android
cf296a2 hostapd_cli: Add support for cli history file
c650f92 hostapd: Add CONFIG_WPA_CLI_EDIT to defconfig
5d30f92 wpa_supplicant: Restore permanent MAC address on reassociation
e3e2fe3 Always propagate scan results to all interfaces
33111c9 Check for NULL qsort() base pointers
4b5b8a5 WPS: Force BSSID for WPS provisioning step connection
bf07e05 ERP: Do not pass full EAP header to eap_peer_erp_reauth_start()
2449791 FILS: Update EAPOL-Key Descriptor Version RX rules (AP)
16eb485 FILS: Handle Group Key msg 1/2 without MIC when using AEAD cipher (STA)
75c8563 FILS: Perform AEAD processing after PTK has been confirmed
0ab1dd0 FILS: Use AEAD cipher to check received EAPOL-Key frames (STA)
b729fd8 FILS: Use AEAD cipher to protect EAPOL-Key frames (AP)
3b5b7aa FILS: Use AEAD cipher to check received EAPOL-Key frames (AP)
2022f1d FILS: Use AEAD cipher to protect EAPOL-Key frames (STA)
1049af7 RSN: Pass full PTK to wpa_eapol_key_send() instead of KCK only
b986648 FILS: Update EAPOL-Key RX rules for FILS (AP)
352caf0 FILS: Update EAPOL-Key descriptor version rules for RX (STA)
36a50fd FILS: Set EAPOL-Key Key Descriptor Version to 0 with FILS AKMs (AP)
4a26ccd FILS: Set EAPOL-Key Key Info MIC=0 when using AEAD cipher (supplicant)
f5ff8ae FILS: Do not add Key MIC field in supplicant when using AEAD cipher
dc5bad4 RSN authenticator: Add more debug print details on EAPOL-Key RX
555ff85 wlantest: Recognize EAPOL-Key frames without MIC bit for FILS
6d014ff Make struct wpa_eapol_key easier to use with variable length MIC
94f66e8 FILS: Advertise ERP domain in FILS Indication element
c30ed45 FILS: Allow hostapd to select FILS AKM for connection
b8ae56e FILS: Allow wpa_supplicant to select FILS AKM for connection
7147a83 FILS: Add FILS flags into wpa_supplicant BSS command output
379e2b4 FILS: Add 'GET_CAPABILITY fils' for runtime check
e4d2ce1 FILS: Set FILS Capability bit in management frames from station
f55acd9 FILS: Set FILS Capability bit in management frames from AP
198a942 FILS: Add FILS Indication element to Beacon and Probe Response frames
9b7a2b8 FILS: Add wpa_supplicant configuration options
903ecbe FILS: Add hostapd configuration options
274d8b7 FILS: Add definitions for new frames and values
94318a0 FILS: Add AKM definitions
1d29163 FILS: Add new information elements
325a85b Extend AES-SIV implementation to support different key lengths
e2991ee Move CRC-32 routine from wlantest to src/utils
150948e test: FT: EAP test for mismatching keys
d0175d6 test: FT with locally generated PMK-R0/PMK-R1 from PSK
9659056 FT: Allow PMK-R0 and PMK-R1 for FT-PSK to be generated locally
a25e4ef mka: Add driver op to get macsec capabilities
53b2555 EAP-pwd: Validate Prep field in EAP-pwd-ID/Response
2875e32 EAP-pwd: Fix Prep in EAP-pwd-ID/Response when EAP_PWD_PREP_MS is used
5f5ca28 mka: Pass full structures down to macsec drivers' receive SC ops
8ebfc7c mka: Pass full structures down to macsec drivers' transmit SC ops
b70d508 LibreSSL: Fix compatibility for EAP-FAST
df42673 LibreSSL: Fix TLS initialization/deinitialization
0d42179 LibreSSL: Fix dh5 code
32d08d5 Add QCA vendor attributes for measurement frequency for FTM/AOA
cecdecd mka: Pass full structures down to macsec drivers' receive SA ops
909c1b9 mka: Pass full structures down to macsec drivers' transmit SA ops
7fa5eff mka: Pass full structures down to macsec drivers' packet number ops
f75f6e2 mka: Move structs {transmit,receive}_{sa,sc} to a common header
9d3f4a7 autoscan: Add more debug prints for cases where autoscan is not used
98529f3 The master branch is now used for v2.7 development
2462f34 Change version number to v2.6 for the release
5ac8f86 Fix PNO restart flow
14f34a7 Continue scanning if sched_scan stops unexpectedly
1ac3886 Remove disconnected APs from BSS table if likely out-of-range
ebf59eb Restart PNO/sched_scan on channel list update
746e5c2 Fix spelling mistakes in number of comments
8b66888 Add explicit enum values for QCA vendor config attributes
8f47917 MBO: Add support to send ANQP request to get cellular preference

Test: Wifi Test Suite: b/32709661

Change-Id: If11d88f812812543dab6839879e815892a39f963
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 3587086..43e3558 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -13,7 +13,9 @@
 #include "utils/state_machine.h"
 #include "utils/bitfield.h"
 #include "common/ieee802_11_defs.h"
+#include "crypto/aes.h"
 #include "crypto/aes_wrap.h"
+#include "crypto/aes_siv.h"
 #include "crypto/crypto.h"
 #include "crypto/sha1.h"
 #include "crypto/sha256.h"
@@ -35,6 +37,10 @@
 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);
+#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);
+#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,
 			      struct wpa_group *group);
@@ -52,6 +58,7 @@
 			  struct wpa_group *group);
 static void wpa_group_put(struct wpa_authenticator *wpa_auth,
 			  struct wpa_group *group);
+static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos);
 
 static const u32 dot11RSNAConfigGroupUpdateCount = 4;
 static const u32 dot11RSNAConfigPairwiseUpdateCount = 4;
@@ -232,10 +239,10 @@
 static int wpa_use_aes_cmac(struct wpa_state_machine *sm)
 {
 	int ret = 0;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
 		ret = 1;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_IEEE80211W
 	if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt))
 		ret = 1;
@@ -443,7 +450,7 @@
 		return NULL;
 	}
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init();
 	if (wpa_auth->ft_pmk_cache == NULL) {
 		wpa_printf(MSG_ERROR, "FT PMK cache initialization failed.");
@@ -453,7 +460,7 @@
 		os_free(wpa_auth);
 		return NULL;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 	if (wpa_auth->conf.wpa_gmk_rekey) {
 		eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0,
@@ -513,10 +520,10 @@
 
 	pmksa_cache_auth_deinit(wpa_auth->pmksa);
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache);
 	wpa_auth->ft_pmk_cache = NULL;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_P2P
 	bitfield_free(wpa_auth->ip_pool);
@@ -599,7 +606,7 @@
 	if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
 		return -1;
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (sm->ft_completed) {
 		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
 				"FT authentication already completed - do not "
@@ -608,7 +615,17 @@
 		sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
 		return 0;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_FILS
+	if (sm->fils_completed) {
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+				"FILS authentication already completed - do not start 4-way handshake");
+		/* Go to PTKINITDONE state to allow GTK rekeying */
+		sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
+		return 0;
+	}
+#endif /* CONFIG_FILS */
 
 	if (sm->started) {
 		os_memset(&sm->key_replay, 0, sizeof(sm->key_replay));
@@ -660,10 +677,10 @@
 		sm->group->GKeyDoneStations--;
 		sm->GUpdateStationKeys = FALSE;
 	}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	os_free(sm->assoc_resp_ftie);
 	wpabuf_free(sm->ft_pending_req_ies);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	os_free(sm->last_rx_eapol_key);
 	os_free(sm->wpa_ie);
 	wpa_group_put(sm->wpa_auth, sm->group);
@@ -739,7 +756,7 @@
 }
 
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth,
 			       struct wpa_state_machine *sm,
 			       struct wpa_eapol_ie_parse *kde)
@@ -786,7 +803,7 @@
 
 	return 0;
 }
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 
 static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth,
@@ -830,6 +847,7 @@
 	const u8 *pmk = NULL;
 	unsigned int pmk_len;
 
+	os_memset(&PTK, 0, sizeof(PTK));
 	for (;;) {
 		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
 			pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
@@ -877,39 +895,42 @@
 {
 	struct ieee802_1x_hdr *hdr;
 	struct wpa_eapol_key *key;
-	struct wpa_eapol_key_192 *key192;
 	u16 key_info, key_data_length;
 	enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST,
 	       SMK_M1, SMK_M3, SMK_ERROR } msg;
 	char *msgtxt;
 	struct wpa_eapol_ie_parse kde;
-	int ft;
-	const u8 *eapol_key_ie, *key_data;
-	size_t eapol_key_ie_len, keyhdrlen, mic_len;
+	const u8 *key_data;
+	size_t keyhdrlen, mic_len;
+	u8 *mic;
 
 	if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
 		return;
+	wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL data", data, data_len);
 
 	mic_len = wpa_mic_len(sm->wpa_key_mgmt);
-	keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+	keyhdrlen = sizeof(*key) + mic_len + 2;
 
-	if (data_len < sizeof(*hdr) + keyhdrlen)
+	if (data_len < sizeof(*hdr) + keyhdrlen) {
+		wpa_printf(MSG_DEBUG, "WPA: Ignore too short EAPOL-Key frame");
 		return;
+	}
 
 	hdr = (struct ieee802_1x_hdr *) data;
 	key = (struct wpa_eapol_key *) (hdr + 1);
-	key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+	mic = (u8 *) (key + 1);
 	key_info = WPA_GET_BE16(key->key_info);
-	if (mic_len == 24) {
-		key_data = (const u8 *) (key192 + 1);
-		key_data_length = WPA_GET_BE16(key192->key_data_length);
-	} else {
-		key_data = (const u8 *) (key + 1);
-		key_data_length = WPA_GET_BE16(key->key_data_length);
-	}
+	key_data = mic + mic_len + 2;
+	key_data_length = WPA_GET_BE16(mic + mic_len);
 	wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR
-		   " key_info=0x%x type=%u key_data_length=%u",
-		   MAC2STR(sm->addr), key_info, key->type, key_data_length);
+		   " key_info=0x%x type=%u mic_len=%u key_data_length=%u",
+		   MAC2STR(sm->addr), key_info, key->type,
+		   (unsigned int) mic_len, key_data_length);
+	wpa_hexdump(MSG_MSGDUMP,
+		    "WPA: EAPOL-Key header (ending before Key MIC)",
+		    key, sizeof(*key));
+	wpa_hexdump(MSG_MSGDUMP, "WPA: EAPOL-Key Key MIC",
+		    mic, mic_len);
 	if (key_data_length > data_len - sizeof(*hdr) - keyhdrlen) {
 		wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - "
 			   "key_data overflow (%d > %lu)",
@@ -968,7 +989,9 @@
 	} else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) {
 		msg = GROUP_2;
 		msgtxt = "2/2 Group";
-	} else if (key_data_length == 0) {
+	} else if (key_data_length == 0 ||
+		   (mic_len == 0 && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) &&
+		    key_data_length == AES_BLOCK_SIZE)) {
 		msg = PAIRWISE_4;
 		msgtxt = "4/4 Pairwise";
 	} else {
@@ -985,6 +1008,7 @@
 			if (wpa_use_aes_cmac(sm) &&
 			    sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN &&
 			    !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
+			    !wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
 			    ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
 				wpa_auth_logger(wpa_auth, sm->addr,
 						LOGGER_WARNING,
@@ -995,6 +1019,7 @@
 			}
 
 			if (!wpa_use_aes_cmac(sm) &&
+			    !wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
 			    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
 				wpa_auth_logger(wpa_auth, sm->addr,
 						LOGGER_WARNING,
@@ -1004,7 +1029,8 @@
 			}
 		}
 
-		if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
+		if ((wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
+		     wpa_key_mgmt_fils(sm->wpa_key_mgmt)) &&
 		    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");
@@ -1092,6 +1118,15 @@
 	}
 
 continue_processing:
+#ifdef CONFIG_FILS
+	if (sm->wpa == WPA_VERSION_WPA2 && mic_len == 0 &&
+	    !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+				 "WPA: Encr Key Data bit not set even though AEAD cipher is supposed to be used - drop frame");
+		return;
+	}
+#endif /* CONFIG_FILS */
+
 	switch (msg) {
 	case PAIRWISE_2:
 		if (sm->wpa_ptk_state != WPA_PTK_PTKSTART &&
@@ -1122,67 +1157,6 @@
 			wpa_sta_disconnect(wpa_auth, sm->addr);
 			return;
 		}
-		if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
-					 "received EAPOL-Key msg 2/4 with "
-					 "invalid Key Data contents");
-			return;
-		}
-		if (kde.rsn_ie) {
-			eapol_key_ie = kde.rsn_ie;
-			eapol_key_ie_len = kde.rsn_ie_len;
-		} else if (kde.osen) {
-			eapol_key_ie = kde.osen;
-			eapol_key_ie_len = kde.osen_len;
-		} else {
-			eapol_key_ie = kde.wpa_ie;
-			eapol_key_ie_len = kde.wpa_ie_len;
-		}
-		ft = sm->wpa == WPA_VERSION_WPA2 &&
-			wpa_key_mgmt_ft(sm->wpa_key_mgmt);
-		if (sm->wpa_ie == NULL ||
-		    wpa_compare_rsn_ie(ft,
-				       sm->wpa_ie, sm->wpa_ie_len,
-				       eapol_key_ie, eapol_key_ie_len)) {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
-					"WPA IE from (Re)AssocReq did not "
-					"match with msg 2/4");
-			if (sm->wpa_ie) {
-				wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq",
-					    sm->wpa_ie, sm->wpa_ie_len);
-			}
-			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);
-			return;
-		}
-#ifdef CONFIG_IEEE80211R
-		if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
-			wpa_sta_disconnect(wpa_auth, sm->addr);
-			return;
-		}
-#endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_P2P
-		if (kde.ip_addr_req && kde.ip_addr_req[0] &&
-		    wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
-			int idx;
-			wpa_printf(MSG_DEBUG, "P2P: IP address requested in "
-				   "EAPOL-Key exchange");
-			idx = bitfield_get_first_zero(wpa_auth->ip_pool);
-			if (idx >= 0) {
-				u32 start = WPA_GET_BE32(wpa_auth->conf.
-							 ip_addr_start);
-				bitfield_set(wpa_auth->ip_pool, idx);
-				WPA_PUT_BE32(sm->ip_addr, start + idx);
-				wpa_printf(MSG_DEBUG, "P2P: Assigned IP "
-					   "address %u.%u.%u.%u to " MACSTR,
-					   sm->ip_addr[0], sm->ip_addr[1],
-					   sm->ip_addr[2], sm->ip_addr[3],
-					   MAC2STR(sm->addr));
-			}
-		}
-#endif /* CONFIG_P2P */
 		break;
 	case PAIRWISE_4:
 		if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING ||
@@ -1239,15 +1213,26 @@
 		return;
 	}
 
-	if (!(key_info & WPA_KEY_INFO_MIC)) {
+	if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
+	    !(key_info & WPA_KEY_INFO_MIC)) {
 		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
 				"received invalid EAPOL-Key: Key MIC not set");
 		return;
 	}
 
+#ifdef CONFIG_FILS
+	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
+	    (key_info & WPA_KEY_INFO_MIC)) {
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+				"received invalid EAPOL-Key: Key MIC set");
+		return;
+	}
+#endif /* CONFIG_FILS */
+
 	sm->MICVerified = FALSE;
 	if (sm->PTK_valid && !sm->update_snonce) {
-		if (wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data,
+		if (mic_len &&
+		    wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data,
 				       data_len) &&
 		    (msg != PAIRWISE_4 || !sm->alt_snonce_valid ||
 		     wpa_try_alt_snonce(sm, data, data_len))) {
@@ -1255,6 +1240,15 @@
 					"received EAPOL-Key with invalid MIC");
 			return;
 		}
+#ifdef CONFIG_FILS
+		if (!mic_len &&
+		    wpa_aead_decrypt(sm, &sm->PTK, data, data_len,
+				     &key_data_length) < 0) {
+			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+					"received EAPOL-Key with invalid MIC");
+			return;
+		}
+#endif /* CONFIG_FILS */
 		sm->MICVerified = TRUE;
 		eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
 		sm->pending_1_of_4_timeout = 0;
@@ -1412,24 +1406,24 @@
 {
 	struct ieee802_1x_hdr *hdr;
 	struct wpa_eapol_key *key;
-	struct wpa_eapol_key_192 *key192;
 	size_t len, mic_len, keyhdrlen;
 	int alg;
 	int key_data_len, pad_len = 0;
 	u8 *buf, *pos;
 	int version, pairwise;
 	int i;
-	u8 *key_data;
+	u8 *key_mic, *key_data;
 
 	mic_len = wpa_mic_len(sm->wpa_key_mgmt);
-	keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+	keyhdrlen = sizeof(*key) + mic_len + 2;
 
 	len = sizeof(struct ieee802_1x_hdr) + keyhdrlen;
 
 	if (force_version)
 		version = force_version;
 	else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
-		 wpa_key_mgmt_suite_b(sm->wpa_key_mgmt))
+		 wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
+		 wpa_key_mgmt_fils(sm->wpa_key_mgmt))
 		version = WPA_KEY_INFO_TYPE_AKM_DEFINED;
 	else if (wpa_use_aes_cmac(sm))
 		version = WPA_KEY_INFO_TYPE_AES_128_CMAC;
@@ -1463,6 +1457,8 @@
 	}
 
 	len += key_data_len;
+	if (!mic_len && encr)
+		len += AES_BLOCK_SIZE;
 
 	hdr = os_zalloc(len);
 	if (hdr == NULL)
@@ -1471,7 +1467,7 @@
 	hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
 	hdr->length = host_to_be16(len  - sizeof(*hdr));
 	key = (struct wpa_eapol_key *) (hdr + 1);
-	key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+	key_mic = (u8 *) (key + 1);
 	key_data = ((u8 *) (hdr + 1)) + keyhdrlen;
 
 	key->type = sm->wpa == WPA_VERSION_WPA2 ?
@@ -1510,10 +1506,31 @@
 
 	if (kde && !encr) {
 		os_memcpy(key_data, kde, kde_len);
-		if (mic_len == 24)
-			WPA_PUT_BE16(key192->key_data_length, kde_len);
-		else
-			WPA_PUT_BE16(key->key_data_length, kde_len);
+		WPA_PUT_BE16(key_mic + mic_len, kde_len);
+#ifdef CONFIG_FILS
+	} else if (!mic_len) {
+		const u8 *aad[1];
+		size_t aad_len[1];
+
+		WPA_PUT_BE16(key_mic, AES_BLOCK_SIZE + kde_len);
+		wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
+				kde, kde_len);
+
+		wpa_hexdump_key(MSG_DEBUG, "WPA: KEK",
+				sm->PTK.kek, sm->PTK.kek_len);
+		/* AES-SIV AAD from EAPOL protocol version field (inclusive) to
+		 * to Key Data (exclusive). */
+		aad[0] = (u8 *) hdr;
+		aad_len[0] = key_mic + 2 - (u8 *) hdr;
+		if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len, kde, kde_len,
+				    1, aad, aad_len, key_mic + 2) < 0) {
+			wpa_printf(MSG_DEBUG, "WPA: AES-SIV encryption failed");
+			return;
+		}
+
+		wpa_hexdump(MSG_DEBUG, "WPA: Encrypted Key Data from SIV",
+			    key_mic + 2, AES_BLOCK_SIZE + kde_len);
+#endif /* CONFIG_FILS */
 	} else if (encr && kde) {
 		buf = os_zalloc(key_data_len);
 		if (buf == NULL) {
@@ -1539,12 +1556,7 @@
 				os_free(buf);
 				return;
 			}
-			if (mic_len == 24)
-				WPA_PUT_BE16(key192->key_data_length,
-					     key_data_len);
-			else
-				WPA_PUT_BE16(key->key_data_length,
-					     key_data_len);
+			WPA_PUT_BE16(key_mic + mic_len, key_data_len);
 #ifndef CONFIG_NO_RC4
 		} else if (sm->PTK.kek_len == 16) {
 			u8 ek[32];
@@ -1555,12 +1567,7 @@
 			os_memcpy(ek + 16, sm->PTK.kek, sm->PTK.kek_len);
 			os_memcpy(key_data, buf, key_data_len);
 			rc4_skip(ek, 32, 256, key_data, key_data_len);
-			if (mic_len == 24)
-				WPA_PUT_BE16(key192->key_data_length,
-					     key_data_len);
-			else
-				WPA_PUT_BE16(key->key_data_length,
-					     key_data_len);
+			WPA_PUT_BE16(key_mic + mic_len, key_data_len);
 #endif /* CONFIG_NO_RC4 */
 		} else {
 			os_free(hdr);
@@ -1571,9 +1578,7 @@
 	}
 
 	if (key_info & WPA_KEY_INFO_MIC) {
-		u8 *key_mic;
-
-		if (!sm->PTK_valid) {
+		if (!sm->PTK_valid || !mic_len) {
 			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
 					"PTK not valid when sending EAPOL-Key "
 					"frame");
@@ -1581,7 +1586,6 @@
 			return;
 		}
 
-		key_mic = key192->key_mic; /* same offset for key and key192 */
 		wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len,
 				  sm->wpa_key_mgmt, version,
 				  (u8 *) hdr, len, key_mic);
@@ -1641,10 +1645,9 @@
 {
 	struct ieee802_1x_hdr *hdr;
 	struct wpa_eapol_key *key;
-	struct wpa_eapol_key_192 *key192;
 	u16 key_info;
 	int ret = 0;
-	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN], *mic_pos;
 	size_t mic_len = wpa_mic_len(akmp);
 
 	if (data_len < sizeof(*hdr) + sizeof(*key))
@@ -1652,16 +1655,16 @@
 
 	hdr = (struct ieee802_1x_hdr *) data;
 	key = (struct wpa_eapol_key *) (hdr + 1);
-	key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+	mic_pos = (u8 *) (key + 1);
 	key_info = WPA_GET_BE16(key->key_info);
-	os_memcpy(mic, key192->key_mic, mic_len);
-	os_memset(key192->key_mic, 0, mic_len);
+	os_memcpy(mic, mic_pos, mic_len);
+	os_memset(mic_pos, 0, mic_len);
 	if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp,
 			      key_info & WPA_KEY_INFO_TYPE_MASK,
-			      data, data_len, key192->key_mic) ||
-	    os_memcmp_const(mic, key192->key_mic, mic_len) != 0)
+			      data, data_len, mic_pos) ||
+	    os_memcmp_const(mic, mic_pos, mic_len) != 0)
 		ret = -1;
-	os_memcpy(key192->key_mic, mic, mic_len);
+	os_memcpy(mic_pos, mic, mic_len);
 	return ret;
 }
 
@@ -1670,7 +1673,10 @@
 {
 	sm->PTK_valid = FALSE;
 	os_memset(&sm->PTK, 0, sizeof(sm->PTK));
-	wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, 0);
+	if (wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL,
+			     0))
+		wpa_printf(MSG_DEBUG,
+			   "RSN: PTK removal from the driver failed");
 	sm->pairwise_set = FALSE;
 	eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
 }
@@ -1734,7 +1740,7 @@
 		sm->ReAuthenticationRequest = TRUE;
 		break;
 	case WPA_ASSOC_FT:
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 		wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration "
 			   "after association");
 		wpa_ft_install_ptk(sm);
@@ -1742,19 +1748,24 @@
 		/* Using FT protocol, not WPA auth state machine */
 		sm->ft_completed = 1;
 		return 0;
-#else /* CONFIG_IEEE80211R */
+#else /* CONFIG_IEEE80211R_AP */
 		break;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	}
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	sm->ft_completed = 0;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_IEEE80211W
 	if (sm->mgmt_frame_prot && event == WPA_AUTH)
 		remove_ptk = 0;
 #endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_FILS
+	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
+	    (event == WPA_AUTH || event == WPA_ASSOC))
+		remove_ptk = 0;
+#endif /* CONFIG_FILS */
 
 	if (remove_ptk) {
 		sm->PTK_valid = FALSE;
@@ -1904,9 +1915,9 @@
 	size_t len = 2 * PMK_LEN;
 
 	SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk);
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	sm->xxkey_len = 0;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	if (sm->pmksa) {
 		wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache");
 		os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
@@ -1930,12 +1941,12 @@
 		}
 		os_memcpy(sm->PMK, msk, pmk_len);
 		sm->pmk_len = pmk_len;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 		if (len >= 2 * PMK_LEN) {
 			os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
 			sm->xxkey_len = PMK_LEN;
 		}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	} else {
 		wpa_printf(MSG_DEBUG, "WPA: Could not get PMK, get_msk: %p",
 			   sm->wpa_auth->cb.get_msk);
@@ -1965,10 +1976,10 @@
 	if (psk) {
 		os_memcpy(sm->PMK, psk, PMK_LEN);
 		sm->pmk_len = PMK_LEN;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 		os_memcpy(sm->xxkey, psk, PMK_LEN);
 		sm->xxkey_len = PMK_LEN;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	}
 	sm->req_replay_counter_used = 0;
 }
@@ -2031,10 +2042,10 @@
 			  const u8 *pmk, unsigned int pmk_len,
 			  struct wpa_ptk *ptk)
 {
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
 		return wpa_auth_derive_ptk_ft(sm, pmk, ptk);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 	return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion",
 			      sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce,
@@ -2042,16 +2053,398 @@
 }
 
 
+#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)
+{
+	u8 ick[FILS_ICK_MAX_LEN];
+	size_t ick_len;
+	int res;
+
+	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);
+	if (res < 0)
+		return res;
+	sm->PTK_valid = TRUE;
+
+	res = fils_key_auth_sk(ick, ick_len, snonce, anonce,
+			       sm->addr, sm->wpa_auth->addr,
+			       NULL, 0, NULL, 0, /* TODO: SK+PFS */
+			       sm->wpa_key_mgmt, sm->fils_key_auth_sta,
+			       sm->fils_key_auth_ap,
+			       &sm->fils_key_auth_len);
+	os_memset(ick, 0, sizeof(ick));
+
+	/* Store nonces for (Re)Association Request/Response frame processing */
+	os_memcpy(sm->SNonce, snonce, FILS_NONCE_LEN);
+	os_memcpy(sm->ANonce, anonce, FILS_NONCE_LEN);
+
+	return res;
+}
+
+
+static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
+			    u8 *buf, size_t buf_len, u16 *_key_data_len)
+{
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	u8 *pos;
+	u16 key_data_len;
+	u8 *tmp;
+	const u8 *aad[1];
+	size_t aad_len[1];
+
+	hdr = (struct ieee802_1x_hdr *) buf;
+	key = (struct wpa_eapol_key *) (hdr + 1);
+	pos = (u8 *) (key + 1);
+	key_data_len = WPA_GET_BE16(pos);
+	if (key_data_len < AES_BLOCK_SIZE ||
+	    key_data_len > buf_len - sizeof(*hdr) - sizeof(*key) - 2) {
+		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+				"No room for AES-SIV data in the frame");
+		return -1;
+	}
+	pos += 2; /* Pointing at the Encrypted Key Data field */
+
+	tmp = os_malloc(key_data_len);
+	if (!tmp)
+		return -1;
+
+	/* AES-SIV AAD from EAPOL protocol version field (inclusive) to
+	 * to Key Data (exclusive). */
+	aad[0] = buf;
+	aad_len[0] = pos - buf;
+	if (aes_siv_decrypt(ptk->kek, ptk->kek_len, pos, key_data_len,
+			    1, aad, aad_len, tmp) < 0) {
+		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+				"Invalid AES-SIV data in the frame");
+		bin_clear_free(tmp, key_data_len);
+		return -1;
+	}
+
+	/* AEAD decryption and validation completed successfully */
+	key_data_len -= AES_BLOCK_SIZE;
+	wpa_hexdump_key(MSG_DEBUG, "WPA: Decrypted Key Data",
+			tmp, key_data_len);
+
+	/* Replace Key Data field with the decrypted version */
+	os_memcpy(pos, tmp, key_data_len);
+	pos -= 2; /* Key Data Length field */
+	WPA_PUT_BE16(pos, key_data_len);
+	bin_clear_free(tmp, key_data_len);
+	if (_key_data_len)
+		*_key_data_len = key_data_len;
+	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];
+
+	if (!sm || !sm->PTK_valid) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: No KEK to decrypt Assocication Request frame");
+		return -1;
+	}
+
+	if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Not a FILS AKM - reject association");
+		return -1;
+	}
+
+	end = ((const u8 *) mgmt) + frame_len;
+	fc = le_to_host16(mgmt->frame_control);
+	stype = WLAN_FC_GET_STYPE(fc);
+	if (stype == WLAN_FC_STYPE_REASSOC_REQ)
+		ie_start = mgmt->u.reassoc_req.variable;
+	else
+		ie_start = mgmt->u.assoc_req.variable;
+	ie = ie_start;
+
+	/*
+	 * 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];
+	}
+
+	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) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Too short frame to include AES-SIV data");
+		return -1;
+	}
+
+	/* AES-SIV AAD vectors */
+
+	/* The STA's MAC address */
+	aad[0] = mgmt->sa;
+	aad_len[0] = ETH_ALEN;
+	/* The AP's BSSID */
+	aad[1] = mgmt->da;
+	aad_len[1] = ETH_ALEN;
+	/* The STA's nonce */
+	aad[2] = sm->SNonce;
+	aad_len[2] = FILS_NONCE_LEN;
+	/* The AP's nonce */
+	aad[3] = sm->ANonce;
+	aad_len[3] = FILS_NONCE_LEN;
+	/*
+	 * The (Re)Association Request frame from the Capability Information
+	 * field to the FILS Session element (both inclusive).
+	 */
+	aad[4] = (const u8 *) &mgmt->u.assoc_req.capab_info;
+	aad_len[4] = crypt - aad[0];
+
+	if (aes_siv_decrypt(sm->PTK.kek, sm->PTK.kek_len, crypt, end - crypt,
+			    1, aad, aad_len, pos + (crypt - ie_start)) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Invalid AES-SIV data in the frame");
+		return -1;
+	}
+	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);
+		return -1;
+	}
+
+	return left - AES_BLOCK_SIZE;
+}
+
+
+int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf,
+		       size_t current_len, size_t max_len)
+{
+	u8 *end = buf + max_len;
+	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];
+
+	if (!sm || !sm->PTK_valid)
+		return -1;
+
+	wpa_hexdump(MSG_DEBUG,
+		    "FILS: Association Response frame before FILS processing",
+		    buf, current_len);
+
+	mgmt = (struct ieee80211_mgmt *) buf;
+
+	/* AES-SIV AAD vectors */
+
+	/* The AP's BSSID */
+	aad[0] = mgmt->sa;
+	aad_len[0] = ETH_ALEN;
+	/* The STA's MAC address */
+	aad[1] = mgmt->da;
+	aad_len[1] = ETH_ALEN;
+	/* The AP's nonce */
+	aad[2] = sm->ANonce;
+	aad_len[2] = FILS_NONCE_LEN;
+	/* The STA's nonce */
+	aad[3] = sm->SNonce;
+	aad_len[3] = FILS_NONCE_LEN;
+	/*
+	 * The (Re)Association Response frame from the Capability Information
+	 * field (the same offset in both Association and Reassociation
+	 * Response frames) to the FILS Session element (both inclusive).
+	 */
+	aad[4] = (const u8 *) &mgmt->u.assoc_resp.capab_info;
+	aad_len[4] = pos - aad[4];
+
+	/* The following elements will be encrypted with AES-SIV */
+
+	plain = wpabuf_alloc(1000);
+	if (!plain)
+		return -1;
+
+	/* TODO: FILS Public Key */
+
+	/* FILS Key Confirmation */
+	wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */
+	wpabuf_put_u8(plain, 1 + sm->fils_key_auth_len); /* Length */
+	/* Element ID Extension */
+	wpabuf_put_u8(plain, WLAN_EID_EXT_FILS_KEY_CONFIRM);
+	wpabuf_put_data(plain, sm->fils_key_auth_ap, sm->fils_key_auth_len);
+
+	/* TODO: FILS HLP Container */
+
+	/* TODO: FILS IP Address Assignment */
+
+	/* Key Delivery */
+	gsm = sm->group;
+	wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */
+	len = wpabuf_put(plain, 1);
+	wpabuf_put_u8(plain, WLAN_EID_EXT_KEY_DELIVERY);
+	wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN,
+			    wpabuf_put(plain, WPA_KEY_RSC_LEN));
+	/* GTK KDE */
+	gtk = gsm->GTK[gsm->GN - 1];
+	gtk_len = gsm->GTK_len;
+	if (sm->wpa_auth->conf.disable_gtk) {
+		/*
+		 * Provide unique random GTK to each STA to prevent use
+		 * of GTK in the BSS.
+		 */
+		if (random_get_bytes(dummy_gtk, gtk_len) < 0) {
+			wpabuf_free(plain);
+			return -1;
+		}
+		gtk = dummy_gtk;
+	}
+	hdr[0] = gsm->GN & 0x03;
+	hdr[1] = 0;
+	tmp = wpabuf_put(plain, 0);
+	tmp2 = wpa_add_kde(tmp, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+			   gtk, gtk_len);
+	wpabuf_put(plain, tmp2 - tmp);
+
+	/* IGTK KDE */
+	tmp = wpabuf_put(plain, 0);
+	tmp2 = ieee80211w_kde_add(sm, tmp);
+	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;
+}
+
+
+int fils_set_tk(struct wpa_state_machine *sm)
+{
+	enum wpa_alg alg;
+	int klen;
+
+	if (!sm || !sm->PTK_valid)
+		return -1;
+
+	alg = wpa_cipher_to_alg(sm->pairwise);
+	klen = wpa_cipher_key_len(sm->pairwise);
+
+	wpa_printf(MSG_DEBUG, "FILS: Configure TK to the driver");
+	if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
+			     sm->PTK.tk, klen)) {
+		wpa_printf(MSG_DEBUG, "FILS: Failed to set TK to the driver");
+		return -1;
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_FILS */
+
+
 SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
 {
+	struct wpa_authenticator *wpa_auth = sm->wpa_auth;
 	struct wpa_ptk PTK;
 	int ok = 0, psk_found = 0;
 	const u8 *pmk = NULL;
 	unsigned int pmk_len;
+	int ft;
+	const u8 *eapol_key_ie, *key_data, *mic;
+	u16 key_data_length;
+	size_t mic_len, eapol_key_ie_len;
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	struct wpa_eapol_ie_parse kde;
 
 	SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
 	sm->EAPOLKeyReceived = FALSE;
 	sm->update_snonce = FALSE;
+	os_memset(&PTK, 0, sizeof(PTK));
+
+	mic_len = wpa_mic_len(sm->wpa_key_mgmt);
 
 	/* WPA with IEEE 802.1X: use the derived PMK from EAP
 	 * WPA-PSK: iterate through possible PSKs and select the one matching
@@ -2071,13 +2464,23 @@
 
 		wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK);
 
-		if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK,
+		if (mic_len &&
+		    wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK,
 				       sm->last_rx_eapol_key,
 				       sm->last_rx_eapol_key_len) == 0) {
 			ok = 1;
 			break;
 		}
 
+#ifdef CONFIG_FILS
+		if (!mic_len &&
+		    wpa_aead_decrypt(sm, &PTK, sm->last_rx_eapol_key,
+				     sm->last_rx_eapol_key_len, NULL) == 0) {
+			ok = 1;
+			break;
+		}
+#endif /* CONFIG_FILS */
+
 		if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
 			break;
 	}
@@ -2090,7 +2493,77 @@
 		return;
 	}
 
-#ifdef CONFIG_IEEE80211R
+	/*
+	 * Note: last_rx_eapol_key length fields have already been validated in
+	 * wpa_receive().
+	 */
+	hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key;
+	key = (struct wpa_eapol_key *) (hdr + 1);
+	mic = (u8 *) (key + 1);
+	key_data = mic + mic_len + 2;
+	key_data_length = WPA_GET_BE16(mic + mic_len);
+	if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) -
+	    sizeof(*key) - mic_len - 2)
+		return;
+
+	if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
+		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+				 "received EAPOL-Key msg 2/4 with invalid Key Data contents");
+		return;
+	}
+	if (kde.rsn_ie) {
+		eapol_key_ie = kde.rsn_ie;
+		eapol_key_ie_len = kde.rsn_ie_len;
+	} else if (kde.osen) {
+		eapol_key_ie = kde.osen;
+		eapol_key_ie_len = kde.osen_len;
+	} else {
+		eapol_key_ie = kde.wpa_ie;
+		eapol_key_ie_len = kde.wpa_ie_len;
+	}
+	ft = sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt);
+	if (sm->wpa_ie == NULL ||
+	    wpa_compare_rsn_ie(ft, sm->wpa_ie, sm->wpa_ie_len,
+			       eapol_key_ie, eapol_key_ie_len)) {
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+				"WPA IE from (Re)AssocReq did not match with msg 2/4");
+		if (sm->wpa_ie) {
+			wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq",
+				    sm->wpa_ie, sm->wpa_ie_len);
+		}
+		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);
+		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);
+		return;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_P2P
+	if (kde.ip_addr_req && kde.ip_addr_req[0] &&
+	    wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
+		int idx;
+		wpa_printf(MSG_DEBUG,
+			   "P2P: IP address requested in EAPOL-Key exchange");
+		idx = bitfield_get_first_zero(wpa_auth->ip_pool);
+		if (idx >= 0) {
+			u32 start = WPA_GET_BE32(wpa_auth->conf.ip_addr_start);
+			bitfield_set(wpa_auth->ip_pool, idx);
+			WPA_PUT_BE32(sm->ip_addr, start + idx);
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Assigned IP address %u.%u.%u.%u to "
+				   MACSTR, sm->ip_addr[0], sm->ip_addr[1],
+				   sm->ip_addr[2], sm->ip_addr[3],
+				   MAC2STR(sm->addr));
+		}
+	}
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_IEEE80211R_AP
 	if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
 		/*
 		 * Verify that PMKR1Name from EAPOL-Key message 2/4 matches
@@ -2109,7 +2582,7 @@
 			return;
 		}
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 	sm->pending_1_of_4_timeout = 0;
 	eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
@@ -2279,12 +2752,12 @@
 	kde_len = wpa_ie_len + ieee80211w_kde_len(sm);
 	if (gtk)
 		kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
-#ifdef CONFIG_IEEE80211R
+#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 */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_P2P
 	if (WPA_GET_BE32(sm->ip_addr) > 0)
 		kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4;
@@ -2296,7 +2769,7 @@
 	pos = kde;
 	os_memcpy(pos, wpa_ie, wpa_ie_len);
 	pos += wpa_ie_len;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
 		int res;
 		size_t elen;
@@ -2312,7 +2785,7 @@
 		pos -= wpa_ie_len;
 		pos += elen;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	if (gtk) {
 		u8 hdr[2];
 		hdr[0] = keyidx & 0x03;
@@ -2322,7 +2795,7 @@
 	}
 	pos = ieee80211w_kde_add(sm, pos);
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
 		int res;
 		struct wpa_auth_config *conf;
@@ -2362,7 +2835,7 @@
 		WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60);
 		pos += 4;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_P2P
 	if (WPA_GET_BE32(sm->ip_addr) > 0) {
 		u8 addr[3 * 4];
@@ -2375,7 +2848,8 @@
 #endif /* CONFIG_P2P */
 
 	wpa_send_eapol(sm->wpa_auth, sm,
-		       (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC |
+		       (secure ? WPA_KEY_INFO_SECURE : 0) |
+		       (wpa_mic_len(sm->wpa_key_mgmt) ? 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);
@@ -2431,9 +2905,9 @@
 			 "pairwise key handshake completed (%s)",
 			 sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN");
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 }
 
 
@@ -2619,7 +3093,8 @@
 	}
 
 	wpa_send_eapol(sm->wpa_auth, sm,
-		       WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+		       WPA_KEY_INFO_SECURE |
+		       (wpa_mic_len(sm->wpa_key_mgmt) ? WPA_KEY_INFO_MIC : 0) |
 		       WPA_KEY_INFO_ACK |
 		       (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
 		       rsc, gsm->GNonce, kde, kde_len, gsm->GN, 1);
@@ -3376,11 +3851,12 @@
 
 
 struct rsn_pmksa_cache_entry *
-wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
+wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+		   const u8 *pmkid)
 {
 	if (!wpa_auth || !wpa_auth->pmksa)
 		return NULL;
-	return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL);
+	return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, pmkid);
 }