[RFC 20/30] PASN: Extend PTK derivation to support SMD

Ilan Peer ilan.peer at intel.com
Wed Oct 21 06:43:38 PDT 2026


Extend the PASN PMK to PTK derivation to also support
SMD:

- Include SMD Identifier in the PTK derivation.
- Derive SMD_KDK when needed (for per-AP MLD SMD mode).

The changes include changes in the PASN library and also
in station SME and AP SME.

Signed-off-by: Ilan Peer <ilan.peer at intel.com>
---
 src/ap/ieee802_11.c              |  4 +++-
 src/common/common_module_tests.c |  3 ++-
 src/common/wpa_common.c          | 24 ++++++++++++++++++++----
 src/common/wpa_common.h          |  2 +-
 src/pasn/pasn_common.h           | 13 +++++++++++++
 src/pasn/pasn_initiator.c        |  4 +++-
 src/pasn/pasn_responder.c        |  4 +++-
 src/rsn_supp/wpa.c               |  7 +++++--
 src/rsn_supp/wpa.h               |  1 +
 wpa_supplicant/sme.c             |  4 ++++
 10 files changed, 55 insertions(+), 11 deletions(-)

diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 4dce610ee6f1..608eee822271 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -4779,7 +4779,9 @@ static void pasn_fils_auth_resp(struct hostapd_data *hapd,
 			      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,
-			      pasn->auth_alg == WLAN_AUTH_EPPKE);
+			      pasn->auth_alg == WLAN_AUTH_EPPKE,
+			      pasn_get_smd_id(sta->pasn),
+			      pasn_derive_smd_kdk(sta->pasn) ? pmk_len : 0);
 	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 49beaecdbe0d..f24be70163ad 100644
--- a/src/common/common_module_tests.c
+++ b/src/common/common_module_tests.c
@@ -655,7 +655,8 @@ 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, false);
+			      WPA_KDK_MAX_LEN, 0, &hash_alg, false,
+			      NULL, 0);
 
 	if (ret)
 		return ret;
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index 617278faf931..6ccc781d8e66 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -1755,6 +1755,9 @@ static enum rsn_hash_alg pasn_select_hash_alg(int akmp, int cipher,
  * @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
+ * @smd_id: The SMD ID for EPPKE authentication. Can be NULL
+ * @smd_kdk_len: The length in octets that should be derived for SMD KDK.
+ *     Can be zero
  * Returns: 0 on success, -1 on failure
  */
 int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
@@ -1762,10 +1765,10 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
 		    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,
-		    bool is_eppke)
+		    bool is_eppke, const u8 *smd_id, size_t smd_kdk_len)
 {
 	u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN +
-	       WPA_KDK_MAX_LEN];
+	       WPA_KDK_MAX_LEN + WPA_SMD_KDK_MAX_LEN];
 	const u8 *pos;
 	u8 *data;
 	size_t data_len, ptk_len;
@@ -1796,7 +1799,7 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
 	 * TK = L(PASN-PTK, 256, TK_bits)
 	 * KDK = L(PASN-PTK, 256 + TK_bits, kdk_len * 8)
 	 */
-	data_len = 2 * ETH_ALEN + dhss_len;
+	data_len = 2 * ETH_ALEN + dhss_len + (smd_id ? ETH_ALEN : 0);
 	data = os_zalloc(data_len);
 	if (!data)
 		return -1;
@@ -1804,10 +1807,13 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
 	os_memcpy(data, spa, ETH_ALEN);
 	os_memcpy(data + ETH_ALEN, bssid, ETH_ALEN);
 	os_memcpy(data + 2 * ETH_ALEN, dhss, dhss_len);
+	if (smd_id)
+		os_memcpy(data + 2 * ETH_ALEN + dhss_len, smd_id, ETH_ALEN);
 
 	ptk->kck_len = WPA_PASN_KCK_LEN;
 	ptk->tk_len = wpa_cipher_key_len(cipher);
 	ptk->kdk_len = kdk_len;
+	ptk->smd_kdk_len = smd_kdk_len;
 	ptk->kek_len = kek_len;
 	ptk->kek2_len = 0;
 	ptk->kck2_len = 0;
@@ -1819,7 +1825,8 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
 		goto err;
 	}
 
-	ptk_len = ptk->kck_len + ptk->tk_len + ptk->kdk_len + ptk->kek_len;
+	ptk_len = ptk->kck_len + ptk->tk_len + ptk->kdk_len +
+		  ptk->smd_kdk_len + ptk->kek_len;
 	if (ptk_len > sizeof(tmp))
 		goto err;
 
@@ -1862,6 +1869,8 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
 		   MAC2STR(spa), MAC2STR(bssid));
 
 	wpa_hexdump_key(MSG_DEBUG, "PASN: DHss", dhss, dhss_len);
+	if (smd_id)
+		wpa_hexdump_key(MSG_DEBUG, "PASN: SMD ID", smd_id, ETH_ALEN);
 	wpa_hexdump_key(MSG_DEBUG, "PASN: PMK", pmk, pmk_len);
 	wpa_hexdump_key(MSG_DEBUG, "PASN: PASN-PTK", tmp, ptk_len);
 
@@ -1884,6 +1893,13 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
 		os_memcpy(ptk->kdk, pos, ptk->kdk_len);
 		wpa_hexdump_key(MSG_DEBUG, "PASN: KDK:",
 				ptk->kdk, ptk->kdk_len);
+		pos += ptk->kdk_len;
+	}
+
+	if (smd_kdk_len) {
+		os_memcpy(ptk->smd_kdk, pos, ptk->smd_kdk_len);
+		wpa_hexdump_key(MSG_DEBUG, "PASN: SMD KDK:",
+				ptk->smd_kdk, ptk->smd_kdk_len);
 	}
 
 	ptk->ptk_len = ptk_len;
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 6ade8f047058..c3a3c6b06c3b 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -853,7 +853,7 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
 		    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,
-		    bool is_eppke);
+		    bool is_eppke, const u8 *smd_id, size_t smd_kdk_len);
 
 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 bdeef7c7b9a0..59f07bf3e7f7 100644
--- a/src/pasn/pasn_common.h
+++ b/src/pasn/pasn_common.h
@@ -181,6 +181,8 @@ struct pasn_data {
 	bool authorized;
 	bool tk_configured;
 #endif /* CONFIG_ENC_ASSOC */
+	const u8 *smd_id;
+	bool derive_smd_kdk;
 #ifdef CONFIG_PMKSA_PRIVACY
 	bool pmksa_caching_privacy;
 	u8 epp_pmkid_cur[PMKID_LEN];
@@ -369,6 +371,17 @@ int pasn_get_cipher(struct pasn_data *pasn);
 size_t pasn_get_pmk_len(struct pasn_data *pasn);
 u8 * pasn_get_pmk(struct pasn_data *pasn);
 struct wpa_ptk * pasn_get_ptk(struct pasn_data *pasn);
+
+static inline const u8 * pasn_get_smd_id(struct pasn_data *pasn)
+{
+	return pasn->smd_id;
+}
+
+static inline bool pasn_derive_smd_kdk(struct pasn_data *pasn)
+{
+	return pasn->derive_smd_kdk;
+}
+
 int pasn_add_encrypted_data(struct pasn_data *pasn, struct wpabuf *buf,
 			    const u8 *data, size_t data_len);
 int pasn_parse_encrypted_data(struct pasn_data *pasn, const u8 *data,
diff --git a/src/pasn/pasn_initiator.c b/src/pasn/pasn_initiator.c
index 18a109b51280..5e4f4b9470a1 100644
--- a/src/pasn/pasn_initiator.c
+++ b/src/pasn/pasn_initiator.c
@@ -1563,7 +1563,9 @@ int wpas_parse_pasn_frame(struct pasn_data *pasn, u16 auth_type,
 			      wpabuf_head(secret), wpabuf_len(secret),
 			      &pasn->ptk, pasn->akmp, pasn->cipher,
 			      pasn->kdk_len, pasn->kek_len, &pasn->hash_alg,
-			      pasn->auth_alg == WLAN_AUTH_EPPKE);
+			      pasn->auth_alg == WLAN_AUTH_EPPKE,
+			      pasn_get_smd_id(pasn),
+			      pasn_derive_smd_kdk(pasn) ? pasn->pmk_len : 0);
 	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 1ef066953ef3..e8e1a67ad528 100644
--- a/src/pasn/pasn_responder.c
+++ b/src/pasn/pasn_responder.c
@@ -501,7 +501,9 @@ pasn_derive_keys(struct pasn_data *pasn,
 			      &pasn->ptk, pasn->akmp,
 			      pasn->cipher, pasn->kdk_len, pasn->kek_len,
 			      &pasn->hash_alg,
-			      pasn->auth_alg == WLAN_AUTH_EPPKE);
+			      pasn->auth_alg == WLAN_AUTH_EPPKE,
+			      pasn_get_smd_id(pasn),
+			      pasn_derive_smd_kdk(pasn) ? pmk_len : 0);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK");
 		return -1;
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 5c032e0c5819..b104869a404b 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -749,10 +749,13 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
 }
 
 
-static bool wpa_sm_is_smd_per_ap_mld_ptk(struct wpa_sm *sm)
+bool wpa_sm_is_smd_per_ap_mld_ptk(struct wpa_sm *sm)
 {
 	const struct ieee80211_smd_info *smd;
 
+	if (!sm)
+		return false;
+
 	if (!sm->smd || !sm->ap_smd_info)
 		return false;
 
@@ -6142,7 +6145,7 @@ const u8 * wpa_sm_get_smd_id(struct wpa_sm *sm)
 
 size_t wpa_sm_get_smd_kdk_len(struct wpa_sm *sm, size_t pmk_len)
 {
-	if (!sm || !wpa_sm_is_smd_per_ap_mld_ptk(sm))
+	if (!wpa_sm_is_smd_per_ap_mld_ptk(sm))
 		return 0;
 
 	return pmk_len;
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index c476af9a6e17..80d6b1814360 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -236,6 +236,7 @@ int wpa_sm_set_ap_security_profile(struct wpa_sm *sm, const u8 *elem,
 int wpa_sm_set_ap_smd_info(struct wpa_sm *sm, const u8 *ie);
 const u8 * wpa_sm_get_smd_id(struct wpa_sm *sm);
 size_t wpa_sm_get_smd_kdk_len(struct wpa_sm *sm, size_t pmk_len);
+bool wpa_sm_is_smd_per_ap_mld_ptk(struct wpa_sm *sm);
 int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen);
 
 int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 26f76cc82a5a..dbb291b6583a 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -1773,6 +1773,10 @@ static int wpas_eppke_initialize(struct wpa_supplicant *wpa_s,
 	pasn->group = group;
 	pasn->freq = bss->freq;
 	pasn->auth_alg = WLAN_AUTH_EPPKE;
+#ifdef CONFIG_SMD
+	pasn->smd_id = wpa_sm_get_smd_id(wpa_s->wpa);
+	pasn->derive_smd_kdk = wpa_sm_is_smd_per_ap_mld_ptk(wpa_s->wpa);
+#endif /* CONFIG_SMD */
 
 	os_memcpy(pasn->own_addr, wpa_s->own_addr, ETH_ALEN);
 	if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) &&
-- 
2.55.0




More information about the Hostap mailing list