/* * bcmwpa.c - shared WPA-related functions * * Broadcom Proprietary and Confidential. Copyright (C) 2020, * All Rights Reserved. * * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom; * the contents of this file may not be disclosed to third parties, * copied or duplicated in any form, in whole or in part, without * the prior written permission of Broadcom. * * * <> */ /* include wl driver config file if this file is compiled for driver */ #ifdef BCMDRIVER #include /* HACK: this case for external supplicant use */ #else #include #if defined(BCMEXTSUP) #include #else #ifndef ASSERT #define ASSERT(exp) #endif #endif /* BCMEXTSUP */ #endif /* BCMDRIVER */ #include #include #include <802.11.h> #include #include <802.11r.h> #include #include #include #include #include #include #include #include #ifdef WL_OCV #include #endif /* WL_OCV */ #if defined(BCMSUP_PSK) || defined(WLFBT) || defined(BCMAUTH_PSK) || \ defined(WL_OKC) || defined(WLTDLS) || defined(GTKOE) || defined(WLHOSTFBT) #ifdef WLHOSTFBT #include #endif #endif /* defined(BCMSUP_PSK) || defined(WLFBT) || defined(BCMAUTH_PSK) || * defined(WL_OKC) || defined(WLTDLS) || defined(GTKOE) || defined(WLHOSTFBT) */ /* prefix strings */ #define PMK_NAME_PFX "PMK Name" #define FT_PTK_PFX "FT-PTK" #define FT_R0_PFX "FT-R0" #define FT_R0N_PFX "FT-R0N" #define FT_R1_PFX "FT-R1" #define FT_R1N_PFX "FT-R1N" #define WPA_PTK_PFX "Pairwise key expansion" #define TDLS_PMK_PFX "TDLS PMK" /* end prefix strings */ #ifndef BIT #define BIT(x) (1 << (x)) #endif #define PRF_PREFIXES_NUM 5u typedef struct key_length_entry { uint8 suite; uint8 len; } key_length_entry_t; /* EAPOL key(PMK/KCK/KEK/TK) length lookup tables */ static const key_length_entry_t eapol_pmk_len[] = { {RSN_AKM_SUITEB_SHA384_1X, EAPOL_WPA_PMK_SHA384_LEN}, {RSN_AKM_FBT_SHA384_1X, EAPOL_WPA_PMK_SHA384_LEN}, {RSN_AKM_FBT_SHA384_PSK, EAPOL_WPA_PMK_SHA384_LEN}, {0u, EAPOL_WPA_PMK_DEFAULT_LEN} /* default */ }; static const key_length_entry_t eapol_kck_mic_len[] = { {RSN_AKM_SUITEB_SHA384_1X, EAPOL_WPA_KCK_MIC_SHA384_LEN}, {RSN_AKM_FILS_SHA256, 0u}, {RSN_AKM_FILS_SHA384, 0u}, {RSN_AKM_FBT_SHA256_FILS, EAPOL_WPA_KCK_MIC_DEFAULT_LEN}, {RSN_AKM_FBT_SHA384_FILS, EAPOL_WPA_KCK2_SHA384_LEN}, {RSN_AKM_OWE, EAPOL_WPA_KCK_MIC_DEFAULT_LEN}, {RSN_AKM_FBT_SHA384_1X, EAPOL_WPA_KCK_MIC_SHA384_LEN}, {RSN_AKM_FBT_SHA384_PSK, EAPOL_WPA_KCK_MIC_SHA384_LEN}, {0u, EAPOL_WPA_KCK_MIC_DEFAULT_LEN} /* default */ }; static const key_length_entry_t eapol_kck_len[] = { {RSN_AKM_SUITEB_SHA384_1X, EAPOL_WPA_KCK_SHA384_LEN}, {RSN_AKM_FILS_SHA256, 0u}, {RSN_AKM_FILS_SHA384, 0u}, {RSN_AKM_FBT_SHA256_FILS, 0u}, {RSN_AKM_FBT_SHA384_FILS, 0u}, {RSN_AKM_OWE, EAPOL_WPA_KCK_DEFAULT_LEN}, {RSN_AKM_FBT_SHA384_1X, EAPOL_WPA_KCK_SHA384_LEN}, {RSN_AKM_FBT_SHA384_PSK, EAPOL_WPA_KCK_SHA384_LEN}, {0u, EAPOL_WPA_KCK_DEFAULT_LEN} /* default */ }; static const key_length_entry_t eapol_kek_len[] = { {RSN_AKM_FILS_SHA384, EAPOL_WPA_ENCR_KEY_MAX_LEN}, {RSN_AKM_FBT_SHA384_FILS, EAPOL_WPA_ENCR_KEY_MAX_LEN}, {RSN_AKM_SUITEB_SHA384_1X, EAPOL_WPA_ENCR_KEY_MAX_LEN / 2}, {RSN_AKM_FILS_SHA256, EAPOL_WPA_ENCR_KEY_MAX_LEN / 2}, {RSN_AKM_FBT_SHA256_FILS, EAPOL_WPA_ENCR_KEY_MAX_LEN / 2}, {RSN_AKM_OWE, EAPOL_WPA_ENCR_KEY_DEFAULT_LEN}, {RSN_AKM_FBT_SHA384_1X, EAPOL_WPA_ENCR_KEY_MAX_LEN / 2}, {RSN_AKM_FBT_SHA384_PSK, EAPOL_WPA_ENCR_KEY_MAX_LEN / 2}, {0u, EAPOL_WPA_ENCR_KEY_DEFAULT_LEN} /* default */ }; static const key_length_entry_t eapol_tk_len[] = { {WPA_CIPHER_CCMP_256, EAPOL_WPA_TEMP_ENCR_KEY_MAX_LEN}, {WPA_CIPHER_AES_GCM256, EAPOL_WPA_TEMP_ENCR_KEY_MAX_LEN}, {WPA_CIPHER_BIP_GMAC_256, EAPOL_WPA_TEMP_ENCR_KEY_MAX_LEN}, {WPA_CIPHER_BIP_CMAC_256, EAPOL_WPA_TEMP_ENCR_KEY_MAX_LEN}, {WPA_CIPHER_AES_CCM, EAPOL_WPA_TEMP_ENCR_KEY_MAX_LEN / 2}, {WPA_CIPHER_AES_GCM, EAPOL_WPA_TEMP_ENCR_KEY_MAX_LEN / 2}, {WPA_CIPHER_BIP_GMAC_128, EAPOL_WPA_TEMP_ENCR_KEY_MAX_LEN / 2}, {WPA_CIPHER_TKIP, EAPOL_WPA_TEMP_ENCR_KEY_MAX_LEN}, {0u, 0u} /* default */ }; #if defined(WL_FILS) && defined(WLFBT) static const key_length_entry_t eapol_kck2_len[] = { {RSN_AKM_FBT_SHA256_FILS, EAPOL_WPA_KCK2_SHA256_LEN}, {RSN_AKM_FBT_SHA384_FILS, EAPOL_WPA_KCK2_SHA384_LEN}, {0u, 0u} /* default */ }; static const key_length_entry_t eapol_kek2_len[] = { {RSN_AKM_FBT_SHA256_FILS, EAPOL_WPA_KEK2_SHA256_LEN}, {RSN_AKM_FBT_SHA384_FILS, EAPOL_WPA_KEK2_SHA384_LEN}, {0u, 0u} /* default */ }; #endif /* WL_FILS && WLFBT */ typedef struct key_length_lookup { const eapol_key_type_t key; const key_length_entry_t *key_entry; } key_length_lookup_t; static const key_length_lookup_t eapol_key_lookup_tbl[] = { {EAPOL_KEY_PMK, eapol_pmk_len}, {EAPOL_KEY_KCK_MIC, eapol_kck_mic_len}, {EAPOL_KEY_KCK, eapol_kck_len}, {EAPOL_KEY_KEK, eapol_kek_len}, {EAPOL_KEY_TK, eapol_tk_len}, #if defined(WL_FILS) && defined(WLFBT) {EAPOL_KEY_KCK2, eapol_kck2_len}, {EAPOL_KEY_KEK2, eapol_kek2_len}, #endif /* WL_FILS && WLFBT */ }; typedef struct rsn_akm_lookup_entry { const rsn_akm_t rsn_akm; const sha2_hash_type_t hash_type; } rsn_akm_lookup_entry_t; static const rsn_akm_lookup_entry_t rsn_akm_lookup_tbl[] = { {RSN_AKM_NONE, HASH_SHA1}, {RSN_AKM_UNSPECIFIED, HASH_SHA1}, {RSN_AKM_PSK, HASH_SHA1}, {RSN_AKM_FBT_1X, HASH_SHA256}, {RSN_AKM_FBT_PSK, HASH_SHA256}, {RSN_AKM_MFP_1X, HASH_SHA256}, {RSN_AKM_MFP_PSK, HASH_SHA256}, {RSN_AKM_SHA256_1X, HASH_SHA256}, {RSN_AKM_SHA256_PSK, HASH_SHA256}, {RSN_AKM_TPK, HASH_SHA256}, {RSN_AKM_SAE_PSK, HASH_SHA256}, {RSN_AKM_SAE_FBT, HASH_SHA256}, {RSN_AKM_SUITEB_SHA256_1X, HASH_SHA256}, {RSN_AKM_SUITEB_SHA384_1X, HASH_SHA384}, {RSN_AKM_FBT_SHA384_1X, HASH_SHA384}, {RSN_AKM_FILS_SHA256, HASH_SHA256}, {RSN_AKM_FILS_SHA384, HASH_SHA384}, {RSN_AKM_FBT_SHA256_FILS, HASH_SHA256}, {RSN_AKM_FBT_SHA384_FILS, HASH_SHA384}, {RSN_AKM_OWE, HASH_SHA256}, {RSN_AKM_FBT_SHA384_PSK, HASH_SHA384}, {RSN_AKM_PSK_SHA384, HASH_SHA384}, }; typedef struct rsn_akm_cipher_match_entry { uint16 akm_type; uint32 u_cast; /* BITMAP */ uint32 m_cast; /* BITMAP */ uint32 g_mgmt; /* BITMAP */ } rsn_akm_cipher_match_entry_t; /* list only explicit cipher restriction for given AKM (e.g SuiteB) * refer to 802.11 spec 9.4.2.24.3 * If not listed here, it means no restriction in using any ciphers. */ static const rsn_akm_cipher_match_entry_t rsn_akm_cipher_match_table[] = { {RSN_AKM_SUITEB_SHA256_1X, BCM_BIT(WPA_CIPHER_AES_GCM), BCM_BIT(WPA_CIPHER_AES_GCM), BCM_BIT(WPA_CIPHER_BIP_GMAC_128)}, {RSN_AKM_SUITEB_SHA384_1X, BCM_BIT(WPA_CIPHER_AES_GCM256) | BCM_BIT(WPA_CIPHER_CCMP_256), BCM_BIT(WPA_CIPHER_AES_GCM256) | BCM_BIT(WPA_CIPHER_AES_GCM256), BCM_BIT(WPA_CIPHER_BIP_GMAC_256) | BCM_BIT(WPA_CIPHER_BIP_CMAC_256)}, {RSN_AKM_FBT_SHA384_1X, BCM_BIT(WPA_CIPHER_AES_GCM256) | BCM_BIT(WPA_CIPHER_CCMP_256), BCM_BIT(WPA_CIPHER_AES_GCM256) | BCM_BIT(WPA_CIPHER_AES_GCM256), BCM_BIT(WPA_CIPHER_BIP_GMAC_256) | BCM_BIT(WPA_CIPHER_BIP_CMAC_256)} }; #if defined(WL_BAND6G) static const rsn_akm_mask_t rsn_akm_6g_inval_mask = BCM_BIT(RSN_AKM_PSK) | BCM_BIT(RSN_AKM_FBT_PSK) | BCM_BIT(RSN_AKM_SHA256_PSK) | BCM_BIT(RSN_AKM_FBT_SHA384_PSK) | BCM_BIT(RSN_AKM_PSK_SHA384); static const rsn_ciphers_t cipher_6g_inval_mask = BCM_BIT(WPA_CIPHER_NONE) | BCM_BIT(WPA_CIPHER_WEP_40) | BCM_BIT(WPA_CIPHER_TKIP) | BCM_BIT(WPA_CIPHER_WEP_104); #endif /* WL_BAND6G */ #if defined(BCMSUP_PSK) || defined(BCMSUPPL) typedef struct group_cipher_algo_entry { rsn_cipher_t g_mgmt_cipher; uint8 bip_algo; } group_cipher_algo_entry_t; static const group_cipher_algo_entry_t group_mgmt_cipher_algo[] = { {WPA_CIPHER_BIP_GMAC_256, CRYPTO_ALGO_BIP_GMAC256}, {WPA_CIPHER_BIP_CMAC_256, CRYPTO_ALGO_BIP_CMAC256}, {WPA_CIPHER_BIP_GMAC_128, CRYPTO_ALGO_BIP_GMAC}, {WPA_CIPHER_BIP, CRYPTO_ALGO_BIP}, }; #endif /* defined(BCMSUP_PSK) || defined(BCMSUPPL) */ static uint16 wlc_calc_rsn_desc_version(const rsn_ie_info_t *rsn_info); static int bcmwpa_is_valid_akm(const rsn_akm_t akm); #if defined(BCMSUP_PSK) || defined(BCMAUTH_PSK) || defined(WLFBT) || defined(GTKOE) static sha2_hash_type_t bcmwpa_rsn_akm_to_hash(const rsn_akm_t akm); #ifdef RSN_IE_INFO_STRUCT_RELOCATED static int bcmwpa_decode_cipher_suite(rsn_ie_info_t *info, const uint8 **ptr, uint ie_len, uint *remain_len, uint16 *p_count); #endif #endif /* defined(BCMSUP_PSK) || defined(BCMAUTH_PSK) || defined(WLFBT) || defined(GTKOE) */ #if defined(BCMSUP_PSK) || defined(WLFBT) || defined(WL_OKC) || defined(WLHOSTFBT) #include /* calculate wpa PMKID: HMAC-SHA1-128(PMK, "PMK Name" | AA | SPA) */ static void wpa_calc_pmkid_impl(sha2_hash_type_t hash_type, const struct ether_addr *auth_ea, const struct ether_addr *sta_ea, const uint8 *pmk, uint pmk_len, uint8 *pmkid) { int err; hmac_sha2_ctx_t ctx; err = hmac_sha2_init(&ctx, hash_type, pmk, pmk_len); if (err != BCME_OK) goto done; hmac_sha2_update(&ctx, (const uint8 *)PMK_NAME_PFX, sizeof(PMK_NAME_PFX) - 1); hmac_sha2_update(&ctx, (const uint8 *)auth_ea, ETHER_ADDR_LEN); hmac_sha2_update(&ctx, (const uint8 *)sta_ea, ETHER_ADDR_LEN); hmac_sha2_final(&ctx, pmkid, WPA2_PMKID_LEN); done:; } void wpa_calc_pmkid(const struct ether_addr *auth_ea, const struct ether_addr *sta_ea, const uint8 *pmk, uint pmk_len, uint8 *pmkid) { wpa_calc_pmkid_impl(HASH_SHA1, auth_ea, sta_ea, pmk, pmk_len, pmkid); } void kdf_calc_pmkid(const struct ether_addr *auth_ea, const struct ether_addr *sta_ea, const uint8 *key, uint key_len, uint8 *pmkid, rsn_ie_info_t *rsn_info) { sha2_hash_type_t hash_type; if (rsn_info->sta_akm == RSN_AKM_SUITEB_SHA384_1X) { hash_type = HASH_SHA384; } else { hash_type = HASH_SHA256; } wpa_calc_pmkid_impl(hash_type, auth_ea, sta_ea, key, key_len, pmkid); } #if defined(WLFBT) || defined(WLHOSTFBT) void wpa_calc_pmkR0(sha2_hash_type_t hash_type, const uint8 *ssid, uint ssid_len, uint16 mdid, const uint8 *r0kh, uint r0kh_len, const struct ether_addr *sta_ea, const uint8 *pmk, uint pmk_len, uint8 *pmkr0, uint8 *pmkr0name) { uint8 out[FBT_R0KH_ID_LEN + WPA2_PMKID_LEN - 1]; int out_len = FBT_R0KH_ID_LEN - 1; bcm_const_xlvp_t pfx[7]; bcm_const_xlvp_t pfx2[2]; int npfx = 0; int npfx2 = 0; uint8 mdid_le[2]; uint8 pfx_ssid_len; uint8 pfx_r0kh_len; if (hash_type == HASH_SHA384) { out_len += WPA2_PMKID_LEN; } /* create prefixes for pmkr0 */ pfx[npfx].len = sizeof(FT_R0_PFX) - 1; pfx[npfx++].data = (uint8 *)FT_R0_PFX; /* ssid length and ssid */ pfx_ssid_len = ssid_len & 0xff; pfx[npfx].len = (uint16)sizeof(pfx_ssid_len); pfx[npfx++].data = &pfx_ssid_len; pfx[npfx].len = (uint16)(ssid_len & 0xffff); pfx[npfx++].data = ssid; /* mdid */ htol16_ua_store(mdid, mdid_le); pfx[npfx].len = sizeof(mdid_le); pfx[npfx++].data = mdid_le; /* r0kh len and r0kh */ pfx_r0kh_len = r0kh_len & 0xff; pfx[npfx].len = sizeof(pfx_r0kh_len); pfx[npfx++].data = &pfx_r0kh_len; pfx[npfx].len = (uint16)(r0kh_len & 0xffff); pfx[npfx++].data = r0kh; /* sta addr */ pfx[npfx].len = ETHER_ADDR_LEN; pfx[npfx++].data = (const uint8 *)sta_ea; hmac_sha2_n(hash_type, pmk, pmk_len, pfx, npfx, NULL, 0, out, out_len); (void)memcpy_s(pmkr0, pmk_len, out, pmk_len); /* coverity checks overflow if pfx size changes */ /* create prefixes for pmkr0 name */ pfx2[npfx2].len = sizeof(FT_R0N_PFX) - 1; pfx2[npfx2++].data = (uint8 *)FT_R0N_PFX; pfx2[npfx2].len = WPA2_PMKID_LEN; pfx2[npfx2++].data = &out[pmk_len]; (void)sha2(hash_type, pfx2, npfx2, NULL, 0, pmkr0name, WPA2_PMKID_LEN); } void wpa_calc_pmkR1(sha2_hash_type_t hash_type, const struct ether_addr *r1kh, const struct ether_addr *sta_ea, const uint8 *pmk, uint pmk_len, const uint8 *pmkr0name, uint8 *pmkr1, uint8 *pmkr1name) { bcm_const_xlvp_t pfx[3]; bcm_const_xlvp_t pfx2[4]; int npfx = 0; int npfx2 = 0; if (!pmkr1 && !pmkr1name) goto done; else if (!pmkr1) goto calc_r1name; /* create prefixes for pmkr1 */ pfx[npfx].len = sizeof(FT_R1_PFX) - 1; pfx[npfx++].data = (uint8 *)FT_R1_PFX; pfx[npfx].len = ETHER_ADDR_LEN; pfx[npfx++].data = (const uint8 *)r1kh; pfx[npfx].len = ETHER_ADDR_LEN; pfx[npfx++].data = (const uint8 *)sta_ea; hmac_sha2_n(hash_type, pmk, pmk_len, pfx, npfx, NULL, 0, pmkr1, sha2_digest_len(hash_type)); calc_r1name: /* create prefixes for pmkr1 name */ pfx2[npfx2].len = sizeof(FT_R1N_PFX) - 1; pfx2[npfx2++].data = (uint8 *)FT_R1N_PFX; pfx2[npfx2].len = WPA2_PMKID_LEN; pfx2[npfx2++].data = pmkr0name; pfx2[npfx2].len = ETHER_ADDR_LEN; pfx2[npfx2++].data = (const uint8 *)r1kh; pfx2[npfx2].len = ETHER_ADDR_LEN; pfx2[npfx2++].data = (const uint8 *)sta_ea; sha2(hash_type, pfx2, npfx2, NULL, 0, pmkr1name, WPA2_PMKID_LEN); done:; } void wpa_calc_ft_ptk(sha2_hash_type_t hash_type, const struct ether_addr *bssid, const struct ether_addr *sta_ea, const uint8 *anonce, const uint8* snonce, const uint8 *pmk, uint pmk_len, uint8 *ptk, uint ptk_len) { bcm_const_xlvp_t pfx[5]; int npfx = 0; /* FT-PTK||SNONCE||ANONCE||BSSID||STA Addr */ pfx[npfx].len = sizeof(FT_PTK_PFX) - 1; pfx[npfx++].data = (uint8 *)FT_PTK_PFX; pfx[npfx].len = EAPOL_WPA_KEY_NONCE_LEN; pfx[npfx++].data = snonce; pfx[npfx].len = EAPOL_WPA_KEY_NONCE_LEN; pfx[npfx++].data = anonce; pfx[npfx].len = ETHER_ADDR_LEN; pfx[npfx++].data = (const uint8 *)bssid; pfx[npfx].len = ETHER_ADDR_LEN; pfx[npfx++].data = (const uint8 *)sta_ea; hmac_sha2_n(hash_type, pmk, pmk_len, pfx, npfx, NULL, 0, ptk, ptk_len); } void wpa_derive_pmkR1_name(sha2_hash_type_t hash_type, struct ether_addr *r1kh, struct ether_addr *sta_ea, uint8 *pmkr0name, uint8 *pmkr1name) { wpa_calc_pmkR1(hash_type, r1kh, sta_ea, NULL /* pmk */, 0, pmkr0name, NULL /* pmkr1 */, pmkr1name); } #endif /* WLFBT || WLHOSTFBT */ #endif /* BCMSUP_PSK || WLFBT || WL_OKC */ #if defined(BCMSUP_PSK) || defined(GTKOE) || defined(BCMAUTH_PSK) || defined(WLFBT) /* Decrypt a key data from a WPA key message */ int wpa_decr_key_data(eapol_wpa_key_header_t *body, uint16 key_info, uint8 *ekey, uint8 *encrkey, rc4_ks_t *rc4key, const rsn_ie_info_t *rsn_info, uint16 *dec_len) { uint16 len; int err = BCME_OK; uint8 *key_data; switch (key_info & (WPA_KEY_DESC_V1 | WPA_KEY_DESC_V2)) { case WPA_KEY_DESC_V1: err = memcpy_s(encrkey, EAPOL_WPA_KEY_IV_LEN + EAPOL_WPA_ENCR_KEY_MAX_LEN, body->iv, EAPOL_WPA_KEY_IV_LEN); if (err) { ASSERT(0); return err; } err = memcpy_s(&encrkey[EAPOL_WPA_KEY_IV_LEN], EAPOL_WPA_ENCR_KEY_MAX_LEN, ekey, rsn_info->kek_len); if (err) { ASSERT(0); return err; } /* decrypt the key data */ prepare_key(encrkey, EAPOL_WPA_KEY_IV_LEN + rsn_info->kek_len, rc4key); rc4(NULL, WPA_KEY_DATA_LEN_256, rc4key); /* dump 256 bytes */ len = ntoh16_ua(EAPOL_WPA_KEY_HDR_DATA_LEN_PTR(body, rsn_info->kck_mic_len)); key_data = EAPOL_WPA_KEY_HDR_DATA_PTR(body, rsn_info->kck_mic_len); rc4(key_data, len, rc4key); break; case WPA_KEY_DESC_V2: case WPA_KEY_DESC_V3: case WPA_KEY_DESC_V0: /* fallthrough */ len = ntoh16_ua(EAPOL_WPA_KEY_HDR_DATA_LEN_PTR(body, rsn_info->kck_mic_len)); if (!len) { *dec_len = 0; break; /* ignore zero length */ } key_data = EAPOL_WPA_KEY_HDR_DATA_PTR(body, rsn_info->kck_mic_len); if (aes_unwrap(rsn_info->kek_len, ekey, len, key_data, key_data)) { *dec_len = 0; err = BCME_DECERR; break; } *dec_len = (len > AKW_BLOCK_LEN) ? (len - AKW_BLOCK_LEN) : 0; break; default: *dec_len = 0; err = BCME_UNSUPPORTED; /* may need revisiting - see 802.11-2016 */ break; } return err; } /* internal function - assumes enouch space allocated, retuns written number */ static int wpa_calc_ptk_prefixes(const uint8 *prefix, uint prefix_len, const struct ether_addr *auth_ea, const struct ether_addr *sta_ea, const uint8 *anonce, uint8 anonce_len, const uint8 *snonce, uint8 snonce_len, bcm_const_xlvp_t *pfx) { int npfx = 0; const uint8 *nonce; /* prefix || min ea || max ea || min nonce || max nonce */ pfx[npfx].len = (uint16)(prefix_len & 0xffff); pfx[npfx++].data = prefix; pfx[npfx].len = ETHER_ADDR_LEN; pfx[npfx++].data = (const uint8 *) wpa_array_cmp(MIN_ARRAY, (const uint8 *)auth_ea, (const uint8 *)sta_ea, ETHER_ADDR_LEN); pfx[npfx].len = ETHER_ADDR_LEN; pfx[npfx++].data = (const uint8 *) wpa_array_cmp(MAX_ARRAY, (const uint8 *)auth_ea, (const uint8 *)sta_ea, ETHER_ADDR_LEN); nonce = (const uint8 *)wpa_array_cmp(MIN_ARRAY, snonce, anonce, snonce_len); if (nonce == snonce) { pfx[npfx].len = snonce_len; pfx[npfx++].data = snonce; pfx[npfx].len = anonce_len; pfx[npfx++].data = anonce; } else { pfx[npfx].len = anonce_len; pfx[npfx++].data = anonce; pfx[npfx].len = snonce_len; pfx[npfx++].data = snonce; } return npfx; } void kdf_calc_ptk(const struct ether_addr *auth_ea, const struct ether_addr *sta_ea, const uint8 *anonce, const uint8* snonce, const uint8 *pmk, uint pmk_len, uint8 *ptk, uint ptk_len) { bcm_const_xlvp_t pfx[5]; int npfx; /* note: kdf omits trailing NULL in prefix */ npfx = wpa_calc_ptk_prefixes((uint8 *)WPA_PTK_PFX, sizeof(WPA_PTK_PFX) - 1, auth_ea, sta_ea, anonce, EAPOL_WPA_KEY_NONCE_LEN, snonce, EAPOL_WPA_KEY_NONCE_LEN, pfx); hmac_sha2_n(HASH_SHA256, pmk, pmk_len, pfx, npfx, NULL, 0, ptk, ptk_len); } #endif /* BCMSUP_PSK || GTKOE || BCMAUTH_PSK || WLFBT */ #if defined(BCMSUP_PSK) || defined(BCMAUTH_PSK) || defined(WLFBT) || defined(GTKOE) /* Compute Message Integrity Code (MIC) over EAPOL message */ int wpa_make_mic(eapol_header_t *eapol, uint key_desc, uint8 *mic_key, rsn_ie_info_t *rsn_info, uchar *mic, uint mic_len) { uint data_len; int err = BCME_OK; sha2_hash_type_t type = HASH_NONE; /* length of eapol pkt from the version field on */ data_len = 4 + ntoh16_ua((uint8 *)&eapol->length); /* Create the MIC for the pkt */ switch (key_desc) { case WPA_KEY_DESC_V1: type = HASH_MD5; break; case WPA_KEY_DESC_V2: /* note: transparent truncation to mic_len */ type = HASH_SHA1; break; case WPA_KEY_DESC_V3: aes_cmac_calc(NULL, 0, &eapol->version, data_len, mic_key, mic_len, mic, AES_BLOCK_SZ); goto exit; case WPA_KEY_DESC_V0: ASSERT(rsn_info != NULL); if (rsn_info == NULL) { return BCME_BADARG; } if (IS_SAE_AKM(rsn_info->sta_akm)) { aes_cmac_calc(NULL, 0, &eapol->version, data_len, mic_key, mic_len, mic, AES_BLOCK_SZ); goto exit; } type = bcmwpa_rsn_akm_to_hash(rsn_info->sta_akm); break; default: /* 11mc D8.0 some AKMs use descriptor version 0 */ err = BCME_UNSUPPORTED; goto exit; } if (type) { err = hmac_sha2(type, mic_key, mic_len, NULL, 0, (uint8 *)&eapol->version, data_len, mic, mic_len); } exit: return err; } int wpa_calc_ptk(rsn_akm_t akm, const struct ether_addr *auth_ea, const struct ether_addr *sta_ea, const uint8 *anonce, uint8 anon_len, const uint8 *snonce, uint8 snon_len, const uint8 *pmk, uint pmk_len, uint8 *ptk, uint ptk_len) { bcm_const_xlvp_t pfx[PRF_PREFIXES_NUM]; int npfx; int ret = BCME_OK; sha2_hash_type_t hash_type; uint label_len; if (RSN_AKM_USE_KDF(akm)) { label_len = sizeof(WPA_PTK_PFX) - 1u; } else { //WPA AKMS label_len = sizeof(WPA_PTK_PFX); /* note: wpa needs trailing NULL in prefix */ } hash_type = bcmwpa_rsn_akm_to_hash(akm); npfx = wpa_calc_ptk_prefixes((uint8 *)WPA_PTK_PFX, label_len, auth_ea, sta_ea, anonce, anon_len, snonce, snon_len, pfx); ret = hmac_sha2_n(hash_type, pmk, pmk_len, pfx, npfx, NULL, 0, ptk, ptk_len); return ret; } bool wpa_encr_key_data(eapol_wpa_key_header_t *body, uint16 key_info, uint8 *ekey, uint8 *gtk, uint8 *data, uint8 *encrkey, rc4_ks_t *rc4key, const rsn_ie_info_t *rsn_info) { uint16 len; uint8 *key_data; switch (key_info & (WPA_KEY_DESC_V1 | WPA_KEY_DESC_V2)) { case WPA_KEY_DESC_V1: if (gtk) { len = ntoh16_ua((uint8 *)&body->key_len); } else { len = ntoh16_ua(EAPOL_WPA_KEY_HDR_DATA_LEN_PTR(body, rsn_info->kck_mic_len)); } /* create the iv/ptk key */ if (memcpy_s(encrkey, EAPOL_WPA_KEY_IV_LEN, body->iv, sizeof(body->iv))) { return FALSE; } if (memcpy_s(&encrkey[EAPOL_WPA_KEY_IV_LEN], EAPOL_WPA_ENCR_KEY_DEFAULT_LEN, ekey, EAPOL_WPA_ENCR_KEY_DEFAULT_LEN)) { return FALSE; } /* encrypt the key data */ prepare_key(encrkey, EAPOL_WPA_KEY_IV_LEN + EAPOL_WPA_ENCR_KEY_DEFAULT_LEN, rc4key); rc4(data, WPA_KEY_DATA_LEN_256, rc4key); /* dump 256 bytes */ key_data = EAPOL_WPA_KEY_HDR_DATA_PTR(body, rsn_info->kck_mic_len); rc4(key_data, len, rc4key); break; case WPA_KEY_DESC_V2: /* fall through */ case WPA_KEY_DESC_V3: case WPA_KEY_DESC_V0: len = ntoh16_ua(EAPOL_WPA_KEY_HDR_DATA_LEN_PTR(body, rsn_info->kck_mic_len)); /* FIXME: data_len is length to encrypt, but need to make sure * buffer is big enought * for expansion. how? problem for caller? */ key_data = EAPOL_WPA_KEY_HDR_DATA_PTR(body, rsn_info->kck_mic_len); /* pad if needed - min. 16 bytes, 8 byte aligned */ /* padding is 0xdd followed by 0's */ if (len < 2u *AKW_BLOCK_LEN) { key_data[len] = WPA2_KEY_DATA_PAD; bzero(&key_data[len + 1u], 2u * AKW_BLOCK_LEN - (len + 1u)); len = 2u *AKW_BLOCK_LEN; } else if (len % AKW_BLOCK_LEN) { key_data[len] = WPA2_KEY_DATA_PAD; bzero(&key_data[len + 1u], AKW_BLOCK_LEN - ((len + 1u) % AKW_BLOCK_LEN)); len += AKW_BLOCK_LEN - (len % AKW_BLOCK_LEN); } if (aes_wrap(rsn_info->kek_len, ekey, len, key_data, key_data)) { return FALSE; } len += AKW_BLOCK_LEN; hton16_ua_store(len, (uint8 *)EAPOL_WPA_KEY_HDR_DATA_LEN_PTR(body, rsn_info->kck_mic_len)); break; default: /* 11mc D8.0 key descriptor version 0 used */ return FALSE; } return TRUE; } /* Check MIC of EAPOL message */ bool wpa_check_mic(eapol_header_t *eapol, uint key_desc, uint8 *mic_key, rsn_ie_info_t *rsn_info) { eapol_wpa_key_header_t *body = NULL; uchar digest[SHA2_MAX_DIGEST_LEN]; uchar mic[EAPOL_WPA_KEY_MAX_MIC_LEN]; if (!mic_key || !rsn_info || !eapol) { return FALSE; } body = (eapol_wpa_key_header_t *)eapol->body; #ifndef EAPOL_KEY_HDR_VER_V2 if (rsn_info->kck_mic_len != EAPOL_WPA_KCK_DEFAULT_LEN) #else if (rsn_info->kck_mic_len > EAPOL_WPA_KEY_MAX_MIC_LEN) #endif /* EAPOL_KEY_HDR_VER_V2 */ { ASSERT(0); return FALSE; } /* save MIC and clear its space in message */ if (memcpy_s(mic, sizeof(mic), EAPOL_WPA_KEY_HDR_MIC_PTR(body), rsn_info->kck_mic_len)) { return FALSE; } bzero(EAPOL_WPA_KEY_HDR_MIC_PTR(body), rsn_info->kck_mic_len); if (wpa_make_mic(eapol, key_desc, mic_key, rsn_info, digest, rsn_info->kck_mic_len) != BCME_OK) { return FALSE; } return !memcmp(digest, mic, rsn_info->kck_mic_len); } static sha2_hash_type_t bcmwpa_rsn_akm_to_hash(const rsn_akm_t akm) { uint i = 0; sha2_hash_type_t type = HASH_NONE; for (i = 0; i < ARRAYSIZE(rsn_akm_lookup_tbl); i++) { if (akm == rsn_akm_lookup_tbl[i].rsn_akm) { type = rsn_akm_lookup_tbl[i].hash_type; break; } } return type; } #endif /* BCMSUP_PSK || BCMAUTH_PSK || WLFBT || GTKOE */ #ifdef WLTDLS void wpa_calc_tpk(const struct ether_addr *init_ea, const struct ether_addr *resp_ea, const struct ether_addr *bssid, const uint8 *anonce, const uint8* snonce, uint8 *tpk, uint tpk_len) { uint8 pmk[SHA2_MAX_DIGEST_LEN]; uint pmk_len; bcm_const_xlvp_t ikpfx[2]; int nikpfx = 0; bcm_const_xlvp_t tpkpfx[4]; int ntpkpfx = 0; pmk_len = sha2_digest_len(HASH_SHA256); /* compute pmk to use - using anonce and snonce - min and then max */ ikpfx[nikpfx].len = EAPOL_WPA_KEY_NONCE_LEN; ikpfx[nikpfx++].data = wpa_array_cmp(MIN_ARRAY, snonce, anonce, EAPOL_WPA_KEY_NONCE_LEN), ikpfx[nikpfx].len = EAPOL_WPA_KEY_NONCE_LEN; ikpfx[nikpfx++].data = wpa_array_cmp(MAX_ARRAY, snonce, anonce, EAPOL_WPA_KEY_NONCE_LEN), (void)sha2(HASH_SHA256, ikpfx, nikpfx, NULL, 0, pmk, SHA2_SHA256_DIGEST_LEN); /* compute the tpk - using prefix, min ea, max ea, bssid */ tpkpfx[ntpkpfx].len = sizeof(TDLS_PMK_PFX) - 1; tpkpfx[ntpkpfx++].data = (const uint8 *)TDLS_PMK_PFX; tpkpfx[ntpkpfx].len = ETHER_ADDR_LEN; tpkpfx[ntpkpfx++].data = wpa_array_cmp(MIN_ARRAY, (const uint8 *)init_ea, (const uint8 *)resp_ea, ETHER_ADDR_LEN), tpkpfx[ntpkpfx].len = ETHER_ADDR_LEN; tpkpfx[ntpkpfx++].data = wpa_array_cmp(MAX_ARRAY, (const uint8 *)init_ea, (const uint8 *)resp_ea, ETHER_ADDR_LEN), tpkpfx[ntpkpfx].len = ETHER_ADDR_LEN; tpkpfx[ntpkpfx++].data = (const uint8 *)bssid; (void)hmac_sha2_n(HASH_SHA256, pmk, pmk_len, tpkpfx, ntpkpfx, NULL, 0, tpk, tpk_len); } #endif /* WLTDLS */ /* Convert WPA/WPA2 IE cipher suite to locally used value */ static bool rsn_cipher(wpa_suite_t *suite, ushort *cipher, const uint8 *std_oui, bool wep_ok) { bool ret = TRUE; if (!memcmp((const char *)suite->oui, std_oui, DOT11_OUI_LEN)) { switch (suite->type) { case WPA_CIPHER_TKIP: *cipher = CRYPTO_ALGO_TKIP; break; case WPA_CIPHER_AES_CCM: *cipher = CRYPTO_ALGO_AES_CCM; break; case WPA_CIPHER_AES_GCM: *cipher = CRYPTO_ALGO_AES_GCM; break; case WPA_CIPHER_AES_GCM256: *cipher = CRYPTO_ALGO_AES_GCM256; break; case WPA_CIPHER_WEP_40: if (wep_ok) *cipher = CRYPTO_ALGO_WEP1; else ret = FALSE; break; case WPA_CIPHER_WEP_104: if (wep_ok) *cipher = CRYPTO_ALGO_WEP128; else ret = FALSE; break; default: ret = FALSE; break; } return ret; } return FALSE; } bool wpa_cipher(wpa_suite_t *suite, ushort *cipher, bool wep_ok) { return rsn_cipher(suite, cipher, (const uchar*)WPA_OUI, wep_ok); } bool wpa2_cipher(wpa_suite_t *suite, ushort *cipher, bool wep_ok) { return rsn_cipher(suite, cipher, (const uchar*)WPA2_OUI, wep_ok); } /* Is any of the tlvs the expected entry? If * not update the tlvs buffer pointer/length. */ bool bcm_has_ie(uint8 *ie, uint8 **tlvs, uint *tlvs_len, const uint8 *oui, uint oui_len, uint8 type) { /* If the contents match the OUI and the type */ if (ie[TLV_LEN_OFF] >= oui_len + 1 && !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) && type == ie[TLV_BODY_OFF + oui_len]) { return TRUE; } /* point to the next ie */ ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN; /* calculate the length of the rest of the buffer */ *tlvs_len -= (uint)(ie - *tlvs); /* update the pointer to the start of the buffer */ *tlvs = ie; return FALSE; } wpa_ie_fixed_t * bcm_find_wpaie(uint8 *parse, uint len) { return (wpa_ie_fixed_t *) bcm_find_ie(parse, len, DOT11_MNG_VS_ID, WPA_OUI_LEN, (const char*) WPA_OUI, WPA_OUI_TYPE); } int bcm_find_security_ies(uint8 *buf, uint buflen, void **wpa_ie, void **rsn_ie) { bcm_tlv_t *tlv = NULL; uint totlen = 0; uint8 *end = NULL; uint len = 0; uint tlvs_len = 0; uint8 *tlvs = NULL; if ((tlv = (bcm_tlv_t*)buf) == NULL || !wpa_ie || !rsn_ie || buflen == 0) { return BCME_BADARG; } totlen = buflen; *rsn_ie = *wpa_ie = NULL; end = buf; end += buflen; /* find rsn ie and wpa ie */ while (totlen >= TLV_HDR_LEN) { len = tlv->len; tlvs_len = buflen; tlvs = buf; /* check if tlv overruns buffer */ if (totlen < (len + TLV_HDR_LEN)) { return BCME_BUFTOOSHORT; } /* validate remaining totlen */ if (totlen >= (len + TLV_HDR_LEN)) { if ((*rsn_ie == NULL) && (tlv->id == DOT11_MNG_RSN_ID)) { *rsn_ie = tlv; } else if ((*wpa_ie == NULL) && (tlv->id == DOT11_MNG_VS_ID)) { /* if vendor ie, check if its wpa ie */ if (bcm_is_wpa_ie((uint8 *)tlv, &tlvs, &tlvs_len)) *wpa_ie = tlv; } } if (*rsn_ie && *wpa_ie) break; tlv = (bcm_tlv_t*)((uint8*)tlv + (len + TLV_HDR_LEN)); totlen -= (len + TLV_HDR_LEN); if (totlen > buflen) { return BCME_BUFTOOLONG; } if ((uint8 *)tlv > end) { return BCME_BUFTOOSHORT; } } if (*wpa_ie || *rsn_ie) return BCME_OK; else return BCME_NOTFOUND; } bcm_tlv_t * bcm_find_wmeie(uint8 *parse, uint len, uint8 subtype, uint8 subtype_len) { bcm_tlv_t *ie; if ((ie = bcm_find_ie(parse, len, DOT11_MNG_VS_ID, WME_OUI_LEN, (const char*) WME_OUI, WME_OUI_TYPE))) { uint ie_len = TLV_HDR_LEN + ie->len; wme_ie_t *ie_data = (wme_ie_t *)ie->data; /* the subtype_len must include OUI+type+subtype */ if (subtype_len > WME_OUI_LEN + 1 && ie_len == (uint)TLV_HDR_LEN + subtype_len && ie_data->subtype == subtype) { return ie; } /* move to next IE */ len -= (uint)((uint8 *)ie + ie_len - parse); parse = (uint8 *)ie + ie_len; } return NULL; } wps_ie_fixed_t * bcm_find_wpsie(const uint8 *parse, uint len) { uint8 type = WPS_OUI_TYPE; return (wps_ie_fixed_t *)bcm_find_vendor_ie(parse, len, WPS_OUI, &type, sizeof(type)); } /* locate the Attribute in the WPS IE */ /* assume the caller has validated the WPS IE tag and length */ wps_at_fixed_t * bcm_wps_find_at(wps_at_fixed_t *at, uint len, uint16 id) { while ((int)len >= WPS_AT_FIXED_LEN) { uint alen = WPS_AT_FIXED_LEN + ntoh16_ua(((wps_at_fixed_t *)at)->len); if (ntoh16_ua(((wps_at_fixed_t *)at)->at) == id && alen <= len) return at; at = (wps_at_fixed_t *)((uint8 *)at + alen); len -= alen; } return NULL; } #ifdef WLP2P wifi_p2p_ie_t * bcm_find_p2pie(const uint8 *parse, uint len) { uint8 type = P2P_OUI_TYPE; return (wifi_p2p_ie_t *)bcm_find_vendor_ie(parse, len, P2P_OUI, &type, sizeof(type)); } #endif bcm_tlv_t * bcm_find_hs20ie(uint8 *parse, uint len) { return bcm_find_ie(parse, len, DOT11_MNG_VS_ID, WFA_OUI_LEN, (const char *)WFA_OUI, WFA_OUI_TYPE_HS20); } bcm_tlv_t * bcm_find_osenie(uint8 *parse, uint len) { return bcm_find_ie(parse, len, DOT11_MNG_VS_ID, WFA_OUI_LEN, (const char *) WFA_OUI, WFA_OUI_TYPE_OSEN); } #if defined(BCMSUP_PSK) || defined(BCMSUPPL) || defined(GTKOE) || defined(WL_FILS) #define wpa_is_kde(ie, tlvs, len, type) bcm_has_ie(ie, tlvs, len, \ (const uint8 *)WPA2_OUI, WPA2_OUI_LEN, type) eapol_wpa2_encap_data_t * wpa_find_kde(const uint8 *parse, uint len, uint8 type) { return (eapol_wpa2_encap_data_t *) bcm_find_ie(parse, len, DOT11_MNG_PROPR_ID, WPA2_OUI_LEN, (const char *) WPA2_OUI, type); } bool wpa_is_gtk_encap(uint8 *ie, uint8 **tlvs, uint *tlvs_len) { return wpa_is_kde(ie, tlvs, tlvs_len, WPA2_KEY_DATA_SUBTYPE_GTK); } eapol_wpa2_encap_data_t * wpa_find_gtk_encap(uint8 *parse, uint len) { eapol_wpa2_encap_data_t *data; /* minimum length includes kde upto gtk field in eapol_wpa2_key_gtk_encap_t */ data = wpa_find_kde(parse, len, WPA2_KEY_DATA_SUBTYPE_GTK); if (data && (data->length < EAPOL_WPA2_GTK_ENCAP_MIN_LEN)) { data = NULL; } return data; } int wpa_find_eapol_kde_data(eapol_header_t* eapol, uint8 eapol_mic_len, uint8 subtype, eapol_wpa2_encap_data_t **out_data) { eapol_wpa_key_header_t *body; uint8 *parse; uint16 body_len; uint16 data_len; if (!eapol) { return BCME_BADARG; } body = (eapol_wpa_key_header_t *)eapol->body; body_len = ntoh16_ua(&eapol->length); data_len = ntoh16_ua(EAPOL_WPA_KEY_HDR_DATA_LEN_PTR(body, eapol_mic_len)); parse = EAPOL_WPA_KEY_HDR_DATA_PTR(body, eapol_mic_len); if (((uint8 *)body + body_len) < ((uint8 *)parse + data_len)) { return BCME_BUFTOOSHORT; } return wpa_find_kde_data(parse, data_len, subtype, out_data); } int wpa_find_kde_data(const uint8 *kde_buf, uint16 buf_len, uint8 subtype, eapol_wpa2_encap_data_t **out_data) { eapol_wpa2_encap_data_t *data; uint8 min_len; if (!kde_buf) { return BCME_BADARG; } /* minimum length includes kde upto gtk field in eapol_wpa2_key_gtk_encap_t */ data = wpa_find_kde(kde_buf, buf_len, subtype); if (!data) { return BCME_IE_NOTFOUND; } switch (subtype) { case WPA2_KEY_DATA_SUBTYPE_GTK: min_len = EAPOL_WPA2_GTK_ENCAP_MIN_LEN; break; case WPA2_KEY_DATA_SUBTYPE_IGTK: min_len = EAPOL_WPA2_BIGTK_ENCAP_MIN_LEN; break; case WPA2_KEY_DATA_SUBTYPE_BIGTK: min_len = EAPOL_WPA2_IGTK_ENCAP_MIN_LEN; break; #ifdef WL_OCV case WPA2_KEY_DATA_SUBTYPE_OCI: min_len = EAPOL_WPA2_OCI_ENCAP_MIN_LEN; break; #endif /* WL_OCV */ default: return BCME_UNSUPPORTED; } if (data->length < min_len) { return BCME_BADLEN; } *out_data = data; return BCME_OK; } #ifdef WL_OCV bool wpa_check_ocv_caps(uint16 local_caps, uint16 peer_caps) { bool ocv_enabled = ((local_caps & RSN_CAP_OCVC) && (peer_caps & RSN_CAP_OCVC)); bool mfp_enabled = ((peer_caps & RSN_CAP_MFPC) || (peer_caps & RSN_CAP_MFPR)); return (ocv_enabled && mfp_enabled); } int wpa_add_oci_encap(chanspec_t chspec, uint8* buf, uint buf_len) { int retval = BCME_OK; eapol_wpa2_encap_data_t* oci_kde; uint len = buf_len; if (buf_len < WPA_OCV_OCI_KDE_SIZE) { retval = BCME_BUFTOOSHORT; goto done; } oci_kde = (eapol_wpa2_encap_data_t*)buf; oci_kde->type = DOT11_MNG_WPA_ID; oci_kde->subtype = WPA2_KEY_DATA_SUBTYPE_OCI; oci_kde->length = (WPA_OCV_OCI_KDE_SIZE - TLV_HDR_LEN); oci_kde->oui[0u] = WPA2_OUI[0u]; oci_kde->oui[1u] = WPA2_OUI[1u]; oci_kde->oui[2u] = WPA2_OUI[2u]; buf += EAPOL_WPA2_ENCAP_DATA_HDR_LEN; len -= EAPOL_WPA2_ENCAP_DATA_HDR_LEN; retval = bcm_ocv_write_oci(chspec, buf, len); if (retval != BCME_OK) { goto done; } done: return retval; } int wpa_add_oci_ie(chanspec_t chspec, uint8* buf, uint buf_len) { int retval = BCME_OK; uint8* oci_buf = buf + BCM_TLV_EXT_HDR_SIZE; if (buf_len < (bcm_ocv_get_oci_len() + BCM_TLV_EXT_HDR_SIZE)) { retval = BCME_BUFTOOSHORT; goto done; } retval = bcm_ocv_write_oci(chspec, oci_buf, bcm_ocv_get_oci_len()); if (retval != BCME_OK) { goto done; } (void)bcm_write_tlv_ext(DOT11_MNG_ID_EXT_ID, OCV_EXTID_MNG_OCI_ID, oci_buf, bcm_ocv_get_oci_len(), buf); done: return retval; } int wpa_add_oci_ft_subelem(chanspec_t chspec, uint8* buf, uint buf_len) { int retval = BCME_OK; uint8* oci_buf = buf + BCM_TLV_HDR_SIZE; if (buf_len < (bcm_ocv_get_oci_len() + BCM_TLV_HDR_SIZE)) { retval = BCME_BUFTOOSHORT; goto done; } retval = bcm_ocv_write_oci(chspec, oci_buf, bcm_ocv_get_oci_len()); if (retval != BCME_OK) { goto done; } bcm_write_tlv_safe(DOT11_FBT_SUBELEM_ID_OCI, oci_buf, bcm_ocv_get_oci_len(), buf, buf_len); done: return retval; } int wpa_validate_oci_encap(chanspec_t chspec, const uint8* buf, uint buf_len) { int retval = BCME_OK; eapol_wpa2_encap_data_t *encap = NULL; retval = wpa_find_kde_data(buf, buf_len, WPA2_KEY_DATA_SUBTYPE_OCI, &encap); if (retval != BCME_OK) { retval = BCME_NOTFOUND; goto done; } retval = bcm_ocv_validate_oci(chspec, encap->data, encap->length); if (retval != BCME_OK) { goto done; } done: return retval; } int wpa_validate_oci_ie(chanspec_t chspec, const uint8* buf, uint buf_len) { int retval = BCME_OK; bcm_tlv_ext_t *oci_ie; oci_ie = (bcm_tlv_ext_t *)bcm_parse_tlvs_dot11(buf, buf_len, OCV_EXTID_MNG_OCI_ID, TRUE); if (!oci_ie) { retval = BCME_NOTFOUND; goto done; } retval = bcm_ocv_validate_oci(chspec, oci_ie->data, oci_ie->len); if (retval != BCME_OK) { goto done; } done: return retval; } int wpa_validate_oci_ft_subelem(chanspec_t chspec, const uint8* buf, uint buf_len) { int retval = BCME_OK; bcm_tlv_t *oci_ie; oci_ie = (bcm_tlv_t *)bcm_parse_tlvs_dot11(buf, buf_len, DOT11_FBT_SUBELEM_ID_OCI, FALSE); if (!oci_ie) { retval = BCME_NOTFOUND; goto done; } retval = bcm_ocv_validate_oci(chspec, oci_ie->data, oci_ie->len); if (retval != BCME_OK) { goto done; } done: return retval; } #endif /* WL_OCV */ #endif /* defined(BCMSUP_PSK) || defined(BCMSUPPL) || defined(GTKOE) || defined(WL_FILS) */ const uint8 * wpa_array_cmp(int max_array, const uint8 *x, const uint8 *y, uint len) { uint i; const uint8 *ret = x; for (i = 0; i < len; i++) if (x[i] != y[i]) break; if (i == len) { /* returning null will cause crash, return value used for copying */ /* return first param in this case to close security loophole */ return x; } if (max_array && (y[i] > x[i])) ret = y; if (!max_array && (y[i] < x[i])) ret = y; return (ret); } void wpa_incr_array(uint8 *array, uint len) { int i; for (i = (len-1); i >= 0; i--) if (array[i]++ != 0xff) { break; } } bool bcmwpa_akm2WPAauth(uint8 *akm, uint32 *auth, bool sta_iswpa) { uint i; oui_akm_wpa_tbl_t wpa_auth_tbl_match[] = { {WPA2_OUI, RSN_AKM_NONE, WPA_AUTH_NONE}, {WPA2_OUI, RSN_AKM_UNSPECIFIED, WPA2_AUTH_UNSPECIFIED}, {WPA2_OUI, RSN_AKM_PSK, WPA2_AUTH_PSK}, {WPA2_OUI, RSN_AKM_FBT_1X, WPA2_AUTH_UNSPECIFIED | WPA2_AUTH_FT}, {WPA2_OUI, RSN_AKM_FBT_PSK, WPA2_AUTH_PSK | WPA2_AUTH_FT}, {WPA2_OUI, RSN_AKM_SHA256_1X, WPA2_AUTH_1X_SHA256}, {WPA2_OUI, RSN_AKM_SHA256_PSK, WPA2_AUTH_PSK_SHA256}, {WPA2_OUI, RSN_AKM_FILS_SHA256, WPA2_AUTH_FILS_SHA256}, {WPA2_OUI, RSN_AKM_FILS_SHA384, WPA2_AUTH_FILS_SHA384}, {WPA2_OUI, RSN_AKM_FBT_SHA256_FILS, WPA2_AUTH_FILS_SHA256 | WPA2_AUTH_FT}, {WPA2_OUI, RSN_AKM_FBT_SHA384_FILS, WPA2_AUTH_FILS_SHA384 | WPA2_AUTH_FT}, {WPA2_OUI, RSN_AKM_SAE_PSK, WPA3_AUTH_SAE_PSK}, {WPA2_OUI, RSN_AKM_SAE_FBT, WPA3_AUTH_SAE_PSK | WPA2_AUTH_FT}, {WPA2_OUI, RSN_AKM_OWE, WPA3_AUTH_OWE}, {WPA2_OUI, RSN_AKM_SUITEB_SHA256_1X, WPA3_AUTH_1X_SUITE_B_SHA256}, {WPA2_OUI, RSN_AKM_SUITEB_SHA384_1X, WPA3_AUTH_1X_SUITE_B_SHA384}, {WFA_OUI, OSEN_AKM_UNSPECIFIED, WPA2_AUTH_UNSPECIFIED}, {WFA_OUI, RSN_AKM_FBT_SHA256_FILS, WPA2_AUTH_FILS_SHA256 | WPA2_AUTH_FT}, {WFA_OUI, RSN_AKM_FBT_SHA384_FILS, WPA2_AUTH_FILS_SHA384 | WPA2_AUTH_FT}, {WFA_OUI, RSN_AKM_DPP, WPA3_AUTH_DPP_AKM}, #ifdef BCMWAPI_WAI {WAPI_OUI, RSN_AKM_NONE, WAPI_AUTH_NONE}, {WAPI_OUI, RSN_AKM_UNSPECIFIED, WAPI_AUTH_UNSPECIFIED}, {WAPI_OUI, RSN_AKM_PSK, WAPI_AUTH_PSK}, #endif /* BCMWAPI_WAI */ {WPA_OUI, RSN_AKM_NONE, WPA_AUTH_NONE}, {WPA_OUI, RSN_AKM_UNSPECIFIED, WPA_AUTH_UNSPECIFIED}, {WPA_OUI, RSN_AKM_PSK, WPA_AUTH_PSK}, }; BCM_REFERENCE(sta_iswpa); for (i = 0; i < ARRAYSIZE(wpa_auth_tbl_match); i++) { if (!memcmp(akm, wpa_auth_tbl_match[i].oui, DOT11_OUI_LEN)) { if (wpa_auth_tbl_match[i].rsn_akm == akm[DOT11_OUI_LEN]) { *auth = wpa_auth_tbl_match[i].wpa_auth; return TRUE; } } } return FALSE; } /* map cipher suite to internal WSEC_XXXX */ /* cs points 4 byte cipher suite, and only the type is used for non CCX ciphers */ bool bcmwpa_cipher2wsec(uint8 *cipher, uint32 *wsec) { #ifdef BCMWAPI_WAI if (!memcmp(cipher, WAPI_OUI, DOT11_OUI_LEN)) { switch (WAPI_CSE_WPI_2_CIPHER(cipher[DOT11_OUI_LEN])) { case WAPI_CIPHER_NONE: *wsec = 0; break; case WAPI_CIPHER_SMS4: *wsec = SMS4_ENABLED; break; default: return FALSE; } return TRUE; } #endif /* BCMWAPI_WAI */ switch (cipher[DOT11_OUI_LEN]) { case WPA_CIPHER_NONE: *wsec = 0; break; case WPA_CIPHER_WEP_40: case WPA_CIPHER_WEP_104: *wsec = WEP_ENABLED; break; case WPA_CIPHER_TKIP: *wsec = TKIP_ENABLED; break; case WPA_CIPHER_AES_CCM: /* fall through */ case WPA_CIPHER_AES_GCM: /* fall through */ case WPA_CIPHER_AES_GCM256: *wsec = AES_ENABLED; break; #ifdef BCMWAPI_WAI case WAPI_CIPHER_SMS4: *wsec = SMS4_ENABLED; break; #endif /* BCMWAPI_WAI */ default: return FALSE; } return TRUE; } #ifdef RSN_IE_INFO_STRUCT_RELOCATED /* map WPA/RSN cipher to internal WSEC */ uint32 bcmwpa_wpaciphers2wsec(uint32 wpacipher) { uint32 wsec = 0; switch (wpacipher) { case BCM_BIT(WPA_CIPHER_WEP_40): case BCM_BIT(WPA_CIPHER_WEP_104): wsec = WEP_ENABLED; break; case BCM_BIT(WPA_CIPHER_TKIP): wsec = TKIP_ENABLED; break; case BCM_BIT(WPA_CIPHER_AES_OCB): /* fall through */ case BCM_BIT(WPA_CIPHER_AES_CCM): wsec = AES_ENABLED; break; case BCM_BIT(WPA_CIPHER_AES_GCM): /* fall through */ case BCM_BIT(WPA_CIPHER_AES_GCM256): wsec = AES_ENABLED; break; #ifdef BCMWAPI_WAI case BCM_BIT(WAPI_CIPHER_SMS4): wsec = SMS4_ENABLED; break; #endif /* BCMWAPI_WAI */ default: break; } return wsec; } uint32 wlc_convert_rsn_to_wsec_bitmap(uint32 ap_cipher_mask) { uint32 ap_wsec = 0; uint32 tmp_mask = ap_cipher_mask; uint32 c; FOREACH_BIT(c, tmp_mask) { ap_wsec |= bcmwpa_wpaciphers2wsec(c); } return ap_wsec; } #else /* Not RSN_IE_INFO_STRUCT_RELOCATED */ uint32 bcmwpa_wpaciphers2wsec(uint8 wpacipher) { uint32 wsec = 0; switch (wpacipher) { case WPA_CIPHER_NONE: break; case WPA_CIPHER_WEP_40: case WPA_CIPHER_WEP_104: wsec = WEP_ENABLED; break; case WPA_CIPHER_TKIP: wsec = TKIP_ENABLED; break; case WPA_CIPHER_AES_OCB: /* fall through */ case WPA_CIPHER_AES_CCM: wsec = AES_ENABLED; break; case WPA_CIPHER_AES_GCM: /* fall through */ case WPA_CIPHER_AES_GCM256: wsec = AES_ENABLED; break; #ifdef BCMWAPI_WAI case WAPI_CIPHER_SMS4: wsec = SMS4_ENABLED; break; #endif /* BCMWAPI_WAI */ default: break; } return wsec; } #endif /* RSN_IE_INFO_STRUCT_RELOCATED */ bool bcmwpa_is_wpa_auth(uint32 auth) { if ((auth == WPA_AUTH_NONE) || (auth == WPA_AUTH_UNSPECIFIED) || (auth == WPA_AUTH_PSK)) return TRUE; else return FALSE; } bool bcmwpa_includes_wpa_auth(uint32 auth) { if (auth & (WPA_AUTH_NONE | WPA_AUTH_UNSPECIFIED | WPA_AUTH_PSK)) return TRUE; else return FALSE; } bool bcmwpa_is_rsn_auth(uint32 auth) { auth = auth & ~WPA2_AUTH_FT; if ((auth == WPA2_AUTH_UNSPECIFIED) || (auth == WPA2_AUTH_PSK) || (auth == BRCM_AUTH_PSK) || (auth == WPA2_AUTH_1X_SHA256) || (auth == WPA2_AUTH_PSK_SHA256) || (auth == WPA3_AUTH_SAE_PSK) || (auth == WPA3_AUTH_OWE) || WPA2_AUTH_IS_FILS(auth) || (auth == WPA3_AUTH_1X_SUITE_B_SHA256) || (auth == WPA3_AUTH_1X_SUITE_B_SHA384) || (auth == WPA3_AUTH_PSK_SHA384) || (auth == WPA3_AUTH_DPP_AKM)) { return TRUE; } else { return FALSE; } } bool bcmwpa_includes_rsn_auth(uint32 auth) { if (auth & (WPA2_AUTH_UNSPECIFIED | WPA2_AUTH_PSK | BRCM_AUTH_PSK | WPA2_AUTH_1X_SHA256 | WPA2_AUTH_PSK_SHA256 | WPA2_AUTH_IS_FILS(auth) | WPA3_AUTH_SAE_PSK | WPA3_AUTH_OWE | WPA3_AUTH_1X_SUITE_B_SHA256 | WPA3_AUTH_1X_SUITE_B_SHA384 | WPA3_AUTH_PSK_SHA384 | WPA3_AUTH_DPP_AKM)) return TRUE; else return FALSE; } #ifdef RSN_IE_INFO_STRUCT_RELOCATED /* decode unicast/multicast cipher in RSNIE */ static int bcmwpa_decode_cipher_suite(rsn_ie_info_t *info, const uint8 **ptr_inc, uint ie_len, uint *remain_len, uint16 *p_count) { const wpa_suite_ucast_t *ucast; const wpa_suite_mcast_t *mcast; uint i; if (!(*remain_len)) { info->g_cipher = WPA_CIPHER_UNSPECIFIED; info->p_ciphers = WPA_P_CIPHERS_UNSPECIFIED; goto done; /* only have upto ver */ } *ptr_inc += ie_len - *remain_len; if (*remain_len < sizeof(wpa_suite_mcast_t)) { info->parse_status = BCME_BADLEN; goto done; } mcast = (const wpa_suite_mcast_t *)*ptr_inc; if (IS_WPA_CIPHER(mcast->type)) { info->g_cipher = mcast->type; } else { info->parse_status = BCME_BAD_IE_DATA; goto done; } /* for rsn pairwise cipher suite */ *ptr_inc += sizeof(wpa_suite_mcast_t); *remain_len -= sizeof(wpa_suite_mcast_t); if (!(*remain_len)) { info->p_ciphers = WPA_P_CIPHERS_UNSPECIFIED; info->sta_akm = WPA_CIPHER_UNSPECIFIED; goto done; } ucast = (const wpa_suite_ucast_t *)*ptr_inc; if ((*remain_len) < sizeof(ucast->count)) { info->parse_status = BCME_BADLEN; goto done; } if (!ucast->count.low && !ucast->count.high) { info->parse_status = BCME_BADLEN; goto done; } *p_count = ltoh16_ua(&ucast->count); if (info->dev_type == DEV_STA && *p_count != 1u) { info->parse_status = BCME_BAD_IE_DATA; goto done; } if ((*remain_len) < (*p_count * WPA_SUITE_LEN + sizeof(ucast->count))) { info->parse_status = BCME_BADLEN; goto done; } if (info->dev_type == DEV_STA) { if (IS_WPA_CIPHER(ucast->list[0].type)) { /* update the pairwise cipher */ info->sta_cipher = ucast->list[0].type; } else { info->parse_status = BCME_BAD_IE_DATA; goto done; } } else { for (i = 0; i < *p_count; i++) { if (IS_WPA_CIPHER(ucast->list[i].type)) { info->p_ciphers |= BIT(ucast->list[i].type); info->rsn_p_ciphers = info->p_ciphers; } else { info->parse_status = BCME_BAD_IE_DATA; goto done; } } } /* update buffer ptr and remaining length */ *ptr_inc += (*p_count * WPA_SUITE_LEN) + sizeof(ucast->count); *remain_len -= (*p_count * WPA_SUITE_LEN) + sizeof(ucast->count); done: if (info->parse_status == BCME_OK) { if (info->g_cipher == WPA_CIPHER_UNSPECIFIED) { info->g_cipher = WPA_CIPHER_AES_CCM; } if (info->p_ciphers == WPA_P_CIPHERS_UNSPECIFIED) { info->p_ciphers = BIT(WPA_CIPHER_AES_CCM); info->rsn_p_ciphers = info->p_ciphers; } } return info->parse_status; } /* sta_akm/sta_cipher must be set before this call */ int bcmwpa_rsnie_eapol_key_len(rsn_ie_info_t *info) { info->pmk_len = bcmwpa_eapol_key_length(EAPOL_KEY_PMK, info->sta_akm, 0); info->kck_mic_len = bcmwpa_eapol_key_length(EAPOL_KEY_KCK_MIC, info->sta_akm, 0); info->kck_len = bcmwpa_eapol_key_length(EAPOL_KEY_KCK, info->sta_akm, 0); info->kek_len = bcmwpa_eapol_key_length(EAPOL_KEY_KEK, info->sta_akm, 0); info->tk_len = bcmwpa_eapol_key_length(EAPOL_KEY_TK, 0, info->sta_cipher); info->ptk_len = info->kck_len + info->kek_len + info->tk_len; #if defined(WL_FILS) && defined(WLFBT) info->kck2_len = bcmwpa_eapol_key_length(EAPOL_KEY_KCK2, info->sta_akm, 0); info->kek2_len = bcmwpa_eapol_key_length(EAPOL_KEY_KEK2, info->sta_akm, 0); if (WPA_IS_FILS_FT_AKM(info->sta_akm)) { info->ptk_len += (info->kck2_len + info->kek2_len); } #endif /* WL_FILS && WLFBT */ return BCME_OK; } /* Extract and store information from WPA or RSN IEs * * called after either * -an association request has been built (STA), * - an association was received (AP) * - a probe request has been built (AP) * - a probe response was received (STA) * * All available information is extracted to be used for subsequent * bss pruning, association request validation, key descriptor compuation etc. * * To be expanded as needed. * * ie: RSN IE input * rsn_info: parsed information. Placed in either bsscfg for self, or scb for peer. * dev_type: STA_RSN or AP_RSN * * Return : parse status. * NOTE: the parse status is also saved in the the parse_status field. * NOTE 2 : the IE itself is copied at the end of the structure. Since there is * no reference to the osh available here, the allocation has to happen outside * and so the structure cannot be zeroed in this function. * For the STA, it should happen everytime. * For the AP, it should happen right after a new beacon/probe has been acquired. */ int bcmwpa_parse_rsnie(const bcm_tlv_t *ie, rsn_ie_info_t *info, device_type_t dev_type) { const uint8 *ptr_inc = NULL; const wpa_suite_mcast_t *mcast; const wpa_suite_auth_key_mgmt_t *mgmt; const wpa_pmkid_list_t *pmkid_list; uint32 remain_len = 0, i; uint8 auth_ie_type; uint16 p_count = 0; uint16 akm_count; ASSERT(info != NULL); /* this function might be called from place where there * is no error detection. * e.g. fron the iem callback. Store status here. */ info->parse_status = BCME_OK; if (!ie) { info->parse_status = BCME_BADARG; goto done; } /* For AP, do not zero this structure since there could be multiple * IEs. In that case, add to the existing * bits in field (ciphers, akms) as necessary. */ if (dev_type == DEV_AP) { /* if already created, check device type */ if (info->dev_type != DEV_NONE) { if (info->dev_type != DEV_AP) { info->parse_status = BCME_BADARG; goto done; } } } info->dev_type = dev_type; ptr_inc = ie->data; /* decode auth IE (WPA vs RSN). Fill in the auth_ie_type and version. * Modify remain_len to indicate the position of the pointer. */ /* NOTE the status field will be updated in this call */ if (bcmwpa_decode_ie_type(ie, info, &remain_len, &auth_ie_type) != BCME_OK) { goto done; } /* decode multicast, unicast ciphers */ if (bcmwpa_decode_cipher_suite(info, &ptr_inc, ie->len, &remain_len, &p_count) != BCME_OK) { goto done; } if (!(remain_len)) { info->akms = BIT(RSN_AKM_UNSPECIFIED); goto done; } mgmt = (const wpa_suite_auth_key_mgmt_t *)ptr_inc; if (remain_len < sizeof(mgmt->count)) { info->parse_status = BCME_BADLEN; goto done; } akm_count = ltoh16_ua(&mgmt->count); if (!akm_count) { info->parse_status = BCME_BADARG; goto done; } if (dev_type == DEV_STA && akm_count != 1) { info->parse_status = BCME_BADARG; goto done; } if ((remain_len) < (akm_count * WPA_SUITE_LEN + sizeof(mgmt->count))) { info->parse_status = BCME_BADLEN; goto done; } if (dev_type == DEV_STA) { info->sta_akm = mgmt->list[0].type; } for (i = 0; i < akm_count; i++) { if (bcmwpa_is_valid_akm(mgmt->list[i].type) == BCME_OK) { ASSERT((mgmt->list[i].type) < (sizeof(info->akms) * NBBY)); info->akms |= BIT(mgmt->list[i].type); } } /* save IE dependent values in their respective fields */ if (dev_type == DEV_AP) { if (auth_ie_type == RSN_AUTH_IE) { info->rsn_akms = info->akms; } else if (auth_ie_type == WPA_AUTH_IE) { info->wpa_akms = info->akms; info->wpa_p_ciphers = info->p_ciphers; } } /* as a STA, at this point, we can compute the key descriptor version */ if (dev_type == DEV_STA) { info->key_desc = wlc_calc_rsn_desc_version(info); /* For STA, we can set the auth ie */ if (auth_ie_type == RSN_AUTH_IE) { info->auth_ie = info->rsn_ie; info->auth_ie_len = info->rsn_ie_len; } else { info->auth_ie = info->wpa_ie; info->auth_ie_len = info->wpa_ie_len; } } /* RSN AKM/cipher suite related EAPOL key length update */ bcmwpa_rsnie_eapol_key_len(info); /* for rsn capabilities */ ptr_inc += akm_count * WPA_SUITE_LEN + sizeof(mgmt->count); remain_len -= akm_count * WPA_SUITE_LEN + sizeof(mgmt->count); if (!(remain_len)) { goto done; } if (remain_len < RSN_CAP_LEN) { info->parse_status = BCME_BADLEN; goto done; } if (ie->id == DOT11_MNG_RSN_ID) { info->caps = ltoh16_ua(ptr_inc); } /* check if AKMs require MFP capable to be set */ if ((info->akms & RSN_MFPC_AKM_MASK) && !(info->caps & RSN_CAP_MFPC)) { /* NOTE: Acting as WPA3 CTT testbed device, it requires to send assoc request frame with user provided mfp value as is. So should not return error here. */ #ifndef WPA3_CTT info->parse_status = BCME_EPERM; goto done; #endif /* WPA3_CTT */ } /* for rsn PMKID */ ptr_inc += RSN_CAP_LEN; remain_len -= RSN_CAP_LEN; if (!(remain_len)) { goto done; } /* here's possible cases after RSN_CAP parsed * a) pmkid_count 2B(00 00) * b) pmkid_count 2B(00 00) + BIP 4B * c) pmkid_count 2B(non zero) + pmkid_count * 16B * d) pmkid_count 2B(non zero) + pmkid_count * 16B + BIP 4B */ /* pmkids_offset set to * 1) if pmkid_count field(2B) present, point to first PMKID offset in the RSN ID * no matter what pmkid_count value is. (true, even if pmkid_count == 00 00) * 2) if pmkid_count field(2B) not present, it shall be zero. */ pmkid_list = (const wpa_pmkid_list_t*)ptr_inc; if ((remain_len) < sizeof(pmkid_list->count)) { info->parse_status = BCME_BADLEN; goto done; } info->pmkid_count = (uint8)ltoh16_ua(&pmkid_list->count); ptr_inc += sizeof(pmkid_list->count); remain_len -= sizeof(pmkid_list->count); if (remain_len < (uint32)(info->pmkid_count * WPA2_PMKID_LEN)) { info->parse_status = BCME_BADLEN; goto done; } info->pmkids_offset = ie->len + TLV_HDR_LEN - remain_len; /* for rsn group management cipher suite */ ptr_inc += info->pmkid_count * WPA2_PMKID_LEN; remain_len -= info->pmkid_count * WPA2_PMKID_LEN; if (!(remain_len)) { goto done; } /* * from WPA2_Security_Improvements_Test_Plan_v1.0 * 4.2.4 APUT RSNE bounds verification using WPA2-PSK * May content RSNE extensibile element ay this point */ if (remain_len < sizeof(wpa_suite_mcast_t)) { info->parse_status = BCME_BADLEN; goto done; } mcast = (const wpa_suite_mcast_t *)ptr_inc; if (IS_VALID_BIP_CIPHER((rsn_cipher_t)mcast->type)) { info->g_mgmt_cipher = (rsn_cipher_t)mcast->type; } done: return info->parse_status; } /* Determine if the IE is of WPA or RSN type. Decode * up to version field. Modify the remaining len parameter to * indicate where the next field is. * Store and return error status. */ int bcmwpa_decode_ie_type(const bcm_tlv_t *ie, rsn_ie_info_t *info, uint32 *remaining, uint8 *type) { const uint8 * ptr_inc = (const uint8 *)ie->data; uint32 remain_len = ie->len; uint8 version, version_len; if (ie->id == DOT11_MNG_WPA_ID) { /* min len check */ if (remain_len < WPA_IE_FIXED_LEN) { info->parse_status = BCME_BADLEN; goto done; } /* WPA IE */ if (memcmp(WPA_OUI, ie->data, WPA_OUI_LEN)) { /* bad OUI */ info->parse_status = BCME_BADARG; goto done; } ptr_inc += WPA_OUI_LEN; if (*ptr_inc == WPA_OUI_TYPE) { *type = WPA_AUTH_IE; } else if (*ptr_inc == WFA_OUI_TYPE_OSEN) { *type = OSEN_AUTH_IE; } else { /* wrong type */ info->parse_status = BCME_BADARG; goto done; } ptr_inc ++; remain_len -= WPA_OUI_LEN + 1u; version_len = WPA_VERSION_LEN; } else if (ie->id == DOT11_MNG_RSN_ID) { if (remain_len < WPA2_VERSION_LEN) { info->parse_status = BCME_BADLEN; goto done; } /* RSN IE */ *type = RSN_AUTH_IE; version_len = WPA2_VERSION_LEN; } else { printf("IE ID %d\n", ie->id); /* TODO : add support for CCX, WAPI ? */ info->parse_status = BCME_UNSUPPORTED; goto done; } info->auth_ie_type |= *type; /* mask down to uint8 for Windows build */ version = 0xff & ltoh16_ua(ptr_inc); if (version > MAX_RSNE_SUPPORTED_VERSION) { info->parse_status = BCME_UNSUPPORTED; goto done; } info->version = (uint8)version; *remaining = remain_len - version_len; done: return info->parse_status; } /* rsn info allocation management. * * In some cases, the rsn ie info structures are embedded in the scan results * which can be shared by different lists. * To keep track of their allocation, we use a reference counter. * The counter is incremented on demand by rsn_ie_info_add_ref() * at the time the reference is shared. * It is decremented in rsn_ie_info_rel_ref * When ref_count gets to 0, bcmwpa_rsn_ie_info_free_mem * is called to free the whole structure. */ /* free rsn_ie and wpa_ie, if any, and zero the rsn_info */ void bcmwpa_rsn_ie_info_reset(rsn_ie_info_t *rsn_info, osl_t *osh) { uint8 ref_count; if (rsn_info == NULL) { return; } ref_count = rsn_info->ref_count; MFREE(osh, rsn_info->rsn_ie, rsn_info->rsn_ie_len); MFREE(osh, rsn_info->wpa_ie, rsn_info->wpa_ie_len); MFREE(osh, rsn_info->rsnxe, rsn_info->rsnxe_len); bzero(rsn_info, sizeof(*rsn_info)); rsn_info->ref_count = ref_count; } static void bcmwpa_rsn_ie_info_free_mem(rsn_ie_info_t **rsn_info, osl_t *osh) { bcmwpa_rsn_ie_info_reset(*rsn_info, osh); MFREE(osh, *rsn_info, sizeof(**rsn_info)); *rsn_info = NULL; } void bcmwpa_rsn_ie_info_rel_ref(rsn_ie_info_t **rsn_info, osl_t *osh) { if (rsn_info == NULL || *rsn_info == NULL) { return; } /* already freed ? */ if ((*rsn_info)->ref_count == 0) { ASSERT(0); return; } /* decrement ref count */ (*rsn_info)->ref_count -= 1; /* clear reference. */ if ((*rsn_info)->ref_count > 0) { *rsn_info = NULL; return; } /* free memory and clear reference */ bcmwpa_rsn_ie_info_free_mem(rsn_info, osh); } int bcmwpa_rsn_ie_info_add_ref(rsn_ie_info_t *rsn_info) { int status = BCME_OK; if (rsn_info == NULL) { goto done; } if (rsn_info->ref_count == 0) { /* don't increase from 0, which means this structure has been freed earlier. * That reference should not exist anymore. */ ASSERT(0); status = BCME_BADARG; goto done; } rsn_info->ref_count++; done: return status; } #else /* Not RSN_IE_INFO_STRUCT_RELOCATED */ int bcmwpa_parse_rsnie(const bcm_tlv_t *ie, rsn_ie_info_t *info, device_type_t dev_type) { const uint8 *ptr_inc = NULL; const wpa_suite_ucast_t *ucast; const wpa_suite_mcast_t *mcast; const wpa_suite_auth_key_mgmt_t *mgmt; const wpa_pmkid_list_t *pmkid_list; uint32 remain_len = 0, i; ASSERT(info != NULL); /* this function might be called from place where there * is no error detection. * e.g. fron the iem callback. Store status here. */ info->parse_status = BCME_OK; if (!ie) { info->parse_status = BCME_BADARG; goto done; } /* For AP, do not zero this structure since there could be multiple * IEs. In that case, add to the existing * bits in field (ciphers, akms) as necessary. */ if (dev_type != DEV_AP) { bzero(info, sizeof(*info)); } else { /* if already created, check device type */ if (info->dev_type != DEV_NONE) { if (info->dev_type != DEV_AP) { info->parse_status = BCME_BADARG; goto done; } } } info->dev_type = dev_type; ptr_inc = ie->data; /* decode auth IE (WPA vs RSN). Fill in the auth_ie_type and version. * Modify remain_len to indicate the position of the pointer. */ /* NOTE the status field will be updated in this call */ if (bcmwpa_decode_ie_type(ie, info, &remain_len) != BCME_OK) { goto done; } if (!(remain_len)) { info->g_cipher = WPA_CIPHER_NONE; goto done; /* only have upto ver */ } ptr_inc += ie->len - remain_len; if (remain_len < sizeof(wpa_suite_mcast_t)) { info->parse_status = BCME_BADLEN; goto done; } mcast = (const wpa_suite_mcast_t *)ptr_inc; if (IS_WPA_CIPHER(mcast->type)) { info->g_cipher = mcast->type; } /* for rsn pairwise cipher suite */ ptr_inc += sizeof(wpa_suite_mcast_t); remain_len -= sizeof(wpa_suite_mcast_t); if (!(remain_len)) { goto done; } ucast = (const wpa_suite_ucast_t *)ptr_inc; if ((remain_len) < sizeof(ucast->count)) { info->parse_status = BCME_BADLEN; goto done; } if (!ucast->count.low && !ucast->count.high) { info->parse_status = BCME_BADLEN; goto done; } info->p_count = (uint8)ltoh16_ua(&ucast->count); if (dev_type == DEV_STA && info->p_count != 1) { info->parse_status = BCME_BADARG; goto done; } if ((remain_len) < (info->p_count * WPA_SUITE_LEN + sizeof(ucast->count))) { info->parse_status = BCME_BADLEN; goto done; } if (IS_WPA_CIPHER(ucast->list[0].type)) { /* update the pairwise cipher */ /* set cipher to invald value */ if (dev_type == DEV_STA) { info->sta_cipher = ucast->list[0].type; } else { for (i = 0; i < info->p_count; i++) { if (IS_WPA_CIPHER(ucast->list[i].type)) { info->p_ciphers |= BIT(ucast->list[i].type); } else { info->parse_status = BCME_BAD_IE_DATA; goto done; } } } } else { info->parse_status = BCME_BAD_IE_DATA; goto done; } /* for rsn AKM authentication */ ptr_inc += info->p_count * WPA_SUITE_LEN + sizeof(ucast->count); remain_len -= (info->p_count * WPA_SUITE_LEN + sizeof(ucast->count)); mgmt = (const wpa_suite_auth_key_mgmt_t *)ptr_inc; if (remain_len < sizeof(mgmt->count)) { info->parse_status = BCME_BADLEN; goto done; } info->akm_count = (uint8)ltoh16_ua(&mgmt->count); if (!info->akm_count) { info->parse_status = BCME_BADARG; goto done; } if (dev_type == DEV_STA && info->akm_count != 1) { info->parse_status = BCME_BADARG; goto done; } if ((remain_len) < (info->akm_count * WPA_SUITE_LEN + sizeof(mgmt->count))) { info->parse_status = BCME_BADLEN; goto done; } if (dev_type == DEV_STA) { info->sta_akm = mgmt->list[0].type; } for (i = 0; i < info->akm_count; i++) { if (bcmwpa_is_valid_akm(mgmt->list[i].type) == BCME_OK) { ASSERT((mgmt->list[i].type) < (sizeof(info->akms) * NBBY)); info->akms |= BIT(mgmt->list[i].type); } } /* RSN AKM/cipher suite related EAPOL key length update */ info->pmk_len = bcmwpa_eapol_key_length(EAPOL_KEY_PMK, info->sta_akm, 0); info->kck_mic_len = bcmwpa_eapol_key_length(EAPOL_KEY_KCK_MIC, info->sta_akm, 0); info->kck_len = bcmwpa_eapol_key_length(EAPOL_KEY_KCK, info->sta_akm, 0); info->kek_len = bcmwpa_eapol_key_length(EAPOL_KEY_KEK, info->sta_akm, 0); info->tk_len = bcmwpa_eapol_key_length(EAPOL_KEY_TK, 0, info->sta_cipher); info->ptk_len = info->kck_mic_len + info->kek_len + info->tk_len; #if defined(WL_FILS) && defined(WLFBT) info->kck2_len = bcmwpa_eapol_key_length(EAPOL_KEY_KCK2, info->sta_akm, 0); info->kek2_len = bcmwpa_eapol_key_length(EAPOL_KEY_KEK2, info->sta_akm, 0); #endif /* WL_FILS && WLFBT */ /* for rsn capabilities */ ptr_inc += info->akm_count * WPA_SUITE_LEN + sizeof(mgmt->count); remain_len -= info->akm_count * WPA_SUITE_LEN + sizeof(mgmt->count); /* as a STA, at this point, we can compute the key descriptor version */ if (dev_type == DEV_STA) { info->key_desc = wlc_calc_rsn_desc_version(info); } if (!(remain_len)) { goto done; } if (remain_len < RSN_CAP_LEN) { info->parse_status = BCME_BADLEN; goto done; } if (ie->id == DOT11_MNG_RSN_ID) { info->caps = ltoh16_ua(ptr_inc); } /* for WFA If MFP required, check that we are using a SHA256 AKM * or higher and nothing else. * In case MFP Required and MFP Capable do not enforce check of AKM. */ if ((info->caps & RSN_CAP_MFPR) && !(info->akms & (1u << RSN_AKM_PSK))) { if ((info->akms & (AKM_SHA256_MASK | AKM_SHA384_MASK)) == 0 || (info->akms & ~(AKM_SHA256_MASK | AKM_SHA384_MASK))) { info->parse_status = BCME_EPERM; goto done; } } /* check if AKMs require MFP capable to be set */ if ((info->akms & RSN_MFPC_AKM_MASK) && !(info->caps & RSN_CAP_MFPC)) { info->parse_status = BCME_EPERM; goto done; } /* for rsn PMKID */ ptr_inc += RSN_CAP_LEN; remain_len -= RSN_CAP_LEN; if (!(remain_len)) { goto done; } pmkid_list = (const wpa_pmkid_list_t*)ptr_inc; if ((remain_len) < sizeof(pmkid_list->count)) { info->parse_status = BCME_BADLEN; goto done; } info->pmkid_count = (uint8)ltoh16_ua(&pmkid_list->count); ptr_inc += sizeof(pmkid_list->count); remain_len -= sizeof(pmkid_list->count); if (info->pmkid_count) { if (remain_len < (uint32)(info->pmkid_count * WPA2_PMKID_LEN)) { info->parse_status = BCME_BADLEN; goto done; } info->pmkids_offset = ie->len + TLV_HDR_LEN - remain_len; /* for rsn group management cipher suite */ ptr_inc += info->pmkid_count * WPA2_PMKID_LEN; remain_len -= info->pmkid_count * WPA2_PMKID_LEN; } if (!(remain_len)) { goto done; } /* * from WPA2_Security_Improvements_Test_Plan_v1.0 * 4.2.4 APUT RSNE bounds verification using WPA2-PSK * May content RSNE extensibile element ay this point */ if (remain_len < sizeof(wpa_suite_mcast_t)) { info->parse_status = BCME_BADLEN; goto done; } mcast = (const wpa_suite_mcast_t *)ptr_inc; if (IS_VALID_BIP_CIPHER((rsn_cipher_t)mcast->type)) { info->g_mgmt_cipher = (rsn_cipher_t)mcast->type; } done: return info->parse_status; } /* Determine if the IE is of WPA or RSN type. Decode * up to version field. Modify the remaining len parameter to * indicate where the next field is. * Store and return error status. */ int bcmwpa_decode_ie_type(const bcm_tlv_t *ie, rsn_ie_info_t *info, uint32 *remaining) { const uint8 * ptr_inc = (const uint8 *)ie->data; uint32 remain_len = ie->len; uint8 version, version_len; if (ie->id == DOT11_MNG_WPA_ID) { /* min len check */ if (remain_len < WPA_IE_FIXED_LEN) { info->parse_status = BCME_BADLEN; goto done; } /* WPA IE */ if (memcmp(WPA_OUI, ie->data, WPA_OUI_LEN)) { /* bad OUI */ info->parse_status = BCME_BADARG; goto done; } ptr_inc += WPA_OUI_LEN; if (*ptr_inc != WPA_OUI_TYPE) { /* wrong type */ info->parse_status = BCME_BADARG; goto done; } ptr_inc ++; remain_len -= WPA_OUI_LEN + 1u; info->auth_ie_type |= WPA_AUTH_IE; version_len = WPA_VERSION_LEN; } else if (ie->id == DOT11_MNG_RSN_ID) { if (remain_len < WPA2_VERSION_LEN) { info->parse_status = BCME_BADLEN; goto done; } /* RSN IE */ info->auth_ie_type |= RSN_AUTH_IE; version_len = WPA2_VERSION_LEN; } else { /* TODO : add support for CCX, WAPI ? */ info->parse_status = BCME_UNSUPPORTED; goto done; } /* mask down to uint8 for Windows build */ version = 0xff & ltoh16_ua(ptr_inc); if (version > MAX_RSNE_SUPPORTED_VERSION) { info->parse_status = BCME_UNSUPPORTED; goto done; } info->version = (uint8)version; *remaining = remain_len - version_len; done: return info->parse_status; } #endif /* RSN_IE_INFO_STRUCT_RELOCATED */ /* return the key descriptor version based on the AKM suite * applicable only for STA with RSN */ static uint16 wlc_calc_rsn_desc_version(const rsn_ie_info_t *rsn_info) { uint16 key_desc_ver = WPA_KEY_DESC_V0; uint8 akm; ASSERT(rsn_info != NULL); ASSERT(rsn_info->dev_type == DEV_STA); akm = rsn_info->sta_akm; /* Refer Draft 802.11REVmd_D1.0.pdf Section 12.7.2 */ if ((akm == RSN_AKM_UNSPECIFIED) || (akm == RSN_AKM_PSK)) { if ((rsn_info->sta_cipher == WPA_CIPHER_TKIP) || (rsn_info->sta_cipher == WPA_CIPHER_NONE)) { key_desc_ver = WPA_KEY_DESC_V1; } else if ((rsn_info->sta_cipher != WPA_CIPHER_TKIP) || (rsn_info->g_cipher != WPA_CIPHER_TKIP)) { key_desc_ver = WPA_KEY_DESC_V2; } } else if ((akm == RSN_AKM_FBT_1X) || (akm == RSN_AKM_FBT_PSK) || (akm == RSN_AKM_SHA256_1X) || (akm == RSN_AKM_SHA256_PSK)) { key_desc_ver = WPA_KEY_DESC_V3; } return key_desc_ver; } /* get EAPOL key length based on RSN IE AKM/Cipher(unicast) suite * key: EAPOL key type * akm: RSN AKM suite selector * cipher: RSN unicast cipher suite selector * return: key length found in matching key_length_entry table */ uint8 bcmwpa_eapol_key_length(eapol_key_type_t key, rsn_akm_t akm, rsn_cipher_t cipher) { uint i; uint8 key_length = 0; uint8 suite; const key_length_entry_t *key_entry = NULL; if (key == EAPOL_KEY_TK) { suite = cipher; } else { suite = akm; } for (i = 0; i < ARRAYSIZE(eapol_key_lookup_tbl); i++) { if (eapol_key_lookup_tbl[i].key == key) { key_entry = eapol_key_lookup_tbl[i].key_entry; break; } } if (key_entry) { i = 0; do { if (key_entry[i].suite == suite || key_entry[i].suite == 0) { key_length = key_entry[i].len; break; } i++; } while (i > 0); } return key_length; } /* check if RSM AKM suite is valid */ static int bcmwpa_is_valid_akm(const rsn_akm_t akm) { uint i = 0; for (i = 0; i < ARRAYSIZE(rsn_akm_lookup_tbl); i++) { if (akm == rsn_akm_lookup_tbl[i].rsn_akm) { return BCME_OK; } } return BCME_ERROR; } /* checking cipher suite selector restriction based on AKM */ int bcmwpa_rsn_akm_cipher_match(rsn_ie_info_t *rsn_info) { uint i; const rsn_akm_cipher_match_entry_t *p_entry = NULL; for (i = 0; i < ARRAYSIZE(rsn_akm_cipher_match_table); i++) { /* akm match */ if (rsn_info->sta_akm == rsn_akm_cipher_match_table[i].akm_type) { p_entry = &rsn_akm_cipher_match_table[i]; break; } } if (p_entry) { /* unicast cipher match */ if (!(rsn_info->p_ciphers & p_entry->u_cast)) { return BCME_UNSUPPORTED; } /* multicast cipher match */ if (!(BCM_BIT(rsn_info->g_cipher) & p_entry->m_cast)) { return BCME_UNSUPPORTED; } /* group management cipher match */ if (!(BCM_BIT(rsn_info->g_mgmt_cipher) & p_entry->g_mgmt)) { return BCME_UNSUPPORTED; } } return BCME_OK; } #if defined(BCMSUP_PSK) || defined(BCMSUPPL) uint8 bcmwpa_find_group_mgmt_algo(rsn_cipher_t g_mgmt_cipher) { uint8 i; uint8 algo = CRYPTO_ALGO_BIP; for (i = 0; i < ARRAYSIZE(group_mgmt_cipher_algo); i++) { if ((group_mgmt_cipher_algo[i].g_mgmt_cipher == g_mgmt_cipher)) { algo = group_mgmt_cipher_algo[i].bip_algo; break; } } return algo; } #endif /* defined(BCMSUP_PSK) || defined(BCMSUPPL) */ #if defined(WL_BAND6G) bool bcmwpa_is_invalid_6g_akm(const rsn_akm_mask_t akms_bmp) { if (akms_bmp & rsn_akm_6g_inval_mask) { return TRUE; } return FALSE; } bool bcmwpa_is_invalid_6g_cipher(const rsn_ciphers_t ciphers_bmp) { if (ciphers_bmp & cipher_6g_inval_mask) { return TRUE; } return FALSE; } #endif /* WL_BAND6G */ /* * bcmwpa_get_algo_key_len returns the key_length for the algorithm. * API : bcm_get_algorithm key length * input: algo: Get the crypto algorithm. * km: Keymgmt information. * output: returns the key length and error status. * BCME_OK is valid else BCME_UNSUPPORTED if not supported */ int bcmwpa_get_algo_key_len(uint8 algo, uint16 *key_len) { int err = BCME_OK; if (key_len == NULL) { return BCME_BADARG; } switch (algo) { case CRYPTO_ALGO_WEP1: *key_len = WEP1_KEY_SIZE; break; case CRYPTO_ALGO_TKIP: *key_len = TKIP_KEY_SIZE; break; case CRYPTO_ALGO_WEP128: *key_len = WEP128_KEY_SIZE; break; case CRYPTO_ALGO_AES_CCM: /* fall through */ case CRYPTO_ALGO_AES_GCM: /* fall through */ case CRYPTO_ALGO_AES_OCB_MSDU : /* fall through */ case CRYPTO_ALGO_AES_OCB_MPDU: *key_len = AES_KEY_SIZE; break; #ifdef BCMWAPI_WPI /* TODO: Need to double check */ case CRYPTO_ALGO_SMS4: *key_len = SMS4_KEY_LEN + SMS4_WPI_CBC_MAC_LEN; break; #endif /* BCMWAPI_WPI */ case CRYPTO_ALGO_BIP: /* fall through */ case CRYPTO_ALGO_BIP_GMAC: *key_len = BIP_KEY_SIZE; break; case CRYPTO_ALGO_AES_CCM256: /* fall through */ case CRYPTO_ALGO_AES_GCM256: /* fall through */ case CRYPTO_ALGO_BIP_CMAC256: /* fall through */ case CRYPTO_ALGO_BIP_GMAC256: *key_len = AES256_KEY_SIZE; break; case CRYPTO_ALGO_OFF: *key_len = 0; break; #if !defined(BCMCCX) && !defined(BCMEXTCCX) case CRYPTO_ALGO_NALG: /* fall through */ #else case CRYPTO_ALGO_CKIP: /* fall through */ case CRYPTO_ALGO_CKIP_MMH: /* fall through */ case CRYPTO_ALGO_WEP_MMH: /* fall through */ case CRYPTO_ALGO_PMK: /* fall through default */ #endif /* !defined(BCMCCX) && !defined(BCMEXTCCX) */ default: *key_len = 0; err = BCME_UNSUPPORTED; break; } return err; }