From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:10 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:10 +0530 Subject: [PATCH v2 00/28] 11bi: Add Support for EPPKE Authentication Message-ID: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Add support for new authentication protocol Enhanced Privacy Protection Key Exchange (EPPKE) as specified in section 12.16.9 of IEEE P802.11bi/D2.0. Verified the EPPKE Authentication for MLO as well as Non-MLO cases with the new hwsim test script test_eppke.py and working fine. All 21 test cases are passing successfully. New hwsim test cases covers below scenario: 1. Initial Connection (MLO and Non-MLO with SAE and SAE-EXT-KEY AKMs) 2. Initial Connection with SAE-EXT-KEY AKM with different groups 3. PMKSA Caching Attempt (MLO and Non-MLO with SAE and SAE-EXT-KEY AKMs) 4. GTK and PTK rekey Depends-on: [PATCH wireless-next v2 0/9] wifi: cfg80211/mac80211: Add Support for EPPKE Authentication [PATCH wireless-next] wifi: mac80211: Fix AAD/Nonce computation for management frames with MLO --- Changes in v1 --> v2: - Resolved reviewer's comments on dependent kernel changes. - Removed few patches, no longer required. --- Ainy Kumari (12): Sync nl80211_copy.h with wireless-next linux/nl80211.h 11bi: Rename FILS nonce element and related constants to generic 'nonce' PASN: Extend RSNXE capability field to 32 bits EPPKE: Add wiphy capability flag for EPPKE authentication wpa_supplicant: Add CONFIG_ENC_ASSOC for association frame encryption support EPPKE: Add EPPKE support to PASN PTK derivation per IEEE P802.11bi/D2.0 EPPKE: Add support for EPPKE authentication for SME-in-Userspace case EPPKE: Add Multi-Link support in Authentication frames PASN: Add support for MIC computation for M3 frame for SME-in-Userspace EPPKE: Update RSNE construction and validation per IEEE P802.11bi/D2.0 EPPKE: Skip 4-Way handshake and authorize supplicant port on association EPPKE: Retrieve KCK/KEK from PTKSA and install group keys for GTK rekey Kavita Kavita (3): sme: Add support to install temporal key for EPPKE Authentication Protocol Add support for temporal key removal on association failure tests: Enable CONFIG_ENC_ASSOC for hwsim wpa_supplicant Sai Pratyusha Magam (13): 11bi: Add wiphy capability flag for (Re)Association frame encryption support 11bi: Enhanced Privacy Protection (EPP) related definitions 11bi: Configuration options to control EPP feature support in AP mode 11bi: RSNE/RSNXE capability Extensions in AP mode PASN: Modify PASN Authentication frame header APIs based on auth_algo EPPKE: Extend existing PASN APIs for EPPKE Authentication EPPKE: PTK/MIC Computation and key installation changes in Responder mode EPPKE: EPP peer indication to driver EPPKE: EPP capabilities negotiation indication EPPKE: RSNE/Key delivery element in (Re)Association Response EPPKE: Skip 4WH and move PTK state directly to PTKINITDONE tests: Enable CONFIG_ENC_ASSOC for hwsim hostapd tests: Add EPPKE authentication test cases hostapd/Makefile | 5 + hostapd/config_file.c | 12 + hostapd/defconfig | 3 + hostapd/hostapd.conf | 23 + src/ap/ap_config.c | 5 + src/ap/ap_config.h | 5 + src/ap/ap_drv_ops.c | 7 +- src/ap/ap_drv_ops.h | 2 +- src/ap/ap_mlme.c | 6 +- src/ap/ieee802_11.c | 173 ++++-- src/ap/ieee802_11_eht.c | 1 + src/ap/ieee802_11_shared.c | 16 + src/ap/sta_info.c | 8 +- src/ap/sta_info.h | 6 +- src/ap/wpa_auth.c | 126 ++++- src/ap/wpa_auth.h | 18 +- src/ap/wpa_auth_glue.c | 25 + src/ap/wpa_auth_i.h | 3 + src/ap/wpa_auth_ie.c | 26 +- src/common/common_module_tests.c | 2 +- src/common/defs.h | 7 + src/common/ieee802_11_common.c | 10 +- src/common/ieee802_11_common.h | 2 +- src/common/ieee802_11_defs.h | 9 +- src/common/wpa_common.c | 186 +++++-- src/common/wpa_common.h | 15 +- src/drivers/driver.h | 16 + src/drivers/driver_atheros.c | 8 +- src/drivers/driver_nl80211.c | 29 + src/drivers/driver_nl80211_capa.c | 9 + src/drivers/nl80211_copy.h | 355 ++++++++++++- src/p2p/p2p.c | 3 +- src/pasn/pasn_common.c | 29 +- src/pasn/pasn_common.h | 36 +- src/pasn/pasn_initiator.c | 266 +++++++--- src/pasn/pasn_responder.c | 105 +++- src/rsn_supp/wpa.c | 199 ++++++- src/rsn_supp/wpa.h | 8 + src/rsn_supp/wpa_i.h | 7 +- tests/hwsim/example-hostapd.config | 1 + tests/hwsim/example-wpa_supplicant.config | 1 + tests/hwsim/test_eppke.py | 614 ++++++++++++++++++++++ wlantest/rx_mgmt.c | 14 +- wpa_supplicant/Makefile | 5 + wpa_supplicant/defconfig | 3 + wpa_supplicant/events.c | 54 ++ wpa_supplicant/pasn_supplicant.c | 6 +- wpa_supplicant/sme.c | 242 ++++++++- wpa_supplicant/wpa_supplicant.c | 6 + 49 files changed, 2449 insertions(+), 268 deletions(-) create mode 100644 tests/hwsim/test_eppke.py base-commit: 3ea5c0df504538f2d12c9f46ad74dfc888d4d99c -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:11 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:11 +0530 Subject: [PATCH v2 01/28] Sync nl80211_copy.h with wireless-next linux/nl80211.h In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-2-ainy.kumari@oss.qualcomm.com> Signed-off-by: Ainy Kumari Signed-off-by: Sai Pratyusha Magam --- src/drivers/nl80211_copy.h | 355 ++++++++++++++++++++++++++++++++++++- 1 file changed, 349 insertions(+), 6 deletions(-) diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h index e9ccf43fe..81ada0c3a 100644 --- a/src/drivers/nl80211_copy.h +++ b/src/drivers/nl80211_copy.h @@ -1085,8 +1085,9 @@ * %NL80211_ATTR_NAN_MASTER_PREF attribute and optional * %NL80211_ATTR_BANDS attributes. If %NL80211_ATTR_BANDS is * omitted or set to 0, it means don't-care and the device will - * decide what to use. After this command NAN functions can be - * added. + * decide what to use. Additional cluster configuration may be + * optionally provided with %NL80211_ATTR_NAN_CONFIG. + * After this command NAN functions can be added. * @NL80211_CMD_STOP_NAN: Stop the NAN operation, identified by * its %NL80211_ATTR_WDEV interface. * @NL80211_CMD_ADD_NAN_FUNCTION: Add a NAN function. The function is defined @@ -1115,6 +1116,10 @@ * current configuration is not changed. If it is present but * set to zero, the configuration is changed to don't-care * (i.e. the device can decide what to do). + * Additional parameters may be provided with + * %NL80211_ATTR_NAN_CONFIG. User space should provide all previously + * configured nested attributes under %NL80211_ATTR_NAN_CONFIG, even if + * only a subset was changed. * @NL80211_CMD_NAN_MATCH: Notification sent when a match is reported. * This will contain a %NL80211_ATTR_NAN_MATCH nested attribute and * %NL80211_ATTR_COOKIE. @@ -1330,12 +1335,32 @@ * TID to Link mapping for downlink/uplink traffic. * * @NL80211_CMD_ASSOC_MLO_RECONF: For a non-AP MLD station, request to - * add/remove links to/from the association. + * add/remove links to/from the association. To indicate link + * reconfiguration request results from the driver, this command is also + * used as an event to notify userspace about the added links information. + * For notifying the removed links information, the existing + * %NL80211_CMD_LINKS_REMOVED command is used. This command is also used to + * notify userspace about newly added links for the current connection in + * case of AP-initiated link recommendation requests, received via + * a BTM (BSS Transition Management) request or a link reconfig notify + * frame, where the driver handles the link recommendation offload. * * @NL80211_CMD_EPCS_CFG: EPCS configuration for a station. Used by userland to * control EPCS configuration. Used to notify userland on the current state * of EPCS. * + * @NL80211_CMD_NAN_NEXT_DW_NOTIFICATION: This command is used to notify + * user space about the next NAN Discovery Window (DW). User space may use + * it to prepare frames to be sent in the next DW. + * %NL80211_ATTR_WIPHY_FREQ is used to indicate the frequency of the next + * DW. SDF transmission should be requested with %NL80211_CMD_FRAME and + * the device/driver shall take care of the actual transmission timing. + * This notification is only sent to the NAN interface owning socket + * (see %NL80211_ATTR_SOCKET_OWNER flag). + * @NL80211_CMD_NAN_CLUSTER_JOINED: This command is used to notify + * user space that the NAN new cluster has been joined. The cluster ID is + * indicated by %NL80211_ATTR_MAC. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1596,6 +1621,9 @@ enum nl80211_commands { NL80211_CMD_ASSOC_MLO_RECONF, NL80211_CMD_EPCS_CFG, + NL80211_CMD_NAN_NEXT_DW_NOTIFICATION, + NL80211_CMD_NAN_CLUSTER_JOINED, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1935,8 +1963,9 @@ enum nl80211_commands { * The driver must also specify support for this with the extended * features NL80211_EXT_FEATURE_BEACON_RATE_LEGACY, * NL80211_EXT_FEATURE_BEACON_RATE_HT, - * NL80211_EXT_FEATURE_BEACON_RATE_VHT and - * NL80211_EXT_FEATURE_BEACON_RATE_HE. + * NL80211_EXT_FEATURE_BEACON_RATE_VHT, + * NL80211_EXT_FEATURE_BEACON_RATE_HE and + * NL80211_EXT_FEATURE_BEACON_RATE_EHT. * * @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain * at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME. @@ -2275,7 +2304,8 @@ enum nl80211_commands { * @NL80211_ATTR_PEER_AID: Association ID for the peer TDLS station (u16). * This is similar to @NL80211_ATTR_STA_AID but with a difference of being * allowed to be used with the first @NL80211_CMD_SET_STATION command to - * update a TDLS peer STA entry. + * update a TDLS peer STA entry. For S1G interfaces, this is limited to + * 1600 for the current mac80211 implementation. * * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information. * @@ -2899,6 +2929,57 @@ enum nl80211_commands { * APs Support". Drivers may set additional flags that they support * in the kernel or device. * + * @NL80211_ATTR_WIPHY_RADIO_INDEX: (int) Integer attribute denoting the index + * of the radio in interest. Internally a value of -1 is used to + * indicate that the radio id is not given in user-space. This means + * that all the attributes are applicable to all the radios. If there is + * a radio index provided in user-space, the attributes will be + * applicable to that specific radio only. If the radio id is greater + * thank the number of radios, error denoting invalid value is returned. + * + * @NL80211_ATTR_S1G_LONG_BEACON_PERIOD: (u8) Integer attribute that represents + * the number of beacon intervals between each long beacon transmission + * for an S1G BSS with short beaconing enabled. This is a required + * attribute for initialising an S1G short beaconing BSS. When updating + * the short beacon data, this is not required. It has a minimum value of + * 2 (i.e 2 beacon intervals). + * + * @NL80211_ATTR_S1G_SHORT_BEACON: Nested attribute containing the short beacon + * head and tail used to set or update the short beacon templates. When + * bringing up a new interface, %NL80211_ATTR_S1G_LONG_BEACON_PERIOD is + * required alongside this attribute. Refer to + * @enum nl80211_s1g_short_beacon_attrs for the attribute definitions. + * + * @NL80211_ATTR_BSS_PARAM: nested attribute used with %NL80211_CMD_GET_WIPHY + * which indicates which BSS parameters can be modified. The attribute can + * also be used as flag attribute by user-space in %NL80211_CMD_SET_BSS to + * indicate that it wants strict checking on the BSS parameters to be + * modified. + * + * @NL80211_ATTR_NAN_CONFIG: Nested attribute for + * extended NAN cluster configuration. This is used with + * %NL80211_CMD_START_NAN and %NL80211_CMD_CHANGE_NAN_CONFIG. + * See &enum nl80211_nan_conf_attributes for details. + * This attribute is optional. + * @NL80211_ATTR_NAN_NEW_CLUSTER: Flag attribute indicating that a new + * NAN cluster has been created. This is used with + * %NL80211_CMD_NAN_CLUSTER_JOINED + * @NL80211_ATTR_NAN_CAPABILITIES: Nested attribute for NAN capabilities. + * This is used with %NL80211_CMD_GET_WIPHY to indicate the NAN + * capabilities supported by the driver. See &enum nl80211_nan_capabilities + * for details. + * + * @NL80211_ATTR_S1G_PRIMARY_2MHZ: flag attribute indicating that the S1G + * primary channel is 2 MHz wide, and the control channel designates + * the 1 MHz primary subchannel within that 2 MHz primary. + * + * @NL80211_ATTR_EPP_PEER: A flag attribute to indicate if the peer is an EPP + * STA. Used with %NL80211_CMD_NEW_STA and %NL80211_CMD_ADD_LINK_STA + * + * @NL80211_ATTR_EPP_FLAGS: A (u32) bitmap attribute to indicate the negotiated + * EPP capabilities of an EPP AP and an EPP non-AP STA. See + * &enum nl80211_epp_flags for details. Used with %NL80211_CMD_SET_STATION + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3456,6 +3537,21 @@ enum nl80211_attrs { NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS, + NL80211_ATTR_WIPHY_RADIO_INDEX, + + NL80211_ATTR_S1G_LONG_BEACON_PERIOD, + NL80211_ATTR_S1G_SHORT_BEACON, + NL80211_ATTR_BSS_PARAM, + NL80211_ATTR_NAN_CONFIG, + NL80211_ATTR_NAN_NEW_CLUSTER, + NL80211_ATTR_NAN_CAPABILITIES, + + NL80211_ATTR_S1G_PRIMARY_2MHZ, + + NL80211_ATTR_EPP_PEER, + + NL80211_ATTR_EPP_FLAGS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3617,6 +3713,21 @@ enum nl80211_sta_flags { NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1 }; +/** + * enum nl80211_epp_flags - EPP Flags + * + * Negotiated EPP capabilities of an EPP STA + * + * @NL80211_EPP_FLAG_ASSOC_FRAME_ENCRYPTION: (Re)Association + * Request/Response frame encryption support + * @NL80211_EPP_FLAG_1X_UTILIZING_AUTHENTICATION_FRAMES: + * IEEE 802.1X (EAP) Authentication utilizing Authentication frames + */ +enum nl80211_epp_flags { + NL80211_EPP_FLAG_ASSOC_FRAME_ENCRYPTION = 0, + NL80211_EPP_FLAG_1X_UTILIZING_AUTHENTICATION_FRAMES, +}; + /** * enum nl80211_sta_p2p_ps_status - station support of P2P PS * @@ -3701,6 +3812,22 @@ enum nl80211_eht_gi { NL80211_RATE_INFO_EHT_GI_3_2, }; +/** + * enum nl80211_eht_ltf - EHT long training field + * @NL80211_RATE_INFO_EHT_1XLTF: 3.2 usec + * @NL80211_RATE_INFO_EHT_2XLTF: 6.4 usec + * @NL80211_RATE_INFO_EHT_4XLTF: 12.8 usec + * @NL80211_RATE_INFO_EHT_6XLTF: 19.2 usec + * @NL80211_RATE_INFO_EHT_8XLTF: 25.6 usec + */ +enum nl80211_eht_ltf { + NL80211_RATE_INFO_EHT_1XLTF, + NL80211_RATE_INFO_EHT_2XLTF, + NL80211_RATE_INFO_EHT_4XLTF, + NL80211_RATE_INFO_EHT_6XLTF, + NL80211_RATE_INFO_EHT_8XLTF, +}; + /** * enum nl80211_eht_ru_alloc - EHT RU allocation values * @NL80211_RATE_INFO_EHT_RU_ALLOC_26: 26-tone RU allocation @@ -4337,6 +4464,12 @@ enum nl80211_wmm_rule { * very low power (VLP) AP, despite being NO_IR. * @NL80211_FREQUENCY_ATTR_ALLOW_20MHZ_ACTIVITY: This channel can be active in * 20 MHz bandwidth, despite being NO_IR. + * @NL80211_FREQUENCY_ATTR_NO_4MHZ: 4 MHz operation is not allowed on this + * channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_NO_8MHZ: 8 MHz operation is not allowed on this + * channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_NO_16MHZ: 16 MHz operation is not allowed on this + * channel in current regulatory domain. * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * currently defined * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use @@ -4382,6 +4515,9 @@ enum nl80211_frequency_attr { NL80211_FREQUENCY_ATTR_CAN_MONITOR, NL80211_FREQUENCY_ATTR_ALLOW_6GHZ_VLP_AP, NL80211_FREQUENCY_ATTR_ALLOW_20MHZ_ACTIVITY, + NL80211_FREQUENCY_ATTR_NO_4MHZ, + NL80211_FREQUENCY_ATTR_NO_8MHZ, + NL80211_FREQUENCY_ATTR_NO_16MHZ, /* keep last */ __NL80211_FREQUENCY_ATTR_AFTER_LAST, @@ -5315,6 +5451,7 @@ enum nl80211_bss_status { * @NL80211_AUTHTYPE_FILS_SK: Fast Initial Link Setup shared key * @NL80211_AUTHTYPE_FILS_SK_PFS: Fast Initial Link Setup shared key with PFS * @NL80211_AUTHTYPE_FILS_PK: Fast Initial Link Setup public key + * @NL80211_AUTHTYPE_EPPKE: Enhanced Privacy Protection Key Exchange * @__NL80211_AUTHTYPE_NUM: internal * @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm * @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by @@ -5330,6 +5467,7 @@ enum nl80211_auth_type { NL80211_AUTHTYPE_FILS_SK, NL80211_AUTHTYPE_FILS_SK_PFS, NL80211_AUTHTYPE_FILS_PK, + NL80211_AUTHTYPE_EPPKE, /* keep last */ __NL80211_AUTHTYPE_NUM, @@ -5447,6 +5585,10 @@ enum nl80211_key_attributes { * see &struct nl80211_txrate_he * @NL80211_TXRATE_HE_GI: configure HE GI, 0.8us, 1.6us and 3.2us. * @NL80211_TXRATE_HE_LTF: configure HE LTF, 1XLTF, 2XLTF and 4XLTF. + * @NL80211_TXRATE_EHT: EHT rates allowed for TX rate selection, + * see &struct nl80211_txrate_eht + * @NL80211_TXRATE_EHT_GI: configure EHT GI, (u8, see &enum nl80211_eht_gi) + * @NL80211_TXRATE_EHT_LTF: configure EHT LTF, (u8, see &enum nl80211_eht_ltf) * @__NL80211_TXRATE_AFTER_LAST: internal * @NL80211_TXRATE_MAX: highest TX rate attribute */ @@ -5459,6 +5601,9 @@ enum nl80211_tx_rate_attributes { NL80211_TXRATE_HE, NL80211_TXRATE_HE_GI, NL80211_TXRATE_HE_LTF, + NL80211_TXRATE_EHT, + NL80211_TXRATE_EHT_GI, + NL80211_TXRATE_EHT_LTF, /* keep last */ __NL80211_TXRATE_AFTER_LAST, @@ -5491,6 +5636,15 @@ enum nl80211_txrate_gi { NL80211_TXRATE_FORCE_LGI, }; +#define NL80211_EHT_NSS_MAX 16 +/** + * struct nl80211_txrate_eht - EHT MCS/NSS txrate bitmap + * @mcs: MCS bitmap table for each NSS (array index 0 for 1 stream, etc.) + */ +struct nl80211_txrate_eht { + __u16 mcs[NL80211_EHT_NSS_MAX]; +}; + /** * enum nl80211_band - Frequency band * @NL80211_BAND_2GHZ: 2.4 GHz ISM band @@ -6615,6 +6769,18 @@ enum nl80211_feature_flags { * (signaling and payload protected) A-MSDUs and this shall be advertised * in the RSNXE. * + * @NL80211_EXT_FEATURE_BEACON_RATE_EHT: Driver supports beacon rate + * configuration (AP/mesh) with EHT rates. + * + * @NL80211_EXT_FEATURE_EPPKE: Driver supports Enhanced Privacy Protection + * Key Exchange (EPPKE) with user space SME (NL80211_CMD_AUTHENTICATE) + * in non-AP STA mode. + * + * @NL80211_EXT_FEATURE_ASSOC_FRAME_ENCRYPTION: This specifies that the + * driver supports encryption of (Re)Association Request and Response + * frames in both non?AP STA and AP mode as specified in + * "IEEE P802.11bi/D3.0, 12.16.6". + * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. */ @@ -6690,6 +6856,9 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_OWE_OFFLOAD_AP, NL80211_EXT_FEATURE_DFS_CONCURRENT, NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT, + NL80211_EXT_FEATURE_BEACON_RATE_EHT, + NL80211_EXT_FEATURE_EPPKE, + NL80211_EXT_FEATURE_ASSOC_FRAME_ENCRYPTION, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, @@ -7244,6 +7413,105 @@ enum nl80211_nan_match_attributes { NL80211_NAN_MATCH_ATTR_MAX = NUM_NL80211_NAN_MATCH_ATTR - 1 }; +/** + * enum nl80211_nan_band_conf_attributes - NAN band configuration attributes + * @__NL80211_NAN_BAND_CONF_INVALID: Invalid. + * @NL80211_NAN_BAND_CONF_BAND: Band for which the configuration is + * being set. The value is according to &enum nl80211_band (u8). + * @NL80211_NAN_BAND_CONF_FREQ: Discovery frequency. This attribute shall not + * be present on 2.4 GHZ band. On 5 GHz band its presence is optional. + * The allowed values are 5220 (channel 44) or 5745 (channel 149). + * If not present, channel 149 is used if allowed, otherwise channel 44 + * will be selected. The value is in MHz (u16). + * @NL80211_NAN_BAND_CONF_RSSI_CLOSE: RSSI close threshold used for NAN state + * transition algorithm as described in chapters 3.3.6 and 3.3.7 "NAN + * Device Role and State Transition" of Wi-Fi Aware (TM) Specification + * v4.0. If not specified, default device value is used. The value should + * be greater than -60 dBm (s8). + * @NL80211_NAN_BAND_CONF_RSSI_MIDDLE: RSSI middle threshold used for NAN state + * transition algorithm as described in chapters 3.3.6 and 3.3.7 "NAN + * Device Role and State Transition" of Wi-Fi Aware (TM) Specification + * v4.0. If not present, default device value is used. The value should be + * greater than -75 dBm and less than %NL80211_NAN_BAND_CONF_RSSI_CLOSE + * (s8). + * @NL80211_NAN_BAND_CONF_WAKE_DW: Committed DW information (values 0-5). + * Value 0 means that the device will not wake up during the + * discovery window. Values 1-5 mean that the device will wake up + * during each 2^(n - 1) discovery window, where n is the value of + * this attribute. Setting this attribute to 0 is not allowed on + * 2.4 GHz band (u8). This is an optional parameter (default is 1). + * @NL80211_NAN_BAND_CONF_DISABLE_SCAN: Optional flag attribute to disable + * scanning (for cluster merge) on the band. If set, the device will not + * scan on this band anymore. Disabling scanning on 2.4 GHz band is not + * allowed. + * @NUM_NL80211_NAN_BAND_CONF_ATTR: Internal. + * @NL80211_NAN_BAND_CONF_ATTR_MAX: Highest NAN band configuration attribute. + * + * These attributes are used to configure NAN band-specific parameters. Note, + * that both RSSI attributes should be configured (or both left unset). + */ +enum nl80211_nan_band_conf_attributes { + __NL80211_NAN_BAND_CONF_INVALID, + NL80211_NAN_BAND_CONF_BAND, + NL80211_NAN_BAND_CONF_FREQ, + NL80211_NAN_BAND_CONF_RSSI_CLOSE, + NL80211_NAN_BAND_CONF_RSSI_MIDDLE, + NL80211_NAN_BAND_CONF_WAKE_DW, + NL80211_NAN_BAND_CONF_DISABLE_SCAN, + + /* keep last */ + NUM_NL80211_NAN_BAND_CONF_ATTR, + NL80211_NAN_BAND_CONF_ATTR_MAX = NUM_NL80211_NAN_BAND_CONF_ATTR - 1, +}; + +/** + * enum nl80211_nan_conf_attributes - NAN configuration attributes + * @__NL80211_NAN_CONF_INVALID: Invalid attribute, used for validation. + * @NL80211_NAN_CONF_CLUSTER_ID: ID for the NAN cluster. This is a MAC + * address that can take values from 50-6F-9A-01-00-00 to + * 50-6F-9A-01-FF-FF. This attribute is optional. If not present, + * a random Cluster ID will be chosen. + * @NL80211_NAN_CONF_EXTRA_ATTRS: Additional NAN attributes to be + * published in the beacons. This is an optional byte array. + * @NL80211_NAN_CONF_VENDOR_ELEMS: Vendor-specific elements that will + * be published in the beacons. This is an optional byte array. + * @NL80211_NAN_CONF_BAND_CONFIGS: This is a nested array attribute, + * containing multiple entries for each supported band. Each band + * configuration consists of &enum nl80211_nan_band_conf_attributes. + * @NL80211_NAN_CONF_SCAN_PERIOD: Scan period in seconds. If not configured, + * device default is used. Zero value will disable scanning. + * This is u16 (optional). + * @NL80211_NAN_CONF_SCAN_DWELL_TIME: Scan dwell time in TUs per channel. + * Only non-zero values are valid. If not configured the device default + * value is used. This is u16 (optional) + * @NL80211_NAN_CONF_DISCOVERY_BEACON_INTERVAL: Discovery beacon interval + * in TUs. Valid range is 50-200 TUs. If not configured the device default + * value is used. This is u8 (optional) + * @NL80211_NAN_CONF_NOTIFY_DW: If set, the driver will notify userspace about + * the upcoming discovery window with + * %NL80211_CMD_NAN_NEXT_DW_NOTIFICATION. + * This is a flag attribute. + * @NUM_NL80211_NAN_CONF_ATTR: Internal. + * @NL80211_NAN_CONF_ATTR_MAX: Highest NAN configuration attribute. + * + * These attributes are used to configure NAN-specific parameters. + */ +enum nl80211_nan_conf_attributes { + __NL80211_NAN_CONF_INVALID, + NL80211_NAN_CONF_CLUSTER_ID, + NL80211_NAN_CONF_EXTRA_ATTRS, + NL80211_NAN_CONF_VENDOR_ELEMS, + NL80211_NAN_CONF_BAND_CONFIGS, + NL80211_NAN_CONF_SCAN_PERIOD, + NL80211_NAN_CONF_SCAN_DWELL_TIME, + NL80211_NAN_CONF_DISCOVERY_BEACON_INTERVAL, + NL80211_NAN_CONF_NOTIFY_DW, + + /* keep last */ + NUM_NL80211_NAN_CONF_ATTR, + NL80211_NAN_CONF_ATTR_MAX = NUM_NL80211_NAN_CONF_ATTR - 1, +}; + /** * enum nl80211_external_auth_action - Action to perform with external * authentication request. Used by NL80211_ATTR_EXTERNAL_AUTH_ACTION. @@ -8088,6 +8356,7 @@ enum nl80211_ap_settings_flags { * and contains attributes defined in &enum nl80211_if_combination_attrs. * @NL80211_WIPHY_RADIO_ATTR_ANTENNA_MASK: bitmask (u32) of antennas * connected to this radio. + * @NL80211_WIPHY_RADIO_ATTR_RTS_THRESHOLD: RTS threshold (u32) of this radio. * * @__NL80211_WIPHY_RADIO_ATTR_LAST: Internal * @NL80211_WIPHY_RADIO_ATTR_MAX: Highest attribute @@ -8099,6 +8368,7 @@ enum nl80211_wiphy_radio_attrs { NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE, NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION, NL80211_WIPHY_RADIO_ATTR_ANTENNA_MASK, + NL80211_WIPHY_RADIO_ATTR_RTS_THRESHOLD, /* keep last */ __NL80211_WIPHY_RADIO_ATTR_LAST, @@ -8128,4 +8398,77 @@ enum nl80211_wiphy_radio_freq_range { NL80211_WIPHY_RADIO_FREQ_ATTR_MAX = __NL80211_WIPHY_RADIO_FREQ_ATTR_LAST - 1, }; +/** + * enum nl80211_s1g_short_beacon_attrs - S1G short beacon data + * + * @__NL80211_S1G_SHORT_BEACON_ATTR_INVALID: Invalid + * + * @NL80211_S1G_SHORT_BEACON_ATTR_HEAD: Short beacon head (binary). + * @NL80211_S1G_SHORT_BEACON_ATTR_TAIL: Short beacon tail (binary). + * + * @__NL80211_S1G_SHORT_BEACON_ATTR_LAST: Internal + * @NL80211_S1G_SHORT_BEACON_ATTR_MAX: Highest attribute + */ +enum nl80211_s1g_short_beacon_attrs { + __NL80211_S1G_SHORT_BEACON_ATTR_INVALID, + + NL80211_S1G_SHORT_BEACON_ATTR_HEAD, + NL80211_S1G_SHORT_BEACON_ATTR_TAIL, + + /* keep last */ + __NL80211_S1G_SHORT_BEACON_ATTR_LAST, + NL80211_S1G_SHORT_BEACON_ATTR_MAX = + __NL80211_S1G_SHORT_BEACON_ATTR_LAST - 1 +}; + +/** + * enum nl80211_nan_capabilities - NAN (Neighbor Aware Networking) + * capabilities. + * + * @__NL80211_NAN_CAPABILITIES_INVALID: Invalid. + * @NL80211_NAN_CAPA_CONFIGURABLE_SYNC: Flag attribute indicating that + * the device supports configurable synchronization. If set, the device + * should be able to handle %NL80211_ATTR_NAN_CONFIG + * attribute in the %NL80211_CMD_START_NAN (and change) command. + * @NL80211_NAN_CAPA_USERSPACE_DE: Flag attribute indicating that + * NAN Discovery Engine (DE) is not offloaded and the driver assumes + * user space DE implementation. When set, %NL80211_CMD_ADD_NAN_FUNCTION, + * %NL80211_CMD_DEL_NAN_FUNCTION and %NL80211_CMD_NAN_MATCH commands + * should not be used. In addition, the device/driver should support + * sending discovery window (DW) notifications using + * %NL80211_CMD_NAN_NEXT_DW_NOTIFICATION and handling transmission and + * reception of NAN SDF frames on NAN device interface during DW windows. + * (%NL80211_CMD_FRAME is used to transmit SDFs) + * @NL80211_NAN_CAPA_OP_MODE: u8 attribute indicating the supported operation + * modes as defined in Wi-Fi Aware (TM) specification Table 81 (Operation + * Mode field format). + * @NL80211_NAN_CAPA_NUM_ANTENNAS: u8 attribute indicating the number of + * TX and RX antennas supported by the device. Lower nibble indicates + * the number of TX antennas and upper nibble indicates the number of RX + * antennas. Value 0 indicates the information is not available. + * See table 79 of Wi-Fi Aware (TM) specification (Number of + * Antennas field). + * @NL80211_NAN_CAPA_MAX_CHANNEL_SWITCH_TIME: u16 attribute indicating the + * maximum time in microseconds that the device requires to switch + * channels. + * @NL80211_NAN_CAPA_CAPABILITIES: u8 attribute containing the + * capabilities of the device as defined in Wi-Fi Aware (TM) + * specification Table 79 (Capabilities field). + * @__NL80211_NAN_CAPABILITIES_LAST: Internal + * @NL80211_NAN_CAPABILITIES_MAX: Highest NAN capability attribute. + */ +enum nl80211_nan_capabilities { + __NL80211_NAN_CAPABILITIES_INVALID, + + NL80211_NAN_CAPA_CONFIGURABLE_SYNC, + NL80211_NAN_CAPA_USERSPACE_DE, + NL80211_NAN_CAPA_OP_MODE, + NL80211_NAN_CAPA_NUM_ANTENNAS, + NL80211_NAN_CAPA_MAX_CHANNEL_SWITCH_TIME, + NL80211_NAN_CAPA_CAPABILITIES, + /* keep last */ + __NL80211_NAN_CAPABILITIES_LAST, + NL80211_NAN_CAPABILITIES_MAX = __NL80211_NAN_CAPABILITIES_LAST - 1, +}; + #endif /* __LINUX_NL80211_H */ -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:12 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:12 +0530 Subject: [PATCH v2 02/28] 11bi: Rename FILS nonce element and related constants to generic 'nonce' In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-3-ainy.kumari@oss.qualcomm.com> Previously, the nonce element and related constants were used exclusively in FILS authentication frames. Rename the nonce element and related constants to reflect their broader usage beyond FILS authentication frames, as specified in P802.11bi/D2.1,9.4.2.188. Signed-off-by: Ainy Kumari --- src/ap/ieee802_11.c | 32 +++++++++++++++--------------- src/ap/sta_info.h | 2 +- src/ap/wpa_auth.c | 12 ++++++------ src/common/ieee802_11_common.c | 10 +++++----- src/common/ieee802_11_common.h | 2 +- src/common/ieee802_11_defs.h | 4 ++-- src/common/wpa_common.c | 36 +++++++++++++++++----------------- src/drivers/driver_atheros.c | 8 ++++---- src/pasn/pasn_common.h | 4 ++-- src/pasn/pasn_initiator.c | 20 +++++++++---------- src/pasn/pasn_responder.c | 6 +++--- src/rsn_supp/wpa.c | 20 +++++++++---------- src/rsn_supp/wpa_i.h | 4 ++-- wlantest/rx_mgmt.c | 14 ++++++------- wpa_supplicant/sme.c | 6 +++--- 15 files changed, 90 insertions(+), 90 deletions(-) diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 24ef8b75f..611017da1 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -2299,14 +2299,14 @@ void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, if (resp != WLAN_STATUS_SUCCESS) goto fail; - if (!elems.fils_nonce) { + if (!elems.nonce) { wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } - wpa_hexdump(MSG_DEBUG, "FILS: SNonce", elems.fils_nonce, - FILS_NONCE_LEN); - os_memcpy(sta->fils_snonce, elems.fils_nonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: SNonce", elems.nonce, + NONCE_LEN); + os_memcpy(sta->fils_snonce, elems.nonce, NONCE_LEN); /* PMKID List */ if (rsn.pmkid && rsn.num_pmkid > 0) { @@ -2414,7 +2414,7 @@ prepare_auth_resp_fils(struct hostapd_data *hapd, const u8 *msk, size_t msk_len, int *is_pub) { - u8 fils_nonce[FILS_NONCE_LEN]; + u8 fils_nonce[NONCE_LEN]; size_t ielen; struct wpabuf *data = NULL; const u8 *ie; @@ -2449,12 +2449,12 @@ prepare_auth_resp_fils(struct hostapd_data *hapd, ie = ie_buf; } - if (random_get_bytes(fils_nonce, FILS_NONCE_LEN) < 0) { + if (random_get_bytes(fils_nonce, NONCE_LEN) < 0) { *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce", - fils_nonce, FILS_NONCE_LEN); + fils_nonce, NONCE_LEN); #ifdef CONFIG_FILS_SK_PFS if (sta->fils_dh_ss && sta->fils_ecdh) { @@ -2506,10 +2506,10 @@ prepare_auth_resp_fils(struct hostapd_data *hapd, /* FILS Nonce */ wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */ - wpabuf_put_u8(data, 1 + FILS_NONCE_LEN); /* Length */ + wpabuf_put_u8(data, 1 + NONCE_LEN); /* Length */ /* Element ID Extension */ - wpabuf_put_u8(data, WLAN_EID_EXT_FILS_NONCE); - wpabuf_put_data(data, fils_nonce, FILS_NONCE_LEN); + wpabuf_put_u8(data, WLAN_EID_EXT_NONCE); + wpabuf_put_data(data, fils_nonce, NONCE_LEN); /* FILS Session */ wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */ @@ -2795,13 +2795,13 @@ static void pasn_fils_auth_resp(struct hostapd_data *hapd, goto fail; } - if (random_get_bytes(fils->anonce, FILS_NONCE_LEN) < 0) { + if (random_get_bytes(fils->anonce, NONCE_LEN) < 0) { wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to get ANonce"); goto fail; } wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS ANonce", - fils->anonce, FILS_NONCE_LEN); + fils->anonce, NONCE_LEN); ret = fils_rmsk_to_pmk(pasn_get_akmp(pasn), msk, msk_len, fils->nonce, fils->anonce, NULL, 0, pmk, &pmk_len); @@ -2913,7 +2913,7 @@ static int pasn_wd_handle_fils(struct hostapd_data *hapd, struct sta_info *sta, return -1; } - if (!elems.rsn_ie || !elems.fils_nonce || !elems.fils_nonce || + if (!elems.rsn_ie || !elems.nonce || !elems.nonce || !elems.wrapped_data || !elems.fils_session) { wpa_printf(MSG_DEBUG, "PASN: FILS: Missing IEs"); return -1; @@ -2938,9 +2938,9 @@ static int pasn_wd_handle_fils(struct hostapd_data *hapd, struct sta_info *sta, return -1; } - wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", elems.fils_nonce, - FILS_NONCE_LEN); - os_memcpy(fils->nonce, elems.fils_nonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", elems.nonce, + NONCE_LEN); + os_memcpy(fils->nonce, elems.nonce, NONCE_LEN); wpa_hexdump(MSG_DEBUG, "PASN: FILS: Session", elems.fils_session, FILS_SESSION_LEN); diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index 4f41f7028..3fb97edd5 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -262,7 +262,7 @@ struct sta_info { #endif /* CONFIG_TAXONOMY */ #ifdef CONFIG_FILS - u8 fils_snonce[FILS_NONCE_LEN]; + u8 fils_snonce[NONCE_LEN]; u8 fils_session[FILS_SESSION_LEN]; u8 fils_erp_pmkid[PMKID_LEN]; u8 *fils_pending_assoc_req; diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 49268b21e..620e55387 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -3131,8 +3131,8 @@ int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk, forced_memzero(ick, sizeof(ick)); /* Store nonces for (Re)Association Request/Response frame processing */ - os_memcpy(sm->SNonce, snonce, FILS_NONCE_LEN); - os_memcpy(sm->ANonce, anonce, FILS_NONCE_LEN); + os_memcpy(sm->SNonce, snonce, NONCE_LEN); + os_memcpy(sm->ANonce, anonce, NONCE_LEN); return res; } @@ -3349,10 +3349,10 @@ int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session, aad_len[1] = ETH_ALEN; /* The STA's nonce */ aad[2] = sm->SNonce; - aad_len[2] = FILS_NONCE_LEN; + aad_len[2] = NONCE_LEN; /* The AP's nonce */ aad[3] = sm->ANonce; - aad_len[3] = FILS_NONCE_LEN; + aad_len[3] = NONCE_LEN; /* * The (Re)Association Request frame from the Capability Information * field to the FILS Session element (both inclusive). @@ -3408,10 +3408,10 @@ int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf, aad_len[1] = ETH_ALEN; /* The AP's nonce */ aad[2] = sm->ANonce; - aad_len[2] = FILS_NONCE_LEN; + aad_len[2] = NONCE_LEN; /* The STA's nonce */ aad[3] = sm->SNonce; - aad_len[3] = FILS_NONCE_LEN; + aad_len[3] = NONCE_LEN; /* * The (Re)Association Response frame from the Capability Information * field (the same offset in both Association and Reassociation diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index 5d1e02f81..451f6eb3a 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -362,10 +362,10 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen, elems->fils_pk = pos; elems->fils_pk_len = elen; break; - case WLAN_EID_EXT_FILS_NONCE: - if (elen != FILS_NONCE_LEN) + case WLAN_EID_EXT_NONCE: + if (elen != NONCE_LEN) break; - elems->fils_nonce = pos; + elems->nonce = pos; break; case WLAN_EID_EXT_OWE_DH_PARAM: if (elen < 2) @@ -941,8 +941,8 @@ void ieee802_11_elems_clear_ext_ids(struct ieee802_11_elems *elems, elems->fils_pk = NULL; elems->fils_pk_len = 0; break; - case WLAN_EID_EXT_FILS_NONCE: - elems->fils_nonce = NULL; + case WLAN_EID_EXT_NONCE: + elems->nonce = NULL; break; case WLAN_EID_EXT_OWE_DH_PARAM: elems->owe_dh = NULL; diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index 09bd0675a..7f6f387c2 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -94,7 +94,7 @@ struct ieee802_11_elems { const u8 *key_delivery; const u8 *wrapped_data; const u8 *fils_pk; - const u8 *fils_nonce; + const u8 *nonce; const u8 *owe_dh; const u8 *power_capab; const u8 *roaming_cons_sel; diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 89f0a1bf8..1b964439b 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -504,7 +504,7 @@ #define WLAN_EID_EXT_EXTENDED_REQUEST 10 #define WLAN_EID_EXT_ESTIMATED_SERVICE_PARAMS 11 #define WLAN_EID_EXT_FILS_PUBLIC_KEY 12 -#define WLAN_EID_EXT_FILS_NONCE 13 +#define WLAN_EID_EXT_NONCE 13 #define WLAN_EID_EXT_FUTURE_CHANNEL_GUIDANCE 14 #define WLAN_EID_EXT_OWE_DH_PARAM 32 #define WLAN_EID_EXT_PASSWORD_IDENTIFIER 33 @@ -974,7 +974,7 @@ enum lci_req_subelem { LCI_REQ_SUBELEM_MAX_AGE = 4, }; -#define FILS_NONCE_LEN 16 +#define NONCE_LEN 16 #define FILS_SESSION_LEN 8 #define FILS_CACHE_ID_LEN 2 #define FILS_MAX_KEY_AUTH_LEN 48 diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index 8b0d92201..d1a6e86a4 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -681,7 +681,7 @@ 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]; + u8 nonces[2 * NONCE_LEN]; const u8 *addr[2]; size_t len[2]; size_t num_elem; @@ -698,12 +698,12 @@ int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len, 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: SNonce", snonce, NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, 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); + os_memcpy(nonces, snonce, NONCE_LEN); + os_memcpy(&nonces[NONCE_LEN], anonce, NONCE_LEN); addr[0] = rmsk; len[0] = rmsk_len; num_elem = 1; @@ -713,10 +713,10 @@ int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len, num_elem++; } if (wpa_key_mgmt_sha384(akmp)) - res = hmac_sha384_vector(nonces, 2 * FILS_NONCE_LEN, num_elem, + res = hmac_sha384_vector(nonces, 2 * NONCE_LEN, num_elem, addr, len, pmk); else - res = hmac_sha256_vector(nonces, 2 * FILS_NONCE_LEN, num_elem, + res = hmac_sha256_vector(nonces, 2 * NONCE_LEN, num_elem, addr, len, pmk); if (res == 0) wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, *pmk_len); @@ -779,7 +779,7 @@ int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa, * KDK = L(FILS-Key-Data, ICK_bits + KEK_bits + TK_bits + FILS-FT_bits, * KDK_bits) */ - data_len = 2 * ETH_ALEN + 2 * FILS_NONCE_LEN + dhss_len; + data_len = 2 * ETH_ALEN + 2 * NONCE_LEN + dhss_len; data = os_malloc(data_len); if (!data) goto err; @@ -788,10 +788,10 @@ int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa, pos += ETH_ALEN; os_memcpy(pos, aa, ETH_ALEN); pos += ETH_ALEN; - os_memcpy(pos, snonce, FILS_NONCE_LEN); - pos += FILS_NONCE_LEN; - os_memcpy(pos, anonce, FILS_NONCE_LEN); - pos += FILS_NONCE_LEN; + os_memcpy(pos, snonce, NONCE_LEN); + pos += NONCE_LEN; + os_memcpy(pos, anonce, NONCE_LEN); + pos += NONCE_LEN; if (dhss) os_memcpy(pos, dhss, dhss_len); @@ -845,8 +845,8 @@ int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa, wpa_printf(MSG_DEBUG, "FILS: PTK derivation - SPA=" MACSTR " AA=" MACSTR, MAC2STR(spa), MAC2STR(aa)); - 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: SNonce", snonce, NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, NONCE_LEN); if (dhss) wpa_hexdump_key(MSG_DEBUG, "FILS: DHss", dhss, dhss_len); wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, pmk_len); @@ -902,8 +902,8 @@ int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce, wpa_printf(MSG_DEBUG, "FILS: Key-Auth derivation: STA-MAC=" MACSTR " AP-BSSID=" MACSTR, MAC2STR(sta_addr), MAC2STR(bssid)); wpa_hexdump_key(MSG_DEBUG, "FILS: ICK", ick, ick_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: SNonce", snonce, NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FILS: gSTA", g_sta, g_sta_len); wpa_hexdump(MSG_DEBUG, "FILS: gAP", g_ap, g_ap_len); @@ -913,9 +913,9 @@ int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce, * [ || gSTA || gAP ]) */ addr[0] = snonce; - len[0] = FILS_NONCE_LEN; + len[0] = NONCE_LEN; addr[1] = anonce; - len[1] = FILS_NONCE_LEN; + len[1] = NONCE_LEN; addr[2] = sta_addr; len[2] = ETH_ALEN; addr[3] = bssid; diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c index 94db413eb..eec0adc70 100644 --- a/src/drivers/driver_atheros.c +++ b/src/drivers/driver_atheros.c @@ -1083,17 +1083,17 @@ atheros_sta_auth(void *priv, struct wpa_driver_sta_auth_params *params) wpa_printf(MSG_DEBUG, "%s: im_op IEEE80211_MLME_AUTH_FILS", __func__); os_memcpy(mlme.fils_aad.ANonce, params->fils_anonce, - IEEE80211_FILS_NONCE_LEN); + IEEE80211_NONCE_LEN); os_memcpy(mlme.fils_aad.SNonce, params->fils_snonce, - IEEE80211_FILS_NONCE_LEN); + IEEE80211_NONCE_LEN); os_memcpy(mlme.fils_aad.kek, params->fils_kek, IEEE80211_MAX_WPA_KEK_LEN); mlme.fils_aad.kek_len = params->fils_kek_len; mlme.im_op = IEEE80211_MLME_AUTH_FILS; wpa_hexdump(MSG_DEBUG, "FILS: ANonce", - mlme.fils_aad.ANonce, FILS_NONCE_LEN); + mlme.fils_aad.ANonce, NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FILS: SNonce", - mlme.fils_aad.SNonce, FILS_NONCE_LEN); + mlme.fils_aad.SNonce, NONCE_LEN); wpa_hexdump_key(MSG_DEBUG, "FILS: KEK", mlme.fils_aad.kek, mlme.fils_aad.kek_len); } else { diff --git a/src/pasn/pasn_common.h b/src/pasn/pasn_common.h index 578e7b122..ded679a4d 100644 --- a/src/pasn/pasn_common.h +++ b/src/pasn/pasn_common.h @@ -29,8 +29,8 @@ enum pasn_fils_state { struct pasn_fils { u8 state; - u8 nonce[FILS_NONCE_LEN]; - u8 anonce[FILS_NONCE_LEN]; + u8 nonce[NONCE_LEN]; + u8 anonce[NONCE_LEN]; u8 session[FILS_SESSION_LEN]; u8 erp_pmkid[PMKID_LEN]; bool completed; diff --git a/src/pasn/pasn_initiator.c b/src/pasn/pasn_initiator.c index 445ac17ae..74dde993b 100644 --- a/src/pasn/pasn_initiator.c +++ b/src/pasn/pasn_initiator.c @@ -273,12 +273,12 @@ static struct wpabuf * wpas_pasn_fils_build_auth(struct pasn_data *pasn) return NULL; } - if (random_get_bytes(pasn->fils.nonce, FILS_NONCE_LEN) < 0 || + if (random_get_bytes(pasn->fils.nonce, NONCE_LEN) < 0 || random_get_bytes(pasn->fils.session, FILS_SESSION_LEN) < 0) goto fail; wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", pasn->fils.nonce, - FILS_NONCE_LEN); + NONCE_LEN); wpa_hexdump(MSG_DEBUG, "PASN: FILS: Session", pasn->fils.session, FILS_SESSION_LEN); @@ -301,9 +301,9 @@ static struct wpabuf * wpas_pasn_fils_build_auth(struct pasn_data *pasn) /* FILS Nonce */ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); - wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN); - wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE); - wpabuf_put_data(buf, pasn->fils.nonce, FILS_NONCE_LEN); + wpabuf_put_u8(buf, 1 + NONCE_LEN); + wpabuf_put_u8(buf, WLAN_EID_EXT_NONCE); + wpabuf_put_data(buf, pasn->fils.nonce, NONCE_LEN); /* FILS Session */ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); @@ -366,7 +366,7 @@ static int wpas_pasn_wd_fils_rx(struct pasn_data *pasn, struct wpabuf *wd) struct wpa_ie_data rsne_data; u8 rmsk[ERP_MAX_KEY_LEN]; size_t rmsk_len; - u8 anonce[FILS_NONCE_LEN]; + u8 anonce[NONCE_LEN]; const u8 *data; size_t buf_len; struct wpabuf *fils_wd = NULL; @@ -410,7 +410,7 @@ static int wpas_pasn_wd_fils_rx(struct pasn_data *pasn, struct wpabuf *wd) return -1; } - if (!elems.rsn_ie || !elems.fils_nonce || !elems.fils_nonce || + if (!elems.rsn_ie || !elems.nonce || !elems.nonce || !elems.wrapped_data) { wpa_printf(MSG_DEBUG, "PASN: FILS: Missing IEs"); return -1; @@ -435,9 +435,9 @@ static int wpas_pasn_wd_fils_rx(struct pasn_data *pasn, struct wpabuf *wd) return -1; } - wpa_hexdump(MSG_DEBUG, "PASN: FILS: ANonce", elems.fils_nonce, - FILS_NONCE_LEN); - os_memcpy(anonce, elems.fils_nonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "PASN: FILS: ANonce", elems.nonce, + NONCE_LEN); + os_memcpy(anonce, elems.nonce, NONCE_LEN); wpa_hexdump(MSG_DEBUG, "PASN: FILS: FILS Session", elems.fils_session, FILS_SESSION_LEN); diff --git a/src/pasn/pasn_responder.c b/src/pasn/pasn_responder.c index 8a09a7a88..568442e28 100644 --- a/src/pasn/pasn_responder.c +++ b/src/pasn/pasn_responder.c @@ -305,9 +305,9 @@ static struct wpabuf * pasn_get_fils_wd(struct pasn_data *pasn) /* FILS Nonce */ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); - wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN); - wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE); - wpabuf_put_data(buf, fils->anonce, FILS_NONCE_LEN); + wpabuf_put_u8(buf, 1 + NONCE_LEN); + wpabuf_put_u8(buf, WLAN_EID_EXT_NONCE); + wpabuf_put_data(buf, fils->anonce, NONCE_LEN); /* FILS Session */ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 70812f4b1..01ca1679d 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -6059,12 +6059,12 @@ struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md) goto fail; } - if (random_get_bytes(sm->fils_nonce, FILS_NONCE_LEN) < 0 || + if (random_get_bytes(sm->fils_nonce, NONCE_LEN) < 0 || random_get_bytes(sm->fils_session, FILS_SESSION_LEN) < 0) goto fail; wpa_hexdump(MSG_DEBUG, "FILS: Generated FILS Nonce", - sm->fils_nonce, FILS_NONCE_LEN); + sm->fils_nonce, NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FILS: Generated FILS Session", sm->fils_session, FILS_SESSION_LEN); @@ -6129,10 +6129,10 @@ struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md) /* FILS Nonce */ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */ - wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN); /* Length */ + wpabuf_put_u8(buf, 1 + NONCE_LEN); /* Length */ /* Element ID Extension */ - wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE); - wpabuf_put_data(buf, sm->fils_nonce, FILS_NONCE_LEN); + wpabuf_put_u8(buf, WLAN_EID_EXT_NONCE); + wpabuf_put_data(buf, sm->fils_nonce, NONCE_LEN); /* FILS Session */ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */ @@ -6257,12 +6257,12 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data, goto fail; } - if (!elems.fils_nonce) { + if (!elems.nonce) { wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field"); goto fail; } - os_memcpy(sm->fils_anonce, elems.fils_nonce, FILS_NONCE_LEN); - wpa_hexdump(MSG_DEBUG, "FILS: ANonce", sm->fils_anonce, FILS_NONCE_LEN); + os_memcpy(sm->fils_anonce, elems.nonce, NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: ANonce", sm->fils_anonce, NONCE_LEN); #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) { @@ -6722,10 +6722,10 @@ struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek, wpa_hexdump_key(MSG_DEBUG, "FILS: KEK for AEAD", *kek, *kek_len); *snonce = sm->fils_nonce; wpa_hexdump(MSG_DEBUG, "FILS: SNonce for AEAD AAD", - *snonce, FILS_NONCE_LEN); + *snonce, NONCE_LEN); *anonce = sm->fils_anonce; wpa_hexdump(MSG_DEBUG, "FILS: ANonce for AEAD AAD", - *anonce, FILS_NONCE_LEN); + *anonce, NONCE_LEN); return buf; } diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 9f9a032f1..d0c3541b5 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -205,9 +205,9 @@ struct wpa_sm { #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_FILS - u8 fils_nonce[FILS_NONCE_LEN]; + u8 fils_nonce[NONCE_LEN]; u8 fils_session[FILS_SESSION_LEN]; - u8 fils_anonce[FILS_NONCE_LEN]; + u8 fils_anonce[NONCE_LEN]; u8 fils_key_auth_ap[FILS_MAX_KEY_AUTH_LEN]; u8 fils_key_auth_sta[FILS_MAX_KEY_AUTH_LEN]; size_t fils_key_auth_len; diff --git a/wlantest/rx_mgmt.c b/wlantest/rx_mgmt.c index be4d53c6d..b27946b27 100644 --- a/wlantest/rx_mgmt.c +++ b/wlantest/rx_mgmt.c @@ -987,16 +987,16 @@ static void process_fils_auth(struct wlantest *wt, struct wlantest_bss *bss, sta->pairwise_cipher = data.pairwise_cipher; } - if (!elems.fils_nonce) { + if (!elems.nonce) { add_note(wt, MSG_INFO, "FILS Authentication frame missing nonce"); return; } if (ether_addr_equal(mgmt->sa, mgmt->bssid)) - os_memcpy(sta->anonce, elems.fils_nonce, FILS_NONCE_LEN); + os_memcpy(sta->anonce, elems.nonce, NONCE_LEN); else - os_memcpy(sta->snonce, elems.fils_nonce, FILS_NONCE_LEN); + os_memcpy(sta->snonce, elems.nonce, NONCE_LEN); } @@ -1365,10 +1365,10 @@ static int try_rmsk(struct wlantest *wt, struct wlantest_bss *bss, aad_len[1] = ETH_ALEN; /* The STA's nonce */ aad[2] = sta->snonce; - aad_len[2] = FILS_NONCE_LEN; + aad_len[2] = NONCE_LEN; /* The AP's nonce */ aad[3] = sta->anonce; - aad_len[3] = FILS_NONCE_LEN; + aad_len[3] = NONCE_LEN; /* * The (Re)Association Request frame from the Capability Information * field to the FILS Session element (both inclusive). @@ -1586,10 +1586,10 @@ static void decrypt_fils_assoc_resp(struct wlantest *wt, aad_len[1] = ETH_ALEN; /* The AP's nonce */ aad[2] = sta->anonce; - aad_len[2] = FILS_NONCE_LEN; + aad_len[2] = NONCE_LEN; /* The STA's nonce */ aad[3] = sta->snonce; - aad_len[3] = FILS_NONCE_LEN; + aad_len[3] = NONCE_LEN; /* * The (Re)Association Response frame from the Capability Information * field to the FILS Session element (both inclusive). diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 860b75f92..fba6508bc 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -2291,7 +2291,7 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, struct ieee802_11_elems elems; struct wpa_ssid *ssid = wpa_s->current_ssid; #ifdef CONFIG_FILS - u8 nonces[2 * FILS_NONCE_LEN]; + u8 nonces[2 * NONCE_LEN]; #endif /* CONFIG_FILS */ #ifdef CONFIG_HT_OVERRIDES struct ieee80211_ht_capabilities htcaps; @@ -2383,8 +2383,8 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, wpa_s->sme.assoc_req_ie, wpa_s->sme.assoc_req_ie_len); - os_memcpy(nonces, snonce, FILS_NONCE_LEN); - os_memcpy(nonces + FILS_NONCE_LEN, anonce, FILS_NONCE_LEN); + os_memcpy(nonces, snonce, NONCE_LEN); + os_memcpy(nonces + NONCE_LEN, anonce, NONCE_LEN); params.fils_nonces = nonces; params.fils_nonces_len = sizeof(nonces); } -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:13 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:13 +0530 Subject: [PATCH v2 03/28] 11bi: Add wiphy capability flag for (Re)Association frame encryption support In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-4-ainy.kumari@oss.qualcomm.com> From: Sai Pratyusha Magam Introduce a new wiphy capability flag to allow AP/STA to fetch driver support for (Re)Association request/response frame encryption via the netlink interface NL80211_CMD_GET_WIPHY. Signed-off-by: Sai Pratyusha Magam Signed-off-by: Rohan Dutta Signed-off-by: Ainy Kumari --- src/drivers/driver.h | 2 ++ src/drivers/driver_nl80211_capa.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 69f1158f4..172a89aa3 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -2433,6 +2433,8 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS2_NON_TRIGGER_BASED_RESPONDER 0x0000000020000000ULL /** Driver supports non-trigger based ranging initiator functionality */ #define WPA_DRIVER_FLAGS2_NON_TRIGGER_BASED_INITIATOR 0x0000000040000000ULL +/** Driver supports (Re)Association Request/Response frame encryption */ +#define WPA_DRIVER_FLAGS2_ASSOCIATION_FRAME_ENCRYPTION 0x0000000080000000ULL u64 flags2; diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c index aed2774dc..7b5b87496 100644 --- a/src/drivers/driver_nl80211_capa.c +++ b/src/drivers/driver_nl80211_capa.c @@ -722,6 +722,10 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info, if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT)) capa->flags2 |= WPA_DRIVER_FLAGS2_SPP_AMSDU; + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_ASSOC_FRAME_ENCRYPTION)) + capa->flags2 |= WPA_DRIVER_FLAGS2_ASSOCIATION_FRAME_ENCRYPTION; } -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:14 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:14 +0530 Subject: [PATCH v2 04/28] PASN: Extend RSNXE capability field to 32 bits In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-5-ainy.kumari@oss.qualcomm.com> Extend RSNXE capability representation from u16 to u32 to support use cases requiring more than 16 bits. Update wpa_pasn_add_rsnxe(), pasn_data structure and related APIs to use u32 for rsnxe_capab. Signed-off-by: Ainy Kumari --- src/common/wpa_common.c | 31 ++++++++++++++++++++++++------- src/common/wpa_common.h | 2 +- src/pasn/pasn_common.c | 2 +- src/pasn/pasn_common.h | 4 ++-- wpa_supplicant/pasn_supplicant.c | 2 +- 5 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index d1a6e86a4..4a32ede9a 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -4381,23 +4381,40 @@ int wpa_pasn_parse_parameter_ie(const u8 *data, u8 len, bool from_ap, } -void wpa_pasn_add_rsnxe(struct wpabuf *buf, u16 capab) +void wpa_pasn_add_rsnxe(struct wpabuf *buf, u32 capab) { size_t flen; - flen = (capab & 0xff00) ? 2 : 1; if (!capab) return; /* no supported extended RSN capabilities */ + + /* Determine how many bytes are needed to represent capab */ + if (capab & 0xFF000000) + flen = 4; + else if (capab & 0x00FF0000) + flen = 3; + else if (capab & 0x0000FF00) + flen = 2; + else + flen = 1; + if (wpabuf_tailroom(buf) < 2 + flen) return; - capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */ + + /* Set field length bits (bit 0-3 = Field length (n - 1)) */ + capab |= (flen - 1); wpabuf_put_u8(buf, WLAN_EID_RSNX); wpabuf_put_u8(buf, flen); - wpabuf_put_u8(buf, capab & 0x00ff); - capab >>= 8; - if (capab) - wpabuf_put_u8(buf, capab); + + /* Write the capability field byte-by-byte (little-endian) */ + wpabuf_put_u8(buf, capab & 0x000000FF); + if (flen > 1) + wpabuf_put_u8(buf, (capab >> 8) & 0x000000FF); + if (flen > 2) + wpabuf_put_u8(buf, (capab >> 16) & 0x000000FF); + if (flen > 3) + wpabuf_put_u8(buf, (capab >> 24) & 0x000000FF); } diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index 255f5b0c7..a3b4d4df9 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -812,7 +812,7 @@ int wpa_pasn_validate_rsne(const struct wpa_ie_data *data); int wpa_pasn_parse_parameter_ie(const u8 *data, u8 len, bool from_ap, struct wpa_pasn_params_data *pasn_params); -void wpa_pasn_add_rsnxe(struct wpabuf *buf, u16 capab); +void wpa_pasn_add_rsnxe(struct wpabuf *buf, u32 capab); int wpa_pasn_add_extra_ies(struct wpabuf *buf, const u8 *extra_ies, size_t len); void rsn_set_snonce_cookie(u8 *snonce); diff --git a/src/pasn/pasn_common.c b/src/pasn/pasn_common.c index ef8649b08..11916bd18 100644 --- a/src/pasn/pasn_common.c +++ b/src/pasn/pasn_common.c @@ -153,7 +153,7 @@ void pasn_set_rsn_pairwise(struct pasn_data *pasn, int rsn_pairwise) } -void pasn_set_rsnxe_caps(struct pasn_data *pasn, u16 rsnxe_capab) +void pasn_set_rsnxe_caps(struct pasn_data *pasn, u32 rsnxe_capab) { if (!pasn) return; diff --git a/src/pasn/pasn_common.h b/src/pasn/pasn_common.h index ded679a4d..58e11e44b 100644 --- a/src/pasn/pasn_common.h +++ b/src/pasn/pasn_common.h @@ -58,7 +58,7 @@ struct pasn_data { const char *password; int wpa_key_mgmt; int rsn_pairwise; - u16 rsnxe_capab; + u32 rsnxe_capab; u8 *rsnxe_ie; bool custom_pmkid_valid; u8 custom_pmkid[PMKID_LEN]; @@ -236,7 +236,7 @@ void pasn_set_noauth(struct pasn_data *pasn, bool noauth); void pasn_set_password(struct pasn_data *pasn, const char *password); void pasn_set_wpa_key_mgmt(struct pasn_data *pasn, int key_mgmt); void pasn_set_rsn_pairwise(struct pasn_data *pasn, int rsn_pairwise); -void pasn_set_rsnxe_caps(struct pasn_data *pasn, u16 rsnxe_capab); +void pasn_set_rsnxe_caps(struct pasn_data *pasn, u32 rsnxe_capab); void pasn_set_rsnxe_ie(struct pasn_data *pasn, const u8 *rsnxe_ie); void pasn_set_custom_pmkid(struct pasn_data *pasn, const u8 *pmkid); int pasn_set_extra_ies(struct pasn_data *pasn, const u8 *extra_ies, diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c index 9d93aedbf..392ad1b08 100644 --- a/wpa_supplicant/pasn_supplicant.c +++ b/wpa_supplicant/pasn_supplicant.c @@ -624,7 +624,7 @@ static void wpas_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit) const u8 *indic; u16 fils_info; #endif /* CONFIG_FILS */ - u16 capab = 0; + u32 capab = 0; bool derive_kdk; int ret; -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:16 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:16 +0530 Subject: [PATCH v2 06/28] 11bi: Enhanced Privacy Protection (EPP) related definitions In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-7-ainy.kumari@oss.qualcomm.com> From: Sai Pratyusha Magam As a preparation to support EPPKE authentication, add additional definitions required to support the flow. The definitions pertain to the following features introduced in draft IEEE P802.11bi/D2.0: -(Re)Association Request/Response frame Encryption -IEEE802.1x (EAP) Utilizing Authentication farmes -PMKSA Caching privacy Signed-off-by: Ainy Kumari Signed-off-by: Rohan Dutta Signed-off-by: Sai Pratyusha Magam --- src/common/defs.h | 7 +++++++ src/common/ieee802_11_defs.h | 5 +++++ src/common/wpa_common.c | 6 ++++++ src/common/wpa_common.h | 1 + src/drivers/driver_nl80211.c | 2 ++ 5 files changed, 21 insertions(+) diff --git a/src/common/defs.h b/src/common/defs.h index 0a79ba2a6..eb410386a 100644 --- a/src/common/defs.h +++ b/src/common/defs.h @@ -52,6 +52,7 @@ #define WPA_KEY_MGMT_SAE_EXT_KEY BIT(26) #define WPA_KEY_MGMT_FT_SAE_EXT_KEY BIT(27) #define WPA_KEY_MGMT_IEEE8021X_SHA384 BIT(28) +#define WPA_KEY_MGMT_EPPKE BIT(29) #define WPA_KEY_MGMT_FT (WPA_KEY_MGMT_FT_PSK | \ @@ -210,12 +211,18 @@ static inline int wpa_key_mgmt_cross_akm(int akm) #define WPA_AUTH_ALG_SAE BIT(4) #define WPA_AUTH_ALG_FILS BIT(5) #define WPA_AUTH_ALG_FILS_SK_PFS BIT(6) +#define WPA_AUTH_ALG_EPPKE BIT(10) static inline int wpa_auth_alg_fils(int alg) { return !!(alg & (WPA_AUTH_ALG_FILS | WPA_AUTH_ALG_FILS_SK_PFS)); } +static inline int wpa_auth_alg_eppke(int alg) +{ + return !!(alg & WPA_AUTH_ALG_EPPKE); +} + enum wpa_alg { WPA_ALG_NONE, WPA_ALG_WEP, diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 1b964439b..35e7852ec 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -98,6 +98,8 @@ #define WLAN_AUTH_FILS_SK_PFS 5 #define WLAN_AUTH_FILS_PK 6 #define WLAN_AUTH_PASN 7 +#define WLAN_AUTH_1X_UTILIZING_AUTHENTICATION_FRAMES 8 +#define WLAN_AUTH_EPPKE 9 #define WLAN_AUTH_LEAP 128 /* Authentication transaction sequence number */ @@ -633,6 +635,9 @@ #define WLAN_RSNX_CAPAB_URNM_MFPR 15 #define WLAN_RSNX_CAPAB_KEK_IN_PASN 18 #define WLAN_RSNX_CAPAB_SSID_PROTECTION 21 +#define WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION 27 +#define WLAN_RSNX_CAPAB_1X_UTILIZING_AUTHENTICATION_FRAMES 28 +#define WLAN_RSNX_CAPAB_PMKSA_CACHING_PRIVACY 29 #define WLAN_RSNX_CAPAB_SAE_PW_ID_CHANGE 34 /* Multiple BSSID element subelements */ diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index 4a32ede9a..9bf675984 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -217,6 +217,8 @@ int rsn_key_mgmt_to_wpa_akm(u32 akm_suite) case RSN_AUTH_KEY_MGMT_PASN: return WPA_KEY_MGMT_PASN; #endif /* CONFIG_PASN */ + case RSN_AUTH_KEY_MGMT_EPPKE: + return WPA_KEY_MGMT_EPPKE; default: return 0; } @@ -2910,6 +2912,8 @@ const char * wpa_key_mgmt_txt(int key_mgmt, int proto) return "PASN"; case WPA_KEY_MGMT_IEEE8021X_SHA384: return "WPA2-EAP-SHA384"; + case WPA_KEY_MGMT_EPPKE: + return "EPPKE"; default: return "UNKNOWN"; } @@ -2964,6 +2968,8 @@ u32 wpa_akm_to_suite(int akm) if (akm & WPA_KEY_MGMT_PASN) return RSN_AUTH_KEY_MGMT_PASN; #endif /* CONFIG_PASN */ + if (akm & WPA_KEY_MGMT_EPPKE) + return RSN_AUTH_KEY_MGMT_EPPKE; return 0; } diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index a3b4d4df9..8a3f23872 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -98,6 +98,7 @@ WPA_CIPHER_BIP_CMAC_256) #define RSN_AUTH_KEY_MGMT_802_1X_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 23) #define RSN_AUTH_KEY_MGMT_SAE_EXT_KEY RSN_SELECTOR(0x00, 0x0f, 0xac, 24) #define RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY RSN_SELECTOR(0x00, 0x0f, 0xac, 25) +#define RSN_AUTH_KEY_MGMT_EPPKE RSN_SELECTOR(0x00, 0x0f, 0xac, 29) #define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00) #define RSN_AUTH_KEY_MGMT_DPP RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x02) diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 24516637e..30433548e 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -4278,6 +4278,8 @@ static enum nl80211_auth_type get_nl_auth_type(int wpa_auth_alg) return NL80211_AUTHTYPE_FILS_SK; if (wpa_auth_alg & WPA_AUTH_ALG_FILS_SK_PFS) return NL80211_AUTHTYPE_FILS_SK_PFS; + if (wpa_auth_alg & WPA_AUTH_ALG_EPPKE) + return NL80211_AUTHTYPE_EPPKE; return NL80211_AUTHTYPE_MAX + 1; } -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:15 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:15 +0530 Subject: [PATCH v2 05/28] EPPKE: Add wiphy capability flag for EPPKE authentication In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-6-ainy.kumari@oss.qualcomm.com> Introduce a wiphy capability flag to allow the supplicant to query EPPKE authentication support in STA mode via NL80211_CMD_GET_WIPHY netlink interface. Signed-off-by: Ainy Kumari --- src/drivers/driver.h | 3 +++ src/drivers/driver_nl80211_capa.c | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 172a89aa3..2ed0282eb 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -2435,6 +2435,9 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS2_NON_TRIGGER_BASED_INITIATOR 0x0000000040000000ULL /** Driver supports (Re)Association Request/Response frame encryption */ #define WPA_DRIVER_FLAGS2_ASSOCIATION_FRAME_ENCRYPTION 0x0000000080000000ULL +/** Driver supports EPPKE authentication */ +#define WPA_DRIVER_FLAGS2_EPPKE 0x0000000100000000ULL + u64 flags2; diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c index 7b5b87496..e1afeb715 100644 --- a/src/drivers/driver_nl80211_capa.c +++ b/src/drivers/driver_nl80211_capa.c @@ -726,6 +726,11 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info, if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_ASSOC_FRAME_ENCRYPTION)) capa->flags2 |= WPA_DRIVER_FLAGS2_ASSOCIATION_FRAME_ENCRYPTION; + + if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_EPPKE)) { + capa->flags2 |= WPA_DRIVER_FLAGS2_EPPKE; + capa->flags2 |= WPA_DRIVER_FLAGS2_ASSOCIATION_FRAME_ENCRYPTION; + } } -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:20 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:20 +0530 Subject: [PATCH v2 10/28] EPPKE: Add EPPKE support to PASN PTK derivation per IEEE P802.11bi/D2.0 In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-11-ainy.kumari@oss.qualcomm.com> Extend PASN PTK derivation to support EPPKE authentication as specified in IEEE P802.11bi/D2.0, section 12.16.9.3.4. Update the PTK derivation label and debug logging to distinguish between PASN and EPPKE authentication. Update all relevant function calls to pass the new is_eppke parameter. Signed-off-by: Ainy Kumari --- src/ap/ieee802_11.c | 3 ++- src/common/common_module_tests.c | 2 +- src/common/wpa_common.c | 12 +++++++++--- src/common/wpa_common.h | 3 ++- src/pasn/pasn_common.h | 1 + src/pasn/pasn_initiator.c | 3 ++- src/pasn/pasn_responder.c | 3 ++- 7 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 611017da1..c1ff3fa8a 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -2815,7 +2815,8 @@ static void pasn_fils_auth_resp(struct hostapd_data *hapd, wpabuf_len(pasn->secret), pasn_get_ptk(sta->pasn), pasn_get_akmp(sta->pasn), pasn_get_cipher(sta->pasn), sta->pasn->kdk_len, - sta->pasn->kek_len, &sta->pasn->hash_alg); + sta->pasn->kek_len, &sta->pasn->hash_alg, + pasn->auth_alg == WLAN_AUTH_EPPKE); if (ret) { wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to derive PTK"); goto fail; diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c index 6b528eabd..bb6b9cf97 100644 --- a/src/common/common_module_tests.c +++ b/src/common/common_module_tests.c @@ -655,7 +655,7 @@ static int pasn_test_pasn_auth(void) spa_addr, bssid, dhss, sizeof(dhss), &ptk, WPA_KEY_MGMT_PASN, WPA_CIPHER_CCMP, - WPA_KDK_MAX_LEN, 0, &hash_alg); + WPA_KDK_MAX_LEN, 0, &hash_alg, false); if (ret) return ret; diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index 9bf675984..092365f24 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -1568,7 +1568,7 @@ static enum rsn_hash_alg pasn_select_hash_alg(int akmp, int cipher, /** - * pasn_pmk_to_ptk - Calculate PASN PTK from PMK, addresses, etc. + * pasn_pmk_to_ptk - Calculate PASN/EPPKE PTK from PMK, addresses, etc. * @pmk: Pairwise master key * @pmk_len: Length of PMK * @spa: Suppplicant address @@ -1582,13 +1582,15 @@ static enum rsn_hash_alg pasn_select_hash_alg(int akmp, int cipher, * @kdk_len: the length in octets that should be derived for HTLK. Can be zero. * @kek_len: The length in octets that should be derived for KEK. Can be zero. * @alg: Output variable for indicating the selected hash algorithm + * @is_eppke: EPPKE authentication * Returns: 0 on success, -1 on failure */ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *bssid, const u8 *dhss, size_t dhss_len, struct wpa_ptk *ptk, int akmp, int cipher, - size_t kdk_len, size_t kek_len, enum rsn_hash_alg *alg) + size_t kdk_len, size_t kek_len, enum rsn_hash_alg *alg, + bool is_eppke) { u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN + WPA_KDK_MAX_LEN]; @@ -1596,7 +1598,8 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len, u8 *data; size_t data_len, ptk_len; int ret = -1; - const char *label = "PASN PTK Derivation"; + const char *label = is_eppke ? "EPPKE PTK Derivation" : + "PASN PTK Derivation"; if (!pmk || !pmk_len) { wpa_printf(MSG_ERROR, "PASN: No PMK set for PTK derivation"); @@ -1609,6 +1612,9 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len, } /* + * Use "EPPKE PTK Derivation" instead of ?PASN PTK Derivation? for + * EPPKE Authentication per IEEE P802.11bi/D2.0, section 12.16.9.3.4. + * * PASN-PTK = KDF(PMK, ?PASN PTK Derivation?, SPA || BSSID || DHss) * * KCK = L(PASN-PTK, 0, 256) diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index 8a3f23872..c90b44fab 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -780,7 +780,8 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *bssid, const u8 *dhss, size_t dhss_len, struct wpa_ptk *ptk, int akmp, int cipher, - size_t kdk_len, size_t kek_len, enum rsn_hash_alg *alg); + size_t kdk_len, size_t kek_len, enum rsn_hash_alg *alg, + bool is_eppke); size_t pasn_mic_len(enum rsn_hash_alg alg); diff --git a/src/pasn/pasn_common.h b/src/pasn/pasn_common.h index 58e11e44b..e71393496 100644 --- a/src/pasn/pasn_common.h +++ b/src/pasn/pasn_common.h @@ -49,6 +49,7 @@ struct pasn_data { bool derive_kdk; size_t kdk_len; void *cb_ctx; + unsigned int auth_alg; #ifdef CONFIG_SAE struct sae_pt *pt; diff --git a/src/pasn/pasn_initiator.c b/src/pasn/pasn_initiator.c index 74dde993b..a5091b562 100644 --- a/src/pasn/pasn_initiator.c +++ b/src/pasn/pasn_initiator.c @@ -1316,7 +1316,8 @@ int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len, pasn->own_addr, pasn->peer_addr, wpabuf_head(secret), wpabuf_len(secret), &pasn->ptk, pasn->akmp, pasn->cipher, - pasn->kdk_len, pasn->kek_len, &pasn->hash_alg); + pasn->kdk_len, pasn->kek_len, &pasn->hash_alg, + pasn->auth_alg == WLAN_AUTH_EPPKE); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK"); goto fail; diff --git a/src/pasn/pasn_responder.c b/src/pasn/pasn_responder.c index 568442e28..21cf33cb3 100644 --- a/src/pasn/pasn_responder.c +++ b/src/pasn/pasn_responder.c @@ -414,7 +414,8 @@ pasn_derive_keys(struct pasn_data *pasn, wpabuf_head(secret), wpabuf_len(secret), &pasn->ptk, pasn->akmp, pasn->cipher, pasn->kdk_len, pasn->kek_len, - &pasn->hash_alg); + &pasn->hash_alg, + pasn->auth_alg == WLAN_AUTH_EPPKE); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK"); return -1; -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:19 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:19 +0530 Subject: [PATCH v2 09/28] wpa_supplicant: Add CONFIG_ENC_ASSOC for association frame encryption support In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-10-ainy.kumari@oss.qualcomm.com> Introduce CONFIG_ENC_ASSOC in defconfig and update Makefile to enable association frame encryption. When enabled, also set CONFIG_PASN, since EPPKE authentication requires association frame encryption and relies on the PASN base code. Signed-off-by: Ainy Kumari --- wpa_supplicant/Makefile | 5 +++++ wpa_supplicant/defconfig | 3 +++ 2 files changed, 8 insertions(+) diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index b92b1e3f2..3bff566fd 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -444,6 +444,11 @@ OBJS += ../src/pasn/pasn_common.o OBJS += pasn_supplicant.o endif +ifdef CONFIG_ENC_ASSOC +CONFIG_PASN=y +CFLAGS += -DCONFIG_ENC_ASSOC +endif + ifdef CONFIG_HS20 OBJS += hs20_supplicant.o CFLAGS += -DCONFIG_HS20 diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig index 84ac8ba12..5861edc9c 100644 --- a/wpa_supplicant/defconfig +++ b/wpa_supplicant/defconfig @@ -682,3 +682,6 @@ CONFIG_DPP2=y # Wi-Fi Aware unsynchronized service discovery (NAN USD) #CONFIG_NAN_USD=y + +# IEEE P802.11bi/D2.0 Support (Enhanced service with Privacy Protection) +#CONFIG_ENC_ASSOC =y -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:18 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:18 +0530 Subject: [PATCH v2 08/28] 11bi: RSNE/RSNXE capability Extensions in AP mode In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-9-ainy.kumari@oss.qualcomm.com> From: Sai Pratyusha Magam Advertise EPPKE AKM suite in RSN IE of Beacons/Probe Response frames Add support to include RSNXE capability indications for the following features in beacons/probe response frames and EPPKE Authentication frame 2: -(Re)Association Request/Response frame encryption -IEEE802.1X (EAP) Authentication Utilizing Authentication frames -PMKSA Caching Privacy Signed-off-by: Sai Pratyusha Magam --- hostapd/config_file.c | 4 ++++ src/ap/ieee802_11_shared.c | 16 ++++++++++++++++ src/ap/wpa_auth_ie.c | 23 +++++++++++++++++++++-- 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/hostapd/config_file.c b/hostapd/config_file.c index ff85ed765..5e9ed06e8 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -717,6 +717,10 @@ static int hostapd_config_parse_key_mgmt(int line, const char *value) else if (os_strcmp(start, "PASN") == 0) val |= WPA_KEY_MGMT_PASN; #endif /* CONFIG_PASN */ +#ifdef CONFIG_ENC_ASSOC + else if (os_strcmp(start, "EPPKE") == 0) + val |= WPA_KEY_MGMT_EPPKE; +#endif /* CONFIG_ENC_ASSOC */ else { wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'", line, start); diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c index 750891425..05fb35bf3 100644 --- a/src/ap/ieee802_11_shared.c +++ b/src/ap/ieee802_11_shared.c @@ -1153,6 +1153,22 @@ u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len) if ((hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SPP_AMSDU) && hapd->conf->spp_amsdu) capab |= BIT(WLAN_RSNX_CAPAB_SPP_A_MSDU); +#ifdef CONFIG_ENC_ASSOC + /* Per IEEE802.11bi/D1.2, 12.16.7 PMKSA caching privacy + * A STA that sets the PMKSA Caching Privacy Support + * field in the RSNXE to 1 shall set the (Re)Association + * Frame Encryption Support field in the RSNXE to 1 + */ + if ((hapd->iface->drv_flags2 & + WPA_DRIVER_FLAGS2_ASSOCIATION_FRAME_ENCRYPTION) && + (hapd->conf->assoc_frame_encryption || + hapd->conf->pmksa_caching_privacy)) + capab |= BIT(WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION); + if (hapd->conf->pmksa_caching_privacy) + capab |= BIT(WLAN_RSNX_CAPAB_PMKSA_CACHING_PRIVACY); + if (hapd->conf->eap_using_authentication_frames) + capab |= BIT(WLAN_RSNX_CAPAB_1X_UTILIZING_AUTHENTICATION_FRAMES); +#endif /* CONFIG_ENC_ASSOC */ if (!capab) return eid; /* no supported extended RSN capabilities */ diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c index 220ac809f..6ae1350a0 100644 --- a/src/ap/wpa_auth_ie.c +++ b/src/ap/wpa_auth_ie.c @@ -303,7 +303,13 @@ static u8 * rsne_write_data(u8 *buf, size_t len, u8 *pos, int group, num_suites++; } #endif /* CONFIG_PASN */ - +#ifdef CONFIG_ENC_ASSOC + if (key_mgmt & WPA_KEY_MGMT_EPPKE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_EPPKE); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_ENC_ASSOC */ #ifdef CONFIG_RSN_TESTING if (rsn_testing) { RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2)); @@ -503,7 +509,20 @@ static u32 rsnxe_capab(struct wpa_auth_config *conf, int key_mgmt) capab |= BIT(WLAN_RSNX_CAPAB_SSID_PROTECTION); if (conf->spp_amsdu) capab |= BIT(WLAN_RSNX_CAPAB_SPP_A_MSDU); - +#ifdef CONFIG_ENC_ASSOC + /* Per IEEE802.11bi/D2.0, 12.16.7 PMKSA caching privacy + * A STA that sets the PMKSA Caching Privacy Support + * field in the RSNXE to 1 shall set the (Re)Association + * Frame Encryption Support field in the RSNXE to 1 + */ + if (conf->assoc_frame_encryption || + conf->pmksa_caching_privacy) + capab |= BIT(WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION); + if (conf->pmksa_caching_privacy) + capab |= BIT(WLAN_RSNX_CAPAB_PMKSA_CACHING_PRIVACY); + if (conf->eap_using_authentication_frames) + capab |= BIT(WLAN_RSNX_CAPAB_1X_UTILIZING_AUTHENTICATION_FRAMES); +#endif /* CONFIG_ENC_ASSOC */ return capab; } -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:21 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:21 +0530 Subject: [PATCH v2 11/28] PASN: Modify PASN Authentication frame header APIs based on auth_algo In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-12-ainy.kumari@oss.qualcomm.com> From: Sai Pratyusha Magam Modify PASN Authentication frame header API - wpa_pasn_build_auth_header() to build EPPKE Authentication frame header if auth_algo is WLAN_AUTH_EPPKE else build a PASN Authentication frame header Signed-off-by: Ainy Kumari Signed-off-by: Rohan Dutta Signed-off-by: Sai Pratyusha Magam --- src/common/wpa_common.c | 10 ++++++---- src/common/wpa_common.h | 2 +- src/pasn/pasn_initiator.c | 6 ++++-- src/pasn/pasn_responder.c | 5 +++-- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index 092365f24..16c956aa8 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -3963,15 +3963,17 @@ int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie) * @dst: Destination address * @trans_seq: Authentication transaction sequence number * @status: Authentication status + * @is_eppke: EPPKE authentication */ void wpa_pasn_build_auth_header(struct wpabuf *buf, const u8 *bssid, const u8 *src, const u8 *dst, - u8 trans_seq, u16 status) + u8 trans_seq, u16 status, bool is_eppke) { struct ieee80211_mgmt *auth; + u16 auth_alg = is_eppke ? WLAN_AUTH_EPPKE : WLAN_AUTH_PASN; - wpa_printf(MSG_DEBUG, "PASN: Add authentication header. trans_seq=%u", - trans_seq); + wpa_printf(MSG_DEBUG, "%s: Add authentication header trans_seq=%u", + is_eppke ? "EPPKE" : "PASN", trans_seq); auth = wpabuf_put(buf, offsetof(struct ieee80211_mgmt, u.auth.variable)); @@ -3984,7 +3986,7 @@ void wpa_pasn_build_auth_header(struct wpabuf *buf, const u8 *bssid, os_memcpy(auth->bssid, bssid, ETH_ALEN); auth->seq_ctrl = 0; - auth->u.auth.auth_alg = host_to_le16(WLAN_AUTH_PASN); + auth->u.auth.auth_alg = host_to_le16(auth_alg); auth->u.auth.auth_transaction = host_to_le16(trans_seq); auth->u.auth.status_code = host_to_le16(status); } diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index c90b44fab..fcd7168e2 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -797,7 +797,7 @@ int pasn_auth_frame_hash(enum rsn_hash_alg alg, const u8 *data, size_t len, void wpa_pasn_build_auth_header(struct wpabuf *buf, const u8 *bssid, const u8 *src, const u8 *dst, - u8 trans_seq, u16 status); + u8 trans_seq, u16 status, bool is_eppke); int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, int akmp, int cipher); diff --git a/src/pasn/pasn_initiator.c b/src/pasn/pasn_initiator.c index a5091b562..68a4f09fa 100644 --- a/src/pasn/pasn_initiator.c +++ b/src/pasn/pasn_initiator.c @@ -611,7 +611,8 @@ static struct wpabuf * wpas_pasn_build_auth_1(struct pasn_data *pasn, wpa_pasn_build_auth_header(buf, pasn->bssid, pasn->own_addr, pasn->peer_addr, - pasn->trans_seq + 1, WLAN_STATUS_SUCCESS); + pasn->trans_seq + 1, WLAN_STATUS_SUCCESS, + pasn->auth_alg == WLAN_AUTH_EPPKE); pmkid = NULL; if (wpa_key_mgmt_ft(pasn->akmp)) { @@ -707,7 +708,8 @@ static struct wpabuf * wpas_pasn_build_auth_3(struct pasn_data *pasn) wpa_pasn_build_auth_header(buf, pasn->bssid, pasn->own_addr, pasn->peer_addr, WLAN_AUTH_TR_SEQ_PASN_AUTH3, - WLAN_STATUS_SUCCESS); + WLAN_STATUS_SUCCESS, + pasn->auth_alg == WLAN_AUTH_EPPKE); wrapped_data_buf = wpas_pasn_get_wrapped_data(pasn); diff --git a/src/pasn/pasn_responder.c b/src/pasn/pasn_responder.c index 21cf33cb3..95d7b2e3c 100644 --- a/src/pasn/pasn_responder.c +++ b/src/pasn/pasn_responder.c @@ -452,7 +452,8 @@ static void handle_auth_pasn_comeback(struct pasn_data *pasn, return; wpa_pasn_build_auth_header(buf, pasn->bssid, own_addr, peer_addr, 2, - WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY); + WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY, + pasn->auth_alg == WLAN_AUTH_EPPKE); /* * Do not include the group as a part of the token since it is not going @@ -510,7 +511,7 @@ int handle_auth_pasn_resp(struct pasn_data *pasn, const u8 *own_addr, goto fail; wpa_pasn_build_auth_header(buf, pasn->bssid, own_addr, peer_addr, 2, - status); + status, pasn->auth_alg == WLAN_AUTH_EPPKE); if (status != WLAN_STATUS_SUCCESS) goto done; -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:17 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:17 +0530 Subject: [PATCH v2 07/28] 11bi: Configuration options to control EPP feature support in AP mode In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-8-ainy.kumari@oss.qualcomm.com> From: Sai Pratyusha Magam Introduce additional configuration options to control the enablement of each of the below EPP features in AP: -(Re)Association Request/Response frame encryption support -PMKSA Caching Privacy Support -IEEE 802.1X Authentication Utilizing Authentication Frame Support Signed-off-by: Sai Pratyusha Magam Signed-off-by: Rohan Dutta --- hostapd/config_file.c | 8 ++++++++ hostapd/hostapd.conf | 23 +++++++++++++++++++++++ src/ap/ap_config.c | 5 +++++ src/ap/ap_config.h | 5 +++++ src/ap/wpa_auth.h | 6 +++++- src/ap/wpa_auth_glue.c | 25 +++++++++++++++++++++++++ 6 files changed, 71 insertions(+), 1 deletion(-) diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 8d5c1433a..ff85ed765 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2865,6 +2865,14 @@ static int hostapd_config_fill(struct hostapd_config *conf, return 1; } bss->extended_key_id = val; +#ifdef CONFIG_ENC_ASSOC + } else if (os_strcmp(buf, "assoc_frame_encryption") == 0) { + bss->assoc_frame_encryption = atoi(pos); + } else if (os_strcmp(buf, "pmksa_caching_privacy") == 0) { + bss->pmksa_caching_privacy = atoi(pos); + } else if (os_strcmp(buf, "eap_using_authentication_frames") == 0) { + bss->eap_using_authentication_frames = atoi(pos); +#endif /* CONFIG_ENC_ASSOC */ } else if (os_strcmp(buf, "wpa_group_rekey") == 0) { bss->wpa_group_rekey = atoi(pos); bss->wpa_group_rekey_set = 1; diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index c76801965..996fa4484 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -3529,3 +3529,26 @@ own_ip_addr=127.0.0.1 #bridge=br-lan #wpa_key_mgmt=SAE #bssid=00:03:7f:12:84:85 + +##### IEEE 802.11bi related configuration ##################################### + +#IEEE Std 802.11bi D2.0 introduces following EPP feature capabilities +#(Re)Association Request/Response frame Encryption: Indicates if AP supports +#encryption of (Re)Association Request and Response frames +# 0 = disabled (default) +# 1 = enabled +#association_frame_encryption_support=0 + +#PMKSA Caching privacy: Indicates if the AP would recompute the PMKID after the +#indicated PMKID in the RSNE identifies a cached PMKSA and a PTKSA was established +#using the identified PMKSA. The recomputed PMKID will be delivered to the non-AP +#STA in the key delivery element added in the encrypted (Re)Association Response frame +# 0 = disabled (default) +# 1 = enabled +#pmksa_caching_privacy=0 + +#IEEE802.1X (EAP) Authentication utilizing Authentication frames: Indicates if +#the EAP PDU will be encapsulated within the IEEE802.1X authentication frames +# 0 = disabled (default) +# 1 = enabled +#eap_using_authentication_frames=0 diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index a92940d75..23c44cef8 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -179,6 +179,11 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) #endif /* CONFIG_PASN */ bss->urnm_mfpr_x20 = -1; bss->urnm_mfpr = -1; +#ifdef CONFIG_ENC_ASSOC + bss->assoc_frame_encryption = 0; + bss->pmksa_caching_privacy = 0; + bss->eap_using_authentication_frames = 0; +#endif /* CONFIG_ENC_ASSOC */ } diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index b427a972e..065f43f55 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -990,6 +990,11 @@ struct hostapd_bss_config { int mbssid_index; bool spp_amsdu; +#ifdef CONFIG_ENC_ASSOC + unsigned int assoc_frame_encryption:1; + unsigned int pmksa_caching_privacy:1; + unsigned int eap_using_authentication_frames:1; +#endif /* CONFIG_ENC_ASSOC */ }; /** diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 12a8b6b1a..36954a19a 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -290,7 +290,11 @@ struct wpa_auth_config { unsigned int secure_ltf:1; unsigned int secure_rtt:1; unsigned int prot_range_neg:1; - +#ifdef CONFIG_ENC_ASSOC + unsigned int assoc_frame_encryption:1; + unsigned int pmksa_caching_privacy:1; + unsigned int eap_using_authentication_frames:1; +#endif /* CONFIG_ENC_ASSOC */ int owe_ptk_workaround; u8 transition_disable; #ifdef CONFIG_DPP2 diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index 8418bf8f0..8c59da3d5 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -81,6 +81,25 @@ static void hostapd_wpa_auth_config_update(struct hostapd_data *hapd, !!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP); +#ifdef CONFIG_ENC_ASSOC + if (_conf->assoc_frame_encryption && + (hapd->iface->drv_flags2 & + WPA_DRIVER_FLAGS2_ASSOCIATION_FRAME_ENCRYPTION)) + _conf->assoc_frame_encryption = 1; + else + _conf->assoc_frame_encryption = 0; + + if (_conf->pmksa_caching_privacy) + _conf->pmksa_caching_privacy = 1; + else + _conf->pmksa_caching_privacy = 0; + + if (_conf->eap_using_authentication_frames) + _conf->eap_using_authentication_frames = 1; + else + _conf->eap_using_authentication_frames = 0; +#endif /* CONFIG_ENC_ASSOC */ + #ifdef CONFIG_IEEE80211BE _conf->mld_addr = NULL; _conf->link_id = -1; @@ -112,6 +131,12 @@ static void hostapd_wpa_auth_conf(struct hostapd_iface *iface, os_memset(wconf, 0, sizeof(*wconf)); wconf->wpa = conf->wpa; +#ifdef CONFIG_ENC_ASSOC + wconf->assoc_frame_encryption = conf->assoc_frame_encryption; + wconf->pmksa_caching_privacy = conf->pmksa_caching_privacy; + wconf->eap_using_authentication_frames = + conf->eap_using_authentication_frames; +#endif /* CONFIG_ENC_ASSOC */ wconf->extended_key_id = conf->extended_key_id; wconf->wpa_key_mgmt = conf->wpa_key_mgmt; wconf->rsn_override_key_mgmt = conf->rsn_override_key_mgmt; -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:23 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:23 +0530 Subject: [PATCH v2 13/28] EPPKE: PTK/MIC Computation and key installation changes in Responder mode In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-14-ainy.kumari@oss.qualcomm.com> From: Sai Pratyusha Magam As per IEEE P802.11bi/D2.0, 12.16.9.3.4 (PTKSA derivation and MIC computation with EPPKE authentication) For MLO, the following modifications shall be used: -The AP MLD MAC address is used instead of the BSSID. -The non-AP MLD MAC address is used instead of the SPA. In AP Responder mode, if the peer is in unauthorized state, and processing of the EPPKE Authentication frame 1 received from initiator is successful and results in a PTKSA derivation, Install the pairwise key TK to the driver and if the peer is already in an authorized state, wait until EPPKE Authentication frame 3 validation is successful to install the pairwise key to the driver. Signed-off-by: Sai Pratyusha Magam Signed-off-by: Rohan Dutta --- src/ap/ieee802_11.c | 32 +++++++++++++++++++++++++++++--- src/common/wpa_common.c | 21 +++++++++++++++------ src/p2p/p2p.c | 3 ++- src/pasn/pasn_common.c | 8 +++++++- src/pasn/pasn_common.h | 14 +++++++++++++- src/pasn/pasn_responder.c | 27 +++++++++++++++++++++++++++ wpa_supplicant/pasn_supplicant.c | 4 ++-- 7 files changed, 95 insertions(+), 14 deletions(-) diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 2e0b9eb95..dbc2ceea8 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -2993,12 +2993,29 @@ static int hapd_pasn_send_mlme(void *ctx, const u8 *data, size_t data_len, } +#ifdef CONFIG_ENC_ASSOC +static int eppk_set_key(void *ctx, enum wpa_alg alg, + const u8 *addr, const u8 *key, + size_t key_len) +{ + struct hostapd_data *hapd = ctx; + + return hostapd_drv_set_key(hapd->conf->iface, hapd, alg, addr, + 0, 0, 1, NULL, 0, key, key_len, + KEY_FLAG_PAIRWISE_RX_TX); +} +#else +#define eppk_set_key NULL +#endif /* CONFIG_ENC_ASSOC */ + + static void hapd_initialize_pasn(struct hostapd_data *hapd, struct sta_info *sta) { struct pasn_data *pasn = sta->pasn; - pasn_register_callbacks(pasn, hapd, hapd_pasn_send_mlme, NULL); + pasn_register_callbacks(pasn, hapd, hapd_pasn_send_mlme, + NULL, eppk_set_key); pasn_set_bssid(pasn, hapd->own_addr); pasn_set_own_addr(pasn, hapd->own_addr); #if defined(CONFIG_IEEE80211BE) && defined(CONFIG_ENC_ASSOC) @@ -3026,6 +3043,7 @@ static void hapd_initialize_pasn(struct hostapd_data *hapd, pasn_set_rsnxe_ie(pasn, hostapd_wpa_ie(hapd, WLAN_EID_RSNX)); pasn->disable_pmksa_caching = hapd->conf->disable_pmksa_caching; #ifdef CONFIG_ENC_ASSOC + pasn->tk_configured = false; pasn_set_responder_pmksa(pasn, wpa_auth_get_pmksa_cache(hapd->wpa_auth, (sta->epp_sta ? @@ -3111,6 +3129,7 @@ static void hapd_pasn_update_params(struct hostapd_data *hapd, } #ifdef CONFIG_ENC_ASSOC pasn->auth_alg = mgmt->u.auth.auth_alg; + pasn->authorized = ap_sta_is_authorized(sta); #ifdef CONFIG_IEEE80211BE pasn->is_ml_peer = sta->mld_info.mld_sta; #endif @@ -3219,7 +3238,8 @@ static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta, return; } - sta->pasn = pasn_data_init(); + if (!sta->pasn) + sta->pasn = pasn_data_init(); if (!sta->pasn) { wpa_printf(MSG_DEBUG, "PASN: Failed to allocate PASN context"); @@ -3266,7 +3286,13 @@ static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta, pasn_get_ptk(sta->pasn), NULL, NULL, pasn_get_akmp(sta->pasn)); #ifdef CONFIG_ENC_ASSOC - if (!sta->epp_sta) + if (sta->epp_sta && !sta->pasn->tk_configured) + sta->pasn->eppk_set_key(sta->pasn->cb_ctx, + wpa_cipher_to_alg(sta->pasn->cipher), + sta->addr, + sta->pasn->ptk.tk, + sta->pasn->ptk.tk_len); + else if (!sta->epp_sta) #endif /* CONFIG_ENC_ASSOC */ pasn_set_keys_from_cache(hapd, hapd->own_addr, sta->addr, diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index 16c956aa8..5effba077 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -1571,8 +1571,12 @@ static enum rsn_hash_alg pasn_select_hash_alg(int akmp, int cipher, * pasn_pmk_to_ptk - Calculate PASN/EPPKE PTK from PMK, addresses, etc. * @pmk: Pairwise master key * @pmk_len: Length of PMK - * @spa: Suppplicant address - * @bssid: AP BSSID + * @spa: As per IEEE802.11bi/D2.0, 12.16.9.3.4, for EPPKE authentication, + * Non-AP MLD MAC address is used for MLO. For PASN authentication or + * EPPKE authentication for Non-MLO, Non-AP Link MAC address is used. + * @bssid: As per IEEE802.11bi/D2.0, 12.16.9.3.4, for EPPKE authentication, + * AP MLD MAC address is used for MLO. For PASN authentication or EPPKE + * authentication for Non-MLO, AP BSSID is used. * @dhss: Is the shared secret (DHss) derived from the PASN ephemeral key * exchange encoded as an octet string * @dhss_len: The length of dhss in octets @@ -1799,10 +1803,15 @@ int wpa_ltf_keyseed(struct wpa_ptk *ptk, int akmp, int cipher) * @alg: Selected hash algorithm from pasn_pmk_to_ptk() * @kck: The key confirmation key for the PASN PTKSA * @kck_len: KCK length in octets - * @addr1: For the 2nd PASN frame supplicant address; for the 3rd frame the - * BSSID - * @addr2: For the 2nd PASN frame the BSSID; for the 3rd frame the supplicant - * address + * @addr1: For the 2nd PASN/EPPKE frame supplicant address is used for Non-MLO; + * for MLO, 2nd EPPKE authentication to use Non-AP MLD MAC address. + * For the 3rd PASN/EPPKE frame BSSID is used for Non-MLO. for MLO, 3rd EPPKE + * authentication to use AP MLD MAC address as per IEEE802.11bi/D2.0, 12.16.9.3.4 + * @addr2: For the 2nd PASN/EPPKE frame BSSID is used for Non-MLO; + * for MLO, 2nd EPPKE authentication to use AP MLD MAC address. + * For the 3rd PASN/EPPKE frame supplicant address is used for Non-MLO. + * for MLO, 3rd EPPKE authentication frame to use Non-AP MLD MAC address as + * per IEEE802.11bi/D2.0, 12.16.9.3.4 * @data: For calculating the MIC for the 2nd PASN frame, this should hold the * Beacon frame RSNE + RSNXE. For calculating the MIC for the 3rd PASN * frame, this should hold the hash of the body of the PASN 1st frame. diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index 711c7343c..25a89d7a0 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -7243,7 +7243,8 @@ int p2p_pasn_auth_rx(struct p2p_data *p2p, const struct ieee80211_mgmt *mgmt, pasn_register_callbacks(pasn, p2p->cfg->cb_ctx, p2p->cfg->pasn_send_mgmt, - p2p->cfg->pasn_validate_pmkid); + p2p->cfg->pasn_validate_pmkid, + NULL); auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); if (dev->role == P2P_ROLE_PAIRING_INITIATOR && diff --git a/src/pasn/pasn_common.c b/src/pasn/pasn_common.c index bd197bebd..f9ec5ca9c 100644 --- a/src/pasn/pasn_common.c +++ b/src/pasn/pasn_common.c @@ -47,7 +47,10 @@ void pasn_register_callbacks(struct pasn_data *pasn, void *cb_ctx, unsigned int wait), int (*validate_custom_pmkid)(void *ctx, const u8 *addr, - const u8 *pmkid)) + const u8 *pmkid), + int (*eppke_set_key)(void *ctx, enum wpa_alg alg, + const u8 *addr, const u8 *key, + size_t key_len)) { if (!pasn) return; @@ -55,6 +58,9 @@ void pasn_register_callbacks(struct pasn_data *pasn, void *cb_ctx, pasn->cb_ctx = cb_ctx; pasn->send_mgmt = send_mgmt; pasn->validate_custom_pmkid = validate_custom_pmkid; +#ifdef CONFIG_ENC_ASSOC + pasn->eppk_set_key = eppke_set_key; +#endif /* CONFIG_ENC_ASSOC */ } diff --git a/src/pasn/pasn_common.h b/src/pasn/pasn_common.h index f5e22c1d9..45ac3b4ce 100644 --- a/src/pasn/pasn_common.h +++ b/src/pasn/pasn_common.h @@ -140,6 +140,10 @@ struct pasn_data { u16 comeback_idx; u16 *comeback_pending_idx; struct wpabuf *frame; +#ifdef CONFIG_ENC_ASSOC + bool authorized; + bool tk_configured; +#endif /* CONFIG_ENC_ASSOC */ /** * send_mgmt - Function handler to transmit a Management frame @@ -165,6 +169,10 @@ struct pasn_data { int (*prepare_data_element)(void *ctx, const u8 *peer_addr); int (*parse_data_element)(void *ctx, const u8 *data, size_t len); +#ifdef CONFIG_ENC_ASSOC + int (*eppk_set_key)(void *ctx, enum wpa_alg alg, const u8 *addr, + const u8 *key, size_t key_len); +#endif /* CONFIG_ENC_ASSOC */ }; /* Initiator */ @@ -207,7 +215,11 @@ void pasn_register_callbacks(struct pasn_data *pasn, void *cb_ctx, unsigned int wait), int (*validate_custom_pmkid)(void *ctx, const u8 *addr, - const u8 *pmkid)); + const u8 *pmkid), + int (*eppke_set_key)(void *ctx, enum wpa_alg alg, + const u8 *addr, const u8 *key, + size_t key_len)); + void pasn_enable_kdk_derivation(struct pasn_data *pasn); void pasn_disable_kdk_derivation(struct pasn_data *pasn); diff --git a/src/pasn/pasn_responder.c b/src/pasn/pasn_responder.c index 8ad6ff03c..1aa72de11 100644 --- a/src/pasn/pasn_responder.c +++ b/src/pasn/pasn_responder.c @@ -413,6 +413,13 @@ pasn_derive_keys(struct pasn_data *pasn, pasn->pmk_len = pmk_len; os_memcpy(pasn->pmk, pmk, pmk_len); + +#ifdef CONFIG_ENC_ASSOC + if (pasn->auth_alg == WLAN_AUTH_EPPKE && + pasn->is_ml_peer) + own_addr = pasn->mld_addr; +#endif /* CONFIG_ENC_ASSOC */ + ret = pasn_pmk_to_ptk(pmk, pmk_len, peer_addr, own_addr, wpabuf_head(secret), wpabuf_len(secret), &pasn->ptk, pasn->akmp, @@ -641,6 +648,12 @@ int handle_auth_pasn_resp(struct pasn_data *pasn, const u8 *own_addr, data = rsn_ie; } +#ifdef CONFIG_ENC_ASSOC + if (pasn->auth_alg == WLAN_AUTH_EPPKE && + pasn->is_ml_peer) + own_addr = pasn->mld_addr; +#endif /* CONFIG_ENC_ASSOC */ + ret = pasn_mic(pasn->hash_alg, pasn->ptk.kck, pasn->ptk.kck_len, own_addr, peer_addr, data, data_len, frame, frame_len, mic); @@ -1013,6 +1026,14 @@ int handle_auth_pasn_1(struct pasn_data *pasn, goto send_resp; } +#ifdef CONFIG_ENC_ASSOC + if (pasn->auth_alg == WLAN_AUTH_EPPKE && !pasn->authorized) { + pasn->eppk_set_key(pasn->cb_ctx, wpa_cipher_to_alg(pasn->cipher), + peer_addr, pasn->ptk.tk, pasn->ptk.tk_len); + pasn->tk_configured = true; + } +#endif /* CONFIG_ENC_ASSOC */ + wpabuf_free(pasn->auth1); pasn->auth1 = wpabuf_alloc_copy(((const u8 *) mgmt) + IEEE80211_HDRLEN, len - IEEE80211_HDRLEN); @@ -1094,6 +1115,12 @@ int handle_auth_pasn_3(struct pasn_data *pasn, const u8 *own_addr, goto fail; } +#ifdef CONFIG_ENC_ASSOC + if (pasn->auth_alg == WLAN_AUTH_EPPKE && + pasn->is_ml_peer) + own_addr = pasn->mld_addr; +#endif /* CONFIG_ENC_ASSOC */ + /* Verify the MIC */ copy_len = len - offsetof(struct ieee80211_mgmt, u.auth); mic_offset = elems.mic - (const u8 *) &mgmt->u.auth; diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c index 392ad1b08..7f91e192a 100644 --- a/wpa_supplicant/pasn_supplicant.c +++ b/wpa_supplicant/pasn_supplicant.c @@ -716,7 +716,7 @@ static void wpas_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit) capab |= BIT(WLAN_RSNX_CAPAB_SPP_A_MSDU); pasn_set_rsnxe_caps(pasn, capab); - pasn_register_callbacks(pasn, wpa_s, wpas_pasn_send_mlme, NULL); + pasn_register_callbacks(pasn, wpa_s, wpas_pasn_send_mlme, NULL, NULL); ssid = wpa_config_get_network(wpa_s->conf, awork->network_id); #ifdef CONFIG_SAE @@ -974,7 +974,7 @@ int wpas_pasn_auth_rx(struct wpa_supplicant *wpa_s, wpabuf_free(pasn->frame); pasn->frame = NULL; - pasn_register_callbacks(pasn, wpa_s, wpas_pasn_send_mlme, NULL); + pasn_register_callbacks(pasn, wpa_s, wpas_pasn_send_mlme, NULL, NULL); ret = wpa_pasn_auth_rx(pasn, (const u8 *) mgmt, len, &pasn_data); if (ret == 0) { ptksa_cache_add(wpa_s->ptksa, pasn->own_addr, pasn->peer_addr, -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:24 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:24 +0530 Subject: [PATCH v2 14/28] EPPKE: EPP peer indication to driver In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-15-ainy.kumari@oss.qualcomm.com> From: Sai Pratyusha Magam Indicate that a peer is an Enhanced Privacy Protection (EPP) peer via the netlink attribute @NL80211_ATTR_EPP_PEER in @NL80211_CMD_NEW_STA and @NL80211_CMD_ADD_LINK_STA Signed-off-by: Sai Pratyusha Magam Signed-off-by: Rohan Dutta --- src/ap/ap_drv_ops.c | 6 ++++-- src/ap/ap_drv_ops.h | 2 +- src/ap/ieee802_11.c | 15 ++++++++++++--- src/ap/sta_info.c | 8 ++++++-- src/drivers/driver.h | 3 +++ src/drivers/driver_nl80211.c | 8 ++++++++ 6 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c index 18bf01772..11c209c6f 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -480,7 +480,7 @@ int hostapd_sta_add(struct hostapd_data *hapd, const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab, u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps, int set, const u8 *link_addr, bool mld_link_sta, - u16 eml_cap) + u16 eml_cap, bool epp_sta) { struct hostapd_sta_add_params params; @@ -510,7 +510,9 @@ int hostapd_sta_add(struct hostapd_data *hapd, params.support_p2p_ps = supp_p2p_ps; params.set = set; params.mld_link_id = -1; - +#ifdef CONFIG_ENC_ASSOC + params.epp_sta = epp_sta; +#endif #ifdef CONFIG_IEEE80211BE /* * An AP MLD needs to always specify to what link the station needs diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index 517c16e35..671b99042 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -50,7 +50,7 @@ int hostapd_sta_add(struct hostapd_data *hapd, const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab, u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps, int set, const u8 *link_addr, bool mld_link_sta, - u16 eml_cap); + u16 eml_cap, bool epp_sta); int hostapd_set_privacy(struct hostapd_data *hapd, int enabled); int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem, size_t elem_len); diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index dbc2ceea8..910025f49 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -4488,7 +4488,11 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, const u8 *wpa_ie; size_t wpa_ie_len; const u8 *p2p_dev_addr = NULL; + bool epp_sta = false; +#ifdef CONFIG_ENC_ASSOC + epp_sta = sta->epp_sta; +#endif if (type != LINK_PARSE_RECONF) { resp = check_ssid(hapd, sta, elems->ssid, elems->ssid_len); if (resp != WLAN_STATUS_SUCCESS) @@ -4777,7 +4781,7 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, wpa_printf(MSG_DEBUG, "SAE: " MACSTR " using PMKSA caching", MAC2STR(sta->addr)); sae_assign_vlan(hapd, sta, sa->sae_vlan_id); - } else if (wpa_auth_uses_sae(sta->wpa_sm) && + } else if (!epp_sta && wpa_auth_uses_sae(sta->wpa_sm) && sta->auth_alg != WLAN_AUTH_SAE && !(sta->auth_alg == WLAN_AUTH_FT && wpa_auth_uses_ft_sae(sta->wpa_sm))) { @@ -5359,9 +5363,13 @@ static int add_associated_sta(struct hostapd_data *hapd, struct ieee80211_eht_capabilities eht_cap; int set = 1; const u8 *mld_link_addr = NULL; - bool mld_link_sta = false; + bool mld_link_sta = false, epp_sta = false; u16 eml_cap = 0; +#ifdef CONFIG_ENC_ASSOC + epp_sta = sta->epp_sta; +#endif + #ifdef CONFIG_IEEE80211BE if (ap_sta_is_mld(hapd, sta)) { u8 mld_link_id = hapd->mld_link_id; @@ -5452,7 +5460,8 @@ static int add_associated_sta(struct hostapd_data *hapd, sta->he_6ghz_capab, sta->flags | WLAN_STA_ASSOC, sta->qosinfo, sta->vht_opmode, sta->p2p_ie ? 1 : 0, - set, mld_link_addr, mld_link_sta, eml_cap)) { + set, mld_link_addr, mld_link_sta, eml_cap, + epp_sta)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, "Could not %s STA to kernel driver", diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 6670174cd..6d2a15176 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -1986,9 +1986,12 @@ static void ap_sta_remove_link_sta(struct hostapd_data *hapd, int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta) { const u8 *mld_link_addr = NULL; - bool mld_link_sta = false; + bool mld_link_sta = false, epp_sta = false; u16 eml_cap = 0; +#ifdef CONFIG_ENC_ASSOC + epp_sta = sta->epp_sta; +#endif /* * If a station that is already associated to the AP, is trying to * authenticate again, remove the STA entry, in order to make sure the @@ -2022,7 +2025,8 @@ int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta) sta->supported_rates_len, 0, NULL, NULL, NULL, 0, NULL, 0, NULL, sta->flags, 0, 0, 0, 0, - mld_link_addr, mld_link_sta, eml_cap)) { + mld_link_addr, mld_link_sta, eml_cap, + epp_sta)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 2ed0282eb..e727dabf9 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -2668,6 +2668,9 @@ struct hostapd_sta_add_params { size_t eht_capab_len; u32 flags; /* bitmask of WPA_STA_* flags */ u32 flags_mask; /* unset bits in flags */ +#ifdef CONFIG_ENC_ASSOC + bool epp_sta; +#endif #ifdef CONFIG_MESH enum mesh_plink_state plink_state; u16 peer_aid; diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 30433548e..86a1d7557 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -6217,6 +6217,14 @@ static int wpa_driver_nl80211_sta_add(void *priv, goto fail; } +#ifdef CONFIG_ENC_ASSOC + if (params->epp_sta) { + wpa_printf(MSG_DEBUG, "EPP STA"); + if (nla_put_flag(msg, NL80211_ATTR_EPP_PEER)) + goto fail; + } +#endif + ret = send_and_recv_cmd(drv, msg); msg = NULL; if (ret) -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:25 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:25 +0530 Subject: [PATCH v2 15/28] EPPKE: EPP capabilities negotiation indication In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-16-ainy.kumari@oss.qualcomm.com> From: Sai Pratyusha Magam Indicate the negotiated EPP capabilities in (Re)Association Request frame via netlink attribute @NL80211_ATTR_EPP_FLAGS in @NL80211_CMD_SET_STATION The Extended RSN capabilities for EPP that an EPP AP and an EPP non-AP STA can negotiate are as per IEEE P802.11bi/D2.0, 9.4.2.240 (RSNXE), few of which are driver dependent, hence need them to be communicated from userspace for later use. Signed-off-by: Sai Pratyusha Magam Signed-off-by: Rohan Dutta --- src/ap/ap_drv_ops.c | 3 ++- src/ap/ap_drv_ops.h | 2 +- src/ap/ieee802_11.c | 4 +++- src/ap/sta_info.c | 2 +- src/ap/wpa_auth.c | 32 ++++++++++++++++++++++++++++++++ src/ap/wpa_auth.h | 4 +++- src/ap/wpa_auth_i.h | 3 +++ src/ap/wpa_auth_ie.c | 3 +++ src/drivers/driver.h | 8 ++++++++ src/drivers/driver_nl80211.c | 19 +++++++++++++++++++ 10 files changed, 75 insertions(+), 5 deletions(-) diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c index 11c209c6f..3b7f2434d 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -480,7 +480,7 @@ int hostapd_sta_add(struct hostapd_data *hapd, const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab, u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps, int set, const u8 *link_addr, bool mld_link_sta, - u16 eml_cap, bool epp_sta) + u16 eml_cap, bool epp_sta, u32 epp_flags) { struct hostapd_sta_add_params params; @@ -512,6 +512,7 @@ int hostapd_sta_add(struct hostapd_data *hapd, params.mld_link_id = -1; #ifdef CONFIG_ENC_ASSOC params.epp_sta = epp_sta; + params.epp_flags = epp_flags; #endif #ifdef CONFIG_IEEE80211BE /* diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index 671b99042..4e8745a97 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -50,7 +50,7 @@ int hostapd_sta_add(struct hostapd_data *hapd, const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab, u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps, int set, const u8 *link_addr, bool mld_link_sta, - u16 eml_cap, bool epp_sta); + u16 eml_cap, bool epp_sta, u32 epp_flags); int hostapd_set_privacy(struct hostapd_data *hapd, int enabled); int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem, size_t elem_len); diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 910025f49..bf81e3835 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -5365,9 +5365,11 @@ static int add_associated_sta(struct hostapd_data *hapd, const u8 *mld_link_addr = NULL; bool mld_link_sta = false, epp_sta = false; u16 eml_cap = 0; + u32 epp_flags = 0; #ifdef CONFIG_ENC_ASSOC epp_sta = sta->epp_sta; + epp_flags = wpa_auth_get_epp_flags(sta->wpa_sm); #endif #ifdef CONFIG_IEEE80211BE @@ -5461,7 +5463,7 @@ static int add_associated_sta(struct hostapd_data *hapd, sta->flags | WLAN_STA_ASSOC, sta->qosinfo, sta->vht_opmode, sta->p2p_ie ? 1 : 0, set, mld_link_addr, mld_link_sta, eml_cap, - epp_sta)) { + epp_sta, epp_flags)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, "Could not %s STA to kernel driver", diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 6d2a15176..252c47023 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -2026,7 +2026,7 @@ int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta) 0, NULL, NULL, NULL, 0, NULL, 0, NULL, sta->flags, 0, 0, 0, 0, mld_link_addr, mld_link_sta, eml_cap, - epp_sta)) { + epp_sta, 0)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 040efd4a4..28824438b 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -107,6 +107,16 @@ static const u8 * wpa_auth_get_spa(const struct wpa_state_machine *sm) } +#ifdef CONFIG_ENC_ASSOC +const u32 wpa_auth_get_epp_flags(struct wpa_state_machine *sm) +{ + if (!sm) + return 0; + return sm->epp_flags; +} +#endif /* CONFIG_ENC_ASSOC */ + + static void wpa_gkeydone_sta(struct wpa_state_machine *sm) { #ifdef CONFIG_IEEE80211BE @@ -7351,6 +7361,28 @@ void wpa_auth_set_rsn_selection(struct wpa_state_machine *sm, const u8 *ie, } +#ifdef CONFIG_ENC_ASSOC +void wpa_auth_ap_sta_epp_flags(struct wpa_state_machine *sm, const u8 *rsnxe) +{ + struct wpa_auth_config *conf; + + if (!sm || !rsnxe) + return; + + conf = &sm->wpa_auth->conf; + + if (conf->assoc_frame_encryption && + ieee802_11_rsnx_capab(sm->rsnxe, + WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION)) + sm->epp_flags |= WPA_EPP_ASSOC_FRAME_ENCRYPTION; + if (conf->eap_using_authentication_frames && + ieee802_11_rsnx_capab(sm->rsnxe, + WLAN_RSNX_CAPAB_1X_UTILIZING_AUTHENTICATION_FRAMES)) + sm->epp_flags |= WPA_EPP_1X_UTILIZING_AUTHENTICATION_FRAMES; +} +#endif /* CONFIG_ENC_ASSOC */ + + #ifdef CONFIG_DPP2 void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z) { diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 4883e2db9..9d4c9a132 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -507,6 +507,8 @@ int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm); int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm); int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, struct rsn_pmksa_cache_entry *entry); +u32 wpa_auth_get_epp_flags(struct wpa_state_machine *sm); + struct rsn_pmksa_cache_entry * wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm); void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm); @@ -558,7 +560,7 @@ void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa, int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id); void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int ack); - +void wpa_auth_ap_sta_epp_flags(struct wpa_state_machine *sm, const u8 *rsnxe); #ifdef CONFIG_IEEE80211R_AP u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, size_t max_len, int auth_alg, diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index e97db8c7f..f7836cc1a 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -192,6 +192,9 @@ struct wpa_state_machine { #endif /* CONFIG_IEEE80211BE */ bool ssid_protection; +#ifdef CONFIG_ENC_ASSOC + u32 epp_flags; /* See %enum epp_flags */ +#endif /* CONFIG_ENC_ASSOC */ struct wpabuf *sae_pw_id; unsigned int sae_pw_id_counter; diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c index 6ae1350a0..f5bbedc04 100644 --- a/src/ap/wpa_auth_ie.c +++ b/src/ap/wpa_auth_ie.c @@ -1388,6 +1388,9 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, } os_memcpy(sm->rsnxe, rsnxe, rsnxe_len); sm->rsnxe_len = rsnxe_len; +#ifdef CONFIG_ENC_ASSOC + wpa_auth_ap_sta_epp_flags(sm, rsnxe); +#endif } else { os_free(sm->rsnxe); sm->rsnxe = NULL; diff --git a/src/drivers/driver.h b/src/drivers/driver.h index e727dabf9..53fb9c81f 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -2670,6 +2670,7 @@ struct hostapd_sta_add_params { u32 flags_mask; /* unset bits in flags */ #ifdef CONFIG_ENC_ASSOC bool epp_sta; + u32 epp_flags; /* See %enum epp_flags */ #endif #ifdef CONFIG_MESH enum mesh_plink_state plink_state; @@ -2738,6 +2739,13 @@ struct wpa_bss_params { #define WPA_STA_ASSOCIATED BIT(6) #define WPA_STA_SPP_AMSDU BIT(7) +#ifdef CONFIG_ENC_ASSOC +enum epp_flags { + WPA_EPP_ASSOC_FRAME_ENCRYPTION = BIT(0), + WPA_EPP_1X_UTILIZING_AUTHENTICATION_FRAMES = BIT(1) +}; +#endif + enum tdls_oper { TDLS_DISCOVERY_REQ, TDLS_SETUP, diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 86a1d7557..3c1334cad 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -5900,6 +5900,19 @@ static u32 sta_flags_nl80211(int flags) return f; } +#ifdef CONFIG_ENC_ASSOC +static u32 epp_flags_nl80211(u32 epp_flags) +{ + u32 f = 0; + + if (epp_flags & WPA_EPP_ASSOC_FRAME_ENCRYPTION) + f |= BIT(NL80211_EPP_FLAG_ASSOC_FRAME_ENCRYPTION); + if (epp_flags & WPA_EPP_1X_UTILIZING_AUTHENTICATION_FRAMES) + f |= BIT(NL80211_EPP_FLAG_1X_UTILIZING_AUTHENTICATION_FRAMES); + + return f; +} +#endif #ifdef CONFIG_MESH static u32 sta_plink_state_nl80211(enum mesh_plink_state state) @@ -6115,6 +6128,12 @@ static int wpa_driver_nl80211_sta_add(void *priv, goto fail; } +#ifdef CONFIG_ENC_ASSOC + if (nla_put_u32(msg, NL80211_ATTR_EPP_FLAGS, + epp_flags_nl80211(params->epp_flags))) + goto fail; +#endif + os_memset(&upd, 0, sizeof(upd)); upd.set = sta_flags_nl80211(params->flags); upd.mask = upd.set | sta_flags_nl80211(params->flags_mask); -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:26 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:26 +0530 Subject: [PATCH v2 16/28] EPPKE: RSNE/Key delivery element in (Re)Association Response In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-17-ainy.kumari@oss.qualcomm.com> From: Sai Pratyusha Magam -Include RSN IE and Key delivery element indicating group KDEs to an EPP non-AP STA in (Re)Association Response frame and also set the protected bit in the frame control in 802.11 frame header as an indication to driver/firmware that this frame needs to be encrypted Signed-off-by: Sai Pratyusha Magam Signed-off-by: Rohan Dutta --- src/ap/ieee802_11.c | 9 +++++++ src/ap/wpa_auth.c | 62 +++++++++++++++++++++++++++++++++++++++++++++ src/ap/wpa_auth.h | 6 ++++- 3 files changed, 76 insertions(+), 1 deletion(-) diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index bf81e3835..8beca257f 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -5776,6 +5776,15 @@ rsnxe_done: wpabuf_len(hapd->conf->assocresp_elements)); p += wpabuf_len(hapd->conf->assocresp_elements); } +#ifdef CONFIG_ENC_ASSOC + if (sta && sta->auth_alg == WLAN_AUTH_EPPKE && + status_code == WLAN_STATUS_SUCCESS) { + reply->frame_control |= WLAN_FC_ISWEP; + p = wpa_auth_write_assoc_resp_eppke(sta->wpa_sm, p, + (buf + buflen - p), + ap_sta_is_mld(hapd, sta)); + } +#endif /* CONFIG_ENC_ASSOC */ send_len += p - reply->u.assoc_resp.variable; diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 28824438b..99d8e45c2 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -7868,6 +7868,68 @@ bool wpa_auth_sm_known_sta_identification(struct wpa_state_machine *sm, } +#ifdef CONFIG_ENC_ASSOC +u8 * wpa_auth_write_assoc_resp_eppke(struct wpa_state_machine *sm, + u8 *pos, size_t max_len, bool is_ml) +{ + int res; + + if (!sm) + return pos; + + res = wpa_write_rsn_ie(&sm->wpa_auth->conf, pos, max_len, NULL); + if (res < 0) + return pos; + pos = wpa_auth_eid_key_delivery((pos + res), sm, is_ml); + + return pos; +} + + +/*TODO Key delivery element is fragmentable*/ +u8 * wpa_auth_eid_key_delivery(u8 *eid, struct wpa_state_machine *sm, + bool is_ml) +{ + size_t gtk_len, kde_len = 0; + u8 rsc[WPA_KEY_RSC_LEN] = {0}, *gtk; + struct wpa_group *gsm = sm->group; + u8 hdr[2]; + /* + *ElementID(0xff)|Length(1B)|ElementID EXtn(1B)|RSC(8B)|KDE list + */ + *eid++ = WLAN_EID_EXTENSION; + + if (!is_ml) { + /*GTK KDE: 0xdd|len(1B)|RSN Selector(4B)|KeyID(2B)|GTK|*/ + kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + + ieee80211w_kde_len(sm); + } else + kde_len = wpa_auth_ml_group_kdes_len(sm, KDE_ALL_LINKS); + + *eid++ = 1 + WPA_KEY_RSC_LEN + kde_len; + *eid++ = WLAN_EID_EXT_KEY_DELIVERY; + /*RSC*/ + if (!is_ml && sm->group->wpa_group_state == WPA_GROUP_SETKEYSDONE) + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); + os_memcpy(eid, rsc, WPA_KEY_RSC_LEN); + + eid += WPA_KEY_RSC_LEN; + + if (is_ml) + eid = wpa_auth_ml_group_kdes(sm, eid, KDE_ALL_LINKS); + else { + gtk = gsm->GTK[gsm->GN - 1]; + gtk_len = gsm->GTK_len; + hdr[0] = gsm->GN & 0x03; + eid = wpa_add_kde(eid, RSN_KEY_DATA_GROUPKEY, hdr, 2, gtk, gtk_len); + eid = ieee80211w_kde_add(sm, eid); + } + + return eid; +} +#endif /* CONFIG_ENC_ASSOC */ + + void wpa_reset_assoc_sm_info(struct wpa_state_machine *assoc_sm, struct wpa_authenticator *wpa_auth, u8 mld_assoc_link_id) diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 9d4c9a132..8fc955434 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -655,7 +655,11 @@ void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z); void wpa_auth_set_ssid_protection(struct wpa_state_machine *sm, bool val); void wpa_auth_set_transition_disable(struct wpa_authenticator *wpa_auth, u8 val); - +u8 * wpa_auth_eid_key_delivery(u8 *eid, + struct wpa_state_machine *sm, + bool is_ml); +u8 * wpa_auth_write_assoc_resp_eppke(struct wpa_state_machine *sm, + u8 *pos, size_t max_len, bool is_ml); int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce, void (*cb)(void *ctx1, void *ctx2), void *ctx1, void *ctx2); -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:27 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:27 +0530 Subject: [PATCH v2 17/28] EPPKE: Skip 4WH and move PTK state directly to PTKINITDONE In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-18-ainy.kumari@oss.qualcomm.com> From: Sai Pratyusha Magam A successful EPPKE Authentication results in the establishment of a PTKSA. So skip 4-way handshake procedure and move the PTK state directly to PTKINITDONE Also move the peer state to authorized. Signed-off-by: Rohan Dutta Signed-off-by: Sai Pratyusha Magam --- src/ap/ap_mlme.c | 6 ++++-- src/ap/ieee802_11.c | 3 ++- src/ap/wpa_auth.c | 16 ++++++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/ap/ap_mlme.c b/src/ap/ap_mlme.c index 309e69a3f..efc214fe5 100644 --- a/src/ap/ap_mlme.c +++ b/src/ap/ap_mlme.c @@ -112,7 +112,8 @@ void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta) if (sta->auth_alg != WLAN_AUTH_FT && sta->auth_alg != WLAN_AUTH_FILS_SK && sta->auth_alg != WLAN_AUTH_FILS_SK_PFS && - sta->auth_alg != WLAN_AUTH_FILS_PK) + sta->auth_alg != WLAN_AUTH_FILS_PK && + sta->auth_alg != WLAN_AUTH_EPPKE) mlme_deletekeys_request(hapd, sta); ap_sta_clear_disconnect_timeouts(hapd, sta); } @@ -140,7 +141,8 @@ void mlme_reassociate_indication(struct hostapd_data *hapd, if (sta->auth_alg != WLAN_AUTH_FT && sta->auth_alg != WLAN_AUTH_FILS_SK && sta->auth_alg != WLAN_AUTH_FILS_SK_PFS && - sta->auth_alg != WLAN_AUTH_FILS_PK) + sta->auth_alg != WLAN_AUTH_FILS_PK && + sta->auth_alg != WLAN_AUTH_EPPKE) mlme_deletekeys_request(hapd, sta); ap_sta_clear_disconnect_timeouts(hapd, sta); } diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 8beca257f..27491a1c0 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -7316,7 +7316,8 @@ static void handle_assoc_cb(struct hostapd_data *hapd, sta->auth_alg == WLAN_AUTH_FILS_SK || sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || sta->auth_alg == WLAN_AUTH_FILS_PK || - sta->auth_alg == WLAN_AUTH_FT) { + sta->auth_alg == WLAN_AUTH_FT || + sta->auth_alg == WLAN_AUTH_EPPKE) { /* * Open, static WEP, FT protocol, or FILS; no separate * authorization step. diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 99d8e45c2..bbdf76a70 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -1051,6 +1051,17 @@ int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, if (!wpa_auth || !wpa_auth->conf.wpa || !sm) return -1; +#ifdef CONFIG_ENC_ASSOC + if (sm->auth_alg == WLAN_AUTH_EPPKE) { + wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG, + "EPPKE authentication already completed - do not start 4-way handshake"); + /* Go to PTKINITDONE state to allow GTK rekeying */ + sm->wpa_ptk_state = WPA_PTK_PTKINITDONE; + sm->Pair = true; + return 0; + } +#endif /* CONFIG_ENC_ASSOC */ + #ifdef CONFIG_IEEE80211R_AP if (sm->ft_completed) { wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG, @@ -2532,6 +2543,11 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event) (event == WPA_AUTH || event == WPA_ASSOC)) remove_ptk = 0; #endif /* CONFIG_FILS */ +#ifdef CONFIG_ENC_ASSOC + if (sm->auth_alg == WLAN_AUTH_EPPKE && + (event == WPA_AUTH || event == WPA_ASSOC)) + remove_ptk = 0; +#endif /* CONFIG_ENC_ASSOC */ if (remove_ptk) { sm->PTK_valid = false; -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:28 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:28 +0530 Subject: [PATCH v2 18/28] EPPKE: Add support for EPPKE authentication for SME-in-Userspace case In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-19-ainy.kumari@oss.qualcomm.com> Extend existing PASN Implementation for authentication frame construction and processing logic to align with IEEE 802.11bi/D2.0, section 12.16.9.3.2 for EPPKE authentication support. Apply PASN logic for EPPKE authentication with necessary differences to support SME-in-Userspace scenarios. Below are the Key Changes: 1. Modify PASN Authentication request frame construction APIs: - Remove 'static' from M1 and M3 frame building functions to make them globally accessible. - Add 'is_sme_drv' parameter to differentiate between frames sent via %NL80211_CMD_AUTHENTICATE (false) and %NL80211_CMD_FRAME (true) interface and prepare auth_data for authentication request frames accordingly. 2. Refactor PASN authentication response frame parsing API: - Introduce a new helper function wpas_parse_pasn_frame() to handle authentication response frame parsing in a modular way. This separates the frame parsing logic from wpa_pasn_auth_rx() and enables reuse for SME-in-Userspace scenarios. - Simplify pasn_parse_encrypted_data() by removing ieee80211_mgmt dependency and enables reuse for SME-in-Userspace scenarios. 3. Update SME code to integrate EPPKE flow: - Add initialization, frame construction and event handling for EPPKE using extended PASN APIs for building authentication request frames and parsing the authentication response frame. Signed-off-by: Ainy Kumari --- src/pasn/pasn_common.c | 9 +- src/pasn/pasn_common.h | 8 ++ src/pasn/pasn_initiator.c | 154 +++++++++++++++++-------- src/pasn/pasn_responder.c | 4 +- wpa_supplicant/sme.c | 196 +++++++++++++++++++++++++++++++- wpa_supplicant/wpa_supplicant.c | 1 + 6 files changed, 309 insertions(+), 63 deletions(-) diff --git a/src/pasn/pasn_common.c b/src/pasn/pasn_common.c index f9ec5ca9c..35b91b32d 100644 --- a/src/pasn/pasn_common.c +++ b/src/pasn/pasn_common.c @@ -331,14 +331,7 @@ int pasn_parse_encrypted_data(struct pasn_data *pasn, const u8 *data, u8 *buf; u16 buf_len; struct ieee802_11_elems elems; - const struct ieee80211_mgmt *mgmt = - (const struct ieee80211_mgmt *) data; - - if (len < 24 + 6 || - ieee802_11_parse_elems(mgmt->u.auth.variable, - len - offsetof(struct ieee80211_mgmt, - u.auth.variable), - &elems, 0) == ParseFailed) { + if (ieee802_11_parse_elems(data, len, &elems, 0) == ParseFailed) { wpa_printf(MSG_DEBUG, "PASN: Failed parsing Authentication frame"); return -1; diff --git a/src/pasn/pasn_common.h b/src/pasn/pasn_common.h index 45ac3b4ce..cf9ef93b1 100644 --- a/src/pasn/pasn_common.h +++ b/src/pasn/pasn_common.h @@ -183,6 +183,10 @@ int wpas_pasn_start(struct pasn_data *pasn, const u8 *own_addr, int freq, const u8 *beacon_rsne, u8 beacon_rsne_len, const u8 *beacon_rsnxe, u8 beacon_rsnxe_len, const struct wpabuf *comeback); +struct wpabuf *wpas_pasn_build_auth_1(struct pasn_data *pasn, + const struct wpabuf *comeback, + bool verify, bool is_sme_drv); +struct wpabuf *wpas_pasn_build_auth_3(struct pasn_data *pasn, bool is_sme_drv); int wpa_pasn_verify(struct pasn_data *pasn, const u8 *own_addr, const u8 *peer_addr, const u8 *bssid, int akmp, int cipher, u16 group, @@ -193,6 +197,10 @@ int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len, struct wpa_pasn_params_data *pasn_params); int wpa_pasn_auth_tx_status(struct pasn_data *pasn, const u8 *data, size_t data_len, u8 acked); +int wpas_parse_pasn_frame(struct pasn_data *pasn, u16 auth_type, u16 auth_transaction, + u16 status_code, const u8 *frame_data, size_t frame_data_len, + struct wpa_pasn_params_data *pasn_params); + /* Responder */ int handle_auth_pasn_1(struct pasn_data *pasn, diff --git a/src/pasn/pasn_initiator.c b/src/pasn/pasn_initiator.c index 68a4f09fa..ec356173a 100644 --- a/src/pasn/pasn_initiator.c +++ b/src/pasn/pasn_initiator.c @@ -582,9 +582,9 @@ static u8 wpas_pasn_get_wrapped_data_format(struct pasn_data *pasn) } -static struct wpabuf * wpas_pasn_build_auth_1(struct pasn_data *pasn, +struct wpabuf *wpas_pasn_build_auth_1(struct pasn_data *pasn, const struct wpabuf *comeback, - bool verify) + bool verify, bool is_sme_drv) { struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL; const u8 *pmkid; @@ -609,10 +609,16 @@ static struct wpabuf * wpas_pasn_build_auth_1(struct pasn_data *pasn, wrapped_data = wpas_pasn_get_wrapped_data_format(pasn); - wpa_pasn_build_auth_header(buf, pasn->bssid, - pasn->own_addr, pasn->peer_addr, - pasn->trans_seq + 1, WLAN_STATUS_SUCCESS, - pasn->auth_alg == WLAN_AUTH_EPPKE); + if (!is_sme_drv) { + wpabuf_put_le16(buf, pasn->trans_seq + 1); + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + } else { + wpa_pasn_build_auth_header(buf, pasn->bssid, + pasn->own_addr, pasn->peer_addr, + pasn->trans_seq + 1, + WLAN_STATUS_SUCCESS, + pasn->auth_alg == WLAN_AUTH_EPPKE); + } pmkid = NULL; if (wpa_key_mgmt_ft(pasn->akmp)) { @@ -682,7 +688,8 @@ fail: } -static struct wpabuf * wpas_pasn_build_auth_3(struct pasn_data *pasn) +struct wpabuf *wpas_pasn_build_auth_3(struct pasn_data *pasn, + bool is_sme_drv) { struct wpabuf *buf, *wrapped_data_buf = NULL; u8 mic[WPA_PASN_MAX_MIC_LEN]; @@ -703,13 +710,18 @@ static struct wpabuf * wpas_pasn_build_auth_3(struct pasn_data *pasn) if (!buf) goto fail; - wrapped_data = wpas_pasn_get_wrapped_data_format(pasn); + if (!is_sme_drv) { + wpabuf_put_le16(buf, WLAN_AUTH_TR_SEQ_PASN_AUTH3); + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + } else { + wpa_pasn_build_auth_header(buf, pasn->bssid, + pasn->own_addr, pasn->peer_addr, + WLAN_AUTH_TR_SEQ_PASN_AUTH3, + WLAN_STATUS_SUCCESS, + pasn->auth_alg == WLAN_AUTH_EPPKE); + } - wpa_pasn_build_auth_header(buf, pasn->bssid, - pasn->own_addr, pasn->peer_addr, - WLAN_AUTH_TR_SEQ_PASN_AUTH3, - WLAN_STATUS_SUCCESS, - pasn->auth_alg == WLAN_AUTH_EPPKE); + wrapped_data = wpas_pasn_get_wrapped_data_format(pasn); wrapped_data_buf = wpas_pasn_get_wrapped_data(pasn); @@ -1010,7 +1022,7 @@ static int wpas_pasn_send_auth_1(struct pasn_data *pasn, const u8 *own_addr, MAC2STR(pasn->peer_addr), pasn->akmp, pasn->cipher, pasn->group); - frame = wpas_pasn_build_auth_1(pasn, comeback, verify); + frame = wpas_pasn_build_auth_1(pasn, comeback, verify, true); if (!frame) { wpa_printf(MSG_DEBUG, "PASN: Failed building 1st auth frame"); goto fail; @@ -1153,36 +1165,27 @@ static bool is_pasn_auth_frame(struct pasn_data *pasn, } -int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len, - struct wpa_pasn_params_data *pasn_params) - +int wpas_parse_pasn_frame(struct pasn_data *pasn, u16 auth_type, + u16 auth_transaction, u16 status, + const u8 *auth_data, size_t auth_data_len, + struct wpa_pasn_params_data *pasn_params) { struct ieee802_11_elems elems; struct wpa_ie_data rsn_data; - const struct ieee80211_mgmt *mgmt = - (const struct ieee80211_mgmt *) data; - struct wpabuf *wrapped_data = NULL, *secret = NULL, *frame = NULL; + struct wpabuf *wrapped_data = NULL, *secret = NULL; u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN]; u8 mic_len; - u16 status; int ret, inc_y; u8 *copy = NULL; size_t mic_offset, copy_len; - if (!is_pasn_auth_frame(pasn, mgmt, len, true)) - return -2; - - if (mgmt->u.auth.auth_transaction != - host_to_le16(pasn->trans_seq + 1)) { + if (auth_transaction != pasn->trans_seq + 1) { wpa_printf(MSG_DEBUG, "PASN: RX: Invalid transaction sequence: (%u != %u)", - le_to_host16(mgmt->u.auth.auth_transaction), - pasn->trans_seq + 1); + auth_transaction, pasn->trans_seq + 1); return -3; } - status = le_to_host16(mgmt->u.auth.status_code); - if (status != WLAN_STATUS_SUCCESS && status != WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) { wpa_printf(MSG_DEBUG, @@ -1190,10 +1193,8 @@ int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len, goto fail; } - if (ieee802_11_parse_elems(mgmt->u.auth.variable, - len - offsetof(struct ieee80211_mgmt, - u.auth.variable), - &elems, 0) == ParseFailed) { + if (ieee802_11_parse_elems(auth_data, auth_data_len, &elems, 0) == + ParseFailed) { wpa_printf(MSG_DEBUG, "PASN: Failed parsing Authentication frame"); goto fail; @@ -1354,14 +1355,24 @@ int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len, /* Use a copy of the message since we need to clear the MIC field */ if (!elems.mic) goto fail; - mic_offset = elems.mic - (const u8 *) &mgmt->u.auth; - copy_len = len - offsetof(struct ieee80211_mgmt, u.auth); - if (mic_offset + mic_len > copy_len) - goto fail; - copy = os_memdup(&mgmt->u.auth, copy_len); + + mic_offset = elems.mic - auth_data; + /* 6 bytes for auth algorithm, auth transaction and status code. + * 2 bytes each. + */ + copy = os_malloc(auth_data_len + 6); if (!copy) goto fail; - os_memset(copy + mic_offset, 0, mic_len); + + os_memcpy(copy, &auth_type, 2); + os_memcpy(copy + 2, &auth_transaction, 2); + os_memcpy(copy + 4, &status, 2); + os_memcpy(copy + 6, auth_data, auth_data_len); + copy_len = auth_data_len + 6; + if (mic_offset + mic_len > auth_data_len) + goto fail; + + os_memset(copy + mic_offset + 6, 0, mic_len); if (pasn->beacon_rsne_rsnxe) { /* Verify the MIC */ @@ -1421,12 +1432,61 @@ int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len, wpa_printf(MSG_DEBUG, "PASN: Success verifying Authentication frame"); - if (pasn_parse_encrypted_data(pasn, data, len) < 0) { + if (pasn_parse_encrypted_data(pasn, auth_data, auth_data_len) < 0) { wpa_printf(MSG_DEBUG, "PASN: Encrypted data processing failed"); goto fail; } - frame = wpas_pasn_build_auth_3(pasn); + return 0; +fail: + wpabuf_free(wrapped_data); + wpabuf_free(secret); + os_free(copy); + + /* + * TODO: In case of an error the standard allows to silently drop + * the frame and terminate the authentication exchange. However, better + * reply to the AP with an error status. + */ + if (status == WLAN_STATUS_SUCCESS) + pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE; + else + pasn->status = status; + + return -1; + +} + + +int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len, + struct wpa_pasn_params_data *pasn_params) +{ + const struct ieee80211_mgmt *mgmt = + (const struct ieee80211_mgmt *) data; + struct wpabuf *frame = NULL; + int ret; + + if (!is_pasn_auth_frame(pasn, mgmt, len, true)) + return -2; + + ret = wpas_parse_pasn_frame(pasn, le_to_host16(mgmt->u.auth.auth_alg), + le_to_host16(mgmt->u.auth.auth_transaction), + le_to_host16(mgmt->u.auth.status_code), + mgmt->u.auth.variable, + len - offsetof(struct ieee80211_mgmt, + u.auth.variable), pasn_params); + + if (ret < 0) { + wpa_printf(MSG_DEBUG, "PASN: Failed parsing PASN M2 auth frame"); + goto fail; + } + + if (ret == 1) { + wpa_printf(MSG_DEBUG, "PASN: Temporary rejection, Retry"); + return 1; + } + + frame = wpas_pasn_build_auth_3(pasn, true); if (!frame) { wpa_printf(MSG_DEBUG, "PASN: Failed building 3rd auth frame"); goto fail; @@ -1451,21 +1511,17 @@ int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len, return 0; fail: - wpa_printf(MSG_DEBUG, "PASN: Failed RX processing - terminating"); - wpabuf_free(wrapped_data); - wpabuf_free(secret); - os_free(copy); - /* * TODO: In case of an error the standard allows to silently drop * the frame and terminate the authentication exchange. However, better * reply to the AP with an error status. */ - if (status == WLAN_STATUS_SUCCESS) + if (le_to_host16(mgmt->u.auth.status_code) == WLAN_STATUS_SUCCESS) pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE; else - pasn->status = status; + pasn->status = le_to_host16(mgmt->u.auth.status_code); + wpa_printf(MSG_DEBUG, "PASN: Failed RX processing - terminating"); return -1; } diff --git a/src/pasn/pasn_responder.c b/src/pasn/pasn_responder.c index 1aa72de11..f7f1bc413 100644 --- a/src/pasn/pasn_responder.c +++ b/src/pasn/pasn_responder.c @@ -1184,7 +1184,9 @@ int handle_auth_pasn_3(struct pasn_data *pasn, const u8 *own_addr, wpabuf_free(wrapped_data); } - if (pasn_parse_encrypted_data(pasn, (const u8 *) mgmt, len) < 0) { + if (pasn_parse_encrypted_data(pasn, (const u8 *) mgmt->u.auth.variable, + len - offsetof(struct ieee80211_mgmt, + u.auth.variable)) < 0) { wpa_printf(MSG_DEBUG, "PASN: Encrypted data processing failed"); goto fail; } diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index fba6508bc..3510faf74 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -32,6 +32,9 @@ #include "scan.h" #include "sme.h" #include "hs20_supplicant.h" +#include "pasn/pasn_common.h" +#include "common/dragonfly.h" +#include "common/ptksa_cache.h" #define SME_AUTH_TIMEOUT 5 #define SME_ASSOC_TIMEOUT 5 @@ -41,6 +44,7 @@ static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx); static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx); static void sme_stop_sa_query(struct wpa_supplicant *wpa_s); +static const int dot11RSNAConfigPMKLifetime = 43200; #ifdef CONFIG_SAE @@ -597,6 +601,133 @@ static void sme_add_assoc_req_ie(struct wpa_supplicant *wpa_s, } } +#ifdef CONFIG_ENC_ASSOC +static struct sae_pt * +sme_eppke_sae_derive_pt(struct wpa_ssid *ssid, int group) +{ + const char *password = ssid->sae_password; + int groups[2] = { group, 0 }; + + if (!password) + password = ssid->passphrase; + + if (!password) { + wpa_printf(MSG_DEBUG, "PASN: SAE without a password"); + return NULL; + } + + return sae_derive_pt(groups, ssid->ssid, ssid->ssid_len, + (const u8 *) password, os_strlen(password), + (const u8 *) ssid->sae_password_id, + ssid->sae_password_id ? + os_strlen(ssid->sae_password_id) : 0); +} + +static void wpas_eppke_initialize(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + struct wpa_ssid *ssid) +{ + struct pasn_data *pasn; + const u8 *beacon_rsne, *beacon_rsnxe; + u8 beacon_rsne_len, beacon_rsnxe_len; + u32 capab = 0; + int group; + + pasn = &wpa_s->pasn; + + if (sme_set_sae_group(wpa_s, 0) < 0) { + wpa_printf(MSG_DEBUG, "EPPKE: Failed to select group"); + return; + } + group = wpa_s->sme.sae.group; + + if (!dragonfly_suitable_group(group, 1)) { + wpa_printf(MSG_DEBUG, + "PASN: Reject unsuitable group %u", group); + return; + } + + beacon_rsne = wpa_bss_get_rsne(wpa_s, bss, NULL, false); + if (!beacon_rsne) { + wpa_printf(MSG_DEBUG, "EPPKE: Can't connect without RSNE"); + return; + } + + beacon_rsnxe = wpa_bss_get_rsnxe(wpa_s, bss, NULL, false); + + beacon_rsne_len = *(beacon_rsne + 1) + 2; + beacon_rsnxe_len = beacon_rsnxe ? *(beacon_rsnxe + 1) + 2 : 0; + if (beacon_rsne && beacon_rsne_len) { + wpabuf_free(pasn->beacon_rsne_rsnxe); + pasn->beacon_rsne_rsnxe = wpabuf_alloc(beacon_rsne_len + + beacon_rsnxe_len); + if (!pasn->beacon_rsne_rsnxe) { + wpa_printf(MSG_DEBUG, + "PASN: Failed storing beacon RSNE/RSNXE"); + return; + } + + wpabuf_put_data(pasn->beacon_rsne_rsnxe, beacon_rsne, + beacon_rsne_len); + if (beacon_rsnxe && beacon_rsnxe_len) + wpabuf_put_data(pasn->beacon_rsne_rsnxe, + beacon_rsnxe, beacon_rsnxe_len); + } + capab |= BIT(WLAN_RSNX_CAPAB_KEK_IN_PASN); + capab |= BIT(WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION); + +#ifdef CONFIG_SAE + if (wpa_key_mgmt_sae(ssid->key_mgmt)) { + capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E); + if (beacon_rsnxe && !ieee802_11_rsnx_capab(beacon_rsnxe, + WLAN_RSNX_CAPAB_SAE_H2E)) { + wpa_printf(MSG_DEBUG, "PASN: AP does not support SAE H2E"); + return; + } + if (pasn->pt) + sae_deinit_pt(pasn->pt); + pasn_set_pt(pasn, sme_eppke_sae_derive_pt(ssid, group)); + if (!pasn->pt) { + wpa_printf(MSG_DEBUG, "PASN: Failed to derive PT"); + return; + } + pasn->sae.state = SAE_NOTHING; + pasn->sae.send_confirm = 0; + } else { + wpa_msg(wpa_s, MSG_INFO, "Base AKM not present"); + return; + } +#endif /* CONFIG_SAE */ + + pasn_set_rsnxe_caps(pasn, capab); + pasn_set_initiator_pmksa(pasn, wpa_sm_get_pmksa_cache(wpa_s->wpa)); + + if (pasn->ecdh) + crypto_ecdh_deinit(pasn->ecdh); + pasn->ecdh = crypto_ecdh_init(group); + if (!pasn->ecdh) { + wpa_printf(MSG_INFO, "PASN: Failed to init ECDH"); + return; + } + pasn->akmp = wpa_s->key_mgmt; + pasn->cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher, 1); + pasn->group = group; + pasn->freq = bss->freq; + pasn->auth_alg = WLAN_AUTH_EPPKE; + + os_memcpy(pasn->own_addr, wpa_s->own_addr, ETH_ALEN); + if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) + os_memcpy(pasn->peer_addr, wpa_s->ap_mld_addr, ETH_ALEN); + else + os_memcpy(pasn->peer_addr, bss->bssid, ETH_ALEN); + os_memcpy(pasn->bssid, bss->bssid, ETH_ALEN); + + wpa_printf(MSG_DEBUG, + "PASN: Init: " MACSTR " akmp=0x%x, cipher=0x%x, group=%u", + MAC2STR(pasn->peer_addr), pasn->akmp, + pasn->cipher, pasn->group); +} +#endif /* CONFIG_ENC_ASSOC */ + static void sme_send_authentication(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid, @@ -698,14 +829,22 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, if (!rsn) { wpa_dbg(wpa_s, MSG_DEBUG, "SAE enabled, but target BSS does not advertise RSN"); + } + if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied)) { + wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE data"); + return; #ifdef CONFIG_DPP - } else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 && - (ssid->key_mgmt & WPA_KEY_MGMT_DPP) && + } else if ((ssid->key_mgmt & WPA_KEY_MGMT_DPP) && (ied.key_mgmt & WPA_KEY_MGMT_DPP)) { wpa_dbg(wpa_s, MSG_DEBUG, "Prefer DPP over SAE when both are enabled"); #endif /* CONFIG_DPP */ - } else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 && - wpa_key_mgmt_sae(ied.key_mgmt)) { +#ifdef CONFIG_ENC_ASSOC + } else if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_EPPKE && + (ied.key_mgmt & WPA_KEY_MGMT_EPPKE)) { + wpa_dbg(wpa_s, MSG_DEBUG, "Prefer EPPKE over SAE when both are enabled"); + params.auth_alg = WPA_AUTH_ALG_EPPKE; +#endif /* CONFIG_ENC_ASSOC */ + } else if (wpa_key_mgmt_sae(ied.key_mgmt)) { if (wpas_is_sae_avoided(wpa_s, ssid, &ied)) { wpa_dbg(wpa_s, MSG_DEBUG, "SAE enabled, but disallowing SAE auth_alg without PMF"); @@ -1026,6 +1165,20 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_MBO */ +#ifdef CONFIG_ENC_ASSOC + if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_EPPKE) { + wpas_eppke_initialize(wpa_s, bss, ssid); + if (start) + resp = wpas_pasn_build_auth_1(&wpa_s->pasn, NULL, + false, 0); + else + resp = wpas_pasn_build_auth_3(&wpa_s->pasn, 0); + + params.auth_data = wpabuf_head(resp); + params.auth_data_len = wpabuf_len(resp); + wpa_s->sme.auth_alg = params.auth_alg; + } +#endif /* CONFIG_ENC_ASSOC */ #ifdef CONFIG_SAE if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE && pmksa_cache_set_current(wpa_s->wpa, NULL, @@ -1167,7 +1320,9 @@ no_fils: wpa_ssid_txt(params.ssid, params.ssid_len), params.freq); eapol_sm_notify_portValid(wpa_s->eapol, false); - wpa_clear_keys(wpa_s, bss->bssid); + if (wpa_s->sme.auth_alg != WPA_AUTH_ALG_EPPKE) { + wpa_clear_keys(wpa_s, bss->bssid); + } wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING); if (old_ssid != wpa_s->current_ssid) wpas_notify_network_changed(wpa_s); @@ -2080,6 +2235,37 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); +#ifdef CONFIG_ENC_ASSOC + if (data->auth.auth_type == WLAN_AUTH_EPPKE) { + struct pasn_data *pasn = &wpa_s->pasn; + struct wpa_pasn_params_data pasn_params; + int res; + + res = wpas_parse_pasn_frame(pasn, data->auth.auth_type, + data->auth.auth_transaction, + data->auth.status_code, + data->auth.ies, data->auth.ies_len, + &pasn_params); + + if (res < 0) { + wpas_connection_failed(wpa_s, wpa_s->pending_bssid, + NULL); + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + } + + ptksa_cache_add(wpa_s->ptksa, pasn->own_addr, pasn->peer_addr, + pasn_get_cipher(pasn), + dot11RSNAConfigPMKLifetime, + pasn_get_ptk(pasn), NULL, NULL, + pasn_get_akmp(pasn)); + + if (pasn->pmksa_entry) + wpa_sm_set_cur_pmksa(wpa_s->wpa, pasn->pmksa_entry); + + sme_send_authentication(wpa_s, wpa_s->current_bss, + wpa_s->current_ssid, 0); + } +#endif /* CONFIG_ENC_ASSOC */ #ifdef CONFIG_SAE if (data->auth.auth_type == WLAN_AUTH_SAE) { const u8 *addr = wpa_s->pending_bssid; diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 293d4920e..70c92ea00 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -9476,6 +9476,7 @@ void wpas_request_connection(struct wpa_supplicant *wpa_s) wpa_s->disconnected = 0; wpa_s->reassociate = 1; wpa_s->last_owe_group = 0; + wpa_pasn_reset(&wpa_s->pasn); if (wpa_supplicant_fast_associate(wpa_s) != 1) wpa_supplicant_req_scan(wpa_s, 0, 0); -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:29 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:29 +0530 Subject: [PATCH v2 19/28] EPPKE: Add Multi-Link support in Authentication frames In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-20-ainy.kumari@oss.qualcomm.com> Enhance EPPKE authentication to include Multi-Link Element (MLE) for MLO-capable stations. The MLE is added to authentication request frames when the station supports Multi-Link operation, ensuring proper handling of MLO connections during EPPKE authentication. Signed-off-by: Ainy Kumari --- src/pasn/pasn_initiator.c | 24 ++++++++++++++++++++++++ wpa_supplicant/sme.c | 6 ++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/pasn/pasn_initiator.c b/src/pasn/pasn_initiator.c index ec356173a..30f42fb65 100644 --- a/src/pasn/pasn_initiator.c +++ b/src/pasn/pasn_initiator.c @@ -664,6 +664,18 @@ struct wpabuf *wpas_pasn_build_auth_1(struct pasn_data *pasn, wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len); +#ifdef CONFIG_ENC_ASSOC + if (pasn->auth_alg == WLAN_AUTH_EPPKE && pasn->is_ml_peer) { + wpa_printf(MSG_DEBUG, "EPPKE: Include Multi Link Element"); + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 10); + wpabuf_put_u8(buf, WLAN_EID_EXT_MULTI_LINK); + wpabuf_put_le16(buf, MULTI_LINK_CONTROL_TYPE_BASIC); + wpabuf_put_u8(buf, ETH_ALEN + 1); + wpabuf_put_data(buf, pasn->own_addr, ETH_ALEN); + } +#endif /* CONFIG_ENC_ASSOC */ + wpabuf_free(pasn->auth1); pasn->auth1 = wpabuf_alloc_copy(wpabuf_head_u8(buf) + IEEE80211_HDRLEN, wpabuf_len(buf) - IEEE80211_HDRLEN); @@ -741,6 +753,18 @@ struct wpabuf *wpas_pasn_build_auth_3(struct pasn_data *pasn, wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len); +#ifdef CONFIG_ENC_ASSOC + if (pasn->auth_alg == WLAN_AUTH_EPPKE && pasn->is_ml_peer) { + wpa_printf(MSG_DEBUG, "EPPKE: Include Multi Link Element"); + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 10); + wpabuf_put_u8(buf, WLAN_EID_EXT_MULTI_LINK); + wpabuf_put_le16(buf, MULTI_LINK_CONTROL_TYPE_BASIC); + wpabuf_put_u8(buf, ETH_ALEN + 1); + wpabuf_put_data(buf, pasn->own_addr, ETH_ALEN); + } +#endif /* CONFIG_ENC_ASSOC */ + /* Add the MIC */ mic_len = pasn_mic_len(pasn->hash_alg); wpabuf_put_u8(buf, WLAN_EID_MIC); diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 3510faf74..107fd2d15 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -715,10 +715,12 @@ static void wpas_eppke_initialize(struct wpa_supplicant *wpa_s, struct wpa_bss * pasn->auth_alg = WLAN_AUTH_EPPKE; os_memcpy(pasn->own_addr, wpa_s->own_addr, ETH_ALEN); - if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) + if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) { + pasn->is_ml_peer = true; os_memcpy(pasn->peer_addr, wpa_s->ap_mld_addr, ETH_ALEN); - else + } else { os_memcpy(pasn->peer_addr, bss->bssid, ETH_ALEN); + } os_memcpy(pasn->bssid, bss->bssid, ETH_ALEN); wpa_printf(MSG_DEBUG, -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:30 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:30 +0530 Subject: [PATCH v2 20/28] PASN: Add support for MIC computation for M3 frame for SME-in-Userspace In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-21-ainy.kumari@oss.qualcomm.com> For SME-in-Userspace scenarios, MIC is computed on frame3 body, hash of frame1 body, but frame1 and frame3 contain auth_data starting from transaction number to comply with mac80211 requirement, hence prepend auth algorithm to frame body for preparing MIC. Signed-off-by: Ainy Kumari --- src/pasn/pasn_initiator.c | 53 ++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/src/pasn/pasn_initiator.c b/src/pasn/pasn_initiator.c index 30f42fb65..b7ed5ee9d 100644 --- a/src/pasn/pasn_initiator.c +++ b/src/pasn/pasn_initiator.c @@ -582,6 +582,18 @@ static u8 wpas_pasn_get_wrapped_data_format(struct pasn_data *pasn) } +static u8 * pasn_prepend_auth_alg(u16 auth_alg, const u8 *buf, size_t len) +{ + u8 *new_buf = os_malloc(len + 2); + if (!new_buf) + return NULL; + + os_memcpy(new_buf, &auth_alg, 2); + os_memcpy(new_buf + 2, buf, len); + return new_buf; +} + + struct wpabuf *wpas_pasn_build_auth_1(struct pasn_data *pasn, const struct wpabuf *comeback, bool verify, bool is_sme_drv) @@ -589,6 +601,9 @@ struct wpabuf *wpas_pasn_build_auth_1(struct pasn_data *pasn, struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL; const u8 *pmkid; u8 wrapped_data; + const u8 *data; + size_t data_len; + u8 *copy = NULL; wpa_printf(MSG_DEBUG, "PASN: Building frame 1"); @@ -676,9 +691,23 @@ struct wpabuf *wpas_pasn_build_auth_1(struct pasn_data *pasn, } #endif /* CONFIG_ENC_ASSOC */ + if (!is_sme_drv) { + copy = pasn_prepend_auth_alg(pasn->auth_alg, + wpabuf_head_u8(buf), + wpabuf_len(buf)); + if (!copy) + goto fail; + data = copy; + data_len = wpabuf_len(buf) + 2; + os_free(copy); + } else { + data = wpabuf_head_u8(buf) + IEEE80211_HDRLEN; + data_len = wpabuf_len(buf) - IEEE80211_HDRLEN; + } + wpabuf_free(pasn->auth1); - pasn->auth1 = wpabuf_alloc_copy(wpabuf_head_u8(buf) + IEEE80211_HDRLEN, - wpabuf_len(buf) - IEEE80211_HDRLEN); + pasn->auth1 = wpabuf_alloc_copy(data, data_len); + if (!pasn->auth1) { wpa_printf(MSG_DEBUG, "PASN: Failed to store a copy of Auth1"); goto fail; @@ -696,6 +725,7 @@ fail: wpabuf_free(wrapped_data_buf); wpabuf_free(pubkey); wpabuf_free(buf); + os_free(copy); return NULL; } @@ -708,7 +738,7 @@ struct wpabuf *wpas_pasn_build_auth_3(struct pasn_data *pasn, u8 mic_len; size_t data_len; const u8 *data; - u8 *ptr; + u8 *ptr, *copy = NULL; u8 wrapped_data; int ret; u8 hash[SHA512_MAC_LEN]; @@ -770,11 +800,21 @@ struct wpabuf *wpas_pasn_build_auth_3(struct pasn_data *pasn, wpabuf_put_u8(buf, WLAN_EID_MIC); wpabuf_put_u8(buf, mic_len); ptr = wpabuf_put(buf, mic_len); - os_memset(ptr, 0, mic_len); - data = wpabuf_head_u8(buf) + IEEE80211_HDRLEN; - data_len = wpabuf_len(buf) - IEEE80211_HDRLEN; + if (!is_sme_drv) { + copy = pasn_prepend_auth_alg(pasn->auth_alg, + wpabuf_head_u8(buf), wpabuf_len(buf)); + if (!copy) + goto fail; + data = copy; + data_len = wpabuf_len(buf) + 2; + os_free(copy); + } else { + data = wpabuf_head_u8(buf) + IEEE80211_HDRLEN; + data_len = wpabuf_len(buf) - IEEE80211_HDRLEN; + } + if (!pasn->auth1 || pasn_auth_frame_hash(pasn->hash_alg, wpabuf_head(pasn->auth1), @@ -808,6 +848,7 @@ fail: pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE; wpabuf_free(wrapped_data_buf); wpabuf_free(buf); + os_free(copy); return NULL; } -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:34 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:34 +0530 Subject: [PATCH v2 24/28] EPPKE: Skip 4-Way handshake and authorize supplicant port on association In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-25-ainy.kumari@oss.qualcomm.com> For EPPKE authentication, PTK is derived during authentication frame exchange. Skip EAPOL 4-Way handshake and move supplicant state to WPA_CONNECTED after association. Update state handling to authorize the port and ensure proper control for SME-in-Userspace scenarios. Signed-off-by: Ainy Kumari --- src/rsn_supp/wpa.c | 10 ++++++++++ src/rsn_supp/wpa.h | 6 ++++++ src/rsn_supp/wpa_i.h | 3 +++ wpa_supplicant/events.c | 12 ++++++++++++ wpa_supplicant/wpa_supplicant.c | 5 +++++ 5 files changed, 36 insertions(+) diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 01ca1679d..5f0b0e3d8 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -7076,6 +7076,16 @@ int wpa_fils_is_completed(struct wpa_sm *sm) } +int wpa_eppke_is_completed(struct wpa_sm *sm) +{ +#ifdef CONFIG_ENC_ASSOC + return sm && sm->eppke_completed; +#else /* CONFIG_ENC_ASSOC */ + return 0; +#endif /* CONFIG_ENC_ASSOC */ +} + + #ifdef CONFIG_OWE struct wpabuf * owe_build_assoc_req(struct wpa_sm *sm, u16 group) diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index acd905b71..46e03ad99 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -280,6 +280,7 @@ void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck, size_t ptk_kck_len, const u8 *ptk_kek, size_t ptk_kek_len); int wpa_fils_is_completed(struct wpa_sm *sm); +int wpa_eppke_is_completed(struct wpa_sm *sm); void wpa_sm_pmksa_cache_reconfig(struct wpa_sm *sm); int wpa_sm_set_mlo_params(struct wpa_sm *sm, const struct wpa_sm_mlo *mlo); void wpa_sm_set_driver_bss_selection(struct wpa_sm *sm, @@ -518,6 +519,11 @@ static inline int wpa_fils_is_completed(struct wpa_sm *sm) return 0; } +static inline int wpa_eppke_is_completed(struct wpa_sm *sm) +{ + return 0; +} + static inline void wpa_sm_pmksa_cache_reconfig(struct wpa_sm *sm) { } diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index d0c3541b5..c36a1f3ae 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -223,6 +223,9 @@ struct wpa_sm { u8 fils_ft[FILS_FT_MAX_LEN]; size_t fils_ft_len; #endif /* CONFIG_FILS */ +#ifdef CONFIG_ENC_ASSOC + unsigned int eppke_completed:1; +#endif /* CONFIG_ENC_ASSOC */ #ifdef CONFIG_OWE struct crypto_ecdh *owe_ecdh; diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 3083271f8..ba79f7f6d 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -4409,6 +4409,9 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, if (!ft_completed) ft_completed = wpa_fils_is_completed(wpa_s->wpa); + if (!ft_completed) + ft_completed = wpa_eppke_is_completed(wpa_s->wpa); + wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED); if (!ether_addr_equal(bssid, wpa_s->bssid)) { if (os_reltime_initialized(&wpa_s->session_start)) { @@ -4432,6 +4435,7 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, if (wpa_supplicant_dynamic_keys(wpa_s) && !ft_completed) { wpa_clear_keys(wpa_s, bssid); } + if (wpa_supplicant_select_config(wpa_s, data) < 0) { wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); @@ -4467,6 +4471,14 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, #endif /* CONFIG_SME */ wpa_msg(wpa_s, MSG_INFO, "Associated with " MACSTR, MAC2STR(bssid)); +#ifdef CONFIG_SME +#ifdef CONFIG_ENC_ASSOC + if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_EPPKE) { + data->assoc_info.authorized = true; + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); + } +#endif /* CONFIG_ENC_ASSOC */ +#endif if (wpa_s->current_ssid) { /* When using scanning (ap_scan=1), SIM PC/SC interface can be * initialized before association, but for other modes, diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 70c92ea00..1bd43a0bf 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -1216,6 +1216,11 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, MAC2STR(wpa_s->ap_mld_addr)); #ifdef CONFIG_SME +#ifdef CONFIG_ENC_ASSOC + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && + wpa_auth_alg_eppke(wpa_s->sme.auth_alg)) + wpa_drv_set_supp_port(wpa_s, 1); +#endif /* CONFIG_ENC_ASSOC */ if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && wpa_auth_alg_fils(wpa_s->sme.auth_alg)) fils_hlp_sent = 1; -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:22 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:22 +0530 Subject: [PATCH v2 12/28] EPPKE: Extend existing PASN APIs for EPPKE Authentication In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-13-ainy.kumari@oss.qualcomm.com> From: Sai Pratyusha Magam As per IEEE P802.11bi/D2.0, 12.16.9.3.2 (EPPKE Frame Construction and Processing), PASN frame construction and processing apply for EPPKE authentication with few differences. So Extend the existing PASN APIs to accommodate EPPKE authentication This change adds following extensions for AP Responder: -Skip sta object deletion after Authentication frame 3 processing -Bypass the vendor interface to set keys to driver (QCA_NL80211_VENDOR_SUBCMD_SECURE_RANGING_CONTEXT). -NL80211_CMD_NEW_KEY will be used to set keys to driver for EPPK initiated link after processing of EPPKE Authentication frame 1 -For MLO Association: Provision PMK to be cached in ML PMK cache Include Basic MLE in Authentication frame 2 To support the EPPKE flow, extend pasn_data structure to hold: AP MLD Address ML STA indication Signed-off-by: Sai Pratyusha Magam Signed-off-by: Rohan Dutta --- hostapd/Makefile | 5 +++ hostapd/defconfig | 3 ++ src/ap/ieee802_11.c | 77 +++++++++++++++++++++++++++++++++------ src/ap/ieee802_11_eht.c | 1 + src/ap/sta_info.h | 4 +- src/ap/wpa_auth.c | 4 +- src/ap/wpa_auth.h | 2 +- src/pasn/pasn_common.c | 10 +++++ src/pasn/pasn_common.h | 3 ++ src/pasn/pasn_responder.c | 38 ++++++++++++++++++- 10 files changed, 131 insertions(+), 16 deletions(-) diff --git a/hostapd/Makefile b/hostapd/Makefile index 70029bb0d..b9992530e 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -615,6 +615,11 @@ OBJS += ../src/ap/nan_usd_ap.o CFLAGS += -DCONFIG_NAN_USD endif +ifdef CONFIG_ENC_ASSOC +CONFIG_PASN=y +CFLAGS += -DCONFIG_ENC_ASSOC +endif + ifdef CONFIG_PASN CFLAGS += -DCONFIG_PASN CFLAGS += -DCONFIG_PTKSA_CACHE diff --git a/hostapd/defconfig b/hostapd/defconfig index 2dd132831..dddd75d95 100644 --- a/hostapd/defconfig +++ b/hostapd/defconfig @@ -418,3 +418,6 @@ CONFIG_DPP2=y # Wi-Fi Aware unsynchronized service discovery (NAN USD) #CONFIG_NAN_USD=y + +# Feature support based on IEEE P802.11bi/D2.0 +#CONFIG_ENC_ASSOC=y diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index c1ff3fa8a..2e0b9eb95 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -3001,6 +3001,10 @@ static void hapd_initialize_pasn(struct hostapd_data *hapd, pasn_register_callbacks(pasn, hapd, hapd_pasn_send_mlme, NULL); pasn_set_bssid(pasn, hapd->own_addr); pasn_set_own_addr(pasn, hapd->own_addr); +#if defined(CONFIG_IEEE80211BE) && defined(CONFIG_ENC_ASSOC) + if (hapd->conf->mld_ap) + pasn_set_own_mld_addr(pasn, hapd->mld->mld_addr); +#endif /* CONFIG_IEEE80211BE && CONFIG_ENC_ASSOC */ pasn_set_peer_addr(pasn, sta->addr); pasn_set_wpa_key_mgmt(pasn, hapd->conf->wpa_key_mgmt); pasn_set_rsn_pairwise(pasn, hapd->conf->rsn_pairwise); @@ -3021,8 +3025,15 @@ static void hapd_initialize_pasn(struct hostapd_data *hapd, pasn->rsn_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &pasn->rsn_ie_len); pasn_set_rsnxe_ie(pasn, hostapd_wpa_ie(hapd, WLAN_EID_RSNX)); pasn->disable_pmksa_caching = hapd->conf->disable_pmksa_caching; +#ifdef CONFIG_ENC_ASSOC pasn_set_responder_pmksa(pasn, - wpa_auth_get_pmksa_cache(hapd->wpa_auth)); + wpa_auth_get_pmksa_cache(hapd->wpa_auth, + (sta->epp_sta ? + ap_sta_is_mld(hapd, sta) : false))); +#else + pasn_set_responder_pmksa(pasn, + wpa_auth_get_pmksa_cache(hapd->wpa_auth, false)); +#endif /* CONFIG_ENC_ASSOC */ pasn->comeback_after = hapd->conf->pasn_comeback_after; pasn->comeback_idx = hapd->comeback_idx; @@ -3098,7 +3109,12 @@ static void hapd_pasn_update_params(struct hostapd_data *hapd, wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher"); return; } - +#ifdef CONFIG_ENC_ASSOC + pasn->auth_alg = mgmt->u.auth.auth_alg; +#ifdef CONFIG_IEEE80211BE + pasn->is_ml_peer = sta->mld_info.mld_sta; +#endif +#endif /* CONFIG_ENC_ASSOC */ pasn_set_akmp(pasn, rsn_data.key_mgmt); pasn_set_cipher(pasn, rsn_data.pairwise_cipher); @@ -3237,19 +3253,31 @@ static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta, return; } - if (handle_auth_pasn_3(sta->pasn, hapd->own_addr, - sta->addr, mgmt, len) == 0) { + if ((ret = handle_auth_pasn_3(sta->pasn, hapd->own_addr, + sta->addr, mgmt, len)) == 0) { +#ifdef CONFIG_ENC_ASSOC + if (sta->epp_sta) { + sta->auth_alg = WLAN_AUTH_EPPKE; + sta->flags |= WLAN_STA_AUTH; + } +#endif /* CONFIG_ENC_ASSOC */ ptksa_cache_add(hapd->ptksa, hapd->own_addr, sta->addr, pasn_get_cipher(sta->pasn), 43200, pasn_get_ptk(sta->pasn), NULL, NULL, pasn_get_akmp(sta->pasn)); +#ifdef CONFIG_ENC_ASSOC + if (!sta->epp_sta) +#endif /* CONFIG_ENC_ASSOC */ + pasn_set_keys_from_cache(hapd, hapd->own_addr, + sta->addr, + pasn_get_cipher(sta->pasn), + pasn_get_akmp(sta->pasn)); + } +#ifdef CONFIG_ENC_ASSOC + if (!sta->epp_sta || (sta->epp_sta && ret < 0)) +#endif /* CONFIG_ENC_ASSOC */ + ap_free_sta(hapd, sta); - pasn_set_keys_from_cache(hapd, hapd->own_addr, - sta->addr, - pasn_get_cipher(sta->pasn), - pasn_get_akmp(sta->pasn)); - } - ap_free_sta(hapd, sta); } else { wpa_printf(MSG_DEBUG, "PASN: Invalid transaction %u - ignore", trans_seq); @@ -3339,6 +3367,17 @@ static void handle_auth(struct hostapd_data *hapd, } #endif /* CONFIG_NO_RC4 */ +#ifdef CONFIG_ENC_ASSOC + if (auth_alg == WLAN_AUTH_EPPKE && + !hapd->conf->assoc_frame_encryption) { + wpa_printf(MSG_INFO, + "Unsupported EPPKE authentication algorithm (%d)", + auth_alg); + resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; + goto fail; + } +#endif /* CONFIG_ENC_ASSOC */ + if (hapd->tkip_countermeasures) { wpa_printf(MSG_DEBUG, "Ongoing TKIP countermeasures (Michael MIC failure) - reject authentication"); @@ -3371,6 +3410,11 @@ static void handle_auth(struct hostapd_data *hapd, (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PASN) && auth_alg == WLAN_AUTH_PASN) || #endif /* CONFIG_PASN */ +#ifdef CONFIG_ENC_ASSOC + (hapd->conf->wpa && + (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_EPPKE) && + auth_alg == WLAN_AUTH_EPPKE) || +#endif /* CONFIG_ENC_ASSOC */ ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) && auth_alg == WLAN_AUTH_SHARED_KEY))) { wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)", @@ -3388,6 +3432,9 @@ static void handle_auth(struct hostapd_data *hapd, (auth_alg == WLAN_AUTH_PASN && auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH3) || #endif /* CONFIG_PASN */ +#ifdef CONFIG_ENC_ASSOC + (auth_alg == WLAN_AUTH_EPPKE && auth_transaction == 3) || +#endif /* CONFIG_ENC_ASSOC */ (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) { wpa_printf(MSG_INFO, "Unknown authentication transaction number (%d)", auth_transaction); @@ -3565,7 +3612,12 @@ static void handle_auth(struct hostapd_data *hapd, goto fail; } } - +#ifdef CONFIG_ENC_ASSOC + if (auth_alg == WLAN_AUTH_EPPKE) { + wpa_printf(MSG_DEBUG, "Mark the station as an EPP Peer"); + sta->epp_sta = true; + } +#endif /* CONFIG_ENC_ASSOC */ #ifdef CONFIG_IEEE80211BE /* Set the non-AP MLD information based on the initial Authentication * frame. Once the STA entry has been added to the driver, the driver @@ -3730,6 +3782,9 @@ static void handle_auth(struct hostapd_data *hapd, handle_auth_fils_finish); return; #endif /* CONFIG_FILS */ +#ifdef CONFIG_ENC_ASSOC + case WLAN_AUTH_EPPKE: +#endif /* CONFIG_ENC_ASSOC */ #ifdef CONFIG_PASN case WLAN_AUTH_PASN: handle_auth_pasn(hapd, sta, mgmt, len, auth_transaction, diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c index feeb77ebe..cb63a5b22 100644 --- a/src/ap/ieee802_11_eht.c +++ b/src/ap/ieee802_11_eht.c @@ -1060,6 +1060,7 @@ static const u8 * auth_skip_fixed_fields(struct hostapd_data *hapd, * (Presence of fields and elements in Authentications frames) */ switch (auth_alg) { case WLAN_AUTH_OPEN: + case WLAN_AUTH_EPPKE: return pos; #ifdef CONFIG_SAE case WLAN_AUTH_SAE: diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index 3fb97edd5..18218b949 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -99,7 +99,9 @@ struct sta_info { u8 supported_rates[WLAN_SUPP_RATES_MAX]; int supported_rates_len; u8 qosinfo; /* Valid when WLAN_STA_WMM is set */ - +#ifdef CONFIG_ENC_ASSOC + bool epp_sta; /* Indicates if the station is an EPP peer */ +#endif /* CONFIG_ENC_ASSOC */ #ifdef CONFIG_MESH enum mesh_plink_state plink_state; u16 peer_lid; diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 620e55387..040efd4a4 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -6895,11 +6895,11 @@ int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth, struct rsn_pmksa_cache * -wpa_auth_get_pmksa_cache(struct wpa_authenticator *wpa_auth) +wpa_auth_get_pmksa_cache(struct wpa_authenticator *wpa_auth, bool is_ml) { if (!wpa_auth || !wpa_auth->pmksa) return NULL; - return wpa_auth->pmksa; + return is_ml ? wpa_auth->ml_pmksa : wpa_auth->pmksa; } diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 36954a19a..4883e2db9 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -541,7 +541,7 @@ wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk, int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth, struct rsn_pmksa_cache_entry *entry); struct rsn_pmksa_cache * -wpa_auth_get_pmksa_cache(struct wpa_authenticator *wpa_auth); +wpa_auth_get_pmksa_cache(struct wpa_authenticator *wpa_auth, bool is_ml); struct rsn_pmksa_cache_entry * wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, const u8 *pmkid); diff --git a/src/pasn/pasn_common.c b/src/pasn/pasn_common.c index 11916bd18..bd197bebd 100644 --- a/src/pasn/pasn_common.c +++ b/src/pasn/pasn_common.c @@ -100,6 +100,16 @@ void pasn_set_own_addr(struct pasn_data *pasn, const u8 *addr) } +#ifdef CONFIG_ENC_ASSOC +void pasn_set_own_mld_addr(struct pasn_data *pasn, const u8 *addr) +{ + if (!pasn || !addr) + return; + os_memcpy(pasn->mld_addr, addr, ETH_ALEN); +} +#endif /* CONFIG_ENC_ASSOC */ + + void pasn_set_peer_addr(struct pasn_data *pasn, const u8 *addr) { if (!pasn || !addr) diff --git a/src/pasn/pasn_common.h b/src/pasn/pasn_common.h index e71393496..f5e22c1d9 100644 --- a/src/pasn/pasn_common.h +++ b/src/pasn/pasn_common.h @@ -50,6 +50,8 @@ struct pasn_data { size_t kdk_len; void *cb_ctx; unsigned int auth_alg; + u8 mld_addr[ETH_ALEN]; + bool is_ml_peer; #ifdef CONFIG_SAE struct sae_pt *pt; @@ -212,6 +214,7 @@ void pasn_disable_kdk_derivation(struct pasn_data *pasn); void pasn_set_akmp(struct pasn_data *pasn, int akmp); void pasn_set_cipher(struct pasn_data *pasn, int cipher); void pasn_set_own_addr(struct pasn_data *pasn, const u8 *addr); +void pasn_set_own_mld_addr(struct pasn_data *pasn, const u8 *addr); void pasn_set_peer_addr(struct pasn_data *pasn, const u8 *addr); void pasn_set_bssid(struct pasn_data *pasn, const u8 *addr); void pasn_set_initiator_pmksa(struct pasn_data *pasn, diff --git a/src/pasn/pasn_responder.c b/src/pasn/pasn_responder.c index 95d7b2e3c..8ad6ff03c 100644 --- a/src/pasn/pasn_responder.c +++ b/src/pasn/pasn_responder.c @@ -147,7 +147,10 @@ static int pasn_wd_handle_sae_commit(struct pasn_data *pasn, wpa_printf(MSG_DEBUG, "PASN: No SAE PT found"); return -1; } - +#ifdef CONFIG_ENC_ASSOC + if (pasn->auth_alg == WLAN_AUTH_EPPKE && pasn->is_ml_peer) + own_addr = pasn->mld_addr; +#endif ret = sae_prepare_commit_pt(&pasn->sae, pasn->pt, own_addr, peer_addr, NULL, NULL); if (ret) { @@ -575,6 +578,17 @@ int handle_auth_pasn_resp(struct pasn_data *pasn, const u8 *own_addr, pasn->prepare_data_element(pasn->cb_ctx, peer_addr); wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len); +#ifdef CONFIG_ENC_ASSOC + if (pasn->auth_alg == WLAN_AUTH_EPPKE && pasn->is_ml_peer) { + wpa_printf(MSG_DEBUG, "EPPKE: Add Multi Link IE"); + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 10); + wpabuf_put_u8(buf, WLAN_EID_EXT_MULTI_LINK); + wpabuf_put_le16(buf, MULTI_LINK_CONTROL_TYPE_BASIC); + wpabuf_put_u8(buf, ETH_ALEN + 1); + wpabuf_put_data(buf, pasn->mld_addr, ETH_ALEN); + } +#endif /* CONFIG_ENC_ASSOC */ /* Add the mic */ mic_len = pasn_mic_len(pasn->hash_alg); @@ -728,6 +742,18 @@ int handle_auth_pasn_1(struct pasn_data *pasn, goto send_resp; } +#ifdef CONFIG_ENC_ASSOC + /* IEEE802.11 bi D2.0 12.16.9 Enhanced Data Privacy Key Exchange + * allows the use of SAE/SAE-EXT/FT-SAE/FT-SAE-EXT as base AKMP + */ + if (mgmt->u.auth.auth_alg == WLAN_AUTH_EPPKE && + !wpa_key_mgmt_sae(rsn_data.key_mgmt)) { + wpa_printf(MSG_DEBUG, "EPPKE: Invalid Base AKM"); + status = WLAN_STATUS_INVALID_RSNIE; + goto send_resp; + } +#endif /* CONFIG_ENC_ASSOC */ + if (!(rsn_data.key_mgmt & pasn->wpa_key_mgmt) || !(rsn_data.pairwise_cipher & pasn->rsn_pairwise)) { wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher"); @@ -758,6 +784,16 @@ int handle_auth_pasn_1(struct pasn_data *pasn, wpa_printf(MSG_DEBUG, "PASN: kek_len=%zu", pasn->kek_len); + if (mgmt->u.auth.auth_alg == WLAN_AUTH_EPPKE && + !ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len, + WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION)) { + wpa_printf(MSG_DEBUG, + "EPPKE: Missing (Re)Association Request/Response frame " + "enryption support in RSNXE"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + if (!elems.pasn_params || !elems.pasn_params_len) { wpa_printf(MSG_DEBUG, "PASN: No PASN Parameters element found"); -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:33 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:33 +0530 Subject: [PATCH v2 23/28] EPPKE: Update RSNE construction and validation per IEEE P802.11bi/D2.0 In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-24-ainy.kumari@oss.qualcomm.com> Update existing PASN APIs for RSNE generation and validation logic to align with IEEE 802.11bi/D2.0, section 12.16.9.3.2 to support EPPKE authentication. Below are the key changes: - Add wpa_pick_group_mgmt_cipher() to select group management cipher. - Extend wpa_pasn_add_rsne() and wpa_pasn_validate_rsne() to support Group Data and Group Management Cipher Suites inclusion for EPPKE. - Pass group cipher parameters in PASN initiator/responder mode. - Skip MFPR bit setting in RSN Capabilities for EPPKE. - Ensure RSNE reflects correct capabilities and cipher suites based on auth type Signed-off-by: Sai Pratyusha Magam Signed-off-by: Ainy Kumari --- src/ap/ieee802_11.c | 2 +- src/common/wpa_common.c | 70 +++++++++++++++++++++++++++++---------- src/common/wpa_common.h | 7 ++-- src/pasn/pasn_common.h | 2 ++ src/pasn/pasn_initiator.c | 14 +++++--- src/pasn/pasn_responder.c | 22 ++++++++---- wpa_supplicant/sme.c | 7 ++++ 7 files changed, 93 insertions(+), 31 deletions(-) diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 27491a1c0..ec65700ec 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -2927,7 +2927,7 @@ static int pasn_wd_handle_fils(struct hostapd_data *hapd, struct sta_info *sta, return -1; } - ret = wpa_pasn_validate_rsne(&rsne_data); + ret = wpa_pasn_validate_rsne(&rsne_data, false); if (ret) { wpa_printf(MSG_DEBUG, "PASN: FILS: Failed validating RSNE"); return -1; diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index 5effba077..cdde7ec02 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -3327,6 +3327,20 @@ int wpa_pick_group_cipher(int ciphers) } +int wpa_pick_group_mgmt_cipher(int ciphers) +{ + if (ciphers & WPA_CIPHER_AES_128_CMAC) + return WPA_CIPHER_AES_128_CMAC; + if (ciphers & WPA_CIPHER_BIP_GMAC_128) + return WPA_CIPHER_BIP_GMAC_128; + if (ciphers & WPA_CIPHER_BIP_GMAC_256) + return WPA_CIPHER_BIP_GMAC_256; + if (ciphers & WPA_CIPHER_BIP_CMAC_256) + return WPA_CIPHER_BIP_CMAC_256; + return -1; +} + + int wpa_parse_cipher(const char *value) { int val = 0, last; @@ -4002,13 +4016,15 @@ void wpa_pasn_build_auth_header(struct wpabuf *buf, const u8 *bssid, /* - * wpa_pasn_add_rsne - Add an RSNE for PASN authentication + * wpa_pasn_add_rsne - Add an RSNE for PASN/EPPKE authentication * @buf: Buffer in which the IE will be added * @pmkid: Optional PMKID. Can be NULL. * @akmp: Authentication and key management protocol * @cipher: The cipher suite + * @is_eppke: EPPKE Authentication */ -int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, int akmp, int cipher) +int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, int akmp, int cipher, + bool is_eppke, int group_cipher, int group_mgmt_cipher) { struct rsn_ie_hdr *hdr; u32 suite; @@ -4030,8 +4046,13 @@ int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, int akmp, int cipher) WPA_PUT_LE16(hdr->version, RSN_VERSION); pos = (u8 *) (hdr + 1); - /* Group addressed data is not allowed */ - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); + if (is_eppke) { + /* EPPKE: Group addressed data is allowed */ + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher)); + } else { + /* PASN: Group addressed data is not allowed */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); + } pos += RSN_SELECTOR_LEN; /* Add the pairwise cipher */ @@ -4082,8 +4103,13 @@ int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, int akmp, int cipher) } pos += RSN_SELECTOR_LEN; - /* RSN Capabilities: PASN mandates both MFP capable and required */ - capab = WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR; + if (is_eppke) { + /* RSN Capabilities: EPPKE does not mandate setting MFPR to 1 */ + capab = WPA_CAPABILITY_MFPC; + } else { + /* RSN Capabilities: PASN mandates both MFP capable and required */ + capab = WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR; + } WPA_PUT_LE16(pos, capab); pos += 2; @@ -4099,9 +4125,13 @@ int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, int akmp, int cipher) pos += 2; } - /* Group addressed management is not allowed */ - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); - + if (is_eppke) { + /* EPPKE: Group addressed management is allowed */ + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, group_mgmt_cipher)); + } else { + /* PASN: Group addressed management is not allowed */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); + } return 0; } @@ -4239,23 +4269,29 @@ int wpa_pasn_add_wrapped_data(struct wpabuf *buf, /* - * wpa_pasn_validate_rsne - Validate PSAN specific data of RSNE + * wpa_pasn_validate_rsne - Validate PASN/EPPKE specific data of RSNE * @data: Parsed representation of an RSNE + * @is_eppke: EPPKE Authentication * Returns -1 for invalid data; otherwise 0 */ -int wpa_pasn_validate_rsne(const struct wpa_ie_data *data) +int wpa_pasn_validate_rsne(const struct wpa_ie_data *data, bool is_eppke) { - u16 capab = WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR; + u16 capab = WPA_CAPABILITY_MFPC; + + if (!is_eppke) + capab |= WPA_CAPABILITY_MFPR; if (data->proto != WPA_PROTO_RSN) return -1; if ((data->capabilities & capab) != capab) { - wpa_printf(MSG_DEBUG, "PASN: Invalid RSNE capabilities"); + wpa_printf(MSG_DEBUG, "%s: Invalid RSNE capabilities", + is_eppke ? "EPPKE" : "PASN"); return -1; } - if (!data->has_group || data->group_cipher != WPA_CIPHER_GTK_NOT_USED) { + if (!data->has_group || + (!is_eppke && data->group_cipher != WPA_CIPHER_GTK_NOT_USED)) { wpa_printf(MSG_DEBUG, "PASN: Invalid group data cipher"); return -1; } @@ -4286,12 +4322,12 @@ int wpa_pasn_validate_rsne(const struct wpa_ie_data *data) case WPA_KEY_MGMT_PASN: break; default: - wpa_printf(MSG_ERROR, "PASN: invalid key_mgmt: 0x%0x", - data->key_mgmt); + wpa_printf(MSG_ERROR, "%s: invalid key_mgmt: 0x%0x", + is_eppke ? "EPPKE" : "PASN", data->key_mgmt); return -1; } - if (data->mgmt_group_cipher != WPA_CIPHER_GTK_NOT_USED) { + if (!is_eppke && (data->mgmt_group_cipher != WPA_CIPHER_GTK_NOT_USED)) { wpa_printf(MSG_DEBUG, "PASN: Invalid group mgmt cipher"); return -1; } diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index fcd7168e2..1419dced2 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -767,6 +767,7 @@ int rsn_cipher_put_suites(u8 *pos, int ciphers); int wpa_cipher_put_suites(u8 *pos, int ciphers); int wpa_pick_pairwise_cipher(int ciphers, int none_allowed); int wpa_pick_group_cipher(int ciphers); +int wpa_pick_group_mgmt_cipher(int ciphers); int wpa_parse_cipher(const char *value); int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim); int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise); @@ -799,8 +800,8 @@ void wpa_pasn_build_auth_header(struct wpabuf *buf, const u8 *bssid, const u8 *src, const u8 *dst, u8 trans_seq, u16 status, bool is_eppke); -int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, - int akmp, int cipher); +int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, int akmp, int cipher, + bool is_eppke, int group_cipher, int group_mgmt_cipher); void wpa_pasn_add_parameter_ie(struct wpabuf *buf, u16 pasn_group, u8 wrapped_data_format, @@ -810,7 +811,7 @@ void wpa_pasn_add_parameter_ie(struct wpabuf *buf, u16 pasn_group, int wpa_pasn_add_wrapped_data(struct wpabuf *buf, struct wpabuf *wrapped_data_buf); -int wpa_pasn_validate_rsne(const struct wpa_ie_data *data); +int wpa_pasn_validate_rsne(const struct wpa_ie_data *data, bool is_eppke); int wpa_pasn_parse_parameter_ie(const u8 *data, u8 len, bool from_ap, struct wpa_pasn_params_data *pasn_params); diff --git a/src/pasn/pasn_common.h b/src/pasn/pasn_common.h index cf9ef93b1..09b1fb717 100644 --- a/src/pasn/pasn_common.h +++ b/src/pasn/pasn_common.h @@ -52,6 +52,8 @@ struct pasn_data { unsigned int auth_alg; u8 mld_addr[ETH_ALEN]; bool is_ml_peer; + int group_cipher; + int group_mgmt_cipher; #ifdef CONFIG_SAE struct sae_pt *pt; diff --git a/src/pasn/pasn_initiator.c b/src/pasn/pasn_initiator.c index b7ed5ee9d..9253db943 100644 --- a/src/pasn/pasn_initiator.c +++ b/src/pasn/pasn_initiator.c @@ -297,7 +297,9 @@ static struct wpabuf * wpas_pasn_fils_build_auth(struct pasn_data *pasn) wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); /* Own RSNE */ - wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher); + wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher, + pasn->auth_alg == WLAN_AUTH_EPPKE, + pasn->group_cipher, pasn->group_mgmt_cipher); /* FILS Nonce */ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); @@ -423,7 +425,8 @@ static int wpas_pasn_wd_fils_rx(struct pasn_data *pasn, struct wpabuf *wd) return -1; } - ret = wpa_pasn_validate_rsne(&rsne_data); + ret = wpa_pasn_validate_rsne(&rsne_data, + pasn->auth_alg == WLAN_AUTH_EPPKE); if (ret) { wpa_printf(MSG_DEBUG, "PASN: FILS: Failed validating RSNE"); return -1; @@ -660,7 +663,9 @@ struct wpabuf *wpas_pasn_build_auth_1(struct pasn_data *pasn, wrapped_data_buf = wpas_pasn_get_wrapped_data(pasn); } - if (wpa_pasn_add_rsne(buf, pmkid, pasn->akmp, pasn->cipher) < 0) + if (wpa_pasn_add_rsne(buf, pmkid, pasn->akmp, pasn->cipher, + pasn->auth_alg == WLAN_AUTH_EPPKE, + pasn->group_cipher, pasn->group_mgmt_cipher) < 0) goto fail; if (!wrapped_data_buf) @@ -1320,7 +1325,8 @@ int wpas_parse_pasn_frame(struct pasn_data *pasn, u16 auth_type, goto fail; } - ret = wpa_pasn_validate_rsne(&rsn_data); + ret = wpa_pasn_validate_rsne(&rsn_data, + pasn->auth_alg == WLAN_AUTH_EPPKE); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE"); goto fail; diff --git a/src/pasn/pasn_responder.c b/src/pasn/pasn_responder.c index f7f1bc413..aebee493f 100644 --- a/src/pasn/pasn_responder.c +++ b/src/pasn/pasn_responder.c @@ -304,7 +304,9 @@ static struct wpabuf * pasn_get_fils_wd(struct pasn_data *pasn) wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); /* Own RSNE */ - wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher); + wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher, + pasn->auth_alg == WLAN_AUTH_EPPKE, + pasn->group_cipher, pasn->group_mgmt_cipher); /* FILS Nonce */ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); @@ -544,8 +546,9 @@ int handle_auth_pasn_resp(struct pasn_data *pasn, const u8 *own_addr, #endif /* CONFIG_FILS */ } - if (wpa_pasn_add_rsne(buf, pmkid, - pasn->akmp, pasn->cipher) < 0) + if (wpa_pasn_add_rsne(buf, pmkid, pasn->akmp, pasn->cipher, + pasn->auth_alg == WLAN_AUTH_EPPKE, + pasn->group_cipher, pasn->group_mgmt_cipher) < 0) goto fail; /* No need to derive PMK if PMKSA is given */ @@ -622,8 +625,10 @@ int handle_auth_pasn_resp(struct pasn_data *pasn, const u8 *own_addr, if (!rsn_buf) goto fail; - if (wpa_pasn_add_rsne(rsn_buf, pmkid, - pasn->akmp, pasn->cipher) < 0) + if (wpa_pasn_add_rsne(rsn_buf, pmkid, pasn->akmp, pasn->cipher, + pasn->auth_alg == WLAN_AUTH_EPPKE, + pasn->group_cipher, + pasn->group_mgmt_cipher) < 0) goto fail; rsn_ie = wpabuf_head_u8(rsn_buf); @@ -748,7 +753,8 @@ int handle_auth_pasn_1(struct pasn_data *pasn, goto send_resp; } - ret = wpa_pasn_validate_rsne(&rsn_data); + ret = wpa_pasn_validate_rsne(&rsn_data, + pasn->auth_alg == WLAN_AUTH_EPPKE); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE"); status = WLAN_STATUS_INVALID_RSNIE; @@ -776,6 +782,10 @@ int handle_auth_pasn_1(struct pasn_data *pasn, pasn->akmp = rsn_data.key_mgmt; pasn->cipher = rsn_data.pairwise_cipher; +#ifdef CONFIG_ENC_ASSOC + pasn->group_cipher = rsn_data.group_cipher; + pasn->group_mgmt_cipher = rsn_data.mgmt_group_cipher; +#endif /* CONFIG_ENC_ASSOC */ if (pasn->derive_kdk && ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len, diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 092ad6ff5..f1db461d2 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -631,6 +631,7 @@ static void wpas_eppke_initialize(struct wpa_supplicant *wpa_s, struct wpa_bss * u8 beacon_rsne_len, beacon_rsnxe_len; u32 capab = 0; int group; + int group_mgmt_cipher; pasn = &wpa_s->pasn; @@ -710,6 +711,12 @@ static void wpas_eppke_initialize(struct wpa_supplicant *wpa_s, struct wpa_bss * } pasn->akmp = wpa_s->key_mgmt; pasn->cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher, 1); + pasn->group_cipher = wpa_pick_group_cipher(ssid->group_cipher); + if (ssid->group_mgmt_cipher != 0) + group_mgmt_cipher = ssid->group_mgmt_cipher; + else + group_mgmt_cipher = WPA_CIPHER_AES_128_CMAC; + pasn->group_mgmt_cipher = wpa_pick_group_mgmt_cipher(group_mgmt_cipher); pasn->group = group; pasn->freq = bss->freq; pasn->auth_alg = WLAN_AUTH_EPPKE; -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:32 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:32 +0530 Subject: [PATCH v2 22/28] Add support for temporal key removal on association failure In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-23-ainy.kumari@oss.qualcomm.com> From: Kavita Kavita This change adds logic to remove the configured temporal key (TK) for Enhanced Privacy Protection Key Exchange (EPPKE) in the event of an association request and/or response failure. The removal is triggered immediately upon detection of association request/response failure. Signed-off-by: Kavita Kavita --- wpa_supplicant/events.c | 10 ++++++++++ wpa_supplicant/sme.c | 20 ++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index d831557b3..3083271f8 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -390,6 +390,16 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) } wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + +#ifdef CONFIG_ENC_ASSOC + /* Clear configured keys and PTKSA */ + + if (wpa_s->ptksa && + ptksa_cache_get(wpa_s->ptksa, wpa_s->bssid, WPA_CIPHER_NONE)) { + wpa_clear_keys(wpa_s, wpa_s->bssid); + ptksa_cache_flush(wpa_s->ptksa, wpa_s->bssid, WPA_CIPHER_NONE); + } +#endif bssid_changed = !is_zero_ether_addr(wpa_s->bssid); os_memset(wpa_s->bssid, 0, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index eaed91c1c..092ad6ff5 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -2997,6 +2997,17 @@ mscs_fail: wpas_connection_failed(wpa_s, wpa_s->pending_bssid, NULL); wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); +#ifdef CONFIG_ENC_ASSOC + /* Clear configured keys and PTKSA */ + + if (wpa_s->ptksa && ptksa_cache_get(wpa_s->ptksa, + wpa_s->bssid, + WPA_CIPHER_NONE)) { + wpa_clear_keys(wpa_s, wpa_s->bssid); + ptksa_cache_flush(wpa_s->ptksa, wpa_s->bssid, + WPA_CIPHER_NONE); + } +#endif os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); } return; @@ -3059,6 +3070,15 @@ static void sme_deauth(struct wpa_supplicant *wpa_s, const u8 **link_bssids) wpas_connection_failed(wpa_s, wpa_s->pending_bssid, link_bssids); wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); +#ifdef CONFIG_ENC_ASSOC + /* Clear configured keys and PTKSA */ + + if (wpa_s->ptksa && + ptksa_cache_get(wpa_s->ptksa, wpa_s->bssid, WPA_CIPHER_NONE)) { + wpa_clear_keys(wpa_s, wpa_s->bssid); + ptksa_cache_flush(wpa_s->ptksa, wpa_s->bssid, WPA_CIPHER_NONE); + } +#endif os_memset(wpa_s->bssid, 0, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); if (bssid_changed) -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:31 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:31 +0530 Subject: [PATCH v2 21/28] sme: Add support to install temporal key for EPPKE Authentication Protocol In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-22-ainy.kumari@oss.qualcomm.com> From: Kavita Kavita Add support to install the temporal key for Enhanced Privacy Protection Key Exchange (EPPKE) as specified in section 12.16.9 of IEEE P802.11bi/D2.0 after authentication completes. This commit add support to configure temporal key TK with the driver immediately after authentication completes. Signed-off-by: Kavita Kavita Signed-off-by: Ainy Kumari --- wpa_supplicant/sme.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 107fd2d15..eaed91c1c 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -2242,6 +2242,9 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) struct pasn_data *pasn = &wpa_s->pasn; struct wpa_pasn_params_data pasn_params; int res; + enum wpa_alg alg; + u8 zero[WPA_TK_MAX_LEN] = {0}; + struct ptksa_cache_entry *entry; res = wpas_parse_pasn_frame(pasn, data->auth.auth_type, data->auth.auth_transaction, @@ -2266,6 +2269,14 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) sme_send_authentication(wpa_s, wpa_s->current_bss, wpa_s->current_ssid, 0); + + alg = wpa_cipher_to_alg(pasn_get_cipher(pasn)); + entry = ptksa_cache_get(wpa_s->ptksa, pasn->peer_addr, + pasn_get_cipher(pasn)); + + wpa_drv_set_key(wpa_s, -1, alg, pasn->peer_addr, 0, 1, + zero, 6, entry->ptk.tk, entry->ptk.tk_len, + KEY_FLAG_PAIRWISE_RX_TX); } #endif /* CONFIG_ENC_ASSOC */ #ifdef CONFIG_SAE -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:35 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:35 +0530 Subject: [PATCH v2 25/28] EPPKE: Retrieve KCK/KEK from PTKSA and install group keys for GTK rekey In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-26-ainy.kumari@oss.qualcomm.com> This change adds support for: - Retrieving KCK (Key Confirmation Key) and KEK (Key Encryption Key) from PTKSA cache and storing them in wpa_sm for use during GTK rekey operations. - Installing GTK and related group keys to the driver as part of EPPKE-based association flow. Signed-off-by: Ainy Kumari --- src/rsn_supp/wpa.c | 169 +++++++++++++++++++++++++++++++++++++++- src/rsn_supp/wpa.h | 2 + wpa_supplicant/events.c | 32 ++++++++ 3 files changed, 201 insertions(+), 2 deletions(-) diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 5f0b0e3d8..865432545 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -3155,7 +3155,8 @@ static void wpa_supplicant_process_mlo_1_of_2(struct wpa_sm *sm, u8 i; struct wpa_eapol_ie_parse ie; - if (!sm->msg_3_of_4_ok && !wpa_fils_is_completed(sm)) { + if (!sm->msg_3_of_4_ok && !wpa_fils_is_completed(sm) && + !wpa_eppke_is_completed(sm)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "MLO RSN: Group Key Handshake started prior to completion of 4-way handshake"); goto failed; @@ -3391,7 +3392,8 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, struct wpa_eapol_ie_parse ie; u16 gtk_len; - if (!sm->msg_3_of_4_ok && !wpa_fils_is_completed(sm)) { + if (!sm->msg_3_of_4_ok && !wpa_fils_is_completed(sm) && + !wpa_eppke_is_completed(sm)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: Group Key Handshake started prior to completion of 4-way handshake"); goto failed; @@ -7086,6 +7088,169 @@ int wpa_eppke_is_completed(struct wpa_sm *sm) } +#ifdef CONFIG_ENC_ASSOC +int eppke_process_assoc_resp(struct wpa_sm *sm, u64 flags, int link_id, + const u8 *resp, size_t len) +{ + struct ieee802_11_elems elems; + struct wpa_gtk_data gd; + int maxkeylen; + struct wpa_eapol_ie_parse kde; + + if (!sm || !sm->ptk_set) { + wpa_printf(MSG_DEBUG, "EPPKE: No KEK available"); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "EPPKE: (Re)Association Response frame", + resp, len); + + if (ieee802_11_parse_elems(resp, len, &elems, 1) == ParseFailed) { + wpa_printf(MSG_DEBUG, + "EPPKE: Failed to parse decrypted elements"); + goto fail; + } + + if (!elems.rsn_ie) { + wpa_printf(MSG_DEBUG, + "EPPKE: No RSNE in (Re)Association Response"); + } else if (wpa_compare_rsn_ie(wpa_key_mgmt_sae(sm->key_mgmt), + sm->ap_rsn_ie, sm->ap_rsn_ie_len, + elems.rsn_ie - 2, elems.rsn_ie_len + 2)) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "EPPKE: RSNE mismatch between Beacon/Probe Response and (Re)Association Response"); + wpa_hexdump(MSG_DEBUG, "EPPKE: RSNE in Beacon/Probe Response", + sm->ap_rsn_ie, sm->ap_rsn_ie_len); + wpa_hexdump(MSG_DEBUG, "EPPKE: RSNE in (Re)Association Response", + elems.rsn_ie, elems.rsn_ie_len); + goto fail; + } + + if ((sm->ap_rsnxe && !elems.rsnxe) || + (!sm->ap_rsnxe && elems.rsnxe) || + (sm->ap_rsnxe && elems.rsnxe && sm->ap_rsnxe_len >= 2 && + (sm->ap_rsnxe_len != 2U + elems.rsnxe_len || + os_memcmp(sm->ap_rsnxe + 2, elems.rsnxe, sm->ap_rsnxe_len - 2) != + 0))) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "EPPKE: RSNXE mismatch between Beacon/Probe Response and (Re)Association Response"); + wpa_hexdump(MSG_INFO, "EPPKE: RSNXE in Beacon/Probe Response", + sm->ap_rsnxe, sm->ap_rsnxe_len); + wpa_hexdump(MSG_INFO, "EPPKE: RSNXE in (Re)Association Response", + elems.rsnxe, elems.rsnxe_len); + if (sm->assoc_rsnxe && sm->assoc_rsnxe_len) + goto fail; + } + + /* Key Delivery */ + if (!elems.key_delivery) { + wpa_printf(MSG_DEBUG, "EPPKE: No Key Delivery element"); + goto fail; + } + + /* Parse GTK and set the key to the driver */ + os_memset(&gd, 0, sizeof(gd)); + if (wpa_supplicant_parse_ies(elems.key_delivery + WPA_KEY_RSC_LEN, + elems.key_delivery_len - WPA_KEY_RSC_LEN, + &kde) < 0) { + wpa_printf(MSG_DEBUG, "EPPKE: Failed to parse KDEs"); + goto fail; + } + + if ((flags & WPA_DRIVER_FLAGS2_MLO) && link_id != -1) { + if (!kde.mlo_gtk[link_id]) { + wpa_printf(MSG_DEBUG, "EPPKE: No MLO GTK KDE"); + goto fail; + } + + maxkeylen = gd.gtk_len = kde.mlo_gtk_len[link_id] - 7; + + if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, + gd.gtk_len, maxkeylen, + &gd.key_rsc_len, &gd.alg)) + goto fail; + + + wpa_hexdump_key(MSG_DEBUG, "EPPKE: Received MLO GTK", + kde.mlo_gtk[link_id], kde.mlo_gtk_len[link_id]); + + gd.keyidx = kde.mlo_gtk[link_id][0] & 0x3; + gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm, + !!(kde.mlo_gtk[link_id][0] & BIT(2))); + + if (kde.mlo_gtk_len[link_id] - 7 > sizeof(gd.gtk)) { + wpa_printf(MSG_DEBUG, "EPPKE: Too long GTK in GTK KDE (len=%lu)", + (unsigned long) kde.mlo_gtk_len[link_id] - 2); + goto fail; + } + os_memcpy(gd.gtk, kde.mlo_gtk[link_id] + 7, kde.mlo_gtk_len[link_id] - 7); + + wpa_printf(MSG_DEBUG, "EPPKE: Set MLO GTK to driver"); + if (wpa_supplicant_install_mlo_gtk(sm, link_id, &gd, elems.key_delivery, 0) < 0) { + wpa_printf(MSG_DEBUG, "EPPKE: Failed to set MLO GTK"); + goto fail; + } + + if (_mlo_ieee80211w_set_keys(sm, link_id, &kde) < 0) { + wpa_printf(MSG_DEBUG, "EPPKE: Failed to set MLO IGTK"); + goto fail; + } + } else { + if (!kde.gtk) { + wpa_printf(MSG_DEBUG, "EPPKE: No GTK KDE"); + goto fail; + } + maxkeylen = gd.gtk_len = kde.gtk_len - 2; + if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, + gd.gtk_len, maxkeylen, + &gd.key_rsc_len, &gd.alg)) + goto fail; + + wpa_hexdump_key(MSG_DEBUG, "EPPKE: Received GTK", kde.gtk, kde.gtk_len); + gd.keyidx = kde.gtk[0] & 0x3; + gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm, + !!(kde.gtk[0] & BIT(2))); + if (kde.gtk_len - 2 > sizeof(gd.gtk)) { + wpa_printf(MSG_DEBUG, "EPPKE: Too long GTK in GTK KDE (len=%lu)", + (unsigned long) kde.gtk_len - 2); + goto fail; + } + os_memcpy(gd.gtk, kde.gtk + 2, kde.gtk_len - 2); + + wpa_printf(MSG_DEBUG, "EPPKE: Set GTK to driver"); + if (wpa_supplicant_install_gtk(sm, &gd, elems.key_delivery, 0) < 0) { + wpa_printf(MSG_DEBUG, "EPPKE: Failed to set GTK"); + goto fail; + } + + if (ieee80211w_set_keys(sm, &kde) < 0) { + wpa_printf(MSG_DEBUG, "EPPKE: Failed to set IGTK"); + goto fail; + } + } + + wpa_sm_store_ptk(sm, sm->bssid, sm->pairwise_cipher, + sm->dot11RSNAConfigPMKLifetime, &sm->ptk); + + wpa_sm_set_rekey_offload(sm); + + wpa_printf(MSG_DEBUG, "EPPKE: Auth+Assoc completed successfully"); + sm->eppke_completed = 1; + forced_memzero(&gd, sizeof(gd)); + + return 0; +fail: + forced_memzero(&gd, sizeof(gd)); + return -1; +} + + +void wpa_sm_set_reset_eppke_completed(struct wpa_sm *sm, int set) +{ + if (sm) + sm->eppke_completed = !!set; +} +#endif /* CONFIG_ENC_ASSOC */ #ifdef CONFIG_OWE struct wpabuf * owe_build_assoc_req(struct wpa_sm *sm, u16 group) diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index 46e03ad99..06c84fc88 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -670,6 +670,8 @@ struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek, const struct wpabuf **hlp, unsigned int num_hlp); int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len); +int eppke_process_assoc_resp(struct wpa_sm *sm, u64 flags, int link_id, + const u8 *resp, size_t len); struct wpabuf * owe_build_assoc_req(struct wpa_sm *sm, u16 group); int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid, diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index ba79f7f6d..1899ea2ac 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -3660,7 +3660,39 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) wpa_sm_set_reset_fils_completed(wpa_s->wpa, 1); #endif /* CONFIG_FILS */ +#ifdef CONFIG_ENC_ASSOC +#ifdef CONFIG_SME + if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_EPPKE) { + struct ptksa_cache_entry *entry; + if (wpa_s->ptksa == NULL) { + wpa_printf(MSG_DEBUG, "EPPKE: PTKSA not cached"); + return -1; + } + + entry = ptksa_cache_get(wpa_s->ptksa, wpa_s->valid_links ? + wpa_s->ap_mld_addr : bssid, + wpa_s->pairwise_cipher); + + wpa_sm_set_ptk_kck_kek(wpa_s->wpa, entry->ptk.kck, + entry->ptk.kck_len, entry->ptk.kek, + entry->ptk.kek_len); + + if (!data->assoc_info.resp_ies || + eppke_process_assoc_resp(wpa_s->wpa, + wpa_s->drv_flags2, + wpa_s->valid_links ? + wpa_s->mlo_assoc_link_id : -1, + data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len) < + 0) { + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_UNSPECIFIED); + return -1; + } + } +#endif /* CONFIG_SME */ +#endif /* CONFIG_ENC_ASSOC */ #ifdef CONFIG_OWE if (wpa_s->key_mgmt == WPA_KEY_MGMT_OWE && !(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_OWE_OFFLOAD_STA) && -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:36 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:36 +0530 Subject: [PATCH v2 26/28] tests: Enable CONFIG_ENC_ASSOC for hwsim wpa_supplicant In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-27-ainy.kumari@oss.qualcomm.com> From: Kavita Kavita Enable CONFIG_ENC_ASSOC option for wpa_supplicant in hwsim to run EPPKE related tests. Signed-off-by: Kavita Kavita --- tests/hwsim/example-wpa_supplicant.config | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/hwsim/example-wpa_supplicant.config b/tests/hwsim/example-wpa_supplicant.config index d9b26464c..9343ce32e 100644 --- a/tests/hwsim/example-wpa_supplicant.config +++ b/tests/hwsim/example-wpa_supplicant.config @@ -170,3 +170,4 @@ CONFIG_NAN_USD=y CONFIG_IEEE80211BE=y CONFIG_PROCESS_COORDINATION=y +CONFIG_ENC_ASSOC=y -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:37 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:37 +0530 Subject: [PATCH v2 27/28] tests: Enable CONFIG_ENC_ASSOC for hwsim hostapd In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-28-ainy.kumari@oss.qualcomm.com> From: Sai Pratyusha Magam Enable CONFIG_ENC_ASSOC option for hostapd in hwsim to run EPPKE related tests. Signed-off-by: Ainy Kumari Signed-off-by: Sai Pratyusha Magam --- tests/hwsim/example-hostapd.config | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/hwsim/example-hostapd.config b/tests/hwsim/example-hostapd.config index 65e6d7e5e..cd2696b74 100644 --- a/tests/hwsim/example-hostapd.config +++ b/tests/hwsim/example-hostapd.config @@ -125,3 +125,4 @@ CONFIG_AIRTIME_POLICY=y CONFIG_IEEE80211BE=y CONFIG_NAN_USD=y CONFIG_PROCESS_COORDINATION=y +CONFIG_ENC_ASSOC=y -- 2.25.1 From ainy.kumari at oss.qualcomm.com Tue Jan 6 05:45:38 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Tue, 6 Jan 2026 19:15:38 +0530 Subject: [PATCH v2 28/28] tests: Add EPPKE authentication test cases In-Reply-To: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> References: <20260106134538.2594232-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260106134538.2594232-29-ainy.kumari@oss.qualcomm.com> From: Sai Pratyusha Magam 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 Signed-off-by: Sai Pratyusha Magam --- tests/hwsim/test_eppke.py | 614 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 614 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..a29babc76 --- /dev/null +++ b/tests/hwsim/test_eppke.py @@ -0,0 +1,614 @@ +# 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) + finally: + dev[0].set("sae_groups", "") + dev[0].set("sae_pwe", "0") + +def test_eppke_mld_ap_with_base_akm_sae_ext_legacy_client(dev, apdev): + """EPPKE authentication with an MLD AP with base AKM SAE-EXT 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-EXT-KEY", 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-EXT-KEY", 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-24' 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-24' or sta["auth_alg"] != '9': + raise Exception("Incorrect Auth Algo/AKMSuiteSelector value") + else: + raise Exception("Unknown BSSID: " + bssid) + finally: + dev[0].set("sae_groups", "") + dev[0].set("sae_pwe", "0") + +def run_eppke_mld_two_links(dev, apdev, key_mgmt): + with HWSimRadio(use_mlo=True) as (hapd_radio, hapd_iface), \ + HWSimRadio(use_mlo=True) as (wpas_radio, wpas_iface): + + wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') + wpas.interface_add(wpas_iface) + + passphrase = '1234567890' + ssid = "test-eppke-authentication" + params = eht_mld_ap_wpa2_params(ssid, passphrase, + key_mgmt=key_mgmt, mfp="2", pwe='1', + beacon_prot=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) + + wpas.set("sae_groups", "") + wpas.set("sae_pwe", "1") + wpas.connect(ssid, sae_password=passphrase, scan_freq="2412 2437", + key_mgmt=key_mgmt, ieee80211w="2", beacon_prot="1", + pairwise="CCMP GCMP-256") + eht_verify_status(wpas, hapd0, 2412, 20, is_ht=True, mld=True, + valid_links=3, active_links=3) + +def run_eppke_mld_one_link(dev, apdev, key_mgmt): + with HWSimRadio(use_mlo=True) as (hapd_radio, hapd_iface), \ + HWSimRadio(use_mlo=True) as (wpas_radio, wpas_iface): + + wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') + wpas.interface_add(wpas_iface) + + passphrase = '1234567890' + ssid = "test-eppke-authentication" + params = eht_mld_ap_wpa2_params(ssid, passphrase, + key_mgmt=key_mgmt, mfp="2", pwe='1', + beacon_prot=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) + + wpas.set("sae_groups", "") + wpas.set("sae_pwe", "1") + wpas.connect(ssid, sae_password=passphrase, scan_freq="2412", + key_mgmt=key_mgmt, ieee80211w="2", beacon_prot="1", + pairwise="CCMP GCMP-256") + eht_verify_status(wpas, hapd0, 2412, 20, is_ht=True, mld=True, + valid_links=1, active_links=1) + +def test_eppke_with_base_akm_sae_single_link(dev, apdev): + """EPPKE authentication with an MLD AP with base AKM SAE and MLD client using single link""" + run_eppke_mld_one_link(dev, apdev, key_mgmt="SAE") + +def test_eppke_with_base_akm_sae_ext_single_link(dev, apdev): + """EPPKE authentication with an MLD AP with base AKM SAE-EXT and MLD client using single link""" + run_eppke_mld_one_link(dev, apdev, key_mgmt="SAE-EXT-KEY") + +def test_eppke_with_base_akm_sae_two_link(dev, apdev): + """EPPKE authentication with an MLD AP with base AKM SAE and MLD client using two links""" + run_eppke_mld_two_links(dev, apdev, key_mgmt="SAE") + +def test_eppke_with_base_akm_sae_ext_two_link(dev, apdev): + """EPPKE authentication with an MLD AP with base AKM SAE-EXT and MLD client using two links""" + run_eppke_mld_two_links(dev, apdev, key_mgmt="SAE-EXT-KEY") + +def test_eppke_ap_with_base_akm_sae_legacy_client_pmksa_cached(dev, apdev): + """EPPKE authentication with a Non-MLO AP with base AKM SAE and legacy client with PMKSA Caching""" + 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") + + dev[0].request("DISCONNECT") + dev[0].wait_disconnected() + dev[0].request("RECONNECT") + dev[0].wait_connected(timeout=15, error="Reconnect timed out") + val = dev[0].get_status_field('sae_group') + if val is not None: + raise Exception("SAE group claimed to have been used: " + val) + sta = hapd.get_sta(dev[0].own_addr()) + if sta['auth_alg'] != '9' or sta['AKMSuiteSelector'] != '00-0f-ac-8': + raise Exception("Incorrect Auth Algo/AKMSuiteSelector value after PMKSA caching") + + finally: + dev[0].set("sae_groups", "") + dev[0].set("sae_pwe", "0") + +def test_eppke_ap_with_base_akm_sae_ext_legacy_client_pmksa_cached(dev, apdev): + """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' + 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-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") + + dev[0].request("DISCONNECT") + dev[0].wait_disconnected() + dev[0].request("RECONNECT") + dev[0].wait_connected(timeout=15, error="Reconnect timed out") + val = dev[0].get_status_field('sae_group') + if val is not None: + raise Exception("SAE group claimed to have been used: " + val) + sta = hapd.get_sta(dev[0].own_addr()) + if sta['auth_alg'] != '9' or sta['AKMSuiteSelector'] != '00-0f-ac-24': + raise Exception("Incorrect Auth Algo/AKMSuiteSelector value after PMKSA caching") + + finally: + dev[0].set("sae_groups", "") + dev[0].set("sae_pwe", "0") + +def test_eppke_mld_ap_with_base_akm_sae_legacy_client_pmksa_cached(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) + + dev[0].request("DISCONNECT") + dev[0].wait_disconnected() + dev[0].request("RECONNECT") + dev[0].wait_connected(timeout=15, error="Reconnect timed out") + val = dev[0].get_status_field('sae_group') + if val is not None: + raise Exception("SAE group claimed to have been used: " + val) + + bssid = dev[0].get_status_field("bssid") + if hapd0.own_addr() == bssid: + hapd0.wait_sta(); + sta = hapd0.get_sta(dev[0].own_addr()) + elif hapd1.own_addr() == bssid: + hapd1.wait_sta(); + sta = hapd1.get_sta(dev[0].own_addr()) + else: + raise Exception("Unknown BSSID: " + bssid) + + if sta['auth_alg'] != '9' or sta['AKMSuiteSelector'] != '00-0f-ac-8': + raise Exception("Incorrect Auth Algo/AKMSuiteSelector value after PMKSA caching") + + finally: + dev[0].set("sae_groups", "") + dev[0].set("sae_pwe", "0") + +def test_eppke_mld_ap_with_base_akm_sae_ext_legacy_client_pmksa_cached(dev, apdev): + """EPPKE authentication with an MLD AP with base AKM SAE-EXT 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-EXT-KEY", 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-EXT-KEY", 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-24' 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-24' or sta["auth_alg"] != '9': + raise Exception("Incorrect Auth Algo/AKMSuiteSelector value") + else: + raise Exception("Unknown BSSID: " + bssid) + + dev[0].request("DISCONNECT") + dev[0].wait_disconnected() + dev[0].request("RECONNECT") + dev[0].wait_connected(timeout=15, error="Reconnect timed out") + val = dev[0].get_status_field('sae_group') + if val is not None: + raise Exception("SAE group claimed to have been used: " + val) + + bssid = dev[0].get_status_field("bssid") + if hapd0.own_addr() == bssid: + hapd0.wait_sta(); + sta = hapd0.get_sta(dev[0].own_addr()) + elif hapd1.own_addr() == bssid: + hapd1.wait_sta(); + sta = hapd1.get_sta(dev[0].own_addr()) + else: + raise Exception("Unknown BSSID: " + bssid) + + if sta['auth_alg'] != '9' or sta['AKMSuiteSelector'] != '00-0f-ac-24': + raise Exception("Incorrect Auth Algo/AKMSuiteSelector value after PMKSA caching") + + finally: + dev[0].set("sae_groups", "") + dev[0].set("sae_pwe", "0") + +def run_eppke_mld_one_link_pmksa_cached(dev, apdev, key_mgmt): + with HWSimRadio(use_mlo=True) as (hapd_radio, hapd_iface), \ + HWSimRadio(use_mlo=True) as (wpas_radio, wpas_iface): + + wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') + wpas.interface_add(wpas_iface) + + passphrase = '1234567890' + ssid = "test-eppke-authentication" + params = eht_mld_ap_wpa2_params(ssid, passphrase, + key_mgmt=key_mgmt, mfp="2", pwe='1', + beacon_prot=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) + + wpas.set("sae_groups", "") + wpas.set("sae_pwe", "1") + wpas.connect(ssid, sae_password=passphrase, scan_freq="2412", + key_mgmt=key_mgmt, ieee80211w="2", beacon_prot="1", + pairwise="CCMP GCMP-256") + eht_verify_status(wpas, hapd0, 2412, 20, is_ht=True, mld=True, + valid_links=1, active_links=1) + + wpas.request("DISCONNECT") + wpas.wait_disconnected() + wpas.request("RECONNECT") + wpas.wait_connected(timeout=15, error="Reconnect timed out") + val = wpas.get_status_field('sae_group') + if val is not None: + raise Exception("SAE group claimed to have been used: " + val) + eht_verify_status(wpas, hapd0, 2412, 20, is_ht=True, mld=True, + valid_links=1, active_links=1) + +def test_eppke_with_base_akm_sae_single_link_pmksa_cached(dev, apdev): + """EPPKE authentication with an MLD AP with base AKM SAE and MLD client using single link""" + run_eppke_mld_one_link_pmksa_cached(dev, apdev, key_mgmt="SAE") + +def test_eppke_with_base_akm_sae_ext_single_link_pmksa_cached(dev, apdev): + """EPPKE authentication with an MLD AP with base AKM SAE-EXT and MLD client using single link""" + run_eppke_mld_one_link_pmksa_cached(dev, apdev, key_mgmt="SAE-EXT-KEY") + +def run_eppke_mld_two_links_pmksa_cached(dev, apdev, key_mgmt): + with HWSimRadio(use_mlo=True) as (hapd_radio, hapd_iface), \ + HWSimRadio(use_mlo=True) as (wpas_radio, wpas_iface): + + wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') + wpas.interface_add(wpas_iface) + + passphrase = '1234567890' + ssid = "test-eppke-authentication" + params = eht_mld_ap_wpa2_params(ssid, passphrase, + key_mgmt=key_mgmt, mfp="2", pwe='1', + beacon_prot=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) + + wpas.set("sae_groups", "") + wpas.set("sae_pwe", "1") + wpas.connect(ssid, sae_password=passphrase, scan_freq="2412 2437", + key_mgmt=key_mgmt, ieee80211w="2", beacon_prot="1", + pairwise="CCMP GCMP-256") + eht_verify_status(wpas, hapd0, 2412, 20, is_ht=True, mld=True, + valid_links=3, active_links=3) + + wpas.request("DISCONNECT") + wpas.wait_disconnected() + wpas.request("RECONNECT") + wpas.wait_connected(timeout=15, error="Reconnect timed out") + val = wpas.get_status_field('sae_group') + if val is not None: + raise Exception("SAE group claimed to have been used: " + val) + eht_verify_status(wpas, hapd0, 2412, 20, is_ht=True, mld=True, + valid_links=3, active_links=3) + +def test_eppke_with_base_akm_sae_two_link_pmksa_cached(dev, apdev): + """EPPKE authentication with an MLD AP with base AKM SAE and MLD client using two links""" + run_eppke_mld_two_links_pmksa_cached(dev, apdev, key_mgmt="SAE") + +def test_eppke_with_base_akm_sae_ext_two_link_pmksa_cached(dev, apdev): + """EPPKE authentication with an MLD AP with base AKM SAE-EXT and MLD client using two links""" + run_eppke_mld_two_links_pmksa_cached(dev, apdev, key_mgmt="SAE-EXT-KEY") + +def test_eppke_ap_gtk_rekey_with_base_akm_sae_ext_legacy_client(dev, apdev): + """EPPKE AP and GTK rekey""" + 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['wpa_group_rekey'] = '1' + 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-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_ptk_rekey_with_base_akm_sae_ext_legacy_client(dev, apdev): + """EPPKE AP and PTK rekey""" + 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['wpa_ptk_rekey'] = '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-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") -- 2.25.1