diff options
Diffstat (limited to 'src/common/wpa_common.c')
-rw-r--r-- | src/common/wpa_common.c | 791 |
1 files changed, 583 insertions, 208 deletions
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index b78db05a..15ebcabb 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -36,6 +36,9 @@ static unsigned int wpa_kck_len(int akmp, size_t pmk_len) return pmk_len / 2; case WPA_KEY_MGMT_OWE: return pmk_len / 2; + case WPA_KEY_MGMT_SAE_EXT_KEY: + case WPA_KEY_MGMT_FT_SAE_EXT_KEY: + return pmk_len / 2; default: return 16; } @@ -72,6 +75,9 @@ static unsigned int wpa_kek_len(int akmp, size_t pmk_len) return pmk_len <= 32 ? 16 : 32; case WPA_KEY_MGMT_OWE: return pmk_len <= 32 ? 16 : 32; + case WPA_KEY_MGMT_SAE_EXT_KEY: + case WPA_KEY_MGMT_FT_SAE_EXT_KEY: + return pmk_len <= 32 ? 16 : 32; default: return 16; } @@ -108,6 +114,9 @@ unsigned int wpa_mic_len(int akmp, size_t pmk_len) return pmk_len / 2; case WPA_KEY_MGMT_OWE: return pmk_len / 2; + case WPA_KEY_MGMT_SAE_EXT_KEY: + case WPA_KEY_MGMT_FT_SAE_EXT_KEY: + return pmk_len / 2; default: return 16; } @@ -143,7 +152,8 @@ int wpa_use_cmac(int akmp) akmp == WPA_KEY_MGMT_DPP || wpa_key_mgmt_ft(akmp) || wpa_key_mgmt_sha256(akmp) || - wpa_key_mgmt_sae(akmp) || + (wpa_key_mgmt_sae(akmp) && + !wpa_key_mgmt_sae_ext_key(akmp)) || wpa_key_mgmt_suite_b(akmp); } @@ -223,6 +233,32 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver, wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using AES-CMAC (AKM-defined - SAE)"); return omac1_aes_128(key, buf, len, mic); + case WPA_KEY_MGMT_SAE_EXT_KEY: + case WPA_KEY_MGMT_FT_SAE_EXT_KEY: + wpa_printf(MSG_DEBUG, + "WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - SAE-EXT-KEY)", + (unsigned int) key_len * 8 * 2); + if (key_len == 128 / 8) { + if (hmac_sha256(key, key_len, buf, len, hash)) + return -1; +#ifdef CONFIG_SHA384 + } else if (key_len == 192 / 8) { + if (hmac_sha384(key, key_len, buf, len, hash)) + return -1; +#endif /* CONFIG_SHA384 */ +#ifdef CONFIG_SHA512 + } else if (key_len == 256 / 8) { + if (hmac_sha512(key, key_len, buf, len, hash)) + return -1; +#endif /* CONFIG_SHA512 */ + } else { + wpa_printf(MSG_INFO, + "SAE: Unsupported KCK length: %u", + (unsigned int) key_len); + return -1; + } + os_memcpy(mic, hash, key_len); + break; #endif /* CONFIG_SAE */ #ifdef CONFIG_HS20 case WPA_KEY_MGMT_OSEN: @@ -473,6 +509,36 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, (unsigned int) pmk_len); return -1; #endif /* CONFIG_DPP */ +#ifdef CONFIG_SAE + } else if (wpa_key_mgmt_sae_ext_key(akmp)) { + if (pmk_len == 32) { + wpa_printf(MSG_DEBUG, + "SAE: PTK derivation using PRF(SHA256)"); + if (sha256_prf(pmk, pmk_len, label, data, data_len, + tmp, ptk_len) < 0) + return -1; +#ifdef CONFIG_SHA384 + } else if (pmk_len == 48) { + wpa_printf(MSG_DEBUG, + "SAE: PTK derivation using PRF(SHA384)"); + if (sha384_prf(pmk, pmk_len, label, data, data_len, + tmp, ptk_len) < 0) + return -1; +#endif /* CONFIG_SHA384 */ +#ifdef CONFIG_SHA512 + } else if (pmk_len == 64) { + wpa_printf(MSG_DEBUG, + "SAE: PTK derivation using PRF(SHA512)"); + if (sha512_prf(pmk, pmk_len, label, data, data_len, + tmp, ptk_len) < 0) + return -1; +#endif /* CONFIG_SHA512 */ + } else { + wpa_printf(MSG_INFO, "SAE: Unknown PMK length %u", + (unsigned int) pmk_len); + return -1; + } +#endif /* CONFIG_SAE */ } else { wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA1)"); if (sha1_prf(pmk, pmk_len, label, data, data_len, tmp, @@ -816,7 +882,7 @@ int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce, #ifdef CONFIG_IEEE80211R -int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, +int wpa_ft_mic(int key_mgmt, const u8 *kck, size_t kck_len, const u8 *sta_addr, const u8 *ap_addr, u8 transaction_seqnum, const u8 *mdie, size_t mdie_len, const u8 *ftie, size_t ftie_len, @@ -828,8 +894,9 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, const u8 *addr[10]; size_t len[10]; size_t i, num_elem = 0; - u8 zero_mic[24]; + u8 zero_mic[32]; size_t mic_len, fte_fixed_len; + int res; if (kck_len == 16) { mic_len = 16; @@ -837,6 +904,10 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, } else if (kck_len == 24) { mic_len = 24; #endif /* CONFIG_SHA384 */ +#ifdef CONFIG_SHA512 + } else if (kck_len == 32) { + mic_len = 32; +#endif /* CONFIG_SHA512 */ } else { wpa_printf(MSG_WARNING, "FT: Unsupported KCK length %u", (unsigned int) kck_len); @@ -901,6 +972,17 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, for (i = 0; i < num_elem; i++) wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", addr[i], len[i]); + res = -1; +#ifdef CONFIG_SHA512 + if (kck_len == 32) { + u8 hash[SHA512_MAC_LEN]; + + if (hmac_sha512_vector(kck, kck_len, num_elem, addr, len, hash)) + return -1; + os_memcpy(mic, hash, 32); + res = 0; + } +#endif /* CONFIG_SHA384 */ #ifdef CONFIG_SHA384 if (kck_len == 24) { u8 hash[SHA384_MAC_LEN]; @@ -908,26 +990,34 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, if (hmac_sha384_vector(kck, kck_len, num_elem, addr, len, hash)) return -1; os_memcpy(mic, hash, 24); + res = 0; } #endif /* CONFIG_SHA384 */ - if (kck_len == 16 && - omac1_aes_128_vector(kck, num_elem, addr, len, mic)) - return -1; + if (kck_len == 16 && key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY) { + u8 hash[SHA256_MAC_LEN]; - return 0; + if (hmac_sha256_vector(kck, kck_len, num_elem, addr, len, hash)) + return -1; + os_memcpy(mic, hash, 16); + res = 0; + } + if (kck_len == 16 && key_mgmt != WPA_KEY_MGMT_FT_SAE_EXT_KEY && + omac1_aes_128_vector(kck, num_elem, addr, len, mic) == 0) + res = 0; + + return res; } static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, - struct wpa_ft_ies *parse, int use_sha384) + struct wpa_ft_ies *parse, const u8 *opt) { const u8 *end, *pos; parse->ftie = ie; parse->ftie_len = ie_len; - pos = ie + (use_sha384 ? sizeof(struct rsn_ftie_sha384) : - sizeof(struct rsn_ftie)); + pos = opt; end = ie + ie_len; wpa_hexdump(MSG_DEBUG, "FT: Parse FTE subelements", pos, end - pos); @@ -938,7 +1028,7 @@ static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, len = *pos++; if (len > end - pos) { wpa_printf(MSG_DEBUG, "FT: Truncated subelement"); - break; + return -1; } switch (id) { @@ -950,8 +1040,11 @@ static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, return -1; } parse->r1kh_id = pos; + wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", + parse->r1kh_id, FT_R1KH_ID_LEN); break; case FTIE_SUBELEM_GTK: + wpa_printf(MSG_DEBUG, "FT: GTK"); parse->gtk = pos; parse->gtk_len = len; break; @@ -964,8 +1057,11 @@ static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, } parse->r0kh_id = pos; parse->r0kh_id_len = len; + wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", + parse->r0kh_id, parse->r0kh_id_len); break; case FTIE_SUBELEM_IGTK: + wpa_printf(MSG_DEBUG, "FT: IGTK"); parse->igtk = pos; parse->igtk_len = len; break; @@ -973,9 +1069,12 @@ static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, case FTIE_SUBELEM_OCI: parse->oci = pos; parse->oci_len = len; + wpa_hexdump(MSG_DEBUG, "FT: OCI", + parse->oci, parse->oci_len); break; #endif /* CONFIG_OCV */ case FTIE_SUBELEM_BIGTK: + wpa_printf(MSG_DEBUG, "FT: BIGTK"); parse->bigtk = pos; parse->bigtk_len = len; break; @@ -991,20 +1090,73 @@ static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, } -int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, - struct wpa_ft_ies *parse, int use_sha384) +static int wpa_ft_parse_fte(int key_mgmt, const u8 *ie, size_t len, + struct wpa_ft_ies *parse) +{ + size_t mic_len; + u8 mic_len_info; + const u8 *pos = ie; + const u8 *end = pos + len; + + wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC Control", pos, 2); + parse->fte_rsnxe_used = pos[0] & FTE_MIC_CTRL_RSNXE_USED; + mic_len_info = (pos[0] & FTE_MIC_CTRL_MIC_LEN_MASK) >> + FTE_MIC_CTRL_MIC_LEN_SHIFT; + parse->fte_elem_count = pos[1]; + pos += 2; + + if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY) { + switch (mic_len_info) { + case FTE_MIC_LEN_16: + mic_len = 16; + break; + case FTE_MIC_LEN_24: + mic_len = 24; + break; + case FTE_MIC_LEN_32: + mic_len = 32; + break; + default: + wpa_printf(MSG_DEBUG, + "FT: Unknown MIC Length subfield value %u", + mic_len_info); + return -1; + } + } else { + mic_len = wpa_key_mgmt_sha384(key_mgmt) ? 24 : 16; + } + if (mic_len > (size_t) (end - pos)) { + wpa_printf(MSG_DEBUG, "FT: No room for %zu octet MIC in FTE", + mic_len); + return -1; + } + wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC", pos, mic_len); + parse->fte_mic = pos; + parse->fte_mic_len = mic_len; + pos += mic_len; + + if (2 * WPA_NONCE_LEN > end - pos) + return -1; + parse->fte_anonce = pos; + wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce", + parse->fte_anonce, WPA_NONCE_LEN); + pos += WPA_NONCE_LEN; + parse->fte_snonce = pos; + wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce", + parse->fte_snonce, WPA_NONCE_LEN); + pos += WPA_NONCE_LEN; + + return wpa_ft_parse_ftie(ie, len, parse, pos); +} + + +int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse, + int key_mgmt) { const u8 *end, *pos; struct wpa_ie_data data; int ret; - const struct rsn_ftie *ftie; int prot_ie_count = 0; - int update_use_sha384 = 0; - - if (use_sha384 < 0) { - use_sha384 = 0; - update_use_sha384 = 1; - } os_memset(parse, 0, sizeof(*parse)); if (ies == NULL) @@ -1038,11 +1190,8 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, parse->rsn_pmkid = data.pmkid; parse->key_mgmt = data.key_mgmt; parse->pairwise_cipher = data.pairwise_cipher; - if (update_use_sha384) { - use_sha384 = - wpa_key_mgmt_sha384(parse->key_mgmt); - update_use_sha384 = 0; - } + if (!key_mgmt) + key_mgmt = parse->key_mgmt; break; case WLAN_EID_RSNX: wpa_hexdump(MSG_DEBUG, "FT: RSNXE", pos, len); @@ -1060,47 +1209,19 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, break; case WLAN_EID_FAST_BSS_TRANSITION: wpa_hexdump(MSG_DEBUG, "FT: FTE", pos, len); - if (use_sha384) { - const struct rsn_ftie_sha384 *ftie_sha384; - - if (len < sizeof(*ftie_sha384)) - return -1; - ftie_sha384 = - (const struct rsn_ftie_sha384 *) pos; - wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC Control", - ftie_sha384->mic_control, 2); - wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC", - ftie_sha384->mic, - sizeof(ftie_sha384->mic)); - parse->fte_anonce = ftie_sha384->anonce; - wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce", - ftie_sha384->anonce, - WPA_NONCE_LEN); - parse->fte_snonce = ftie_sha384->snonce; - wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce", - ftie_sha384->snonce, - WPA_NONCE_LEN); - prot_ie_count = ftie_sha384->mic_control[1]; - if (wpa_ft_parse_ftie(pos, len, parse, 1) < 0) - return -1; - break; - } - - if (len < sizeof(*ftie)) + /* The first two octets (MIC Control field) is in the + * same offset for all cases, but the second field (MIC) + * has variable length with three different values. + * In particular the FT-SAE-EXT-KEY is inconvinient to + * parse, so try to handle this in pieces instead of + * using the struct rsn_ftie* definitions. */ + + if (len < 2) return -1; - ftie = (const struct rsn_ftie *) pos; - wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC Control", - ftie->mic_control, 2); - wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC", - ftie->mic, sizeof(ftie->mic)); - parse->fte_anonce = ftie->anonce; - wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce", - ftie->anonce, WPA_NONCE_LEN); - parse->fte_snonce = ftie->snonce; - wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce", - ftie->snonce, WPA_NONCE_LEN); - prot_ie_count = ftie->mic_control[1]; - if (wpa_ft_parse_ftie(pos, len, parse, 0) < 0) + prot_ie_count = pos[1]; /* Element Count field in + * MIC Control */ + + if (wpa_ft_parse_fte(key_mgmt, pos, len, parse) < 0) return -1; break; case WLAN_EID_TIMEOUT_INTERVAL: @@ -1183,7 +1304,7 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, * PASN frame. SHA-256 is used as the hash algorithm, except for the ciphers * 00-0F-AC:9 and 00-0F-AC:10 for which SHA-384 is used. */ -static bool pasn_use_sha384(int akmp, int cipher) +bool pasn_use_sha384(int akmp, int cipher) { return (akmp == WPA_KEY_MGMT_PASN && (cipher == WPA_CIPHER_CCMP_256 || cipher == WPA_CIPHER_GCMP_256)) || @@ -1318,6 +1439,62 @@ u8 pasn_mic_len(int akmp, int cipher) /** + * wpa_ltf_keyseed - Compute LTF keyseed from KDK + * @ptk: Buffer that holds pairwise transient key + * @akmp: Negotiated AKM + * @cipher: Negotiated pairwise cipher + * Returns: 0 on success, -1 on failure + */ +int wpa_ltf_keyseed(struct wpa_ptk *ptk, int akmp, int cipher) +{ + u8 *buf; + size_t buf_len; + u8 hash[SHA384_MAC_LEN]; + const u8 *kdk = ptk->kdk; + size_t kdk_len = ptk->kdk_len; + const char *label = "Secure LTF key seed"; + + if (!kdk || !kdk_len) { + wpa_printf(MSG_ERROR, "WPA: No KDK for LTF keyseed generation"); + return -1; + } + + buf = (u8 *)label; + buf_len = os_strlen(label); + + if (pasn_use_sha384(akmp, cipher)) { + wpa_printf(MSG_DEBUG, + "WPA: Secure LTF keyseed using HMAC-SHA384"); + + if (hmac_sha384(kdk, kdk_len, buf, buf_len, hash)) { + wpa_printf(MSG_ERROR, + "WPA: HMAC-SHA384 compute failed"); + return -1; + } + os_memcpy(ptk->ltf_keyseed, hash, SHA384_MAC_LEN); + ptk->ltf_keyseed_len = SHA384_MAC_LEN; + wpa_hexdump_key(MSG_DEBUG, "WPA: Secure LTF keyseed: ", + ptk->ltf_keyseed, ptk->ltf_keyseed_len); + + } else { + wpa_printf(MSG_DEBUG, "WPA: LTF keyseed using HMAC-SHA256"); + + if (hmac_sha256(kdk, kdk_len, buf, buf_len, hash)) { + wpa_printf(MSG_ERROR, + "WPA: HMAC-SHA256 compute failed"); + return -1; + } + os_memcpy(ptk->ltf_keyseed, hash, SHA256_MAC_LEN); + ptk->ltf_keyseed_len = SHA256_MAC_LEN; + wpa_hexdump_key(MSG_DEBUG, "WPA: Secure LTF keyseed: ", + ptk->ltf_keyseed, ptk->ltf_keyseed_len); + } + + return 0; +} + + +/** * pasn_mic - Calculate PASN MIC * @kck: The key confirmation key for the PASN PTKSA * @akmp: Negotiated AKM @@ -1479,8 +1656,12 @@ static int rsn_key_mgmt_to_bitfield(const u8 *s) #ifdef CONFIG_SAE if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_SAE) return WPA_KEY_MGMT_SAE; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_SAE_EXT_KEY) + return WPA_KEY_MGMT_SAE_EXT_KEY; if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_SAE) return WPA_KEY_MGMT_FT_SAE; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY) + return WPA_KEY_MGMT_FT_SAE_EXT_KEY; #endif /* CONFIG_SAE */ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B) return WPA_KEY_MGMT_IEEE8021X_SUITE_B; @@ -1599,6 +1780,13 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, "%s: invalid group cipher 0x%x (%08x)", __func__, data->group_cipher, WPA_GET_BE32(pos)); +#ifdef CONFIG_NO_TKIP + if (RSN_SELECTOR_GET(pos) == RSN_CIPHER_SUITE_TKIP) { + wpa_printf(MSG_DEBUG, + "%s: TKIP as group cipher not supported in CONFIG_NO_TKIP=y build", + __func__); + } +#endif /* CONFIG_NO_TKIP */ return -1; } pos += RSN_SELECTOR_LEN; @@ -1855,30 +2043,40 @@ int wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, const u8 *ssid, size_t ssid_len, const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len, const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name, - int use_sha384) + int key_mgmt) { u8 buf[1 + SSID_MAX_LEN + MOBILITY_DOMAIN_ID_LEN + 1 + FT_R0KH_ID_MAX_LEN + ETH_ALEN]; - u8 *pos, r0_key_data[64], hash[48]; + u8 *pos, r0_key_data[64 + 16], hash[64]; const u8 *addr[2]; size_t len[2]; - size_t q = use_sha384 ? 48 : 32; - size_t r0_key_data_len = q + 16; + size_t q, r0_key_data_len; + int res; + + if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY && + (xxkey_len == SHA256_MAC_LEN || xxkey_len == SHA384_MAC_LEN || + xxkey_len == SHA512_MAC_LEN)) + q = xxkey_len; + else if (wpa_key_mgmt_sha384(key_mgmt)) + q = SHA384_MAC_LEN; + else + q = SHA256_MAC_LEN; + r0_key_data_len = q + 16; /* - * R0-Key-Data = KDF-384(XXKey, "FT-R0", + * R0-Key-Data = KDF-Hash-Length(XXKey, "FT-R0", * SSIDlength || SSID || MDID || R0KHlength || * R0KH-ID || S0KH-ID) * XXKey is either the second 256 bits of MSK or PSK; or the first - * 384 bits of MSK for FT-EAP-SHA384. + * 384 bits of MSK for FT-EAP-SHA384; or PMK from SAE. * PMK-R0 = L(R0-Key-Data, 0, Q) * PMK-R0Name-Salt = L(R0-Key-Data, Q, 128) - * Q = 384 for FT-EAP-SHA384; otherwise, 256 + * Q = 384 for FT-EAP-SHA384; the length of the digest generated by H() + * for FT-SAE-EXT-KEY; or otherwise, 256 */ if (ssid_len > SSID_MAX_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN) return -1; - wpa_printf(MSG_DEBUG, "FT: Derive PMK-R0 using KDF-%s", - use_sha384 ? "SHA384" : "SHA256"); + wpa_printf(MSG_DEBUG, "FT: Derive PMK-R0 using KDF-SHA%zu", q * 8); wpa_hexdump_key(MSG_DEBUG, "FT: XXKey", xxkey, xxkey_len); wpa_hexdump_ascii(MSG_DEBUG, "FT: SSID", ssid, ssid_len); wpa_hexdump(MSG_DEBUG, "FT: MDID", mdid, MOBILITY_DOMAIN_ID_LEN); @@ -1896,30 +2094,43 @@ int wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, os_memcpy(pos, s0kh_id, ETH_ALEN); pos += ETH_ALEN; + res = -1; +#ifdef CONFIG_SHA512 + if (q == SHA512_MAC_LEN) { + if (xxkey_len != SHA512_MAC_LEN) { + wpa_printf(MSG_ERROR, + "FT: Unexpected XXKey length %d (expected %d)", + (int) xxkey_len, SHA512_MAC_LEN); + return -1; + } + res = sha512_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf, + r0_key_data, r0_key_data_len); + } +#endif /* CONFIG_SHA512 */ #ifdef CONFIG_SHA384 - if (use_sha384) { + if (q == SHA384_MAC_LEN) { if (xxkey_len != SHA384_MAC_LEN) { wpa_printf(MSG_ERROR, "FT: Unexpected XXKey length %d (expected %d)", (int) xxkey_len, SHA384_MAC_LEN); return -1; } - if (sha384_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf, - r0_key_data, r0_key_data_len) < 0) - return -1; + res = sha384_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf, + r0_key_data, r0_key_data_len); } #endif /* CONFIG_SHA384 */ - if (!use_sha384) { + if (q == SHA256_MAC_LEN) { if (xxkey_len != PMK_LEN) { wpa_printf(MSG_ERROR, "FT: Unexpected XXKey length %d (expected %d)", (int) xxkey_len, PMK_LEN); return -1; } - if (sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf, - r0_key_data, r0_key_data_len) < 0) - return -1; + res = sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf, + r0_key_data, r0_key_data_len); } + if (res < 0) + return res; os_memcpy(pmk_r0, r0_key_data, q); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, q); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0Name-Salt", &r0_key_data[q], 16); @@ -1932,12 +2143,23 @@ int wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, addr[1] = &r0_key_data[q]; len[1] = 16; + res = -1; +#ifdef CONFIG_SHA512 + if (q == SHA512_MAC_LEN) + res = sha512_vector(2, addr, len, hash); +#endif /* CONFIG_SHA512 */ #ifdef CONFIG_SHA384 - if (use_sha384 && sha384_vector(2, addr, len, hash) < 0) - return -1; + if (q == SHA384_MAC_LEN) + res = sha384_vector(2, addr, len, hash); #endif /* CONFIG_SHA384 */ - if (!use_sha384 && sha256_vector(2, addr, len, hash) < 0) - return -1; + if (q == SHA256_MAC_LEN) + res = sha256_vector(2, addr, len, hash); + if (res < 0) { + wpa_printf(MSG_DEBUG, + "FT: Failed to derive PMKR0Name (PMK-R0 len %zu)", + q); + return res; + } os_memcpy(pmk_r0_name, hash, WPA_PMK_NAME_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN); forced_memzero(r0_key_data, sizeof(r0_key_data)); @@ -1951,11 +2173,14 @@ int wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, * IEEE Std 802.11r-2008 - 8.5.1.5.4 */ int wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id, - const u8 *s1kh_id, u8 *pmk_r1_name, int use_sha384) + const u8 *s1kh_id, u8 *pmk_r1_name, + size_t pmk_r1_len) { - u8 hash[48]; + u8 hash[64]; const u8 *addr[4]; size_t len[4]; + int res; + const char *title; /* * PMKR1Name = Truncate-128(Hash("FT-R1N" || PMKR0Name || @@ -1970,14 +2195,31 @@ int wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id, addr[3] = s1kh_id; len[3] = ETH_ALEN; + res = -1; +#ifdef CONFIG_SHA512 + if (pmk_r1_len == SHA512_MAC_LEN) { + title = "FT: PMKR1Name (using SHA512)"; + res = sha512_vector(4, addr, len, hash); + } +#endif /* CONFIG_SHA512 */ #ifdef CONFIG_SHA384 - if (use_sha384 && sha384_vector(4, addr, len, hash) < 0) - return -1; + if (pmk_r1_len == SHA384_MAC_LEN) { + title = "FT: PMKR1Name (using SHA384)"; + res = sha384_vector(4, addr, len, hash); + } #endif /* CONFIG_SHA384 */ - if (!use_sha384 && sha256_vector(4, addr, len, hash) < 0) - return -1; + if (pmk_r1_len == SHA256_MAC_LEN) { + title = "FT: PMKR1Name (using SHA256)"; + res = sha256_vector(4, addr, len, hash); + } + if (res < 0) { + wpa_printf(MSG_DEBUG, + "FT: Failed to derive PMKR1Name (PMK-R1 len %zu)", + pmk_r1_len); + return res; + } os_memcpy(pmk_r1_name, hash, WPA_PMK_NAME_LEN); - wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN); + wpa_hexdump(MSG_DEBUG, title, pmk_r1_name, WPA_PMK_NAME_LEN); return 0; } @@ -1994,10 +2236,11 @@ int wpa_derive_pmk_r1(const u8 *pmk_r0, size_t pmk_r0_len, { u8 buf[FT_R1KH_ID_LEN + ETH_ALEN]; u8 *pos; + int res; - /* PMK-R1 = KDF-256(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID) */ - wpa_printf(MSG_DEBUG, "FT: Derive PMK-R1 using KDF-%s", - pmk_r0_len == SHA384_MAC_LEN ? "SHA384" : "SHA256"); + /* PMK-R1 = KDF-Hash(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID) */ + wpa_printf(MSG_DEBUG, "FT: Derive PMK-R1 using KDF-SHA%zu", + pmk_r0_len * 8); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, pmk_r0_len); wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", r1kh_id, FT_R1KH_ID_LEN); wpa_printf(MSG_DEBUG, "FT: S1KH-ID: " MACSTR, MAC2STR(s1kh_id)); @@ -2007,26 +2250,28 @@ int wpa_derive_pmk_r1(const u8 *pmk_r0, size_t pmk_r0_len, os_memcpy(pos, s1kh_id, ETH_ALEN); pos += ETH_ALEN; + res = -1; +#ifdef CONFIG_SHA512 + if (pmk_r0_len == SHA512_MAC_LEN) + res = sha512_prf(pmk_r0, pmk_r0_len, "FT-R1", + buf, pos - buf, pmk_r1, pmk_r0_len); +#endif /* CONFIG_SHA512 */ #ifdef CONFIG_SHA384 - if (pmk_r0_len == SHA384_MAC_LEN && - sha384_prf(pmk_r0, pmk_r0_len, "FT-R1", - buf, pos - buf, pmk_r1, pmk_r0_len) < 0) - return -1; + if (pmk_r0_len == SHA384_MAC_LEN) + res = sha384_prf(pmk_r0, pmk_r0_len, "FT-R1", + buf, pos - buf, pmk_r1, pmk_r0_len); #endif /* CONFIG_SHA384 */ - if (pmk_r0_len == PMK_LEN && - sha256_prf(pmk_r0, pmk_r0_len, "FT-R1", - buf, pos - buf, pmk_r1, pmk_r0_len) < 0) - return -1; - if (pmk_r0_len != SHA384_MAC_LEN && pmk_r0_len != PMK_LEN) { - wpa_printf(MSG_ERROR, "FT: Unexpected PMK-R0 length %d", - (int) pmk_r0_len); - return -1; + if (pmk_r0_len == SHA256_MAC_LEN) + res = sha256_prf(pmk_r0, pmk_r0_len, "FT-R1", + buf, pos - buf, pmk_r1, pmk_r0_len); + if (res < 0) { + wpa_printf(MSG_ERROR, "FT: Failed to derive PMK-R1"); + return res; } wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r0_len); return wpa_derive_pmk_r1_name(pmk_r0_name, r1kh_id, s1kh_id, - pmk_r1_name, - pmk_r0_len == SHA384_MAC_LEN); + pmk_r1_name, pmk_r0_len); } @@ -2049,7 +2294,8 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len, u8 tmp[2 * WPA_KCK_MAX_LEN + 2 * WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN + WPA_KDK_MAX_LEN]; size_t ptk_len, offset; - int use_sha384 = wpa_key_mgmt_sha384(akmp); + size_t key_len; + int res; if (kdk_len > WPA_KDK_MAX_LEN) { wpa_printf(MSG_ERROR, @@ -2058,12 +2304,20 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len, return -1; } + if (akmp == WPA_KEY_MGMT_FT_SAE_EXT_KEY && + (pmk_r1_len == SHA256_MAC_LEN || pmk_r1_len == SHA384_MAC_LEN || + pmk_r1_len == SHA512_MAC_LEN)) + key_len = pmk_r1_len; + else if (wpa_key_mgmt_sha384(akmp)) + key_len = SHA384_MAC_LEN; + else + key_len = SHA256_MAC_LEN; + /* * PTK = KDF-PTKLen(PMK-R1, "FT-PTK", SNonce || ANonce || * BSSID || STA-ADDR) */ - wpa_printf(MSG_DEBUG, "FT: Derive PTK using KDF-%s", - use_sha384 ? "SHA384" : "SHA256"); + wpa_printf(MSG_DEBUG, "FT: Derive PTK using KDF-SHA%zu", key_len * 8); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r1_len); wpa_hexdump(MSG_DEBUG, "FT: SNonce", snonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: ANonce", anonce, WPA_NONCE_LEN); @@ -2079,39 +2333,52 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len, os_memcpy(pos, sta_addr, ETH_ALEN); pos += ETH_ALEN; - ptk->kck_len = wpa_kck_len(akmp, PMK_LEN); + ptk->kck_len = wpa_kck_len(akmp, key_len); ptk->kck2_len = wpa_kck2_len(akmp); - ptk->kek_len = wpa_kek_len(akmp, PMK_LEN); + ptk->kek_len = wpa_kek_len(akmp, key_len); ptk->kek2_len = wpa_kek2_len(akmp); ptk->tk_len = wpa_cipher_key_len(cipher); ptk->kdk_len = kdk_len; ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len + ptk->kck2_len + ptk->kek2_len + ptk->kdk_len; + res = -1; +#ifdef CONFIG_SHA512 + if (key_len == SHA512_MAC_LEN) { + if (pmk_r1_len != SHA512_MAC_LEN) { + wpa_printf(MSG_ERROR, + "FT: Unexpected PMK-R1 length %d (expected %d)", + (int) pmk_r1_len, SHA512_MAC_LEN); + return -1; + } + res = sha512_prf(pmk_r1, pmk_r1_len, "FT-PTK", + buf, pos - buf, tmp, ptk_len); + } +#endif /* CONFIG_SHA512 */ #ifdef CONFIG_SHA384 - if (use_sha384) { + if (key_len == SHA384_MAC_LEN) { if (pmk_r1_len != SHA384_MAC_LEN) { wpa_printf(MSG_ERROR, "FT: Unexpected PMK-R1 length %d (expected %d)", (int) pmk_r1_len, SHA384_MAC_LEN); return -1; } - if (sha384_prf(pmk_r1, pmk_r1_len, "FT-PTK", - buf, pos - buf, tmp, ptk_len) < 0) - return -1; + res = sha384_prf(pmk_r1, pmk_r1_len, "FT-PTK", + buf, pos - buf, tmp, ptk_len); } #endif /* CONFIG_SHA384 */ - if (!use_sha384) { + if (key_len == SHA256_MAC_LEN) { if (pmk_r1_len != PMK_LEN) { wpa_printf(MSG_ERROR, "FT: Unexpected PMK-R1 length %d (expected %d)", (int) pmk_r1_len, PMK_LEN); return -1; } - if (sha256_prf(pmk_r1, pmk_r1_len, "FT-PTK", - buf, pos - buf, tmp, ptk_len) < 0) - return -1; + res = sha256_prf(pmk_r1, pmk_r1_len, "FT-PTK", + buf, pos - buf, tmp, ptk_len); } + if (res < 0) + return -1; wpa_hexdump_key(MSG_DEBUG, "FT: PTK", tmp, ptk_len); /* @@ -2180,7 +2447,7 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len, * @akmp: Negotiated key management protocol * * IEEE Std 802.11-2016 - 12.7.1.3 Pairwise key hierarchy - * AKM: 00-0F-AC:5, 00-0F-AC:6, 00-0F-AC:14, 00-0F-AC:16 + * AKM: 00-0F-AC:3, 00-0F-AC:5, 00-0F-AC:6, 00-0F-AC:14, 00-0F-AC:16 * PMKID = Truncate-128(HMAC-SHA-256(PMK, "PMK Name" || AA || SPA)) * AKM: 00-0F-AC:11 * See rsn_pmkid_suite_b() @@ -2372,8 +2639,12 @@ const char * wpa_key_mgmt_txt(int key_mgmt, int proto) return "WPS"; case WPA_KEY_MGMT_SAE: return "SAE"; + case WPA_KEY_MGMT_SAE_EXT_KEY: + return "SAE-EXT-KEY"; case WPA_KEY_MGMT_FT_SAE: return "FT-SAE"; + case WPA_KEY_MGMT_FT_SAE_EXT_KEY: + return "FT-SAE-EXT-KEY"; case WPA_KEY_MGMT_OSEN: return "OSEN"; case WPA_KEY_MGMT_IEEE8021X_SUITE_B: @@ -2434,8 +2705,12 @@ u32 wpa_akm_to_suite(int akm) return RSN_AUTH_KEY_MGMT_FT_FILS_SHA384; if (akm & WPA_KEY_MGMT_SAE) return RSN_AUTH_KEY_MGMT_SAE; + if (akm & WPA_KEY_MGMT_SAE_EXT_KEY) + return RSN_AUTH_KEY_MGMT_SAE_EXT_KEY; if (akm & WPA_KEY_MGMT_FT_SAE) return RSN_AUTH_KEY_MGMT_FT_SAE; + if (akm & WPA_KEY_MGMT_FT_SAE_EXT_KEY) + return RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY; if (akm & WPA_KEY_MGMT_OWE) return RSN_AUTH_KEY_MGMT_OWE; if (akm & WPA_KEY_MGMT_DPP) @@ -2595,9 +2870,9 @@ int wpa_cipher_key_len(int cipher) return 16; case WPA_CIPHER_TKIP: return 32; + default: + return 0; } - - return 0; } @@ -2610,9 +2885,9 @@ int wpa_cipher_rsc_len(int cipher) case WPA_CIPHER_GCMP: case WPA_CIPHER_TKIP: return 6; + default: + return 0; } - - return 0; } @@ -2637,8 +2912,9 @@ enum wpa_alg wpa_cipher_to_alg(int cipher) return WPA_ALG_BIP_GMAC_256; case WPA_CIPHER_BIP_CMAC_256: return WPA_ALG_BIP_CMAC_256; + default: + return WPA_ALG_NONE; } - return WPA_ALG_NONE; } @@ -3018,120 +3294,190 @@ static void wpa_parse_vendor_specific(const u8 *pos, const u8 *end, */ static int wpa_parse_generic(const u8 *pos, struct wpa_eapol_ie_parse *ie) { - if (pos[1] == 0) + u8 len = pos[1]; + size_t dlen = 2 + len; + u32 selector; + const u8 *p; + size_t left; + u8 link_id; + char title[50]; + int ret; + + if (len == 0) return 1; - if (pos[1] >= 6 && - RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE && - pos[2 + WPA_SELECTOR_LEN] == 1 && - pos[2 + WPA_SELECTOR_LEN + 1] == 0) { + if (len < RSN_SELECTOR_LEN) + return 2; + + p = pos + 2; + selector = RSN_SELECTOR_GET(p); + p += RSN_SELECTOR_LEN; + left = len - RSN_SELECTOR_LEN; + + if (left >= 2 && selector == WPA_OUI_TYPE && p[0] == 1 && p[1] == 0) { ie->wpa_ie = pos; - ie->wpa_ie_len = pos[1] + 2; + ie->wpa_ie_len = dlen; wpa_hexdump(MSG_DEBUG, "WPA: WPA IE in EAPOL-Key", ie->wpa_ie, ie->wpa_ie_len); return 0; } - if (pos[1] >= 4 && WPA_GET_BE32(pos + 2) == OSEN_IE_VENDOR_TYPE) { + if (selector == OSEN_IE_VENDOR_TYPE) { ie->osen = pos; - ie->osen_len = pos[1] + 2; + ie->osen_len = dlen; return 0; } - if (pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) { - ie->pmkid = pos + 2 + RSN_SELECTOR_LEN; - wpa_hexdump(MSG_DEBUG, "WPA: PMKID in EAPOL-Key", - pos, pos[1] + 2); + if (left >= PMKID_LEN && selector == RSN_KEY_DATA_PMKID) { + ie->pmkid = p; + wpa_hexdump(MSG_DEBUG, "WPA: PMKID in EAPOL-Key", pos, dlen); return 0; } - if (pos[1] >= RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_KEYID) { - ie->key_id = pos + 2 + RSN_SELECTOR_LEN; - wpa_hexdump(MSG_DEBUG, "WPA: KeyID in EAPOL-Key", - pos, pos[1] + 2); + if (left >= 2 && selector == RSN_KEY_DATA_KEYID) { + ie->key_id = p; + wpa_hexdump(MSG_DEBUG, "WPA: KeyID in EAPOL-Key", pos, dlen); return 0; } - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) { - ie->gtk = pos + 2 + RSN_SELECTOR_LEN; - ie->gtk_len = pos[1] - RSN_SELECTOR_LEN; - wpa_hexdump_key(MSG_DEBUG, "WPA: GTK in EAPOL-Key", - pos, pos[1] + 2); + if (left > 2 && selector == RSN_KEY_DATA_GROUPKEY) { + ie->gtk = p; + ie->gtk_len = left; + wpa_hexdump_key(MSG_DEBUG, "WPA: GTK in EAPOL-Key", pos, dlen); return 0; } - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) { - ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN; - ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN; - wpa_hexdump(MSG_DEBUG, "WPA: MAC Address in EAPOL-Key", - pos, pos[1] + 2); + if (left >= ETH_ALEN && selector == RSN_KEY_DATA_MAC_ADDR) { + ie->mac_addr = p; + wpa_printf(MSG_DEBUG, "WPA: MAC Address in EAPOL-Key: " MACSTR, + MAC2STR(ie->mac_addr)); return 0; } - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) { - ie->igtk = pos + 2 + RSN_SELECTOR_LEN; - ie->igtk_len = pos[1] - RSN_SELECTOR_LEN; + if (left > 2 && selector == RSN_KEY_DATA_IGTK) { + ie->igtk = p; + ie->igtk_len = left; wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK in EAPOL-Key", - pos, pos[1] + 2); + pos, dlen); return 0; } - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_BIGTK) { - ie->bigtk = pos + 2 + RSN_SELECTOR_LEN; - ie->bigtk_len = pos[1] - RSN_SELECTOR_LEN; + if (left > 2 && selector == RSN_KEY_DATA_BIGTK) { + ie->bigtk = p; + ie->bigtk_len = left; wpa_hexdump_key(MSG_DEBUG, "WPA: BIGTK in EAPOL-Key", - pos, pos[1] + 2); + pos, dlen); return 0; } - if (pos[1] >= RSN_SELECTOR_LEN + 1 && - RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) { - ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN; + if (left >= 1 && selector == WFA_KEY_DATA_IP_ADDR_REQ) { + ie->ip_addr_req = p; wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key", - ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN); + ie->ip_addr_req, left); return 0; } - if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 && - RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) { - ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN; + if (left >= 3 * 4 && selector == WFA_KEY_DATA_IP_ADDR_ALLOC) { + ie->ip_addr_alloc = p; wpa_hexdump(MSG_DEBUG, "WPA: IP Address Allocation in EAPOL-Key", - ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN); + ie->ip_addr_alloc, left); return 0; } - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_OCI) { - ie->oci = pos + 2 + RSN_SELECTOR_LEN; - ie->oci_len = pos[1] - RSN_SELECTOR_LEN; + if (left > 2 && selector == RSN_KEY_DATA_OCI) { + ie->oci = p; + ie->oci_len = left; wpa_hexdump(MSG_DEBUG, "WPA: OCI KDE in EAPOL-Key", - pos, pos[1] + 2); + pos, dlen); return 0; } - if (pos[1] >= RSN_SELECTOR_LEN + 1 && - RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_TRANSITION_DISABLE) { - ie->transition_disable = pos + 2 + RSN_SELECTOR_LEN; - ie->transition_disable_len = pos[1] - RSN_SELECTOR_LEN; + if (left >= 1 && selector == WFA_KEY_DATA_TRANSITION_DISABLE) { + ie->transition_disable = p; + ie->transition_disable_len = left; wpa_hexdump(MSG_DEBUG, "WPA: Transition Disable KDE in EAPOL-Key", - pos, pos[1] + 2); + pos, dlen); + return 0; + } + + if (left >= 2 && selector == WFA_KEY_DATA_DPP) { + ie->dpp_kde = p; + ie->dpp_kde_len = left; + wpa_hexdump(MSG_DEBUG, "WPA: DPP KDE in EAPOL-Key", pos, dlen); + return 0; + } + + if (left >= RSN_MLO_GTK_KDE_PREFIX_LENGTH && + selector == RSN_KEY_DATA_MLO_GTK) { + link_id = (p[0] & RSN_MLO_GTK_KDE_PREFIX0_LINK_ID_MASK) >> + RSN_MLO_GTK_KDE_PREFIX0_LINK_ID_SHIFT; + if (link_id >= MAX_NUM_MLO_LINKS) + return 2; + + ie->valid_mlo_gtks |= BIT(link_id); + ie->mlo_gtk[link_id] = p; + ie->mlo_gtk_len[link_id] = left; + ret = os_snprintf(title, sizeof(title), + "RSN: Link ID %u - MLO GTK KDE in EAPOL-Key", + link_id); + if (!os_snprintf_error(sizeof(title), ret)) + wpa_hexdump_key(MSG_DEBUG, title, pos, dlen); + return 0; + } + + if (left >= RSN_MLO_IGTK_KDE_PREFIX_LENGTH && + selector == RSN_KEY_DATA_MLO_IGTK) { + link_id = (p[8] & RSN_MLO_IGTK_KDE_PREFIX8_LINK_ID_MASK) >> + RSN_MLO_IGTK_KDE_PREFIX8_LINK_ID_SHIFT; + if (link_id >= MAX_NUM_MLO_LINKS) + return 2; + + ie->valid_mlo_igtks |= BIT(link_id); + ie->mlo_igtk[link_id] = p; + ie->mlo_igtk_len[link_id] = left; + ret = os_snprintf(title, sizeof(title), + "RSN: Link ID %u - MLO IGTK KDE in EAPOL-Key", + link_id); + if (!os_snprintf_error(sizeof(title), ret)) + wpa_hexdump_key(MSG_DEBUG, title, pos, dlen); return 0; } - if (pos[1] >= RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_DPP) { - ie->dpp_kde = pos + 2 + RSN_SELECTOR_LEN; - ie->dpp_kde_len = pos[1] - RSN_SELECTOR_LEN; - wpa_hexdump(MSG_DEBUG, "WPA: DPP KDE in EAPOL-Key", - pos, pos[1] + 2); + if (left >= RSN_MLO_BIGTK_KDE_PREFIX_LENGTH && + selector == RSN_KEY_DATA_MLO_BIGTK) { + link_id = (p[8] & RSN_MLO_BIGTK_KDE_PREFIX8_LINK_ID_MASK) >> + RSN_MLO_BIGTK_KDE_PREFIX8_LINK_ID_SHIFT; + if (link_id >= MAX_NUM_MLO_LINKS) + return 2; + + ie->valid_mlo_bigtks |= BIT(link_id); + ie->mlo_bigtk[link_id] = p; + ie->mlo_bigtk_len[link_id] = left; + ret = os_snprintf(title, sizeof(title), + "RSN: Link ID %u - MLO BIGTK KDE in EAPOL-Key", + link_id); + if (!os_snprintf_error(sizeof(title), ret)) + wpa_hexdump_key(MSG_DEBUG, title, pos, dlen); + return 0; + } + + if (left >= RSN_MLO_LINK_KDE_FIXED_LENGTH && + selector == RSN_KEY_DATA_MLO_LINK) { + link_id = (p[0] & RSN_MLO_LINK_KDE_LI_LINK_ID_MASK) >> + RSN_MLO_LINK_KDE_LI_LINK_ID_SHIFT; + if (link_id >= MAX_NUM_MLO_LINKS) + return 2; + + ie->valid_mlo_links |= BIT(link_id); + ie->mlo_link[link_id] = p; + ie->mlo_link_len[link_id] = left; + ret = os_snprintf(title, sizeof(title), + "RSN: Link ID %u - MLO Link KDE in EAPOL-Key", + link_id); + if (!os_snprintf_error(sizeof(title), ret)) + wpa_hexdump(MSG_DEBUG, title, pos, dlen); return 0; } @@ -3150,15 +3496,17 @@ int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie) { const u8 *pos, *end; int ret = 0; + size_t dlen = 0; os_memset(ie, 0, sizeof(*ie)); - for (pos = buf, end = pos + len; end - pos > 1; pos += 2 + pos[1]) { + for (pos = buf, end = pos + len; end - pos > 1; pos += dlen) { if (pos[0] == 0xdd && ((pos == buf + len - 1) || pos[1] == 0)) { /* Ignore padding */ break; } - if (2 + pos[1] > end - pos) { + dlen = 2 + pos[1]; + if ((int) dlen > end - pos) { wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data underflow (ie=%d len=%d pos=%d)", pos[0], pos[1], (int) (pos - buf)); @@ -3168,22 +3516,22 @@ int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie) } if (*pos == WLAN_EID_RSN) { ie->rsn_ie = pos; - ie->rsn_ie_len = pos[1] + 2; + ie->rsn_ie_len = dlen; wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key", ie->rsn_ie, ie->rsn_ie_len); } else if (*pos == WLAN_EID_RSNX) { ie->rsnxe = pos; - ie->rsnxe_len = pos[1] + 2; + ie->rsnxe_len = dlen; wpa_hexdump(MSG_DEBUG, "WPA: RSNXE in EAPOL-Key", ie->rsnxe, ie->rsnxe_len); } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) { ie->mdie = pos; - ie->mdie_len = pos[1] + 2; + ie->mdie_len = dlen; wpa_hexdump(MSG_DEBUG, "WPA: MDIE in EAPOL-Key", ie->mdie, ie->mdie_len); } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) { ie->ftie = pos; - ie->ftie_len = pos[1] + 2; + ie->ftie_len = dlen; wpa_hexdump(MSG_DEBUG, "WPA: FTIE in EAPOL-Key", ie->ftie, ie->ftie_len); } else if (*pos == WLAN_EID_TIMEOUT_INTERVAL && pos[1] >= 5) { @@ -3191,31 +3539,31 @@ int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie) ie->reassoc_deadline = pos; wpa_hexdump(MSG_DEBUG, "WPA: Reassoc Deadline " "in EAPOL-Key", - ie->reassoc_deadline, pos[1] + 2); + ie->reassoc_deadline, dlen); } else if (pos[2] == WLAN_TIMEOUT_KEY_LIFETIME) { ie->key_lifetime = pos; wpa_hexdump(MSG_DEBUG, "WPA: KeyLifetime " "in EAPOL-Key", - ie->key_lifetime, pos[1] + 2); + ie->key_lifetime, dlen); } else { wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized " "EAPOL-Key Key Data IE", - pos, 2 + pos[1]); + pos, dlen); } } else if (*pos == WLAN_EID_LINK_ID) { if (pos[1] >= 18) { ie->lnkid = pos; - ie->lnkid_len = pos[1] + 2; + ie->lnkid_len = dlen; } } else if (*pos == WLAN_EID_EXT_CAPAB) { ie->ext_capab = pos; - ie->ext_capab_len = pos[1] + 2; + ie->ext_capab_len = dlen; } else if (*pos == WLAN_EID_SUPP_RATES) { ie->supp_rates = pos; - ie->supp_rates_len = pos[1] + 2; + ie->supp_rates_len = dlen; } else if (*pos == WLAN_EID_EXT_SUPP_RATES) { ie->ext_supp_rates = pos; - ie->ext_supp_rates_len = pos[1] + 2; + ie->ext_supp_rates_len = dlen; } else if (*pos == WLAN_EID_HT_CAP && pos[1] >= sizeof(struct ieee80211_ht_capabilities)) { ie->ht_capabilities = pos + 2; @@ -3269,7 +3617,7 @@ int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie) } else { wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key Key Data IE", - pos, 2 + pos[1]); + pos, dlen); } } @@ -3368,6 +3716,9 @@ int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, int akmp, int cipher) case WPA_KEY_MGMT_SAE: RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE); break; + case WPA_KEY_MGMT_SAE_EXT_KEY: + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE_EXT_KEY); + break; #endif /* CONFIG_SAE */ #ifdef CONFIG_FILS case WPA_KEY_MGMT_FILS_SHA256: @@ -3581,6 +3932,7 @@ int wpa_pasn_validate_rsne(const struct wpa_ie_data *data) switch (data->key_mgmt) { #ifdef CONFIG_SAE case WPA_KEY_MGMT_SAE: + case WPA_KEY_MGMT_SAE_EXT_KEY: /* fall through */ #endif /* CONFIG_SAE */ #ifdef CONFIG_FILS @@ -3734,4 +4086,27 @@ void wpa_pasn_add_rsnxe(struct wpabuf *buf, u16 capab) wpabuf_put_u8(buf, capab); } + +/* + * wpa_pasn_add_extra_ies - Add protocol specific IEs in Authentication + * frame for PASN. + * + * @buf: Buffer in which the elements will be added + * @extra_ies: Protocol specific elements to add + * @len: Length of the elements + * Returns: 0 on success, -1 on failure + */ + +int wpa_pasn_add_extra_ies(struct wpabuf *buf, const u8 *extra_ies, size_t len) +{ + if (!len || !extra_ies || !buf) + return 0; + + if (wpabuf_tailroom(buf) < sizeof(len)) + return -1; + + wpabuf_put_data(buf, extra_ies, len); + return 0; +} + #endif /* CONFIG_PASN */ |