121baa36dSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 28fc8598eSJerry Chuang /* 38fc8598eSJerry Chuang * Host AP crypt: host-based CCMP encryption implementation for Host AP driver 48fc8598eSJerry Chuang * 58fc8598eSJerry Chuang * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi> 68fc8598eSJerry Chuang */ 78fc8598eSJerry Chuang 88fc8598eSJerry Chuang #include <linux/module.h> 98fc8598eSJerry Chuang #include <linux/init.h> 108fc8598eSJerry Chuang #include <linux/slab.h> 118fc8598eSJerry Chuang #include <linux/random.h> 128fc8598eSJerry Chuang #include <linux/skbuff.h> 138fc8598eSJerry Chuang #include <linux/netdevice.h> 148fc8598eSJerry Chuang #include <linux/if_ether.h> 158fc8598eSJerry Chuang #include <linux/if_arp.h> 16a6a93bc0SDilek Uzulmez #include <linux/string.h> 178fc8598eSJerry Chuang #include <linux/wireless.h> 188fc8598eSJerry Chuang 198fc8598eSJerry Chuang #include "ieee80211.h" 208fc8598eSJerry Chuang 218fc8598eSJerry Chuang #include <linux/crypto.h> 22eb0e7bf3SChristina Quast #include <crypto/aead.h> 238fc8598eSJerry Chuang #include <linux/scatterlist.h> 248fc8598eSJerry Chuang 258fc8598eSJerry Chuang MODULE_AUTHOR("Jouni Malinen"); 268fc8598eSJerry Chuang MODULE_DESCRIPTION("Host AP crypt: CCMP"); 278fc8598eSJerry Chuang MODULE_LICENSE("GPL"); 288fc8598eSJerry Chuang 298fc8598eSJerry Chuang #define AES_BLOCK_LEN 16 308fc8598eSJerry Chuang #define CCMP_HDR_LEN 8 318fc8598eSJerry Chuang #define CCMP_MIC_LEN 8 328fc8598eSJerry Chuang #define CCMP_TK_LEN 16 338fc8598eSJerry Chuang #define CCMP_PN_LEN 6 348fc8598eSJerry Chuang 358fc8598eSJerry Chuang struct ieee80211_ccmp_data { 368fc8598eSJerry Chuang u8 key[CCMP_TK_LEN]; 378fc8598eSJerry Chuang int key_set; 388fc8598eSJerry Chuang 398fc8598eSJerry Chuang u8 tx_pn[CCMP_PN_LEN]; 408fc8598eSJerry Chuang u8 rx_pn[CCMP_PN_LEN]; 418fc8598eSJerry Chuang 428fc8598eSJerry Chuang u32 dot11RSNAStatsCCMPFormatErrors; 438fc8598eSJerry Chuang u32 dot11RSNAStatsCCMPReplays; 448fc8598eSJerry Chuang u32 dot11RSNAStatsCCMPDecryptErrors; 458fc8598eSJerry Chuang 468fc8598eSJerry Chuang int key_idx; 478fc8598eSJerry Chuang 48eb0e7bf3SChristina Quast struct crypto_aead *tfm; 498fc8598eSJerry Chuang 508fc8598eSJerry Chuang /* scratch buffers for virt_to_page() (crypto API) */ 51eb0e7bf3SChristina Quast u8 tx_aad[2 * AES_BLOCK_LEN]; 52eb0e7bf3SChristina Quast u8 rx_aad[2 * AES_BLOCK_LEN]; 538fc8598eSJerry Chuang }; 548fc8598eSJerry Chuang 558fc8598eSJerry Chuang static void *ieee80211_ccmp_init(int key_idx) 568fc8598eSJerry Chuang { 578fc8598eSJerry Chuang struct ieee80211_ccmp_data *priv; 588fc8598eSJerry Chuang 5998743f87SJia-Ju Bai priv = kzalloc(sizeof(*priv), GFP_KERNEL); 60ce53f4faSsimran singhal if (!priv) 618fc8598eSJerry Chuang goto fail; 628fc8598eSJerry Chuang priv->key_idx = key_idx; 638fc8598eSJerry Chuang 64eb0e7bf3SChristina Quast priv->tfm = crypto_alloc_aead("ccm(aes)", 0, CRYPTO_ALG_ASYNC); 658fc8598eSJerry Chuang if (IS_ERR(priv->tfm)) { 6611bc6596SArushi Singhal pr_debug("ieee80211_crypt_ccmp: could not allocate crypto API aes\n"); 678fc8598eSJerry Chuang priv->tfm = NULL; 688fc8598eSJerry Chuang goto fail; 698fc8598eSJerry Chuang } 70f61fb935SMauro Carvalho Chehab 718fc8598eSJerry Chuang return priv; 728fc8598eSJerry Chuang 738fc8598eSJerry Chuang fail: 748fc8598eSJerry Chuang if (priv) { 758fc8598eSJerry Chuang if (priv->tfm) 76eb0e7bf3SChristina Quast crypto_free_aead(priv->tfm); 778fc8598eSJerry Chuang kfree(priv); 788fc8598eSJerry Chuang } 798fc8598eSJerry Chuang 808fc8598eSJerry Chuang return NULL; 818fc8598eSJerry Chuang } 828fc8598eSJerry Chuang 838fc8598eSJerry Chuang static void ieee80211_ccmp_deinit(void *priv) 848fc8598eSJerry Chuang { 858fc8598eSJerry Chuang struct ieee80211_ccmp_data *_priv = priv; 86f61fb935SMauro Carvalho Chehab 878fc8598eSJerry Chuang if (_priv && _priv->tfm) 88eb0e7bf3SChristina Quast crypto_free_aead(_priv->tfm); 898fc8598eSJerry Chuang kfree(priv); 908fc8598eSJerry Chuang } 918fc8598eSJerry Chuang 92eb0e7bf3SChristina Quast static int ccmp_init_iv_and_aad(struct rtl_80211_hdr_4addr *hdr, 93eb0e7bf3SChristina Quast u8 *pn, u8 *iv, u8 *aad) 948fc8598eSJerry Chuang { 958fc8598eSJerry Chuang u8 *pos, qc = 0; 968fc8598eSJerry Chuang size_t aad_len; 978fc8598eSJerry Chuang u16 fc; 988fc8598eSJerry Chuang int a4_included, qc_included; 998fc8598eSJerry Chuang 1008fc8598eSJerry Chuang fc = le16_to_cpu(hdr->frame_ctl); 1018fc8598eSJerry Chuang a4_included = ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == 1028fc8598eSJerry Chuang (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)); 10327a98a28Smike dupuis /* qc_included = ((WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA) && 10427a98a28Smike dupuis * (WLAN_FC_GET_STYPE(fc) & 0x08)); 1058fc8598eSJerry Chuang */ 1064d2c47a9SHatice ERTÜRK /* fixed by David :2006.9.6 */ 1072060f31aSHaneen Mohammed qc_included = (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA) && 1082060f31aSHaneen Mohammed (WLAN_FC_GET_STYPE(fc) & 0x80); 1098fc8598eSJerry Chuang aad_len = 22; 1108fc8598eSJerry Chuang if (a4_included) 1118fc8598eSJerry Chuang aad_len += 6; 1128fc8598eSJerry Chuang if (qc_included) { 1138fc8598eSJerry Chuang pos = (u8 *)&hdr->addr4; 1148fc8598eSJerry Chuang if (a4_included) 1158fc8598eSJerry Chuang pos += 6; 1168fc8598eSJerry Chuang qc = *pos & 0x0f; 1178fc8598eSJerry Chuang aad_len += 2; 1188fc8598eSJerry Chuang } 119eb0e7bf3SChristina Quast 120eb0e7bf3SChristina Quast /* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC 121eb0e7bf3SChristina Quast * mode authentication are not allowed to collide, yet both are derived 122eb0e7bf3SChristina Quast * from the same vector. We only set L := 1 here to indicate that the 123eb0e7bf3SChristina Quast * data size can be represented in (L+1) bytes. The CCM layer will take 124eb0e7bf3SChristina Quast * care of storing the data length in the top (L+1) bytes and setting 125eb0e7bf3SChristina Quast * and clearing the other bits as is required to derive the two IVs. 12627a98a28Smike dupuis */ 127eb0e7bf3SChristina Quast iv[0] = 0x1; 128eb0e7bf3SChristina Quast 129eb0e7bf3SChristina Quast /* Nonce: QC | A2 | PN */ 130eb0e7bf3SChristina Quast iv[1] = qc; 131eb0e7bf3SChristina Quast memcpy(iv + 2, hdr->addr2, ETH_ALEN); 132eb0e7bf3SChristina Quast memcpy(iv + 8, pn, CCMP_PN_LEN); 1338fc8598eSJerry Chuang 1348fc8598eSJerry Chuang /* AAD: 1358fc8598eSJerry Chuang * FC with bits 4..6 and 11..13 masked to zero; 14 is always one 1368fc8598eSJerry Chuang * A1 | A2 | A3 1378fc8598eSJerry Chuang * SC with bits 4..15 (seq#) masked to zero 1388fc8598eSJerry Chuang * A4 (if present) 1398fc8598eSJerry Chuang * QC (if present) 1408fc8598eSJerry Chuang */ 1418fc8598eSJerry Chuang pos = (u8 *)hdr; 142eb0e7bf3SChristina Quast aad[0] = pos[0] & 0x8f; 143eb0e7bf3SChristina Quast aad[1] = pos[1] & 0xc7; 1441b3c6cccSKees Cook memcpy(&aad[2], &hdr->addr1, ETH_ALEN); 1451b3c6cccSKees Cook memcpy(&aad[8], &hdr->addr2, ETH_ALEN); 1461b3c6cccSKees Cook memcpy(&aad[14], &hdr->addr3, ETH_ALEN); 1478fc8598eSJerry Chuang pos = (u8 *)&hdr->seq_ctl; 148eb0e7bf3SChristina Quast aad[20] = pos[0] & 0x0f; 149eb0e7bf3SChristina Quast aad[21] = 0; /* all bits masked */ 150eb0e7bf3SChristina Quast memset(aad + 22, 0, 8); 1518fc8598eSJerry Chuang if (a4_included) 152eb0e7bf3SChristina Quast memcpy(aad + 22, hdr->addr4, ETH_ALEN); 1538fc8598eSJerry Chuang if (qc_included) { 154eb0e7bf3SChristina Quast aad[a4_included ? 28 : 22] = qc; 1558fc8598eSJerry Chuang /* rest of QC masked */ 1568fc8598eSJerry Chuang } 1578fc8598eSJerry Chuang 158eb0e7bf3SChristina Quast return aad_len; 1598fc8598eSJerry Chuang } 1608fc8598eSJerry Chuang 1618fc8598eSJerry Chuang static int ieee80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv) 1628fc8598eSJerry Chuang { 1638fc8598eSJerry Chuang struct ieee80211_ccmp_data *key = priv; 16468772c84SYueHaibing int i; 1658fc8598eSJerry Chuang u8 *pos; 1665c2918a5SPaul Gortmaker struct rtl_80211_hdr_4addr *hdr; 16720f896c4Ssimran singhal struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE); 1688fc8598eSJerry Chuang 1698fc8598eSJerry Chuang if (skb_headroom(skb) < CCMP_HDR_LEN || 1708fc8598eSJerry Chuang skb_tailroom(skb) < CCMP_MIC_LEN || 1718fc8598eSJerry Chuang skb->len < hdr_len) 1728fc8598eSJerry Chuang return -1; 1738fc8598eSJerry Chuang 1748fc8598eSJerry Chuang pos = skb_push(skb, CCMP_HDR_LEN); 1758fc8598eSJerry Chuang memmove(pos, pos + CCMP_HDR_LEN, hdr_len); 1768fc8598eSJerry Chuang pos += hdr_len; 1774d2c47a9SHatice ERTÜRK /* mic = skb_put(skb, CCMP_MIC_LEN); */ 1788fc8598eSJerry Chuang 1798fc8598eSJerry Chuang i = CCMP_PN_LEN - 1; 1808fc8598eSJerry Chuang while (i >= 0) { 1818fc8598eSJerry Chuang key->tx_pn[i]++; 1828fc8598eSJerry Chuang if (key->tx_pn[i] != 0) 1838fc8598eSJerry Chuang break; 1848fc8598eSJerry Chuang i--; 1858fc8598eSJerry Chuang } 1868fc8598eSJerry Chuang 1878fc8598eSJerry Chuang *pos++ = key->tx_pn[5]; 1888fc8598eSJerry Chuang *pos++ = key->tx_pn[4]; 1898fc8598eSJerry Chuang *pos++ = 0; 1909f383969SPayal Kshirsagar *pos++ = (key->key_idx << 6) | BIT(5) /* Ext IV included */; 1918fc8598eSJerry Chuang *pos++ = key->tx_pn[3]; 1928fc8598eSJerry Chuang *pos++ = key->tx_pn[2]; 1938fc8598eSJerry Chuang *pos++ = key->tx_pn[1]; 1948fc8598eSJerry Chuang *pos++ = key->tx_pn[0]; 1958fc8598eSJerry Chuang 1965c2918a5SPaul Gortmaker hdr = (struct rtl_80211_hdr_4addr *)skb->data; 197d9098c55Smike dupuis if (!tcb_desc->bHwSec) { 198eb0e7bf3SChristina Quast struct aead_request *req; 199eb0e7bf3SChristina Quast struct scatterlist sg[2]; 200eb0e7bf3SChristina Quast u8 *aad = key->tx_aad; 201eb0e7bf3SChristina Quast u8 iv[AES_BLOCK_LEN]; 202eb0e7bf3SChristina Quast int aad_len, ret; 203eb0e7bf3SChristina Quast size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN; 2048fc8598eSJerry Chuang 205eb0e7bf3SChristina Quast req = aead_request_alloc(key->tfm, GFP_ATOMIC); 206eb0e7bf3SChristina Quast if (!req) 207eb0e7bf3SChristina Quast return -ENOMEM; 2088fc8598eSJerry Chuang 209eb0e7bf3SChristina Quast aad_len = ccmp_init_iv_and_aad(hdr, key->tx_pn, iv, aad); 2108fc8598eSJerry Chuang 211eb0e7bf3SChristina Quast skb_put(skb, CCMP_MIC_LEN); 2128fc8598eSJerry Chuang 213eb0e7bf3SChristina Quast sg_init_table(sg, 2); 214eb0e7bf3SChristina Quast sg_set_buf(&sg[0], aad, aad_len); 215eb0e7bf3SChristina Quast sg_set_buf(&sg[1], skb->data + hdr_len + CCMP_HDR_LEN, 216eb0e7bf3SChristina Quast data_len + CCMP_MIC_LEN); 2178fc8598eSJerry Chuang 218eb0e7bf3SChristina Quast aead_request_set_callback(req, 0, NULL, NULL); 219eb0e7bf3SChristina Quast aead_request_set_ad(req, aad_len); 220eb0e7bf3SChristina Quast aead_request_set_crypt(req, sg, sg, data_len, iv); 221eb0e7bf3SChristina Quast 222eb0e7bf3SChristina Quast ret = crypto_aead_encrypt(req); 223eb0e7bf3SChristina Quast aead_request_free(req); 224eb0e7bf3SChristina Quast 225eb0e7bf3SChristina Quast return ret; 2268fc8598eSJerry Chuang } 2278fc8598eSJerry Chuang return 0; 2288fc8598eSJerry Chuang } 2298fc8598eSJerry Chuang 2308fc8598eSJerry Chuang static int ieee80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv) 2318fc8598eSJerry Chuang { 2328fc8598eSJerry Chuang struct ieee80211_ccmp_data *key = priv; 2338fc8598eSJerry Chuang u8 keyidx, *pos; 2345c2918a5SPaul Gortmaker struct rtl_80211_hdr_4addr *hdr; 23520f896c4Ssimran singhal struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE); 2368fc8598eSJerry Chuang u8 pn[6]; 2378fc8598eSJerry Chuang 2388fc8598eSJerry Chuang if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) { 2398fc8598eSJerry Chuang key->dot11RSNAStatsCCMPFormatErrors++; 2408fc8598eSJerry Chuang return -1; 2418fc8598eSJerry Chuang } 2428fc8598eSJerry Chuang 2435c2918a5SPaul Gortmaker hdr = (struct rtl_80211_hdr_4addr *)skb->data; 2448fc8598eSJerry Chuang pos = skb->data + hdr_len; 2458fc8598eSJerry Chuang keyidx = pos[3]; 2469f383969SPayal Kshirsagar if (!(keyidx & BIT(5))) { 2478fc8598eSJerry Chuang if (net_ratelimit()) { 24811bc6596SArushi Singhal netdev_dbg(skb->dev, "CCMP: received packet without ExtIV flag from %pM\n", 24920433972Smike dupuis hdr->addr2); 2508fc8598eSJerry Chuang } 2518fc8598eSJerry Chuang key->dot11RSNAStatsCCMPFormatErrors++; 2528fc8598eSJerry Chuang return -2; 2538fc8598eSJerry Chuang } 2548fc8598eSJerry Chuang keyidx >>= 6; 2558fc8598eSJerry Chuang if (key->key_idx != keyidx) { 25611bc6596SArushi Singhal netdev_dbg(skb->dev, "CCMP: RX tkey->key_idx=%d frame keyidx=%d priv=%p\n", 25720433972Smike dupuis key->key_idx, keyidx, priv); 2588fc8598eSJerry Chuang return -6; 2598fc8598eSJerry Chuang } 2608fc8598eSJerry Chuang if (!key->key_set) { 2618fc8598eSJerry Chuang if (net_ratelimit()) { 26211bc6596SArushi Singhal netdev_dbg(skb->dev, "CCMP: received packet from %pM with keyid=%d that does not have a configured key\n", 26320433972Smike dupuis hdr->addr2, keyidx); 2648fc8598eSJerry Chuang } 2658fc8598eSJerry Chuang return -3; 2668fc8598eSJerry Chuang } 2678fc8598eSJerry Chuang 2688fc8598eSJerry Chuang pn[0] = pos[7]; 2698fc8598eSJerry Chuang pn[1] = pos[6]; 2708fc8598eSJerry Chuang pn[2] = pos[5]; 2718fc8598eSJerry Chuang pn[3] = pos[4]; 2728fc8598eSJerry Chuang pn[4] = pos[1]; 2738fc8598eSJerry Chuang pn[5] = pos[0]; 2748fc8598eSJerry Chuang pos += 8; 2758fc8598eSJerry Chuang 2768fc8598eSJerry Chuang if (memcmp(pn, key->rx_pn, CCMP_PN_LEN) <= 0) { 2778fc8598eSJerry Chuang if (net_ratelimit()) { 27811bc6596SArushi Singhal netdev_dbg(skb->dev, "CCMP: replay detected: STA=%pM previous PN %pm received PN %pm\n", 2790ee9f67cSJoe Perches hdr->addr2, key->rx_pn, pn); 2808fc8598eSJerry Chuang } 2818fc8598eSJerry Chuang key->dot11RSNAStatsCCMPReplays++; 2828fc8598eSJerry Chuang return -4; 2838fc8598eSJerry Chuang } 284d9098c55Smike dupuis if (!tcb_desc->bHwSec) { 285eb0e7bf3SChristina Quast struct aead_request *req; 286eb0e7bf3SChristina Quast struct scatterlist sg[2]; 287eb0e7bf3SChristina Quast u8 *aad = key->rx_aad; 288eb0e7bf3SChristina Quast u8 iv[AES_BLOCK_LEN]; 289eb0e7bf3SChristina Quast int aad_len, ret; 290eb0e7bf3SChristina Quast size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN; 2918fc8598eSJerry Chuang 292eb0e7bf3SChristina Quast req = aead_request_alloc(key->tfm, GFP_ATOMIC); 293eb0e7bf3SChristina Quast if (!req) 294eb0e7bf3SChristina Quast return -ENOMEM; 2958fc8598eSJerry Chuang 296eb0e7bf3SChristina Quast aad_len = ccmp_init_iv_and_aad(hdr, pn, iv, aad); 2978fc8598eSJerry Chuang 298eb0e7bf3SChristina Quast sg_init_table(sg, 2); 299eb0e7bf3SChristina Quast sg_set_buf(&sg[0], aad, aad_len); 300eb0e7bf3SChristina Quast sg_set_buf(&sg[1], pos, data_len); 3018fc8598eSJerry Chuang 302eb0e7bf3SChristina Quast aead_request_set_callback(req, 0, NULL, NULL); 303eb0e7bf3SChristina Quast aead_request_set_ad(req, aad_len); 304eb0e7bf3SChristina Quast aead_request_set_crypt(req, sg, sg, data_len, iv); 305eb0e7bf3SChristina Quast 306eb0e7bf3SChristina Quast ret = crypto_aead_decrypt(req); 307eb0e7bf3SChristina Quast aead_request_free(req); 308eb0e7bf3SChristina Quast 309eb0e7bf3SChristina Quast if (ret) { 3108fc8598eSJerry Chuang if (net_ratelimit()) { 31111bc6596SArushi Singhal netdev_dbg(skb->dev, "CCMP: decrypt failed: STA=%pM\n", 31220433972Smike dupuis hdr->addr2); 3138fc8598eSJerry Chuang } 3148fc8598eSJerry Chuang key->dot11RSNAStatsCCMPDecryptErrors++; 3158fc8598eSJerry Chuang return -5; 3168fc8598eSJerry Chuang } 3178fc8598eSJerry Chuang 3188fc8598eSJerry Chuang memcpy(key->rx_pn, pn, CCMP_PN_LEN); 3198fc8598eSJerry Chuang } 3208fc8598eSJerry Chuang /* Remove hdr and MIC */ 3218fc8598eSJerry Chuang memmove(skb->data + CCMP_HDR_LEN, skb->data, hdr_len); 3228fc8598eSJerry Chuang skb_pull(skb, CCMP_HDR_LEN); 3238fc8598eSJerry Chuang skb_trim(skb, skb->len - CCMP_MIC_LEN); 3248fc8598eSJerry Chuang 3258fc8598eSJerry Chuang return keyidx; 3268fc8598eSJerry Chuang } 3278fc8598eSJerry Chuang 3288fc8598eSJerry Chuang static int ieee80211_ccmp_set_key(void *key, int len, u8 *seq, void *priv) 3298fc8598eSJerry Chuang { 3308fc8598eSJerry Chuang struct ieee80211_ccmp_data *data = priv; 3318fc8598eSJerry Chuang int keyidx; 332eb0e7bf3SChristina Quast struct crypto_aead *tfm = data->tfm; 3338fc8598eSJerry Chuang 3348fc8598eSJerry Chuang keyidx = data->key_idx; 3358fc8598eSJerry Chuang memset(data, 0, sizeof(*data)); 3368fc8598eSJerry Chuang data->key_idx = keyidx; 3378fc8598eSJerry Chuang if (len == CCMP_TK_LEN) { 3388fc8598eSJerry Chuang memcpy(data->key, key, CCMP_TK_LEN); 3398fc8598eSJerry Chuang data->key_set = 1; 3408fc8598eSJerry Chuang if (seq) { 3418fc8598eSJerry Chuang data->rx_pn[0] = seq[5]; 3428fc8598eSJerry Chuang data->rx_pn[1] = seq[4]; 3438fc8598eSJerry Chuang data->rx_pn[2] = seq[3]; 3448fc8598eSJerry Chuang data->rx_pn[3] = seq[2]; 3458fc8598eSJerry Chuang data->rx_pn[4] = seq[1]; 3468fc8598eSJerry Chuang data->rx_pn[5] = seq[0]; 3478fc8598eSJerry Chuang } 348eb0e7bf3SChristina Quast if (crypto_aead_setauthsize(tfm, CCMP_MIC_LEN) || 349eb0e7bf3SChristina Quast crypto_aead_setkey(tfm, data->key, CCMP_TK_LEN)) 350eb0e7bf3SChristina Quast return -1; 35164ae44afSsimran singhal } else if (len == 0) { 3528fc8598eSJerry Chuang data->key_set = 0; 35364ae44afSsimran singhal } else { 3548fc8598eSJerry Chuang return -1; 35564ae44afSsimran singhal } 3568fc8598eSJerry Chuang 3578fc8598eSJerry Chuang return 0; 3588fc8598eSJerry Chuang } 3598fc8598eSJerry Chuang 3608fc8598eSJerry Chuang static int ieee80211_ccmp_get_key(void *key, int len, u8 *seq, void *priv) 3618fc8598eSJerry Chuang { 3628fc8598eSJerry Chuang struct ieee80211_ccmp_data *data = priv; 3638fc8598eSJerry Chuang 3648fc8598eSJerry Chuang if (len < CCMP_TK_LEN) 3658fc8598eSJerry Chuang return -1; 3668fc8598eSJerry Chuang 3678fc8598eSJerry Chuang if (!data->key_set) 3688fc8598eSJerry Chuang return 0; 3698fc8598eSJerry Chuang memcpy(key, data->key, CCMP_TK_LEN); 3708fc8598eSJerry Chuang 3718fc8598eSJerry Chuang if (seq) { 3728fc8598eSJerry Chuang seq[0] = data->tx_pn[5]; 3738fc8598eSJerry Chuang seq[1] = data->tx_pn[4]; 3748fc8598eSJerry Chuang seq[2] = data->tx_pn[3]; 3758fc8598eSJerry Chuang seq[3] = data->tx_pn[2]; 3768fc8598eSJerry Chuang seq[4] = data->tx_pn[1]; 3778fc8598eSJerry Chuang seq[5] = data->tx_pn[0]; 3788fc8598eSJerry Chuang } 3798fc8598eSJerry Chuang 3808fc8598eSJerry Chuang return CCMP_TK_LEN; 3818fc8598eSJerry Chuang } 3828fc8598eSJerry Chuang 3838fc8598eSJerry Chuang static char *ieee80211_ccmp_print_stats(char *p, void *priv) 3848fc8598eSJerry Chuang { 3858fc8598eSJerry Chuang struct ieee80211_ccmp_data *ccmp = priv; 38628b70100Smike dupuis 38720433972Smike dupuis p += sprintf(p, "key[%d] alg=CCMP key_set=%d tx_pn=%pm rx_pn=%pm format_errors=%d replays=%d decrypt_errors=%d\n", 3888fc8598eSJerry Chuang ccmp->key_idx, ccmp->key_set, 3890ee9f67cSJoe Perches ccmp->tx_pn, ccmp->rx_pn, 3908fc8598eSJerry Chuang ccmp->dot11RSNAStatsCCMPFormatErrors, 3918fc8598eSJerry Chuang ccmp->dot11RSNAStatsCCMPReplays, 3928fc8598eSJerry Chuang ccmp->dot11RSNAStatsCCMPDecryptErrors); 3938fc8598eSJerry Chuang 3948fc8598eSJerry Chuang return p; 3958fc8598eSJerry Chuang } 3968fc8598eSJerry Chuang 3978fc8598eSJerry Chuang static struct ieee80211_crypto_ops ieee80211_crypt_ccmp = { 3988fc8598eSJerry Chuang .name = "CCMP", 3998fc8598eSJerry Chuang .init = ieee80211_ccmp_init, 4008fc8598eSJerry Chuang .deinit = ieee80211_ccmp_deinit, 4018fc8598eSJerry Chuang .encrypt_mpdu = ieee80211_ccmp_encrypt, 4028fc8598eSJerry Chuang .decrypt_mpdu = ieee80211_ccmp_decrypt, 4038fc8598eSJerry Chuang .encrypt_msdu = NULL, 4048fc8598eSJerry Chuang .decrypt_msdu = NULL, 4058fc8598eSJerry Chuang .set_key = ieee80211_ccmp_set_key, 4068fc8598eSJerry Chuang .get_key = ieee80211_ccmp_get_key, 4078fc8598eSJerry Chuang .print_stats = ieee80211_ccmp_print_stats, 4088fc8598eSJerry Chuang .extra_prefix_len = CCMP_HDR_LEN, 4098fc8598eSJerry Chuang .extra_postfix_len = CCMP_MIC_LEN, 4108fc8598eSJerry Chuang .owner = THIS_MODULE, 4118fc8598eSJerry Chuang }; 4128fc8598eSJerry Chuang 413f61fb935SMauro Carvalho Chehab int __init ieee80211_crypto_ccmp_init(void) 4148fc8598eSJerry Chuang { 4158fc8598eSJerry Chuang return ieee80211_register_crypto_ops(&ieee80211_crypt_ccmp); 4168fc8598eSJerry Chuang } 4178fc8598eSJerry Chuang 418*57078a3cSTong Zhang void ieee80211_crypto_ccmp_exit(void) 4198fc8598eSJerry Chuang { 4208fc8598eSJerry Chuang ieee80211_unregister_crypto_ops(&ieee80211_crypt_ccmp); 4218fc8598eSJerry Chuang } 422