/* * Copyright (c) 2015 South Silicon Valley Microelectronics Inc. * Copyright (c) 2015 iComm Corporation * * 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 3 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, see . */ #include #include #include #include #include #include "wapi_sms4.h" #include "sec_wpi.h" #include "sec.h" #define IWAPIELEMENT 68 #define WID_WAPI_KEY 0x3033 u8 g_wapi_oui[3] = {0x00,0x14,0x72}; const u16 frame_cntl_mask = 0x8FC7; const u16 seq_cntl_mask = 0x0F00; struct lib80211_wpi_data { TRUTH_VALUE_T wapi_enable; TRUTH_VALUE_T wapi_key_ok; u8 wapi_version[2]; u8 ap_address[ETH_ALEN]; u8 key_index; u8 pn_key[WAPI_PN_LEN]; u8 pmsk_key[3][WAPI_PN_LEN]; u8 mic_key[3][WAPI_PN_LEN]; }; TRUTH_VALUE_T mget_wapi_key_ok(void *priv) { struct lib80211_wpi_data *data = priv; return data->wapi_key_ok; } void mset_wapi_key_ok(TRUTH_VALUE_T val, void *priv) { struct lib80211_wpi_data *data = priv; data->wapi_key_ok = val; } TRUTH_VALUE_T mget_wapi_enable(void *priv) { struct lib80211_wpi_data *data = priv; return data->wapi_enable; } void mset_wapi_enable(TRUTH_VALUE_T val, void *priv) { struct lib80211_wpi_data *data = priv; data->wapi_enable = val; } u8* mget_wapi_version(void *priv) { struct lib80211_wpi_data *data = priv; return data->wapi_version; } void mset_wapi_version(u8* val, void *priv) { struct lib80211_wpi_data *data = priv; memcpy(data->wapi_version,val,2); } u8* mget_wapi_address(void *priv) { struct lib80211_wpi_data *data = priv; return data->ap_address; } void mset_wapi_address(u8* val, void *priv) { struct lib80211_wpi_data *data = priv; memcpy(data->ap_address,val, ETH_ALEN); } u8 mget_key_index(void *priv) { struct lib80211_wpi_data *data = priv; return data->key_index; } void mset_key_index(int index, void *priv) { struct lib80211_wpi_data *data = priv; if(index <= 3 ) { data->key_index = index; } } u8* mget_pn_key(void *priv) { struct lib80211_wpi_data *data = priv; return data->pn_key; } void mset_pn_key(u8* val, void *priv) { struct lib80211_wpi_data *data = priv; memcpy(data->pn_key,val,WAPI_PN_LEN); } u8 *inc_pn_key(void *priv) { struct lib80211_wpi_data *data = priv; int i; data->pn_key[15] += 2; if( data->pn_key[15] == 0x00 ) { for(i = 14 ; i >= 0 ; i--) { if( (data->pn_key[i] += 1) != 0x00 ) { break; } } } return data->pn_key; } u8 *mget_pmsk_key(int index, void *priv) { struct lib80211_wpi_data *data = priv; return ( index >= 3 ) ? NULL : data->pmsk_key[index]; } void mset_pmsk_key(int index,u8* val, void *priv) { struct lib80211_wpi_data *data = priv; if(index < 3 ) { memcpy(data->pmsk_key[index],val, WAPI_MIC_LEN); } } u8* mget_mic_key(int index, void *priv) { struct lib80211_wpi_data *data = priv; return ( index >= 3 ) ? NULL : data->mic_key[index]; } void mset_mic_key(int index,u8* val, void *priv) { struct lib80211_wpi_data *data = priv; if(index < 3 ) { memcpy(data->mic_key[index],val,WAPI_MIC_LEN); } } u16 wlan_tx_wapi_encryption(u8 * header, u8 * data,u16 data_len, u8 * mic_pos, void *priv) { int i = 0; u16 offset = 0; BOOL_T qos_in = BFALSE; BOOL_T valid_addr4 = BTRUE; u8 ptk_header[36] = {0}; u16 ptk_headr_len = 32; u8 *p_ptk_header = ptk_header; u8 *data_mic = mic_pos; u8 *iv = NULL; u8 keyid = 0; keyid = mget_key_index(priv); #ifdef MULTI_THREAD_ENCRYPT iv = kzalloc(WAPI_PN_LEN, GFP_KERNEL); memcpy(iv, data + WAPI_KEYID_LEN + WAPI_RESERVD_LEN, WAPI_PN_LEN); data_len -= WAPI_IV_LEN; #else iv = inc_pn_key(priv); #endif *data = keyid; *(data + 1) = 0x00; data += 2; for( i = 15 ; i >= 0 ; i-- ) { *data = iv[i]; data++; } *p_ptk_header = header[offset] & (frame_cntl_mask >> 8); *(p_ptk_header + 1) = header[offset + 1] & (frame_cntl_mask & 0xFF); if(*p_ptk_header & 0x80) { qos_in = BTRUE; ptk_headr_len += 2; } if((*(p_ptk_header + 1) & 0x03 ) != 0x03) { valid_addr4 = BFALSE; } p_ptk_header += 2; offset += 2; offset += 2; memcpy(p_ptk_header, &header[offset], ADDID_LEN); p_ptk_header += ADDID_LEN; offset += ADDID_LEN; *p_ptk_header = header[offset + ETH_ALEN] & (seq_cntl_mask >> 8); *(p_ptk_header + 1) = header[offset + ETH_ALEN + 1] & (seq_cntl_mask & 0xFF); p_ptk_header += 2; memcpy(p_ptk_header, &header[offset], ETH_ALEN); p_ptk_header += ETH_ALEN; offset += ETH_ALEN; offset += 2; if(valid_addr4) { memcpy(p_ptk_header, &header[offset], ETH_ALEN); p_ptk_header += ETH_ALEN; offset += ETH_ALEN; } else { memset(p_ptk_header,0x00, ETH_ALEN); p_ptk_header += ETH_ALEN; } if(qos_in) { memcpy(p_ptk_header, &header[offset], 2); p_ptk_header += 2; offset += 2; } *p_ptk_header = keyid; p_ptk_header++; *p_ptk_header = 0x00; p_ptk_header++; *p_ptk_header = (data_len >> 8); *(p_ptk_header+1) = data_len & 0xFF; WapiCryptoSms4Mic(iv, mget_mic_key(keyid, priv), ptk_header, ptk_headr_len, data, data_len, data_mic); data_len += WAPI_MIC_LEN; WapiCryptoSms4(iv, mget_pmsk_key(keyid, priv), data, data_len, data); #ifdef MULTI_THREAD_ENCRYPT kfree(iv); #endif return data_len + WAPI_IV_LEN; } BOOL_T is_group(u8* addr) { if((addr[0] & BIT(0)) != 0) return BTRUE; return BFALSE; } u16 wlan_rx_wapi_decryption(u8 * input_ptk,u16 header_len,u16 data_len, u8 * output_buf, void *priv) { u16 offset = 0; BOOL_T qos_in = BFALSE; BOOL_T valid_addr4 = BTRUE; BOOL_T is_group_ptk = BFALSE; u8 ptk_header[36] = {0}; u16 ptk_headr_len = 32; u8 * p_ptk_header = ptk_header; u8 data_mic[WAPI_MIC_LEN] = {0}; u8 calc_data_mic[WAPI_MIC_LEN] = {0}; u8 iv[WAPI_PN_LEN] = {0}; u8 keyid = {0}; u16 ral_data_len = 0; u16 encryp_data_len = 0; int i = 0; *p_ptk_header = input_ptk[offset] & (frame_cntl_mask >> 8); *(p_ptk_header+1) = input_ptk[offset+1] & (frame_cntl_mask & 0xFF); if(*p_ptk_header & 0x80) { qos_in = BTRUE; ptk_headr_len += 2; } if((*(p_ptk_header+1) & 0x03 ) != 0x03) { valid_addr4 = BFALSE; } p_ptk_header += 2; offset += 2; offset += 2; memcpy(p_ptk_header, &input_ptk[offset], ADDID_LEN); is_group_ptk = is_group(p_ptk_header); p_ptk_header += ADDID_LEN; offset += ADDID_LEN; *p_ptk_header = input_ptk[offset+6] & (seq_cntl_mask >> 8); *(p_ptk_header+1) = input_ptk[offset+6+1] & (seq_cntl_mask & 0xFF); p_ptk_header += 2; memcpy(p_ptk_header, &input_ptk[offset], ETH_ALEN); p_ptk_header += ETH_ALEN; offset += ETH_ALEN; offset += 2; if(valid_addr4) { memcpy(p_ptk_header, &input_ptk[offset], ETH_ALEN); p_ptk_header += ETH_ALEN; offset += ETH_ALEN; } else { memset(p_ptk_header, 0x00, ETH_ALEN); p_ptk_header += ETH_ALEN; } if(qos_in) { memcpy(p_ptk_header,&input_ptk[offset], 2); p_ptk_header += 2; offset += 2; } *p_ptk_header = input_ptk[offset]; keyid = input_ptk[offset]; p_ptk_header++; offset++; *p_ptk_header = input_ptk[offset]; p_ptk_header++; offset++; encryp_data_len = data_len - WAPI_IV_LEN; ral_data_len = data_len - WAPI_IV_LEN - WAPI_MIC_LEN; *p_ptk_header = (ral_data_len >> 8); *(p_ptk_header+1) = ral_data_len & 0xFF; for( i = 15 ; i >= 0 ; i-- ) { iv[i] = input_ptk[offset]; offset++; } if(is_group_ptk) { printk("%s, is_group_ptk\n", __func__); } else { if( (iv[15] & 0x01) != 0x01 ) { printk(KERN_ALERT "decry pairwise error,iv[15]=%x\n", iv[15]); return 0; } } WapiCryptoSms4(iv, mget_pmsk_key(keyid, priv), (input_ptk + header_len + WAPI_IV_LEN), encryp_data_len, output_buf); memcpy(data_mic, output_buf + ral_data_len, WAPI_MIC_LEN); WapiCryptoSms4Mic(iv, mget_mic_key(keyid, priv), ptk_header, ptk_headr_len, (output_buf), ral_data_len, calc_data_mic); if( memcmp(calc_data_mic, data_mic, WAPI_MIC_LEN) != 0 ) { printk(KERN_ALERT "calc_data_mic != data_mic\n"); return 0; } else { return ral_data_len; } } int lib80211_wpi_encrypt(struct sk_buff *mpdu, int hdr_len, void *priv) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)mpdu->data; u8 *pos, *mic_pos; int hdrlen = 0, len = 0, out_len = 0; u8 *pdata = NULL; #ifdef MULTI_THREAD_ENCRYPT u32 wapi_iv_icv_offset = WAPI_IV_ICV_OFFSET - WAPI_IV_LEN; #else u32 wapi_iv_icv_offset = WAPI_IV_ICV_OFFSET; #endif hdrlen = ieee80211_hdrlen(hdr->frame_control); pdata = (mpdu->data) + hdrlen; if (mpdu->protocol != cpu_to_be16(0x88b4)) { if (WARN_ON(skb_headroom(mpdu) < wapi_iv_icv_offset)) { printk("[I] skb_headroom(skb) < %d\n", wapi_iv_icv_offset); return 0; } len = mpdu->len - hdrlen; pos = skb_push(mpdu, wapi_iv_icv_offset); #ifdef MULTI_THREAD_ENCRYPT memmove(pos, pos + wapi_iv_icv_offset, mpdu->len - WAPI_MIC_LEN); #else memmove(pos, pos + wapi_iv_icv_offset, hdrlen); memmove(pos + hdrlen + WAPI_IV_LEN, pos + wapi_iv_icv_offset + hdrlen, len); #endif hdr = (struct ieee80211_hdr *)pos; pos += hdrlen; mic_pos = mpdu->data + mpdu->len - WAPI_MIC_LEN; out_len = wlan_tx_wapi_encryption((u8 *)hdr, pos, len, mic_pos, priv); } else { if (ieee80211_has_protected(hdr->frame_control)) hdr->frame_control &= ~(cpu_to_le16(IEEE80211_FCTL_PROTECTED)); printk("[I] send WAPI WAI data pkt\n"); } return 1; } int lib80211_wpi_decrypt(struct sk_buff *rx_skb, int hdr_len, void *priv) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(rx_skb->data); int hdrlen, len, dcry_len; char *pdata = NULL; int ret = 0; hdrlen = ieee80211_hdrlen(hdr->frame_control); pdata = ((char*)(rx_skb->data)) + hdrlen; len = rx_skb->len - hdrlen; dcry_len = wlan_rx_wapi_decryption((u8 *)rx_skb->data, hdrlen, len, pdata, priv); if (dcry_len) { skb_trim(rx_skb, hdrlen + dcry_len); hdr->frame_control &= ~(cpu_to_le16(IEEE80211_FCTL_PROTECTED)); ret = dcry_len; } return ret; } void *lib80211_wpi_init(int key_idx) { struct lib80211_wpi_data *priv; priv = kzalloc(sizeof(*priv), GFP_ATOMIC); if (priv == NULL) { printk("allocate lib80211_wpi_data failed\n"); return NULL; } priv->key_index = key_idx; return priv; } void lib80211_wpi_deinit(void *priv) { if (priv) { printk("%s\n", __func__); kfree(priv); priv = NULL; } else printk("%s, passing NULL lib80211_wpi_data?\n", __func__); } #ifdef MULTI_THREAD_ENCRYPT int lib80211_wpi_encrypt_prepare(struct sk_buff *mpdu, int hdr_len, void *priv) { struct lib80211_wpi_data *data = priv; u8 *pos = NULL; unsigned char *iv = NULL; if (mpdu->protocol != cpu_to_be16(0x88b4) && (skb_headroom(mpdu) >= WAPI_IV_LEN)) { pos = skb_push(mpdu, WAPI_IV_LEN); memmove(pos, pos + WAPI_IV_LEN, hdr_len); pos += hdr_len; *pos = data->key_index; pos++; *pos = 0x00; pos++; iv = inc_pn_key(priv); memcpy(pos, iv, WAPI_PN_LEN); return 0; } printk("%s, pass through\n", __func__); return 0; } #endif int lib80211_wpi_set_key(void *key, int len, u8 *seq, void *priv) { struct lib80211_wpi_data *data = priv; int keyidx = data->key_index; u8 WapiASUEPNInitialValueSrc[16] = { 0x5C, 0x36, 0x5C, 0x36, 0x5C, 0x36, 0x5C, 0x36, 0x5C, 0x36, 0x5C, 0x36, 0x5C, 0x36, 0x5C, 0x36 }; printk("%s\n", __func__); mset_key_index(keyidx, priv); mset_pn_key(WapiASUEPNInitialValueSrc, priv); mset_pmsk_key(keyidx, key, priv); mset_mic_key(keyidx, key + WAPI_PN_LEN, priv); if (seq) { memcpy(data->ap_address, (u8 *)seq, ETH_ALEN); printk("%s: set ap_address %pM\n", __func__, data->ap_address); } mset_wapi_key_ok(TV_TRUE, priv); return 0; } static struct ssv_crypto_ops ssv_crypto_wpi = { .name = "WPI", .init = lib80211_wpi_init, .deinit = lib80211_wpi_deinit, .encrypt_mpdu = lib80211_wpi_encrypt, .decrypt_mpdu = lib80211_wpi_decrypt, .encrypt_msdu = NULL, .decrypt_msdu = NULL, .set_tx_pn = NULL, .set_key = lib80211_wpi_set_key, .get_key = NULL, .print_stats = NULL, .extra_mpdu_prefix_len = WAPI_IV_LEN, .extra_mpdu_postfix_len = WAPI_MIC_LEN, #ifdef MULTI_THREAD_ENCRYPT .encrypt_prepare = lib80211_wpi_encrypt_prepare, .decrypt_prepare = NULL, #endif }; struct ssv_crypto_ops *get_crypto_wpi_ops(void) { return &ssv_crypto_wpi; }