162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * lib80211 crypt: host-based CCMP encryption implementation for lib80211 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi> 662306a36Sopenharmony_ci * Copyright (c) 2008, John W. Linville <linville@tuxdriver.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/random.h> 1562306a36Sopenharmony_ci#include <linux/skbuff.h> 1662306a36Sopenharmony_ci#include <linux/netdevice.h> 1762306a36Sopenharmony_ci#include <linux/if_ether.h> 1862306a36Sopenharmony_ci#include <linux/if_arp.h> 1962306a36Sopenharmony_ci#include <asm/string.h> 2062306a36Sopenharmony_ci#include <linux/wireless.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <linux/ieee80211.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <linux/crypto.h> 2562306a36Sopenharmony_ci#include <crypto/aead.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <net/lib80211.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ciMODULE_AUTHOR("Jouni Malinen"); 3062306a36Sopenharmony_ciMODULE_DESCRIPTION("Host AP crypt: CCMP"); 3162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define AES_BLOCK_LEN 16 3462306a36Sopenharmony_ci#define CCMP_HDR_LEN 8 3562306a36Sopenharmony_ci#define CCMP_MIC_LEN 8 3662306a36Sopenharmony_ci#define CCMP_TK_LEN 16 3762306a36Sopenharmony_ci#define CCMP_PN_LEN 6 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistruct lib80211_ccmp_data { 4062306a36Sopenharmony_ci u8 key[CCMP_TK_LEN]; 4162306a36Sopenharmony_ci int key_set; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci u8 tx_pn[CCMP_PN_LEN]; 4462306a36Sopenharmony_ci u8 rx_pn[CCMP_PN_LEN]; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci u32 dot11RSNAStatsCCMPFormatErrors; 4762306a36Sopenharmony_ci u32 dot11RSNAStatsCCMPReplays; 4862306a36Sopenharmony_ci u32 dot11RSNAStatsCCMPDecryptErrors; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci int key_idx; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci struct crypto_aead *tfm; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci /* scratch buffers for virt_to_page() (crypto API) */ 5562306a36Sopenharmony_ci u8 tx_aad[2 * AES_BLOCK_LEN]; 5662306a36Sopenharmony_ci u8 rx_aad[2 * AES_BLOCK_LEN]; 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void *lib80211_ccmp_init(int key_idx) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci struct lib80211_ccmp_data *priv; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci priv = kzalloc(sizeof(*priv), GFP_ATOMIC); 6462306a36Sopenharmony_ci if (priv == NULL) 6562306a36Sopenharmony_ci goto fail; 6662306a36Sopenharmony_ci priv->key_idx = key_idx; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci priv->tfm = crypto_alloc_aead("ccm(aes)", 0, CRYPTO_ALG_ASYNC); 6962306a36Sopenharmony_ci if (IS_ERR(priv->tfm)) { 7062306a36Sopenharmony_ci priv->tfm = NULL; 7162306a36Sopenharmony_ci goto fail; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci return priv; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci fail: 7762306a36Sopenharmony_ci if (priv) { 7862306a36Sopenharmony_ci if (priv->tfm) 7962306a36Sopenharmony_ci crypto_free_aead(priv->tfm); 8062306a36Sopenharmony_ci kfree(priv); 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return NULL; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic void lib80211_ccmp_deinit(void *priv) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct lib80211_ccmp_data *_priv = priv; 8962306a36Sopenharmony_ci if (_priv && _priv->tfm) 9062306a36Sopenharmony_ci crypto_free_aead(_priv->tfm); 9162306a36Sopenharmony_ci kfree(priv); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic int ccmp_init_iv_and_aad(const struct ieee80211_hdr *hdr, 9562306a36Sopenharmony_ci const u8 *pn, u8 *iv, u8 *aad) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci u8 *pos, qc = 0; 9862306a36Sopenharmony_ci size_t aad_len; 9962306a36Sopenharmony_ci int a4_included, qc_included; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci a4_included = ieee80211_has_a4(hdr->frame_control); 10262306a36Sopenharmony_ci qc_included = ieee80211_is_data_qos(hdr->frame_control); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci aad_len = 22; 10562306a36Sopenharmony_ci if (a4_included) 10662306a36Sopenharmony_ci aad_len += 6; 10762306a36Sopenharmony_ci if (qc_included) { 10862306a36Sopenharmony_ci pos = (u8 *) & hdr->addr4; 10962306a36Sopenharmony_ci if (a4_included) 11062306a36Sopenharmony_ci pos += 6; 11162306a36Sopenharmony_ci qc = *pos & 0x0f; 11262306a36Sopenharmony_ci aad_len += 2; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC 11662306a36Sopenharmony_ci * mode authentication are not allowed to collide, yet both are derived 11762306a36Sopenharmony_ci * from the same vector. We only set L := 1 here to indicate that the 11862306a36Sopenharmony_ci * data size can be represented in (L+1) bytes. The CCM layer will take 11962306a36Sopenharmony_ci * care of storing the data length in the top (L+1) bytes and setting 12062306a36Sopenharmony_ci * and clearing the other bits as is required to derive the two IVs. 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_ci iv[0] = 0x1; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* Nonce: QC | A2 | PN */ 12562306a36Sopenharmony_ci iv[1] = qc; 12662306a36Sopenharmony_ci memcpy(iv + 2, hdr->addr2, ETH_ALEN); 12762306a36Sopenharmony_ci memcpy(iv + 8, pn, CCMP_PN_LEN); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* AAD: 13062306a36Sopenharmony_ci * FC with bits 4..6 and 11..13 masked to zero; 14 is always one 13162306a36Sopenharmony_ci * A1 | A2 | A3 13262306a36Sopenharmony_ci * SC with bits 4..15 (seq#) masked to zero 13362306a36Sopenharmony_ci * A4 (if present) 13462306a36Sopenharmony_ci * QC (if present) 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ci pos = (u8 *) hdr; 13762306a36Sopenharmony_ci aad[0] = pos[0] & 0x8f; 13862306a36Sopenharmony_ci aad[1] = pos[1] & 0xc7; 13962306a36Sopenharmony_ci memcpy(aad + 2, &hdr->addrs, 3 * ETH_ALEN); 14062306a36Sopenharmony_ci pos = (u8 *) & hdr->seq_ctrl; 14162306a36Sopenharmony_ci aad[20] = pos[0] & 0x0f; 14262306a36Sopenharmony_ci aad[21] = 0; /* all bits masked */ 14362306a36Sopenharmony_ci memset(aad + 22, 0, 8); 14462306a36Sopenharmony_ci if (a4_included) 14562306a36Sopenharmony_ci memcpy(aad + 22, hdr->addr4, ETH_ALEN); 14662306a36Sopenharmony_ci if (qc_included) { 14762306a36Sopenharmony_ci aad[a4_included ? 28 : 22] = qc; 14862306a36Sopenharmony_ci /* rest of QC masked */ 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci return aad_len; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic int lib80211_ccmp_hdr(struct sk_buff *skb, int hdr_len, 15462306a36Sopenharmony_ci u8 *aeskey, int keylen, void *priv) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct lib80211_ccmp_data *key = priv; 15762306a36Sopenharmony_ci int i; 15862306a36Sopenharmony_ci u8 *pos; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (skb_headroom(skb) < CCMP_HDR_LEN || skb->len < hdr_len) 16162306a36Sopenharmony_ci return -1; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (aeskey != NULL && keylen >= CCMP_TK_LEN) 16462306a36Sopenharmony_ci memcpy(aeskey, key->key, CCMP_TK_LEN); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci pos = skb_push(skb, CCMP_HDR_LEN); 16762306a36Sopenharmony_ci memmove(pos, pos + CCMP_HDR_LEN, hdr_len); 16862306a36Sopenharmony_ci pos += hdr_len; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci i = CCMP_PN_LEN - 1; 17162306a36Sopenharmony_ci while (i >= 0) { 17262306a36Sopenharmony_ci key->tx_pn[i]++; 17362306a36Sopenharmony_ci if (key->tx_pn[i] != 0) 17462306a36Sopenharmony_ci break; 17562306a36Sopenharmony_ci i--; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci *pos++ = key->tx_pn[5]; 17962306a36Sopenharmony_ci *pos++ = key->tx_pn[4]; 18062306a36Sopenharmony_ci *pos++ = 0; 18162306a36Sopenharmony_ci *pos++ = (key->key_idx << 6) | (1 << 5) /* Ext IV included */ ; 18262306a36Sopenharmony_ci *pos++ = key->tx_pn[3]; 18362306a36Sopenharmony_ci *pos++ = key->tx_pn[2]; 18462306a36Sopenharmony_ci *pos++ = key->tx_pn[1]; 18562306a36Sopenharmony_ci *pos++ = key->tx_pn[0]; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return CCMP_HDR_LEN; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct lib80211_ccmp_data *key = priv; 19362306a36Sopenharmony_ci struct ieee80211_hdr *hdr; 19462306a36Sopenharmony_ci struct aead_request *req; 19562306a36Sopenharmony_ci struct scatterlist sg[2]; 19662306a36Sopenharmony_ci u8 *aad = key->tx_aad; 19762306a36Sopenharmony_ci u8 iv[AES_BLOCK_LEN]; 19862306a36Sopenharmony_ci int len, data_len, aad_len; 19962306a36Sopenharmony_ci int ret; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (skb_tailroom(skb) < CCMP_MIC_LEN || skb->len < hdr_len) 20262306a36Sopenharmony_ci return -1; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci data_len = skb->len - hdr_len; 20562306a36Sopenharmony_ci len = lib80211_ccmp_hdr(skb, hdr_len, NULL, 0, priv); 20662306a36Sopenharmony_ci if (len < 0) 20762306a36Sopenharmony_ci return -1; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci req = aead_request_alloc(key->tfm, GFP_ATOMIC); 21062306a36Sopenharmony_ci if (!req) 21162306a36Sopenharmony_ci return -ENOMEM; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci hdr = (struct ieee80211_hdr *)skb->data; 21462306a36Sopenharmony_ci aad_len = ccmp_init_iv_and_aad(hdr, key->tx_pn, iv, aad); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci skb_put(skb, CCMP_MIC_LEN); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci sg_init_table(sg, 2); 21962306a36Sopenharmony_ci sg_set_buf(&sg[0], aad, aad_len); 22062306a36Sopenharmony_ci sg_set_buf(&sg[1], skb->data + hdr_len + CCMP_HDR_LEN, 22162306a36Sopenharmony_ci data_len + CCMP_MIC_LEN); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci aead_request_set_callback(req, 0, NULL, NULL); 22462306a36Sopenharmony_ci aead_request_set_ad(req, aad_len); 22562306a36Sopenharmony_ci aead_request_set_crypt(req, sg, sg, data_len, iv); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci ret = crypto_aead_encrypt(req); 22862306a36Sopenharmony_ci aead_request_free(req); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci return ret; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci/* 23462306a36Sopenharmony_ci * deal with seq counter wrapping correctly. 23562306a36Sopenharmony_ci * refer to timer_after() for jiffies wrapping handling 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_cistatic inline int ccmp_replay_check(u8 *pn_n, u8 *pn_o) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci u32 iv32_n, iv16_n; 24062306a36Sopenharmony_ci u32 iv32_o, iv16_o; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci iv32_n = (pn_n[0] << 24) | (pn_n[1] << 16) | (pn_n[2] << 8) | pn_n[3]; 24362306a36Sopenharmony_ci iv16_n = (pn_n[4] << 8) | pn_n[5]; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci iv32_o = (pn_o[0] << 24) | (pn_o[1] << 16) | (pn_o[2] << 8) | pn_o[3]; 24662306a36Sopenharmony_ci iv16_o = (pn_o[4] << 8) | pn_o[5]; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if ((s32)iv32_n - (s32)iv32_o < 0 || 24962306a36Sopenharmony_ci (iv32_n == iv32_o && iv16_n <= iv16_o)) 25062306a36Sopenharmony_ci return 1; 25162306a36Sopenharmony_ci return 0; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int lib80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct lib80211_ccmp_data *key = priv; 25762306a36Sopenharmony_ci u8 keyidx, *pos; 25862306a36Sopenharmony_ci struct ieee80211_hdr *hdr; 25962306a36Sopenharmony_ci struct aead_request *req; 26062306a36Sopenharmony_ci struct scatterlist sg[2]; 26162306a36Sopenharmony_ci u8 *aad = key->rx_aad; 26262306a36Sopenharmony_ci u8 iv[AES_BLOCK_LEN]; 26362306a36Sopenharmony_ci u8 pn[6]; 26462306a36Sopenharmony_ci int aad_len, ret; 26562306a36Sopenharmony_ci size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) { 26862306a36Sopenharmony_ci key->dot11RSNAStatsCCMPFormatErrors++; 26962306a36Sopenharmony_ci return -1; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci hdr = (struct ieee80211_hdr *)skb->data; 27362306a36Sopenharmony_ci pos = skb->data + hdr_len; 27462306a36Sopenharmony_ci keyidx = pos[3]; 27562306a36Sopenharmony_ci if (!(keyidx & (1 << 5))) { 27662306a36Sopenharmony_ci net_dbg_ratelimited("CCMP: received packet without ExtIV flag from %pM\n", 27762306a36Sopenharmony_ci hdr->addr2); 27862306a36Sopenharmony_ci key->dot11RSNAStatsCCMPFormatErrors++; 27962306a36Sopenharmony_ci return -2; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci keyidx >>= 6; 28262306a36Sopenharmony_ci if (key->key_idx != keyidx) { 28362306a36Sopenharmony_ci net_dbg_ratelimited("CCMP: RX tkey->key_idx=%d frame keyidx=%d\n", 28462306a36Sopenharmony_ci key->key_idx, keyidx); 28562306a36Sopenharmony_ci return -6; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci if (!key->key_set) { 28862306a36Sopenharmony_ci net_dbg_ratelimited("CCMP: received packet from %pM with keyid=%d that does not have a configured key\n", 28962306a36Sopenharmony_ci hdr->addr2, keyidx); 29062306a36Sopenharmony_ci return -3; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci pn[0] = pos[7]; 29462306a36Sopenharmony_ci pn[1] = pos[6]; 29562306a36Sopenharmony_ci pn[2] = pos[5]; 29662306a36Sopenharmony_ci pn[3] = pos[4]; 29762306a36Sopenharmony_ci pn[4] = pos[1]; 29862306a36Sopenharmony_ci pn[5] = pos[0]; 29962306a36Sopenharmony_ci pos += 8; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (ccmp_replay_check(pn, key->rx_pn)) { 30262306a36Sopenharmony_ci#ifdef CONFIG_LIB80211_DEBUG 30362306a36Sopenharmony_ci net_dbg_ratelimited("CCMP: replay detected: STA=%pM previous PN %02x%02x%02x%02x%02x%02x received PN %02x%02x%02x%02x%02x%02x\n", 30462306a36Sopenharmony_ci hdr->addr2, 30562306a36Sopenharmony_ci key->rx_pn[0], key->rx_pn[1], key->rx_pn[2], 30662306a36Sopenharmony_ci key->rx_pn[3], key->rx_pn[4], key->rx_pn[5], 30762306a36Sopenharmony_ci pn[0], pn[1], pn[2], pn[3], pn[4], pn[5]); 30862306a36Sopenharmony_ci#endif 30962306a36Sopenharmony_ci key->dot11RSNAStatsCCMPReplays++; 31062306a36Sopenharmony_ci return -4; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci req = aead_request_alloc(key->tfm, GFP_ATOMIC); 31462306a36Sopenharmony_ci if (!req) 31562306a36Sopenharmony_ci return -ENOMEM; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci aad_len = ccmp_init_iv_and_aad(hdr, pn, iv, aad); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci sg_init_table(sg, 2); 32062306a36Sopenharmony_ci sg_set_buf(&sg[0], aad, aad_len); 32162306a36Sopenharmony_ci sg_set_buf(&sg[1], pos, data_len); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci aead_request_set_callback(req, 0, NULL, NULL); 32462306a36Sopenharmony_ci aead_request_set_ad(req, aad_len); 32562306a36Sopenharmony_ci aead_request_set_crypt(req, sg, sg, data_len, iv); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci ret = crypto_aead_decrypt(req); 32862306a36Sopenharmony_ci aead_request_free(req); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (ret) { 33162306a36Sopenharmony_ci net_dbg_ratelimited("CCMP: decrypt failed: STA=%pM (%d)\n", 33262306a36Sopenharmony_ci hdr->addr2, ret); 33362306a36Sopenharmony_ci key->dot11RSNAStatsCCMPDecryptErrors++; 33462306a36Sopenharmony_ci return -5; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci memcpy(key->rx_pn, pn, CCMP_PN_LEN); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* Remove hdr and MIC */ 34062306a36Sopenharmony_ci memmove(skb->data + CCMP_HDR_LEN, skb->data, hdr_len); 34162306a36Sopenharmony_ci skb_pull(skb, CCMP_HDR_LEN); 34262306a36Sopenharmony_ci skb_trim(skb, skb->len - CCMP_MIC_LEN); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci return keyidx; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic int lib80211_ccmp_set_key(void *key, int len, u8 * seq, void *priv) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci struct lib80211_ccmp_data *data = priv; 35062306a36Sopenharmony_ci int keyidx; 35162306a36Sopenharmony_ci struct crypto_aead *tfm = data->tfm; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci keyidx = data->key_idx; 35462306a36Sopenharmony_ci memset(data, 0, sizeof(*data)); 35562306a36Sopenharmony_ci data->key_idx = keyidx; 35662306a36Sopenharmony_ci data->tfm = tfm; 35762306a36Sopenharmony_ci if (len == CCMP_TK_LEN) { 35862306a36Sopenharmony_ci memcpy(data->key, key, CCMP_TK_LEN); 35962306a36Sopenharmony_ci data->key_set = 1; 36062306a36Sopenharmony_ci if (seq) { 36162306a36Sopenharmony_ci data->rx_pn[0] = seq[5]; 36262306a36Sopenharmony_ci data->rx_pn[1] = seq[4]; 36362306a36Sopenharmony_ci data->rx_pn[2] = seq[3]; 36462306a36Sopenharmony_ci data->rx_pn[3] = seq[2]; 36562306a36Sopenharmony_ci data->rx_pn[4] = seq[1]; 36662306a36Sopenharmony_ci data->rx_pn[5] = seq[0]; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci if (crypto_aead_setauthsize(data->tfm, CCMP_MIC_LEN) || 36962306a36Sopenharmony_ci crypto_aead_setkey(data->tfm, data->key, CCMP_TK_LEN)) 37062306a36Sopenharmony_ci return -1; 37162306a36Sopenharmony_ci } else if (len == 0) 37262306a36Sopenharmony_ci data->key_set = 0; 37362306a36Sopenharmony_ci else 37462306a36Sopenharmony_ci return -1; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return 0; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic int lib80211_ccmp_get_key(void *key, int len, u8 * seq, void *priv) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct lib80211_ccmp_data *data = priv; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (len < CCMP_TK_LEN) 38462306a36Sopenharmony_ci return -1; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (!data->key_set) 38762306a36Sopenharmony_ci return 0; 38862306a36Sopenharmony_ci memcpy(key, data->key, CCMP_TK_LEN); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (seq) { 39162306a36Sopenharmony_ci seq[0] = data->tx_pn[5]; 39262306a36Sopenharmony_ci seq[1] = data->tx_pn[4]; 39362306a36Sopenharmony_ci seq[2] = data->tx_pn[3]; 39462306a36Sopenharmony_ci seq[3] = data->tx_pn[2]; 39562306a36Sopenharmony_ci seq[4] = data->tx_pn[1]; 39662306a36Sopenharmony_ci seq[5] = data->tx_pn[0]; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci return CCMP_TK_LEN; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic void lib80211_ccmp_print_stats(struct seq_file *m, void *priv) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct lib80211_ccmp_data *ccmp = priv; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci seq_printf(m, 40762306a36Sopenharmony_ci "key[%d] alg=CCMP key_set=%d " 40862306a36Sopenharmony_ci "tx_pn=%02x%02x%02x%02x%02x%02x " 40962306a36Sopenharmony_ci "rx_pn=%02x%02x%02x%02x%02x%02x " 41062306a36Sopenharmony_ci "format_errors=%d replays=%d decrypt_errors=%d\n", 41162306a36Sopenharmony_ci ccmp->key_idx, ccmp->key_set, 41262306a36Sopenharmony_ci ccmp->tx_pn[0], ccmp->tx_pn[1], ccmp->tx_pn[2], 41362306a36Sopenharmony_ci ccmp->tx_pn[3], ccmp->tx_pn[4], ccmp->tx_pn[5], 41462306a36Sopenharmony_ci ccmp->rx_pn[0], ccmp->rx_pn[1], ccmp->rx_pn[2], 41562306a36Sopenharmony_ci ccmp->rx_pn[3], ccmp->rx_pn[4], ccmp->rx_pn[5], 41662306a36Sopenharmony_ci ccmp->dot11RSNAStatsCCMPFormatErrors, 41762306a36Sopenharmony_ci ccmp->dot11RSNAStatsCCMPReplays, 41862306a36Sopenharmony_ci ccmp->dot11RSNAStatsCCMPDecryptErrors); 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic struct lib80211_crypto_ops lib80211_crypt_ccmp = { 42262306a36Sopenharmony_ci .name = "CCMP", 42362306a36Sopenharmony_ci .init = lib80211_ccmp_init, 42462306a36Sopenharmony_ci .deinit = lib80211_ccmp_deinit, 42562306a36Sopenharmony_ci .encrypt_mpdu = lib80211_ccmp_encrypt, 42662306a36Sopenharmony_ci .decrypt_mpdu = lib80211_ccmp_decrypt, 42762306a36Sopenharmony_ci .encrypt_msdu = NULL, 42862306a36Sopenharmony_ci .decrypt_msdu = NULL, 42962306a36Sopenharmony_ci .set_key = lib80211_ccmp_set_key, 43062306a36Sopenharmony_ci .get_key = lib80211_ccmp_get_key, 43162306a36Sopenharmony_ci .print_stats = lib80211_ccmp_print_stats, 43262306a36Sopenharmony_ci .extra_mpdu_prefix_len = CCMP_HDR_LEN, 43362306a36Sopenharmony_ci .extra_mpdu_postfix_len = CCMP_MIC_LEN, 43462306a36Sopenharmony_ci .owner = THIS_MODULE, 43562306a36Sopenharmony_ci}; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic int __init lib80211_crypto_ccmp_init(void) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci return lib80211_register_crypto_ops(&lib80211_crypt_ccmp); 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic void __exit lib80211_crypto_ccmp_exit(void) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci lib80211_unregister_crypto_ops(&lib80211_crypt_ccmp); 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cimodule_init(lib80211_crypto_ccmp_init); 44862306a36Sopenharmony_cimodule_exit(lib80211_crypto_ccmp_exit); 449