Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags
/*
 * 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 <http://www.gnu.org/licenses/>.
 */

#include <linux/kernel.h>
#include <linux/string.h>
#include <net/cfg80211.h>
#include <net/mac80211.h>
#include <linux/etherdevice.h>
#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;
}