[PATCH 15/29] EPPKE: PTK/MIC Computation and key installation changes in Responder mode

Sai Pratyusha Magam smagam at qti.qualcomm.com
Thu Dec 11 05:14:29 PST 2025


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 <smagam at qti.qualcomm.com>
Signed-off-by: Rohan Dutta <drohan at qti.qualcomm.com>
---
 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        | 28 ++++++++++++++++++++++++++++
 wpa_supplicant/pasn_supplicant.c |  4 ++--
 7 files changed, 96 insertions(+), 14 deletions(-)

diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index e111aec99..adbe87f3c 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -2986,12 +2986,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)
@@ -3019,6 +3036,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 ?
@@ -3104,6 +3122,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_sta = sta->mld_info.mld_sta;
 #endif
@@ -3212,7 +3231,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");
@@ -3259,7 +3279,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 1284f396c..c18e71cf5 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -1600,8 +1600,12 @@ int sae_ext_key_group(size_t pmk_len)
  * 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
@@ -1819,10 +1823,15 @@ int wpa_ltf_keyseed(struct wpa_ptk *ptk, int akmp, int cipher)
  * @kck_len: KCK length in octets
  * @akmp: Negotiated AKM
  * @cipher: Negotiated pairwise cipher
- * @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 8a97091b4..0c0f04f33 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -7242,7 +7242,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 && auth_transaction == 2) {
diff --git a/src/pasn/pasn_common.c b/src/pasn/pasn_common.c
index 0d9a87768..e9b30ba99 100644
--- a/src/pasn/pasn_common.c
+++ b/src/pasn/pasn_common.c
@@ -46,7 +46,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;
@@ -54,6 +57,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 6be976ba6..ffc3c8b21 100644
--- a/src/pasn/pasn_common.h
+++ b/src/pasn/pasn_common.h
@@ -141,6 +141,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
@@ -166,6 +170,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 */
@@ -208,7 +216,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 2a3503b7d..8c9222386 100644
--- a/src/pasn/pasn_responder.c
+++ b/src/pasn/pasn_responder.c
@@ -428,6 +428,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_sta)
+		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,
@@ -655,6 +662,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_sta)
+		own_addr = pasn->mld_addr;
+#endif /* CONFIG_ENC_ASSOC */
+
 	ret = pasn_mic(pasn->ptk.kck, pasn->ptk.kck_len, pasn->akmp,
 		       pasn->cipher, own_addr, peer_addr, data, data_len,
 		       frame, frame_len, mic);
@@ -1028,6 +1041,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 */
+
 	ret = pasn_auth_frame_hash(pasn->akmp, pasn->cipher,
 				   ((const u8 *) mgmt) + IEEE80211_HDRLEN,
 				   len - IEEE80211_HDRLEN, pasn->hash,
@@ -1119,6 +1140,13 @@ int handle_auth_pasn_3(struct pasn_data *pasn, const u8 *own_addr,
 	if (!copy)
 		goto fail;
 	os_memset(copy + mic_offset, 0, mic_len);
+
+#ifdef CONFIG_ENC_ASSOC
+	if (pasn->auth_alg == WLAN_AUTH_EPPKE &&
+	    pasn->is_ml_sta)
+		own_addr = pasn->mld_addr;
+#endif /* CONFIG_ENC_ASSOC */
+
 	ret = pasn_mic(pasn->ptk.kck, pasn->ptk.kck_len, pasn->akmp,
 		       pasn->cipher, peer_addr, own_addr,
 		       pasn->hash, mic_len * 2,
diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c
index 46343ea6e..090df4eee 100644
--- a/wpa_supplicant/pasn_supplicant.c
+++ b/wpa_supplicant/pasn_supplicant.c
@@ -715,7 +715,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
@@ -973,7 +973,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




More information about the Hostap mailing list