| /* |
| ************************************************************************* |
| * Ralink Tech Inc. |
| * 5F., No.36, Taiyuan St., Jhubei City, |
| * Hsinchu County 302, |
| * Taiwan, R.O.C. |
| * |
| * (c) Copyright 2002-2007, Ralink Technology, Inc. |
| * |
| * This program is free software; you can redistribute it and/or modify * |
| * it under the terms of the GNU General Public License as published by * |
| * the Free Software Foundation; either version 2 of the License, or * |
| * (at your option) any later version. * |
| * * |
| * This program is distributed in the hope that it will be useful, * |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of * |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
| * GNU General Public License for more details. * |
| * * |
| * You should have received a copy of the GNU General Public License * |
| * along with this program; if not, write to the * |
| * Free Software Foundation, Inc., * |
| * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * |
| * * |
| ************************************************************************* |
| |
| Module Name: |
| wpa.c |
| |
| Abstract: |
| |
| Revision History: |
| Who When What |
| -------- ---------- ---------------------------------------------- |
| Jan Lee 03-07-22 Initial |
| Paul Lin 03-11-28 Modify for supplicant |
| */ |
| #include "../rt_config.h" |
| // WPA OUI |
| UCHAR OUI_WPA_NONE_AKM[4] = {0x00, 0x50, 0xF2, 0x00}; |
| UCHAR OUI_WPA_VERSION[4] = {0x00, 0x50, 0xF2, 0x01}; |
| UCHAR OUI_WPA_TKIP[4] = {0x00, 0x50, 0xF2, 0x02}; |
| UCHAR OUI_WPA_CCMP[4] = {0x00, 0x50, 0xF2, 0x04}; |
| UCHAR OUI_WPA_8021X_AKM[4] = {0x00, 0x50, 0xF2, 0x01}; |
| UCHAR OUI_WPA_PSK_AKM[4] = {0x00, 0x50, 0xF2, 0x02}; |
| // WPA2 OUI |
| UCHAR OUI_WPA2_WEP40[4] = {0x00, 0x0F, 0xAC, 0x01}; |
| UCHAR OUI_WPA2_TKIP[4] = {0x00, 0x0F, 0xAC, 0x02}; |
| UCHAR OUI_WPA2_CCMP[4] = {0x00, 0x0F, 0xAC, 0x04}; |
| UCHAR OUI_WPA2_8021X_AKM[4] = {0x00, 0x0F, 0xAC, 0x01}; |
| UCHAR OUI_WPA2_PSK_AKM[4] = {0x00, 0x0F, 0xAC, 0x02}; |
| // MSA OUI |
| UCHAR OUI_MSA_8021X_AKM[4] = {0x00, 0x0F, 0xAC, 0x05}; // Not yet final - IEEE 802.11s-D1.06 |
| UCHAR OUI_MSA_PSK_AKM[4] = {0x00, 0x0F, 0xAC, 0x06}; // Not yet final - IEEE 802.11s-D1.06 |
| |
| /* |
| ======================================================================== |
| |
| Routine Description: |
| The pseudo-random function(PRF) that hashes various inputs to |
| derive a pseudo-random value. To add liveness to the pseudo-random |
| value, a nonce should be one of the inputs. |
| |
| It is used to generate PTK, GTK or some specific random value. |
| |
| Arguments: |
| UCHAR *key, - the key material for HMAC_SHA1 use |
| INT key_len - the length of key |
| UCHAR *prefix - a prefix label |
| INT prefix_len - the length of the label |
| UCHAR *data - a specific data with variable length |
| INT data_len - the length of a specific data |
| INT len - the output lenght |
| |
| Return Value: |
| UCHAR *output - the calculated result |
| |
| Note: |
| 802.11i-2004 Annex H.3 |
| |
| ======================================================================== |
| */ |
| VOID PRF( |
| IN UCHAR *key, |
| IN INT key_len, |
| IN UCHAR *prefix, |
| IN INT prefix_len, |
| IN UCHAR *data, |
| IN INT data_len, |
| OUT UCHAR *output, |
| IN INT len) |
| { |
| INT i; |
| UCHAR *input; |
| INT currentindex = 0; |
| INT total_len; |
| |
| // Allocate memory for input |
| os_alloc_mem(NULL, (PUCHAR *)&input, 1024); |
| |
| if (input == NULL) |
| { |
| DBGPRINT(RT_DEBUG_ERROR, ("!!!PRF: no memory!!!\n")); |
| return; |
| } |
| |
| // Generate concatenation input |
| NdisMoveMemory(input, prefix, prefix_len); |
| |
| // Concatenate a single octet containing 0 |
| input[prefix_len] = 0; |
| |
| // Concatenate specific data |
| NdisMoveMemory(&input[prefix_len + 1], data, data_len); |
| total_len = prefix_len + 1 + data_len; |
| |
| // Concatenate a single octet containing 0 |
| // This octet shall be update later |
| input[total_len] = 0; |
| total_len++; |
| |
| // Iterate to calculate the result by hmac-sha-1 |
| // Then concatenate to last result |
| for (i = 0; i < (len + 19) / 20; i++) |
| { |
| HMAC_SHA1(input, total_len, key, key_len, &output[currentindex]); |
| currentindex += 20; |
| |
| // update the last octet |
| input[total_len - 1]++; |
| } |
| os_free_mem(NULL, input); |
| } |
| |
| /* |
| ======================================================================== |
| |
| Routine Description: |
| It utilizes PRF-384 or PRF-512 to derive session-specific keys from a PMK. |
| It shall be called by 4-way handshake processing. |
| |
| Arguments: |
| pAd - pointer to our pAdapter context |
| PMK - pointer to PMK |
| ANonce - pointer to ANonce |
| AA - pointer to Authenticator Address |
| SNonce - pointer to SNonce |
| SA - pointer to Supplicant Address |
| len - indicate the length of PTK (octet) |
| |
| Return Value: |
| Output pointer to the PTK |
| |
| Note: |
| Refer to IEEE 802.11i-2004 8.5.1.2 |
| |
| ======================================================================== |
| */ |
| VOID WpaCountPTK( |
| IN PRTMP_ADAPTER pAd, |
| IN UCHAR *PMK, |
| IN UCHAR *ANonce, |
| IN UCHAR *AA, |
| IN UCHAR *SNonce, |
| IN UCHAR *SA, |
| OUT UCHAR *output, |
| IN UINT len) |
| { |
| UCHAR concatenation[76]; |
| UINT CurrPos = 0; |
| UCHAR temp[32]; |
| UCHAR Prefix[] = {'P', 'a', 'i', 'r', 'w', 'i', 's', 'e', ' ', 'k', 'e', 'y', ' ', |
| 'e', 'x', 'p', 'a', 'n', 's', 'i', 'o', 'n'}; |
| |
| // initiate the concatenation input |
| NdisZeroMemory(temp, sizeof(temp)); |
| NdisZeroMemory(concatenation, 76); |
| |
| // Get smaller address |
| if (RTMPCompareMemory(SA, AA, 6) == 1) |
| NdisMoveMemory(concatenation, AA, 6); |
| else |
| NdisMoveMemory(concatenation, SA, 6); |
| CurrPos += 6; |
| |
| // Get larger address |
| if (RTMPCompareMemory(SA, AA, 6) == 1) |
| NdisMoveMemory(&concatenation[CurrPos], SA, 6); |
| else |
| NdisMoveMemory(&concatenation[CurrPos], AA, 6); |
| |
| // store the larger mac address for backward compatible of |
| // ralink proprietary STA-key issue |
| NdisMoveMemory(temp, &concatenation[CurrPos], MAC_ADDR_LEN); |
| CurrPos += 6; |
| |
| // Get smaller Nonce |
| if (RTMPCompareMemory(ANonce, SNonce, 32) == 0) |
| NdisMoveMemory(&concatenation[CurrPos], temp, 32); // patch for ralink proprietary STA-key issue |
| else if (RTMPCompareMemory(ANonce, SNonce, 32) == 1) |
| NdisMoveMemory(&concatenation[CurrPos], SNonce, 32); |
| else |
| NdisMoveMemory(&concatenation[CurrPos], ANonce, 32); |
| CurrPos += 32; |
| |
| // Get larger Nonce |
| if (RTMPCompareMemory(ANonce, SNonce, 32) == 0) |
| NdisMoveMemory(&concatenation[CurrPos], temp, 32); // patch for ralink proprietary STA-key issue |
| else if (RTMPCompareMemory(ANonce, SNonce, 32) == 1) |
| NdisMoveMemory(&concatenation[CurrPos], ANonce, 32); |
| else |
| NdisMoveMemory(&concatenation[CurrPos], SNonce, 32); |
| CurrPos += 32; |
| |
| hex_dump("concatenation=", concatenation, 76); |
| |
| // Use PRF to generate PTK |
| PRF(PMK, LEN_MASTER_KEY, Prefix, 22, concatenation, 76, output, len); |
| |
| } |
| |
| /* |
| ======================================================================== |
| |
| Routine Description: |
| Generate random number by software. |
| |
| Arguments: |
| pAd - pointer to our pAdapter context |
| macAddr - pointer to local MAC address |
| |
| Return Value: |
| |
| Note: |
| 802.1ii-2004 Annex H.5 |
| |
| ======================================================================== |
| */ |
| VOID GenRandom( |
| IN PRTMP_ADAPTER pAd, |
| IN UCHAR *macAddr, |
| OUT UCHAR *random) |
| { |
| INT i, curr; |
| UCHAR local[80], KeyCounter[32]; |
| UCHAR result[80]; |
| ULONG CurrentTime; |
| UCHAR prefix[] = {'I', 'n', 'i', 't', ' ', 'C', 'o', 'u', 'n', 't', 'e', 'r'}; |
| |
| // Zero the related information |
| NdisZeroMemory(result, 80); |
| NdisZeroMemory(local, 80); |
| NdisZeroMemory(KeyCounter, 32); |
| |
| for (i = 0; i < 32; i++) |
| { |
| // copy the local MAC address |
| COPY_MAC_ADDR(local, macAddr); |
| curr = MAC_ADDR_LEN; |
| |
| // concatenate the current time |
| NdisGetSystemUpTime(&CurrentTime); |
| NdisMoveMemory(&local[curr], &CurrentTime, sizeof(CurrentTime)); |
| curr += sizeof(CurrentTime); |
| |
| // concatenate the last result |
| NdisMoveMemory(&local[curr], result, 32); |
| curr += 32; |
| |
| // concatenate a variable |
| NdisMoveMemory(&local[curr], &i, 2); |
| curr += 2; |
| |
| // calculate the result |
| PRF(KeyCounter, 32, prefix,12, local, curr, result, 32); |
| } |
| |
| NdisMoveMemory(random, result, 32); |
| } |
| |
| /* |
| ======================================================================== |
| |
| Routine Description: |
| Build cipher suite in RSN-IE. |
| It only shall be called by RTMPMakeRSNIE. |
| |
| Arguments: |
| pAd - pointer to our pAdapter context |
| ElementID - indicate the WPA1 or WPA2 |
| WepStatus - indicate the encryption type |
| bMixCipher - a boolean to indicate the pairwise cipher and group |
| cipher are the same or not |
| |
| Return Value: |
| |
| Note: |
| |
| ======================================================================== |
| */ |
| static VOID RTMPInsertRsnIeCipher( |
| IN PRTMP_ADAPTER pAd, |
| IN UCHAR ElementID, |
| IN UINT WepStatus, |
| IN BOOLEAN bMixCipher, |
| IN UCHAR FlexibleCipher, |
| OUT PUCHAR pRsnIe, |
| OUT UCHAR *rsn_len) |
| { |
| UCHAR PairwiseCnt; |
| |
| *rsn_len = 0; |
| |
| // decide WPA2 or WPA1 |
| if (ElementID == Wpa2Ie) |
| { |
| RSNIE2 *pRsnie_cipher = (RSNIE2*)pRsnIe; |
| |
| // Assign the verson as 1 |
| pRsnie_cipher->version = 1; |
| |
| switch (WepStatus) |
| { |
| // TKIP mode |
| case Ndis802_11Encryption2Enabled: |
| NdisMoveMemory(pRsnie_cipher->mcast, OUI_WPA2_TKIP, 4); |
| pRsnie_cipher->ucount = 1; |
| NdisMoveMemory(pRsnie_cipher->ucast[0].oui, OUI_WPA2_TKIP, 4); |
| *rsn_len = sizeof(RSNIE2); |
| break; |
| |
| // AES mode |
| case Ndis802_11Encryption3Enabled: |
| if (bMixCipher) |
| NdisMoveMemory(pRsnie_cipher->mcast, OUI_WPA2_TKIP, 4); |
| else |
| NdisMoveMemory(pRsnie_cipher->mcast, OUI_WPA2_CCMP, 4); |
| pRsnie_cipher->ucount = 1; |
| NdisMoveMemory(pRsnie_cipher->ucast[0].oui, OUI_WPA2_CCMP, 4); |
| *rsn_len = sizeof(RSNIE2); |
| break; |
| |
| // TKIP-AES mix mode |
| case Ndis802_11Encryption4Enabled: |
| NdisMoveMemory(pRsnie_cipher->mcast, OUI_WPA2_TKIP, 4); |
| |
| PairwiseCnt = 1; |
| // Insert WPA2 TKIP as the first pairwise cipher |
| if (MIX_CIPHER_WPA2_TKIP_ON(FlexibleCipher)) |
| { |
| NdisMoveMemory(pRsnie_cipher->ucast[0].oui, OUI_WPA2_TKIP, 4); |
| // Insert WPA2 AES as the secondary pairwise cipher |
| if (MIX_CIPHER_WPA2_AES_ON(FlexibleCipher)) |
| { |
| NdisMoveMemory(pRsnie_cipher->ucast[0].oui + 4, OUI_WPA2_CCMP, 4); |
| PairwiseCnt = 2; |
| } |
| } |
| else |
| { |
| // Insert WPA2 AES as the first pairwise cipher |
| NdisMoveMemory(pRsnie_cipher->ucast[0].oui, OUI_WPA2_CCMP, 4); |
| } |
| |
| pRsnie_cipher->ucount = PairwiseCnt; |
| *rsn_len = sizeof(RSNIE2) + (4 * (PairwiseCnt - 1)); |
| break; |
| } |
| |
| // swap for big-endian platform |
| pRsnie_cipher->version = cpu2le16(pRsnie_cipher->version); |
| pRsnie_cipher->ucount = cpu2le16(pRsnie_cipher->ucount); |
| } |
| else |
| { |
| RSNIE *pRsnie_cipher = (RSNIE*)pRsnIe; |
| |
| // Assign OUI and version |
| NdisMoveMemory(pRsnie_cipher->oui, OUI_WPA_VERSION, 4); |
| pRsnie_cipher->version = 1; |
| |
| switch (WepStatus) |
| { |
| // TKIP mode |
| case Ndis802_11Encryption2Enabled: |
| NdisMoveMemory(pRsnie_cipher->mcast, OUI_WPA_TKIP, 4); |
| pRsnie_cipher->ucount = 1; |
| NdisMoveMemory(pRsnie_cipher->ucast[0].oui, OUI_WPA_TKIP, 4); |
| *rsn_len = sizeof(RSNIE); |
| break; |
| |
| // AES mode |
| case Ndis802_11Encryption3Enabled: |
| if (bMixCipher) |
| NdisMoveMemory(pRsnie_cipher->mcast, OUI_WPA_TKIP, 4); |
| else |
| NdisMoveMemory(pRsnie_cipher->mcast, OUI_WPA_CCMP, 4); |
| pRsnie_cipher->ucount = 1; |
| NdisMoveMemory(pRsnie_cipher->ucast[0].oui, OUI_WPA_CCMP, 4); |
| *rsn_len = sizeof(RSNIE); |
| break; |
| |
| // TKIP-AES mix mode |
| case Ndis802_11Encryption4Enabled: |
| NdisMoveMemory(pRsnie_cipher->mcast, OUI_WPA_TKIP, 4); |
| |
| PairwiseCnt = 1; |
| // Insert WPA TKIP as the first pairwise cipher |
| if (MIX_CIPHER_WPA_TKIP_ON(FlexibleCipher)) |
| { |
| NdisMoveMemory(pRsnie_cipher->ucast[0].oui, OUI_WPA_TKIP, 4); |
| // Insert WPA AES as the secondary pairwise cipher |
| if (MIX_CIPHER_WPA_AES_ON(FlexibleCipher)) |
| { |
| NdisMoveMemory(pRsnie_cipher->ucast[0].oui + 4, OUI_WPA_CCMP, 4); |
| PairwiseCnt = 2; |
| } |
| } |
| else |
| { |
| // Insert WPA AES as the first pairwise cipher |
| NdisMoveMemory(pRsnie_cipher->ucast[0].oui, OUI_WPA_CCMP, 4); |
| } |
| |
| pRsnie_cipher->ucount = PairwiseCnt; |
| *rsn_len = sizeof(RSNIE) + (4 * (PairwiseCnt - 1)); |
| break; |
| } |
| |
| // swap for big-endian platform |
| pRsnie_cipher->version = cpu2le16(pRsnie_cipher->version); |
| pRsnie_cipher->ucount = cpu2le16(pRsnie_cipher->ucount); |
| } |
| |
| } |
| |
| /* |
| ======================================================================== |
| |
| Routine Description: |
| Build AKM suite in RSN-IE. |
| It only shall be called by RTMPMakeRSNIE. |
| |
| Arguments: |
| pAd - pointer to our pAdapter context |
| ElementID - indicate the WPA1 or WPA2 |
| AuthMode - indicate the authentication mode |
| apidx - indicate the interface index |
| |
| Return Value: |
| |
| Note: |
| |
| ======================================================================== |
| */ |
| static VOID RTMPInsertRsnIeAKM( |
| IN PRTMP_ADAPTER pAd, |
| IN UCHAR ElementID, |
| IN UINT AuthMode, |
| IN UCHAR apidx, |
| OUT PUCHAR pRsnIe, |
| OUT UCHAR *rsn_len) |
| { |
| RSNIE_AUTH *pRsnie_auth; |
| |
| pRsnie_auth = (RSNIE_AUTH*)(pRsnIe + (*rsn_len)); |
| |
| // decide WPA2 or WPA1 |
| if (ElementID == Wpa2Ie) |
| { |
| switch (AuthMode) |
| { |
| case Ndis802_11AuthModeWPA2: |
| case Ndis802_11AuthModeWPA1WPA2: |
| pRsnie_auth->acount = 1; |
| NdisMoveMemory(pRsnie_auth->auth[0].oui, OUI_WPA2_8021X_AKM, 4); |
| break; |
| |
| case Ndis802_11AuthModeWPA2PSK: |
| case Ndis802_11AuthModeWPA1PSKWPA2PSK: |
| pRsnie_auth->acount = 1; |
| NdisMoveMemory(pRsnie_auth->auth[0].oui, OUI_WPA2_PSK_AKM, 4); |
| break; |
| } |
| } |
| else |
| { |
| switch (AuthMode) |
| { |
| case Ndis802_11AuthModeWPA: |
| case Ndis802_11AuthModeWPA1WPA2: |
| pRsnie_auth->acount = 1; |
| NdisMoveMemory(pRsnie_auth->auth[0].oui, OUI_WPA_8021X_AKM, 4); |
| break; |
| |
| case Ndis802_11AuthModeWPAPSK: |
| case Ndis802_11AuthModeWPA1PSKWPA2PSK: |
| pRsnie_auth->acount = 1; |
| NdisMoveMemory(pRsnie_auth->auth[0].oui, OUI_WPA_PSK_AKM, 4); |
| break; |
| |
| case Ndis802_11AuthModeWPANone: |
| pRsnie_auth->acount = 1; |
| NdisMoveMemory(pRsnie_auth->auth[0].oui, OUI_WPA_NONE_AKM, 4); |
| break; |
| } |
| } |
| |
| pRsnie_auth->acount = cpu2le16(pRsnie_auth->acount); |
| |
| (*rsn_len) += sizeof(RSNIE_AUTH); // update current RSNIE length |
| |
| } |
| |
| /* |
| ======================================================================== |
| |
| Routine Description: |
| Build capability in RSN-IE. |
| It only shall be called by RTMPMakeRSNIE. |
| |
| Arguments: |
| pAd - pointer to our pAdapter context |
| ElementID - indicate the WPA1 or WPA2 |
| apidx - indicate the interface index |
| |
| Return Value: |
| |
| Note: |
| |
| ======================================================================== |
| */ |
| static VOID RTMPInsertRsnIeCap( |
| IN PRTMP_ADAPTER pAd, |
| IN UCHAR ElementID, |
| IN UCHAR apidx, |
| OUT PUCHAR pRsnIe, |
| OUT UCHAR *rsn_len) |
| { |
| RSN_CAPABILITIES *pRSN_Cap; |
| |
| // it could be ignored in WPA1 mode |
| if (ElementID == WpaIe) |
| return; |
| |
| pRSN_Cap = (RSN_CAPABILITIES*)(pRsnIe + (*rsn_len)); |
| |
| |
| pRSN_Cap->word = cpu2le16(pRSN_Cap->word); |
| |
| (*rsn_len) += sizeof(RSN_CAPABILITIES); // update current RSNIE length |
| |
| } |
| |
| |
| /* |
| ======================================================================== |
| |
| Routine Description: |
| Build RSN IE context. It is not included element-ID and length. |
| |
| Arguments: |
| pAd - pointer to our pAdapter context |
| AuthMode - indicate the authentication mode |
| WepStatus - indicate the encryption type |
| apidx - indicate the interface index |
| |
| Return Value: |
| |
| Note: |
| |
| ======================================================================== |
| */ |
| VOID RTMPMakeRSNIE( |
| IN PRTMP_ADAPTER pAd, |
| IN UINT AuthMode, |
| IN UINT WepStatus, |
| IN UCHAR apidx) |
| { |
| PUCHAR pRsnIe = NULL; // primary RSNIE |
| UCHAR *rsnielen_cur_p = 0; // the length of the primary RSNIE |
| UCHAR *rsnielen_ex_cur_p = 0; // the length of the secondary RSNIE |
| UCHAR PrimaryRsnie; |
| BOOLEAN bMixCipher = FALSE; // indicate the pairwise and group cipher are different |
| UCHAR p_offset; |
| WPA_MIX_PAIR_CIPHER FlexibleCipher = MIX_CIPHER_NOTUSE; // it provide the more flexible cipher combination in WPA-WPA2 and TKIPAES mode |
| |
| rsnielen_cur_p = NULL; |
| rsnielen_ex_cur_p = NULL; |
| |
| { |
| #ifdef CONFIG_STA_SUPPORT |
| IF_DEV_CONFIG_OPMODE_ON_STA(pAd) |
| { |
| #ifdef WPA_SUPPLICANT_SUPPORT |
| if (pAd->StaCfg.WpaSupplicantUP != WPA_SUPPLICANT_DISABLE) |
| { |
| if (AuthMode < Ndis802_11AuthModeWPA) |
| return; |
| } |
| else |
| #endif // WPA_SUPPLICANT_SUPPORT // |
| { |
| // Support WPAPSK or WPA2PSK in STA-Infra mode |
| // Support WPANone in STA-Adhoc mode |
| if ((AuthMode != Ndis802_11AuthModeWPAPSK) && |
| (AuthMode != Ndis802_11AuthModeWPA2PSK) && |
| (AuthMode != Ndis802_11AuthModeWPANone) |
| ) |
| return; |
| } |
| |
| DBGPRINT(RT_DEBUG_TRACE,("==> RTMPMakeRSNIE(STA)\n")); |
| |
| // Zero RSNIE context |
| pAd->StaCfg.RSNIE_Len = 0; |
| NdisZeroMemory(pAd->StaCfg.RSN_IE, MAX_LEN_OF_RSNIE); |
| |
| // Pointer to RSNIE |
| rsnielen_cur_p = &pAd->StaCfg.RSNIE_Len; |
| pRsnIe = pAd->StaCfg.RSN_IE; |
| |
| bMixCipher = pAd->StaCfg.bMixCipher; |
| } |
| #endif // CONFIG_STA_SUPPORT // |
| } |
| |
| // indicate primary RSNIE as WPA or WPA2 |
| if ((AuthMode == Ndis802_11AuthModeWPA) || |
| (AuthMode == Ndis802_11AuthModeWPAPSK) || |
| (AuthMode == Ndis802_11AuthModeWPANone) || |
| (AuthMode == Ndis802_11AuthModeWPA1WPA2) || |
| (AuthMode == Ndis802_11AuthModeWPA1PSKWPA2PSK)) |
| PrimaryRsnie = WpaIe; |
| else |
| PrimaryRsnie = Wpa2Ie; |
| |
| { |
| // Build the primary RSNIE |
| // 1. insert cipher suite |
| RTMPInsertRsnIeCipher(pAd, PrimaryRsnie, WepStatus, bMixCipher, FlexibleCipher, pRsnIe, &p_offset); |
| |
| // 2. insert AKM |
| RTMPInsertRsnIeAKM(pAd, PrimaryRsnie, AuthMode, apidx, pRsnIe, &p_offset); |
| |
| // 3. insert capability |
| RTMPInsertRsnIeCap(pAd, PrimaryRsnie, apidx, pRsnIe, &p_offset); |
| } |
| |
| // 4. update the RSNIE length |
| *rsnielen_cur_p = p_offset; |
| |
| hex_dump("The primary RSNIE", pRsnIe, (*rsnielen_cur_p)); |
| |
| |
| } |
| |
| /* |
| ========================================================================== |
| Description: |
| Check whether the received frame is EAP frame. |
| |
| Arguments: |
| pAd - pointer to our pAdapter context |
| pEntry - pointer to active entry |
| pData - the received frame |
| DataByteCount - the received frame's length |
| FromWhichBSSID - indicate the interface index |
| |
| Return: |
| TRUE - This frame is EAP frame |
| FALSE - otherwise |
| ========================================================================== |
| */ |
| BOOLEAN RTMPCheckWPAframe( |
| IN PRTMP_ADAPTER pAd, |
| IN PMAC_TABLE_ENTRY pEntry, |
| IN PUCHAR pData, |
| IN ULONG DataByteCount, |
| IN UCHAR FromWhichBSSID) |
| { |
| ULONG Body_len; |
| BOOLEAN Cancelled; |
| |
| |
| if(DataByteCount < (LENGTH_802_1_H + LENGTH_EAPOL_H)) |
| return FALSE; |
| |
| |
| // Skip LLC header |
| if (NdisEqualMemory(SNAP_802_1H, pData, 6) || |
| // Cisco 1200 AP may send packet with SNAP_BRIDGE_TUNNEL |
| NdisEqualMemory(SNAP_BRIDGE_TUNNEL, pData, 6)) |
| { |
| pData += 6; |
| } |
| // Skip 2-bytes EAPoL type |
| if (NdisEqualMemory(EAPOL, pData, 2)) |
| { |
| pData += 2; |
| } |
| else |
| return FALSE; |
| |
| switch (*(pData+1)) |
| { |
| case EAPPacket: |
| Body_len = (*(pData+2)<<8) | (*(pData+3)); |
| DBGPRINT(RT_DEBUG_TRACE, ("Receive EAP-Packet frame, TYPE = 0, Length = %ld\n", Body_len)); |
| break; |
| case EAPOLStart: |
| DBGPRINT(RT_DEBUG_TRACE, ("Receive EAPOL-Start frame, TYPE = 1 \n")); |
| if (pEntry->EnqueueEapolStartTimerRunning != EAPOL_START_DISABLE) |
| { |
| DBGPRINT(RT_DEBUG_TRACE, ("Cancel the EnqueueEapolStartTimerRunning \n")); |
| RTMPCancelTimer(&pEntry->EnqueueStartForPSKTimer, &Cancelled); |
| pEntry->EnqueueEapolStartTimerRunning = EAPOL_START_DISABLE; |
| } |
| break; |
| case EAPOLLogoff: |
| DBGPRINT(RT_DEBUG_TRACE, ("Receive EAPOLLogoff frame, TYPE = 2 \n")); |
| break; |
| case EAPOLKey: |
| Body_len = (*(pData+2)<<8) | (*(pData+3)); |
| DBGPRINT(RT_DEBUG_TRACE, ("Receive EAPOL-Key frame, TYPE = 3, Length = %ld\n", Body_len)); |
| break; |
| case EAPOLASFAlert: |
| DBGPRINT(RT_DEBUG_TRACE, ("Receive EAPOLASFAlert frame, TYPE = 4 \n")); |
| break; |
| default: |
| return FALSE; |
| |
| } |
| return TRUE; |
| } |
| |
| |
| /* |
| ========================================================================== |
| Description: |
| ENCRYPT AES GTK before sending in EAPOL frame. |
| AES GTK length = 128 bit, so fix blocks for aes-key-wrap as 2 in this function. |
| This function references to RFC 3394 for aes key wrap algorithm. |
| Return: |
| ========================================================================== |
| */ |
| VOID AES_GTK_KEY_WRAP( |
| IN UCHAR *key, |
| IN UCHAR *plaintext, |
| IN UCHAR p_len, |
| OUT UCHAR *ciphertext) |
| { |
| UCHAR A[8], BIN[16], BOUT[16]; |
| UCHAR R[512]; |
| INT num_blocks = p_len/8; // unit:64bits |
| INT i, j; |
| aes_context aesctx; |
| UCHAR xor; |
| |
| rtmp_aes_set_key(&aesctx, key, 128); |
| |
| // Init IA |
| for (i = 0; i < 8; i++) |
| A[i] = 0xa6; |
| |
| //Input plaintext |
| for (i = 0; i < num_blocks; i++) |
| { |
| for (j = 0 ; j < 8; j++) |
| R[8 * (i + 1) + j] = plaintext[8 * i + j]; |
| } |
| |
| // Key Mix |
| for (j = 0; j < 6; j++) |
| { |
| for(i = 1; i <= num_blocks; i++) |
| { |
| //phase 1 |
| NdisMoveMemory(BIN, A, 8); |
| NdisMoveMemory(&BIN[8], &R[8 * i], 8); |
| rtmp_aes_encrypt(&aesctx, BIN, BOUT); |
| |
| NdisMoveMemory(A, &BOUT[0], 8); |
| xor = num_blocks * j + i; |
| A[7] = BOUT[7] ^ xor; |
| NdisMoveMemory(&R[8 * i], &BOUT[8], 8); |
| } |
| } |
| |
| // Output ciphertext |
| NdisMoveMemory(ciphertext, A, 8); |
| |
| for (i = 1; i <= num_blocks; i++) |
| { |
| for (j = 0 ; j < 8; j++) |
| ciphertext[8 * i + j] = R[8 * i + j]; |
| } |
| } |
| |
| |
| /* |
| ======================================================================== |
| |
| Routine Description: |
| Misc function to decrypt AES body |
| |
| Arguments: |
| |
| Return Value: |
| |
| Note: |
| This function references to RFC 3394 for aes key unwrap algorithm. |
| |
| ======================================================================== |
| */ |
| VOID AES_GTK_KEY_UNWRAP( |
| IN UCHAR *key, |
| OUT UCHAR *plaintext, |
| IN UCHAR c_len, |
| IN UCHAR *ciphertext) |
| |
| { |
| UCHAR A[8], BIN[16], BOUT[16]; |
| UCHAR xor; |
| INT i, j; |
| aes_context aesctx; |
| UCHAR *R; |
| INT num_blocks = c_len/8; // unit:64bits |
| |
| |
| os_alloc_mem(NULL, (PUCHAR *)&R, 512); |
| |
| if (R == NULL) |
| { |
| DBGPRINT(RT_DEBUG_ERROR, ("!!!AES_GTK_KEY_UNWRAP: no memory!!!\n")); |
| return; |
| } /* End of if */ |
| |
| // Initialize |
| NdisMoveMemory(A, ciphertext, 8); |
| //Input plaintext |
| for(i = 0; i < (c_len-8); i++) |
| { |
| R[ i] = ciphertext[i + 8]; |
| } |
| |
| rtmp_aes_set_key(&aesctx, key, 128); |
| |
| for(j = 5; j >= 0; j--) |
| { |
| for(i = (num_blocks-1); i > 0; i--) |
| { |
| xor = (num_blocks -1 )* j + i; |
| NdisMoveMemory(BIN, A, 8); |
| BIN[7] = A[7] ^ xor; |
| NdisMoveMemory(&BIN[8], &R[(i-1)*8], 8); |
| rtmp_aes_decrypt(&aesctx, BIN, BOUT); |
| NdisMoveMemory(A, &BOUT[0], 8); |
| NdisMoveMemory(&R[(i-1)*8], &BOUT[8], 8); |
| } |
| } |
| |
| // OUTPUT |
| for(i = 0; i < c_len; i++) |
| { |
| plaintext[i] = R[i]; |
| } |
| |
| |
| os_free_mem(NULL, R); |
| } |
| |
| /* |
| ========================================================================== |
| Description: |
| Report the EAP message type |
| |
| Arguments: |
| msg - EAPOL_PAIR_MSG_1 |
| EAPOL_PAIR_MSG_2 |
| EAPOL_PAIR_MSG_3 |
| EAPOL_PAIR_MSG_4 |
| EAPOL_GROUP_MSG_1 |
| EAPOL_GROUP_MSG_2 |
| |
| Return: |
| message type string |
| |
| ========================================================================== |
| */ |
| CHAR *GetEapolMsgType(CHAR msg) |
| { |
| if(msg == EAPOL_PAIR_MSG_1) |
| return "Pairwise Message 1"; |
| else if(msg == EAPOL_PAIR_MSG_2) |
| return "Pairwise Message 2"; |
| else if(msg == EAPOL_PAIR_MSG_3) |
| return "Pairwise Message 3"; |
| else if(msg == EAPOL_PAIR_MSG_4) |
| return "Pairwise Message 4"; |
| else if(msg == EAPOL_GROUP_MSG_1) |
| return "Group Message 1"; |
| else if(msg == EAPOL_GROUP_MSG_2) |
| return "Group Message 2"; |
| else |
| return "Invalid Message"; |
| } |
| |
| |
| /* |
| ======================================================================== |
| |
| Routine Description: |
| Check Sanity RSN IE of EAPoL message |
| |
| Arguments: |
| |
| Return Value: |
| |
| |
| ======================================================================== |
| */ |
| BOOLEAN RTMPCheckRSNIE( |
| IN PRTMP_ADAPTER pAd, |
| IN PUCHAR pData, |
| IN UCHAR DataLen, |
| IN MAC_TABLE_ENTRY *pEntry, |
| OUT UCHAR *Offset) |
| { |
| PUCHAR pVIE; |
| UCHAR len; |
| PEID_STRUCT pEid; |
| BOOLEAN result = FALSE; |
| |
| pVIE = pData; |
| len = DataLen; |
| *Offset = 0; |
| |
| while (len > sizeof(RSNIE2)) |
| { |
| pEid = (PEID_STRUCT) pVIE; |
| // WPA RSN IE |
| if ((pEid->Eid == IE_WPA) && (NdisEqualMemory(pEid->Octet, WPA_OUI, 4))) |
| { |
| if ((pEntry->AuthMode == Ndis802_11AuthModeWPA || pEntry->AuthMode == Ndis802_11AuthModeWPAPSK) && |
| (NdisEqualMemory(pVIE, pEntry->RSN_IE, pEntry->RSNIE_Len)) && |
| (pEntry->RSNIE_Len == (pEid->Len + 2))) |
| { |
| result = TRUE; |
| } |
| |
| *Offset += (pEid->Len + 2); |
| } |
| // WPA2 RSN IE |
| else if ((pEid->Eid == IE_RSN) && (NdisEqualMemory(pEid->Octet + 2, RSN_OUI, 3))) |
| { |
| if ((pEntry->AuthMode == Ndis802_11AuthModeWPA2 || pEntry->AuthMode == Ndis802_11AuthModeWPA2PSK) && |
| (NdisEqualMemory(pVIE, pEntry->RSN_IE, pEntry->RSNIE_Len)) && |
| (pEntry->RSNIE_Len == (pEid->Len + 2))/* ToDo-AlbertY for mesh*/) |
| { |
| result = TRUE; |
| } |
| |
| *Offset += (pEid->Len + 2); |
| } |
| else |
| { |
| break; |
| } |
| |
| pVIE += (pEid->Len + 2); |
| len -= (pEid->Len + 2); |
| } |
| |
| |
| return result; |
| |
| } |
| |
| |
| /* |
| ======================================================================== |
| |
| Routine Description: |
| Parse KEYDATA field. KEYDATA[] May contain 2 RSN IE and optionally GTK. |
| GTK is encaptulated in KDE format at p.83 802.11i D10 |
| |
| Arguments: |
| |
| Return Value: |
| |
| Note: |
| 802.11i D10 |
| |
| ======================================================================== |
| */ |
| BOOLEAN RTMPParseEapolKeyData( |
| IN PRTMP_ADAPTER pAd, |
| IN PUCHAR pKeyData, |
| IN UCHAR KeyDataLen, |
| IN UCHAR GroupKeyIndex, |
| IN UCHAR MsgType, |
| IN BOOLEAN bWPA2, |
| IN MAC_TABLE_ENTRY *pEntry) |
| { |
| PKDE_ENCAP pKDE = NULL; |
| PUCHAR pMyKeyData = pKeyData; |
| UCHAR KeyDataLength = KeyDataLen; |
| UCHAR GTKLEN = 0; |
| UCHAR DefaultIdx = 0; |
| UCHAR skip_offset; |
| |
| // Verify The RSN IE contained in pairewise_msg_2 && pairewise_msg_3 and skip it |
| if (MsgType == EAPOL_PAIR_MSG_2 || MsgType == EAPOL_PAIR_MSG_3) |
| { |
| // Check RSN IE whether it is WPA2/WPA2PSK |
| if (!RTMPCheckRSNIE(pAd, pKeyData, KeyDataLen, pEntry, &skip_offset)) |
| { |
| // send wireless event - for RSN IE different |
| if (pAd->CommonCfg.bWirelessEvent) |
| RTMPSendWirelessEvent(pAd, IW_RSNIE_DIFF_EVENT_FLAG, pEntry->Addr, pEntry->apidx, 0); |
| |
| DBGPRINT(RT_DEBUG_ERROR, ("RSN_IE Different in msg %d of 4-way handshake!\n", MsgType)); |
| hex_dump("Receive RSN_IE ", pKeyData, KeyDataLen); |
| hex_dump("Desired RSN_IE ", pEntry->RSN_IE, pEntry->RSNIE_Len); |
| |
| return FALSE; |
| } |
| else |
| { |
| if (bWPA2 && MsgType == EAPOL_PAIR_MSG_3) |
| { |
| // skip RSN IE |
| pMyKeyData += skip_offset; |
| KeyDataLength -= skip_offset; |
| DBGPRINT(RT_DEBUG_TRACE, ("RTMPParseEapolKeyData ==> WPA2/WPA2PSK RSN IE matched in Msg 3, Length(%d) \n", skip_offset)); |
| } |
| else |
| return TRUE; |
| } |
| } |
| |
| DBGPRINT(RT_DEBUG_TRACE,("RTMPParseEapolKeyData ==> KeyDataLength %d without RSN_IE \n", KeyDataLength)); |
| |
| // Parse EKD format in pairwise_msg_3_WPA2 && group_msg_1_WPA2 |
| if (bWPA2 && (MsgType == EAPOL_PAIR_MSG_3 || MsgType == EAPOL_GROUP_MSG_1)) |
| { |
| if (KeyDataLength >= 8) // KDE format exclude GTK length |
| { |
| pKDE = (PKDE_ENCAP) pMyKeyData; |
| |
| |
| DefaultIdx = pKDE->GTKEncap.Kid; |
| |
| // Sanity check - KED length |
| if (KeyDataLength < (pKDE->Len + 2)) |
| { |
| DBGPRINT(RT_DEBUG_ERROR, ("ERROR: The len from KDE is too short \n")); |
| return FALSE; |
| } |
| |
| // Get GTK length - refer to IEEE 802.11i-2004 p.82 |
| GTKLEN = pKDE->Len -6; |
| if (GTKLEN < LEN_AES_KEY) |
| { |
| DBGPRINT(RT_DEBUG_ERROR, ("ERROR: GTK Key length is too short (%d) \n", GTKLEN)); |
| return FALSE; |
| } |
| |
| } |
| else |
| { |
| DBGPRINT(RT_DEBUG_ERROR, ("ERROR: KDE format length is too short \n")); |
| return FALSE; |
| } |
| |
| DBGPRINT(RT_DEBUG_TRACE, ("GTK in KDE format ,DefaultKeyID=%d, KeyLen=%d \n", DefaultIdx, GTKLEN)); |
| // skip it |
| pMyKeyData += 8; |
| KeyDataLength -= 8; |
| |
| } |
| else if (!bWPA2 && MsgType == EAPOL_GROUP_MSG_1) |
| { |
| DefaultIdx = GroupKeyIndex; |
| DBGPRINT(RT_DEBUG_TRACE, ("GTK DefaultKeyID=%d \n", DefaultIdx)); |
| } |
| |
| // Sanity check - shared key index must be 1 ~ 3 |
| if (DefaultIdx < 1 || DefaultIdx > 3) |
| { |
| DBGPRINT(RT_DEBUG_ERROR, ("ERROR: GTK Key index(%d) is invalid in %s %s \n", DefaultIdx, ((bWPA2) ? "WPA2" : "WPA"), GetEapolMsgType(MsgType))); |
| return FALSE; |
| } |
| |
| |
| #ifdef CONFIG_STA_SUPPORT |
| // Todo |
| #endif // CONFIG_STA_SUPPORT // |
| |
| return TRUE; |
| |
| } |
| |
| |
| /* |
| ======================================================================== |
| |
| Routine Description: |
| Construct EAPoL message for WPA handshaking |
| Its format is below, |
| |
| +--------------------+ |
| | Protocol Version | 1 octet |
| +--------------------+ |
| | Protocol Type | 1 octet |
| +--------------------+ |
| | Body Length | 2 octets |
| +--------------------+ |
| | Descriptor Type | 1 octet |
| +--------------------+ |
| | Key Information | 2 octets |
| +--------------------+ |
| | Key Length | 1 octet |
| +--------------------+ |
| | Key Repaly Counter | 8 octets |
| +--------------------+ |
| | Key Nonce | 32 octets |
| +--------------------+ |
| | Key IV | 16 octets |
| +--------------------+ |
| | Key RSC | 8 octets |
| +--------------------+ |
| | Key ID or Reserved | 8 octets |
| +--------------------+ |
| | Key MIC | 16 octets |
| +--------------------+ |
| | Key Data Length | 2 octets |
| +--------------------+ |
| | Key Data | n octets |
| +--------------------+ |
| |
| |
| Arguments: |
| pAd Pointer to our adapter |
| |
| Return Value: |
| None |
| |
| Note: |
| |
| ======================================================================== |
| */ |
| VOID ConstructEapolMsg( |
| IN PRTMP_ADAPTER pAd, |
| IN UCHAR AuthMode, |
| IN UCHAR WepStatus, |
| IN UCHAR GroupKeyWepStatus, |
| IN UCHAR MsgType, |
| IN UCHAR DefaultKeyIdx, |
| IN UCHAR *ReplayCounter, |
| IN UCHAR *KeyNonce, |
| IN UCHAR *TxRSC, |
| IN UCHAR *PTK, |
| IN UCHAR *GTK, |
| IN UCHAR *RSNIE, |
| IN UCHAR RSNIE_Len, |
| OUT PEAPOL_PACKET pMsg) |
| { |
| BOOLEAN bWPA2 = FALSE; |
| |
| // Choose WPA2 or not |
| if ((AuthMode == Ndis802_11AuthModeWPA2) || (AuthMode == Ndis802_11AuthModeWPA2PSK)) |
| bWPA2 = TRUE; |
| |
| // Init Packet and Fill header |
| pMsg->ProVer = EAPOL_VER; |
| pMsg->ProType = EAPOLKey; |
| |
| // Default 95 bytes, the EAPoL-Key descriptor exclude Key-data field |
| pMsg->Body_Len[1] = LEN_EAPOL_KEY_MSG; |
| |
| // Fill in EAPoL descriptor |
| if (bWPA2) |
| pMsg->KeyDesc.Type = WPA2_KEY_DESC; |
| else |
| pMsg->KeyDesc.Type = WPA1_KEY_DESC; |
| |
| // Fill in Key information, refer to IEEE Std 802.11i-2004 page 78 |
| // When either the pairwise or the group cipher is AES, the DESC_TYPE_AES(2) shall be used. |
| pMsg->KeyDesc.KeyInfo.KeyDescVer = |
| (((WepStatus == Ndis802_11Encryption3Enabled) || (GroupKeyWepStatus == Ndis802_11Encryption3Enabled)) ? (DESC_TYPE_AES) : (DESC_TYPE_TKIP)); |
| |
| // Specify Key Type as Group(0) or Pairwise(1) |
| if (MsgType >= EAPOL_GROUP_MSG_1) |
| pMsg->KeyDesc.KeyInfo.KeyType = GROUPKEY; |
| else |
| pMsg->KeyDesc.KeyInfo.KeyType = PAIRWISEKEY; |
| |
| // Specify Key Index, only group_msg1_WPA1 |
| if (!bWPA2 && (MsgType >= EAPOL_GROUP_MSG_1)) |
| pMsg->KeyDesc.KeyInfo.KeyIndex = DefaultKeyIdx; |
| |
| if (MsgType == EAPOL_PAIR_MSG_3) |
| pMsg->KeyDesc.KeyInfo.Install = 1; |
| |
| if ((MsgType == EAPOL_PAIR_MSG_1) || (MsgType == EAPOL_PAIR_MSG_3) || (MsgType == EAPOL_GROUP_MSG_1)) |
| pMsg->KeyDesc.KeyInfo.KeyAck = 1; |
| |
| if (MsgType != EAPOL_PAIR_MSG_1) |
| pMsg->KeyDesc.KeyInfo.KeyMic = 1; |
| |
| if ((bWPA2 && (MsgType >= EAPOL_PAIR_MSG_3)) || (!bWPA2 && (MsgType >= EAPOL_GROUP_MSG_1))) |
| { |
| pMsg->KeyDesc.KeyInfo.Secure = 1; |
| } |
| |
| if (bWPA2 && ((MsgType == EAPOL_PAIR_MSG_3) || (MsgType == EAPOL_GROUP_MSG_1))) |
| { |
| pMsg->KeyDesc.KeyInfo.EKD_DL = 1; |
| } |
| |
| // key Information element has done. |
| *(USHORT *)(&pMsg->KeyDesc.KeyInfo) = cpu2le16(*(USHORT *)(&pMsg->KeyDesc.KeyInfo)); |
| |
| // Fill in Key Length |
| { |
| if (MsgType >= EAPOL_GROUP_MSG_1) |
| { |
| // the length of group key cipher |
| pMsg->KeyDesc.KeyLength[1] = ((GroupKeyWepStatus == Ndis802_11Encryption2Enabled) ? TKIP_GTK_LENGTH : LEN_AES_KEY); |
| } |
| else |
| { |
| // the length of pairwise key cipher |
| pMsg->KeyDesc.KeyLength[1] = ((WepStatus == Ndis802_11Encryption2Enabled) ? LEN_TKIP_KEY : LEN_AES_KEY); |
| } |
| } |
| |
| // Fill in replay counter |
| NdisMoveMemory(pMsg->KeyDesc.ReplayCounter, ReplayCounter, LEN_KEY_DESC_REPLAY); |
| |
| // Fill Key Nonce field |
| // ANonce : pairwise_msg1 & pairwise_msg3 |
| // SNonce : pairwise_msg2 |
| // GNonce : group_msg1_wpa1 |
| if ((MsgType <= EAPOL_PAIR_MSG_3) || ((!bWPA2 && (MsgType == EAPOL_GROUP_MSG_1)))) |
| NdisMoveMemory(pMsg->KeyDesc.KeyNonce, KeyNonce, LEN_KEY_DESC_NONCE); |
| |
| // Fill key IV - WPA2 as 0, WPA1 as random |
| if (!bWPA2 && (MsgType == EAPOL_GROUP_MSG_1)) |
| { |
| // Suggest IV be random number plus some number, |
| NdisMoveMemory(pMsg->KeyDesc.KeyIv, &KeyNonce[16], LEN_KEY_DESC_IV); |
| pMsg->KeyDesc.KeyIv[15] += 2; |
| } |
| |
| // Fill Key RSC field |
| // It contains the RSC for the GTK being installed. |
| if ((MsgType == EAPOL_PAIR_MSG_3 && bWPA2) || (MsgType == EAPOL_GROUP_MSG_1)) |
| { |
| NdisMoveMemory(pMsg->KeyDesc.KeyRsc, TxRSC, 6); |
| } |
| |
| // Clear Key MIC field for MIC calculation later |
| NdisZeroMemory(pMsg->KeyDesc.KeyMic, LEN_KEY_DESC_MIC); |
| |
| ConstructEapolKeyData(pAd, |
| AuthMode, |
| WepStatus, |
| GroupKeyWepStatus, |
| MsgType, |
| DefaultKeyIdx, |
| bWPA2, |
| PTK, |
| GTK, |
| RSNIE, |
| RSNIE_Len, |
| pMsg); |
| |
| // Calculate MIC and fill in KeyMic Field except Pairwise Msg 1. |
| if (MsgType != EAPOL_PAIR_MSG_1) |
| { |
| CalculateMIC(pAd, WepStatus, PTK, pMsg); |
| } |
| |
| DBGPRINT(RT_DEBUG_TRACE, ("===> ConstructEapolMsg for %s %s\n", ((bWPA2) ? "WPA2" : "WPA"), GetEapolMsgType(MsgType))); |
| DBGPRINT(RT_DEBUG_TRACE, (" Body length = %d \n", pMsg->Body_Len[1])); |
| DBGPRINT(RT_DEBUG_TRACE, (" Key length = %d \n", pMsg->KeyDesc.KeyLength[1])); |
| |
| |
| } |
| |
| /* |
| ======================================================================== |
| |
| Routine Description: |
| Construct the Key Data field of EAPoL message |
| |
| Arguments: |
| pAd Pointer to our adapter |
| Elem Message body |
| |
| Return Value: |
| None |
| |
| Note: |
| |
| ======================================================================== |
| */ |
| VOID ConstructEapolKeyData( |
| IN PRTMP_ADAPTER pAd, |
| IN UCHAR AuthMode, |
| IN UCHAR WepStatus, |
| IN UCHAR GroupKeyWepStatus, |
| IN UCHAR MsgType, |
| IN UCHAR DefaultKeyIdx, |
| IN BOOLEAN bWPA2Capable, |
| IN UCHAR *PTK, |
| IN UCHAR *GTK, |
| IN UCHAR *RSNIE, |
| IN UCHAR RSNIE_LEN, |
| OUT PEAPOL_PACKET pMsg) |
| { |
| UCHAR *mpool, *Key_Data, *Rc4GTK; |
| UCHAR ekey[(LEN_KEY_DESC_IV+LEN_EAP_EK)]; |
| UCHAR data_offset; |
| |
| |
| if (MsgType == EAPOL_PAIR_MSG_1 || MsgType == EAPOL_PAIR_MSG_4 || MsgType == EAPOL_GROUP_MSG_2) |
| return; |
| |
| // allocate memory pool |
| os_alloc_mem(pAd, (PUCHAR *)&mpool, 1500); |
| |
| if (mpool == NULL) |
| return; |
| |
| /* Rc4GTK Len = 512 */ |
| Rc4GTK = (UCHAR *) ROUND_UP(mpool, 4); |
| /* Key_Data Len = 512 */ |
| Key_Data = (UCHAR *) ROUND_UP(Rc4GTK + 512, 4); |
| |
| NdisZeroMemory(Key_Data, 512); |
| pMsg->KeyDesc.KeyDataLen[1] = 0; |
| data_offset = 0; |
| |
| // Encapsulate RSNIE in pairwise_msg2 & pairwise_msg3 |
| if (RSNIE_LEN && ((MsgType == EAPOL_PAIR_MSG_2) || (MsgType == EAPOL_PAIR_MSG_3))) |
| { |
| if (bWPA2Capable) |
| Key_Data[data_offset + 0] = IE_WPA2; |
| else |
| Key_Data[data_offset + 0] = IE_WPA; |
| |
| Key_Data[data_offset + 1] = RSNIE_LEN; |
| NdisMoveMemory(&Key_Data[data_offset + 2], RSNIE, RSNIE_LEN); |
| data_offset += (2 + RSNIE_LEN); |
| } |
| |
| // Encapsulate KDE format in pairwise_msg3_WPA2 & group_msg1_WPA2 |
| if (bWPA2Capable && ((MsgType == EAPOL_PAIR_MSG_3) || (MsgType == EAPOL_GROUP_MSG_1))) |
| { |
| // Key Data Encapsulation (KDE) format - 802.11i-2004 Figure-43w and Table-20h |
| Key_Data[data_offset + 0] = 0xDD; |
| |
| if (GroupKeyWepStatus == Ndis802_11Encryption3Enabled) |
| { |
| Key_Data[data_offset + 1] = 0x16;// 4+2+16(OUI+DataType+DataField) |
| } |
| else |
| { |
| Key_Data[data_offset + 1] = 0x26;// 4+2+32(OUI+DataType+DataField) |
| } |
| |
| Key_Data[data_offset + 2] = 0x00; |
| Key_Data[data_offset + 3] = 0x0F; |
| Key_Data[data_offset + 4] = 0xAC; |
| Key_Data[data_offset + 5] = 0x01; |
| |
| // GTK KDE format - 802.11i-2004 Figure-43x |
| Key_Data[data_offset + 6] = (DefaultKeyIdx & 0x03); |
| Key_Data[data_offset + 7] = 0x00; // Reserved Byte |
| |
| data_offset += 8; |
| } |
| |
| |
| // Encapsulate GTK and encrypt the key-data field with KEK. |
| // Only for pairwise_msg3_WPA2 and group_msg1 |
| if ((MsgType == EAPOL_PAIR_MSG_3 && bWPA2Capable) || (MsgType == EAPOL_GROUP_MSG_1)) |
| { |
| // Fill in GTK |
| if (GroupKeyWepStatus == Ndis802_11Encryption3Enabled) |
| { |
| NdisMoveMemory(&Key_Data[data_offset], GTK, LEN_AES_KEY); |
| data_offset += LEN_AES_KEY; |
| } |
| else |
| { |
| NdisMoveMemory(&Key_Data[data_offset], GTK, TKIP_GTK_LENGTH); |
| data_offset += TKIP_GTK_LENGTH; |
| } |
| |
| // Still dont know why, but if not append will occur "GTK not include in MSG3" |
| // Patch for compatibility between zero config and funk |
| if (MsgType == EAPOL_PAIR_MSG_3 && bWPA2Capable) |
| { |
| if (GroupKeyWepStatus == Ndis802_11Encryption3Enabled) |
| { |
| Key_Data[data_offset + 0] = 0xDD; |
| Key_Data[data_offset + 1] = 0; |
| data_offset += 2; |
| } |
| else |
| { |
| Key_Data[data_offset + 0] = 0xDD; |
| Key_Data[data_offset + 1] = 0; |
| Key_Data[data_offset + 2] = 0; |
| Key_Data[data_offset + 3] = 0; |
| Key_Data[data_offset + 4] = 0; |
| Key_Data[data_offset + 5] = 0; |
| data_offset += 6; |
| } |
| } |
| |
| // Encrypt the data material in key data field |
| if (WepStatus == Ndis802_11Encryption3Enabled) |
| { |
| AES_GTK_KEY_WRAP(&PTK[16], Key_Data, data_offset, Rc4GTK); |
| // AES wrap function will grow 8 bytes in length |
| data_offset += 8; |
| } |
| else |
| { |
| // PREPARE Encrypted "Key DATA" field. (Encrypt GTK with RC4, usinf PTK[16]->[31] as Key, IV-field as IV) |
| // put TxTsc in Key RSC field |
| pAd->PrivateInfo.FCSCRC32 = PPPINITFCS32; //Init crc32. |
| |
| // ekey is the contanetion of IV-field, and PTK[16]->PTK[31] |
| NdisMoveMemory(ekey, pMsg->KeyDesc.KeyIv, LEN_KEY_DESC_IV); |
| NdisMoveMemory(&ekey[LEN_KEY_DESC_IV], &PTK[16], LEN_EAP_EK); |
| ARCFOUR_INIT(&pAd->PrivateInfo.WEPCONTEXT, ekey, sizeof(ekey)); //INIT SBOX, KEYLEN+3(IV) |
| pAd->PrivateInfo.FCSCRC32 = RTMP_CALC_FCS32(pAd->PrivateInfo.FCSCRC32, Key_Data, data_offset); |
| WPAARCFOUR_ENCRYPT(&pAd->PrivateInfo.WEPCONTEXT, Rc4GTK, Key_Data, data_offset); |
| } |
| |
| NdisMoveMemory(pMsg->KeyDesc.KeyData, Rc4GTK, data_offset); |
| } |
| else |
| { |
| NdisMoveMemory(pMsg->KeyDesc.KeyData, Key_Data, data_offset); |
| } |
| |
| // set key data length field and total length |
| pMsg->KeyDesc.KeyDataLen[1] = data_offset; |
| pMsg->Body_Len[1] += data_offset; |
| |
| os_free_mem(pAd, mpool); |
| |
| } |
| |
| /* |
| ======================================================================== |
| |
| Routine Description: |
| Calcaulate MIC. It is used during 4-ways handsharking. |
| |
| Arguments: |
| pAd - pointer to our pAdapter context |
| PeerWepStatus - indicate the encryption type |
| |
| Return Value: |
| |
| Note: |
| |
| ======================================================================== |
| */ |
| VOID CalculateMIC( |
| IN PRTMP_ADAPTER pAd, |
| IN UCHAR PeerWepStatus, |
| IN UCHAR *PTK, |
| OUT PEAPOL_PACKET pMsg) |
| { |
| UCHAR *OutBuffer; |
| ULONG FrameLen = 0; |
| UCHAR mic[LEN_KEY_DESC_MIC]; |
| UCHAR digest[80]; |
| |
| // allocate memory for MIC calculation |
| os_alloc_mem(pAd, (PUCHAR *)&OutBuffer, 512); |
| |
| if (OutBuffer == NULL) |
| { |
| DBGPRINT(RT_DEBUG_ERROR, ("!!!CalculateMIC: no memory!!!\n")); |
| return; |
| } |
| |
| // make a frame for calculating MIC. |
| MakeOutgoingFrame(OutBuffer, &FrameLen, |
| pMsg->Body_Len[1] + 4, pMsg, |
| END_OF_ARGS); |
| |
| NdisZeroMemory(mic, sizeof(mic)); |
| |
| // Calculate MIC |
| if (PeerWepStatus == Ndis802_11Encryption3Enabled) |
| { |
| HMAC_SHA1(OutBuffer, FrameLen, PTK, LEN_EAP_MICK, digest); |
| NdisMoveMemory(mic, digest, LEN_KEY_DESC_MIC); |
| } |
| else |
| { |
| hmac_md5(PTK, LEN_EAP_MICK, OutBuffer, FrameLen, mic); |
| } |
| |
| // store the calculated MIC |
| NdisMoveMemory(pMsg->KeyDesc.KeyMic, mic, LEN_KEY_DESC_MIC); |
| |
| os_free_mem(pAd, OutBuffer); |
| } |
| |
| /* |
| ======================================================================== |
| |
| Routine Description: |
| Some received frames can't decrypt by Asic, so decrypt them by software. |
| |
| Arguments: |
| pAd - pointer to our pAdapter context |
| PeerWepStatus - indicate the encryption type |
| |
| Return Value: |
| NDIS_STATUS_SUCCESS - decryption successful |
| NDIS_STATUS_FAILURE - decryption failure |
| |
| ======================================================================== |
| */ |
| NDIS_STATUS RTMPSoftDecryptBroadCastData( |
| IN PRTMP_ADAPTER pAd, |
| IN RX_BLK *pRxBlk, |
| IN NDIS_802_11_ENCRYPTION_STATUS GroupCipher, |
| IN PCIPHER_KEY pShard_key) |
| { |
| PRXWI_STRUC pRxWI = pRxBlk->pRxWI; |
| |
| |
| |
| // handle WEP decryption |
| if (GroupCipher == Ndis802_11Encryption1Enabled) |
| { |
| if (RTMPSoftDecryptWEP(pAd, pRxBlk->pData, pRxWI->MPDUtotalByteCount, pShard_key)) |
| { |
| |
| //Minus IV[4] & ICV[4] |
| pRxWI->MPDUtotalByteCount -= 8; |
| } |
| else |
| { |
| DBGPRINT(RT_DEBUG_ERROR, ("ERROR : Software decrypt WEP data fails.\n")); |
| // give up this frame |
| return NDIS_STATUS_FAILURE; |
| } |
| } |
| // handle TKIP decryption |
| else if (GroupCipher == Ndis802_11Encryption2Enabled) |
| { |
| if (RTMPSoftDecryptTKIP(pAd, pRxBlk->pData, pRxWI->MPDUtotalByteCount, 0, pShard_key)) |
| { |
| |
| //Minus 8 bytes MIC, 8 bytes IV/EIV, 4 bytes ICV |
| pRxWI->MPDUtotalByteCount -= 20; |
| } |
| else |
| { |
| DBGPRINT(RT_DEBUG_ERROR, ("ERROR : RTMPSoftDecryptTKIP Failed\n")); |
| // give up this frame |
| return NDIS_STATUS_FAILURE; |
| } |
| } |
| // handle AES decryption |
| else if (GroupCipher == Ndis802_11Encryption3Enabled) |
| { |
| if (RTMPSoftDecryptAES(pAd, pRxBlk->pData, pRxWI->MPDUtotalByteCount , pShard_key)) |
| { |
| |
| //8 bytes MIC, 8 bytes IV/EIV (CCMP Header) |
| pRxWI->MPDUtotalByteCount -= 16; |
| } |
| else |
| { |
| DBGPRINT(RT_DEBUG_ERROR, ("ERROR : RTMPSoftDecryptAES Failed\n")); |
| // give up this frame |
| return NDIS_STATUS_FAILURE; |
| } |
| } |
| else |
| { |
| // give up this frame |
| return NDIS_STATUS_FAILURE; |
| } |
| |
| return NDIS_STATUS_SUCCESS; |
| |
| } |
| |