diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index fdc2bac3c..060b63517 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -1166,6 +1166,7 @@ static void handle_auth_fils_finish(struct hostapd_data *hapd, u8 *ie_buf = NULL; const u8 *pmk = NULL; size_t pmk_len = 0; + u8 pmk_buf[PMK_LEN_MAX]; if (resp != WLAN_STATUS_SUCCESS) goto fail; @@ -1234,8 +1235,16 @@ static void handle_auth_fils_finish(struct hostapd_data *hapd, wpabuf_put_u8(data, WLAN_EID_EXT_FILS_WRAPPED_DATA); wpabuf_put_buf(data, erp_resp); - pmk = msk; - pmk_len = msk_len > PMK_LEN ? PMK_LEN : msk_len; + if (fils_rmsk_to_pmk(wpa_auth_sta_key_mgmt(sta->wpa_sm), + msk, msk_len, sta->fils_snonce, fils_nonce, + NULL, 0, pmk_buf, &pmk_len)) { + wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + wpabuf_free(data); + data = NULL; + goto fail; + } + pmk = pmk_buf; } else if (pmksa) { pmk = pmksa->pmk; pmk_len = pmksa->pmk_len; diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index 6eb1e6631..c9cdc6984 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -230,6 +230,78 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, #ifdef CONFIG_FILS +int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len, + const u8 *snonce, const u8 *anonce, const u8 *dh_ss, + size_t dh_ss_len, u8 *pmk, size_t *pmk_len) +{ + u8 nonces[2 * FILS_NONCE_LEN]; + const u8 *addr[2]; + size_t len[2]; + size_t num_elem; + int res; + + /* PMK = HMAC-Hash(SNonce || ANonce, rMSK [ || DHss ]) */ + wpa_printf(MSG_DEBUG, "FILS: rMSK to PMK derivation"); + + if (wpa_key_mgmt_sha384(akmp)) + *pmk_len = SHA384_MAC_LEN; + else if (wpa_key_mgmt_sha256(akmp)) + *pmk_len = SHA256_MAC_LEN; + else + return -1; + + wpa_hexdump_key(MSG_DEBUG, "FILS: rMSK", rmsk, rmsk_len); + wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: DHss", dh_ss, dh_ss_len); + + os_memcpy(nonces, snonce, FILS_NONCE_LEN); + os_memcpy(&nonces[FILS_NONCE_LEN], anonce, FILS_NONCE_LEN); + addr[0] = rmsk; + len[0] = rmsk_len; + num_elem = 1; + if (dh_ss) { + addr[1] = dh_ss; + len[1] = dh_ss_len; + num_elem++; + } + if (wpa_key_mgmt_sha384(akmp)) + res = hmac_sha384_vector(nonces, 2 * FILS_NONCE_LEN, num_elem, + addr, len, pmk); + else + res = hmac_sha256_vector(nonces, 2 * FILS_NONCE_LEN, num_elem, + addr, len, pmk); + if (res == 0) + wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, *pmk_len); + return res; +} + + +int fils_pmkid_erp(int akmp, const u8 *reauth, size_t reauth_len, + u8 *pmkid) +{ + const u8 *addr[1]; + size_t len[1]; + u8 hash[SHA384_MAC_LEN]; + int res; + + /* PMKID = Truncate-128(Hash(EAP-Initiate/Reauth)) */ + addr[0] = reauth; + len[0] = reauth_len; + if (wpa_key_mgmt_sha384(akmp)) + res = sha384_vector(1, addr, len, hash); + else if (wpa_key_mgmt_sha256(akmp)) + res = sha256_vector(1, addr, len, hash); + else + return -1; + if (res) + return res; + os_memcpy(pmkid, hash, PMKID_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: PMKID", pmkid, PMKID_LEN); + return 0; +} + + int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa, const u8 *snonce, const u8 *anonce, struct wpa_ptk *ptk, u8 *ick, size_t *ick_len, int akmp, int cipher) diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index 4e952c1f8..3288a029b 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -330,6 +330,11 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, const u8 *addr1, const u8 *addr2, const u8 *nonce1, const u8 *nonce2, struct wpa_ptk *ptk, int akmp, int cipher); +int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len, + const u8 *snonce, const u8 *anonce, const u8 *dh_ss, + size_t dh_ss_len, u8 *pmk, size_t *pmk_len); +int fils_pmkid_erp(int akmp, const u8 *reauth, size_t reauth_len, + u8 *pmkid); int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa, const u8 *snonce, const u8 *anonce, struct wpa_ptk *ptk, u8 *ick, size_t *ick_len, int akmp, int cipher); diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 365845f7c..b2fc0ba7d 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -17,6 +17,7 @@ #include "crypto/aes_siv.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" +#include "eap_common/eap_defs.h" #include "eapol_supp/eapol_supp_sm.h" #include "wpa.h" #include "eloop.h" @@ -3302,12 +3303,19 @@ struct wpabuf * fils_build_auth(struct wpa_sm *sm) wpabuf_put_data(buf, sm->fils_session, FILS_SESSION_LEN); /* FILS Wrapped Data */ + sm->fils_erp_pmkid_set = 0; 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); + /* Calculate pending PMKID here so that we do not need to + * maintain a copy of the EAP-Initiate/Reauth message. */ + if (fils_pmkid_erp(sm->key_mgmt, wpabuf_head(erp_msg), + wpabuf_len(erp_msg), + sm->fils_erp_pmkid) == 0) + sm->fils_erp_pmkid_set = 1; } wpa_hexdump_buf(MSG_DEBUG, "RSN: FILS fields for Authentication frame", @@ -3407,6 +3415,9 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *data, size_t len) /* FILS Wrapped Data */ if (!sm->cur_pmksa && elems.fils_wrapped_data) { + u8 rmsk[ERP_MAX_KEY_LEN]; + size_t rmsk_len; + wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data", elems.fils_wrapped_data, elems.fils_wrapped_data_len); @@ -3415,14 +3426,30 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *data, size_t len) if (eapol_sm_failed(sm->eapol)) return -1; - res = eapol_sm_get_key(sm->eapol, sm->pmk, PMK_LEN); + rmsk_len = ERP_MAX_KEY_LEN; + res = eapol_sm_get_key(sm->eapol, rmsk, rmsk_len); + if (res == PMK_LEN) { + rmsk_len = PMK_LEN; + res = eapol_sm_get_key(sm->eapol, rmsk, rmsk_len); + } if (res) return -1; + res = fils_rmsk_to_pmk(sm->key_mgmt, rmsk, rmsk_len, + sm->fils_nonce, sm->fils_anonce, NULL, 0, + sm->pmk, &sm->pmk_len); + os_memset(rmsk, 0, sizeof(rmsk)); + + if (!sm->fils_erp_pmkid_set) { + wpa_printf(MSG_DEBUG, "FILS: PMKID not available"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "FILS: PMKID", sm->fils_erp_pmkid, + PMKID_LEN); 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->cur_pmksa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, + sm->fils_erp_pmkid, NULL, 0, + sm->bssid, sm->own_addr, sm->network_ctx, sm->key_mgmt); } diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 180d4680a..491fc986a 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -147,6 +147,8 @@ struct wpa_sm { u8 fils_key_auth_sta[FILS_MAX_KEY_AUTH_LEN]; size_t fils_key_auth_len; unsigned int fils_completed:1; + unsigned int fils_erp_pmkid_set:1; + u8 fils_erp_pmkid[PMKID_LEN]; #endif /* CONFIG_FILS */ };