[PATCH v3 27/27] tests: Add EPPKE authentication test cases
Benjamin Berg
benjamin at sipsolutions.net
Thu Jan 22 23:04:23 PST 2026
[resend to avoid list size limit]
Hi,
for most of the tests you already have a run_* function as they are
almost identical. I did notice these two though that could also share
code by using a run_ handler (maybe there are others):
* test_eppke_mld_ap_with_base_akm_sae_legacy_client_pmksa_cached
* test_eppke_mld_ap_with_base_akm_sae_ext_legacy_client_pmksa_cached
One more comment inline at the end.
On Thu, 2026-01-15 at 22:03 +0530, Ainy Kumari wrote:
> From: Sai Pratyusha Magam <smagam at qti.qualcomm.com>
>
> Add hwsim test cases to verify EPPKE authentication,
> including (Re)Association frame encryption in
> multi-link and legacy 11ax scenarios. Tests cover AP and
> MLD configurations with SAE and SAE-EXT-KEY base AKMs,
> SAE-EXT-KEY AKM with different groups, PMKSA Caching,
> validating RSNXE flags, AKM suite selection, and
> authentication algorithm handling.
>
> Signed-off-by: Ainy Kumari <ainy.kumari at oss.qualcomm.com>
> Signed-off-by: Sai Pratyusha Magam <smagam at qti.qualcomm.com>
> ---
> tests/hwsim/test_eppke.py | 675 ++++++++++++++++++++++++++++++++++++++
> 1 file changed, 675 insertions(+)
> create mode 100644 tests/hwsim/test_eppke.py
>
> diff --git a/tests/hwsim/test_eppke.py b/tests/hwsim/test_eppke.py
> new file mode 100644
> index 000000000..e52e96e1e
> --- /dev/null
> +++ b/tests/hwsim/test_eppke.py
> @@ -0,0 +1,675 @@
> +# Test cases for Enhanced Privacy Protection Key Exchange(EPPKE)
> +# Copyright (c) 2025, Qualcomm Innovation Center, Inc.
> +#
> +# This software may be distributed under the terms of the BSD license.
> +# See README for more details.
> +
> +import time
> +
> +import hostapd
> +from wpasupplicant import WpaSupplicant
> +from utils import *
> +from hwsim import HWSimRadio
> +from test_eht import eht_mld_ap_wpa2_params, eht_mld_enable_ap, traffic_test, eht_verify_status
> +
> +def test_eppke_akm_suite_and_rsnxe_feature_flags(dev, apdev):
> + """AP EPPKE AKM Advertisement with SAE base AKM and EPPKE related feature flags"""
> + ssid = "test-eppke-authentication"
> + params = hostapd.wpa3_params(ssid=ssid,
> + password = "1234567890")
> + params['wpa_key_mgmt'] = params['wpa_key_mgmt'] + ' ' + 'EPPKE'
> + params['assoc_frame_encryption'] = '1'
> + params['pmksa_caching_privacy'] = '1'
> + params['eap_using_authentication_frames'] = '1'
> + params['sae_pwe'] = '2'
> +
> + hapd = hostapd.add_ap(apdev[0], params)
> + time.sleep(2)
> + #TODO: Add pcap file checks to validate correct
> + #RSNXE bits and AKM suite presence in Beacon frames
> +
> + #Disable all EPPKE related RSNXE flags and test
> + params['assoc_frame_encryption'] = '0'
> + params['pmksa_caching_privacy'] = '0'
> + params['eap_using_authentication_frames'] = '0'
> + hapd = hostapd.add_ap(apdev[0], params)
> + time.sleep(2)
> +
> +def test_eppke_ap_with_base_akm_sae_legacy_client(dev, apdev):
> + """EPPKE authentication with a Non-MLO AP with base AKM SAE and legacy client"""
> + ssid = "test-eppke-authentication"
> + passphrase = '1234567890'
> + params = hostapd.wpa3_params(ssid=ssid,
> + password = passphrase)
> + params['wpa_key_mgmt'] = params['wpa_key_mgmt'] + ' ' + 'EPPKE'
> + params['assoc_frame_encryption'] = '1'
> + params['pmksa_caching_privacy'] = '1'
> + params['eap_using_authentication_frames'] = '1'
> + params['sae_pwe'] = '2'
> + hapd = hostapd.add_ap(apdev[0], params)
> +
> + try:
> + dev[0].set("sae_groups", "")
> + dev[0].set("sae_pwe", "1")
> + dev[0].connect(ssid, sae_password=passphrase, scan_freq="2412",
> + key_mgmt="SAE", ieee80211w="2", beacon_prot="1",
> + pairwise="CCMP")
> + hapd.wait_sta();
> + sta = hapd.get_sta(dev[0].own_addr())
> + if sta["AKMSuiteSelector"] != '00-0f-ac-8' or sta["auth_alg"] != '9':
> + raise Exception("Incorrect Auth Algo/AKMSuiteSelector value")
> +
> + finally:
> + dev[0].set("sae_groups", "")
> + dev[0].set("sae_pwe", "0")
> +
> +def run_eppke_sae_ext_key(dev, apdev, group):
> + """EPPKE authentication with a Non-MLO AP with base AKM SAE-EXT-KEY and legacy client"""
> + ssid = "test-eppke-authentication"
> + passphrase = '1234567890'
> + params = hostapd.wpa3_params(ssid=ssid,
> + password = passphrase)
> + params['wpa_key_mgmt'] = params['wpa_key_mgmt'] + ' ' + 'SAE-EXT-KEY EPPKE'
> + params['assoc_frame_encryption'] = '1'
> + params['pmksa_caching_privacy'] = '1'
> + params['eap_using_authentication_frames'] = '1'
> + params['sae_pwe'] = '2'
> + params['pasn_groups'] = str(group)
> + params['sae_groups'] = str(group)
> + hapd = hostapd.add_ap(apdev[0], params)
> +
> + try:
> + dev[0].set("sae_groups", str(group))
> + dev[0].set("sae_pwe", "1")
> + dev[0].connect(ssid, sae_password=passphrase, scan_freq="2412",
> + key_mgmt="SAE-EXT-KEY", ieee80211w="2", beacon_prot="1",
> + pairwise="CCMP")
> + hapd.wait_sta();
> + sta = hapd.get_sta(dev[0].own_addr())
> + if sta["AKMSuiteSelector"] != '00-0f-ac-24' or sta["auth_alg"] != '9':
> + raise Exception("Incorrect Auth Algo/AKMSuiteSelector value")
> +
> + finally:
> + dev[0].set("sae_groups", "")
> + dev[0].set("sae_pwe", "0")
> +
> +def test_eppke_ap_with_base_akm_sae_ext_legacy_client_19(dev, apdev):
> + """EPPKE authentication with a Non-MLO AP with base AKM SAE-EXT-KEY and legacy client, group 19"""
> + run_eppke_sae_ext_key(dev, apdev, 19)
> +
> +def test_eppke_ap_with_base_akm_sae_ext_legacy_client_20(dev, apdev):
> + """EPPKE authentication with a Non-MLO AP with base AKM SAE-EXT-KEY and legacy client, group 20"""
> + run_eppke_sae_ext_key(dev, apdev, 20)
> +
> +def test_eppke_ap_with_base_akm_sae_ext_legacy_client_21(dev, apdev):
> + """EPPKE authentication with a Non-MLO AP with base AKM SAE-EXT-KEY and legacy client, group 21"""
> + run_eppke_sae_ext_key(dev, apdev, 21)
> +
> +def test_eppke_mld_ap_with_base_akm_sae_legacy_client(dev, apdev):
> + """EPPKE authentication with an MLD AP with base AKM SAE and legacy client"""
> + with HWSimRadio(use_mlo=True) as (hapd_radio, hapd_iface):
> + passphrase = '1234567890'
> + ssid = "test-eppke-authentication"
> + params = eht_mld_ap_wpa2_params(ssid, passphrase,
> + key_mgmt="SAE", mfp="2", pwe='1')
> + params['wpa_key_mgmt'] = params['wpa_key_mgmt'] + ' ' + 'EPPKE'
> + params['assoc_frame_encryption'] = '1'
> + params['pmksa_caching_privacy'] = '1'
> + params['eap_using_authentication_frames'] = '1'
> + params['rsn_pairwise'] = "CCMP GCMP-256"
> +
> + hapd0 = eht_mld_enable_ap(hapd_iface, 0, params)
> +
> + params['channel'] = '6'
> + hapd1 = eht_mld_enable_ap(hapd_iface, 1, params)
> +
> + try:
> + dev[0].set("sae_groups", "")
> + dev[0].set("sae_pwe", "1")
> + dev[0].connect(ssid, sae_password=passphrase, scan_freq="2412",
> + key_mgmt="SAE", ieee80211w="2", beacon_prot="1",
> + pairwise="CCMP GCMP-256")
> + bssid = dev[0].get_status_field("bssid")
> + if hapd0.own_addr() == bssid:
> + hapd0.wait_sta();
> + sta = hapd0.get_sta(dev[0].own_addr())
> + if sta["AKMSuiteSelector"] != '00-0f-ac-8' or sta["auth_alg"] != '9':
> + raise Exception("Incorrect Auth Algo/AKMSuiteSelector value")
> + elif hapd1.own_addr() == bssid:
> + hapd1.wait_sta();
> + sta = hapd1.get_sta(dev[0].own_addr())
> + if sta["AKMSuiteSelector"] != '00-0f-ac-8' or sta["auth_alg"] != '9':
> + raise Exception("Incorrect Auth Algo/AKMSuiteSelector value")
> + else:
> + raise Exception("Unknown BSSID: " + bssid)
I see this pattern quite a bit in the patch. For
https://patchwork.ozlabs.org/project/hostap/patch/20251016143754.2532665-3-benjamin@sipsolutions.net/
I added a simple "mld_wait_event" helper, that will accept the event
from any of the given hapd instances.
Maybe you could pick it up and add it to your patch? Thinking about it
now, it could make sense to return a tuple with (hapd, event) instead,
so the API user knows which hostapd instance replied.
Feel free to improve further if you find the API suboptimal.
Benjamin
> [Removed rest of the patch]
More information about the Hostap
mailing list