/** @file keyMgmtAp_rom.c * * @brief This file defines api for key managment * * Copyright (C) 2014-2017, 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; }