[PATCH 2/2] tests: SAE PMKSA caching attempt rejected with deauthentication

Louis Kotze loukot at gmail.com
Thu Jul 9 13:56:35 PDT 2026


Verify that wpa_supplicant drops its PMKSA cache entry and falls back
to a full SAE authentication when the AP rejects a PMKSA caching
attempt by deauthenticating the STA instead of responding to the
Association Request frame with an error status code.

Signed-off-by: Louis Kotze <loukot at gmail.com>
---
 tests/hwsim/test_sae.py | 48 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/tests/hwsim/test_sae.py b/tests/hwsim/test_sae.py
index 5b04b9268..bcab0dd0a 100644
--- a/tests/hwsim/test_sae.py
+++ b/tests/hwsim/test_sae.py
@@ -3834,3 +3834,51 @@ def test_sae_pmksa_caching_ap_lost_entry(dev, apdev):
     hapd.request("PMKSA_FLUSH");
     dev[0].request("RECONNECT")
     dev[0].wait_connected(timeout=15, error="Reconnect timed out")
+
+def test_sae_pmksa_caching_reject_deauth(dev, apdev):
+    """SAE PMKSA caching attempt rejected with Deauthentication frame"""
+    check_sae_capab(dev[0])
+    params = hostapd.wpa2_params(ssid="test-sae",
+                                 passphrase="12345678")
+    params['wpa_key_mgmt'] = 'SAE'
+    hapd = hostapd.add_ap(apdev[0], params)
+
+    dev[0].set("sae_groups", "")
+    dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+                   scan_freq="2412")
+    dev[0].request("DISCONNECT")
+    dev[0].wait_disconnected()
+
+    # The AP loses its PMKSA cache entry and rejects the PMKSA caching
+    # attempt by deauthenticating the STA (e.g., with reason code 9) instead
+    # of responding to the Association Request frame with an error status
+    # code. wpa_supplicant needs to drop its own PMKSA cache entry and fall
+    # back to a full SAE authentication for the connection to recover.
+    hapd.request("PMKSA_FLUSH")
+    hapd.set("ext_mgmt_frame_handling", "1")
+    hapd.dump_monitor()
+    dev[0].request("RECONNECT")
+    sae_auth = False
+    for i in range(30):
+        req = hapd.mgmt_rx()
+        if req is None:
+            raise Exception("MGMT RX wait timed out")
+        if req['subtype'] == 11:
+            algo, = struct.unpack('<H', req['payload'][0:2])
+            if algo == 3:
+                # Fallback to SAE authentication seen; process this frame
+                # normally and let the connection complete.
+                sae_auth = True
+                hapd.set("ext_mgmt_frame_handling", "0")
+                hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
+                break
+        if req['subtype'] == 0:
+            # Association Request frame of the PMKSA caching attempt: do not
+            # respond to it; deauthenticate the STA instead.
+            hapd.request("DEAUTHENTICATE " + dev[0].own_addr() +
+                         " reason=9")
+            continue
+        hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
+    if not sae_auth:
+        raise Exception("No fallback to SAE authentication seen")
+    dev[0].wait_connected(timeout=15, error="Reconnect timed out")
-- 
2.55.0




More information about the Hostap mailing list