[RFC 17/30] WPA: Support station mode SMD per-AP MLD PTK
Ilan Peer
ilan.peer at intel.com
Wed Oct 21 06:43:35 PDT 2026
- Support deriving a new PTK and store it as a temporary
PTK.
- Support calculating MIC for per-AP MLD PTK ST flow.
- Support setting the temporary PTK to the current PTK.
TODO: Need to support installing keys to the driver.
Signed-off-by: Ilan Peer <ilan.peer at intel.com>
---
src/common/wpa_common.h | 30 +++++++++++++
src/rsn_supp/wpa.c | 97 +++++++++++++++++++++++++++++++++++++++++
src/rsn_supp/wpa.h | 6 +++
3 files changed, 133 insertions(+)
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index b6bed37da7d9..6ade8f047058 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -41,6 +41,36 @@ enum rsn_hash_alg {
RSN_HASH_SHA512,
};
+/**
+ * wpa_akm_hash_alg - Get the hash algorithm to use for a given AKM
+ * @akmp: Negotiated AKM
+ * @pmk_len: Length of the PMK
+ * Returns: The hash algorithm to use, or RSN_HASH_NOT_SPECIFIED if it cannot
+ * be determined
+ */
+static inline enum rsn_hash_alg wpa_akm_hash_alg(int akmp, size_t pmk_len)
+{
+ if (wpa_key_mgmt_sha384(akmp))
+ return RSN_HASH_SHA384;
+ if (wpa_key_mgmt_sha256(akmp))
+ return RSN_HASH_SHA256;
+
+ /*
+ * For the remaining AKMs, e.g., SAE with extended key, the hash
+ * algorithm is determined by the PMK length.
+ */
+ switch (pmk_len) {
+ case 32:
+ return RSN_HASH_SHA256;
+ case 48:
+ return RSN_HASH_SHA384;
+ case 64:
+ return RSN_HASH_SHA512;
+ }
+
+ return RSN_HASH_NOT_SPECIFIED;
+}
+
#ifdef CONFIG_NO_TKIP
#define WPA_ALLOWED_PAIRWISE_CIPHERS \
(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_NONE | \
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 6e6f67e0593b..1f129a3595a8 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -3659,12 +3659,25 @@ failed:
static void wpa_sm_tptk_to_ptk(struct wpa_sm *sm)
{
+ u8 tmp[WPA_SMD_KDK_MAX_LEN];
+ size_t smd_kdk_len = sm->ptk.smd_kdk_len;
+
+ if (sm->ptk.smd_kdk_len > 0) {
+ os_memcpy(tmp, sm->ptk.smd_kdk, sm->ptk.smd_kdk_len);
+ smd_kdk_len = sm->ptk.smd_kdk_len;
+ }
+
sm->tptk_set = 0;
sm->ptk_set = 1;
os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk));
os_memset(&sm->tptk, 0, sizeof(sm->tptk));
sm->hash_alg = sm->ptk.hash_alg;
+ if (smd_kdk_len > 0) {
+ os_memcpy(sm->ptk.smd_kdk, tmp, smd_kdk_len);
+ sm->ptk.smd_kdk_len = smd_kdk_len;
+ }
+
if (wpa_sm_pmf_enabled(sm)) {
os_memcpy(sm->last_kck, sm->ptk.kck, sm->ptk.kck_len);
sm->last_kck_len = sm->ptk.kck_len;
@@ -8223,3 +8236,87 @@ int wpa_sm_get_cached_tk(struct wpa_sm *sm, u8 *tk, size_t *tk_len)
return 0;
}
#endif /* CONFIG_TESTING_OPTIONS */
+
+
+int wpa_sm_smd_calc_mic(struct wpa_sm *sm, const u8 *aa, const u8 *snonce,
+ const u8 *anonce, bool st_prep, u8 *mic)
+{
+ enum rsn_hash_alg hash_alg;
+
+ if (!sm->tptk_set || !sm->tptk.kck_len) {
+ wpa_printf(MSG_DEBUG,
+ "SMD: TPKT not available for MIC calculation");
+ return -1;
+ }
+
+ hash_alg = sm->hash_alg;
+ if (hash_alg == RSN_HASH_NOT_SPECIFIED)
+ hash_alg = wpa_akm_hash_alg(sm->key_mgmt, sm->pmk_len);
+
+ return wpa_smd_mic(sm->tptk.kck, sm->tptk.kck_len,
+ hash_alg, aa, sm->own_addr,
+ anonce, snonce, st_prep, mic);
+}
+
+
+int wpa_sm_smd_derive_ptk(struct wpa_sm *sm, const u8 *aa, const u8 *snonce,
+ const u8 *anonce, const u8 *z, size_t z_len)
+{
+ struct wpa_ptk *ptk = &sm->tptk;
+ const u8 *smd_id = wpa_sm_get_smd_id(sm);
+ u8 z_buf[WPA_MAX_Z_LEN];
+ int ret;
+
+ if (!sm->ptk.smd_kdk_len) {
+ wpa_printf(MSG_DEBUG, "SMD: KDK not available");
+ return -1;
+ }
+
+ if (!smd_id || z_len > WPA_MAX_Z_LEN - ETH_ALEN) {
+ wpa_printf(MSG_DEBUG, "SMD: Invalid PTK derivation context");
+ return -1;
+ }
+
+ /* Z = DHss || SMD Identifier */
+ os_memcpy(z_buf, z, z_len);
+ os_memcpy(z_buf + z_len, smd_id, ETH_ALEN);
+
+ ret = wpa_pmk_to_ptk(sm->ptk.smd_kdk, sm->ptk.smd_kdk_len,
+ "Pairwise key expansion",
+ sm->own_addr, aa,
+ snonce, anonce,
+ ptk, sm->key_mgmt,
+ sm->pairwise_cipher,
+ z_buf, z_len + ETH_ALEN,
+ sm->ptk.kdk_len, sm->pasn_group, 0);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "SMD: Failed to derive PTK");
+ return -1;
+ }
+
+ /*
+ * The PTK is only a temporary one and should be used during ST flow.
+ * Once the ST flow is done it can be installed
+ */
+ sm->tptk_set = 1;
+
+ return 0;
+}
+
+
+int wpa_sm_smd_set_tptk_to_ptk(struct wpa_sm *sm)
+{
+ if (!sm->tptk_set) {
+ wpa_printf(MSG_DEBUG, "SMD: TPTK: not set");
+ return -1;
+ }
+
+ wpa_sm_tptk_to_ptk(sm);
+
+ wpa_printf(MSG_DEBUG, "SMD: TPTK->PTK");
+
+ /* TODO: Need to handle TK installation */
+ wpa_printf(MSG_DEBUG, "SMD: TODO: Need to install TK");
+
+ return 0;
+}
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index d510f372f357..5a57dd21d12f 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -752,4 +752,10 @@ bool wpa_sm_pmksa_privacy_supported(struct wpa_sm *sm);
void wpa_sm_set_802_1x_auth_caps(struct wpa_sm *sm, u64 flags2);
+int wpa_sm_smd_calc_mic(struct wpa_sm *sm, const u8 *aa, const u8 *snonce,
+ const u8 *anone, bool st_prep, u8 *mic);
+int wpa_sm_smd_derive_ptk(struct wpa_sm *sm, const u8 *aa, const u8 *snonce,
+ const u8 *anonce, const u8 *z, size_t z_len);
+int wpa_sm_smd_set_tptk_to_ptk(struct wpa_sm *sm);
+
#endif /* WPA_H */
--
2.55.0
More information about the Hostap
mailing list