[PATCH v2 12/28] EPPKE: Extend existing PASN APIs for EPPKE Authentication

Ainy Kumari ainy.kumari at oss.qualcomm.com
Tue Jan 6 05:45:22 PST 2026


From: Sai Pratyusha Magam <smagam at qti.qualcomm.com>

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 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




More information about the Hostap mailing list