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 
ieee80211_ccmp_init(int key_idx)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 
ieee80211_ccmp_deinit(void * priv)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 
ccmp_init_iv_and_aad(struct rtl_80211_hdr_4addr * hdr,u8 * pn,u8 * iv,u8 * aad)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 
ieee80211_ccmp_encrypt(struct sk_buff * skb,int hdr_len,void * priv)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 
ieee80211_ccmp_decrypt(struct sk_buff * skb,int hdr_len,void * priv)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 
ieee80211_ccmp_set_key(void * key,int len,u8 * seq,void * priv)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 
ieee80211_ccmp_get_key(void * key,int len,u8 * seq,void * priv)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)
365*17c8129eSRebecca Mckeever 		return 0;
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 
ieee80211_ccmp_print_stats(char * p,void * priv)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 
ieee80211_crypto_ccmp_init(void)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 
ieee80211_crypto_ccmp_exit(void)41857078a3cSTong Zhang void ieee80211_crypto_ccmp_exit(void)
4198fc8598eSJerry Chuang {
4208fc8598eSJerry Chuang 	ieee80211_unregister_crypto_ops(&ieee80211_crypt_ccmp);
4218fc8598eSJerry Chuang }
422