| /** @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; |
| } |