|
@@ -1747,6 +1747,10 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
|
|
|
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)
|
|
|
+ remove_ptk = 0;
|
|
|
+#endif /* CONFIG_FILS */
|
|
|
|
|
|
if (remove_ptk) {
|
|
|
sm->PTK_valid = FALSE;
|
|
@@ -2057,6 +2061,11 @@ int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
|
|
|
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;
|
|
|
}
|
|
|
|
|
@@ -2114,6 +2123,138 @@ static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
|
|
|
*_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;
|
|
|
+}
|
|
|
+
|
|
|
#endif /* CONFIG_FILS */
|
|
|
|
|
|
|