[RFC 05/30] WPA: Extend PMK to PTK derivation to also derive a SMD_KDK

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


Extend the PMK to PTK derivation to also derive a SMD_KDK. In
addition, extend the function to also allow including the
SMD Identifier as part of the 'z'.

Similarly, also extend wpa_auth_802_1x_pmk_to_ptk().

Signed-off-by: Ilan Peer <ilan.peer at intel.com>
---
 src/ap/ieee802_11.c              |  2 +-
 src/ap/wpa_auth.c                |  2 +-
 src/common/common_module_tests.c |  2 +-
 src/common/wpa_common.c          | 38 ++++++++++++++++++++++++--------
 src/common/wpa_common.h          | 11 +++++++--
 src/rsn_supp/wpa.c               |  2 +-
 wlantest/rx_eapol.c              |  2 +-
 wpa_supplicant/sme.c             |  2 +-
 8 files changed, 44 insertions(+), 17 deletions(-)

diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index ecaf97713221..10b127760c4f 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -3575,7 +3575,7 @@ static int ieee80211_802_1x_derive_ptk(struct hostapd_data *hapd,
 		    sta->eap_auth_data.cipher,
 		    wpabuf_head_u8(sta->eap_auth_data.dhss),
 		    wpabuf_len(sta->eap_auth_data.dhss),
-		    &sta->eap_auth_data.ptk, kdk_len)) {
+		    &sta->eap_auth_data.ptk, kdk_len, 0)) {
 		wpa_printf(MSG_INFO, "Failed to derive the PTK");
 		return -1;
 	}
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 699a3d2b0a9b..5140d378b78e 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -3162,7 +3162,7 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
 			     wpa_auth_get_aa(sm), wpa_auth_get_spa(sm),
 			     sm->ANonce, snonce, ptk, akmp,
 			     sm->pairwise, z, z_len, kdk_len,
-			     sm->pasn_group);
+			     sm->pasn_group, 0);
 	if (ret) {
 		wpa_printf(MSG_DEBUG,
 			   "WPA: PTK derivation failed");
diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c
index 74a0ea368202..49beaecdbe0d 100644
--- a/src/common/common_module_tests.c
+++ b/src/common/common_module_tests.c
@@ -735,7 +735,7 @@ static int pasn_test_no_pasn_auth(void)
 			     "Pairwise key expansion",
 			     spa, aa, snonce, anonce,
 			     &ptk, WPA_KEY_MGMT_SAE, WPA_CIPHER_CCMP,
-			     NULL, 0, WPA_KDK_MAX_LEN, 19);
+			     NULL, 0, WPA_KDK_MAX_LEN, 19, 0);
 
 	if (ret)
 		return ret;
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index 55e690ded236..d3edb0eeb588 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -596,6 +596,7 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp,
  * @z: Additional context data
  * @z_len: Length of additional context data
  * @kdk_len: The length in octets that should be derived for KDK
+ * @smd_kdk_len: The length in octets that should be derived for SMD KDK
  * Returns: 0 on success, -1 on failure
  *
  * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
@@ -607,19 +608,20 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp,
  * The optional Z.x component is used only with DPP and that part is not defined
  * in IEEE 802.11. The additional context data is also used to carry the DHss
  * (Diffie-Hellman shared secret) when IEEE 802.1X authentication in
- * Authentication frames is  used.
+ * Authentication frames is used and SMD Indentifier when the association is
+ * an SMD association
  */
 int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
 		   const u8 *addr1, const u8 *addr2,
 		   const u8 *nonce1, const u8 *nonce2,
 		   struct wpa_ptk *ptk, int akmp, int cipher,
-		   const u8 *z, size_t z_len, size_t kdk_len, u16 pasn_group)
+		   const u8 *z, size_t z_len, size_t kdk_len, u16 pasn_group,
+		   size_t smd_kdk_len)
 {
-#define MAX_Z_LEN 66 /* with NIST P-521 */
-	u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN + MAX_Z_LEN];
+	u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN + WPA_MAX_Z_LEN];
 	size_t data_len = 2 * ETH_ALEN + 2 * WPA_NONCE_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];
 	size_t ptk_len;
 #ifdef CONFIG_OWE
 	int owe_ptk_workaround = 0;
@@ -635,7 +637,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
 		return -1;
 	}
 
-	if (z_len > MAX_Z_LEN)
+	if (z_len > WPA_MAX_Z_LEN)
 		return -1;
 
 	if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) {
@@ -668,17 +670,28 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
 		return -1;
 	}
 
+	if (smd_kdk_len > WPA_SMD_KDK_MAX_LEN) {
+		wpa_printf(MSG_ERROR,
+			   "WPA: SMD KDK len=%zu exceeds max supported len",
+			   smd_kdk_len);
+		return -1;
+	}
+
+	/* Only some of the AKMs below derive an explicit hash algorithm */
+	ptk->hash_alg = RSN_HASH_NOT_SPECIFIED;
 	ptk->kck_len = wpa_kck_len(akmp, pmk_len);
 	ptk->kek_len = wpa_kek_len(akmp, pmk_len, pasn_group);
 	ptk->tk_len = wpa_cipher_key_len(cipher);
 	ptk->kdk_len = kdk_len;
+	ptk->smd_kdk_len = smd_kdk_len;
 	if (ptk->tk_len == 0) {
 		wpa_printf(MSG_ERROR,
 			   "WPA: Unsupported cipher (0x%x) used in PTK derivation",
 			   cipher);
 		return -1;
 	}
-	ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len + ptk->kdk_len;
+	ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len + ptk->kdk_len +
+		  ptk->smd_kdk_len;
 
 	if (wpa_key_mgmt_sha384(akmp)) {
 #ifdef CONFIG_SHA384
@@ -815,6 +828,13 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
 		wpa_hexdump_key(MSG_DEBUG, "WPA: KDK", ptk->kdk, ptk->kdk_len);
 	}
 
+	if (smd_kdk_len) {
+		os_memcpy(ptk->smd_kdk, tmp + ptk->kck_len + ptk->kek_len +
+			  ptk->tk_len + ptk->kdk_len, ptk->smd_kdk_len);
+		wpa_hexdump_key(MSG_DEBUG, "WPA: SMD KDK",
+				ptk->smd_kdk, ptk->smd_kdk_len);
+	}
+
 	ptk->kek2_len = 0;
 	ptk->kck2_len = 0;
 
@@ -2140,12 +2160,12 @@ int wpa_auth_802_1x_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa,
 			       const u8 *aa, const u8 *snonce, const u8 *anonce,
 			       int akmp, int cipher, const u8 *dhss,
 			       size_t dhss_len, struct wpa_ptk *ptk,
-			       size_t kdk_len)
+			       size_t kdk_len, size_t smd_kdk_len)
 {
 	return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion",
 			      spa, aa, snonce, anonce, ptk, akmp,
 			      cipher, dhss, dhss_len, kdk_len,
-			      PASN_GROUP_NOT_SPECIFIED);
+			      PASN_GROUP_NOT_SPECIFIED, smd_kdk_len);
 }
 
 
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 158f79afd167..2c301601174d 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -267,6 +267,10 @@ struct wpa_eapol_key {
 #define FILS_FT_MAX_LEN 48
 #define WPA_PASN_KCK_LEN 32
 #define WPA_LTF_KEYSEED_MAX_LEN 48
+#define WPA_SMD_KDK_MAX_LEN PMK_LEN_MAX
+
+ /* Maximal DHss for NIST P-521 + optional SMD Identifier */
+#define WPA_MAX_Z_LEN (66 + ETH_ALEN)
 
 /**
  * struct wpa_ptk - WPA Pairwise Transient Key
@@ -279,6 +283,7 @@ struct wpa_ptk {
 	u8 kck2[WPA_KCK_MAX_LEN]; /* FT reasoc Key Confirmation Key (KCK2) */
 	u8 kek2[WPA_KEK_MAX_LEN]; /* FT reassoc Key Encryption Key (KEK2) */
 	u8 kdk[WPA_KDK_MAX_LEN]; /* Key Derivation Key */
+	u8 smd_kdk[WPA_SMD_KDK_MAX_LEN]; /* SMD Key Derivation Key */
 	u8 ltf_keyseed[WPA_LTF_KEYSEED_MAX_LEN]; /* LTF Key seed */
 	size_t kck_len;
 	size_t kek_len;
@@ -288,6 +293,7 @@ struct wpa_ptk {
 	size_t kdk_len;
 	size_t ptk_len;
 	size_t ltf_keyseed_len;
+	size_t smd_kdk_len;
 	enum rsn_hash_alg hash_alg;
 	int installed; /* 1 if key has already been installed to driver */
 	bool installed_rx; /* whether TK has been installed as the next TK
@@ -495,7 +501,8 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
 		   const u8 *addr1, const u8 *addr2,
 		   const u8 *nonce1, const u8 *nonce2,
 		   struct wpa_ptk *ptk, int akmp, int cipher,
-		   const u8 *z, size_t z_len, size_t kdk_len, u16 pasn_group);
+		   const u8 *z, size_t z_len, size_t kdk_len, u16 pasn_group,
+		   size_t smd_kdk_len);
 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);
@@ -874,7 +881,7 @@ int wpa_auth_802_1x_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa,
 			       const u8 *aa, const u8 *snonce, const u8 *anonce,
 			       int akmp, int cipher, const u8 *dhss,
 			       size_t dhss_len, struct wpa_ptk *ptk,
-			       size_t kdk_len);
+			       size_t kdk_len, size_t smd_kdk_len);
 void wpa_add_supported_groups(struct wpabuf *buf, const int *groups);
 
 /* Security Profile entry (IEEE P802.11bn/D2.0) */
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 5a05a991b9f9..c8f5d359af8b 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -792,7 +792,7 @@ static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr,
 			     sm->own_addr, wpa_sm_get_auth_addr(sm), sm->snonce,
 			     key->key_nonce, ptk, akmp,
 			     sm->pairwise_cipher, z, z_len,
-			     kdk_len, sm->pasn_group);
+			     kdk_len, sm->pasn_group, 0);
 	if (ret) {
 		wpa_printf(MSG_ERROR, "WPA: PTK derivation failed");
 		return ret;
diff --git a/wlantest/rx_eapol.c b/wlantest/rx_eapol.c
index d0fbd8ad6163..57c5480a3db1 100644
--- a/wlantest/rx_eapol.c
+++ b/wlantest/rx_eapol.c
@@ -250,7 +250,7 @@ static int try_pmk(struct wlantest *wt, struct wlantest_bss *bss,
 				  aa, sa, sta->anonce,
 				  sta->snonce, &ptk, sta->key_mgmt,
 				  sta->pairwise_cipher, NULL, 0, kdk_len,
-				  PASN_GROUP_NOT_SPECIFIED) < 0 ||
+				  PASN_GROUP_NOT_SPECIFIED, 0) < 0 ||
 		   check_mic(sta, ptk.kck, ptk.kck_len, ver, data, len) < 0) {
 		return -1;
 	}
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 1e4dc15dec1b..c107947033ee 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -4627,7 +4627,7 @@ static int sme_802_1x_derive_ptk_and_install(struct wpa_supplicant *wpa_s,
 				       key_mgmt, pairwise_cipher,
 				       wpabuf_head(auth_1x->dhss),
 				       wpabuf_len(auth_1x->dhss),
-				       &ptk, kdk_len) < 0) {
+				       &ptk, kdk_len, 0) < 0) {
 		wpa_msg(wpa_s, MSG_INFO, "SME: PTK derivation failed");
 		forced_memzero(pmk, PMK_LEN_MAX);
 		return -1;
-- 
2.55.0




More information about the Hostap mailing list