[PATCH 14/29] EPPKE: Extend existing PASN APIs for EPPKE Authentication

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


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 <smagam at qti.qualcomm.com>
Signed-off-by: Rohan Dutta <drohan at qti.qualcomm.com>
---
 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 afd2e1cf5..162ef6a3d 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 7002b3a25..658212ad0 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -422,3 +422,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 5b0e2a176..e111aec99 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -2994,6 +2994,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);
@@ -3014,8 +3018,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;
@@ -3091,7 +3102,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_sta = 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);
 
@@ -3230,19 +3246,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);
@@ -3332,6 +3360,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");
@@ -3364,6 +3403,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)",
@@ -3376,6 +3420,9 @@ static void handle_auth(struct hostapd_data *hapd,
 #ifdef CONFIG_PASN
 	      (auth_alg == WLAN_AUTH_PASN && auth_transaction == 3) ||
 #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);
@@ -3552,7 +3599,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
@@ -3717,6 +3769,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 ac36c9c48..6e009d2c0 100644
--- a/src/ap/ieee802_11_eht.c
+++ b/src/ap/ieee802_11_eht.c
@@ -1059,6 +1059,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 d09558121..0d9a87768 100644
--- a/src/pasn/pasn_common.c
+++ b/src/pasn/pasn_common.c
@@ -99,6 +99,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 98355d748..6be976ba6 100644
--- a/src/pasn/pasn_common.h
+++ b/src/pasn/pasn_common.h
@@ -52,6 +52,8 @@ struct pasn_data {
 	size_t kdk_len;
 	void *cb_ctx;
 	unsigned int auth_alg;
+	u8 mld_addr[ETH_ALEN];
+	bool is_ml_sta;
 
 #ifdef CONFIG_SAE
 	struct sae_pt *pt;
@@ -213,6 +215,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 0d3d4a5e3..2a3503b7d 100644
--- a/src/pasn/pasn_responder.c
+++ b/src/pasn/pasn_responder.c
@@ -146,7 +146,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_sta)
+		own_addr = pasn->mld_addr;
+#endif
 	ret = sae_prepare_commit_pt(&pasn->sae, pasn->pt, own_addr, peer_addr,
 				    NULL, NULL);
 	if (ret) {
@@ -589,6 +592,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_sta) {
+		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->akmp, pasn->cipher, pasn->pmk_len);
@@ -742,6 +756,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");
@@ -772,6 +798,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




More information about the Hostap mailing list