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: I249f5fec85ad69ce3879247b07f0db84136ab996
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 3c47879..65e257a 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -10,10 +10,13 @@
#include "includes.h"
#include "common.h"
+#include "crypto/aes.h"
#include "crypto/aes_wrap.h"
#include "crypto/crypto.h"
#include "crypto/random.h"
+#include "crypto/aes_siv.h"
#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "wpa.h"
#include "eloop.h"
@@ -30,8 +33,7 @@
/**
* wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message
* @sm: Pointer to WPA state machine data from wpa_sm_init()
- * @kck: Key Confirmation Key (KCK, part of PTK)
- * @kck_len: KCK length in octets
+ * @ptk: PTK for Key Confirmation/Encryption Key
* @ver: Version field from Key Info
* @dest: Destination address for the frame
* @proto: Ethertype (usually ETH_P_EAPOL)
@@ -40,7 +42,7 @@
* @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written
* Returns: >= 0 on success, < 0 on failure
*/
-int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
+int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk,
int ver, const u8 *dest, u16 proto,
u8 *msg, size_t msg_len, u8 *key_mic)
{
@@ -64,16 +66,87 @@
MAC2STR(dest));
}
}
- if (key_mic &&
- wpa_eapol_key_mic(kck, kck_len, sm->key_mgmt, ver, msg, msg_len,
- key_mic)) {
- wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
- "WPA: Failed to generate EAPOL-Key version %d key_mgmt 0x%x MIC",
- ver, sm->key_mgmt);
+
+ if (mic_len) {
+ if (key_mic && (!ptk || !ptk->kck_len))
+ goto out;
+
+ if (key_mic &&
+ wpa_eapol_key_mic(ptk->kck, ptk->kck_len, sm->key_mgmt, ver,
+ msg, msg_len, key_mic)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+ "WPA: Failed to generate EAPOL-Key version %d key_mgmt 0x%x MIC",
+ ver, sm->key_mgmt);
+ goto out;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", ptk->kck, ptk->kck_len);
+ wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC",
+ key_mic, mic_len);
+ } else {
+#ifdef CONFIG_FILS
+ /* AEAD cipher - Key MIC field not used */
+ struct ieee802_1x_hdr *s_hdr, *hdr;
+ struct wpa_eapol_key *s_key, *key;
+ u8 *buf, *s_key_data, *key_data;
+ size_t buf_len = msg_len + AES_BLOCK_SIZE;
+ size_t key_data_len;
+ u16 eapol_len;
+ const u8 *aad[1];
+ size_t aad_len[1];
+
+ if (!ptk || !ptk->kek_len)
+ goto out;
+
+ key_data_len = msg_len - sizeof(struct ieee802_1x_hdr) -
+ sizeof(struct wpa_eapol_key) - 2;
+
+ buf = os_malloc(buf_len);
+ if (!buf)
+ goto out;
+
+ os_memcpy(buf, msg, msg_len);
+ hdr = (struct ieee802_1x_hdr *) buf;
+ key = (struct wpa_eapol_key *) (hdr + 1);
+ key_data = ((u8 *) (key + 1)) + 2;
+
+ /* Update EAPOL header to include AES-SIV overhead */
+ eapol_len = be_to_host16(hdr->length);
+ eapol_len += AES_BLOCK_SIZE;
+ hdr->length = host_to_be16(eapol_len);
+
+ /* Update Key Data Length field to include AES-SIV overhead */
+ WPA_PUT_BE16((u8 *) (key + 1), AES_BLOCK_SIZE + key_data_len);
+
+ s_hdr = (struct ieee802_1x_hdr *) msg;
+ s_key = (struct wpa_eapol_key *) (s_hdr + 1);
+ s_key_data = ((u8 *) (s_key + 1)) + 2;
+
+ wpa_hexdump_key(MSG_DEBUG, "WPA: Plaintext Key Data",
+ s_key_data, key_data_len);
+
+ wpa_hexdump_key(MSG_DEBUG, "WPA: KEK", ptk->kek, ptk->kek_len);
+ /* AES-SIV AAD from EAPOL protocol version field (inclusive) to
+ * to Key Data (exclusive). */
+ aad[0] = buf;
+ aad_len[0] = key_data - buf;
+ if (aes_siv_encrypt(ptk->kek, ptk->kek_len,
+ s_key_data, key_data_len,
+ 1, aad, aad_len, key_data) < 0) {
+ os_free(buf);
+ goto out;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "WPA: Encrypted Key Data from SIV",
+ key_data, AES_BLOCK_SIZE + key_data_len);
+
+ os_free(msg);
+ msg = buf;
+ msg_len = buf_len;
+#else /* CONFIG_FILS */
goto out;
+#endif /* CONFIG_FILS */
}
- wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, kck_len);
- wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, mic_len);
+
wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len);
ret = wpa_sm_ether_send(sm, dest, proto, msg, msg_len);
eapol_sm_notify_tx_eapol_key(sm->eapol);
@@ -97,9 +170,8 @@
{
size_t mic_len, hdrlen, rlen;
struct wpa_eapol_key *reply;
- struct wpa_eapol_key_192 *reply192;
int key_info, ver;
- u8 bssid[ETH_ALEN], *rbuf, *key_mic;
+ u8 bssid[ETH_ALEN], *rbuf, *key_mic, *mic;
if (sm->key_mgmt == WPA_KEY_MGMT_OSEN ||
wpa_key_mgmt_suite_b(sm->key_mgmt))
@@ -119,19 +191,20 @@
}
mic_len = wpa_mic_len(sm->key_mgmt);
- hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
+ hdrlen = sizeof(*reply) + mic_len + 2;
rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
hdrlen, &rlen, (void *) &reply);
if (rbuf == NULL)
return;
- reply192 = (struct wpa_eapol_key_192 *) reply;
reply->type = (sm->proto == WPA_PROTO_RSN ||
sm->proto == WPA_PROTO_OSEN) ?
EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
key_info = WPA_KEY_INFO_REQUEST | ver;
if (sm->ptk_set)
- key_info |= WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
+ key_info |= WPA_KEY_INFO_SECURE;
+ if (sm->ptk_set && mic_len)
+ key_info |= WPA_KEY_INFO_MIC;
if (error)
key_info |= WPA_KEY_INFO_ERROR;
if (pairwise)
@@ -142,21 +215,19 @@
WPA_REPLAY_COUNTER_LEN);
inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
- if (mic_len == 24)
- WPA_PUT_BE16(reply192->key_data_length, 0);
- else
- WPA_PUT_BE16(reply->key_data_length, 0);
+ mic = (u8 *) (reply + 1);
+ WPA_PUT_BE16(mic + mic_len, 0);
if (!(key_info & WPA_KEY_INFO_MIC))
key_mic = NULL;
else
- key_mic = reply192->key_mic; /* same offset in reply */
+ key_mic = mic;
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: Sending EAPOL-Key Request (error=%d "
"pairwise=%d ptk_set=%d len=%lu)",
error, pairwise, sm->ptk_set, (unsigned long) rlen);
- wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, bssid,
- ETH_P_EAPOL, rbuf, rlen, key_mic);
+ wpa_eapol_key_send(sm, &sm->ptk, ver, bssid, ETH_P_EAPOL, rbuf, rlen,
+ key_mic);
}
@@ -341,9 +412,9 @@
{
size_t mic_len, hdrlen, rlen;
struct wpa_eapol_key *reply;
- struct wpa_eapol_key_192 *reply192;
u8 *rbuf, *key_mic;
u8 *rsn_ie_buf = NULL;
+ u16 key_info;
if (wpa_ie == NULL) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No wpa_ie set - "
@@ -384,7 +455,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);
- hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
+ hdrlen = sizeof(*reply) + mic_len + 2;
rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
NULL, hdrlen + wpa_ie_len,
&rlen, (void *) &reply);
@@ -392,13 +463,16 @@
os_free(rsn_ie_buf);
return -1;
}
- reply192 = (struct wpa_eapol_key_192 *) reply;
reply->type = (sm->proto == WPA_PROTO_RSN ||
sm->proto == WPA_PROTO_OSEN) ?
EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
- WPA_PUT_BE16(reply->key_info,
- ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC);
+ key_info = ver | WPA_KEY_INFO_KEY_TYPE;
+ 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);
else
@@ -408,21 +482,16 @@
wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter", reply->replay_counter,
WPA_REPLAY_COUNTER_LEN);
- key_mic = reply192->key_mic; /* same offset for reply and reply192 */
- if (mic_len == 24) {
- WPA_PUT_BE16(reply192->key_data_length, wpa_ie_len);
- os_memcpy(reply192 + 1, wpa_ie, wpa_ie_len);
- } else {
- WPA_PUT_BE16(reply->key_data_length, wpa_ie_len);
- os_memcpy(reply + 1, wpa_ie, wpa_ie_len);
- }
+ key_mic = (u8 *) (reply + 1);
+ WPA_PUT_BE16(key_mic + mic_len, wpa_ie_len); /* Key Data Length */
+ os_memcpy(key_mic + mic_len + 2, wpa_ie, wpa_ie_len); /* Key Data */
os_free(rsn_ie_buf);
os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN);
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4");
- return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst,
- ETH_P_EAPOL, rbuf, rlen, key_mic);
+ return wpa_eapol_key_send(sm, ptk, ver, dst, ETH_P_EAPOL, rbuf, rlen,
+ key_mic);
}
@@ -1147,22 +1216,24 @@
{
size_t mic_len, hdrlen, rlen;
struct wpa_eapol_key *reply;
- struct wpa_eapol_key_192 *reply192;
u8 *rbuf, *key_mic;
mic_len = wpa_mic_len(sm->key_mgmt);
- hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
+ hdrlen = sizeof(*reply) + mic_len + 2;
rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
hdrlen, &rlen, (void *) &reply);
if (rbuf == NULL)
return -1;
- reply192 = (struct wpa_eapol_key_192 *) reply;
reply->type = (sm->proto == WPA_PROTO_RSN ||
sm->proto == WPA_PROTO_OSEN) ?
EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
key_info &= WPA_KEY_INFO_SECURE;
- key_info |= ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC;
+ key_info |= ver | WPA_KEY_INFO_KEY_TYPE;
+ 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);
@@ -1171,15 +1242,12 @@
os_memcpy(reply->replay_counter, key->replay_counter,
WPA_REPLAY_COUNTER_LEN);
- key_mic = reply192->key_mic; /* same offset for reply and reply192 */
- if (mic_len == 24)
- WPA_PUT_BE16(reply192->key_data_length, 0);
- else
- WPA_PUT_BE16(reply->key_data_length, 0);
+ key_mic = (u8 *) (reply + 1);
+ WPA_PUT_BE16(key_mic + mic_len, 0);
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4");
- return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst,
- ETH_P_EAPOL, rbuf, rlen, key_mic);
+ return wpa_eapol_key_send(sm, ptk, ver, dst, ETH_P_EAPOL, rbuf, rlen,
+ key_mic);
}
@@ -1456,22 +1524,22 @@
{
size_t mic_len, hdrlen, rlen;
struct wpa_eapol_key *reply;
- struct wpa_eapol_key_192 *reply192;
u8 *rbuf, *key_mic;
mic_len = wpa_mic_len(sm->key_mgmt);
- hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
+ hdrlen = sizeof(*reply) + mic_len + 2;
rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
hdrlen, &rlen, (void *) &reply);
if (rbuf == NULL)
return -1;
- reply192 = (struct wpa_eapol_key_192 *) reply;
reply->type = (sm->proto == WPA_PROTO_RSN ||
sm->proto == WPA_PROTO_OSEN) ?
EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
key_info &= WPA_KEY_INFO_KEY_INDEX_MASK;
- key_info |= ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
+ key_info |= ver | WPA_KEY_INFO_SECURE;
+ if (mic_len)
+ key_info |= WPA_KEY_INFO_MIC;
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);
@@ -1480,15 +1548,12 @@
os_memcpy(reply->replay_counter, key->replay_counter,
WPA_REPLAY_COUNTER_LEN);
- key_mic = reply192->key_mic; /* same offset for reply and reply192 */
- if (mic_len == 24)
- WPA_PUT_BE16(reply192->key_data_length, 0);
- else
- WPA_PUT_BE16(reply->key_data_length, 0);
+ key_mic = (u8 *) (reply + 1);
+ WPA_PUT_BE16(key_mic + mic_len, 0);
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
- return wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver,
- sm->bssid, ETH_P_EAPOL, rbuf, rlen, key_mic);
+ return wpa_eapol_key_send(sm, &sm->ptk, ver, sm->bssid, ETH_P_EAPOL,
+ rbuf, rlen, key_mic);
}
@@ -1564,7 +1629,7 @@
static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
- struct wpa_eapol_key_192 *key,
+ struct wpa_eapol_key *key,
u16 ver,
const u8 *buf, size_t len)
{
@@ -1572,12 +1637,12 @@
int ok = 0;
size_t mic_len = wpa_mic_len(sm->key_mgmt);
- os_memcpy(mic, key->key_mic, mic_len);
+ os_memcpy(mic, key + 1, mic_len);
if (sm->tptk_set) {
- os_memset(key->key_mic, 0, mic_len);
+ os_memset(key + 1, 0, mic_len);
wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len, sm->key_mgmt,
- ver, buf, len, key->key_mic);
- if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
+ ver, buf, len, (u8 *) (key + 1));
+ if (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");
@@ -1591,10 +1656,10 @@
}
if (!ok && sm->ptk_set) {
- os_memset(key->key_mic, 0, mic_len);
+ os_memset(key + 1, 0, mic_len);
wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len, sm->key_mgmt,
- ver, buf, len, key->key_mic);
- if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
+ ver, buf, len, (u8 *) (key + 1));
+ if (os_memcmp_const(mic, key + 1, mic_len) != 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Invalid EAPOL-Key MIC - "
"dropping packet");
@@ -1619,7 +1684,8 @@
/* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */
static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
- struct wpa_eapol_key *key, u16 ver,
+ struct wpa_eapol_key *key,
+ size_t mic_len, u16 ver,
u8 *key_data, size_t *key_data_len)
{
wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data",
@@ -1678,7 +1744,7 @@
}
os_memcpy(key_data, buf, *key_data_len);
bin_clear_free(buf, *key_data_len);
- WPA_PUT_BE16(key->key_data_length, *key_data_len);
+ WPA_PUT_BE16(((u8 *) (key + 1)) + mic_len, *key_data_len);
} else {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Unsupported key_info type %d", ver);
@@ -1741,6 +1807,76 @@
}
+#ifdef CONFIG_FILS
+static int wpa_supp_aead_decrypt(struct wpa_sm *sm, u8 *buf, size_t buf_len,
+ size_t *key_data_len)
+{
+ struct wpa_ptk *ptk;
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+ u8 *pos, *tmp;
+ const u8 *aad[1];
+ size_t aad_len[1];
+
+ if (*key_data_len < AES_BLOCK_SIZE) {
+ wpa_printf(MSG_INFO, "No room for AES-SIV data in the frame");
+ return -1;
+ }
+
+ if (sm->tptk_set)
+ ptk = &sm->tptk;
+ else if (sm->ptk_set)
+ ptk = &sm->ptk;
+ else
+ return -1;
+
+ hdr = (struct ieee802_1x_hdr *) buf;
+ key = (struct wpa_eapol_key *) (hdr + 1);
+ pos = (u8 *) (key + 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_printf(MSG_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 (sm->tptk_set) {
+ sm->tptk_set = 0;
+ sm->ptk_set = 1;
+ os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk));
+ os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+ }
+
+ os_memcpy(sm->rx_replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ sm->rx_replay_counter_set = 1;
+
+ return 0;
+}
+#endif /* CONFIG_FILS */
+
+
/**
* wpa_sm_rx_eapol - Process received WPA EAPOL frames
* @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -1763,12 +1899,11 @@
size_t plen, data_len, key_data_len;
const struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
- struct wpa_eapol_key_192 *key192;
u16 key_info, ver;
u8 *tmp = NULL;
int ret = -1;
struct wpa_peerkey *peerkey = NULL;
- u8 *key_data;
+ u8 *mic, *key_data;
size_t mic_len, keyhdrlen;
#ifdef CONFIG_IEEE80211R
@@ -1776,7 +1911,7 @@
#endif /* CONFIG_IEEE80211R */
mic_len = wpa_mic_len(sm->key_mgmt);
- keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+ keyhdrlen = sizeof(*key) + mic_len + 2;
if (len < sizeof(*hdr) + keyhdrlen) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
@@ -1828,12 +1963,8 @@
goto out;
os_memcpy(tmp, buf, data_len);
key = (struct wpa_eapol_key *) (tmp + sizeof(struct ieee802_1x_hdr));
- key192 = (struct wpa_eapol_key_192 *)
- (tmp + sizeof(struct ieee802_1x_hdr));
- if (mic_len == 24)
- key_data = (u8 *) (key192 + 1);
- else
- key_data = (u8 *) (key + 1);
+ mic = (u8 *) (key + 1);
+ key_data = mic + mic_len + 2;
if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN)
{
@@ -1844,11 +1975,8 @@
goto out;
}
- if (mic_len == 24)
- key_data_len = WPA_GET_BE16(key192->key_data_length);
- else
- key_data_len = WPA_GET_BE16(key->key_data_length);
- wpa_eapol_key_dump(sm, key, key_data_len, key192->key_mic, mic_len);
+ key_data_len = WPA_GET_BE16(mic + mic_len);
+ wpa_eapol_key_dump(sm, key, key_data_len, mic, mic_len);
if (key_data_len > plen - keyhdrlen) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key "
@@ -1867,6 +1995,7 @@
#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
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_OSEN) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: Unsupported EAPOL-Key descriptor version %d",
@@ -1882,7 +2011,8 @@
goto out;
}
- if (wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+ if ((wpa_key_mgmt_suite_b(sm->key_mgmt) ||
+ wpa_key_mgmt_fils(sm->key_mgmt)) &&
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)",
@@ -1904,6 +2034,7 @@
if (wpa_key_mgmt_sha256(sm->key_mgmt)) {
if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
sm->key_mgmt != WPA_KEY_MGMT_OSEN &&
+ !wpa_key_mgmt_fils(sm->key_mgmt) &&
!wpa_key_mgmt_suite_b(sm->key_mgmt)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: AP did not use the "
@@ -1914,6 +2045,7 @@
#endif /* CONFIG_IEEE80211W */
if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
!wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+ !wpa_key_mgmt_fils(sm->key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: CCMP is used, but EAPOL-Key "
@@ -2004,19 +2136,27 @@
}
if ((key_info & WPA_KEY_INFO_MIC) && !peerkey &&
- wpa_supplicant_verify_eapol_key_mic(sm, key192, ver, tmp, data_len))
+ 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, key192, ver, tmp,
+ 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))
+ goto out;
+ }
+#endif /* CONFIG_FILS */
+
if ((sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) &&
- (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
- if (wpa_supplicant_decrypt_key_data(sm, key, ver, key_data,
+ (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) && mic_len) {
+ if (wpa_supplicant_decrypt_key_data(sm, key, mic_len,
+ ver, key_data,
&key_data_len))
goto out;
}
@@ -2032,7 +2172,8 @@
/* 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) {
+ } else 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);
@@ -2047,14 +2188,15 @@
peerkey_rx_eapol_smk(sm, src_addr, key, key_data_len, key_info,
ver);
} else {
- if (key_info & WPA_KEY_INFO_MIC) {
+ if ((mic_len && (key_info & WPA_KEY_INFO_MIC)) ||
+ (!mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA))) {
/* 1/2 Group Key Handshake */
wpa_supplicant_process_1_of_2(sm, src_addr, key,
key_data, key_data_len,
ver);
} else {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: EAPOL-Key (Group) without Mic bit - "
+ "WPA: EAPOL-Key (Group) without Mic/Encr bit - "
"dropped");
}
}
@@ -2336,6 +2478,16 @@
clear_ptk = 0;
}
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_FILS
+ if (sm->fils_completed) {
+ /*
+ * Clear portValid to kick EAPOL state machine to re-enter
+ * AUTHENTICATED state to get the EAPOL port Authorized.
+ */
+ wpa_supplicant_key_neg_complete(sm, sm->bssid, 1);
+ clear_ptk = 0;
+ }
+#endif /* CONFIG_FILS */
if (clear_ptk) {
/*
@@ -2378,6 +2530,9 @@
#ifdef CONFIG_TDLS
wpa_tdls_disassoc(sm);
#endif /* CONFIG_TDLS */
+#ifdef CONFIG_FILS
+ sm->fils_completed = 0;
+#endif /* CONFIG_FILS */
/* Keys are not needed in the WPA state machine anymore */
wpa_sm_drop_sa(sm);
@@ -2726,6 +2881,10 @@
os_memcpy(sm->assoc_wpa_ie, wpa_ie, *wpa_ie_len);
sm->assoc_wpa_ie_len = *wpa_ie_len;
+ } else {
+ wpa_hexdump(MSG_DEBUG,
+ "WPA: Leave previously set WPA IE default",
+ sm->assoc_wpa_ie, sm->assoc_wpa_ie_len);
}
return 0;
@@ -3054,3 +3213,441 @@
sm->test_assoc_ie = buf;
}
#endif /* CONFIG_TESTING_OPTIONS */
+
+
+#ifdef CONFIG_FILS
+
+struct wpabuf * fils_build_auth(struct wpa_sm *sm)
+{
+ struct wpabuf *buf = NULL;
+ struct wpabuf *erp_msg;
+
+ erp_msg = eapol_sm_build_erp_reauth_start(sm->eapol);
+ if (!erp_msg && !sm->cur_pmksa) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Neither ERP EAP-Initiate/Re-auth nor PMKSA cache entry is available - skip FILS");
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG, "FILS: Try to use FILS (erp=%d pmksa_cache=%d)",
+ erp_msg != NULL, sm->cur_pmksa != NULL);
+
+ sm->fils_completed = 0;
+
+ if (!sm->assoc_wpa_ie) {
+ wpa_printf(MSG_INFO, "FILS: No own RSN IE set for FILS");
+ goto fail;
+ }
+
+ if (random_get_bytes(sm->fils_nonce, FILS_NONCE_LEN) < 0 ||
+ random_get_bytes(sm->fils_session, FILS_SESSION_LEN) < 0)
+ goto fail;
+
+ wpa_hexdump(MSG_DEBUG, "FILS: Generated FILS Nonce",
+ sm->fils_nonce, FILS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS: Generated FILS Session",
+ sm->fils_session, FILS_SESSION_LEN);
+
+ buf = wpabuf_alloc(1000 + sm->assoc_wpa_ie_len);
+ if (!buf)
+ goto fail;
+
+ /* Fields following the Authentication algorithm number field */
+
+ /* Authentication Transaction seq# */
+ wpabuf_put_le16(buf, 1);
+
+ /* 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 */
+
+ /* 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 */
+
+ /* FILS Nonce */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
+ wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN); /* Length */
+ /* Element ID Extension */
+ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE);
+ wpabuf_put_data(buf, sm->fils_nonce, FILS_NONCE_LEN);
+
+ /* FILS Session */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
+ wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); /* Length */
+ /* Element ID Extension */
+ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION);
+ wpabuf_put_data(buf, sm->fils_session, FILS_SESSION_LEN);
+
+ /* FILS Wrapped Data */
+ if (erp_msg) {
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
+ wpabuf_put_u8(buf, 1 + wpabuf_len(erp_msg)); /* Length */
+ /* Element ID Extension */
+ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_WRAPPED_DATA);
+ wpabuf_put_buf(buf, erp_msg);
+ }
+
+ wpa_hexdump_buf(MSG_DEBUG, "RSN: FILS fields for Authentication frame",
+ buf);
+
+fail:
+ wpabuf_free(erp_msg);
+ return buf;
+}
+
+
+int fils_process_auth(struct wpa_sm *sm, const u8 *data, size_t len)
+{
+ const u8 *pos, *end;
+ struct ieee802_11_elems elems;
+ struct wpa_ie_data rsn;
+ int pmkid_match = 0;
+ u8 ick[FILS_ICK_MAX_LEN];
+ size_t ick_len;
+ int res;
+
+ 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 */
+
+ 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;
+ }
+
+ /* RSNE */
+ wpa_hexdump(MSG_DEBUG, "FILS: RSN element", elems.rsn_ie,
+ elems.rsn_ie_len);
+ if (!elems.rsn_ie ||
+ 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;
+ }
+
+ if (!elems.fils_nonce) {
+ wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
+ return -1;
+ }
+ 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 */
+
+ /* PMKID List */
+ if (rsn.pmkid && rsn.num_pmkid > 0) {
+ wpa_hexdump(MSG_DEBUG, "FILS: PMKID List",
+ rsn.pmkid, rsn.num_pmkid * PMKID_LEN);
+
+ if (rsn.num_pmkid != 1) {
+ wpa_printf(MSG_DEBUG, "FILS: Invalid PMKID selection");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "FILS: PMKID", rsn.pmkid, PMKID_LEN);
+ if (os_memcmp(sm->cur_pmksa->pmkid, rsn.pmkid, PMKID_LEN) != 0)
+ {
+ wpa_printf(MSG_DEBUG, "FILS: PMKID mismatch");
+ wpa_hexdump(MSG_DEBUG, "FILS: Expected PMKID",
+ sm->cur_pmksa->pmkid, PMKID_LEN);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG,
+ "FILS: Matching PMKID - continue using PMKSA caching");
+ pmkid_match = 1;
+ }
+ if (!pmkid_match && sm->cur_pmksa) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: No PMKID match - cannot use cached PMKSA entry");
+ sm->cur_pmksa = NULL;
+ }
+
+ /* FILS Session */
+ if (!elems.fils_session) {
+ wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session,
+ FILS_SESSION_LEN);
+ if (os_memcmp(sm->fils_session, elems.fils_session, FILS_SESSION_LEN)
+ != 0) {
+ wpa_printf(MSG_DEBUG, "FILS: Session mismatch");
+ wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
+ sm->fils_session, FILS_SESSION_LEN);
+ return -1;
+ }
+
+ /* FILS Wrapped Data */
+ if (!sm->cur_pmksa && elems.fils_wrapped_data) {
+ wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data",
+ elems.fils_wrapped_data,
+ elems.fils_wrapped_data_len);
+ eapol_sm_process_erp_finish(sm->eapol, elems.fils_wrapped_data,
+ elems.fils_wrapped_data_len);
+ if (eapol_sm_failed(sm->eapol))
+ return -1;
+
+ res = eapol_sm_get_key(sm->eapol, sm->pmk, PMK_LEN);
+ if (res)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "FILS: ERP processing succeeded - add PMKSA cache entry for the result");
+ sm->cur_pmksa = pmksa_cache_add(sm->pmksa, sm->pmk, PMK_LEN,
+ NULL, NULL, 0, sm->bssid,
+ sm->own_addr,
+ sm->network_ctx, sm->key_mgmt);
+ }
+
+ if (!sm->cur_pmksa) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: No remaining options to continue FILS authentication");
+ return -1;
+ }
+
+ 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) {
+ wpa_printf(MSG_DEBUG, "FILS: Failed to derive PTK");
+ return -1;
+ }
+ sm->ptk_set = 1;
+ sm->tptk_set = 0;
+ os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+
+ 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 */
+ sm->key_mgmt, sm->fils_key_auth_sta,
+ sm->fils_key_auth_ap,
+ &sm->fils_key_auth_len);
+ os_memset(ick, 0, sizeof(ick));
+ return res;
+}
+
+
+struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek,
+ size_t *kek_len, const u8 **snonce,
+ const u8 **anonce)
+{
+ struct wpabuf *buf;
+
+ buf = wpabuf_alloc(1000);
+ if (!buf)
+ return NULL;
+
+ /* FILS Session */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
+ wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); /* Length */
+ /* Element ID Extension */
+ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION);
+ wpabuf_put_data(buf, sm->fils_session, FILS_SESSION_LEN);
+
+ /* Everything after FILS Session element gets encrypted in the driver
+ * with KEK. The buffer returned from here is the plaintext version. */
+
+ /* TODO: FILS Public Key */
+
+ /* FILS Key Confirm */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
+ wpabuf_put_u8(buf, 1 + sm->fils_key_auth_len); /* Length */
+ /* Element ID Extension */
+ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_KEY_CONFIRM);
+ wpabuf_put_data(buf, sm->fils_key_auth_sta, sm->fils_key_auth_len);
+
+ /* TODO: FILS HLP Container */
+
+ /* TODO: FILS IP Address Assignment */
+
+ wpa_hexdump_buf(MSG_DEBUG, "FILS: Association Request plaintext", buf);
+
+ *kek = sm->ptk.kek;
+ *kek_len = sm->ptk.kek_len;
+ wpa_hexdump_key(MSG_DEBUG, "FILS: KEK for AEAD", *kek, *kek_len);
+ *snonce = sm->fils_nonce;
+ wpa_hexdump(MSG_DEBUG, "FILS: SNonce for AEAD AAD",
+ *snonce, FILS_NONCE_LEN);
+ *anonce = sm->fils_anonce;
+ wpa_hexdump(MSG_DEBUG, "FILS: ANonce for AEAD AAD",
+ *anonce, FILS_NONCE_LEN);
+
+ return buf;
+}
+
+
+int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt;
+ const u8 *end, *ie_start;
+ struct ieee802_11_elems elems;
+ int keylen, rsclen;
+ enum wpa_alg alg;
+ struct wpa_gtk_data gd;
+ int maxkeylen;
+ struct wpa_eapol_ie_parse kde;
+
+ if (!sm || !sm->ptk_set) {
+ wpa_printf(MSG_DEBUG, "FILS: No KEK available");
+ return -1;
+ }
+
+ if (!wpa_key_mgmt_fils(sm->key_mgmt)) {
+ wpa_printf(MSG_DEBUG, "FILS: Not a FILS AKM");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "FILS: (Re)Association Response frame",
+ resp, len);
+
+ mgmt = (const struct ieee80211_mgmt *) resp;
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_resp))
+ return -1;
+
+ end = resp + len;
+ /* Same offset for Association Response and Reassociation Response */
+ ie_start = mgmt->u.assoc_resp.variable;
+
+ if (ieee802_11_parse_elems(ie_start, end - ie_start, &elems, 1) ==
+ ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Failed to parse decrypted elements");
+ goto fail;
+ }
+
+ if (!elems.fils_session) {
+ wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
+ return -1;
+ }
+ if (os_memcmp(elems.fils_session, sm->fils_session,
+ FILS_SESSION_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FILS: FILS Session mismatch");
+ wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session",
+ elems.fils_session, FILS_SESSION_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
+ sm->fils_session, FILS_SESSION_LEN);
+ }
+
+ /* TODO: FILS Public Key */
+
+ if (!elems.fils_key_confirm) {
+ wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element");
+ goto fail;
+ }
+ 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);
+ goto fail;
+ }
+ if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_ap,
+ 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_ap, sm->fils_key_auth_len);
+ goto fail;
+ }
+
+ /* Key Delivery */
+ if (!elems.key_delivery) {
+ wpa_printf(MSG_DEBUG, "FILS: No Key Delivery element");
+ goto fail;
+ }
+
+ /* Parse GTK and set the key to the driver */
+ os_memset(&gd, 0, sizeof(gd));
+ if (wpa_supplicant_parse_ies(elems.key_delivery + WPA_KEY_RSC_LEN,
+ elems.key_delivery_len - WPA_KEY_RSC_LEN,
+ &kde) < 0) {
+ wpa_printf(MSG_DEBUG, "FILS: Failed to parse KDEs");
+ goto fail;
+ }
+ if (!kde.gtk) {
+ wpa_printf(MSG_DEBUG, "FILS: No GTK KDE");
+ goto fail;
+ }
+ maxkeylen = gd.gtk_len = kde.gtk_len - 2;
+ if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
+ gd.gtk_len, maxkeylen,
+ &gd.key_rsc_len, &gd.alg))
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "FILS: Received GTK", kde.gtk, kde.gtk_len);
+ gd.keyidx = kde.gtk[0] & 0x3;
+ gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
+ !!(kde.gtk[0] & BIT(2)));
+ if (kde.gtk_len - 2 > sizeof(gd.gtk)) {
+ wpa_printf(MSG_DEBUG, "FILS: Too long GTK in GTK KDE (len=%lu)",
+ (unsigned long) kde.gtk_len - 2);
+ goto fail;
+ }
+ 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) {
+ wpa_printf(MSG_DEBUG, "FILS: Failed to set GTK");
+ goto fail;
+ }
+
+ if (ieee80211w_set_keys(sm, &kde) < 0) {
+ wpa_printf(MSG_DEBUG, "FILS: Failed to set IGTK");
+ goto fail;
+ }
+
+ alg = wpa_cipher_to_alg(sm->pairwise_cipher);
+ keylen = wpa_cipher_key_len(sm->pairwise_cipher);
+ rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
+ wpa_hexdump_key(MSG_DEBUG, "FILS: Set TK to driver",
+ sm->ptk.tk, keylen);
+ if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, null_rsc, rsclen,
+ sm->ptk.tk, keylen) < 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "FILS: Failed to set PTK to the driver (alg=%d keylen=%d bssid="
+ MACSTR ")",
+ alg, keylen, MAC2STR(sm->bssid));
+ goto fail;
+ }
+
+ /* TODO: TK could be cleared after auth frame exchange now that driver
+ * takes care of association frame encryption/decryption. */
+ /* TK is not needed anymore in supplicant */
+ os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
+
+ /* TODO: FILS HLP Container */
+
+ /* TODO: FILS IP Address Assignment */
+
+ wpa_printf(MSG_DEBUG, "FILS: Auth+Assoc completed successfully");
+ sm->fils_completed = 1;
+
+ return 0;
+fail:
+ return -1;
+}
+
+#endif /* CONFIG_FILS */
+
+
+int wpa_fils_is_completed(struct wpa_sm *sm)
+{
+#ifdef CONFIG_FILS
+ return sm && sm->fils_completed;
+#else /* CONFIG_FILS */
+ return 0;
+#endif /* CONFIG_FILS */
+}