blob: 62738c119a9bba5c5ca4d36bc6ffad18817990ea [file] [log] [blame]
/*
* 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.
*
*
* <<Broadcom-WL-IPTag/Proprietary:>>
*/
/* include wl driver config file if this file is compiled for driver */
#ifdef BCMDRIVER
#include <osl.h>
/* HACK: this case for external supplicant use */
#else
#include <string.h>
#if defined(BCMEXTSUP)
#include <bcm_osl.h>
#else
#ifndef ASSERT
#define ASSERT(exp)
#endif
#endif /* BCMEXTSUP */
#endif /* BCMDRIVER */
#include <ethernet.h>
#include <eapol.h>
#include <802.11.h>
#include <wpa.h>
#include <802.11r.h>
#include <bcmutils.h>
#include <bcmendian.h>
#include <bcmwpa.h>
#include <aeskeywrap.h>
#include <bcmstdlib_s.h>
#include <wlioctl.h>
#include <bcmutils.h>
#include <bcmwpa.h>
#ifdef WL_OCV
#include <bcm_ocv.h>
#endif /* WL_OCV */
#if defined(BCMSUP_PSK) || defined(WLFBT) || defined(BCMAUTH_PSK) || \
defined(WL_OKC) || defined(WLTDLS) || defined(GTKOE) || defined(WLHOSTFBT)
#ifdef WLHOSTFBT
#include <string.h>
#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 <rc4.h>
/* 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;
}