[PATCH 2/2] tests: Verify PMKSA rejection preserves other AP entries
Andrei Otcheretianski
andrei.otcheretianski at intel.com
Thu Oct 29 06:32:03 PDT 2026
From: Daniel Gabay <daniel.gabay at intel.com>
Signed-off-by: Daniel Gabay <daniel.gabay at intel.com>
---
tests/hwsim/test_sae.py | 58 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 58 insertions(+)
diff --git a/tests/hwsim/test_sae.py b/tests/hwsim/test_sae.py
index e1e1614bcf..1a079baf27 100644
--- a/tests/hwsim/test_sae.py
+++ b/tests/hwsim/test_sae.py
@@ -3857,6 +3857,64 @@ def test_sae_pmksa_caching_ap_lost_entry(dev, apdev):
dev[0].request("RECONNECT")
dev[0].wait_connected(timeout=15, error="Reconnect timed out")
+def test_sae_pmksa_caching_reject_preserves_other_ap(dev, apdev):
+ """SAE PMKSA rejection preserves another AP's cached key"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+
+ # Full SAE with AP1 caches a PMKSA for AP1
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ bssid0 = hapd0.own_addr()
+ dev[0].set("sae_groups", "")
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412")
+ pmksa0 = dev[0].get_pmksa(bssid0)
+ if pmksa0 is None:
+ raise Exception("Missing PMKSA entry for AP1")
+
+ # Full SAE with AP2 caches a second PMKSA, for AP2
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ bssid1 = hapd1.own_addr()
+ dev[0].scan_for_bss(bssid1, freq="2412")
+ dev[0].roam(bssid1)
+ pmksa1 = dev[0].get_pmksa(bssid1)
+ if pmksa1 is None:
+ raise Exception("Missing PMKSA entry for AP2")
+
+ # AP1 drops its PMKSA, so the STA's AP1 PMKID becomes stale
+ if "OK" not in hapd0.request("PMKSA_FLUSH"):
+ raise Exception("AP1 PMKSA_FLUSH failed")
+
+ # PMKSA caching back to AP1 is rejected with status 53 (invalid PMKID)
+ dev[0].dump_monitor()
+ if "OK" not in dev[0].request("ROAM " + bssid0):
+ raise Exception("ROAM to AP1 failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=10)
+ if ev is None or "status_code=53" not in ev:
+ raise Exception(f"Expected invalid PMKID rejection: {ev}")
+
+ # Full SAE fallback reconnects to AP1 with a new PMKSA
+ dev[0].wait_connected(timeout=15)
+ if dev[0].get_status_field('bssid') != bssid0:
+ raise Exception("Did not reconnect to AP1")
+ hwsim_utils.test_connectivity(dev[0], hapd0)
+ replacement = dev[0].get_pmksa(bssid0)
+ if replacement is None or replacement['pmkid'] == pmksa0['pmkid']:
+ raise Exception("AP1 stale PMKSA entry was not replaced")
+
+ # Only the AP1 entry was flushed; the AP2 entry is kept
+ preserved = dev[0].get_pmksa(bssid1)
+ if preserved is None or preserved['pmkid'] != pmksa1['pmkid']:
+ raise Exception("AP2 PMKSA entry lost after AP1 rejection")
+
+ # Roaming back to AP2 reuses the kept PMKSA (no new SAE)
+ dev[0].roam(bssid1)
+ reused = dev[0].get_pmksa(bssid1)
+ if reused is None or reused['pmkid'] != pmksa1['pmkid']:
+ raise Exception("AP2 PMKSA entry not reused on roam back")
+ hwsim_utils.test_connectivity(dev[0], hapd1)
+
def test_sae_alt_sae_password_id_config(dev, apdev):
"""Configuration of alternative SAE password identifiers"""
check_sae_capab(dev[0])
--
2.53.0
More information about the Hostap
mailing list