diff --git a/hostapd/config_file.c b/hostapd/config_file.c index b3dc8f81a..05dc96736 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -4577,6 +4577,12 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->disable_11ac = !!atoi(pos); } else if (os_strcmp(buf, "disable_11ax") == 0) { bss->disable_11ax = !!atoi(pos); +#ifdef CONFIG_PASN +#ifdef CONFIG_TESTING_OPTIONS + } else if (os_strcmp(buf, "force_kdk_derivation") == 0) { + bss->force_kdk_derivation = atoi(pos); +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_PASN */ } else { wpa_printf(MSG_ERROR, "Line %d: unknown configuration item '%s'", diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index f7a344e0e..4eb4ee272 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -862,6 +862,16 @@ struct hostapd_bss_config { */ u8 mka_psk_set; #endif /* CONFIG_MACSEC */ + +#ifdef CONFIG_PASN +#ifdef CONFIG_TESTING_OPTIONS + /* + * Normally, KDK should be derived if and only if both sides support + * secure LTF. Allow forcing KDK derivation for testing purposes. + */ + int force_kdk_derivation; +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_PASN */ }; /** diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 82a97468d..ebf35cf30 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -2290,7 +2290,8 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, akmp |= WPA_KEY_MGMT_PSK_SHA256; return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion", sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce, - ptk, akmp, sm->pairwise, z, z_len); + ptk, akmp, sm->pairwise, z, z_len, + sm->wpa_auth->conf.kdk ? WPA_KDK_MAX_LEN : 0); } diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 5f9df9c89..5dc3cc5c6 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -262,6 +262,12 @@ struct wpa_auth_config { #ifdef CONFIG_DPP2 int dpp_pfs; #endif /* CONFIG_DPP2 */ + + /* + * If set Key Derivation Key should be derived as part of PMK to + * PTK derivation. + */ + bool kdk; }; typedef enum { diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index c01654f38..a829361b5 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -208,6 +208,11 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, #ifdef CONFIG_DPP2 wconf->dpp_pfs = conf->dpp_pfs; #endif /* CONFIG_DPP2 */ +#ifdef CONFIG_PASN +#ifdef CONFIG_TESTING_OPTIONS + wconf->kdk = conf->force_kdk_derivation; +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_PASN */ } diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index 3d04650d3..750a3c8c4 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -333,6 +333,7 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver, * @ptk: Buffer for pairwise transient key * @akmp: Negotiated AKM * @cipher: Negotiated pairwise cipher + * @kdk_len: The length in octets that should be derived for KDK * Returns: 0 on success, -1 on failure * * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy @@ -348,12 +349,13 @@ 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) + const u8 *z, size_t z_len, size_t kdk_len) { #define MAX_Z_LEN 66 /* with NIST P-521 */ u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN + 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]; + u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN + + WPA_KDK_MAX_LEN]; size_t ptk_len; #ifdef CONFIG_OWE int owe_ptk_workaround = 0; @@ -395,16 +397,24 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, data_len += z_len; } + if (kdk_len > WPA_KDK_MAX_LEN) { + wpa_printf(MSG_ERROR, + "WPA: KDK len=%zu exceeds max supported len", + kdk_len); + return -1; + } + ptk->kck_len = wpa_kck_len(akmp, pmk_len); ptk->kek_len = wpa_kek_len(akmp, pmk_len); ptk->tk_len = wpa_cipher_key_len(cipher); + ptk->kdk_len = 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_len = ptk->kck_len + ptk->kek_len + ptk->tk_len + ptk->kdk_len; if (wpa_key_mgmt_sha384(akmp)) { #if defined(CONFIG_SUITEB192) || defined(CONFIG_FILS) @@ -488,6 +498,12 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len); wpa_hexdump_key(MSG_DEBUG, "WPA: TK", ptk->tk, ptk->tk_len); + if (kdk_len) { + os_memcpy(ptk->kdk, tmp + ptk->kck_len + ptk->kek_len + + ptk->tk_len, ptk->kdk_len); + wpa_hexdump_key(MSG_DEBUG, "WPA: KDK", ptk->kdk, ptk->kdk_len); + } + ptk->kek2_len = 0; ptk->kck2_len = 0; diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index 23e5418e6..44b803e65 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -216,6 +216,7 @@ struct wpa_eapol_key { #define WPA_KCK_MAX_LEN 32 #define WPA_KEK_MAX_LEN 64 #define WPA_TK_MAX_LEN 32 +#define WPA_KDK_MAX_LEN 32 #define FILS_ICK_MAX_LEN 48 #define FILS_FT_MAX_LEN 48 @@ -229,11 +230,13 @@ struct wpa_ptk { u8 tk[WPA_TK_MAX_LEN]; /* Temporal Key (TK) */ 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 */ size_t kck_len; size_t kek_len; size_t tk_len; size_t kck2_len; size_t kek2_len; + size_t kdk_len; int installed; /* 1 if key has already been installed to driver */ }; @@ -383,7 +386,7 @@ 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); + const u8 *z, size_t z_len, size_t 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); diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 834658324..24d7422bf 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -606,7 +606,8 @@ static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr, return wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion", sm->own_addr, sm->bssid, sm->snonce, key->key_nonce, ptk, akmp, - sm->pairwise_cipher, z, z_len); + sm->pairwise_cipher, z, z_len, + sm->kdk ? WPA_KDK_MAX_LEN : 0); } @@ -3184,6 +3185,7 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) sm->p2p = config->p2p; sm->wpa_rsc_relaxation = config->wpa_rsc_relaxation; sm->owe_ptk_workaround = config->owe_ptk_workaround; + sm->kdk = config->kdk; #ifdef CONFIG_FILS if (config->fils_cache_id) { sm->fils_cache_id_set = 1; @@ -3206,6 +3208,7 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) sm->wpa_rsc_relaxation = 0; sm->owe_ptk_workaround = 0; sm->beacon_prot = 0; + sm->kdk = false; } } diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index 2142772d7..fda3979df 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -130,6 +130,7 @@ struct rsn_supp_config { int owe_ptk_workaround; const u8 *fils_cache_id; int beacon_prot; + bool kdk; }; #ifndef CONFIG_NO_WPA diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 96b07fc68..1ac453c77 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -73,6 +73,12 @@ struct wpa_sm { * to be used */ int keyidx_active; /* Key ID for the active TK */ + /* + * If set Key Derivation Key should be derived as part of PMK to + * PTK derivation. + */ + bool kdk; + u8 own_addr[ETH_ALEN]; const char *ifname; const char *bridge_ifname; diff --git a/wlantest/rx_eapol.c b/wlantest/rx_eapol.c index 1e5d66735..cdc1fff0c 100644 --- a/wlantest/rx_eapol.c +++ b/wlantest/rx_eapol.c @@ -128,7 +128,7 @@ static int try_pmk(struct wlantest *wt, struct wlantest_bss *bss, "Pairwise key expansion", bss->bssid, sta->addr, sta->anonce, sta->snonce, &ptk, sta->key_mgmt, - sta->pairwise_cipher, NULL, 0) < 0 || + sta->pairwise_cipher, NULL, 0, 0) < 0 || check_mic(ptk.kck, ptk.kck_len, sta->key_mgmt, ver, data, len) < 0) { return -1; diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 4c1e42260..60129cf48 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -5149,6 +5149,11 @@ static const struct global_parse_data global_fields[] = { { INT_RANGE(extended_key_id, 0, 1), 0 }, #endif /* CONFIG_WNM */ { INT_RANGE(wowlan_disconnect_on_deinit, 0, 1), 0}, +#ifdef CONFIG_PASN +#ifdef CONFIG_TESTING_OPTIONS + { INT_RANGE(force_kdk_derivation, 0, 1), 0 }, +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_PASN */ }; #undef FUNC diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index 611b99318..6ce737c03 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -1608,6 +1608,16 @@ struct wpa_config { * 1 = Trigger disconnection */ int wowlan_disconnect_on_deinit; + +#ifdef CONFIG_PASN +#ifdef CONFIG_TESTING_OPTIONS + /* + * Normally, KDK should be derived if and only if both sides support + * secure LTF. Allow forcing KDK derivation for testing purposes. + */ + int force_kdk_derivation; +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_PASN*/ }; diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c index a9a66baad..6cee1e790 100644 --- a/wpa_supplicant/wpas_glue.c +++ b/wpa_supplicant/wpas_glue.c @@ -1451,5 +1451,10 @@ void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s, #endif /* CONFIG_FILS */ conf.beacon_prot = ssid->beacon_prot; } +#ifdef CONFIG_PASN +#ifdef CONFIG_TESTING_OPTIONS + conf.kdk = wpa_s->conf->force_kdk_derivation; +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_PASN*/ wpa_sm_set_config(wpa_s->wpa, ssid ? &conf : NULL); }