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 From andrei.otcheretianski at intel.com Sun Jan 11 04:01:31 2026 From: andrei.otcheretianski at intel.com (Andrei Otcheretianski) Date: Sun, 11 Jan 2026 14:01:31 +0200 Subject: [PATCH 2/2] tests: Add EHT MLD test with autonomous P2P GO In-Reply-To: <20260111120131.187784-1-andrei.otcheretianski@intel.com> References: <20260111120131.187784-1-andrei.otcheretianski@intel.com> Message-ID: <20260111120131.187784-2-andrei.otcheretianski@intel.com> From: Ilan Peer Verify that the P2P GO channels is one of the channels on which the station interface is associated. Signed-off-by: Ilan Peer --- tests/hwsim/test_eht.py | 44 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/hwsim/test_eht.py b/tests/hwsim/test_eht.py index 363805ea71..901ccabdb6 100644 --- a/tests/hwsim/test_eht.py +++ b/tests/hwsim/test_eht.py @@ -19,6 +19,8 @@ from tshark import run_tshark from test_gas import hs20_ap_params from test_dpp import check_dpp_capab, wait_auth_success from test_rrm import build_beacon_request, run_req_beacon, BeaconReport +from p2p_utils import * +import utils def eht_verify_wifi_version(dev): status = dev.get_status() @@ -2782,3 +2784,45 @@ def test_eht_ml_setup_reconfig_AB_A_AB(dev, apdev, params): timeout=2) if ev is None: raise Exception("GTK rekey timed out after link addition") + +def test_eht_mld_and_autogo(dev, apdev): + """EHT MLD connection + autonomous P2P GO on the station device""" + + with HWSimRadio(use_mlo=True, n_channels=2) as (hapd0_radio, hapd0_iface), \ + HWSimRadio(use_mlo=True, n_channels=2) as (wpas_radio, wpas_iface): + + wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') + wpas.interface_add(wpas_iface) + + ssid = "ap_mld" + params = eht_mld_ap_wpa2_params(ssid, key_mgmt="OWE", mfp="2") + + hapds = [] + freqs = "" + for i in range(1, 3): + params['channel'] = str(i) + freqs += str(2407 + 5 * i) + " " + hapds.append(eht_mld_enable_ap(hapd0_iface, i - 1, params)) + + wpas.connect(ssid, scan_freq=freqs, key_mgmt="OWE", ieee80211w="2") + eht_verify_status(wpas, hapds[0], 2412, 20, is_ht=True, mld=True, valid_links=0x03, + active_links=0x03) + eht_verify_wifi_version(wpas) + hwsim_utils.test_connectivity(wpas, hapds[0]) + + res = autogo(wpas) + if "p2p-wlan" not in res['ifname']: + raise Exception("Unexpected group interface name on GO") + + if res['ifname'] not in utils.get_ifnames(): + raise Exception("Could not find group interface netdev") + + if res['freq'] != "2412" and res['freq'] != "2417": + raise Exception("Unexpected group interface frequency") + + connect_cli(wpas, dev[0]) + wpas.remove_group() + + dev[0].wait_go_ending_session() + if res['ifname'] in utils.get_ifnames(): + raise Exception("Group interface netdev was not removed") -- 2.52.0 From andrei.otcheretianski at intel.com Sun Jan 11 04:01:30 2026 From: andrei.otcheretianski at intel.com (Andrei Otcheretianski) Date: Sun, 11 Jan 2026 14:01:30 +0200 Subject: [PATCH 1/2] MLD: Change get_shared_radio_freqs_data() to also support MLD connections Message-ID: <20260111120131.187784-1-andrei.otcheretianski@intel.com> From: Ilan Peer For now, the frequencies are the ones associated with the valid links and not the active links. Signed-off-by: Ilan Peer --- wpa_supplicant/wpa_supplicant.c | 64 +++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 293d4920ea..62336f9cdc 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -9539,8 +9539,8 @@ int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s, { struct wpa_supplicant *ifs; u8 bssid[ETH_ALEN]; - int freq; - unsigned int idx = 0, i; + int freqs[MAX_NUM_MLD_LINKS]; + unsigned int idx = 0, i, j, n_freqs; wpa_dbg(wpa_s, MSG_DEBUG, "Determining shared radio frequencies (max len %u)", len); @@ -9548,36 +9548,62 @@ int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s, dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, radio_list) { + n_freqs = 0; if (idx == len) break; if (exclude_current && ifs == wpa_s) continue; - if (ifs->current_ssid == NULL || ifs->assoc_freq == 0) + if (!ifs->current_ssid || (!ifs->assoc_freq && + !ifs->valid_links)) continue; if (ifs->current_ssid->mode == WPAS_MODE_AP || ifs->current_ssid->mode == WPAS_MODE_P2P_GO || - ifs->current_ssid->mode == WPAS_MODE_MESH) - freq = ifs->current_ssid->frequency; - else if (wpa_drv_get_bssid(ifs, bssid) == 0) - freq = ifs->assoc_freq; - else - continue; + ifs->current_ssid->mode == WPAS_MODE_MESH) { + freqs[n_freqs++] = ifs->current_ssid->frequency; + } else if (ifs->valid_links) { + struct driver_sta_mlo_info drv_mlo; - /* Hold only distinct freqs */ - for (i = 0; i < idx; i++) - if (freqs_data[i].freq == freq) - break; + os_memset(&drv_mlo, 0, sizeof(drv_mlo)); - if (i == idx) - freqs_data[idx++].freq = freq; + if (wpas_drv_get_sta_mlo_info(ifs, &drv_mlo)) { + wpa_dbg(wpa_s, MSG_INFO, + "Failed to get MLO link info"); + continue; + } + + if (!drv_mlo.valid_links) + continue; - if (ifs->current_ssid->mode == WPAS_MODE_INFRA) { - freqs_data[i].flags |= ifs->current_ssid->p2p_group ? - WPA_FREQ_USED_BY_P2P_CLIENT : - WPA_FREQ_USED_BY_INFRA_STATION; + for_each_link(drv_mlo.valid_links, j) { + if (!drv_mlo.links[j].freq) + continue; + + freqs[n_freqs++] = drv_mlo.links[j].freq; + } + } else if (wpa_drv_get_bssid(ifs, bssid) == 0) { + freqs[n_freqs++] = ifs->assoc_freq; + } else { + continue; + } + + /* Hold only distinct freqs */ + for (j = 0; j < n_freqs && idx < len; j++) { + for (i = 0; i < idx; i++) + if (freqs_data[i].freq == freqs[j]) + break; + + if (i == idx) + freqs_data[idx++].freq = freqs[j]; + + if (ifs->current_ssid->mode == WPAS_MODE_INFRA) { + freqs_data[i].flags |= + ifs->current_ssid->p2p_group ? + WPA_FREQ_USED_BY_P2P_CLIENT : + WPA_FREQ_USED_BY_INFRA_STATION; + } } } -- 2.52.0 From johannes at sipsolutions.net Mon Jan 12 11:12:08 2026 From: johannes at sipsolutions.net (Johannes Berg) Date: Mon, 12 Jan 2026 20:12:08 +0100 Subject: [DESIGN RFC v3] AP Architecture for Wi-Fi-8 Multi-AP Coordination (MAPC) In-Reply-To: <653a4207-38f8-408c-8067-c4960c11a71f@oss.qualcomm.com> (sfid-20260112_195259_393455_5899DC2E) References: <653a4207-38f8-408c-8067-c4960c11a71f@oss.qualcomm.com> (sfid-20260112_195259_393455_5899DC2E) Message-ID: <05381371a41f154058429f0a1079204c4a454f45.camel@sipsolutions.net> Hi, I wish you guys (generally Qualcomm, not you personally) could finish one thing before opening yet another ;-) Couple of quick comments: > +---------------------------------------------------------------+ > > WLAN Driver (e.g., ath12k) | > > | > > +----------------------+ +---------------------------+ | > > | MAPC Peer Mgmt | | MAPC Policy Config Mgmt | | > > +----------------------+ +---------------------------+ | > > | > > +---------------------------+ | > > | MAPC Event Gen/Notify | | > > +---------------------------+ | > +---------------------------------------------------------------+ I think you need to do a lot more to explain why this is in firmware? > +--------------------------------------------------------------------+ > > UHR AP-1 | > > +---------+ +---------+ +---------+ +---------+ | +---------+ > > | | | | | | | | | | | > > | hostapd | | cfg80211| | mac80211| | wlan | | | UHR | > > | | | | | | | driver | | | AP-2 | > > +----+----+ +----+----+ +----+----+ +----+----+ | +----+----+ > > | | | | | | > > +----+---------------+ | | | | | > > | init hostapd & HW | | | | | | > > | MAPC support update| | | | | | > > +----+---------------+ | MAPC Discovery | | | | > > | | Request | | | | > > | |(MAPC capability)| | | | > > +-----------------+-----------------+-----------------+------|------> > > | | MAPC Discovery | | | | > > | | Response | | | | > > | |(MAPC capability)| | | | > > <-----------------+-----------------+-----------------+------|------+ Those are action frames just going between the hostapd instances (or similar), why does the firmware need to create an event to hostapd itself? > +--------------------------------------------------------------------+ > > UHR AP-1 | > > +---------+ +---------+ +---------+ +---------+ | +---------+ > > | | | | | | | | | | | > > | hostapd | | cfg80211| | mac80211| | wlan | | | UHR | > > | | | | | | | driver | | | AP-2 | > > +----+----+ +----+----+ +----+----+ +----+----+ | +----+----+ > > | | | | | | > > | | | +--------------+-----+| | > > | | | | Notify hostapd to || | > > | | | | Trigger MAPC || | > > | | | | Negotiation || | > > | | | +--------------+-----+| | > > | NL80211_CMD_MAPC_NEGOTIATION_TRIGGER(scheme-id,action) | | > > <-----------------+-----------------+-----------------+ | | ? Here, why? What does this do in FW? > > > > +----+---------------+ | | | | | > > |parse scheme-id & | | | | | | > > |action. Init MAPC | |MAPC Negotiation | | | | > > | Negotiation | | Request | | | | > > +----+---------------+(scheme params & APID1 | | | > > | Optype:ESTABLISHMENT/UPDATE) | | | > > +-----------------+-----------------+-----------------+------+------> Hostapd parses something the ... FW gave it? That seems odd. We should give it in a decent data structure format, if this is at all sensible, I have no idea. > New notifications from cfg80211 to user space: > - NL80211_CMD_MAPC_NEGOTIATION_TRIGGER > Direction: kernel to user space. > Source: cfg80211 on behalf of the WLAN driver/firmware. > Purpose: Provide a hint to hostapd that MAPC Negotiation > Request should be triggered on this interface. For each > MAPC scheme, the driver/firmware may request establishment, > update, or teardown of an agreement. hostapd is expected > to apply policy and iterate over the locally known MAPC > peers for this interface. > > Attributes: > - NL80211_ATTR_MAPC_SCHEMES (nested) > Contains one or more per-scheme MAPC negotiation hints. Each > nested entry includes: > - NL80211_ATTR_MAPC_SCHEME_TYPE > Identifies the MAPC scheme (e.g., Co-BF, Co-SR, Co-TDMA, > Co-RTWT, Co-CR). > - NL80211_ATTR_MAPC_ACTION > Requested negotiation action for this scheme: > - NL80211_MAPC_ACTION_ESTABLISH: establish new agreement(s). > - NL80211_MAPC_ACTION_UPDATE: update existing agreement(s). > - NL80211_MAPC_ACTION_TEARDOWN: teardown existing agreement(s). Why do you always want to let firmware be in control of everything? Seems at least for some of this you'd really want the upper layers to control it for purposes of coordination? How does the FW even know which other AP it can coordinate with, isn't that something a network controller would determine? > (C) mac80211 > Two design options are considered for representing MAPC peers in > mac80211: > > Option-A(Preferred): Reuse existing station management infra > - Allocate standard station objects (struct sta_info / struct > ieee80211_sta) to represent MAPC peers, and call the WLAN driver. > - Use a subset of the existing mac80211 station states (NOTEXIST, > NONE, AUTH) to represent a MAPC stations. I'd say AUTH doesn't work, for purposes of key management etc. you really want this to be in AUTHORIZED state. > Option-B: Define lightweight MAPC peer/station management > infra But that doesn't seem better either really. johannes From johannes at sipsolutions.net Mon Jan 12 11:18:24 2026 From: johannes at sipsolutions.net (Johannes Berg) Date: Mon, 12 Jan 2026 20:18:24 +0100 Subject: [DESIGN RFC v3] AP Architecture for Wi-Fi-8 Multi-AP Coordination (MAPC) In-Reply-To: <05381371a41f154058429f0a1079204c4a454f45.camel@sipsolutions.net> (sfid-20260112_201230_217667_8E744336) References: <653a4207-38f8-408c-8067-c4960c11a71f@oss.qualcomm.com> (sfid-20260112_195259_393455_5899DC2E) <05381371a41f154058429f0a1079204c4a454f45.camel@sipsolutions.net> (sfid-20260112_201230_217667_8E744336) Message-ID: <59cec65b29ded381c85d1be943d88e956a4b7e74.camel@sipsolutions.net> On Mon, 2026-01-12 at 20:12 +0100, Johannes Berg wrote: > > Why do you always want to let firmware be in control of everything? > Seems at least for some of this you'd really want the upper layers to > control it for purposes of coordination? How does the FW even know which > other AP it can coordinate with, isn't that something a network > controller would determine? A less generous reading of this could be: you guys want everything to be controlled by FW, so you don't have to open-source it in hostapd. Now you realize oops, don't really want to do all the security handshake in FW, so we need to ask hostapd and then we need keys and stations and all this stuff. So let's build something nobody else can use, upstream it and we get the best of both worlds - others will maintain the mac80211 code for us anyway. Am I wrong? Is there a technical reason for not simply doing MAPC discovery/agreement negotiation etc. in hostapd as well, based on driver/hw/fw capabilities, and then you don't need all these strange "triggered by firmware" flows? johannes From dominique.martinet at atmark-techno.com Mon Jan 12 18:28:51 2026 From: dominique.martinet at atmark-techno.com (Dominique Martinet) Date: Tue, 13 Jan 2026 11:28:51 +0900 Subject: [RFC PATCH] nl80211: re-add interface into bridge on interface re-creation In-Reply-To: <20251218082059.2206506-1-dominique.martinet@atmark-techno.com> References: <20251218082059.2206506-1-dominique.martinet@atmark-techno.com> Message-ID: Hi, Dominique Martinet wrote on Thu, Dec 18, 2025 at 05:20:59PM +0900: > When using the nl80211 driver with a bridge, the driver properly detects > when the interface is re-created ("nl80211: Update ifindex for a removed > interface") and wpa_driver_nl80211_finish_drv_init() reinits it, but if > the interface was part of a bridge nothing adds it back there. > > This adds the interface to the bridge in this function if the initial > init setup a bridge in the first place: > - the first time wpa_driver_nl80211_finish_drv_init() is called, > i802_init() has not yet setup the bridge (i802_check_bridge()), so > this flag is false and nothing changes > - when the interface is re-added the flag is only set if > i802_check_bridge() went to the end, which also ensures the bss->bridge > variable is sensible. Would it be possible to have any feedback on this? I've been running with this patch on our devices and hell didn't break loose, so if there is no comment I'll resend without RFC in a couple of weeks. Thanks, -- Dominique Martinet From allen.ye at mediatek.com Wed Jan 14 01:20:07 2026 From: allen.ye at mediatek.com (Allen Ye) Date: Wed, 14 Jan 2026 17:20:07 +0800 Subject: [PATCH] AP: Add support for MLD link group key initialization Message-ID: <20260114092007.3125348-1-allen.ye@mediatek.com> Currently, only the assoc link sets the first_sta_seen flag in a mld connection. That makes the GTK of the other links may be reset if another station associates with the other links. This patch initializes the GTK and sets the first_sta_seen flag for all links in a mld connection. Reviewed-by: Money Wang Signed-off-by: Allen Ye --- src/ap/wpa_auth.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 49268b21e..0bc89f771 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -2641,9 +2641,16 @@ static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth, SM_STATE(WPA_PTK, AUTHENTICATION2) { + int link_id; + SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk); wpa_group_ensure_init(sm->wpa_auth, sm->group); +#ifdef CONFIG_IEEE80211BE + for_each_sm_auth(sm, link_id) + wpa_group_ensure_init(sm->mld_links[link_id].wpa_auth, + sm->mld_links[link_id].wpa_auth->group); +#endif /* CONFIG_IEEE80211BE */ sm->ReAuthenticationRequest = false; /* -- 2.45.2 From jeff.johnson at oss.qualcomm.com Wed Jan 14 11:15:16 2026 From: jeff.johnson at oss.qualcomm.com (Jeff Johnson) Date: Wed, 14 Jan 2026 11:15:16 -0800 Subject: [DESIGN RFC v3] AP Architecture for Wi-Fi-8 Multi-AP Coordination (MAPC) In-Reply-To: <59cec65b29ded381c85d1be943d88e956a4b7e74.camel@sipsolutions.net> References: <653a4207-38f8-408c-8067-c4960c11a71f@oss.qualcomm.com> <05381371a41f154058429f0a1079204c4a454f45.camel@sipsolutions.net> <59cec65b29ded381c85d1be943d88e956a4b7e74.camel@sipsolutions.net> Message-ID: <63ff0362-4ee4-4f13-a212-dc1351eefe08@oss.qualcomm.com> On 1/12/2026 11:18 AM, Johannes Berg wrote: > On Mon, 2026-01-12 at 20:12 +0100, Johannes Berg wrote: >> >> Why do you always want to let firmware be in control of everything? >> Seems at least for some of this you'd really want the upper layers to >> control it for purposes of coordination? How does the FW even know which >> other AP it can coordinate with, isn't that something a network >> controller would determine? > > A less generous reading of this could be: you guys want everything to be > controlled by FW, so you don't have to open-source it in hostapd. Now > you realize oops, don't really want to do all the security handshake in > FW, so we need to ask hostapd and then we need keys and stations and all > this stuff. So let's build something nobody else can use, upstream it > and we get the best of both worlds - others will maintain the mac80211 > code for us anyway. > > Am I wrong? Is there a technical reason for not simply doing MAPC > discovery/agreement negotiation etc. in hostapd as well, based on > driver/hw/fw capabilities, and then you don't need all these strange > "triggered by firmware" flows? My perception is that Qualcomm would love for all the Wi-Fi 8 functionality to be in userspace and nl/cfg/mac80211 since then there would be no code maintenance overhead on our part -- just you and the userspace maintainers. But there are concerns about latency, and internal consensus is that some aspects of this functionality has to be handled in firmware (or even hardware) in order to meet customer KPIs. This comes from our history of supporting large-scale deployments of APs, and the expectations of how Wi-Fi 8 will make things better. That is why we are posting design RFCs, so that you, as well as engineers from other vendors, can review our proposals and give your feedback and counter-proposals. We want to avoid developing what might be an architecturally pure design that doesn't actually meet customer needs. And apologies for the "firehose" of both design and code, but we have a desire to ship Wi-Fi 8 products using upstream code. I've passed along information that you want our engineering team to focus on the base NPCA patches so that there is the appropriate foundation. But in parallel we do also hope there is engagement from other vendors on the Design RFCs we are posting. Our goal is to upstream as much AP functionality as you can absorb. /jeff From johannes at sipsolutions.net Thu Jan 15 06:17:59 2026 From: johannes at sipsolutions.net (Johannes Berg) Date: Thu, 15 Jan 2026 15:17:59 +0100 Subject: [DESIGN RFC v3] AP Architecture for Wi-Fi-8 Multi-AP Coordination (MAPC) In-Reply-To: <63ff0362-4ee4-4f13-a212-dc1351eefe08@oss.qualcomm.com> (sfid-20260114_201523_158312_F45EB4BD) References: <653a4207-38f8-408c-8067-c4960c11a71f@oss.qualcomm.com> <05381371a41f154058429f0a1079204c4a454f45.camel@sipsolutions.net> <59cec65b29ded381c85d1be943d88e956a4b7e74.camel@sipsolutions.net> <63ff0362-4ee4-4f13-a212-dc1351eefe08@oss.qualcomm.com> (sfid-20260114_201523_158312_F45EB4BD) Message-ID: <52356be3d21920d84579d1a8fd803540c6f9644d.camel@sipsolutions.net> On Wed, 2026-01-14 at 11:15 -0800, Jeff Johnson wrote: > On 1/12/2026 11:18 AM, Johannes Berg wrote: > > On Mon, 2026-01-12 at 20:12 +0100, Johannes Berg wrote: > > > > > > Why do you always want to let firmware be in control of everything? > > > Seems at least for some of this you'd really want the upper layers to > > > control it for purposes of coordination? How does the FW even know which > > > other AP it can coordinate with, isn't that something a network > > > controller would determine? > > > > A less generous reading of this could be: you guys want everything to be > > controlled by FW, so you don't have to open-source it in hostapd. Now > > you realize oops, don't really want to do all the security handshake in > > FW, so we need to ask hostapd and then we need keys and stations and all > > this stuff. So let's build something nobody else can use, upstream it > > and we get the best of both worlds - others will maintain the mac80211 > > code for us anyway. > > > > Am I wrong? Is there a technical reason for not simply doing MAPC > > discovery/agreement negotiation etc. in hostapd as well, based on > > driver/hw/fw capabilities, and then you don't need all these strange > > "triggered by firmware" flows? > > My perception is that Qualcomm would love for all the Wi-Fi 8 functionality to > be in userspace and nl/cfg/mac80211 since then there would be no code > maintenance overhead on our part -- just you and the userspace maintainers. :) > But there are concerns about latency, and internal consensus is that some > aspects of this functionality has to be handled in firmware (or even hardware) > in order to meet customer KPIs. This comes from our history of supporting > large-scale deployments of APs, and the expectations of how Wi-Fi 8 will make > things better. That is why we are posting design RFCs, so that you, as well as > engineers from other vendors, can review our proposals and give your feedback > and counter-proposals. We want to avoid developing what might be an > architecturally pure design that doesn't actually meet customer needs. Sure, that's fair. Maybe I can just ask folks to spell out the constraints and reasoning behind the design? Taking this specific example, it basically says "FW sends a request to hostapd, hostapd does the handshake and installs the MAPC station. This is how we think we should handle the MAPC stations." It never says _why_ and how any of this happens. What's the magic thing only the firmware can do to figure out it should start coordinating a given AP? (Clearly that operation can't be concerned much about latency if it asks hostapd.) In fact I'd have expected that in certain cases some controller infrastructure sitting *on top of hostapd* would decide which APs coordinate with each other, rather than the firmware. Although I guess if you hear the beacon on the same channel with a good enough RSSI then you can coordinate. I could keep guessing - maybe there's a limited space to do this in FW? But there's not even anything built into the design where the firmware can ask to _remove_ it again, as far as I can tell, unless there was an (unstated) assumption that it can just delete the MAPC station and send a DEL_STATION notification about it. The document even says that phase 1 is discovery, and then goes to completely ignore phase 1 (it's hidden in FW), and describes basically only phase 2. > And apologies for the "firehose" of both design and code, but we have a desire > to ship Wi-Fi 8 products using upstream code. I've passed along information > that you want our engineering team to focus on the base NPCA patches so that > there is the appropriate foundation. But in parallel we do also hope there is > engagement from other vendors on the Design RFCs we are posting. Our goal is > to upstream as much AP functionality as you can absorb. Sure, and I appreciate that this is coming. I'm a little overwhelmed by having so many parallel things going on right now, and all the parallel design documents don't help. Maybe this is the point where we consider inviting everyone who wants to a room for a few days? Even a video room might be better ;-) List some topics first, present the design briefly, etc.? But I don't know if it's just the medium. johannes From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:32:39 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:02:39 +0530 Subject: [PATCH v3 00/27] 11bi: Add Support for EPPKE Authentication Message-ID: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> From: Sai Pratyusha Magam 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. --- Changes in v2 --> v3: - "epp_flags" are currently not being used, so removed below patch: [PATCH v2 15/28] EPPKE: EPP capabilities negotiation indication 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 (12): 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: 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 | 6 +- src/ap/ap_drv_ops.h | 2 +- src/ap/ap_mlme.c | 6 +- src/ap/ieee802_11.c | 171 ++++-- 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 | 94 ++- src/ap/wpa_auth.h | 14 +- src/ap/wpa_auth_glue.c | 25 + src/ap/wpa_auth_ie.c | 23 +- 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 | 8 + src/drivers/driver_atheros.c | 8 +- src/drivers/driver_nl80211.c | 10 + 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 | 233 +++++++- 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 | 675 ++++++++++++++++++++++ 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 + 48 files changed, 2473 insertions(+), 267 deletions(-) create mode 100644 tests/hwsim/test_eppke.py base-commit: e17107912c8ef24dad8dd2407b71a2541a5b34e8 -- 2.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:32:40 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:02:40 +0530 Subject: [PATCH v3 01/27] Sync nl80211_copy.h with wireless-next linux/nl80211.h In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:32:41 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:02:41 +0530 Subject: [PATCH v3 02/27] 11bi: Rename FILS nonce element and related constants to generic 'nonce' In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:32:42 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:02:42 +0530 Subject: [PATCH v3 03/27] 11bi: Add wiphy capability flag for (Re)Association frame encryption support In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:32:43 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:02:43 +0530 Subject: [PATCH v3 04/27] PASN: Extend RSNXE capability field to 32 bits In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:32:44 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:02:44 +0530 Subject: [PATCH v3 05/27] EPPKE: Add wiphy capability flag for EPPKE authentication In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:32:45 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:02:45 +0530 Subject: [PATCH v3 06/27] 11bi: Enhanced Privacy Protection (EPP) related definitions In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:32:46 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:02:46 +0530 Subject: [PATCH v3 07/27] 11bi: Configuration options to control EPP feature support in AP mode In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:32:47 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:02:47 +0530 Subject: [PATCH v3 08/27] 11bi: RSNE/RSNXE capability Extensions in AP mode In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:32:48 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:02:48 +0530 Subject: [PATCH v3 09/27] wpa_supplicant: Add CONFIG_ENC_ASSOC for association frame encryption support In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:32:49 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:02:49 +0530 Subject: [PATCH v3 10/27] EPPKE: Add EPPKE support to PASN PTK derivation per IEEE P802.11bi/D2.0 In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:32:50 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:02:50 +0530 Subject: [PATCH v3 11/27] PASN: Modify PASN Authentication frame header APIs based on auth_algo In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:32:51 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:02:51 +0530 Subject: [PATCH v3 12/27] EPPKE: Extend existing PASN APIs for EPPKE Authentication In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:32:52 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:02:52 +0530 Subject: [PATCH v3 13/27] EPPKE: PTK/MIC Computation and key installation changes in Responder mode In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:32:53 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:02:53 +0530 Subject: [PATCH v3 14/27] EPPKE: EPP peer indication to driver In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:32:54 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:02:54 +0530 Subject: [PATCH v3 15/27] EPPKE: RSNE/Key delivery element in (Re)Association Response In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-16-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 910025f49..87ad421c7 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -5774,6 +5774,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 040efd4a4..f7fc9eda4 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -7836,6 +7836,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 4883e2db9..c023168f9 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -653,7 +653,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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:32:56 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:02:56 +0530 Subject: [PATCH v3 17/27] EPPKE: Add support for EPPKE authentication for SME-in-Userspace case In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-18-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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:32:55 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:02:55 +0530 Subject: [PATCH v3 16/27] EPPKE: Skip 4WH and move PTK state directly to PTKINITDONE In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-17-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 87ad421c7..a9c37d079 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -7314,7 +7314,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 f7fc9eda4..bfcd257d7 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -1041,6 +1041,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, @@ -2522,6 +2533,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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:32:58 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:02:58 +0530 Subject: [PATCH v3 19/27] PASN: Add support for MIC computation for M3 frame for SME-in-Userspace In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-20-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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:32:57 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:02:57 +0530 Subject: [PATCH v3 18/27] EPPKE: Add Multi-Link support in Authentication frames In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-19-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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:33:01 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:03:01 +0530 Subject: [PATCH v3 22/27] EPPKE: Update RSNE construction and validation per IEEE P802.11bi/D2.0 In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-23-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 a9c37d079..420e80a34 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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:33:00 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:03:00 +0530 Subject: [PATCH v3 21/27] Add support for temporal key removal on association failure In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-22-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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:33:02 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:03:02 +0530 Subject: [PATCH v3 23/27] EPPKE: Skip 4-Way handshake and authorize supplicant port on association In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-24-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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:33:03 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:03:03 +0530 Subject: [PATCH v3 24/27] EPPKE: Retrieve KCK/KEK from PTKSA and install group keys for GTK rekey In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-25-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 | 203 +++++++++++++++++++++++++++++++++++++++- src/rsn_supp/wpa.h | 2 + wpa_supplicant/events.c | 32 +++++++ 3 files changed, 235 insertions(+), 2 deletions(-) diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 5f0b0e3d8..0897b1bef 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,203 @@ 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 valid_links, + 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) && valid_links != -1) { + u8 i; + + for_each_link(valid_links, i) { + size_t gtk_kde_len; + struct wpa_gtk_data gd; + int rsc_len; + + if (!kde.mlo_gtk[i]) { + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "EPPKE: No MLO GTK for link ID %u", i); + return -1; + } + + os_memset(&gd, 0, sizeof(gd)); + gtk_kde_len = kde.mlo_gtk_len[i]; + + /* Minimal sanity: + * 1 byte KeyID + RSC + at least 5 bytes GTK + */ + if (gtk_kde_len < 1 + 6 + 5) { + wpa_printf(MSG_DEBUG, "EPPKE: Invalid MLO GTK" + " KDE len=%lu for link %u", + gtk_kde_len, i); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "EPPKE: Received MLO GTK", + kde.mlo_gtk[i], gtk_kde_len); + + /* KeyID in low 2 bits; + * Tx bit workaround from KDE[0] bit2 + */ + gd.keyidx = kde.mlo_gtk[i][0] & 0x3; + gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm, + !!(kde.mlo_gtk[i][0] & BIT(2))); + + rsc_len = wpa_cipher_rsc_len(sm->group_cipher); /* returns PN/RSC length */ + if (rsc_len <= 0) { + wpa_printf(MSG_DEBUG, "EPPKE: Unsupported group cipher (no RSC len)"); + goto fail; + } + + /* Actual GTK length = total KDE - KeyID(1) - RSC/PN. + * For CCMP/GCMP, gtk_kde_len is typically 23 -> 23 - 1 - 6 = 16. + */ + gd.gtk_len = gtk_kde_len - 1 - rsc_len; + + /* Validate GTK length against algorithm requirements */ + if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, + gd.gtk_len, gd.gtk_len, + &gd.key_rsc_len, &gd.alg)) + goto fail; + + if (gd.gtk_len > sizeof(gd.gtk)) { + wpa_printf(MSG_DEBUG, "EPPKE: Too long GTK in" + " GTK KDE (len=%u)", gd.gtk_len); + goto fail; + } + + os_memcpy(gd.gtk, kde.mlo_gtk[i] + 1 + rsc_len, gd.gtk_len); + + if (wpa_supplicant_install_mlo_gtk(sm, i, &gd, elems.key_delivery, 0) < 0) { + wpa_printf(MSG_DEBUG, "EPPKE: Failed to set MLO GTK (link=%u)", i); + goto fail; + } + + if (_mlo_ieee80211w_set_keys(sm, i, &kde) < 0) { + wpa_printf(MSG_DEBUG, "EPPKE: Failed to set MLO IGTK (link=%u)", i); + 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..d1217ec5f 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->valid_links : -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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:33:04 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:03:04 +0530 Subject: [PATCH v3 25/27] tests: Enable CONFIG_ENC_ASSOC for hwsim wpa_supplicant In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-26-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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:33:05 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:03:05 +0530 Subject: [PATCH v3 26/27] tests: Enable CONFIG_ENC_ASSOC for hwsim hostapd In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-27-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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:33:06 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:03:06 +0530 Subject: [PATCH v3 27/27] tests: Add EPPKE authentication test cases In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-28-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 | 675 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 675 insertions(+) create mode 100644 tests/hwsim/test_eppke.py diff --git a/tests/hwsim/test_eppke.py b/tests/hwsim/test_eppke.py new file mode 100644 index 000000000..e52e96e1e --- /dev/null +++ b/tests/hwsim/test_eppke.py @@ -0,0 +1,675 @@ +# Test cases for Enhanced Privacy Protection Key Exchange(EPPKE) +# Copyright (c) 2025, Qualcomm Innovation Center, Inc. +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. + +import time + +import hostapd +from wpasupplicant import WpaSupplicant +from utils import * +from hwsim import HWSimRadio +from test_eht import eht_mld_ap_wpa2_params, eht_mld_enable_ap, traffic_test, eht_verify_status + +def test_eppke_akm_suite_and_rsnxe_feature_flags(dev, apdev): + """AP EPPKE AKM Advertisement with SAE base AKM and EPPKE related feature flags""" + ssid = "test-eppke-authentication" + params = hostapd.wpa3_params(ssid=ssid, + password = "1234567890") + params['wpa_key_mgmt'] = params['wpa_key_mgmt'] + ' ' + 'EPPKE' + params['assoc_frame_encryption'] = '1' + params['pmksa_caching_privacy'] = '1' + params['eap_using_authentication_frames'] = '1' + params['sae_pwe'] = '2' + + hapd = hostapd.add_ap(apdev[0], params) + time.sleep(2) + #TODO: Add pcap file checks to validate correct + #RSNXE bits and AKM suite presence in Beacon frames + + #Disable all EPPKE related RSNXE flags and test + params['assoc_frame_encryption'] = '0' + params['pmksa_caching_privacy'] = '0' + params['eap_using_authentication_frames'] = '0' + hapd = hostapd.add_ap(apdev[0], params) + time.sleep(2) + +def test_eppke_ap_with_base_akm_sae_legacy_client(dev, apdev): + """EPPKE authentication with a Non-MLO AP with base AKM SAE and legacy client""" + ssid = "test-eppke-authentication" + passphrase = '1234567890' + params = hostapd.wpa3_params(ssid=ssid, + password = passphrase) + params['wpa_key_mgmt'] = params['wpa_key_mgmt'] + ' ' + 'EPPKE' + params['assoc_frame_encryption'] = '1' + params['pmksa_caching_privacy'] = '1' + params['eap_using_authentication_frames'] = '1' + params['sae_pwe'] = '2' + hapd = hostapd.add_ap(apdev[0], params) + + try: + dev[0].set("sae_groups", "") + dev[0].set("sae_pwe", "1") + dev[0].connect(ssid, sae_password=passphrase, scan_freq="2412", + key_mgmt="SAE", ieee80211w="2", beacon_prot="1", + pairwise="CCMP") + hapd.wait_sta(); + sta = hapd.get_sta(dev[0].own_addr()) + if sta["AKMSuiteSelector"] != '00-0f-ac-8' or sta["auth_alg"] != '9': + raise Exception("Incorrect Auth Algo/AKMSuiteSelector value") + + finally: + dev[0].set("sae_groups", "") + dev[0].set("sae_pwe", "0") + +def run_eppke_sae_ext_key(dev, apdev, group): + """EPPKE authentication with a Non-MLO AP with base AKM SAE-EXT-KEY and legacy client""" + ssid = "test-eppke-authentication" + passphrase = '1234567890' + params = hostapd.wpa3_params(ssid=ssid, + password = passphrase) + params['wpa_key_mgmt'] = params['wpa_key_mgmt'] + ' ' + 'SAE-EXT-KEY EPPKE' + params['assoc_frame_encryption'] = '1' + params['pmksa_caching_privacy'] = '1' + params['eap_using_authentication_frames'] = '1' + params['sae_pwe'] = '2' + params['pasn_groups'] = str(group) + params['sae_groups'] = str(group) + hapd = hostapd.add_ap(apdev[0], params) + + try: + dev[0].set("sae_groups", str(group)) + dev[0].set("sae_pwe", "1") + dev[0].connect(ssid, sae_password=passphrase, scan_freq="2412", + key_mgmt="SAE-EXT-KEY", ieee80211w="2", beacon_prot="1", + pairwise="CCMP") + hapd.wait_sta(); + sta = hapd.get_sta(dev[0].own_addr()) + if sta["AKMSuiteSelector"] != '00-0f-ac-24' or sta["auth_alg"] != '9': + raise Exception("Incorrect Auth Algo/AKMSuiteSelector value") + + finally: + dev[0].set("sae_groups", "") + dev[0].set("sae_pwe", "0") + +def test_eppke_ap_with_base_akm_sae_ext_legacy_client_19(dev, apdev): + """EPPKE authentication with a Non-MLO AP with base AKM SAE-EXT-KEY and legacy client, group 19""" + run_eppke_sae_ext_key(dev, apdev, 19) + +def test_eppke_ap_with_base_akm_sae_ext_legacy_client_20(dev, apdev): + """EPPKE authentication with a Non-MLO AP with base AKM SAE-EXT-KEY and legacy client, group 20""" + run_eppke_sae_ext_key(dev, apdev, 20) + +def test_eppke_ap_with_base_akm_sae_ext_legacy_client_21(dev, apdev): + """EPPKE authentication with a Non-MLO AP with base AKM SAE-EXT-KEY and legacy client, group 21""" + run_eppke_sae_ext_key(dev, apdev, 21) + +def test_eppke_mld_ap_with_base_akm_sae_legacy_client(dev, apdev): + """EPPKE authentication with an MLD AP with base AKM SAE and legacy client""" + with HWSimRadio(use_mlo=True) as (hapd_radio, hapd_iface): + passphrase = '1234567890' + ssid = "test-eppke-authentication" + params = eht_mld_ap_wpa2_params(ssid, passphrase, + key_mgmt="SAE", mfp="2", pwe='1') + params['wpa_key_mgmt'] = params['wpa_key_mgmt'] + ' ' + 'EPPKE' + params['assoc_frame_encryption'] = '1' + params['pmksa_caching_privacy'] = '1' + params['eap_using_authentication_frames'] = '1' + params['rsn_pairwise'] = "CCMP GCMP-256" + + hapd0 = eht_mld_enable_ap(hapd_iface, 0, params) + + params['channel'] = '6' + hapd1 = eht_mld_enable_ap(hapd_iface, 1, params) + + try: + dev[0].set("sae_groups", "") + dev[0].set("sae_pwe", "1") + dev[0].connect(ssid, sae_password=passphrase, scan_freq="2412", + key_mgmt="SAE", ieee80211w="2", beacon_prot="1", + pairwise="CCMP GCMP-256") + bssid = dev[0].get_status_field("bssid") + if hapd0.own_addr() == bssid: + hapd0.wait_sta(); + sta = hapd0.get_sta(dev[0].own_addr()) + if sta["AKMSuiteSelector"] != '00-0f-ac-8' or sta["auth_alg"] != '9': + raise Exception("Incorrect Auth Algo/AKMSuiteSelector value") + elif hapd1.own_addr() == bssid: + hapd1.wait_sta(); + sta = hapd1.get_sta(dev[0].own_addr()) + if sta["AKMSuiteSelector"] != '00-0f-ac-8' or sta["auth_alg"] != '9': + raise Exception("Incorrect Auth Algo/AKMSuiteSelector value") + else: + raise Exception("Unknown BSSID: " + bssid) + 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_gtk_rekey_with_base_akm_sae_ext_key_one_link(dev, apdev): + """EPPKE AP and GTK rekey with MLO AP with 1 link""" + 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="SAE-EXT-KEY", 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" + params['wpa_group_rekey'] = '1' + 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="SAE-EXT-KEY", 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_ap_gtk_rekey_with_base_akm_sae_ext_key_two_link(dev, apdev): + """EPPKE AP and GTK rekey with MLO AP with 2 links""" + 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="SAE-EXT-KEY", 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" + params['wpa_group_rekey'] = '1' + 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="SAE-EXT-KEY", 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 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.34.1 From ainy.kumari at oss.qualcomm.com Thu Jan 15 08:32:59 2026 From: ainy.kumari at oss.qualcomm.com (Ainy Kumari) Date: Thu, 15 Jan 2026 22:02:59 +0530 Subject: [PATCH v3 20/27] sme: Add support to install temporal key for EPPKE Authentication Protocol In-Reply-To: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> Message-ID: <20260115163306.367753-21-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.34.1 From trevor at freedisc.co.uk Sat Jan 17 03:31:48 2026 From: trevor at freedisc.co.uk (Trevor North) Date: Sat, 17 Jan 2026 11:31:48 +0000 Subject: [PATCH] Add support for HE PHY type in neighbor reports Message-ID: <20260117113148.77268-1-trevor@freedisc.co.uk> Add support for HE PHY type in neighbor reports as defined in IEEE Std 802.11-2024 dot11PHYType. To the best of my understanding this is the intended behaviour and I have observed no adverse effects with the clients I have available in my environment. I must caveat however that I am far from a subject matter expert and most of my clients are modern. Signed-off-by: Trevor North --- src/ap/neighbor_db.c | 2 +- src/common/ieee802_11_common.c | 4 +++- src/common/ieee802_11_common.h | 2 +- src/common/ieee802_11_defs.h | 1 + wpa_supplicant/rrm.c | 4 +++- wpa_supplicant/wnm_sta.c | 4 +++- 6 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c index 0239ec259..ca651da18 100644 --- a/src/ap/neighbor_db.c +++ b/src/ap/neighbor_db.c @@ -310,7 +310,7 @@ void hostapd_neighbor_set_own_report(struct hostapd_data *hapd) wpabuf_put_le32(nr, bssid_info); wpabuf_put_u8(nr, op_class); wpabuf_put_u8(nr, channel); - wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht)); + wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht, he)); /* * Wide Bandwidth Channel subelement may be needed to allow the diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index 5d1e02f81..61fd6134b 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -2528,8 +2528,10 @@ static enum phy_type ieee80211_phy_type_by_freq(int freq) /* ieee80211_get_phy_type - Derive the phy type by freq and bandwidth */ -enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht) +enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht, int he) { + if (he) + return PHY_TYPE_HE; if (vht) return PHY_TYPE_VHT; if (ht) diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index 09bd0675a..d869bec24 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -257,7 +257,7 @@ int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes, u16 num_modes); int is_dfs_global_op_class(u8 op_class); bool is_80plus_op_class(u8 op_class); -enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht); +enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht, int he); int supp_rates_11b_only(struct ieee802_11_elems *elems); int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf, diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 89f0a1bf8..03399e0f3 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -2411,6 +2411,7 @@ enum phy_type { PHY_TYPE_S1G = 11, PHY_TYPE_CDMG = 12, PHY_TYPE_CMMG = 13, + PHY_TYPE_HE = 14, }; /* IEEE Std 802.11-2024, 9.4.2.35 - Neighbor Report element */ diff --git a/wpa_supplicant/rrm.c b/wpa_supplicant/rrm.c index 74f190e34..dabbf8cfa 100644 --- a/wpa_supplicant/rrm.c +++ b/wpa_supplicant/rrm.c @@ -805,8 +805,10 @@ int wpas_get_op_chan_phy(int freq, const u8 *ies, size_t ies_len, return -1; } + int he = get_ie_ext(ies, ies_len, WLAN_EID_EXT_HE_OPERATION) != NULL; + *phy_type = ieee80211_get_phy_type(freq, ht_oper != NULL, - vht_oper != NULL); + vht_oper != NULL, he); if (*phy_type == PHY_TYPE_UNSPECIFIED) { wpa_printf(MSG_DEBUG, "Cannot determine phy type"); return -1; diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index a09f47620..07b59b198 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -926,8 +926,10 @@ static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s, return -2; } + int he = wpa_bss_get_ie_ext(bss, WLAN_EID_EXT_HE_OPERATION) != NULL; + phy_type = ieee80211_get_phy_type(bss->freq, (ht_oper != NULL), - (vht_oper != NULL)); + (vht_oper != NULL), he); if (phy_type == PHY_TYPE_UNSPECIFIED) { wpa_printf(MSG_DEBUG, "WNM: Cannot determine BSS phy type for Neighbor Report"); -- 2.52.0 From pmartin-gomez at freebox.fr Mon Jan 19 03:52:49 2026 From: pmartin-gomez at freebox.fr (Pablo MG) Date: Mon, 19 Jan 2026 12:52:49 +0100 Subject: [PATCH] Add support for HE PHY type in neighbor reports In-Reply-To: <20260117113148.77268-1-trevor@freedisc.co.uk> References: <20260117113148.77268-1-trevor@freedisc.co.uk> Message-ID: <30a02fe0-4560-4c4f-ae63-b213d1caab8c@freebox.fr> Hello, Le 17/01/2026 ? 12:31, Trevor North a ?crit?: > Add support for HE PHY type in neighbor reports as defined in > IEEE Std 802.11-2024 dot11PHYType. > > To the best of my understanding this is the intended behaviour and I > have observed no adverse effects with the clients I have available in my > environment. I must caveat however that I am far from a subject matter > expert and most of my clients are modern. This is a part of the standard I'm interested in and as far as I understand it, the PHY Type field is more intended for non HT/VHT/HE/EHT STAs; for HT/VHT/HE/EHT STAs you have the bits in the BSSID Information field. But I don't think there is harm in updating the PHY Type to the newest PHY type supported. > diff --git a/wpa_supplicant/rrm.c b/wpa_supplicant/rrm.c > index 74f190e34..dabbf8cfa 100644 > --- a/wpa_supplicant/rrm.c > +++ b/wpa_supplicant/rrm.c > @@ -805,8 +805,10 @@ int wpas_get_op_chan_phy(int freq, const u8 *ies, size_t ies_len, If you are going to update this function to support HE, you should also update it to support (correctly) 6 GHz operation. If the current frequency is in the 6 GHz band, both `ht_oper` and `vht_oper` are NULL, and `sec_chan` and `vht` have their default so `ieee80211_freq_to_channel_ext` is going to return the wrong `op_class` (correct band but wrong bandwidth). > return -1; > } > > + int he = get_ie_ext(ies, ies_len, WLAN_EID_EXT_HE_OPERATION) != NULL; > + > *phy_type = ieee80211_get_phy_type(freq, ht_oper != NULL, > - vht_oper != NULL); > + vht_oper != NULL, he); > if (*phy_type == PHY_TYPE_UNSPECIFIED) { > wpa_printf(MSG_DEBUG, "Cannot determine phy type"); > return -1; > diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c > index a09f47620..07b59b198 100644 > --- a/wpa_supplicant/wnm_sta.c > +++ b/wpa_supplicant/wnm_sta.c > @@ -926,8 +926,10 @@ static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s, Same issue than with `wpas_get_op_chan_phy` > return -2; > } > > + int he = wpa_bss_get_ie_ext(bss, WLAN_EID_EXT_HE_OPERATION) != NULL; > + > phy_type = ieee80211_get_phy_type(bss->freq, (ht_oper != NULL), > - (vht_oper != NULL)); > + (vht_oper != NULL), he); > if (phy_type == PHY_TYPE_UNSPECIFIED) { > wpa_printf(MSG_DEBUG, > "WNM: Cannot determine BSS phy type for Neighbor Report"); Best regards, Pablo MG From nchernikov at maxlinear.com Thu Jan 15 02:18:28 2026 From: nchernikov at maxlinear.com (Nikita Chernikov) Date: Thu, 15 Jan 2026 10:18:28 +0000 Subject: [PATCH] Beacon protection with MBSSID: Fix getting correct pn for bigtk Message-ID: >From d0bdc5446f1d3d924d08f17f97187ff4a6407515 Mon Sep 17 00:00:00 2001 From: Nikita Chernikov Date: Thu, 15 Jan 2026 01:51:42 -0800 Subject: [PATCH] Beacon protection with MBSSID: Fix getting correct pn for bigtk To: hostap at lists.infradead.org When STA is connecting to a MBSSID non-TX BSS, pn for bigtk is requested from a non beaconing BSS, when it should be requested for the TX BSS in MBSSID. This is due to pn is requested from original auth when it should be requested from the tx_bss_auth which exists and replaced in case of MBSSID. Signed-off-by: Nikita Chernikov --- src/ap/wpa_auth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 49268b21e..b28675cb4 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -4211,7 +4211,7 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) bigtk.keyid[0] = gsm->GN_bigtk; bigtk.keyid[1] = 0; if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE || - wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, rsc) < 0) + wpa_auth_get_seqnum(wpa_auth, NULL, gsm->GN_bigtk, rsc) < 0) os_memset(bigtk.pn, 0, sizeof(bigtk.pn)); else os_memcpy(bigtk.pn, rsc, sizeof(bigtk.pn)); -- 2.43.5 From abhishek.suryawanshi at oss.qualcomm.com Mon Jan 12 10:52:52 2026 From: abhishek.suryawanshi at oss.qualcomm.com (Abhishek Rajkapur Suryawanshi) Date: Mon, 12 Jan 2026 10:52:52 -0800 Subject: [DESIGN RFC v3] AP Architecture for Wi-Fi-8 Multi-AP Coordination (MAPC) Message-ID: <653a4207-38f8-408c-8067-c4960c11a71f@oss.qualcomm.com> Hello Everyone, This RFC presents a high-level design proposal for Multi-AP Coordination framework in IEEE 802.11bn (Wi-Fi 8). The design is based on IEEE 802.11bn-D1.1 (as of Dec 2025) and may be updated based on future draft updates. We hope to gather feedback on the proposed architecture and subsystem integration strategy. This document does not include implementation patches or code and is intended solely for design-level discussion. ============================== Table of Contents ============================== (1) Introduction & Scope (2) Background (3) Proposal (3.1) Control Path ============================== (1) Introduction & Scope ============================== The IEEE 802.11bn specification has introduced the concept of Multi-AP Coordination (MAPC) framework which includes set of schemes (such as Co-BF, Co-SR, Co-TDMA, Co-RTWT and Co-CR) and procedure in which APs operating their BSSs on the same primary 20 MHz channel coordinate to reduce interference and to improve network performance, such as medium utilization efficiency, communication reliability, and latency. The scope of this RFC is as follows: 1. Enable MAPC framework support across hostapd, mac80211, cfg80211 and WLAN-drivers up to 802.11bn-D1.1 2. Focus on AP-side support - Capability exchange - Feature-specific negotiation - MAPC context management 3. Focus on WiFi based inter-AP coordination Multi-AP coordination over wired connections will be added later, based on specification updates The following sub-features of the MAPC framework will be deferred to subsequent versions of this RFC, as the specification is still evaluating options for MAPC frame handling in multi-cohosted VAP and MBSSID scenarios: - MAPC security Patches will be proposed in the subsequent version of the RFC once we have achieved general consensus on the architecture. ============================== (2) Background ============================== The MAPC framework operates in three phases: - Phase 1 Discovery AP advertise MAPC capabilities and common parameters using Public Action MAPC Discovery Request/Response frames (Ref.IEEE 802.11bn-D1.1, 9.6.7.65 to 9.6.7.66). - Phase 2 Negotiation AP establish, update, or teardown scheme-specific agreements (e.g., Co-BF, Co-SR, Co-TDMA, Co-RTWT, Co-CR) using individually addressed MAPC Negotiation Request/Response frames (Ref. IEEE 802.11bn-D1.1, 9.6.7.68 to 9.6.7.69). - APID Assignment When establishing the first MAPC agreement the AP assigns an APID to the peer AP (Ref. IEEE 802.11bn-D1.1, 9.4.1.8). APIDs share the AID pool(1 to 2006) with non-AP STAs. APIDs remain valid while any agreement exists. - Phase 3 Operations Feature specific frame exchange with peer AP for the established agreement. ============================= (3) Proposal ============================= (3.1) Control Path (MAPC) The MAPC control path is handled in user space by hostapd. The kernel layers (cfg80211 and mac80211) pass the required information to the WLAN driver. Figure 1: Proposed Subsystem Integration for MAPC ----------------------------------------------------- +---------------------------------------------------------------+ | hostapd | | | | +----------------------+ +----------------------+ | | | MAPC Discovery | | MAPC Negotiation | | | +----------------------+ +----------------------+ | | | | +----------------------+ +----------------------+ | | | MAPC Peer Mgmt | | MAPC Security | | | +----------------------+ +----------------------+ | | | | +----------------------+ | | | NL80211 | | | +----------------------+ | +---------------------------------------------------------------+ +---------------------------------------------------------------+ | cfg80211 | | | | +----------------------+ | | | NL80211 | | | +----------------------+ | +---------------------------------------------------------------+ +---------------------------------------------------------------+ | mac80211 | | | | +----------------------+ | | | MAPC Peer Mgmt | | | +----------------------+ | +---------------------------------------------------------------+ +---------------------------------------------------------------+ | WLAN Driver (e.g., ath12k) | | | | +----------------------+ +---------------------------+ | | | MAPC Peer Mgmt | | MAPC Policy Config Mgmt | | | +----------------------+ +---------------------------+ | | | | +---------------------------+ | | | MAPC Event Gen/Notify | | | +---------------------------+ | +---------------------------------------------------------------+ (A) Hostapd - Protocol Parsing & MAPC context management: hostapd handles the transmission and reception of MAPC Discovery and MAPC Negotiation public action frames with neighboring APs. - MAPC discovery: - After BSS setup, hostapd may initiate MAPC Discovery exchanges with neighboring APs. - On receiving a MAPC Discovery frame from a peer AP, hostapd: - Extracts MAPC capabilities, common parameters and (if present) Per Scheme Profiles. - Creates a MAPC peer in hostapd and triggers peer creation in the kernel using NL80211_CMD_NEW_STATION with NL80211_ATTR_MAPC_PEER, marking the station as a MAPC peer. Figure 2: MAPC Discovery Sequence ----------------------------------------------------- +--------------------------------------------------------------------+ | UHR AP-1 | | +---------+ +---------+ +---------+ +---------+ | +---------+ | | | | | | | | | | | | | | hostapd | | cfg80211| | mac80211| | wlan | | | UHR | | | | | | | | | driver | | | AP-2 | | +----+----+ +----+----+ +----+----+ +----+----+ | +----+----+ | | | | | | | | +----+---------------+ | | | | | | | init hostapd & HW | | | | | | | | MAPC support update| | | | | | | +----+---------------+ | MAPC Discovery | | | | | | | Request | | | | | | |(MAPC capability)| | | | | +-----------------+-----------------+-----------------+------|------> | | | MAPC Discovery | | | | | | | Response | | | | | | |(MAPC capability)| | | | | <-----------------+-----------------+-----------------+------|------+ | | | | | | | | +----+---------------+ | | | | | | |alloc sta_info store| | | | | | | |MAPC capa & params | | | | | | | +----+---------------+ | | | | | | | | | | | | | nl80211_new_station() | | | | | +-----------------> | | | | | |(attr MAPC_PEER) | | | | | | | | | | | | | | |rdev_add_station() | | | | | +-----------------> | | | | | | | | | | | | | +-----------------+ | | | | | | | alloc mapc sta | | | | | | | | object | | | | | | | +-----------------+ | | | | | | | | | | | | | +-----------------> | | | | | | | | | | | | | +-----------------+| | | | | | | alloc mapc sta || | | | | | | object || | | | | | +-----------------+| | | | | | | | | | v v v v | v +--------------------------------------------------------------------+ - MAPC Negotiation: - hostapd initiates and responds to MAPC Negotiation exchanges for one or more schemes. When a negotiation completes successfully: - hostapd programs the APID and per-scheme parameters into the kernel via NL80211_CMD_SET_STATION. - When all MAPC agreements with a peer AP are removed, hostapd deletes the MAPC peer from the kernel using NL80211_CMD_DEL_STATION Figure 3: MAPC Negotiation Sequence (ESTABLISHMENT/UPDATE) --------------------------------------------------------- +--------------------------------------------------------------------+ | UHR AP-1 | | +---------+ +---------+ +---------+ +---------+ | +---------+ | | | | | | | | | | | | | | hostapd | | cfg80211| | mac80211| | wlan | | | UHR | | | | | | | | | driver | | | AP-2 | | +----+----+ +----+----+ +----+----+ +----+----+ | +----+----+ | | | | | | | | | | | +--------------+-----+| | | | | | | Notify hostapd to || | | | | | | Trigger MAPC || | | | | | | Negotiation || | | | | | +--------------+-----+| | | | NL80211_CMD_MAPC_NEGOTIATION_TRIGGER(scheme-id,action) | | | <-----------------+-----------------+-----------------+ | | | +----+---------------+ | | | | | | |parse scheme-id & | | | | | | | |action. Init MAPC | |MAPC Negotiation | | | | | | Negotiation | | Request | | | | | +----+---------------+(scheme params & APID1 | | | | | Optype:ESTABLISHMENT/UPDATE) | | | | +-----------------+-----------------+-----------------+------+------> | | |MAPC Negotiation | | | | | | | Response | | | | | | (scheme params & APID2 | | | | | Optype:ACCEPT/REJECT/ALTERNATE) | | | | <-----------------+-----------------+-----------------+------+------+ | +----+---------------+ | | | | | | |store APID & scheme | | | | | | | |params. | | | | | | | +----+---------------+ | | | | | | | | | | | | | nl80211_set_station() | | | | | +-----------------> | | | | | | (MAPC scheme | | | | | | | info & APID) |rdev_set_station() | | | | | +-----------------> | | | | | | +-----------------> | | | | | | | | | | | | | +--------------+| | | | | | | MAPC Policy || | | | | | | config set || | | | | | +--------------+| | | | | | | | | | v v v v | v +--------------------------------------------------------------------+ [NOTE] MAPC Negotiation AGREEMENT_UPDATE does not modify the APID assigned during AGRREMENT_ESTABLISHMENT Figure 4: MAPC Negotiation Sequence (TEARDOWN) ----------------------------------------------------- +--------------------------------------------------------------------+ | UHR AP-1 | | +---------+ +---------+ +---------+ +---------+ | +---------+ | | | | | | | | | | | | | | hostapd | | cfg80211| | mac80211| | wlan | | | UHR | | | | | | | | | driver | | | AP-2 | | +----+----+ +----+----+ +----+----+ +----+----+ | +----+----+ | | | | | | | | | | | +---------------+-----+| | | | | | |Notify hostapd to || | | | | | |Trigger MAPC || | | | | | |Negotiation(Teardown)|| | | | | | +---------------+-----+| | | | NL80211_CMD_MAPC_NEGOTIATION_TRIGGER(scheme-id,action) | | | <-----------------+-----------------+-----------------+ | | | +----+----------------+| | | | | | |parse scheme-id & || | | | | | |action. Init MAPC || | | | | | |Negotiation(Teardown)|| | | | | | +----+----------------+|MAPC Negotiation | | | | | | | Request | | | | | | |(Optype:TEARDOWN)| | | | | +-----------------+-----------------+-----------------+------+------> | | |MAPC Negotiation | | | | | | | Response | | | | | | | (Optype:ACCEPT) | | | | | <-----------------+-----------------+-----------------+------+------+ | +----+----------------+| | | | | | |If agreement count || | | | | | |is zero for MAPC peer|| | | | | | |then initiate MAPC || | | | | | |peer delete || | | | | | +----+----------------+| | | | | | | | | | | | | nl80211_del_station() | | | | | +-----------------> | | | | | | | | | | | | | |rdev_del_station() | | | | | +-----------------> | | | | | | +-----------------> | | | | | | | | | | | | | +--------------+| | | | | | | MAPC peer || | | | | | | clean-up || | | | | | +--------+-----+| | | | | | | | | | | | | | | | | | | | | | | +-------v-----------------v-----------------v-----------------v------+ v [NOTE] The MAPC peer concept is necessary because the specification defines protected-dual variants of the MAPC Negotiation Request and Response frames. When secured MAPC negotiation is required, each coordinating AP must maintain a separate MPASN derived security context for every neighboring AP. (B) cfg80211 cfg80211 handles the following commands and notifications for MAPC - NL80211_CMD_NEW_STATION: to create a MAPC station for an MAPC peer, marked by NL80211_ATTR_MAPC_PEER. - NL80211_CMD_SET_STATION: to set APID and per scheme parameters. - NL80211_CMD_DEL_STATION: to delete MAPC stations - NL80211_CMD_GET_STATION: to retrieve MAPC station information New notifications from cfg80211 to user space: - NL80211_CMD_MAPC_NEGOTIATION_TRIGGER Direction: kernel to user space. Source: cfg80211 on behalf of the WLAN driver/firmware. Purpose: Provide a hint to hostapd that MAPC Negotiation Request should be triggered on this interface. For each MAPC scheme, the driver/firmware may request establishment, update, or teardown of an agreement. hostapd is expected to apply policy and iterate over the locally known MAPC peers for this interface. Attributes: - NL80211_ATTR_MAPC_SCHEMES (nested) Contains one or more per-scheme MAPC negotiation hints. Each nested entry includes: - NL80211_ATTR_MAPC_SCHEME_TYPE Identifies the MAPC scheme (e.g., Co-BF, Co-SR, Co-TDMA, Co-RTWT, Co-CR). - NL80211_ATTR_MAPC_ACTION Requested negotiation action for this scheme: - NL80211_MAPC_ACTION_ESTABLISH: establish new agreement(s). - NL80211_MAPC_ACTION_UPDATE: update existing agreement(s). - NL80211_MAPC_ACTION_TEARDOWN: teardown existing agreement(s). (C) mac80211 Two design options are considered for representing MAPC peers in mac80211: Option-A(Preferred): Reuse existing station management infra - Allocate standard station objects (struct sta_info / struct ieee80211_sta) to represent MAPC peers, and call the WLAN driver. - Use a subset of the existing mac80211 station states (NOTEXIST, NONE, AUTH) to represent a MAPC stations. - Receive MAPC parameters from cfg80211 and forward them to the WLAN driver (for example, via existing station update paths). - Use NL80211_ATTR_MAPC_PEER to mark a station as a MAPC station, so that data path features (e.g., rate control, aggregation, buffering) can be bypassed. Pros: - Minimal changes to existing mac80211 data structures and state machine. - Reuses existing add_station / sta_state transitions and driver callbacks. Cons: - Overloads the full station infra for control only usage. Option-B: Define lightweight MAPC peer/station management infra In this option, mac80211 defines a lightweight MAPC station management infra, decoupled from normal data-path stations: - MAPC requesting and responding APs exchange only management/control frames. Most datapath related fields in the existing sta_info/ ieee80211_sta structures are not applicable. - Defines a lightweight MAPC station object (e.g., struct ieee80211_mapc_sta)to hold MAPC peer context, including: - Peer MAC address (BSSID), APID, MAPC security context. MAPC capabilities and common parameters. - A separate MAPC station list is maintained in mac80211 to track all MAPC stations. New mac80211 to driver callbacks to be introduced, for example: - add_mapc_station() create a MAPC peer/station in the driver. - change_mapc_station() update APID and scheme parameters for an existing MAPC peer/station. - del_mapc_station() delete a MAPC peer/station in the driver Pros: - Clean separation between datapath stations and MAPC control only stations. Cons: - Requires new data structures and callbacks in mac80211 and WLAN driver. - Slightly larger implementation versus Option A. (D) WLAN Driver - The WLAN driver maps MAPC stations from mac80211 into driver peers and programs scheme specific parameters for Phase 3 operations. - MAPC station/peer creation and deletion: For Option A: - Reuse standard station callbacks (add_station, change_station, remove_station) and treat the MAPC peers as control-only (no data-path handling). For Option B: - Implement the new MAPC-specific callbacks (add_mapc_station, change_mapc_station, del_mapc_station) to manage MAPC peers. - Event reporting: - Inform mac80211/cfg80211 that MAPC negotiation should be establish, update or torn down with NL80211_CMD_MAPC_NEGOTIATION_TRIGGER. Regards, Abhishek From benjamin at sipsolutions.net Tue Jan 20 01:45:04 2026 From: benjamin at sipsolutions.net (Benjamin Berg) Date: Tue, 20 Jan 2026 10:45:04 +0100 Subject: [PATCH] AP: send TTLM if a link is indicated as disabled In-Reply-To: <20251030122957.240082-1-benjamin@sipsolutions.net> References: <20251030122957.240082-1-benjamin@sipsolutions.net> Message-ID: <1ecfff69b646d86ccdce433d3b452ddcc8794740.camel@sipsolutions.net> Hi, note that the relevant patches got merged into wireless, so the test is currently failing. For reference: https://patch.msgid.link/20260118093904.754e057896a5.Ifd06f5ef839a93bfd54d0593dc932870f95f3242 at changeid Benjamin On Thu, 2025-10-30 at 13:29 +0100, Benjamin Berg wrote: > From: Benjamin Berg > > When a link is indicated as disabled, then a corresponding TTLM > should > be sent. Add the appropriate code to generate the TTLM. > > Co-authored-by: Ilan Peer > Signed-off-by: Benjamin Berg > Signed-off-by: Ilan Peer > > --- > > Hi, > > we are going to queue some mac80211 fixes for TTLM handling during > association. Without this fix, the eht_mld_owe_two_links_one_disabled > hwsim test will start failing as mac80211 does not know that the > second > link is disabled. > > Benjamin > --- > ?src/ap/beacon.c????????????? | 12 ++++++++ > ?src/ap/ieee802_11.c????????? |? 4 +++ > ?src/ap/ieee802_11.h????????? |? 3 ++ > ?src/ap/ieee802_11_eht.c????? | 57 > ++++++++++++++++++++++++++++++++++++ > ?src/common/ieee802_11_defs.h | 10 +++++++ > ?5 files changed, 86 insertions(+) > > diff --git a/src/ap/beacon.c b/src/ap/beacon.c > index 5a99611197..9ffff4f7d2 100644 > --- a/src/ap/beacon.c > +++ b/src/ap/beacon.c > @@ -797,6 +797,10 @@ static size_t > hostapd_probe_resp_elems_len(struct hostapd_data *hapd, > ? * switch */ > ? buflen += 6; > ? } > + > + if (hapd->conf->mld_ap) > + buflen += > hostapd_eid_eht_ml_tid_to_link_map_len( > + hapd); > ? } > ?#endif /* CONFIG_IEEE80211BE */ > ? > @@ -966,6 +970,9 @@ static u8 * hostapd_probe_resp_fill_elems(struct > hostapd_data *hapd, > ? > ? pos = hostapd_eid_eht_capab(hapd, pos, > IEEE80211_MODE_AP); > ? pos = hostapd_eid_eht_operation(hapd, pos); > + > + if (hapd->conf->mld_ap) > + pos = > hostapd_eid_eht_ml_tid_to_link_map(hapd, pos); > ? } > ?#endif /* CONFIG_IEEE80211BE */ > ? > @@ -2256,6 +2263,8 @@ int ieee802_11_build_ap_params(struct > hostapd_data *hapd, > ? */ > ? if (hapd->conf->mld_ap) { > ? tail_len += 256; > + tail_len += > + hostapd_eid_eht_ml_tid_to_link_map_l > en(hapd); > ? > ? /* for Max Channel Switch Time element > during channel > ? * switch */ > @@ -2444,6 +2453,9 @@ int ieee802_11_build_ap_params(struct > hostapd_data *hapd, > ? tailpos = hostapd_eid_eht_capab(hapd, tailpos, > ? IEEE80211_MODE_AP); > ? tailpos = hostapd_eid_eht_operation(hapd, tailpos); > + if (hapd->conf->mld_ap) > + tailpos = > hostapd_eid_eht_ml_tid_to_link_map(hapd, > + ???? > tailpos); > ? } > ?#endif /* CONFIG_IEEE80211BE */ > ? > diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c > index dc3b397c5f..49731cc965 100644 > --- a/src/ap/ieee802_11.c > +++ b/src/ap/ieee802_11.c > @@ -5411,6 +5411,8 @@ static u16 send_assoc_resp(struct hostapd_data > *hapd, struct sta_info *sta, > ? buflen += 3 + sizeof(struct > ieee80211_eht_operation); > ? if (hapd->iconf->punct_bitmap) > ? buflen += > EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE; > + if (ap_sta_is_mld(hapd, sta)) > + buflen += > hostapd_eid_eht_ml_tid_to_link_map_len(hapd); > ? } > ?#endif /* CONFIG_IEEE80211BE */ > ? > @@ -5572,6 +5574,8 @@ rsnxe_done: > ? p = hostapd_eid_eht_ml_assoc(hapd, sta, p); > ? p = hostapd_eid_eht_capab(hapd, p, > IEEE80211_MODE_AP); > ? p = hostapd_eid_eht_operation(hapd, p); > + if (ap_sta_is_mld(hapd, sta)) > + p = hostapd_eid_eht_ml_tid_to_link_map(hapd, > p); > ? } > ?#endif /* CONFIG_IEEE80211BE */ > ? > diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h > index 77704faaec..4764dc8ab8 100644 > --- a/src/ap/ieee802_11.h > +++ b/src/ap/ieee802_11.h > @@ -323,4 +323,7 @@ void hostapd_link_reconf_resp_tx_status(struct > hostapd_data *hapd, > ? const struct ieee80211_mgmt > *mgmt, > ? size_t len, int ok); > ? > +size_t hostapd_eid_eht_ml_tid_to_link_map_len(struct hostapd_data > *hapd); > +u8 *hostapd_eid_eht_ml_tid_to_link_map(struct hostapd_data *hapd, u8 > *eid); > + > ?#endif /* IEEE802_11_H */ > diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c > index ac36c9c48c..9a88dace4c 100644 > --- a/src/ap/ieee802_11_eht.c > +++ b/src/ap/ieee802_11_eht.c > @@ -2748,3 +2748,60 @@ void ieee802_11_rx_protected_eht_action(struct > hostapd_data *hapd, > ? ?? "MLD: Unsupported Protected EHT Action %u from " > MACSTR > ? ?? " discarded", action, MAC2STR(mgmt->sa)); > ?} > + > + > +size_t hostapd_eid_eht_ml_tid_to_link_map_len(struct hostapd_data > *hapd) > +{ > + if (!hapd->conf->mld_ap) > + return 0; > + > +#ifdef CONFIG_TESTING_OPTIONS > + /* > + * Allocate enough space for mld_indicate_disabled. i.e.: > + *? EID, Length and extended EID (3) + > + *? control including presence bitmap (2) + 8 * 2 byte link > mappings > + */ > + return 3 + 2 + 8 * 2; > +#else > + return 0; > +#endif /* CONFIG_TESTING_OPTIONS */ > +} > + > + > +u8 *hostapd_eid_eht_ml_tid_to_link_map(struct hostapd_data *hapd, u8 > *eid) > +{ > +#ifdef CONFIG_TESTING_OPTIONS > + struct hostapd_data *other_hapd; > + bool need_ttlm = false; > + u16 ttlm = 0; > +#endif > + u8 *pos = eid; > + > + if (!hapd->conf->mld_ap) > + return eid; > + > +#ifdef CONFIG_TESTING_OPTIONS > + for_each_mld_link(other_hapd, hapd) { > + if (other_hapd->conf->mld_indicate_disabled) > + need_ttlm = true; > + else > + ttlm |= BIT(other_hapd->mld_link_id); > + } > + > + if (need_ttlm) { > + *pos++ = WLAN_EID_EXTENSION; > + /* ext EID + 2 bytes control + 8 * link mappings */ > + *pos++ = 1 + 2 + 8 * 2; > + *pos++ = WLAN_EID_EXT_TID_TO_LINK_MAPPING; > + *pos++ = EHT_TID_TO_LINK_MAP_DIRECTION_BOTH; > + *pos++ = 0xff; /* link mapping presence bitmap */ > + > + for (int i = 0; i < 8; i++) { > + WPA_PUT_LE16(pos, ttlm); > + pos += 2; > + } > + } > +#endif /* CONFIG_TESTING_OPTIONS */ > + > + return pos; > +} > diff --git a/src/common/ieee802_11_defs.h > b/src/common/ieee802_11_defs.h > index 1d803660ca..9d8d0c2c56 100644 > --- a/src/common/ieee802_11_defs.h > +++ b/src/common/ieee802_11_defs.h > @@ -3103,6 +3103,16 @@ enum scs_direction { > ?#define EHT_QOS_CONTROL_INFO_PRESENCE_MASK_OFFSET 9 > ?#define EHT_QOS_CONTROL_INFO_LINK_ID_OFFSET 25 > ? > +/* IEEE P802.11REVmf/D1.0, 9.4.2.325 (TID-To-Link Mapping element) > */ > +#define EHT_TID_TO_LINK_MAP_DIRECTION_DOWN 0 > +#define EHT_TID_TO_LINK_MAP_DIRECTION_UP 1 > +#define EHT_TID_TO_LINK_MAP_DIRECTION_BOTH 2 > +#define EHT_TID_TO_LINK_MAP_DIRECTION_MSK 0x03 > +#define EHT_TID_TO_LINK_MAP_DEFAULT 0x04 > +#define EHT_TID_TO_LINK_MAP_SWITCH_TIME_PRESENT 0x08 > +#define EHT_TID_TO_LINK_MAP_EXPECT_DUR_PRESENT 0x10 > +#define EHT_TID_TO_LINK_MAP_LINK_MAPPING_SIZE 0x20 > + > ?/* > ? * IEEE P802.11be/D4.0, 9.4.2.316 QoS Characteristics element, > ? * Presence Bitmap Of Additional Parameters From benjamin at sipsolutions.net Thu Jan 22 05:14:59 2026 From: benjamin at sipsolutions.net (Benjamin Berg) Date: Thu, 22 Jan 2026 14:14:59 +0100 Subject: [PATCH v3 19/27] PASN: Add support for MIC computation for M3 frame for SME-in-Userspace In-Reply-To: <20260115163306.367753-20-ainy.kumari@oss.qualcomm.com> (sfid-20260115_173451_707617_60AA01E6) References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> <20260115163306.367753-20-ainy.kumari@oss.qualcomm.com> (sfid-20260115_173451_707617_60AA01E6) Message-ID: <48669e3fd4f4a5d88b0aa8ab14e2247186381cc1.camel@sipsolutions.net> Hi, because this is not the first time I have seen this type of bug. It would be good if everyone would enable ASAN for testing. It used to be problematic with UML, but that issues has been solved. These days, there should be no reason to not run hwsim tests with sanitizers enabled. See inline On Thu, 2026-01-15 at 22:02 +0530, Ainy Kumari wrote: > 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); Free'ing the "copy" means that "data" is also invalid. You need to move the free to the end of the function. > + } 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); Same here. Benjamin > + } 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; > ?} > ? From benjamin at sipsolutions.net Thu Jan 22 05:18:19 2026 From: benjamin at sipsolutions.net (Benjamin Berg) Date: Thu, 22 Jan 2026 14:18:19 +0100 Subject: [PATCH v3 17/27] EPPKE: Add support for EPPKE authentication for SME-in-Userspace case In-Reply-To: <20260115163306.367753-18-ainy.kumari@oss.qualcomm.com> (sfid-20260115_173436_047709_FDA79C93) References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> <20260115163306.367753-18-ainy.kumari@oss.qualcomm.com> (sfid-20260115_173436_047709_FDA79C93) Message-ID: <7f55b2b73c42e12087b5bc0687cb5fbfeefe5c8c.camel@sipsolutions.net> Hi, On Thu, 2026-01-15 at 22:02 +0530, Ainy Kumari wrote: > 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); > + You are not handling the error case here if (resp == NULL). Benjamin > + 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); From alex.gavin at candelatech.com Tue Jan 20 20:16:48 2026 From: alex.gavin at candelatech.com (Alex Gavin) Date: Tue, 20 Jan 2026 20:16:48 -0800 Subject: mt7996 AP Stops Beaconing after Channel Switch with 6.17+ Message-ID: <3872e1bb-20ab-45b5-87af-5df5bb1507c6@candelatech.com> Hi there. When performing an AP channel switch using a mt7996 radio, 6.17+ kernels and latest hostapd (both vanilla), the AP stops beaconing shortly after switching channels. The issue occurs regardless of band and notably does not occur when using other radios like the mt7915 with the same kernel and hostapd. The following command is used to trigger the channel switch from the hostapd CLI. Running the 'update_beacon' command after restores the AP back to beaconing state on the new channel. Config file used available here [1]. chan_switch 3 5745 sec_channel_offset=1 center_freq1=5775 bandwidth=80 he Interestingly, the CSA count matches the number of beacons sent on the new channel before stopping. In each of these beacons on the new channel, the destination MAC is sent with the first octet modified, which looks a lot like the CSA count decrementing. For example with CSA count set to three (edited for readability) using a 5 GHz AP initially configured to 5180 MHz switching to 5745 MHz a packet capture shows the following: $ tshark -r capture.pcapng xx:_:xx -> Broadcast 5180MHz ... Beacon frame <- non-CSA xx:_:xx -> Broadcast 5180MHz ... Beacon frame <- CSA 3 xx:_:xx -> Broadcast 5180MHz ... Beacon frame <- CSA 2 xx:_:xx -> Broadcast 5180MHz ... Beacon frame <- CSA 1 xx:_:xx -> 03:ff:ff:ff:ff:ff 5745MHz ... Beacon frame <- new channel xx:_:xx -> 02:ff:ff:ff:ff:ff 5745MHz ... Beacon frame xx:_:xx -> 01:ff:ff:ff:ff:ff 5745MHz ... Beacon frame (nothing) This has made for interesting 'spelunking' through the channel switching and beacon configuration infrastructure. However, I'm not confident I can come up with a fix in any sort of reasonable timeframe. I'd be happy to test any proposed fix and/or share any further information needed (e.g. 'trace-cmd' output). [1] https://codeberg.org/a-gavin/hostap-confs/src/branch/main/wpa2/hostapd_5GHz-wpa2.conf From abhishek.suryawanshi at oss.qualcomm.com Thu Jan 22 00:42:44 2026 From: abhishek.suryawanshi at oss.qualcomm.com (Abhishek Rajkapur Suryawanshi) Date: Thu, 22 Jan 2026 00:42:44 -0800 Subject: [DESIGN RFC v3] AP Architecture for Wi-Fi-8 Multi-AP Coordination (MAPC) In-Reply-To: <52356be3d21920d84579d1a8fd803540c6f9644d.camel@sipsolutions.net> References: <653a4207-38f8-408c-8067-c4960c11a71f@oss.qualcomm.com> <05381371a41f154058429f0a1079204c4a454f45.camel@sipsolutions.net> <59cec65b29ded381c85d1be943d88e956a4b7e74.camel@sipsolutions.net> <63ff0362-4ee4-4f13-a212-dc1351eefe08@oss.qualcomm.com> <52356be3d21920d84579d1a8fd803540c6f9644d.camel@sipsolutions.net> Message-ID: <88f05d03-2742-4b61-964f-54beafdd2e70@oss.qualcomm.com> On 1/15/2026 6:17 AM, Johannes Berg wrote: > On Wed, 2026-01-14 at 11:15 -0800, Jeff Johnson wrote: >> On 1/12/2026 11:18 AM, Johannes Berg wrote: >>> On Mon, 2026-01-12 at 20:12 +0100, Johannes Berg wrote: >>>> >>>> Why do you always want to let firmware be in control of everything? >>>> Seems at least for some of this you'd really want the upper layers to >>>> control it for purposes of coordination? How does the FW even know which >>>> other AP it can coordinate with, isn't that something a network >>>> controller would determine? >>> >>> A less generous reading of this could be: you guys want everything to be >>> controlled by FW, so you don't have to open-source it in hostapd. Now >>> you realize oops, don't really want to do all the security handshake in >>> FW, so we need to ask hostapd and then we need keys and stations and all >>> this stuff. So let's build something nobody else can use, upstream it >>> and we get the best of both worlds - others will maintain the mac80211 >>> code for us anyway. >>> >>> Am I wrong? Is there a technical reason for not simply doing MAPC >>> discovery/agreement negotiation etc. in hostapd as well, based on >>> driver/hw/fw capabilities, and then you don't need all these strange >>> "triggered by firmware" flows? >> >> My perception is that Qualcomm would love for all the Wi-Fi 8 functionality to >> be in userspace and nl/cfg/mac80211 since then there would be no code >> maintenance overhead on our part -- just you and the userspace maintainers. > > :) > >> But there are concerns about latency, and internal consensus is that some >> aspects of this functionality has to be handled in firmware (or even hardware) >> in order to meet customer KPIs. This comes from our history of supporting >> large-scale deployments of APs, and the expectations of how Wi-Fi 8 will make >> things better. That is why we are posting design RFCs, so that you, as well as >> engineers from other vendors, can review our proposals and give your feedback >> and counter-proposals. We want to avoid developing what might be an >> architecturally pure design that doesn't actually meet customer needs. > > Sure, that's fair. > > Maybe I can just ask folks to spell out the constraints and reasoning > behind the design? > > Taking this specific example, it basically says "FW sends a request to > hostapd, hostapd does the handshake and installs the MAPC station. This > is how we think we should handle the MAPC stations." hostapd controls and manages all MAPC related discovery and MAPC peer creation part. No trigger from firmware for MAPC Discovery Phase. > It never says _why_ and how any of this happens. What's the magic thing > only the firmware can do to figure out it should start coordinating a > given AP? (Clearly that operation can't be concerned much about latency > if it asks hostapd.) In our offload architecture, the firmware monitors various telemetry and controls all data?path?related scheduling. That's the primary reason why our design requires offload-based trigger for MAPC feature. Additionally, the offload-based design only signals intent or need; hostapd still owns MAPC peer selection to kick-start the MAPC negotiation. This trigger will be configurable (i.e could be enable/disable from user-space). > In fact I'd have expected that in certain cases some controller > infrastructure sitting *on top of hostapd* would decide which APs > coordinate with each other, rather than the firmware. Although I guess > if you hear the beacon on the same channel with a good enough RSSI then > you can coordinate. Offload based MAPC negotiation trigger is primarily intended for non?controller-based AP deployments. In controller?based AP configuration & deployments, controller is fully responsible for triggering hostapd to initiate the MAPC Negotiation Request (e.g., via centralized telemetry and policy enforcement). In these deployments, the offload base MAPC negotiation trigger shall be disabled. > I could keep guessing - maybe there's a limited space to do this in FW? > But there's not even anything built into the design where the firmware > can ask to _remove_ it again, as far as I can tell, unless there was an > (unstated) assumption that it can just delete the MAPC station and send > a DEL_STATION notification about it. Like offload based MAPC trigger intent for adding MAPC neighbors, it would be extended for the MAPC teardown intent as well. hostapd will still controls MAPC peer deletion & MAPC tear down protocol > The document even says that phase 1 is discovery, and then goes to > completely ignore phase 1 (it's hidden in FW), and describes basically > only phase 2. Phase 1 discovery is completely handled by hostapd, as outlined under section (A)hostapd >> And apologies for the "firehose" of both design and code, but we have a desire >> to ship Wi-Fi 8 products using upstream code. I've passed along information >> that you want our engineering team to focus on the base NPCA patches so that >> there is the appropriate foundation. But in parallel we do also hope there is >> engagement from other vendors on the Design RFCs we are posting. Our goal is >> to upstream as much AP functionality as you can absorb. > > Sure, and I appreciate that this is coming. I'm a little overwhelmed by > having so many parallel things going on right now, and all the parallel > design documents don't help. > > Maybe this is the point where we consider inviting everyone who wants to > a room for a few days? Even a video room might be better ;-) List some > topics first, present the design briefly, etc.? But I don't know if it's > just the medium. > > johannes Abhishek From benjamin at sipsolutions.net Thu Jan 22 07:21:48 2026 From: benjamin at sipsolutions.net (Benjamin Berg) Date: Thu, 22 Jan 2026 16:21:48 +0100 Subject: [PATCH] FILS: Fix memory leak if AES-SIV encryption fails Message-ID: <20260122162147.ce70615a9ed6.Iaf5f25425a0467ef17bc22105539cfa9ec625e52@changeid> From: Benjamin Berg The dynamically allocated header was not free'ed in the error path. Add the appropriate os_free call. Fixes: b729fd8df9f6 ("FILS: Use AEAD cipher to protect EAPOL-Key frames (AP)") Signed-off-by: Benjamin Berg --- src/ap/wpa_auth.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 49268b21e2..0155a3cecb 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -2178,6 +2178,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len, kde, kde_len, 1, aad, aad_len, key_mic + 2) < 0) { wpa_printf(MSG_DEBUG, "WPA: AES-SIV encryption failed"); + os_free(hdr); return; } -- 2.52.0 From benjamin at sipsolutions.net Thu Jan 22 23:04:23 2026 From: benjamin at sipsolutions.net (Benjamin Berg) Date: Fri, 23 Jan 2026 08:04:23 +0100 Subject: [PATCH v3 27/27] tests: Add EPPKE authentication test cases In-Reply-To: <20260115163306.367753-28-ainy.kumari@oss.qualcomm.com> (sfid-20260115_175340_369403_F8EA730A) References: <20260115163306.367753-1-ainy.kumari@oss.qualcomm.com> <20260115163306.367753-28-ainy.kumari@oss.qualcomm.com> (sfid-20260115_175340_369403_F8EA730A) Message-ID: <319c7b2276aeffd40f9ea687c69c45cd592f8635.camel@sipsolutions.net> [resend to avoid list size limit] Hi, for most of the tests you already have a run_* function as they are almost identical. I did notice these two though that could also share code by using a run_ handler (maybe there are others): * test_eppke_mld_ap_with_base_akm_sae_legacy_client_pmksa_cached * test_eppke_mld_ap_with_base_akm_sae_ext_legacy_client_pmksa_cached One more comment inline at the end. On Thu, 2026-01-15 at 22:03 +0530, Ainy Kumari wrote: > From: Sai Pratyusha Magam > > 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 | 675 ++++++++++++++++++++++++++++++++++++++ > ?1 file changed, 675 insertions(+) > ?create mode 100644 tests/hwsim/test_eppke.py > > diff --git a/tests/hwsim/test_eppke.py b/tests/hwsim/test_eppke.py > new file mode 100644 > index 000000000..e52e96e1e > --- /dev/null > +++ b/tests/hwsim/test_eppke.py > @@ -0,0 +1,675 @@ > +# Test cases for Enhanced Privacy Protection Key Exchange(EPPKE) > +# Copyright (c) 2025, Qualcomm Innovation Center, Inc. > +# > +# This software may be distributed under the terms of the BSD license. > +# See README for more details. > + > +import time > + > +import hostapd > +from wpasupplicant import WpaSupplicant > +from utils import * > +from hwsim import HWSimRadio > +from test_eht import eht_mld_ap_wpa2_params, eht_mld_enable_ap, traffic_test, eht_verify_status > + > +def test_eppke_akm_suite_and_rsnxe_feature_flags(dev, apdev): > +??? """AP EPPKE AKM Advertisement with SAE base AKM and EPPKE related feature flags""" > +??? ssid = "test-eppke-authentication" > +??? params = hostapd.wpa3_params(ssid=ssid, > +???????????????????????????????? password = "1234567890") > +??? params['wpa_key_mgmt'] = params['wpa_key_mgmt'] + ' ' + 'EPPKE' > +??? params['assoc_frame_encryption'] = '1' > +??? params['pmksa_caching_privacy'] = '1' > +??? params['eap_using_authentication_frames'] = '1' > +??? params['sae_pwe'] = '2' > + > +??? hapd = hostapd.add_ap(apdev[0], params) > +??? time.sleep(2) > +??? #TODO: Add pcap file checks to validate correct > +??? #RSNXE bits and AKM suite presence in Beacon frames > + > +??? #Disable all EPPKE related RSNXE flags and test > +??? params['assoc_frame_encryption'] = '0' > +??? params['pmksa_caching_privacy'] = '0' > +??? params['eap_using_authentication_frames'] = '0' > +??? hapd = hostapd.add_ap(apdev[0], params) > +??? time.sleep(2) > + > +def test_eppke_ap_with_base_akm_sae_legacy_client(dev, apdev): > +??? """EPPKE authentication with a Non-MLO AP with base AKM SAE and legacy client""" > +??? ssid = "test-eppke-authentication" > +??? passphrase = '1234567890' > +??? params = hostapd.wpa3_params(ssid=ssid, > + password = passphrase) > +??? params['wpa_key_mgmt'] = params['wpa_key_mgmt'] + ' ' + 'EPPKE' > +??? params['assoc_frame_encryption'] = '1' > +??? params['pmksa_caching_privacy'] = '1' > +??? params['eap_using_authentication_frames'] = '1' > +??? params['sae_pwe'] = '2' > +??? hapd = hostapd.add_ap(apdev[0], params) > + > +??? try: > +??????? dev[0].set("sae_groups", "") > +??????? dev[0].set("sae_pwe", "1") > +??????? dev[0].connect(ssid, sae_password=passphrase, scan_freq="2412", > +?????????????????????? key_mgmt="SAE", ieee80211w="2", beacon_prot="1", > +?????????????????????? pairwise="CCMP") > +??????? hapd.wait_sta(); > +??????? sta = hapd.get_sta(dev[0].own_addr()) > +??????? if sta["AKMSuiteSelector"] != '00-0f-ac-8' or sta["auth_alg"] != '9': > +??????????? raise Exception("Incorrect Auth Algo/AKMSuiteSelector value") > + > +??? finally: > +??????? dev[0].set("sae_groups", "") > +??????? dev[0].set("sae_pwe", "0") > + > +def run_eppke_sae_ext_key(dev, apdev, group): > +??? """EPPKE authentication with a Non-MLO AP with base AKM SAE-EXT-KEY and legacy client""" > +??? ssid = "test-eppke-authentication" > +??? passphrase = '1234567890' > +??? params = hostapd.wpa3_params(ssid=ssid, > + password = passphrase) > +??? params['wpa_key_mgmt'] = params['wpa_key_mgmt'] + ' ' + 'SAE-EXT-KEY EPPKE' > +??? params['assoc_frame_encryption'] = '1' > +??? params['pmksa_caching_privacy'] = '1' > +??? params['eap_using_authentication_frames'] = '1' > +??? params['sae_pwe'] = '2' > +??? params['pasn_groups'] = str(group) > +??? params['sae_groups'] = str(group) > +??? hapd = hostapd.add_ap(apdev[0], params) > + > +??? try: > +??????? dev[0].set("sae_groups", str(group)) > +??????? dev[0].set("sae_pwe", "1") > +??????? dev[0].connect(ssid, sae_password=passphrase, scan_freq="2412", > +?????????????????????? key_mgmt="SAE-EXT-KEY", ieee80211w="2", beacon_prot="1", > +?????????????????????? pairwise="CCMP") > +??????? hapd.wait_sta(); > +??????? sta = hapd.get_sta(dev[0].own_addr()) > +??????? if sta["AKMSuiteSelector"] != '00-0f-ac-24' or sta["auth_alg"] != '9': > +??????????? raise Exception("Incorrect Auth Algo/AKMSuiteSelector value") > + > +??? finally: > +??????? dev[0].set("sae_groups", "") > +??????? dev[0].set("sae_pwe", "0") > + > +def test_eppke_ap_with_base_akm_sae_ext_legacy_client_19(dev, apdev): > +??? """EPPKE authentication with a Non-MLO AP with base AKM SAE-EXT-KEY and legacy client, group 19""" > +??? run_eppke_sae_ext_key(dev, apdev, 19) > + > +def test_eppke_ap_with_base_akm_sae_ext_legacy_client_20(dev, apdev): > +??? """EPPKE authentication with a Non-MLO AP with base AKM SAE-EXT-KEY and legacy client, group 20""" > +??? run_eppke_sae_ext_key(dev, apdev, 20) > + > +def test_eppke_ap_with_base_akm_sae_ext_legacy_client_21(dev, apdev): > +??? """EPPKE authentication with a Non-MLO AP with base AKM SAE-EXT-KEY and legacy client, group 21""" > +??? run_eppke_sae_ext_key(dev, apdev, 21) > + > +def test_eppke_mld_ap_with_base_akm_sae_legacy_client(dev, apdev): > +??? """EPPKE authentication with an MLD AP with base AKM SAE and legacy client""" > +??? with HWSimRadio(use_mlo=True) as (hapd_radio, hapd_iface): > +??????? passphrase = '1234567890' > +??????? ssid = "test-eppke-authentication" > +??????? params = eht_mld_ap_wpa2_params(ssid, passphrase, > +??????????????????????????????????????? key_mgmt="SAE", mfp="2", pwe='1') > +??????? params['wpa_key_mgmt'] = params['wpa_key_mgmt'] + ' ' + 'EPPKE' > +??????? params['assoc_frame_encryption'] = '1' > +??????? params['pmksa_caching_privacy'] = '1' > +??????? params['eap_using_authentication_frames'] = '1' > +??????? params['rsn_pairwise'] = "CCMP GCMP-256" > + > +??????? hapd0 = eht_mld_enable_ap(hapd_iface, 0, params) > + > +??????? params['channel'] = '6' > +??????? hapd1 = eht_mld_enable_ap(hapd_iface, 1, params) > + > +??????? try: > +??????????? dev[0].set("sae_groups", "") > +??????????? dev[0].set("sae_pwe", "1") > +??????????? dev[0].connect(ssid, sae_password=passphrase, scan_freq="2412", > +?????????????????????????? key_mgmt="SAE", ieee80211w="2", beacon_prot="1", > +?????????????????????????? pairwise="CCMP GCMP-256") > +??????????? bssid = dev[0].get_status_field("bssid") > +??????????? if hapd0.own_addr() == bssid: > +??????????????? hapd0.wait_sta(); > +??????????????? sta = hapd0.get_sta(dev[0].own_addr()) > +??????????????? if sta["AKMSuiteSelector"] != '00-0f-ac-8' or sta["auth_alg"] != '9': > +??????????????????? raise Exception("Incorrect Auth Algo/AKMSuiteSelector value") > +??????????? elif hapd1.own_addr() == bssid: > +??????????????? hapd1.wait_sta(); > +??????????????? sta = hapd1.get_sta(dev[0].own_addr()) > +??????????????? if sta["AKMSuiteSelector"] != '00-0f-ac-8' or sta["auth_alg"] != '9': > +??????????????????? raise Exception("Incorrect Auth Algo/AKMSuiteSelector value") > +??????????? else: > +??????????????? raise Exception("Unknown BSSID: " + bssid) I see this pattern quite a bit in the patch. For https://patchwork.ozlabs.org/project/hostap/patch/20251016143754.2532665-3-benjamin at sipsolutions.net/ I added a simple "mld_wait_event" helper, that will accept the event from any of the given hapd instances. Maybe you could pick it up and add it to your patch? Thinking about it now, it could make sense to return a tuple with (hapd, event) instead, so the API user knows which hostapd instance replied. Feel free to improve further if you find the API suboptimal. Benjamin > [Removed rest of the patch] From j at w1.fi Sat Jan 24 09:05:19 2026 From: j at w1.fi (Jouni Malinen) Date: Sat, 24 Jan 2026 19:05:19 +0200 Subject: [PATCH] Beacon protection with MBSSID: Fix getting correct pn for bigtk In-Reply-To: References: Message-ID: On Thu, Jan 15, 2026 at 10:18:28AM +0000, Nikita Chernikov wrote: > When STA is connecting to a MBSSID non-TX BSS, pn for bigtk is requested from a non beaconing BSS, > when it should be requested for the TX BSS in MBSSID. > This is due to pn is requested from original auth when it should be requested from the tx_bss_auth > which exists and replaced in case of MBSSID. > diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c > @@ -4211,7 +4211,7 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) > - wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, rsc) < 0) > + wpa_auth_get_seqnum(wpa_auth, NULL, gsm->GN_bigtk, rsc) < 0) Thanks for sending this. I happened to have another pending patch that had the exact same change, so the change itself is now in hostap.git even though I did not apply this specific patch. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Sat Jan 24 09:59:26 2026 From: j at w1.fi (Jouni Malinen) Date: Sat, 24 Jan 2026 19:59:26 +0200 Subject: [PATCH] P2P: add missing NULL check in p2p_ctrl_flush() In-Reply-To: <20251110030802.459705-1-dan.callaghan@morsemicro.com> References: <20251110030802.459705-1-dan.callaghan@morsemicro.com> Message-ID: On Mon, Nov 10, 2025 at 02:08:02PM +1100, Dan Callaghan wrote: > When wpa_supplicant is compiled with CONFIG_TESTING_OPTIONS and receives > a FLUSH command from wpa_cli, p2p_ctrl_flush() may crash due to passing > a NULL pointer into p2p_set_invitation_op_freq(). > > Add a NULL pointer check, similar to the other NULL pointer check > already in p2p_ctrl_flush(). Thanks, applied. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Sat Jan 24 09:59:45 2026 From: j at w1.fi (Jouni Malinen) Date: Sat, 24 Jan 2026 19:59:45 +0200 Subject: [PATCH 0/2] Avoid/Accept RSNXE in association request for WPA 1 In-Reply-To: <20251218161934.284933-1-benjamin@sipsolutions.net> References: <20251218161934.284933-1-benjamin@sipsolutions.net> Message-ID: On Thu, Dec 18, 2025 at 05:19:32PM +0100, Benjamin Berg wrote: > So if we connect using WPA 1 only, we would send the RSNXE in the > association request but not the KDE. hostapd on the other hand, would > verify that the two patch and would error out. Fix the problem on both > sides as there are APs in the wild that have the current hostapd check. > > Note that this can easily be tested by adding a wiphy_ext_feature_set > call to mac80211_hwsim and setting > NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE. If one then runs the > hwsim tests, then tests that explicitly set WPA 1 will fail. > Benjamin Berg (2): > SME: Do not send RSNXE for WPA 1 connections > AP: Do not store RSNXE for WPA 1 Thanks, applied. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Sat Jan 24 10:00:15 2026 From: j at w1.fi (Jouni Malinen) Date: Sat, 24 Jan 2026 20:00:15 +0200 Subject: [PATCH v2] Pass more capabilities of MLO link to framework In-Reply-To: References: Message-ID: On Fri, Dec 19, 2025 at 10:25:46AM +0000, Bruno.Kremp at sony.com wrote: > Pass Channel Bandwidth and Maximum Number of Spatial Stream > of MLO link to framework, the information is necessary to > calculate the theoretical maximum speed for MLO link. Thanks, applied with some cleanup and fixes. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Sat Jan 24 10:00:30 2026 From: j at w1.fi (Jouni Malinen) Date: Sat, 24 Jan 2026 20:00:30 +0200 Subject: [PATCH] wpa_supplicant: allow incompatible SAE H2E conf if there is a, non-SAE alternative In-Reply-To: <9b8b4048-f6f5-48e0-be4b-d92f975dbaf9@freebox.fr> References: <9b8b4048-f6f5-48e0-be4b-d92f975dbaf9@freebox.fr> Message-ID: On Fri, Dec 19, 2025 at 07:24:35PM +0100, Pablo MARTIN-GOMEZ wrote: > Currently, if a configuration forces SAE H2E and SAE is among the key > management authorized, wpa_supplicant will skip a network that does not > support SAE H2E, even if that network does not support SAE or has > alternatives AKMs that could work with the configuration. > > Skip a network only if a configuration requires a SAE key management. Thanks, applied. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Sat Jan 24 10:00:48 2026 From: j at w1.fi (Jouni Malinen) Date: Sat, 24 Jan 2026 20:00:48 +0200 Subject: [PATCH] AP: Add support for MLD link group key initialization In-Reply-To: <20260114092007.3125348-1-allen.ye@mediatek.com> References: <20260114092007.3125348-1-allen.ye@mediatek.com> Message-ID: On Wed, Jan 14, 2026 at 05:20:07PM +0800, Allen Ye wrote: > Currently, only the assoc link sets the first_sta_seen flag in a mld > connection. That makes the GTK of the other links may be reset if > another station associates with the other links. > > This patch initializes the GTK and sets the first_sta_seen flag for all > links in a mld connection. Thanks, applied. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Sat Jan 24 10:01:03 2026 From: j at w1.fi (Jouni Malinen) Date: Sat, 24 Jan 2026 20:01:03 +0200 Subject: [PATCH] FILS: Fix memory leak if AES-SIV encryption fails In-Reply-To: <20260122162147.ce70615a9ed6.Iaf5f25425a0467ef17bc22105539cfa9ec625e52@changeid> References: <20260122162147.ce70615a9ed6.Iaf5f25425a0467ef17bc22105539cfa9ec625e52@changeid> Message-ID: On Thu, Jan 22, 2026 at 04:21:48PM +0100, Benjamin Berg wrote: > The dynamically allocated header was not free'ed in the error path. Add > the appropriate os_free call. Thanks, applied. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Sun Jan 25 09:23:27 2026 From: j at w1.fi (Jouni Malinen) Date: Sun, 25 Jan 2026 19:23:27 +0200 Subject: [PATCH 1/5] USD: Support suspending USD operations In-Reply-To: <20251023104534.222481-1-andrei.otcheretianski@intel.com> References: <20251023104534.222481-1-andrei.otcheretianski@intel.com> Message-ID: Thanks, all five patches applied with some cleanup. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Sun Jan 25 09:23:55 2026 From: j at w1.fi (Jouni Malinen) Date: Sun, 25 Jan 2026 19:23:55 +0200 Subject: [PATCH] AP: send TTLM if a link is indicated as disabled In-Reply-To: <20251030122957.240082-1-benjamin@sipsolutions.net> References: <20251030122957.240082-1-benjamin@sipsolutions.net> Message-ID: On Thu, Oct 30, 2025 at 01:29:57PM +0100, Benjamin Berg wrote: > When a link is indicated as disabled, then a corresponding TTLM should > be sent. Add the appropriate code to generate the TTLM. Thanks, applied. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Sun Jan 25 10:27:58 2026 From: j at w1.fi (Jouni Malinen) Date: Sun, 25 Jan 2026 20:27:58 +0200 Subject: Subject: [PATCH] hostapd: Add testing option for association_response_status_code In-Reply-To: <4763E451-E6B4-44EF-A207-29B216BE44E1@amazon.com> References: <4763E451-E6B4-44EF-A207-29B216BE44E1@amazon.com> Message-ID: On Mon, Oct 27, 2025 at 12:47:17PM +0000, Raga, Gopi wrote: > From 654e923d8322da21fb1f925766711ff2ee1c0a6c Mon Sep 17 00:00:00 2001 > From: gopiraga That From: entry (i.e., source of author information for the commit) does not look correct.. > This adds a new testing option to allow controlling the association > response status code returned by hostapd for testing purposes. > > Signed-off-by: Gopi Raga Nor that email address.. > diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf > @@ -3402,6 +3402,11 @@ own_ip_addr=127.0.0.1 > +# Send Custom Status Code in Association Response > +# Example association_response_status_code=15 > +#association_response_status_code=0 This feels quite strange item to configure as a fixed value for all association frames. What kind of testing is this for? I would have expected something more dynamic like specifying status code for the following single (Re)Association Response frame using the control interface would be more useful. > diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c > @@ -6179,6 +6179,15 @@ static void handle_assoc(struct hostapd_data *hapd, > > +#ifdef CONFIG_TESTING_OPTIONS > + if (hapd->conf->association_response_status_code >= 0) { > + wpa_printf(MSG_INFO, > + "TESTING: Overriding association response status code from %d to %d for STA " MACSTR, > + resp, hapd->conf->association_response_status_code, MAC2STR(mgmt->sa)); > + resp = hapd->conf->association_response_status_code; > + } > +#endif /* CONFIG_TESTING_OPTIONS */ This was under CONFIG_TESTING_OPTIONS, but the other parts were not.. Those other ones should have been as well to be consistent. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Sun Jan 25 10:43:51 2026 From: j at w1.fi (Jouni Malinen) Date: Sun, 25 Jan 2026 20:43:51 +0200 Subject: [PATCH 1/1] Add p2p_group_get_member_connection_info In-Reply-To: <20251104000655epcms1p3b0c286ab505cd0bd54c19009a5d27634@epcms1p3> References: <20251104000655epcms1p3b0c286ab505cd0bd54c19009a5d27634@epcms1p3> Message-ID: On Tue, Nov 04, 2025 at 09:06:55AM +0900, ??? wrote: > Implement event handling for P2P group association to record IEEE standard, and bandwidth. > Add p2p_group_get_member_connection_info function to retrive connection capabilities of group clients. This would leave that p2p_group_get_member_connection_info() unused, i.e., none of the information derived here is actually used. I'm assuming this is for some external uses, but in general, I'd prefer to see something within hostap.git using all functionality as well. This could be, e.g., through wpa_supplicant/ctrl_iface.c. That said, I don't think this proposed direction is really the best way for the particular need.. > diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c > if (req_ies) { > - p2p_group_notif_assoc(hapd->p2p_group, sta->addr, > + if (elems.ht_capabilities) { > + if (!sta->ht_capabilities) { > + sta->ht_capabilities = os_zalloc(sizeof(struct ieee80211_ht_capabilities)); > + if (!sta->ht_capabilities) { > + wpa_printf(MSG_ERROR, "Failed to allocate memory for HT capabilities"); > + return 0; > + } It would be fine to fill in sta->ht_capabilities also in cases where hostapd AP SME is not used, but ideally, this would be using the copy_sta*() functions like the code in ieee802_11.c is doing for the AP SME in hostapd case. > + p2p_group_notif_assoc(hapd->p2p_group, sta, > req_ies, req_ies_len); This is not acceptable due to code separation between the P2P module in src/p2p and AP code in src/ap. src/p2p/*.c is not supposed to dereference struct sta_info directly. > diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c > #ifdef CONFIG_P2P > if (ies && ies_len) > - p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len); > + p2p_group_notif_assoc(hapd->p2p_group, sta, ies, ies_len); > #endif /* CONFIG_P2P */ Same here. > diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h > +#include "ap/sta_info.h" This is not allowed due to that code separation goal. > diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c > @@ -26,6 +26,11 @@ struct p2p_group_member { > struct wpabuf *wfd_ie; > struct wpabuf *client_info; > u8 dev_capab; > + unsigned int connection_ht:1; > + unsigned int connection_vht:1; > + unsigned int connection_he:1; > + unsigned int connection_eht:1; > + unsigned int connection_channel_bandwidth:5; Why would these be needed in any P2P specific code? This information would be available on the P2P GO from the STA entries (i.e., struct sta_info) and all this information could and should be made available through that mechanism instead of any new P2P specific code. For this particular set of information, it would be more appropriate to extend ieee802_11_get_mib_sta() to provide that information and use interface like the existing STA-FIRST/STA/STA-NEXT in the wpa_supplicant control interface to access the information. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Sun Jan 25 10:46:34 2026 From: j at w1.fi (Jouni Malinen) Date: Sun, 25 Jan 2026 20:46:34 +0200 Subject: [PATCH] tests: Add test that fails the MLD association after hostapd success In-Reply-To: <7667c4d6e46cbc1238624ccbe5bdde7e270283f1.camel@sipsolutions.net> References: <20251030175606.275085-1-benjamin@sipsolutions.net> <7667c4d6e46cbc1238624ccbe5bdde7e270283f1.camel@sipsolutions.net> Message-ID: On Thu, Oct 30, 2025 at 08:15:12PM +0100, Benjamin Berg wrote: > On Thu, 2025-10-30 at 18:56 +0100, Benjamin Berg wrote: > > This test checks the flow where an error happens on the first > > association attempt but everything looks fine from the perspective of > > the AP. In this case, the AP will receive a second authentication even > > though it thinks the client is already associated. > > > > Currently this test triggers a segfault in hostapd. > > +??????? with fail_test(wpas, 1, "denied-unspec:mlme_event_assoc"): > > Ah, I changed that to be more specific in the last minute and didn't > notice that it broke the test. The function name cannot be matched > there (I expect it is inlined). That said, the tag is there, so one can > just specify only "denied-unspec" to trigger the bug. It's not that function name not being available, but incorrect separator being used.. Replacing that colon with semicolon fixes that. I fixed the hostapd issue in not clearing sta->wpa_sm appropriately for this particular sequence and applied this patch after that. -- Jouni Malinen PGP id EFC895FA From trevor at freedisc.co.uk Mon Jan 26 02:27:39 2026 From: trevor at freedisc.co.uk (Trevor North) Date: Mon, 26 Jan 2026 10:27:39 +0000 Subject: [FIX 1] Correct op class and chan for HE PHY In-Reply-To: <30a02fe0-4560-4c4f-ae63-b213d1caab8c@freebox.fr> References: <30a02fe0-4560-4c4f-ae63-b213d1caab8c@freebox.fr> Message-ID: <20260126102739.56637-1-trevor@freedisc.co.uk> This leverages the more recently added get_operation_channel_width to maintain return of the correct operating class and channel when a HE PHY type is determined. Signed-off-by: Trevor North --- wpa_supplicant/rrm.c | 66 ++++++++++++++-------------------------- wpa_supplicant/wnm_sta.c | 48 ++++++++++++++--------------- 2 files changed, 45 insertions(+), 69 deletions(-) diff --git a/wpa_supplicant/rrm.c b/wpa_supplicant/rrm.c index dabbf8cfa..dc8374350 100644 --- a/wpa_supplicant/rrm.c +++ b/wpa_supplicant/rrm.c @@ -751,18 +751,24 @@ static int * wpas_beacon_request_freqs(struct wpa_supplicant *wpa_s, int wpas_get_op_chan_phy(int freq, const u8 *ies, size_t ies_len, u8 *op_class, u8 *chan, u8 *phy_type) { - const u8 *ie; - int sec_chan = 0, vht = 0; - struct ieee80211_ht_operation *ht_oper = NULL; - struct ieee80211_vht_operation *vht_oper = NULL; - u8 seg0, seg1; - - ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION); - if (ie && ie[1] >= sizeof(struct ieee80211_ht_operation)) { - u8 sec_chan_offset; - - ht_oper = (struct ieee80211_ht_operation *) (ie + 2); - sec_chan_offset = ht_oper->ht_param & + int sec_chan = 0, chanwidth = 0; + struct ieee802_11_elems elems; + struct ieee80211_ht_operation *ht_oper; + + if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) { + return -1; + } + + chanwidth = get_operation_channel_width(&elems); + if (chanwidth == CHAN_WIDTH_UNKNOWN) { + wpa_printf(MSG_DEBUG, + "Cannot determine channel width"); + return -1; + } + + ht_oper = (struct ieee80211_ht_operation *) elems.ht_operation; + if (ht_oper) { + u8 sec_chan_offset = ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK; if (sec_chan_offset == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) sec_chan = 1; @@ -771,44 +777,16 @@ int wpas_get_op_chan_phy(int freq, const u8 *ies, size_t ies_len, sec_chan = -1; } - ie = get_ie(ies, ies_len, WLAN_EID_VHT_OPERATION); - if (ie && ie[1] >= sizeof(struct ieee80211_vht_operation)) { - vht_oper = (struct ieee80211_vht_operation *) (ie + 2); - - switch (vht_oper->vht_op_info_chwidth) { - case CHANWIDTH_80MHZ: - seg0 = vht_oper->vht_op_info_chan_center_freq_seg0_idx; - seg1 = vht_oper->vht_op_info_chan_center_freq_seg1_idx; - if (seg1 && abs(seg1 - seg0) == 8) - vht = CONF_OPER_CHWIDTH_160MHZ; - else if (seg1) - vht = CONF_OPER_CHWIDTH_80P80MHZ; - else - vht = CONF_OPER_CHWIDTH_80MHZ; - break; - case CHANWIDTH_160MHZ: - vht = CONF_OPER_CHWIDTH_160MHZ; - break; - case CHANWIDTH_80P80MHZ: - vht = CONF_OPER_CHWIDTH_80P80MHZ; - break; - default: - vht = CONF_OPER_CHWIDTH_USE_HT; - break; - } - } - - if (ieee80211_freq_to_channel_ext(freq, sec_chan, vht, op_class, + if (ieee80211_chaninfo_to_channel(freq, chanwidth, sec_chan, op_class, chan) == NUM_HOSTAPD_MODES) { wpa_printf(MSG_DEBUG, "Cannot determine operating class and channel"); return -1; } - int he = get_ie_ext(ies, ies_len, WLAN_EID_EXT_HE_OPERATION) != NULL; - - *phy_type = ieee80211_get_phy_type(freq, ht_oper != NULL, - vht_oper != NULL, he); + *phy_type = ieee80211_get_phy_type(freq, elems.ht_operation != NULL, + elems.vht_operation != NULL, + elems.he_operation != NULL); if (*phy_type == PHY_TYPE_UNSPECIFIED) { wpa_printf(MSG_DEBUG, "Cannot determine phy type"); return -1; diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index 07b59b198..3eefd72ed 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -889,47 +889,45 @@ static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpabuf **buf, u8 pref) { - const u8 *ie; u8 op_class, chan; - int sec_chan = 0; - enum oper_chan_width vht = CONF_OPER_CHWIDTH_USE_HT; + int sec_chan = 0, chanwidth = 0; + struct ieee802_11_elems elems; + struct ieee80211_ht_operation *ht_oper; enum phy_type phy_type; u32 info; - struct ieee80211_ht_operation *ht_oper = NULL; - struct ieee80211_vht_operation *vht_oper = NULL; - ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION); - if (ie && ie[1] >= 2) { - ht_oper = (struct ieee80211_ht_operation *) (ie + 2); + if (ieee802_11_parse_elems(wpa_bss_ie_ptr(bss), bss->ie_len, &elems, 1) == ParseFailed) { + return -2; + } + + chanwidth = get_operation_channel_width(&elems); + if (chanwidth == CHAN_WIDTH_UNKNOWN) { + wpa_printf(MSG_DEBUG, + "Cannot determine channel width"); + return -2; + } - if (ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) + ht_oper = (struct ieee80211_ht_operation *) elems.ht_operation; + if (ht_oper) { + u8 sec_chan_offset = ht_oper->ht_param & + HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK; + if (sec_chan_offset == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) sec_chan = 1; - else if (ht_oper->ht_param & + else if (sec_chan_offset == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) sec_chan = -1; } - ie = wpa_bss_get_ie(bss, WLAN_EID_VHT_OPERATION); - if (ie && ie[1] >= 1) { - vht_oper = (struct ieee80211_vht_operation *) (ie + 2); - - if (vht_oper->vht_op_info_chwidth == CHANWIDTH_80MHZ || - vht_oper->vht_op_info_chwidth == CHANWIDTH_160MHZ || - vht_oper->vht_op_info_chwidth == CHANWIDTH_80P80MHZ) - vht = vht_oper->vht_op_info_chwidth; - } - - if (ieee80211_freq_to_channel_ext(bss->freq, sec_chan, vht, &op_class, + if (ieee80211_freq_to_channel_ext(bss->freq, sec_chan, chanwidth, &op_class, &chan) == NUM_HOSTAPD_MODES) { wpa_printf(MSG_DEBUG, "WNM: Cannot determine operating class and channel"); return -2; } - int he = wpa_bss_get_ie_ext(bss, WLAN_EID_EXT_HE_OPERATION) != NULL; - - phy_type = ieee80211_get_phy_type(bss->freq, (ht_oper != NULL), - (vht_oper != NULL), he); + phy_type = ieee80211_get_phy_type(bss->freq, elems.ht_operation != NULL, + elems.vht_operation != NULL, + elems.he_operation != NULL); if (phy_type == PHY_TYPE_UNSPECIFIED) { wpa_printf(MSG_DEBUG, "WNM: Cannot determine BSS phy type for Neighbor Report"); -- 2.52.0 From tgeppert at digitx.de Mon Jan 26 05:52:53 2026 From: tgeppert at digitx.de (Thomas Geppert) Date: Mon, 26 Jan 2026 14:52:53 +0100 Subject: EAP-TLS: Limiting the Signature Algorithms offered by wpa_supplicant Message-ID: <479dc4d7-6bf5-4910-ae67-b4e63f7db71c@digitx.de> Starting with openssl version 3.5 the TLS Signature algorithms defaults now include all three ML-DSA variants as first algorithms, i.e. 0x0904 (mldsa44), 0x0905 (mldsa65), and 0x0906 (mldsa87). I have an AP that cannot deal with an offer including these algorithms and therefore rejects authentication. I found the 'openssl_ciphers=' option of wpa_supplicant to limit the ciphers it offers in the handshake. Is there some other configuration option or method in wpa_supplicant to limit the offered signature algorithms? If not, do you know a configuration option or method to limit the offered signature algorithms on the openssl side? From benjamin at sipsolutions.net Mon Jan 26 07:16:48 2026 From: benjamin at sipsolutions.net (Benjamin Berg) Date: Mon, 26 Jan 2026 16:16:48 +0100 Subject: [PATCH 1/3] MLD: Pass SSID when doing multi-link probe check Message-ID: <20260126151647.2588514-4-benjamin@sipsolutions.net> From: Benjamin Berg If the SSID is not passed, then the function will resolve hidden ML neighbors even when the SSID is not yet known. Should this happen, then we will later fail to find the link and it will not be used. Signed-off-by: Benjamin Berg Reviewed-by: Andrei Otcheretianski --- wpa_supplicant/events.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 4ad6c5138d..a8feadc514 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -1897,7 +1897,7 @@ static int wpa_supplicant_connect_ml_missing(struct wpa_supplicant *wpa_s, (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))) return 0; - usable_links = wpa_bss_get_usable_links(wpa_s, selected, NULL, + usable_links = wpa_bss_get_usable_links(wpa_s, selected, ssid, &missing_links); if (!usable_links || !missing_links) return 0; -- 2.52.0 From benjamin at sipsolutions.net Mon Jan 26 07:16:50 2026 From: benjamin at sipsolutions.net (Benjamin Berg) Date: Mon, 26 Jan 2026 16:16:50 +0100 Subject: [PATCH 3/3] tests: Add test for connecting to a hidden MLD SSID In-Reply-To: <20260126151647.2588514-4-benjamin@sipsolutions.net> References: <20260126151647.2588514-4-benjamin@sipsolutions.net> Message-ID: <20260126151647.2588514-6-benjamin@sipsolutions.net> From: Benjamin Berg This adds a test that does an ML probe for a hidden SSID in order to connect to it. Signed-off-by: Benjamin Berg Reviewed-by: Andrei Otcheretianski --- tests/hwsim/test_eht.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/hwsim/test_eht.py b/tests/hwsim/test_eht.py index d90683bb85..a6ff8a213e 100644 --- a/tests/hwsim/test_eht.py +++ b/tests/hwsim/test_eht.py @@ -946,7 +946,7 @@ def test_eht_ml_probe_req(dev, apdev): if ev is None: raise Exception("ML_PROBE_REQ did not result in scan results") -def test_eht_mld_connect_probes(dev, apdev, params): +def _eht_mld_connect_probes(params, hidden=False): """MLD client sends ML probe to connect to not discovered links""" with HWSimRadio(use_mlo=True) as (hapd_radio, hapd_iface), \ HWSimRadio(use_mlo=True) as (wpas_radio, wpas_iface): @@ -961,15 +961,22 @@ def test_eht_mld_connect_probes(dev, apdev, params): key_mgmt="SAE", pwe='2') link_params['channel'] = '1' link_params['bssid'] = '00:11:22:33:44:01' + if hidden: + link_params['ignore_broadcast_ssid'] = '1' + hapd0 = eht_mld_enable_ap(hapd_iface, 0, link_params) link_params['channel'] = '6' link_params['bssid'] = '00:11:22:33:44:02' hapd1 = eht_mld_enable_ap(hapd_iface, 1, link_params) + # In the hidden test, scan passively to have the BSSs without SSID + if hidden: + wpas.scan(freq="2412,2437", passive=True) + wpas.set("sae_pwe", "1") wpas.connect(ssid, sae_password= passphrase, ieee80211w="2", - key_mgmt="SAE", scan_freq="2412") + key_mgmt="SAE", scan_freq="2412", scan_ssid="1") filters = ['wlan.fc.type_subtype == 0x0004 && wlan.ext_tag.length == 8 && wlan.ext_tag.number == 107 && wlan.eht.multi_link.control == 0x0011 && wlan.eht.multi_link.common_info.length == 2 && wlan.eht.multi_link.common_info.mld_id == 0 && wlan.eht.multi_link.sta_profile.subelt_id == 0 && wlan.eht.multi_link.sta_profile.subelt_len == 2 && wlan.eht.multi_link.type_1.sta_profile_count == 1 && wlan.eht.multi_link.sta_profile_id_list == \'1\'', 'wlan.fc.type_subtype == 0x0004 && wlan.ext_tag.length == 8 && wlan.ext_tag.number == 107 && wlan.eht.multi_link_control == 0x0011 && wlan.eht.multi_link.common_info.length == 2 && wlan.eht.multi_link.common_info.mld_id == 0 && wlan.eht.multi_link.sta_profile.subelt_id == 0 && wlan.eht.multi_link.sta_profile.subelt_len == 2 && wlan.eht.multi_link.type_1.sta_profile_count == 1 && wlan.eht.multi_link.sta_profile_id_list == 1', @@ -994,6 +1001,14 @@ def test_eht_mld_connect_probes(dev, apdev, params): traffic_test(wpas, hapd0) traffic_test(wpas, hapd1) +def test_eht_mld_connect_probes(dev, apdev, params): + """MLD client sends ML probe to connect to not discovered links""" + _eht_mld_connect_probes(params) + +def test_eht_mld_connect_probes_hidden(dev, apdev, params): + """MLD client sends ML probe with SSID to connect to not discovered links""" + _eht_mld_connect_probes(params, hidden=True) + def test_eht_tx_link_rejected_connect_other(dev, apdev, params): """EHT MLD AP with MLD client being rejected on TX link, but then connecting on second link""" with HWSimRadio(use_mlo=True) as (hapd_radio, hapd_iface), \ -- 2.52.0 From benjamin at sipsolutions.net Mon Jan 26 07:16:49 2026 From: benjamin at sipsolutions.net (Benjamin Berg) Date: Mon, 26 Jan 2026 16:16:49 +0100 Subject: [PATCH 2/3] AP: Include MLD RNR entries for hidden SSIDs In-Reply-To: <20260126151647.2588514-4-benjamin@sipsolutions.net> References: <20260126151647.2588514-4-benjamin@sipsolutions.net> Message-ID: <20260126151647.2588514-5-benjamin@sipsolutions.net> From: Benjamin Berg These are required to track updates in an AP MLD and for discovering all of its links. Signed-off-by: Benjamin Berg Reviewed-by: Andrei Otcheretianski --- src/ap/ieee802_11.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 1988d3c8a1..ca11ba3a66 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -8205,7 +8205,8 @@ repeat_rnr_len: #endif /* CONFIG_IEEE80211BE */ if (bss == reporting_hapd || - bss->conf->ignore_broadcast_ssid) + (bss->conf->ignore_broadcast_ssid && + !(ap_mld && mld_update))) continue; if (hostapd_skip_rnr(i, skip_profiles, ap_mld, @@ -8474,9 +8475,10 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd, ap_mld = !!hapd->conf->mld_ap; #endif /* CONFIG_IEEE80211BE */ + /* MLD RNR has to be included for the parameter change count */ if (!bss || !bss->conf || !bss->started || - bss == reporting_hapd || bss->conf->ignore_broadcast_ssid || - !bss->beacon_set_done) + bss == reporting_hapd || !bss->beacon_set_done || + (bss->conf->ignore_broadcast_ssid && !(ap_mld && mld_update))) return false; if (hostapd_skip_rnr(i, skip_profiles, ap_mld, tbtt_info_len, -- 2.52.0 From benjamin at sipsolutions.net Mon Jan 26 08:03:01 2026 From: benjamin at sipsolutions.net (Benjamin Berg) Date: Mon, 26 Jan 2026 17:03:01 +0100 Subject: [PATCH v2 0/3] Fix address confusion after MLO re-authentication Message-ID: <20260126160300.2600380-5-benjamin@sipsolutions.net> From: Benjamin Berg Hi, So this fixes the problem with an MLO station that is authenticating again. The trouble was simply that mac80211 still had the wrong link addresses configured in some cases. That said, I think the logic may not be completely sound in all cases. Specifically, we will also need to do something similar when the client switches between using MLO and not using it. I am planning to submit a test for that later, but I need to upstream some more testing patches for that and wanted to provide the current fix for a start. My hunch is, that the sane thing to do is to remove the added_unassoc flag and instead store whether the STA has been added to the driver. That should give a more consistent way of tracking the state. Benjamin v2: - Add fix. v1 only contained the problematic test. Benjamin Berg (3): AP: Use a constant for mld_sta if support is disabled AP: Always re-add stations that use MLO tests: test reassociation with swapped assoc link src/ap/ieee802_11.c | 10 +++++++--- tests/hwsim/test_eht.py | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) -- 2.52.0 From benjamin at sipsolutions.net Mon Jan 26 08:03:03 2026 From: benjamin at sipsolutions.net (Benjamin Berg) Date: Mon, 26 Jan 2026 17:03:03 +0100 Subject: [PATCH v2 2/3] AP: Always re-add stations that use MLO In-Reply-To: <20260126160300.2600380-5-benjamin@sipsolutions.net> References: <20260126160300.2600380-5-benjamin@sipsolutions.net> Message-ID: <20260126160300.2600380-7-benjamin@sipsolutions.net> From: Benjamin Berg When reauthenticating a STA MLD it might have a different set of links or may have changed its link address. In either case, should anything change then the STA needs to be reconfigured in the driver. It is reasonable to simply always do this reconfiguration even if it is not needed in some corner cases. Signed-off-by: Benjamin Berg Reviewed-by: Andrei Otcheretianski --- src/ap/ieee802_11.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 61a99cc293..a3dbeaf6b3 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -3642,9 +3642,13 @@ static void handle_auth(struct hostapd_data *hapd, * * PASN authentication does not require adding/removing station to the * driver so skip this flow in case of PASN authentication. + * + * Always re-add non-AP MLD stations to ensure only the authentication + * link is inserted with the correct address (which may have changed). */ if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) && - (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta)) && + (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta) || + mld_sta) && !(hapd->conf->mesh & MESH_ENABLED) && !(sta->added_unassoc) && auth_alg != WLAN_AUTH_PASN) { if (ap_sta_re_add(hapd, sta) < 0) { -- 2.52.0 From benjamin at sipsolutions.net Mon Jan 26 08:03:02 2026 From: benjamin at sipsolutions.net (Benjamin Berg) Date: Mon, 26 Jan 2026 17:03:02 +0100 Subject: [PATCH v2 1/3] AP: Use a constant for mld_sta if support is disabled In-Reply-To: <20260126160300.2600380-5-benjamin@sipsolutions.net> References: <20260126160300.2600380-5-benjamin@sipsolutions.net> Message-ID: <20260126160300.2600380-6-benjamin@sipsolutions.net> From: Benjamin Berg Doing this allows removing some of the #ifdef's as the branches become dead code and will be removed by the compiler. Signed-off-by: Benjamin Berg Reviewed-by: Andrei Otcheretianski --- src/ap/ieee802_11.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 1988d3c8a1..61a99cc293 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -3275,6 +3275,8 @@ static void handle_auth(struct hostapd_data *hapd, const u8 *dst, *sa; #ifdef CONFIG_IEEE80211BE bool mld_sta = false; +#else + const bool mld_sta = false; #endif /* CONFIG_IEEE80211BE */ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { @@ -3480,7 +3482,6 @@ static void handle_auth(struct hostapd_data *hapd, if (res == HOSTAPD_ACL_PENDING) return; -#ifdef CONFIG_IEEE80211BE if (mld_sta) { res = ieee802_11_allowed_address(hapd, mgmt->sa, (const u8 *) mgmt, len, @@ -3495,7 +3496,6 @@ static void handle_auth(struct hostapd_data *hapd, if (res == HOSTAPD_ACL_PENDING) return; } -#endif /* CONFIG_IEEE80211BE */ #ifdef CONFIG_SAE if (auth_alg == WLAN_AUTH_SAE && !from_queue && -- 2.52.0 From benjamin at sipsolutions.net Mon Jan 26 08:03:04 2026 From: benjamin at sipsolutions.net (Benjamin Berg) Date: Mon, 26 Jan 2026 17:03:04 +0100 Subject: [PATCH v2 3/3] tests: test reassociation with swapped assoc link In-Reply-To: <20260126160300.2600380-5-benjamin@sipsolutions.net> References: <20260126160300.2600380-5-benjamin@sipsolutions.net> Message-ID: <20260126160300.2600380-8-benjamin@sipsolutions.net> From: Benjamin Berg Add a test that reassociates to the same AP MLD but with the association link changed. Signed-off-by: Benjamin Berg Reviewed-by: Andrei Otcheretianski --- tests/hwsim/test_eht.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/hwsim/test_eht.py b/tests/hwsim/test_eht.py index d90683bb85..a2c7ec0c94 100644 --- a/tests/hwsim/test_eht.py +++ b/tests/hwsim/test_eht.py @@ -2410,6 +2410,43 @@ def test_eht_mld_cohosted_connectivity(dev, apdev, params): stop_mld_devs(hapds, params['prefix']) +def test_eht_mld_reassoc_two_links_change_tx(dev, apdev): + """EHT MLD with two links. Reassociate with swapped association link""" + + with HWSimRadio(use_mlo=True) as (hapd0_radio, hapd0_iface), \ + HWSimRadio(use_mlo=True) as (wpas_radio, wpas_iface): + + wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') + wpas.interface_add(wpas_iface) + + ssid = "mld_ap_owe_two_link" + params = eht_mld_ap_wpa2_params(ssid, key_mgmt="OWE", mfp="2") + params['bssid'] = '00:11:22:33:44:01' + hapd0 = eht_mld_enable_ap(hapd0_iface, 0, params) + + params['channel'] = '6' + params['bssid'] = '00:11:22:33:44:02' + hapd1 = eht_mld_enable_ap(hapd0_iface, 1, params) + + wpas.set("mld_connect_bssid_pref", "00:11:22:33:44:01") + wpas.connect(ssid, scan_freq="2412 2437", key_mgmt="OWE", ieee80211w="2") + eht_verify_status(wpas, hapd0, 2412, 20, is_ht=True, mld=True, valid_links=3, + active_links=3) + eht_verify_wifi_version(wpas) + traffic_test(wpas, hapd0) + + wpas.set("mld_connect_bssid_pref", "00:11:22:33:44:02") + + if "OK" not in wpas.request("REASSOCIATE"): + raise Exception("Failed to request reassociation") + + wpas.wait_connected(timeout=7) + + eht_verify_status(wpas, hapd0, 2412, 20, is_ht=True, mld=True, valid_links=3, + active_links=3) + eht_verify_wifi_version(wpas) + traffic_test(wpas, hapd0) + def test_eht_mlo_color_change(dev, apdev): """AP MLD and Color Change Announcement""" with HWSimRadio(use_mlo=True) as (hapd_radio, hapd_iface), \ -- 2.52.0 From j at w1.fi Mon Jan 26 09:42:08 2026 From: j at w1.fi (Jouni Malinen) Date: Mon, 26 Jan 2026 19:42:08 +0200 Subject: [PATCH] LibreSSL: Fix compilation against LibreSSL >= 4.2.0 In-Reply-To: <20251103-libressl4-2-v1-1-60930b479273@nixdorf.dev> References: <20251103-libressl4-2-v1-1-60930b479273@nixdorf.dev> Message-ID: On Mon, Nov 03, 2025 at 04:15:43PM +0100, Johannes Nixdorf wrote: > The type for the callback function in SSL_set_session_secret_cb() was > changed following the newer OpenSSL and BoringSSL scheme. Fix this by > adding the new LibreSSL version to the existing versions checks. Thanks, applied. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Mon Jan 26 09:42:24 2026 From: j at w1.fi (Jouni Malinen) Date: Mon, 26 Jan 2026 19:42:24 +0200 Subject: [PATCH v2 1/2] AP: Guard against survey list not being initialized In-Reply-To: <20251121100553.3005060-1-benjamin@sipsolutions.net> References: <20251121100553.3005060-1-benjamin@sipsolutions.net> Message-ID: On Fri, Nov 21, 2025 at 11:05:52AM +0100, Benjamin Berg wrote: > Do not try to store survey information in the unusual event that the > channels survey_list is not initialized. That should not usually happen, > but could e.g. be the case if the regulatory changes and new channels > are added after the ACS scan was started. Thanks, both patches applied. -- Jouni Malinen PGP id EFC895FA From tgeppert at digitx.de Mon Jan 26 10:31:38 2026 From: tgeppert at digitx.de (Thomas Geppert) Date: Mon, 26 Jan 2026 19:31:38 +0100 Subject: EAP-TLS: Limiting the Signature Algorithms offered by wpa_supplicant In-Reply-To: <479dc4d7-6bf5-4910-ae67-b4e63f7db71c@digitx.de> References: <479dc4d7-6bf5-4910-ae67-b4e63f7db71c@digitx.de> Message-ID: <4246f65a-3ea4-43f6-9a10-a9491ee813f1@digitx.de> OK, I found the solution myself. It can be limited with the 'SignatureAlgorithms' option in openssl.cnf Am 26.01.26 um 14:52 schrieb Thomas Geppert: > Starting with openssl version 3.5 the TLS Signature algorithms > defaults now include all three ML-DSA variants as first algorithms, > i.e. 0x0904 (mldsa44), 0x0905 (mldsa65), and 0x0906 (mldsa87). > I have an AP that cannot deal with an offer including these algorithms > and therefore rejects authentication. > > I found the 'openssl_ciphers=' option of wpa_supplicant to limit the > ciphers it offers in the handshake. > Is there some other configuration option or method in wpa_supplicant > to limit the offered signature algorithms? > > If not, do you know a configuration option or method to limit the > offered signature algorithms on the openssl side? > > > _______________________________________________ > Hostap mailing list > Hostap at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/hostap From j at w1.fi Mon Jan 26 13:12:28 2026 From: j at w1.fi (Jouni Malinen) Date: Mon, 26 Jan 2026 23:12:28 +0200 Subject: [PATCH v9 0/7] Introduce Automated Frequency Coordination (AFC) support In-Reply-To: <20251201111042.1565266-1-allen.ye@mediatek.com> References: <20251201111042.1565266-1-allen.ye@mediatek.com> Message-ID: On Mon, Dec 01, 2025 at 07:10:35PM +0800, Allen Ye wrote: > This patch series introduces Automated Frequency Coordination (AFC) support > for Standard Power Devices (SPDs) operating in the 6GHz UNII-5 and UNII-7 > bands. AFC is a regulatory requirement for SPDs to determine a list of > available channels and their permissible power levels. > > The implementation introduces a new standalone daemon, afcd, to manage > communication with the AFC server. A client within hostapd then interacts > with afcd via a Unix socket to fetch spectrum availability and update AP > parameters accordingly. The series also includes hwsim tests for validation. > Changes since v8: > - rebase on top of hostapd main branch There were a couple of conflicts with the current snapshot, so it is possible that something went wrong when I merged the conflicts. However, I don't think all of these would be caused by that.. In any case, I cannot compile this due to following warnings or errors: config_file.c: In function ?hostapd_afc_parse_cert_ids?: config_file.c:1323:23: warning: comparison of integer expressions of different signedness: ?int? and ?unsigned int? [-Wsign-compare] 1323 | for (i = 0; i < conf->afc.n_cert_ids; i++) { | ^ ../src/ap/afc.c: In function ?hostapd_afc_build_location_request?: ../src/ap/afc.c:87:24: warning: comparison of integer expressions of different signedness: ?int? and ?unsigned int? [-Wsign-compare] 87 | i < iconf->afc.location.n_linear_polygon_data; i++) { | ^ ../src/ap/afc.c:125:24: warning: comparison of integer expressions of different signedness: ?int? and ?unsigned int? [-Wsign-compare] 125 | i < iconf->afc.location.n_radial_polygon_data; i++) { | ^ ../src/ap/afc.c: In function ?hostapd_afc_build_req_chan_list?: ../src/ap/afc.c:285:23: warning: comparison of integer expressions of different signedness: ?int? and ?unsigned int? [-Wsign-compare] 285 | for (i = 0; i < iconf->afc.n_op_class; i++) { | ^ ../src/ap/afc.c: In function ?hostapd_afc_build_request?: ../src/ap/afc.c:352:31: error: ?struct ? has no member named ?id? 352 | if (iconf->afc.request.id) { | ^ ../src/ap/afc.c:353:68: error: ?struct ? has no member named ?id? 353 | str_obj = json_object_new_string(iconf->afc.request.id); | ^ ../src/ap/afc.c:378:23: warning: comparison of integer expressions of different signedness: ?int? and ?unsigned int? [-Wsign-compare] 378 | for (i = 0; i < iconf->afc.n_cert_ids; i++) { | ^ ../src/ap/afc.c:419:31: warning: comparison of integer expressions of different signedness: ?int? and ?unsigned int? [-Wsign-compare] 419 | for (i = 0; i < iconf->afc.n_freq_range; i++) { | ^ ../src/ap/afc.c: In function ?hostad_afc_parse_available_freq_info?: ../src/ap/afc.c:471:23: warning: comparison of integer expressions of different signedness: ?int? and ?size_t? {aka ?long unsigned int?} [-Wsign-compare] 471 | for (i = 0; i < json_object_array_length(obj); i++) { | ^ ../src/ap/afc.c: In function ?hostad_afc_parse_available_chan_info?: ../src/ap/afc.c:585:23: warning: comparison of integer expressions of different signedness: ?int? and ?size_t? {aka ?long unsigned int?} [-Wsign-compare] 585 | for (i = 0; i < json_object_array_length(obj); i++) { | ^ ../src/ap/afc.c:609:25: warning: comparison of integer expressions of different signedness: ?int? and ?size_t? {aka ?long unsigned int?} [-Wsign-compare] 609 | ch < json_object_array_length(chan_cfi_obj); ch++) { | ^ ../src/ap/afc.c: In function ?hostapd_afc_parse_reply?: ../src/ap/afc.c:689:23: warning: comparison of integer expressions of different signedness: ?int? and ?size_t? {aka ?long unsigned int?} [-Wsign-compare] 689 | for (i = 0; i < json_object_array_length(reply_obj); i++) { | ^ ../src/ap/afc.c:708:39: error: ?struct ? has no member named ?id? 708 | if (iconf->afc.request.id && | ^ In file included from /Git/hostap/src/utils/common.h:12, from ../src/ap/afc.c:14: ../src/ap/afc.c:709:49: error: ?struct ? has no member named ?id? 709 | os_strcmp(iconf->afc.request.id, | ^ /Git/hostap/src/utils/os.h:556:35: note: in definition of macro ?os_strcmp? 556 | #define os_strcmp(s1, s2) strcmp((s1), (s2)) | ^~ ../src/ap/afc.c:724:31: warning: comparison of integer expressions of different signedness: ?int? and ?unsigned int? [-Wsign-compare] 724 | for (j = 0; j < iconf->afc.n_cert_ids; j++) { | ^ ../src/ap/afc.c:730:23: warning: comparison of integer expressions of different signedness: ?int? and ?unsigned int? [-Wsign-compare] 730 | if (j == iconf->afc.n_cert_ids) { | ^~ ../src/ap/afc.c: In function ?hostap_afc_disable_channels?: ../src/ap/afc.c:1087:31: warning: comparison of integer expressions of different signedness: ?int? and ?unsigned int? [-Wsign-compare] 1087 | for (j = 0; j < iface->afc.num_freq_range; j++) { | ^ ../src/ap/afc.c:1093:23: warning: comparison of integer expressions of different signedness: ?int? and ?unsigned int? [-Wsign-compare] 1093 | if (j != iface->afc.num_freq_range) | ^~ ../src/ap/afc.c:1096:31: warning: comparison of integer expressions of different signedness: ?int? and ?unsigned int? [-Wsign-compare] 1096 | for (j = 0; j < iface->afc.num_chan_info; j++) { | ^ ../src/ap/afc.c:1101:23: warning: comparison of integer expressions of different signedness: ?int? and ?unsigned int? [-Wsign-compare] 1101 | if (j != iface->afc.num_chan_info) | ^~ ../src/ap/afc.c: In function ?hostap_afc_get_chan_max_eirp_power?: ../src/ap/afc.c:1124:31: warning: comparison of integer expressions of different signedness: ?int? and ?unsigned int? [-Wsign-compare] 1124 | for (i = 0; i < iface->afc.num_freq_range; i++) { | ^ ../src/ap/afc.c:1135:31: warning: comparison of integer expressions of different signedness: ?int? and ?unsigned int? [-Wsign-compare] 1135 | for (i = 0; i < iface->afc.num_chan_info; i++) { | ^ ../src/ap/afc.c:1143:47: warning: comparison of integer expressions of different signedness: ?int? and ?long unsigned int? [-Wsign-compare] 1143 | for (j = 0; j < ARRAY_SIZE(c->power); j++) | ^ ctrl_iface.c:3965:1: warning: ?hostapd_cli_cmd_afc_send_request? defined but not used [-Wunused-function] 3965 | hostapd_cli_cmd_afc_send_request(struct hostapd_data *hapd, char *cmd, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ctrl_iface.c:3954:1: warning: ?hostapd_cli_cmd_afc_get_response? defined but not used [-Wunused-function] 3954 | hostapd_cli_cmd_afc_get_response(struct hostapd_data *hapd, char *cmd, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ctrl_iface.c:3942:1: warning: ?hostapd_cli_cmd_afc_get_request? defined but not used [-Wunused-function] 3942 | hostapd_cli_cmd_afc_get_request(struct hostapd_data *hapd, char *cmd, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /usr/bin/ld: /Git/hostap/build/hostapd/src/ap/hostapd.o: in function `hostapd_cleanup_iface': /Git/hostap/hostapd/../src/ap/hostapd.c:763:(.text+0x309d): undefined reference to `hostapd_afc_stop' /usr/bin/ld: /Git/hostap/build/hostapd/src/ap/hostapd.o: in function `hostapd_interface_deinit': /Git/hostap/hostapd/../src/ap/hostapd.c:3054:(.text+0x5152): undefined reference to `hostapd_afc_stop' /usr/bin/ld: /Git/hostap/build/hostapd/src/ap/hostapd.o: in function `hostapd_setup_interface_complete_sync': /Git/hostap/hostapd/../src/ap/hostapd.c:2656:(.text+0x6053): undefined reference to `hostapd_afc_handle_request' /usr/bin/ld: /Git/hostap/build/hostapd/src/ap/hw_features.o: in function `hostapd_get_hw_features': /Git/hostap/hostapd/../src/ap/hw_features.c:160:(.text+0x2483): undefined reference to `hostap_afc_disable_channels' /usr/bin/ld: /Git/hostap/hostapd/../src/ap/hw_features.c:160:(.text+0x276e): undefined reference to `hostap_afc_disable_channels' -- Jouni Malinen PGP id EFC895FA From johannes at sipsolutions.net Tue Jan 27 05:57:30 2026 From: johannes at sipsolutions.net (Johannes Berg) Date: Tue, 27 Jan 2026 14:57:30 +0100 Subject: [DESIGN RFC v3] AP Architecture for Wi-Fi-8 Multi-AP Coordination (MAPC) In-Reply-To: <88f05d03-2742-4b61-964f-54beafdd2e70@oss.qualcomm.com> (sfid-20260122_094252_172709_97D85042) References: <653a4207-38f8-408c-8067-c4960c11a71f@oss.qualcomm.com> <05381371a41f154058429f0a1079204c4a454f45.camel@sipsolutions.net> <59cec65b29ded381c85d1be943d88e956a4b7e74.camel@sipsolutions.net> <63ff0362-4ee4-4f13-a212-dc1351eefe08@oss.qualcomm.com> <52356be3d21920d84579d1a8fd803540c6f9644d.camel@sipsolutions.net> <88f05d03-2742-4b61-964f-54beafdd2e70@oss.qualcomm.com> (sfid-20260122_094252_172709_97D85042) Message-ID: <9ea4b65c3fa5750ef57e93dd04b70cc4ec5df445.camel@sipsolutions.net> On Thu, 2026-01-22 at 00:42 -0800, Abhishek Rajkapur Suryawanshi wrote: > > Taking this specific example, it basically says "FW sends a request to > > hostapd, hostapd does the handshake and installs the MAPC station. This > > is how we think we should handle the MAPC stations." > > hostapd controls and manages all MAPC related discovery and MAPC peer > creation part. No trigger from firmware for MAPC Discovery Phase. Seems to me you're splitting hairs. Clearly you have the *negotiation* trigger (NL80211_CMD_MAPC_NEGOTIATION_TRIGGER) prominently featured in the diagrams etc. Sure, it maybe that doesn't explicitly trigger *discovery*, but if hostapd doesn't already know about the station that the FW/driver is requesting a negotiation with, it probably also has to do discovery... Anyway. I went to write a long email replying to some of your individual points but deleted it again, I feel it's not worth your and my time. Clearly, you have already decided on the architecture, and aren't sharing all of it, based on reasons you aren't really sharing either. Which is fine, I can't claim that we always share the full reasoning behind architecture decisions. We should all still make it transparent _how_ it's intended to work, but that could also be just part of the documentation added to the kernel when adding the necessary APIs. Which then means that really in all the hundreds of lines of text you just wanted to ask whether or not "Option-A" or "Option-B" for handling the MAPC peers should be used, and I'd agree that duplicating APIs isn't good, i.e. taking "Option-A". Note we did something similar for NAN stations, even if they have different sets of attributes than regular AP client stations or mesh peers etc. johannes From gubertoli at gmail.com Tue Jan 27 12:25:08 2026 From: gubertoli at gmail.com (Gustavo Bertoli) Date: Tue, 27 Jan 2026 21:25:08 +0100 Subject: [DPP] Issue with WPA-Enterprise - GAS Comeback Delay Message-ID: Hi all, I am working on DPP with WPA-Enterprise. In this scenario, DPP Configurator needs time to process the CSR and return the certBag back to the Enrollee (multiple comeback requests/responses). In my tests, the DPP Configurator correctly sends GAS Initial/Comeback Response frames with a comeback delay of 500ms. The Enrollee respects these delays and schedules the first retries, but does not work for longer processing times required by the DPP Configurator. I suspect that the global GAS query timeout (2s) in gas_query.c may be the cause, as it does not extend when multiple Comeback Delays are received. Checking the test script (test_dpp.py), it seems that with hwsim the processing of CSR and back to the Enrollee with the certBag is possible under 2s. Any opinions on changing gas_query_tx_comeback_req_delay() in gas_query.c to also extend the global timer for gas_query_timeout? Thank you in advance, Gustavo From james.haggerty at morsemicro.com Tue Jan 27 20:19:00 2026 From: james.haggerty at morsemicro.com (James Haggerty) Date: Wed, 28 Jan 2026 15:19:00 +1100 Subject: DPP push button timeouts/sesson-overlap interaction In-Reply-To: References: Message-ID: Following up on my previous email re the likelihood of DPP-PB session overlaps due to regenerating the bootstrap key, my thought is that this could be made a lot better if, when one attempted to initiate an interaction on the AP side, it would fail immediately if an overlap could be predicted. Proposed logic: when DPP PB is initiated on the configurator side, if a DPP PB announcement already exists that's more than 5s but less than 120s old, return failure on dpp pb to the user immediately. This almost always avoids the situation where you begin a DPP PB on the configurator which is almost guaranteed to fail. It also means that if you have initiated the DPP PB on the enrollee side this should still work, as it will be announcing frequently enough that it will be <5s old. On Wed, Feb 26, 2025 at 2:00?PM James Haggerty wrote: > > Hi, > > I've been struggling with DPP push button session-overlap > behaviour. Because a DPP Push button interaction generates a new > bootstrapping key every time on the wpa_supplicant side, if you had > a previous failed interaction hostapd will usually break your current > interaction. So after any failure, you end up in this situation if you > don't wait long enough: > > - DPP failure > - try again -> session overlap > - try again -> session overlap etc. > > To avoid a session overlap, it seems you have to wait 120s after the > last DPP-TX on the wpa_supplicant side. Because the supplicant side has a > 100s timeout, I believe you can't safely initiate a DPP push button for > up to 100+120=220 seconds after your last one (if the last one didn't > succeed). This is quite a long interval! > > Relevant pieces of code: > > 100s timeout in wpa_supplicant: > > os_get_reltime(&now); > if (os_reltime_expired(&now, &wpa_s->dpp_pb_time, 100)) { > wpa_printf(MSG_DEBUG, "DPP: Push button wait time expired"); > wpas_dpp_push_button_stop(wpa_s); return; > } > > 120s timeout on overlap detection in hostapd (if you pass this guard, > it will detect the overlap): > > if (os_reltime_expired(&now, &tmp->rx_time, 120)) { > wpa_hexdump(MSG_DEBUG, "DPP: Push button Enrollee hash > expired", tmp->hash, SHA256_MAC_LEN); > tmp->rx_time.sec = 0; > tmp->rx_time.usec = 0; continue; > } > > Because this was frustrating behaviour from a user perspective (i.e. we > have lock the button out for almost 4 minutes if something goes wrong!), > I was looking into the v3 Wi-Fi EasyConnect spec to determine if there > was any leeway here. I noticed that: > > - the spec does not have the 100s wpa_supplicant timeout, but rather a > number of tighter timeouts. Depending on how you read it, it seems like > it could stop at either 30s (T1e) or 30+30 (T1e+T2e) if no response was > received (which is what happens in session overlap, since a response > frame is never sent back). > > - the spec does not have the 120s session overlap timeout, but rather a > 110s window before the button was pushed (T4) > > - the spec does unfortunately say in the mitigations section that the > bootstrapping key must be regenerated in most situations (even though > it's normally static): "If Push button bootstrapping is aborted for any > reason... the Enrollee shall discard the bootstrapping key used with the > aborted session and generate a new, unique bootstrapping key for > subsequent Push button bootstrapping session" > > Any advice welcome, even if it's just "your spec reading seems right, > feel free to change the timeout behaviour yourself". > > Thanks, > James. From rameshkumar.sundaram at oss.qualcomm.com Tue Jan 27 21:28:14 2026 From: rameshkumar.sundaram at oss.qualcomm.com (Rameshkumar Sundaram) Date: Wed, 28 Jan 2026 10:58:14 +0530 Subject: [PATCH v2 2/3] AP: Always re-add stations that use MLO In-Reply-To: <20260126160300.2600380-7-benjamin@sipsolutions.net> References: <20260126160300.2600380-5-benjamin@sipsolutions.net> <20260126160300.2600380-7-benjamin@sipsolutions.net> Message-ID: <612a2814-216d-435b-a58a-1f17169a944b@oss.qualcomm.com> On 1/26/2026 9:33 PM, Benjamin Berg wrote: > From: Benjamin Berg > > When reauthenticating a STA MLD it might have a different set of links > or may have changed its link address. In either case, should anything > change then the STA needs to be reconfigured in the driver. > > It is reasonable to simply always do this reconfiguration even if it is > not needed in some corner cases. > > Signed-off-by: Benjamin Berg > Reviewed-by: Andrei Otcheretianski > --- > src/ap/ieee802_11.c | 6 +++++- > 1 file changed, 5 insertions(+), 1 deletion(-) > > diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c > index 61a99cc293..a3dbeaf6b3 100644 > --- a/src/ap/ieee802_11.c > +++ b/src/ap/ieee802_11.c > @@ -3642,9 +3642,13 @@ static void handle_auth(struct hostapd_data *hapd, > * > * PASN authentication does not require adding/removing station to the > * driver so skip this flow in case of PASN authentication. > + * > + * Always re-add non-AP MLD stations to ensure only the authentication > + * link is inserted with the correct address (which may have changed). > */ > if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) && > - (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta)) && > + (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta) || > + mld_sta) && Doing this will reset the connection (and keys) of the existing authorized ML STA in the driver. A simple authentication frame received with the MLD address set to the existing authorized STA?s address could terminate the secured association. Should we instead defer this until the association request is received, where SA?Query validation can be performed? Or perhaps until the SAE exchange is completed, if the authentication algorithm is SAE? > !(hapd->conf->mesh & MESH_ENABLED) && > !(sta->added_unassoc) && auth_alg != WLAN_AUTH_PASN) { > if (ap_sta_re_add(hapd, sta) < 0) { -- Ramesh From Allen.Ye at mediatek.com Wed Jan 28 00:10:56 2026 From: Allen.Ye at mediatek.com (=?utf-8?B?QWxsZW4gWWUgKOiRieiKt+WLsyk=?=) Date: Wed, 28 Jan 2026 08:10:56 +0000 Subject: [PATCH v9 0/7] Introduce Automated Frequency Coordination (AFC) support In-Reply-To: References: <20251201111042.1565266-1-allen.ye@mediatek.com> Message-ID: On Mon, 2026-01-26 at 23:12 +0200, Jouni Malinen wrote: > > External email : Please do not click links or open attachments until > you have verified the sender or the content. > > > On Mon, Dec 01, 2025 at 07:10:35PM +0800, Allen Ye wrote: > > This patch series introduces Automated Frequency Coordination (AFC) > > support > > for Standard Power Devices (SPDs) operating in the 6GHz UNII-5 and > > UNII-7 > > bands. AFC is a regulatory requirement for SPDs to determine a list > > of > > available channels and their permissible power levels. > > > > The implementation introduces a new standalone daemon, afcd, to > > manage > > communication with the AFC server. A client within hostapd then > > interacts > > with afcd via a Unix socket to fetch spectrum availability and > > update AP > > parameters accordingly. The series also includes hwsim tests for > > validation. > > > Changes since v8: > > - rebase on top of hostapd main branch > > There were a couple of conflicts with the current snapshot, so it is > possible that something went wrong when I merged the conflicts. > However, > I don't think all of these would be caused by that.. In any case, I > cannot compile this due to following warnings or errors: > Hi Jouni, Thank you for your reply and for testing the patch series. I have tested these patches with the latest hostapd version. I encountered conflicts in ctrl_iface.c and hw_features.c, which I resolved locally. After that, the only issue I saw was the Wsign-compare warnings. Regarding the Wsign-compare warnings, I apologize for missing these. In my development environment, this warning does not appear unless I manually add it to the compile flags, so I overlooked it. If needed, I can address these type issues in the next patch series. > config_file.c: In function ?hostapd_afc_parse_cert_ids?: > config_file.c:1323:23: warning: comparison of integer expressions of > different signedness: ?int? and ?unsigned int? [-Wsign-compare] > ?1323 |???????? for (i = 0; i < conf->afc.n_cert_ids; i++) { > ????? |?????????????????????? ^ > ../src/ap/afc.c: In function ?hostapd_afc_build_location_request?: > ../src/ap/afc.c:87:24: warning: comparison of integer expressions of > different signedness: ?int? and ?unsigned int? [-Wsign-compare] > ?? 87 |????????????????????? i < iconf- > >afc.location.n_linear_polygon_data; i++) { > ????? |??????????????????????? ^ > ../src/ap/afc.c:125:24: warning: comparison of integer expressions of > different signedness: ?int? and ?unsigned int? [-Wsign-compare] > ? 125 |????????????????????? i < iconf- > >afc.location.n_radial_polygon_data; i++) { > ????? |??????????????????????? ^ > ../src/ap/afc.c: In function ?hostapd_afc_build_req_chan_list?: > ../src/ap/afc.c:285:23: warning: comparison of integer expressions of > different signedness: ?int? and ?unsigned int? [-Wsign-compare] > ? 285 |???????? for (i = 0; i < iconf->afc.n_op_class; i++) { > ????? |?????????????????????? ^ > ../src/ap/afc.c: In function ?hostapd_afc_build_request?: > ../src/ap/afc.c:352:31: error: ?struct ? has no member > named ?id? > ? 352 |???????? if (iconf->afc.request.id) { > ????? |?????????????????????????????? ^ > ../src/ap/afc.c:353:68: error: ?struct ? has no member > named ?id? > ? 353 |???????????????? str_obj = json_object_new_string(iconf- > >afc.request.id); > ????? > |??????????????????????????????????????????????????????????????????? > ^ About these struct ? has no member named ?id? errors. The changes seem from v8 patch set. In v9, the request id in hostapd_config was removed and replaced with a random number. There might have been an issue when merging the v9-5 patch. Please make sure you are using the latest v9 changes. > ../src/ap/afc.c:378:23: warning: comparison of integer expressions of > different signedness: ?int? and ?unsigned int? [-Wsign-compare] > ? 378 |???????? for (i = 0; i < iconf->afc.n_cert_ids; i++) { > ????? |?????????????????????? ^ > ../src/ap/afc.c:419:31: warning: comparison of integer expressions of > different signedness: ?int? and ?unsigned int? [-Wsign-compare] > ? 419 |???????????????? for (i = 0; i < iconf->afc.n_freq_range; i++) > { > ????? |?????????????????????????????? ^ > ../src/ap/afc.c: In function ?hostad_afc_parse_available_freq_info?: > ../src/ap/afc.c:471:23: warning: comparison of integer expressions of > different signedness: ?int? and ?size_t? {aka ?long unsigned int?} [- > Wsign-compare] > ? 471 |???????? for (i = 0; i < json_object_array_length(obj); i++) { > ????? |?????????????????????? ^ > ../src/ap/afc.c: In function ?hostad_afc_parse_available_chan_info?: > ../src/ap/afc.c:585:23: warning: comparison of integer expressions of > different signedness: ?int? and ?size_t? {aka ?long unsigned int?} [- > Wsign-compare] > ? 585 |???????? for (i = 0; i < json_object_array_length(obj); i++) { > ????? |?????????????????????? ^ > ../src/ap/afc.c:609:25: warning: comparison of integer expressions of > different signedness: ?int? and ?size_t? {aka ?long unsigned int?} [- > Wsign-compare] > ? 609 |????????????????????? ch < > json_object_array_length(chan_cfi_obj); ch++) { > ????? |???????????????????????? ^ > ../src/ap/afc.c: In function ?hostapd_afc_parse_reply?: > ../src/ap/afc.c:689:23: warning: comparison of integer expressions of > different signedness: ?int? and ?size_t? {aka ?long unsigned int?} [- > Wsign-compare] > ? 689 |???????? for (i = 0; i < json_object_array_length(reply_obj); > i++) { > ????? |?????????????????????? ^ > ../src/ap/afc.c:708:39: error: ?struct ? has no member > named ?id? > ? 708 |???????????????? if (iconf->afc.request.id && > ????? |?????????????????????????????????????? ^ > In file included from /Git/hostap/src/utils/common.h:12, > ???????????????? from ../src/ap/afc.c:14: > ../src/ap/afc.c:709:49: error: ?struct ? has no member > named ?id? > ? 709 |???????????????????? os_strcmp(iconf->afc.request.id, > ????? |???????????????????????????????????????????????? ^ > /Git/hostap/src/utils/os.h:556:35: note: in definition of macro > ?os_strcmp? > ? 556 | #define os_strcmp(s1, s2) strcmp((s1), (s2)) > ????? |?????????????????????????????????? ^~ The same errors here. > ../src/ap/afc.c:724:31: warning: comparison of integer expressions of > different signedness: ?int? and ?unsigned int? [-Wsign-compare] > ? 724 |???????????????? for (j = 0; j < iconf->afc.n_cert_ids; j++) { > ????? |?????????????????????????????? ^ > ../src/ap/afc.c:730:23: warning: comparison of integer expressions of > different signedness: ?int? and ?unsigned int? [-Wsign-compare] > ? 730 |???????????????? if (j == iconf->afc.n_cert_ids) { > ????? |?????????????????????? ^~ > ../src/ap/afc.c: In function ?hostap_afc_disable_channels?: > ../src/ap/afc.c:1087:31: warning: comparison of integer expressions > of different signedness: ?int? and ?unsigned int? [-Wsign-compare] > ?1087 |???????????????? for (j = 0; j < iface->afc.num_freq_range; > j++) { > ????? |?????????????????????????????? ^ > ../src/ap/afc.c:1093:23: warning: comparison of integer expressions > of different signedness: ?int? and ?unsigned int? [-Wsign-compare] > ?1093 |???????????????? if (j != iface->afc.num_freq_range) > ????? |?????????????????????? ^~ > ../src/ap/afc.c:1096:31: warning: comparison of integer expressions > of different signedness: ?int? and ?unsigned int? [-Wsign-compare] > ?1096 |???????????????? for (j = 0; j < iface->afc.num_chan_info; > j++) { > ????? |?????????????????????????????? ^ > ../src/ap/afc.c:1101:23: warning: comparison of integer expressions > of different signedness: ?int? and ?unsigned int? [-Wsign-compare] > ?1101 |???????????????? if (j != iface->afc.num_chan_info) > ????? |?????????????????????? ^~ > ../src/ap/afc.c: In function ?hostap_afc_get_chan_max_eirp_power?: > ../src/ap/afc.c:1124:31: warning: comparison of integer expressions > of different signedness: ?int? and ?unsigned int? [-Wsign-compare] > ?1124 |???????????????? for (i = 0; i < iface->afc.num_freq_range; > i++) { > ????? |?????????????????????????????? ^ > ../src/ap/afc.c:1135:31: warning: comparison of integer expressions > of different signedness: ?int? and ?unsigned int? [-Wsign-compare] > ?1135 |???????????????? for (i = 0; i < iface->afc.num_chan_info; > i++) { > ????? |?????????????????????????????? ^ > ../src/ap/afc.c:1143:47: warning: comparison of integer expressions > of different signedness: ?int? and ?long unsigned int? [-Wsign- > compare] > ?1143 |???????????????????????????????? for (j = 0; j < ARRAY_SIZE(c- > >power); j++) > ????? |?????????????????????????????????????????????? ^ > ctrl_iface.c:3965:1: warning: ?hostapd_cli_cmd_afc_send_request? > defined but not used [-Wunused-function] > ?3965 | hostapd_cli_cmd_afc_send_request(struct hostapd_data *hapd, > char *cmd, > ????? | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > ctrl_iface.c:3954:1: warning: ?hostapd_cli_cmd_afc_get_response? > defined but not used [-Wunused-function] > ?3954 | hostapd_cli_cmd_afc_get_response(struct hostapd_data *hapd, > char *cmd, > ????? | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > ctrl_iface.c:3942:1: warning: ?hostapd_cli_cmd_afc_get_request? > defined but not used [-Wunused-function] > ?3942 | hostapd_cli_cmd_afc_get_request(struct hostapd_data *hapd, > char *cmd, > ????? | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ About the Wunused-function warnings, I believe some related logic may have been accidentally changed during the merge process. Please check if hostapd_ctrl_iface_receive_process is still using the functions, and verify the scope of the related flags. Thanks, Allen > /usr/bin/ld: /Git/hostap/build/hostapd/src/ap/hostapd.o: in function > `hostapd_cleanup_iface': > /Git/hostap/hostapd/../src/ap/hostapd.c:763:(.text+0x309d): undefined > reference to `hostapd_afc_stop' > /usr/bin/ld: /Git/hostap/build/hostapd/src/ap/hostapd.o: in function > `hostapd_interface_deinit': > /Git/hostap/hostapd/../src/ap/hostapd.c:3054:(.text+0x5152): > undefined reference to `hostapd_afc_stop' > /usr/bin/ld: /Git/hostap/build/hostapd/src/ap/hostapd.o: in function > `hostapd_setup_interface_complete_sync': > /Git/hostap/hostapd/../src/ap/hostapd.c:2656:(.text+0x6053): > undefined reference to `hostapd_afc_handle_request' > /usr/bin/ld: /Git/hostap/build/hostapd/src/ap/hw_features.o: in > function `hostapd_get_hw_features': > /Git/hostap/hostapd/../src/ap/hw_features.c:160:(.text+0x2483): > undefined reference to `hostap_afc_disable_channels' > /usr/bin/ld: > /Git/hostap/hostapd/../src/ap/hw_features.c:160:(.text+0x276e): > undefined reference to `hostap_afc_disable_channels' > > -- > Jouni Malinen??????????????????????????????????????????? PGP id > EFC895FA From gopiraga at amazon.com Wed Jan 28 02:52:46 2026 From: gopiraga at amazon.com (Raga, Gopi) Date: Wed, 28 Jan 2026 10:52:46 +0000 Subject: Subject: [PATCH] hostapd: Add testing option for association_response_status_code Message-ID: <8C5B1BB8-9F99-4A9C-88A7-0CDF02244F2B@amazon.com> Hi Jouni, Thanks for the review and the detailed feedback. The motivation for this testing feature is to study client behavior for specific Association Response status codes that are otherwise difficult to trigger naturally, since hostapd sends many of these codes only when the corresponding protocol condition is met. This makes it hard to validate client behavior or collect expected metrics for all possible status codes. I agree with your concern that a static configuration option in hostapd.conf is not appropriate, since it would affect all association responses. To address this, I am planning to redesign this as a dynamic, runtime-only testing feature using the control interface. The updated approach would be: 1. Introduce a new control interface command, for example: hostapd_cli test_assoc_status_code [mac] 2. When enabled, hostapd would override the Association Response status code for subsequent associations. If a MAC address is specified, the override would apply only to that specific STA; otherwise, it would apply to all STAs. 3. The functionality would be implemented entirely under CONFIG_TESTING_OPTIONS, with no persistent configuration. This keeps the feature scoped, dynamic, and suitable for testing without impacting normal operation. Please let me know if this design direction looks reasonable, and I will proceed with an updated patch. Regards, Gopi Raga ?On 25/01/2026, 11:59 PM, "Jouni Malinen" > wrote: CAUTION: This email originated from outside of the organization. Do not click links or open attachments unless you can confirm the sender and know the content is safe. On Mon, Oct 27, 2025 at 12:47:17PM +0000, Raga, Gopi wrote: > From 654e923d8322da21fb1f925766711ff2ee1c0a6c Mon Sep 17 00:00:00 2001 > From: gopiraga > That From: entry (i.e., source of author information for the commit) does not look correct.. > This adds a new testing option to allow controlling the association > response status code returned by hostapd for testing purposes. > > Signed-off-by: Gopi Raga > Nor that email address.. > diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf > @@ -3402,6 +3402,11 @@ own_ip_addr=127.0.0.1 > +# Send Custom Status Code in Association Response > +# Example association_response_status_code=15 > +#association_response_status_code=0 This feels quite strange item to configure as a fixed value for all association frames. What kind of testing is this for? I would have expected something more dynamic like specifying status code for the following single (Re)Association Response frame using the control interface would be more useful. > diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c > @@ -6179,6 +6179,15 @@ static void handle_assoc(struct hostapd_data *hapd, > > +#ifdef CONFIG_TESTING_OPTIONS > + if (hapd->conf->association_response_status_code >= 0) { > + wpa_printf(MSG_INFO, > + "TESTING: Overriding association response status code from %d to %d for STA " MACSTR, > + resp, hapd->conf->association_response_status_code, MAC2STR(mgmt->sa)); > + resp = hapd->conf->association_response_status_code; > + } > +#endif /* CONFIG_TESTING_OPTIONS */ This was under CONFIG_TESTING_OPTIONS, but the other parts were not.. Those other ones should have been as well to be consistent. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Wed Jan 28 09:31:37 2026 From: j at w1.fi (Jouni Malinen) Date: Wed, 28 Jan 2026 19:31:37 +0200 Subject: [PATCH v2 09/35] nl80211: Support NAN Device interface In-Reply-To: <20251223114624.239322-10-andrei.otcheretianski@intel.com> References: <20251223114624.239322-1-andrei.otcheretianski@intel.com> <20251223114624.239322-10-andrei.otcheretianski@intel.com> Message-ID: On Tue, Dec 23, 2025 at 01:45:58PM +0200, Andrei Otcheretianski wrote: > @@ -3347,11 +3367,11 @@ wpa_driver_nl80211_finish_drv_init(struct i802_bss *bss, const u8 *set_addr, > - if (!drv->hostapd && nlmode != NL80211_IFTYPE_P2P_DEVICE) > - netlink_send_oper_ifla(drv->global->netlink, bss->ifindex, > + if (!drv->hostapd && nl80211_is_netdev_iftype(nlmode)) > + netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, > 1, IF_OPER_DORMANT); Is that change from bss->ifindex to drv->ifindex really correct and on purpose here? It does not seem to have anything to do with NAN and that nl80211_is_netdev_iftype() is indeed excluding the NAN case.. I'm dropping this bss->ifindex to drv->ifindex change. If it is really needed, it needs to be in a separate commit that explains the change clearly. -- Jouni Malinen PGP id EFC895FA From benjamin at sipsolutions.net Thu Jan 29 02:08:05 2026 From: benjamin at sipsolutions.net (Benjamin Berg) Date: Thu, 29 Jan 2026 11:08:05 +0100 Subject: [PATCH v2 2/3] AP: Always re-add stations that use MLO In-Reply-To: <612a2814-216d-435b-a58a-1f17169a944b@oss.qualcomm.com> (sfid-20260128_062823_873593_92ECFE2C) References: <20260126160300.2600380-5-benjamin@sipsolutions.net> <20260126160300.2600380-7-benjamin@sipsolutions.net> <612a2814-216d-435b-a58a-1f17169a944b@oss.qualcomm.com> (sfid-20260128_062823_873593_92ECFE2C) Message-ID: <707fe82d66e77aed06002c529322eb9689db027b.camel@sipsolutions.net> On Wed, 2026-01-28 at 10:58 +0530, Rameshkumar Sundaram wrote: > > [SNIP] > > ?? if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) && > > - ??? (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta)) && > > + ??? (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta) || > > + ???? mld_sta) && > > Doing this will reset the connection (and keys) of the existing > authorized ML STA in the driver. A simple authentication frame received > with the MLD address set to the existing authorized STA?s address could > terminate the secured association. Uh, yes, what an embarrassing mistake ? > Should we instead defer this until the association request is received, > where SA?Query validation can be performed? Or perhaps until the SAE > exchange is completed, if the authentication algorithm is SAE? Yes, generally we should wait for SA-Query before kicking/modifying the station. I checked internally, and in particular for 11bi, we should do that once the authentication is completed without doing an SA-Query in that case. That said, I believe we do have a problem until that point as we currently send the frames to the MLD address and rely on address translation which will use the old link address. At least in this case, we will need to send the frames directly to the new link address instead. That said, to properly fix this we need an nl80211/mac80211 API that permits us to disable address translation for the frame. Otherwise we would still get the address translated to the old link address should the new link address match the MLD address. Benjamin > > ?? ??? !(hapd->conf->mesh & MESH_ENABLED) && > > ?? ??? !(sta->added_unassoc) && auth_alg != WLAN_AUTH_PASN) { > > ?? if (ap_sta_re_add(hapd, sta) < 0) { > > > -- > Ramesh > From j at w1.fi Thu Jan 29 02:41:03 2026 From: j at w1.fi (Jouni Malinen) Date: Thu, 29 Jan 2026 12:41:03 +0200 Subject: [PATCH v2 20/35] wpa_supplicant: Refactor NAN USD In-Reply-To: <20251223114624.239322-21-andrei.otcheretianski@intel.com> References: <20251223114624.239322-1-andrei.otcheretianski@intel.com> <20251223114624.239322-21-andrei.otcheretianski@intel.com> Message-ID: On Tue, Dec 23, 2025 at 01:46:09PM +0200, Andrei Otcheretianski wrote: > NAN USD and synchronized discovery have a lot of common parts, > combine them together: > - Rename "usd" functions that are common for sync NAN as well. > - Combine nan_usd.c/h with nan_supplicant.c/h files. > - Provide better inline stubs for all the nan functions to reduce ifdef > usage in core supplicant code. > wpa_supplicant/nan_supplicant.c | 661 +++++++++++++++++++++++ > wpa_supplicant/nan_supplicant.h | 112 ++++ > wpa_supplicant/nan_usd.c | 667 ------------------------ > wpa_supplicant/nan_usd.h | 53 -- I applied patches 1-19 with cleanup. However, this refactoring is way too risky for me to try to merge with other changes that have happened in parallel and that move of everything from wpa_supplicant/nan_usd.c to another file. Furthermore, this would be much nicer for review if that file move were split into a separate commit that moves the contents without changes and then the renaming of functions (etc.) would be in a separate patch. Could you please send an updated version of this refactoring work on top of the current hostap.git snapshot? I'm also dropping the other remaining patches 21-35 since I'm not sure which ones of those might depend on this refactoring. If some of those patches are independent, feel free to send them separately if that is easier to get most changes in first. -- Jouni Malinen PGP id EFC895FA From rameshkumar.sundaram at oss.qualcomm.com Thu Jan 29 09:26:39 2026 From: rameshkumar.sundaram at oss.qualcomm.com (Rameshkumar Sundaram) Date: Thu, 29 Jan 2026 22:56:39 +0530 Subject: [PATCH v2 2/3] AP: Always re-add stations that use MLO In-Reply-To: <707fe82d66e77aed06002c529322eb9689db027b.camel@sipsolutions.net> References: <20260126160300.2600380-5-benjamin@sipsolutions.net> <20260126160300.2600380-7-benjamin@sipsolutions.net> <612a2814-216d-435b-a58a-1f17169a944b@oss.qualcomm.com> <707fe82d66e77aed06002c529322eb9689db027b.camel@sipsolutions.net> Message-ID: On 1/29/2026 3:38 PM, Benjamin Berg wrote: > On Wed, 2026-01-28 at 10:58 +0530, Rameshkumar Sundaram wrote: >>> [SNIP] >>> ?? if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) && >>> - ??? (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta)) && >>> + ??? (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta) || >>> + ???? mld_sta) && >> >> Doing this will reset the connection (and keys) of the existing >> authorized ML STA in the driver. A simple authentication frame received >> with the MLD address set to the existing authorized STA?s address could >> terminate the secured association. > > Uh, yes, what an embarrassing mistake ? > >> Should we instead defer this until the association request is received, >> where SA?Query validation can be performed? Or perhaps until the SAE >> exchange is completed, if the authentication algorithm is SAE? > > Yes, generally we should wait for SA-Query before kicking/modifying the > station. I checked internally, and in particular for 11bi, we should do > that once the authentication is completed without doing an SA-Query in > that case. > > That said, I believe we do have a problem until that point as we > currently send the frames to the MLD address and rely on address > translation which will use the old link address. At least in this case, > we will need to send the frames directly to the new link address > instead. > > That said, to properly fix this we need an nl80211/mac80211 API that > permits us to disable address translation for the frame. Otherwise we > would still get the address translated to the old link address should > the new link address match the MLD address. > That's true?even probe responses get translated when the old link address matches the new link MLD address. Few other cases I encountered: Consider an ML STA with ML address M associated with link A and link B, using link addresses X and Y respectively. If the STA sends an authentication frame with address Y on link A, it gets translated to M (even though link A has no STA with address Y) and forwarded to link B. As a result, hostapd is unaware of the actual TA and queues the authentication reply to the MLD address on link B. This frame will eventually be transmitted over the air on link B with address Y. This will be true even if STA associated in one link and tries to roam to other link of MLD using same link and ML addresses. There also cases where an ML STA roams/re-associates as legacy (non-ML) STA with ML address as link address. The reply would go out with old link address. It seems that address translation at the driver/mac80211 level for management frames could be avoided for both TX and RX, allowing hostapd to handle these frames and their replies more efficiently in cases of roaming and address reuse. -- Ramesh From benjamin at sipsolutions.net Fri Jan 30 04:02:25 2026 From: benjamin at sipsolutions.net (Benjamin Berg) Date: Fri, 30 Jan 2026 13:02:25 +0100 Subject: [PATCH v2 2/3] AP: Always re-add stations that use MLO In-Reply-To: (sfid-20260129_182648_996565_D2417D31) References: <20260126160300.2600380-5-benjamin@sipsolutions.net> <20260126160300.2600380-7-benjamin@sipsolutions.net> <612a2814-216d-435b-a58a-1f17169a944b@oss.qualcomm.com> <707fe82d66e77aed06002c529322eb9689db027b.camel@sipsolutions.net> (sfid-20260129_182648_996565_D2417D31) Message-ID: <97511b524cecf4862d8a83038017c23bc7d715b8.camel@sipsolutions.net> Hi, On Thu, 2026-01-29 at 22:56 +0530, Rameshkumar Sundaram wrote: > On 1/29/2026 3:38 PM, Benjamin Berg wrote: > > [SNIP] > > That said, to properly fix this we need an nl80211/mac80211 API that > > permits us to disable address translation for the frame. Otherwise we > > would still get the address translated to the old link address should > > the new link address match the MLD address. > > > > That's true?even probe responses get translated when the old link > address matches the new link MLD address. > > Few other cases I encountered: > Consider an ML STA with ML address M associated with link A and link B, > using link addresses X and Y respectively. If the STA sends an > authentication frame with address Y on link A, it gets translated to M > (even though link A has no STA with address Y) and forwarded to link B. > As a result, hostapd is unaware of the actual TA and queues the > authentication reply to the MLD address on link B. This frame will > eventually be transmitted over the air on link B with address Y. Honestly, that type of link confusion seems like a bug. We should be able to avoid that as we hopefully know on which link the frame was received. > This will be true even if STA associated in one link and tries to roam > to other link of MLD using same link and ML addresses. > > There also cases where an ML STA roams/re-associates as legacy (non-ML) > STA with ML address as link address. The reply would go out with old > link address. Yes, I think that case is similar a station using its MLD Address on the association link and returning on another link. > It seems that address translation at the driver/mac80211 level for > management frames could be avoided for both TX and RX, allowing hostapd > to handle these frames and their replies more efficiently in cases of > roaming and address reuse. Doing address translation in the TX path is required if the hardware/driver should decide on which link to TX the frame. For RX, it seems sensible to me to do the translation when it is possible. I talked a bit to Johannes about this today, and my current proposal would be add a new flag that is set when mac80211 did not find (RX) or should not use (TX) a STA for the frame. More specifically I think that we could: * Fix the link address based STA lookup to only work when the frame was received on the correct link (the bug from above). * Make sure we drop robust management frames without a STA as we do not want to get into trouble with the next change. * Change ieee80211_rx_for_interface so that it uses only link_sta_info_get_bss if we are an MLD and sta_info_get_bss otherwise. Right now, we will find the station if we see the MLD Address in the frame even when it is not a valid link address. * Add a new nl80211 attribute NL80211_ATTR_FRAME_NO_STA to be used together with NL80211_CMD_FRAME for both TX and RX. - In the RX case, add the attribute if we have no station. If the attribute does not exist, then hostapd should assume the address was translated. - In the TX case, plumb the information through to mac80211 and avoid doing a station lookup based on the address. I think this would be enough to then solve the problem in hostapd. Benjamin From janusz.dziedzic at gmail.com Fri Jan 30 07:16:35 2026 From: janusz.dziedzic at gmail.com (Janusz Dziedzic) Date: Fri, 30 Jan 2026 16:16:35 +0100 Subject: [PATCH] DPP: Add driver listen notifications for push button mode Message-ID: <20260130151832.1132894-1-janusz.dziedzic@gmail.com> Configure driver RX filtering correctly for broadcast public action frames. Signed-off-by: Janusz Dziedzic --- Found when run: ./run-tests.py -c cfg-janusz.py -d ath9k-1 -r ath9k-2 -h dpp_push_button using ath9k hw src/ap/dpp_hostapd.c | 2 ++ wpa_supplicant/dpp_supplicant.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c index 01a057660..2e1343301 100644 --- a/src/ap/dpp_hostapd.c +++ b/src/ap/dpp_hostapd.c @@ -3987,6 +3987,7 @@ int hostapd_dpp_push_button(struct hostapd_data *hapd, const char *cmd) } eloop_register_timeout(100, 0, hostapd_dpp_push_button_expire, hapd, NULL); + hostapd_drv_dpp_listen(hapd, true); wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_STATUS "started"); return 0; @@ -4000,6 +4001,7 @@ void hostapd_dpp_push_button_stop(struct hostapd_data *hapd) if (!ifaces || !ifaces->dpp) return; eloop_cancel_timeout(hostapd_dpp_push_button_expire, hapd, NULL); + hostapd_drv_dpp_listen(hapd, false); if (hostapd_dpp_pb_active(hapd)) { wpa_printf(MSG_DEBUG, "DPP: Stop active push button mode"); if (!ifaces->dpp_pb_result_indicated) diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c index d003bf1d0..c72bcfd81 100644 --- a/wpa_supplicant/dpp_supplicant.c +++ b/wpa_supplicant/dpp_supplicant.c @@ -5783,6 +5783,7 @@ int wpas_dpp_push_button(struct wpa_supplicant *wpa_s, const char *cmd) wpa_s->scan_req = MANUAL_SCAN_REQ; wpa_s->scan_res_handler = wpas_dpp_pb_scan_res_handler; wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_drv_dpp_listen(wpa_s, true); wpa_supplicant_req_scan(wpa_s, 0, 0); wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_STATUS "started"); return 0; @@ -5793,6 +5794,7 @@ void wpas_dpp_push_button_stop(struct wpa_supplicant *wpa_s) { if (!wpa_s->dpp) return; + wpa_drv_dpp_listen(wpa_s, false); os_free(wpa_s->dpp_pb_freqs); wpa_s->dpp_pb_freqs = NULL; wpabuf_free(wpa_s->dpp_pb_announcement); -- 2.43.0 From j at w1.fi Sat Jan 31 02:14:29 2026 From: j at w1.fi (Jouni Malinen) Date: Sat, 31 Jan 2026 12:14:29 +0200 Subject: [PATCH] DPP: Add driver listen notifications for push button mode In-Reply-To: <20260130151832.1132894-1-janusz.dziedzic@gmail.com> References: <20260130151832.1132894-1-janusz.dziedzic@gmail.com> Message-ID: On Fri, Jan 30, 2026 at 04:16:35PM +0100, Janusz Dziedzic wrote: > Configure driver RX filtering correctly for broadcast > public action frames. > diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c > @@ -4000,6 +4001,7 @@ void hostapd_dpp_push_button_stop(struct hostapd_data *hapd) > if (!ifaces || !ifaces->dpp) > return; > eloop_cancel_timeout(hostapd_dpp_push_button_expire, hapd, NULL); > + hostapd_drv_dpp_listen(hapd, false); This seems to cause memory corruption. For example, sae_pk hwsim test case fails due to hostapd segmentation fault. I did not go through all details, but something is wrong with nl80211_dpp_listen() getting called when bss->nl_mgmt is not in suitable state for ELOOP_SOCKET_INVALID unmasking. > if (hostapd_dpp_pb_active(hapd)) { > wpa_printf(MSG_DEBUG, "DPP: Stop active push button mode"); Maybe that call should be within this conditional location instead? Or alternatively, some additional checks are needed elsewhere to avoid the driver interface operation in undesired state. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Sat Jan 31 07:30:45 2026 From: j at w1.fi (Jouni Malinen) Date: Sat, 31 Jan 2026 17:30:45 +0200 Subject: [PATCH 1/3] MLD: Pass SSID when doing multi-link probe check In-Reply-To: <20260126151647.2588514-4-benjamin@sipsolutions.net> References: <20260126151647.2588514-4-benjamin@sipsolutions.net> Message-ID: On Mon, Jan 26, 2026 at 04:16:48PM +0100, Benjamin Berg wrote: > If the SSID is not passed, then the function will resolve hidden ML > neighbors even when the SSID is not yet known. Should this happen, then > we will later fail to find the link and it will not be used. Thanks, all three patches applied. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Sat Jan 31 08:27:31 2026 From: j at w1.fi (Jouni Malinen) Date: Sat, 31 Jan 2026 18:27:31 +0200 Subject: [PATCH] Add support for HE PHY type in neighbor reports In-Reply-To: <20260117113148.77268-1-trevor@freedisc.co.uk> References: <20260117113148.77268-1-trevor@freedisc.co.uk> Message-ID: On Sat, Jan 17, 2026 at 11:31:48AM +0000, Trevor North wrote: > Add support for HE PHY type in neighbor reports as defined in > IEEE Std 802.11-2024 dot11PHYType. > > To the best of my understanding this is the intended behaviour and I > have observed no adverse effects with the clients I have available in my > environment. I must caveat however that I am far from a subject matter > expert and most of my clients are modern. Thanks, applied. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Sat Jan 31 08:27:54 2026 From: j at w1.fi (Jouni Malinen) Date: Sat, 31 Jan 2026 18:27:54 +0200 Subject: [FIX 1] Correct op class and chan for HE PHY In-Reply-To: <20260126102739.56637-1-trevor@freedisc.co.uk> References: <30a02fe0-4560-4c4f-ae63-b213d1caab8c@freebox.fr> <20260126102739.56637-1-trevor@freedisc.co.uk> Message-ID: On Mon, Jan 26, 2026 at 10:27:39AM +0000, Trevor North wrote: > This leverages the more recently added get_operation_channel_width > to maintain return of the correct operating class and channel when a HE > PHY type is determined. Thanks, applied. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Sat Jan 31 08:47:11 2026 From: j at w1.fi (Jouni Malinen) Date: Sat, 31 Jan 2026 18:47:11 +0200 Subject: [PATCH 1/2] MLD: Change get_shared_radio_freqs_data() to also support MLD connections In-Reply-To: <20260111120131.187784-1-andrei.otcheretianski@intel.com> References: <20260111120131.187784-1-andrei.otcheretianski@intel.com> Message-ID: On Sun, Jan 11, 2026 at 02:01:30PM +0200, Andrei Otcheretianski wrote: > For now, the frequencies are the ones associated with the valid links > and not the active links. Thanks, both patches applied. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Sat Jan 31 08:58:03 2026 From: j at w1.fi (Jouni Malinen) Date: Sat, 31 Jan 2026 18:58:03 +0200 Subject: [PATCH] OpenSSL: Support PEM encoded chain from ca_cert blob In-Reply-To: <20251219110905.2873402-1-bgalvani@redhat.com> References: <20251219110905.2873402-1-bgalvani@redhat.com> Message-ID: On Fri, Dec 19, 2025 at 12:09:05PM +0100, Beniamino Galvani wrote: > Support configuring a chain of certificates through a ca_cert blob. > > Since the code to to add the certificate to the store is used in > multiple places in tls_connection_ca_cert(), extract it to a separate > function. Thanks, applied. -- Jouni Malinen PGP id EFC895FA