From d588a95acb32dd7243ed8f4bca784cd087895f13 Mon Sep 17 00:00:00 2001 From: Kapil Gupta Date: Tue, 24 Feb 2026 10:45:16 +0530 Subject: [PATCH] fix(esp_wifi): Add alt snonce verification in softAP --- components/wpa_supplicant/src/ap/wpa_auth.c | 74 ++++++++++++++++++- components/wpa_supplicant/src/ap/wpa_auth_i.h | 4 + 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/components/wpa_supplicant/src/ap/wpa_auth.c b/components/wpa_supplicant/src/ap/wpa_auth.c index bf4ca6e26e5..a9e182e2b29 100644 --- a/components/wpa_supplicant/src/ap/wpa_auth.c +++ b/components/wpa_supplicant/src/ap/wpa_auth.c @@ -43,6 +43,10 @@ static int wpa_sm_step(struct wpa_state_machine *sm); +static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, + const u8 *pmk, unsigned int pmk_len, struct wpa_ptk *ptk); +static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, + size_t data_len); static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data, size_t data_len); static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, @@ -733,6 +737,14 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *s !wpa_replay_counter_valid(sm->key_replay, key->replay_counter)) { int i; + if (msg == PAIRWISE_4 && sm->alt_snonce_valid && + sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING && + os_memcmp(key->replay_counter, sm->alt_replay_counter, + WPA_REPLAY_COUNTER_LEN) == 0) { + wpa_printf(MSG_DEBUG, "WPA: Try to process received EAPOL-Key 4/4 based on old Replay Counter and SNonce from an earlier EAPOL-Key 1/4"); + goto continue_processing; + } + if (msg == PAIRWISE_2 && wpa_replay_counter_valid(sm->prev_key_replay, key->replay_counter) && @@ -746,9 +758,12 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *s * pending requests, so allow the SNonce to be updated * even if we have already sent out EAPOL-Key 3/4. */ + wpa_printf(MSG_DEBUG, "Process SNonce update from STA based on retransmitted EAPOL-Key 1/4"); sm->update_snonce = 1; - wpa_replay_counter_mark_invalid(sm->prev_key_replay, - key->replay_counter); + os_memcpy(sm->alt_SNonce, sm->SNonce, WPA_NONCE_LEN); + sm->alt_snonce_valid = TRUE; + os_memcpy(sm->alt_replay_counter, sm->key_replay[0].counter, + WPA_REPLAY_COUNTER_LEN); goto continue_processing; } @@ -862,7 +877,9 @@ continue_processing: sm->MICVerified = FALSE; if (sm->PTK_valid && !sm->update_snonce) { if (wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data, - data_len)) { + data_len) && + (msg != PAIRWISE_4 || !sm->alt_snonce_valid || + wpa_try_alt_snonce(sm, data, data_len))) { wpa_printf(MSG_INFO, "received EAPOL-Key with invalid MIC"); return; @@ -1210,6 +1227,56 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, eloop_register_timeout(1, 0, resend_eapol_handle, (void*)(sm->index), NULL); } +static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, + size_t data_len) +{ + struct wpa_ptk PTK; + int ok = 0; + 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) && + !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) { + pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, pmk); + if (pmk == NULL) + break; + pmk_len = PMK_LEN; + } else { + pmk = sm->PMK; + pmk_len = sm->pmk_len; + } + + if (!pmk && sm->pmksa) { + pmk = sm->pmksa->pmk; + pmk_len = sm->pmksa->pmk_len; + } + + wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK); + + if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len) == 0) { + ok = 1; + break; + } + + if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || + wpa_key_mgmt_sae(sm->wpa_key_mgmt)) + break; + } + + if (ok) { + wpa_printf(MSG_DEBUG, "WPA: Verified Key MIC using alt_SNonce"); + os_memcpy(&sm->PTK, &PTK, sizeof(PTK)); + sm->PTK_valid = TRUE; + os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN); + sm->alt_snonce_valid = FALSE; + return 0; + } + + return -1; +} + static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data, size_t data_len) { @@ -1246,6 +1313,7 @@ void wpa_remove_ptk(struct wpa_state_machine *sm) memset(&sm->PTK, 0, sizeof(sm->PTK)); wpa_auth_set_key(sm->wpa_auth, 0, WIFI_WPA_ALG_NONE, sm->addr, 0, NULL, 0); sm->pairwise_set = FALSE; + sm->alt_snonce_valid = FALSE; eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); } diff --git a/components/wpa_supplicant/src/ap/wpa_auth_i.h b/components/wpa_supplicant/src/ap/wpa_auth_i.h index 09b254ca2db..73bfae2b214 100644 --- a/components/wpa_supplicant/src/ap/wpa_auth_i.h +++ b/components/wpa_supplicant/src/ap/wpa_auth_i.h @@ -86,6 +86,7 @@ struct wpa_state_machine { unsigned int mgmt_frame_prot:1; unsigned int rx_eapol_key_secure:1; unsigned int update_snonce:1; + unsigned int alt_snonce_valid:1; #ifdef CONFIG_IEEE80211R_AP unsigned int ft_completed:1; unsigned int pmk_r1_name_valid:1; @@ -96,6 +97,9 @@ struct wpa_state_machine { u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN]; int req_replay_counter_used; + u8 alt_SNonce[WPA_NONCE_LEN]; + u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN]; + u8 *wpa_ie; size_t wpa_ie_len; u8 *rsnxe;