blob: 4221b4ca5261e81df15de2a6489a783774446e45 [file] [log] [blame]
/** @file keyMgmtAp_rom.c
*
* @brief This file defines api for key managment
*
* Copyright (C) 2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
* (the "License"). You may use, redistribute and/or modify this File in
* accordance with the terms and conditions of the License, a copy of which
* is available by writing to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
* worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
*/
/******************************************************
Change log:
03/07/2014: Initial version
******************************************************/
//Authenticator related function definitions
#include "wltypes.h"
#include "IEEE_types.h"
#include "hostsa_ext_def.h"
#include "authenticator.h"
#include "keyMgmtAp_rom.h"
#include "crypt_new_rom.h"
#include "keyCommonDef.h"
#include "pmkCache_rom.h"
#include "crypt_new_rom.h"
#include "rc4_rom.h"
#include "aes_cmac_rom.h"
#include "sha1.h"
#include "md5.h"
#include "mrvl_sha256_crypto.h"
UINT32
util_FindLowestBitSet(UINT32 val)
{
UINT32 bitmap = 1;
while (bitmap && (!(bitmap & val))) {
bitmap <<= 1;
}
return bitmap;
}
UINT8
convertMrvlAuthToIEEEAuth(UINT32 mrvlauth)
{
UINT8 auth;
switch (mrvlauth) {
case UAP_HOSTCMD_KEYMGMT_EAP:
auth = IEEEtypes_RSN_AUTH_KEY_SUITE_8021X;
break;
case UAP_HOSTCMD_KEYMGMT_PSK:
auth = IEEEtypes_RSN_AUTH_KEY_SUITE_PSK;
break;
case UAP_HOSTCMD_KEYMGMT_NONE:
default:
auth = IEEEtypes_RSN_AUTH_KEY_SUITE_RSVD;
break;
}
return auth;
}
UINT32
convertIEEEAuthToMrvlAuth(UINT8 auth)
{
UINT32 MrvlAuth = 0;
switch (auth) {
case IEEEtypes_RSN_AUTH_KEY_SUITE_8021X:
MrvlAuth |= UAP_HOSTCMD_KEYMGMT_EAP;
break;
case IEEEtypes_RSN_AUTH_KEY_SUITE_PSK:
MrvlAuth |= UAP_HOSTCMD_KEYMGMT_PSK;
break;
case IEEEtypes_RSN_AUTH_KEY_SUITE_RSVD:
default:
MrvlAuth = 0;
break;
}
return MrvlAuth;
}
UINT8
convertMrvlCipherToIEEECipher(UINT8 mrvlcipher)
{
UINT8 Cipher;
switch (mrvlcipher) {
case UAP_HOSTCMD_CIPHER_WEP40:
Cipher = IEEEtypes_RSN_CIPHER_SUITE_WEP40;
break;
case UAP_HOSTCMD_CIPHER_WEP104:
Cipher = IEEEtypes_RSN_CIPHER_SUITE_WEP104;
break;
case UAP_HOSTCMD_CIPHER_TKIP:
Cipher = IEEEtypes_RSN_CIPHER_SUITE_TKIP;
break;
case UAP_HOSTCMD_CIPHER_CCMP:
Cipher = IEEEtypes_RSN_CIPHER_SUITE_CCMP;
break;
default:
Cipher = IEEEtypes_RSN_CIPHER_SUITE_NONE;
break;
}
return Cipher;
}
UINT32
convertIEEECipherToMrvlCipher(UINT8 cipher)
{
UINT32 MrvlCipher = 0;
switch (cipher) {
case IEEEtypes_RSN_CIPHER_SUITE_WEP40:
MrvlCipher |= UAP_HOSTCMD_CIPHER_WEP40;
break;
case IEEEtypes_RSN_CIPHER_SUITE_TKIP:
MrvlCipher |= UAP_HOSTCMD_CIPHER_TKIP;
break;
case IEEEtypes_RSN_CIPHER_SUITE_CCMP:
MrvlCipher |= UAP_HOSTCMD_CIPHER_CCMP;
break;
case IEEEtypes_RSN_CIPHER_SUITE_WEP104:
MrvlCipher |= UAP_HOSTCMD_CIPHER_WEP104;
break;
case IEEEtypes_RSN_CIPHER_SUITE_NONE:
case IEEEtypes_RSN_CIPHER_SUITE_WRAP:
default:
MrvlCipher = 0;
break;
}
return MrvlCipher;
}
void
GenerateGTK_internal(hostsa_private *priv, KeyData_t *grpKeyData,
UINT8 *nonce, UINT8 *StaMacAddr)
{
hostsa_util_fns *util_fns = &priv->util_fns;
UINT8 inp_data[NONCE_SIZE + sizeof(IEEEtypes_MacAddr_t)];
UINT8 prefix[] = "Group key expansion";
UINT8 GTK[32]; // group transient key
UINT8 grpMasterKey[32];
if (!grpKeyData || !nonce) {
return;
}
memcpy(util_fns, inp_data, StaMacAddr, sizeof(IEEEtypes_MacAddr_t));
supplicantGenerateRand(priv, nonce, NONCE_SIZE);
memcpy(util_fns, inp_data + sizeof(IEEEtypes_MacAddr_t), nonce,
NONCE_SIZE);
supplicantGenerateRand(priv, grpMasterKey, sizeof(grpMasterKey));
Mrvl_PRF((void *)priv, grpMasterKey,
sizeof(grpMasterKey),
prefix,
wlan_strlen((char *)prefix),
inp_data, sizeof(inp_data), GTK, sizeof(GTK));
memcpy(util_fns, grpKeyData->Key, GTK, TK_SIZE);
memcpy(util_fns, grpKeyData->TxMICKey, GTK + TK_SIZE, MIC_KEY_SIZE);
memcpy(util_fns, grpKeyData->RxMICKey, GTK + TK_SIZE + MIC_KEY_SIZE,
MIC_KEY_SIZE);
}
/*
Populates EAPOL frame based on Cipher, given Nonce, replay counters,
and type, which encodes whether this is secure, part of WPA2 or WPA
handshake.
This is currently used for EAPOL sent from AP, msg1, msg3 and group
key msg.
*/
void
PopulateKeyMsg(hostsa_private *priv, EAPOL_KeyMsg_Tx_t *tx_eapol_ptr,
Cipher_t *Cipher,
UINT16 Type, UINT32 replay_cnt[2], UINT8 *Nonce)
{
hostsa_util_fns *util_fns = &priv->util_fns;
key_info_t *pKeyInfo;
if (!tx_eapol_ptr || !Cipher) {
return;
}
pKeyInfo = &tx_eapol_ptr->keyMsg.key_info;
if (Cipher->tkip) {
// TKIP unicast
tx_eapol_ptr->keyMsg.key_length =
SHORT_SWAP((TK_SIZE + TK_SIZE));
} else if (Cipher->ccmp) {
// CCMP unicast
tx_eapol_ptr->keyMsg.key_length = SHORT_SWAP(TK_SIZE);
}
pKeyInfo->KeyAck = 1;
if (Type & PAIRWISE_KEY_MSG) {
pKeyInfo->KeyType = 1;
if (Type & SECURE_HANDSHAKE_FLAG) {
pKeyInfo->KeyMIC = 1;
pKeyInfo->Install = 1;
pKeyInfo->EncryptedKeyData = pKeyInfo->Secure =
(Type & WPA2_HANDSHAKE) ? 1 : 0;
}
} else {
pKeyInfo->Secure = 1;
pKeyInfo->KeyMIC = 1;
pKeyInfo->EncryptedKeyData = (Type & WPA2_HANDSHAKE) ? 1 : 0;
}
tx_eapol_ptr->keyMsg.replay_cnt[0] = WORD_SWAP(replay_cnt[0]);
tx_eapol_ptr->keyMsg.replay_cnt[1] = WORD_SWAP(replay_cnt[1]);
memcpy(util_fns, (void *)tx_eapol_ptr->keyMsg.key_nonce, Nonce,
NONCE_SIZE);
DBG_HEXDUMP(MCMD_D, " nonce ",
(t_u8 *)tx_eapol_ptr->keyMsg.key_nonce, NONCE_SIZE);
}
/*
Function to prepare KDE in EAPOL frame .
Used by the AP to encapsulate GTK
*/
void
prepareKDE(hostsa_private *priv, EAPOL_KeyMsg_Tx_t *tx_eapol_ptr,
KeyData_t *grKey, Cipher_t *cipher)
{
hostsa_util_fns *util_fns = &priv->util_fns;
KDE_t *pKeyDataWPA2;
GTK_KDE_t *pGTK_IE;
UINT8 RsnIE_len = 0, PadLen = 0;
UINT8 *buf_p;
if (!tx_eapol_ptr || !grKey || !cipher) {
return;
}
RsnIE_len = tx_eapol_ptr->keyMsg.key_material_len;
buf_p = (UINT8 *)(tx_eapol_ptr->keyMsg.key_data + RsnIE_len);
pKeyDataWPA2 = (KDE_t *)buf_p;
pKeyDataWPA2->type = 0xdd;
pKeyDataWPA2->length = KEYDATA_SIZE;
pKeyDataWPA2->OUI[0] = kde_oui[0];
pKeyDataWPA2->OUI[1] = kde_oui[1];
pKeyDataWPA2->OUI[2] = kde_oui[2];
pKeyDataWPA2->dataType = 1;
buf_p = buf_p + KDE_SIZE;
pGTK_IE = (GTK_KDE_t *)buf_p;
pGTK_IE->KeyID = 1;
buf_p = buf_p + GTK_IE_SIZE;
// copy over GTK
memcpy(util_fns, (void *)buf_p, (void *)grKey->Key, TK_SIZE);
buf_p = buf_p + TK_SIZE;
if (cipher->tkip) {
pKeyDataWPA2->length += (MIC_KEY_SIZE + MIC_KEY_SIZE);
memcpy(util_fns, (void *)buf_p, (void *)grKey->TxMICKey,
MIC_KEY_SIZE);
buf_p = buf_p + MIC_KEY_SIZE;
memcpy(util_fns, (void *)buf_p, (void *)grKey->RxMICKey,
MIC_KEY_SIZE);
buf_p = buf_p + MIC_KEY_SIZE;
}
tx_eapol_ptr->keyMsg.key_material_len += (pKeyDataWPA2->length
+ KDE_IE_SIZE);
PadLen = ((8 - ((tx_eapol_ptr->keyMsg.key_material_len) % 8)) % 8);
if (PadLen) {
*(buf_p) = 0xdd;
memset(util_fns, (void *)(buf_p + 1), 0, PadLen - 1);
tx_eapol_ptr->keyMsg.key_material_len += PadLen;
}
}
BOOLEAN
Encrypt_keyData(hostsa_private *priv, EAPOL_KeyMsg_Tx_t *tx_eapol_ptr,
UINT8 *EAPOL_Encr_Key, Cipher_t *cipher)
{
hostsa_util_fns *util_fns = &priv->util_fns;
UINT8 key[16];
UINT8 cipherText[400] = { 0 };
if (!tx_eapol_ptr || !EAPOL_Encr_Key || !cipher) {
return FALSE;
}
if (cipher->ccmp) {
// Pairwise is CCMP
memcpy(util_fns, (void *)key, EAPOL_Encr_Key, 16);
// use AES-only mode from AEU HW to perform AES wrap
MRVL_AesWrap(key, 2, tx_eapol_ptr->keyMsg.key_material_len / 8,
tx_eapol_ptr->keyMsg.key_data, NULL, cipherText);
tx_eapol_ptr->keyMsg.key_material_len += 8;
memcpy(util_fns, (void *)tx_eapol_ptr->keyMsg.key_data,
cipherText, tx_eapol_ptr->keyMsg.key_material_len);
} else if (cipher->tkip) {
// Pairwise is TKIP
supplicantGenerateRand(priv,
(UINT8 *)tx_eapol_ptr->keyMsg.
EAPOL_key_IV, 16);
RC4_Encrypt((t_void *)priv, (unsigned char *)EAPOL_Encr_Key,
(unsigned char *)&tx_eapol_ptr->keyMsg.EAPOL_key_IV,
16, (unsigned char *)&tx_eapol_ptr->keyMsg.key_data,
(unsigned short)tx_eapol_ptr->keyMsg.
key_material_len, 256);
} else {
return FALSE;
}
return TRUE;
}
void
KeyMgmtAp_DerivePTK(hostsa_private *priv,
UINT8 *pPMK,
t_u8 *da,
t_u8 *sa,
UINT8 *ANonce,
UINT8 *SNonce,
UINT8 *EAPOL_MIC_Key,
UINT8 *EAPOL_Encr_Key, KeyData_t *newPWKey, BOOLEAN use_kdf)
{
hostsa_util_fns *util_fns = &priv->util_fns;
UINT8 tmp[MIC_KEY_SIZE];
// call STA PTK generation funciton first
KeyMgmtSta_DeriveKeys(priv, pPMK,
da,
sa,
ANonce,
SNonce,
EAPOL_MIC_Key, EAPOL_Encr_Key, newPWKey, use_kdf);
// We need to swap Rx/Tx Keys for AP
memcpy(util_fns, tmp, newPWKey->RxMICKey, MIC_KEY_SIZE);
memcpy(util_fns, newPWKey->RxMICKey, newPWKey->TxMICKey, MIC_KEY_SIZE);
memcpy(util_fns, newPWKey->TxMICKey, tmp, MIC_KEY_SIZE);
}
BOOLEAN
KeyData_CopyWPAWP2(hostsa_private *priv, EAPOL_KeyMsg_Tx_t *pTxEAPOL, void *pIe)
{
hostsa_util_fns *util_fns = &priv->util_fns;
IEEEtypes_InfoElementHdr_t *pElement =
(IEEEtypes_InfoElementHdr_t *)pIe;
if (!pIe) {
return FALSE;
}
pTxEAPOL->keyMsg.key_material_len =
pElement->Len + sizeof(IEEEtypes_InfoElementHdr_t);
memcpy(util_fns, (void *)pTxEAPOL->keyMsg.key_data,
pIe, pTxEAPOL->keyMsg.key_material_len);
return TRUE;
}
BOOLEAN
KeyData_UpdateKeyMaterial(hostsa_private *priv, EAPOL_KeyMsg_Tx_t *pTxEAPOL,
SecurityMode_t *pSecType, void *pWPA, void *pWPA2)
{
if (pSecType->wpa || pSecType->wpaNone) {
if (KeyData_CopyWPAWP2(priv, pTxEAPOL, pWPA) == FALSE) {
return FALSE;
}
} else if (pSecType->wpa2) {
if (KeyData_CopyWPAWP2(priv, pTxEAPOL, pWPA2) == FALSE) {
return FALSE;
}
}
return TRUE;
}
void
KeyData_AddGTK(hostsa_private *priv, EAPOL_KeyMsg_Tx_t *pTxEAPOL,
KeyData_t *grKey, Cipher_t *cipher)
{
hostsa_util_fns *util_fns = &priv->util_fns;
UINT8 *buf_p;
buf_p = (UINT8 *)pTxEAPOL->keyMsg.key_data;
memcpy(util_fns, (void *)buf_p, (void *)grKey, TK_SIZE);
buf_p = buf_p + TK_SIZE;
pTxEAPOL->keyMsg.key_material_len += TK_SIZE;
if (cipher->tkip) {
memcpy(util_fns, (void *)buf_p, (void *)grKey->TxMICKey,
MIC_KEY_SIZE);
buf_p = buf_p + MIC_KEY_SIZE;
memcpy(util_fns, (void *)buf_p, (void *)grKey->RxMICKey,
MIC_KEY_SIZE);
pTxEAPOL->keyMsg.key_material_len += (MIC_KEY_SIZE +
MIC_KEY_SIZE);
}
}
/* Returns FALSE if security type is other than WPA* */
BOOLEAN
KeyData_AddKey(hostsa_private *priv, EAPOL_KeyMsg_Tx_t *pTxEAPOL,
SecurityMode_t *pSecType, KeyData_t *grKey, Cipher_t *cipher)
{
hostsa_util_fns *util_fns = &priv->util_fns;
BOOLEAN status = FALSE;
pTxEAPOL->keyMsg.key_info.KeyIndex = grKey->KeyIndex;
pTxEAPOL->keyMsg.key_RSC[0] = grKey->TxIV16 & 0x00FF;
pTxEAPOL->keyMsg.key_RSC[1] = (grKey->TxIV16 >> 8) & 0x00FF;
memcpy(util_fns, (void *)(pTxEAPOL->keyMsg.key_RSC + 2),
&grKey->TxIV32, 4);
if (pSecType->wpa || pSecType->wpaNone) {
KeyData_AddGTK(priv, pTxEAPOL, grKey, cipher);
status = TRUE;
} else if (pSecType->wpa2) {
prepareKDE(priv, pTxEAPOL, grKey, cipher);
status = TRUE;
}
return status;
}
void
ROM_InitGTK(hostsa_private *priv, KeyData_t *grpKeyData, UINT8 *nonce,
UINT8 *StaMacAddr)
{
grpKeyData->KeyIndex = 1;
grpKeyData->TxIV16 = 1;
grpKeyData->TxIV32 = 0;
GenerateGTK_internal(priv, grpKeyData, nonce, StaMacAddr);
}
t_u16
keyMgmtAp_FormatWPARSN_IE_internal(phostsa_private priv,
IEEEtypes_InfoElementHdr_t *pIe,
UINT8 isRsn,
Cipher_t *pCipher,
UINT8 cipherCnt,
Cipher_t *pMcastCipher,
UINT16 authKey, UINT16 authKeyCnt)
{
phostsa_util_fns util_fns = &priv->util_fns;
int i;
UINT8 *pBuf = NULL;
IEEEtypes_RSNElement_t *pRsn = (IEEEtypes_RSNElement_t *)pIe;
IEEEtypes_WPAElement_t *pWpa = (IEEEtypes_WPAElement_t *)pIe;
UINT16 bitPos = 0;
UINT16 authKeyBitmap = authKey;
UINT8 ucastBitmap = *((UINT8 *)pCipher);
UINT8 mcastBitmap = *((UINT8 *)pMcastCipher);
UINT8 oui[3];
UINT16 ieLength = 0;
pIe->ElementId = (isRsn == 1) ? ELEM_ID_RSN : ELEM_ID_VENDOR_SPECIFIC;
if (isRsn) {
memcpy(util_fns, (void *)&oui, (void *)&kde_oui, sizeof(oui));
pBuf = (UINT8 *)&pRsn->Ver;
} else {
memcpy(util_fns, (void *)&oui, (void *)&wpa_oui01, sizeof(oui));
memcpy(util_fns, (void *)&pWpa->OuiType, (void *)&wpa_oui01,
sizeof(wpa_oui01));
pBuf = (UINT8 *)&pWpa->Ver;
}
pBuf[0] = 0x1;
pBuf[1] = 0x0;
pBuf += 2;
// filling group cipher
memcpy(util_fns, (void *)pBuf, (void *)&oui, sizeof(oui));
if (mcastBitmap) {
bitPos = util_FindLowestBitSet(mcastBitmap);
}
pBuf[3] = convertMrvlCipherToIEEECipher(bitPos);
pBuf += 4;
pBuf[0] = (cipherCnt >> 0) & 0xFF;
pBuf[1] = (cipherCnt >> 16) & 0xFF;
pBuf += 2;
for (i = 0; i < cipherCnt; i++) {
pBuf[0] = oui[0];
pBuf[1] = oui[1];
pBuf[2] = oui[2];
bitPos = util_FindLowestBitSet(ucastBitmap);
pBuf[3] = convertMrvlCipherToIEEECipher(bitPos);
ucastBitmap &= ~bitPos;
pBuf += 4;
}
pBuf[0] = (authKeyCnt >> 0) & 0xFF;
pBuf[1] = (authKeyCnt >> 16) & 0xFF;
pBuf += 2;
for (i = 0; i < authKeyCnt; i++) {
pBuf[0] = oui[0];
pBuf[1] = oui[1];
pBuf[2] = oui[2];
bitPos = util_FindLowestBitSet(authKeyBitmap);
pBuf[3] = convertMrvlAuthToIEEEAuth(bitPos);
authKeyBitmap &= ~bitPos;
pBuf += 4;
}
if (isRsn) {
pBuf[0] = 0x0;
pBuf[1] = 0x0;
pBuf += 2;
}
ieLength = (unsigned long)pBuf - (unsigned long)pIe;
pIe->Len = ieLength - sizeof(IEEEtypes_InfoElementHdr_t);
DBG_HEXDUMP(MCMD_D, "RSN or WPA IE", (t_u8 *)pIe, ieLength);
return ieLength;
}
/* Ideally one day this function should re-use client code */
t_u16
keyMgmtAp_FormatWPARSN_IE(hostsa_private *priv,
IEEEtypes_InfoElementHdr_t *pIe,
UINT8 isRsn,
Cipher_t *pCipher,
UINT8 cipherCount,
Cipher_t *pMcastCipher,
UINT16 authKey, UINT16 authKeyCount)
{
UINT16 ieLength;
ieLength = keyMgmtAp_FormatWPARSN_IE_internal(priv, pIe,
isRsn,
pCipher,
cipherCount,
pMcastCipher,
authKey, authKeyCount);
return ieLength;
}