xref: /openbmc/linux/net/mac80211/wpa.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f0706e82SJiri Benc /*
3f0706e82SJiri Benc  * Copyright 2002-2004, Instant802 Networks, Inc.
4765cb46aSJouni Malinen  * Copyright 2008, Jouni Malinen <j@w1.fi>
59de18d81SDavid Spinadel  * Copyright (C) 2016-2017 Intel Deutschland GmbH
623a5f0afSJohannes Berg  * Copyright (C) 2020-2022 Intel Corporation
7f0706e82SJiri Benc  */
8f0706e82SJiri Benc 
9f0706e82SJiri Benc #include <linux/netdevice.h>
10f0706e82SJiri Benc #include <linux/types.h>
11f0706e82SJiri Benc #include <linux/skbuff.h>
12f0706e82SJiri Benc #include <linux/compiler.h>
13f14df804SHarvey Harrison #include <linux/ieee80211.h>
145a0e3ad6STejun Heo #include <linux/gfp.h>
15f14df804SHarvey Harrison #include <asm/unaligned.h>
16f0706e82SJiri Benc #include <net/mac80211.h>
17aba83a0bSJohannes Berg #include <crypto/aes.h>
18*8da1985fSHerbert Xu #include <crypto/utils.h>
19eb063c17SJohannes Berg 
20f0706e82SJiri Benc #include "ieee80211_i.h"
21f0706e82SJiri Benc #include "michael.h"
22f0706e82SJiri Benc #include "tkip.h"
23f0706e82SJiri Benc #include "aes_ccm.h"
24765cb46aSJouni Malinen #include "aes_cmac.h"
258ade538bSJouni Malinen #include "aes_gmac.h"
2600b9cfa3SJouni Malinen #include "aes_gcm.h"
27f0706e82SJiri Benc #include "wpa.h"
28f0706e82SJiri Benc 
299ae54c84SJohannes Berg ieee80211_tx_result
ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data * tx)305cf121c3SJohannes Berg ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)
31f0706e82SJiri Benc {
32747d753dSJouni Malinen 	u8 *data, *key, *mic;
33f0706e82SJiri Benc 	size_t data_len;
348e8862b7SHarvey Harrison 	unsigned int hdrlen;
358e8862b7SHarvey Harrison 	struct ieee80211_hdr *hdr;
36f0706e82SJiri Benc 	struct sk_buff *skb = tx->skb;
37813d7669SJohannes Berg 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
3823c0752aSJohannes Berg 	int tail;
39f0706e82SJiri Benc 
40c34498b9SHarvey Harrison 	hdr = (struct ieee80211_hdr *)skb->data;
4197359d12SJohannes Berg 	if (!tx->key || tx->key->conf.cipher != WLAN_CIPHER_SUITE_TKIP ||
4297359d12SJohannes Berg 	    skb->len < 24 || !ieee80211_is_data_present(hdr->frame_control))
439ae54c84SJohannes Berg 		return TX_CONTINUE;
44f0706e82SJiri Benc 
458e8862b7SHarvey Harrison 	hdrlen = ieee80211_hdrlen(hdr->frame_control);
468e8862b7SHarvey Harrison 	if (skb->len < hdrlen)
479ae54c84SJohannes Berg 		return TX_DROP;
48f0706e82SJiri Benc 
498e8862b7SHarvey Harrison 	data = skb->data + hdrlen;
508e8862b7SHarvey Harrison 	data_len = skb->len - hdrlen;
518e8862b7SHarvey Harrison 
52681d1190SJouni Malinen 	if (unlikely(info->flags & IEEE80211_TX_INTFL_TKIP_MIC_FAILURE)) {
53681d1190SJouni Malinen 		/* Need to use software crypto for the test */
54681d1190SJouni Malinen 		info->control.hw_key = NULL;
55681d1190SJouni Malinen 	}
56681d1190SJouni Malinen 
57813d7669SJohannes Berg 	if (info->control.hw_key &&
58a26eb27aSJohannes Berg 	    (info->flags & IEEE80211_TX_CTL_DONTFRAG ||
59f3fe4e93SSara Sharon 	     ieee80211_hw_check(&tx->local->hw, SUPPORTS_TX_FRAG)) &&
609de18d81SDavid Spinadel 	    !(tx->key->conf.flags & (IEEE80211_KEY_FLAG_GENERATE_MMIC |
619de18d81SDavid Spinadel 				     IEEE80211_KEY_FLAG_PUT_MIC_SPACE))) {
629de18d81SDavid Spinadel 		/* hwaccel - with no need for SW-generated MMIC or MIC space */
639ae54c84SJohannes Berg 		return TX_CONTINUE;
64f0706e82SJiri Benc 	}
65f0706e82SJiri Benc 
6623c0752aSJohannes Berg 	tail = MICHAEL_MIC_LEN;
67813d7669SJohannes Berg 	if (!info->control.hw_key)
684325f6caSJohannes Berg 		tail += IEEE80211_TKIP_ICV_LEN;
6923c0752aSJohannes Berg 
70b1e9be87SJohannes Berg 	if (WARN(skb_tailroom(skb) < tail ||
71b1e9be87SJohannes Berg 		 skb_headroom(skb) < IEEE80211_TKIP_IV_LEN,
72b1e9be87SJohannes Berg 		 "mmic: not enough head/tail (%d/%d,%d/%d)\n",
73b1e9be87SJohannes Berg 		 skb_headroom(skb), IEEE80211_TKIP_IV_LEN,
74b1e9be87SJohannes Berg 		 skb_tailroom(skb), tail))
759ae54c84SJohannes Berg 		return TX_DROP;
76f0706e82SJiri Benc 
77f0706e82SJiri Benc 	mic = skb_put(skb, MICHAEL_MIC_LEN);
789de18d81SDavid Spinadel 
799de18d81SDavid Spinadel 	if (tx->key->conf.flags & IEEE80211_KEY_FLAG_PUT_MIC_SPACE) {
809de18d81SDavid Spinadel 		/* Zeroed MIC can help with debug */
819de18d81SDavid Spinadel 		memset(mic, 0, MICHAEL_MIC_LEN);
829de18d81SDavid Spinadel 		return TX_CONTINUE;
839de18d81SDavid Spinadel 	}
849de18d81SDavid Spinadel 
859de18d81SDavid Spinadel 	key = &tx->key->conf.key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY];
868e8862b7SHarvey Harrison 	michael_mic(key, hdr, data, data_len, mic);
87681d1190SJouni Malinen 	if (unlikely(info->flags & IEEE80211_TX_INTFL_TKIP_MIC_FAILURE))
88681d1190SJouni Malinen 		mic[0]++;
89f0706e82SJiri Benc 
909ae54c84SJohannes Berg 	return TX_CONTINUE;
91f0706e82SJiri Benc }
92f0706e82SJiri Benc 
93f0706e82SJiri Benc 
949ae54c84SJohannes Berg ieee80211_rx_result
ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data * rx)955cf121c3SJohannes Berg ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)
96f0706e82SJiri Benc {
97747d753dSJouni Malinen 	u8 *data, *key = NULL;
98f0706e82SJiri Benc 	size_t data_len;
998e8862b7SHarvey Harrison 	unsigned int hdrlen;
100f0706e82SJiri Benc 	u8 mic[MICHAEL_MIC_LEN];
101f0706e82SJiri Benc 	struct sk_buff *skb = rx->skb;
102eb9fb5b8SJohannes Berg 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
103eb9fb5b8SJohannes Berg 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
104f0706e82SJiri Benc 
105816c04feSChristian Lamparter 	/*
106816c04feSChristian Lamparter 	 * it makes no sense to check for MIC errors on anything other
107816c04feSChristian Lamparter 	 * than data frames.
108816c04feSChristian Lamparter 	 */
109816c04feSChristian Lamparter 	if (!ieee80211_is_data_present(hdr->frame_control))
1109ae54c84SJohannes Berg 		return RX_CONTINUE;
111f0706e82SJiri Benc 
112816c04feSChristian Lamparter 	/*
113816c04feSChristian Lamparter 	 * No way to verify the MIC if the hardware stripped it or
114816c04feSChristian Lamparter 	 * the IV with the key index. In this case we have solely rely
115816c04feSChristian Lamparter 	 * on the driver to set RX_FLAG_MMIC_ERROR in the event of a
116816c04feSChristian Lamparter 	 * MIC failure report.
117816c04feSChristian Lamparter 	 */
118816c04feSChristian Lamparter 	if (status->flag & (RX_FLAG_MMIC_STRIPPED | RX_FLAG_IV_STRIPPED)) {
119816c04feSChristian Lamparter 		if (status->flag & RX_FLAG_MMIC_ERROR)
120b98ea058SSaravana 			goto mic_fail_no_key;
121816c04feSChristian Lamparter 
1224045f72bSStanislaw Gruszka 		if (!(status->flag & RX_FLAG_IV_STRIPPED) && rx->key &&
1234045f72bSStanislaw Gruszka 		    rx->key->conf.cipher == WLAN_CIPHER_SUITE_TKIP)
124816c04feSChristian Lamparter 			goto update_iv;
125816c04feSChristian Lamparter 
1269ae54c84SJohannes Berg 		return RX_CONTINUE;
127816c04feSChristian Lamparter 	}
128816c04feSChristian Lamparter 
129816c04feSChristian Lamparter 	/*
130816c04feSChristian Lamparter 	 * Some hardware seems to generate Michael MIC failure reports; even
131816c04feSChristian Lamparter 	 * though, the frame was not encrypted with TKIP and therefore has no
132816c04feSChristian Lamparter 	 * MIC. Ignore the flag them to avoid triggering countermeasures.
133816c04feSChristian Lamparter 	 */
134816c04feSChristian Lamparter 	if (!rx->key || rx->key->conf.cipher != WLAN_CIPHER_SUITE_TKIP ||
135816c04feSChristian Lamparter 	    !(status->flag & RX_FLAG_DECRYPTED))
136816c04feSChristian Lamparter 		return RX_CONTINUE;
137816c04feSChristian Lamparter 
138816c04feSChristian Lamparter 	if (rx->sdata->vif.type == NL80211_IFTYPE_AP && rx->key->conf.keyidx) {
139816c04feSChristian Lamparter 		/*
140816c04feSChristian Lamparter 		 * APs with pairwise keys should never receive Michael MIC
141816c04feSChristian Lamparter 		 * errors for non-zero keyidx because these are reserved for
142816c04feSChristian Lamparter 		 * group keys and only the AP is sending real multicast
14387ee475eSEmmanuel Grumbach 		 * frames in the BSS.
144816c04feSChristian Lamparter 		 */
145816c04feSChristian Lamparter 		return RX_DROP_UNUSABLE;
146816c04feSChristian Lamparter 	}
147816c04feSChristian Lamparter 
148816c04feSChristian Lamparter 	if (status->flag & RX_FLAG_MMIC_ERROR)
149816c04feSChristian Lamparter 		goto mic_fail;
150f0706e82SJiri Benc 
1518e8862b7SHarvey Harrison 	hdrlen = ieee80211_hdrlen(hdr->frame_control);
1528e8862b7SHarvey Harrison 	if (skb->len < hdrlen + MICHAEL_MIC_LEN)
153e4c26addSJohannes Berg 		return RX_DROP_UNUSABLE;
154f0706e82SJiri Benc 
155a8286911SJohannes Berg 	if (skb_linearize(rx->skb))
156a8286911SJohannes Berg 		return RX_DROP_UNUSABLE;
157a8286911SJohannes Berg 	hdr = (void *)skb->data;
158a8286911SJohannes Berg 
1598e8862b7SHarvey Harrison 	data = skb->data + hdrlen;
1608e8862b7SHarvey Harrison 	data_len = skb->len - hdrlen - MICHAEL_MIC_LEN;
161747d753dSJouni Malinen 	key = &rx->key->conf.key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY];
1628e8862b7SHarvey Harrison 	michael_mic(key, hdr, data, data_len, mic);
16398c67d18SJason A. Donenfeld 	if (crypto_memneq(mic, data + data_len, MICHAEL_MIC_LEN))
164816c04feSChristian Lamparter 		goto mic_fail;
165f0706e82SJiri Benc 
166f0706e82SJiri Benc 	/* remove Michael MIC from payload */
167f0706e82SJiri Benc 	skb_trim(skb, skb->len - MICHAEL_MIC_LEN);
168f0706e82SJiri Benc 
169816c04feSChristian Lamparter update_iv:
17050741ae0SJohannes Berg 	/* update IV in key information to be able to detect replays */
171bf30ca92SJohannes Berg 	rx->key->u.tkip.rx[rx->security_idx].iv32 = rx->tkip.iv32;
172bf30ca92SJohannes Berg 	rx->key->u.tkip.rx[rx->security_idx].iv16 = rx->tkip.iv16;
17350741ae0SJohannes Berg 
1749ae54c84SJohannes Berg 	return RX_CONTINUE;
175816c04feSChristian Lamparter 
176816c04feSChristian Lamparter mic_fail:
177b98ea058SSaravana 	rx->key->u.tkip.mic_failures++;
178b98ea058SSaravana 
179b98ea058SSaravana mic_fail_no_key:
180a66b98dbSArik Nemtsov 	/*
181a66b98dbSArik Nemtsov 	 * In some cases the key can be unset - e.g. a multicast packet, in
182a66b98dbSArik Nemtsov 	 * a driver that supports HW encryption. Send up the key idx only if
183a66b98dbSArik Nemtsov 	 * the key is set.
184a66b98dbSArik Nemtsov 	 */
1856f1f5d5fSJohannes Berg 	cfg80211_michael_mic_failure(rx->sdata->dev, hdr->addr2,
1866f1f5d5fSJohannes Berg 				     is_multicast_ether_addr(hdr->addr1) ?
1876f1f5d5fSJohannes Berg 				     NL80211_KEYTYPE_GROUP :
1886f1f5d5fSJohannes Berg 				     NL80211_KEYTYPE_PAIRWISE,
189a66b98dbSArik Nemtsov 				     rx->key ? rx->key->conf.keyidx : -1,
1906f1f5d5fSJohannes Berg 				     NULL, GFP_ATOMIC);
191816c04feSChristian Lamparter 	return RX_DROP_UNUSABLE;
192f0706e82SJiri Benc }
193f0706e82SJiri Benc 
tkip_encrypt_skb(struct ieee80211_tx_data * tx,struct sk_buff * skb)194e039fa4aSJohannes Berg static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
195f0706e82SJiri Benc {
196f0706e82SJiri Benc 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
197f0706e82SJiri Benc 	struct ieee80211_key *key = tx->key;
198e039fa4aSJohannes Berg 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
199d5184cacSHarvey Harrison 	unsigned int hdrlen;
200d5184cacSHarvey Harrison 	int len, tail;
201f8079d43SEliad Peller 	u64 pn;
202f0706e82SJiri Benc 	u8 *pos;
203f0706e82SJiri Benc 
204813d7669SJohannes Berg 	if (info->control.hw_key &&
205ee70108fSJanusz.Dziedzic@tieto.com 	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) &&
206ee70108fSJanusz.Dziedzic@tieto.com 	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) {
207813d7669SJohannes Berg 		/* hwaccel - with no need for software-generated IV */
20823c0752aSJohannes Berg 		return 0;
209e039fa4aSJohannes Berg 	}
210e039fa4aSJohannes Berg 
211d5184cacSHarvey Harrison 	hdrlen = ieee80211_hdrlen(hdr->frame_control);
212f0706e82SJiri Benc 	len = skb->len - hdrlen;
213f0706e82SJiri Benc 
214813d7669SJohannes Berg 	if (info->control.hw_key)
21523c0752aSJohannes Berg 		tail = 0;
21611a843b7SJohannes Berg 	else
2174325f6caSJohannes Berg 		tail = IEEE80211_TKIP_ICV_LEN;
2188f20fc24SJohannes Berg 
21923c0752aSJohannes Berg 	if (WARN_ON(skb_tailroom(skb) < tail ||
2204325f6caSJohannes Berg 		    skb_headroom(skb) < IEEE80211_TKIP_IV_LEN))
221f0706e82SJiri Benc 		return -1;
222f0706e82SJiri Benc 
2234325f6caSJohannes Berg 	pos = skb_push(skb, IEEE80211_TKIP_IV_LEN);
2244325f6caSJohannes Berg 	memmove(pos, pos + IEEE80211_TKIP_IV_LEN, hdrlen);
225f0706e82SJiri Benc 	pos += hdrlen;
226f0706e82SJiri Benc 
227ee70108fSJanusz.Dziedzic@tieto.com 	/* the HW only needs room for the IV, but not the actual IV */
228ee70108fSJanusz.Dziedzic@tieto.com 	if (info->control.hw_key &&
229ee70108fSJanusz.Dziedzic@tieto.com 	    (info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))
230ee70108fSJanusz.Dziedzic@tieto.com 		return 0;
231ee70108fSJanusz.Dziedzic@tieto.com 
232f0706e82SJiri Benc 	/* Increase IV for the frame */
233f8079d43SEliad Peller 	pn = atomic64_inc_return(&key->conf.tx_pn);
234f8079d43SEliad Peller 	pos = ieee80211_tkip_add_iv(pos, &key->conf, pn);
235f0706e82SJiri Benc 
236813d7669SJohannes Berg 	/* hwaccel - with software IV */
237813d7669SJohannes Berg 	if (info->control.hw_key)
238f0706e82SJiri Benc 		return 0;
239f0706e82SJiri Benc 
240f0706e82SJiri Benc 	/* Add room for ICV */
2414325f6caSJohannes Berg 	skb_put(skb, IEEE80211_TKIP_ICV_LEN);
242f0706e82SJiri Benc 
2435fdb3735SArd Biesheuvel 	return ieee80211_tkip_encrypt_data(&tx->local->wep_tx_ctx,
244523b02eaSJohannes Berg 					   key, skb, pos, len);
245f0706e82SJiri Benc }
246f0706e82SJiri Benc 
247f0706e82SJiri Benc 
2489ae54c84SJohannes Berg ieee80211_tx_result
ieee80211_crypto_tkip_encrypt(struct ieee80211_tx_data * tx)2495cf121c3SJohannes Berg ieee80211_crypto_tkip_encrypt(struct ieee80211_tx_data *tx)
250f0706e82SJiri Benc {
251252b86c4SJohannes Berg 	struct sk_buff *skb;
252f0706e82SJiri Benc 
2535cf121c3SJohannes Berg 	ieee80211_tx_set_protected(tx);
254f0706e82SJiri Benc 
255252b86c4SJohannes Berg 	skb_queue_walk(&tx->skbs, skb) {
256e039fa4aSJohannes Berg 		if (tkip_encrypt_skb(tx, skb) < 0)
2579ae54c84SJohannes Berg 			return TX_DROP;
258252b86c4SJohannes Berg 	}
259f0706e82SJiri Benc 
2609ae54c84SJohannes Berg 	return TX_CONTINUE;
261f0706e82SJiri Benc }
262f0706e82SJiri Benc 
263f0706e82SJiri Benc 
2649ae54c84SJohannes Berg ieee80211_rx_result
ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data * rx)2655cf121c3SJohannes Berg ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
266f0706e82SJiri Benc {
267f0706e82SJiri Benc 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
268747d753dSJouni Malinen 	int hdrlen, res, hwaccel = 0;
269f0706e82SJiri Benc 	struct ieee80211_key *key = rx->key;
270f0706e82SJiri Benc 	struct sk_buff *skb = rx->skb;
271eb9fb5b8SJohannes Berg 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
272f0706e82SJiri Benc 
273d5184cacSHarvey Harrison 	hdrlen = ieee80211_hdrlen(hdr->frame_control);
274f0706e82SJiri Benc 
275c34498b9SHarvey Harrison 	if (!ieee80211_is_data(hdr->frame_control))
2769ae54c84SJohannes Berg 		return RX_CONTINUE;
277f0706e82SJiri Benc 
278f0706e82SJiri Benc 	if (!rx->sta || skb->len - hdrlen < 12)
279e4c26addSJohannes Berg 		return RX_DROP_UNUSABLE;
280f0706e82SJiri Benc 
281a8286911SJohannes Berg 	/* it may be possible to optimize this a bit more */
282a8286911SJohannes Berg 	if (skb_linearize(rx->skb))
283a8286911SJohannes Berg 		return RX_DROP_UNUSABLE;
284a8286911SJohannes Berg 	hdr = (void *)skb->data;
285a8286911SJohannes Berg 
2867848ba7dSJohannes Berg 	/*
287dc1580ddSJohannes Berg 	 * Let TKIP code verify IV, but skip decryption.
288dc1580ddSJohannes Berg 	 * In the case where hardware checks the IV as well,
289dc1580ddSJohannes Berg 	 * we don't even get here, see ieee80211_rx_h_decrypt()
2907848ba7dSJohannes Berg 	 */
291dc1580ddSJohannes Berg 	if (status->flag & RX_FLAG_DECRYPTED)
292f0706e82SJiri Benc 		hwaccel = 1;
293f0706e82SJiri Benc 
2945fdb3735SArd Biesheuvel 	res = ieee80211_tkip_decrypt_data(&rx->local->wep_rx_ctx,
295f0706e82SJiri Benc 					  key, skb->data + hdrlen,
29617741cdcSJohannes Berg 					  skb->len - hdrlen, rx->sta->sta.addr,
2979e26297aSJohannes Berg 					  hdr->addr1, hwaccel, rx->security_idx,
298bf30ca92SJohannes Berg 					  &rx->tkip.iv32,
299bf30ca92SJohannes Berg 					  &rx->tkip.iv16);
300747d753dSJouni Malinen 	if (res != TKIP_DECRYPT_OK)
301e4c26addSJohannes Berg 		return RX_DROP_UNUSABLE;
302f0706e82SJiri Benc 
303f0706e82SJiri Benc 	/* Trim ICV */
304cef0acd4SDavid Spinadel 	if (!(status->flag & RX_FLAG_ICV_STRIPPED))
3054325f6caSJohannes Berg 		skb_trim(skb, skb->len - IEEE80211_TKIP_ICV_LEN);
306f0706e82SJiri Benc 
307f0706e82SJiri Benc 	/* Remove IV */
3084325f6caSJohannes Berg 	memmove(skb->data + IEEE80211_TKIP_IV_LEN, skb->data, hdrlen);
3094325f6caSJohannes Berg 	skb_pull(skb, IEEE80211_TKIP_IV_LEN);
310f0706e82SJiri Benc 
3119ae54c84SJohannes Berg 	return RX_CONTINUE;
312f0706e82SJiri Benc }
313f0706e82SJiri Benc 
31497f7a470SJohannes Berg /*
31597f7a470SJohannes Berg  * Calculate AAD for CCMP/GCMP, returning qos_tid since we
31697f7a470SJohannes Berg  * need that in CCMP also for b_0.
31797f7a470SJohannes Berg  */
ccmp_gcmp_aad(struct sk_buff * skb,u8 * aad)31897f7a470SJohannes Berg static u8 ccmp_gcmp_aad(struct sk_buff *skb, u8 *aad)
319f0706e82SJiri Benc {
32097f7a470SJohannes Berg 	struct ieee80211_hdr *hdr = (void *)skb->data;
321f14df804SHarvey Harrison 	__le16 mask_fc;
322fb733336SJouni Malinen 	int a4_included, mgmt;
323f14df804SHarvey Harrison 	u8 qos_tid;
324fa4d58daSPing-Ke Shih 	u16 len_a = 22;
325f0706e82SJiri Benc 
326f14df804SHarvey Harrison 	/*
327fb733336SJouni Malinen 	 * Mask FC: zero subtype b4 b5 b6 (if not mgmt)
328fa4d58daSPing-Ke Shih 	 * Retry, PwrMgt, MoreData, Order (if Qos Data); set Protected
329f14df804SHarvey Harrison 	 */
330fb733336SJouni Malinen 	mgmt = ieee80211_is_mgmt(hdr->frame_control);
331f14df804SHarvey Harrison 	mask_fc = hdr->frame_control;
332fb733336SJouni Malinen 	mask_fc &= ~cpu_to_le16(IEEE80211_FCTL_RETRY |
333f14df804SHarvey Harrison 				IEEE80211_FCTL_PM | IEEE80211_FCTL_MOREDATA);
334fb733336SJouni Malinen 	if (!mgmt)
335fb733336SJouni Malinen 		mask_fc &= ~cpu_to_le16(0x0070);
336f14df804SHarvey Harrison 	mask_fc |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
337f0706e82SJiri Benc 
338f14df804SHarvey Harrison 	a4_included = ieee80211_has_a4(hdr->frame_control);
339fa4d58daSPing-Ke Shih 	if (a4_included)
340fa4d58daSPing-Ke Shih 		len_a += 6;
341f14df804SHarvey Harrison 
342fa4d58daSPing-Ke Shih 	if (ieee80211_is_data_qos(hdr->frame_control)) {
343a1f2ba04SSara Sharon 		qos_tid = ieee80211_get_tid(hdr);
344fa4d58daSPing-Ke Shih 		mask_fc &= ~cpu_to_le16(IEEE80211_FCTL_ORDER);
345fa4d58daSPing-Ke Shih 		len_a += 2;
346fa4d58daSPing-Ke Shih 	} else {
347f14df804SHarvey Harrison 		qos_tid = 0;
348fa4d58daSPing-Ke Shih 	}
349f14df804SHarvey Harrison 
350f0706e82SJiri Benc 	/* AAD (extra authenticate-only data) / masked 802.11 header
351f0706e82SJiri Benc 	 * FC | A1 | A2 | A3 | SC | [A4] | [QC] */
352f14df804SHarvey Harrison 	put_unaligned_be16(len_a, &aad[0]);
353f14df804SHarvey Harrison 	put_unaligned(mask_fc, (__le16 *)&aad[2]);
3542aec9099SJohannes Berg 	memcpy(&aad[4], &hdr->addrs, 3 * ETH_ALEN);
355f0706e82SJiri Benc 
356f0706e82SJiri Benc 	/* Mask Seq#, leave Frag# */
357f0706e82SJiri Benc 	aad[22] = *((u8 *) &hdr->seq_ctrl) & 0x0f;
358f0706e82SJiri Benc 	aad[23] = 0;
359f14df804SHarvey Harrison 
360f0706e82SJiri Benc 	if (a4_included) {
36173e1f7c8SHarvey Harrison 		memcpy(&aad[24], hdr->addr4, ETH_ALEN);
362f14df804SHarvey Harrison 		aad[30] = qos_tid;
363f0706e82SJiri Benc 		aad[31] = 0;
364f14df804SHarvey Harrison 	} else {
36573e1f7c8SHarvey Harrison 		memset(&aad[24], 0, ETH_ALEN + IEEE80211_QOS_CTL_LEN);
366f14df804SHarvey Harrison 		aad[24] = qos_tid;
367f0706e82SJiri Benc 	}
36897f7a470SJohannes Berg 
36997f7a470SJohannes Berg 	return qos_tid;
370f0706e82SJiri Benc }
371f0706e82SJiri Benc 
ccmp_special_blocks(struct sk_buff * skb,u8 * pn,u8 * b_0,u8 * aad)37297f7a470SJohannes Berg static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad)
37397f7a470SJohannes Berg {
37497f7a470SJohannes Berg 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
37597f7a470SJohannes Berg 	u8 qos_tid = ccmp_gcmp_aad(skb, aad);
37697f7a470SJohannes Berg 
37797f7a470SJohannes Berg 	/* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC
37897f7a470SJohannes Berg 	 * mode authentication are not allowed to collide, yet both are derived
37997f7a470SJohannes Berg 	 * from this vector b_0. We only set L := 1 here to indicate that the
38097f7a470SJohannes Berg 	 * data size can be represented in (L+1) bytes. The CCM layer will take
38197f7a470SJohannes Berg 	 * care of storing the data length in the top (L+1) bytes and setting
38297f7a470SJohannes Berg 	 * and clearing the other bits as is required to derive the two IVs.
38397f7a470SJohannes Berg 	 */
38497f7a470SJohannes Berg 	b_0[0] = 0x1;
38597f7a470SJohannes Berg 
38697f7a470SJohannes Berg 	/* Nonce: Nonce Flags | A2 | PN
38797f7a470SJohannes Berg 	 * Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7)
38897f7a470SJohannes Berg 	 */
38997f7a470SJohannes Berg 	b_0[1] = qos_tid | (ieee80211_is_mgmt(hdr->frame_control) << 4);
39097f7a470SJohannes Berg 	memcpy(&b_0[2], hdr->addr2, ETH_ALEN);
39197f7a470SJohannes Berg 	memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN);
39297f7a470SJohannes Berg }
393f0706e82SJiri Benc 
ccmp_pn2hdr(u8 * hdr,u8 * pn,int key_id)394f0706e82SJiri Benc static inline void ccmp_pn2hdr(u8 *hdr, u8 *pn, int key_id)
395f0706e82SJiri Benc {
396f0706e82SJiri Benc 	hdr[0] = pn[5];
397f0706e82SJiri Benc 	hdr[1] = pn[4];
398f0706e82SJiri Benc 	hdr[2] = 0;
399f0706e82SJiri Benc 	hdr[3] = 0x20 | (key_id << 6);
400f0706e82SJiri Benc 	hdr[4] = pn[3];
401f0706e82SJiri Benc 	hdr[5] = pn[2];
402f0706e82SJiri Benc 	hdr[6] = pn[1];
403f0706e82SJiri Benc 	hdr[7] = pn[0];
404f0706e82SJiri Benc }
405f0706e82SJiri Benc 
406f0706e82SJiri Benc 
ccmp_hdr2pn(u8 * pn,u8 * hdr)407c6a1fa12SJohannes Berg static inline void ccmp_hdr2pn(u8 *pn, u8 *hdr)
408f0706e82SJiri Benc {
409f0706e82SJiri Benc 	pn[0] = hdr[7];
410f0706e82SJiri Benc 	pn[1] = hdr[6];
411f0706e82SJiri Benc 	pn[2] = hdr[5];
412f0706e82SJiri Benc 	pn[3] = hdr[4];
413f0706e82SJiri Benc 	pn[4] = hdr[1];
414f0706e82SJiri Benc 	pn[5] = hdr[0];
415f0706e82SJiri Benc }
416f0706e82SJiri Benc 
417f0706e82SJiri Benc 
ccmp_encrypt_skb(struct ieee80211_tx_data * tx,struct sk_buff * skb,unsigned int mic_len)4182b2ba0dbSJouni Malinen static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb,
4192b2ba0dbSJouni Malinen 			    unsigned int mic_len)
420f0706e82SJiri Benc {
421f0706e82SJiri Benc 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
422f0706e82SJiri Benc 	struct ieee80211_key *key = tx->key;
423e039fa4aSJohannes Berg 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
42423c0752aSJohannes Berg 	int hdrlen, len, tail;
425aba83a0bSJohannes Berg 	u8 *pos;
426aba83a0bSJohannes Berg 	u8 pn[6];
427aba83a0bSJohannes Berg 	u64 pn64;
428f4a067f9SArd Biesheuvel 	u8 aad[CCM_AAD_LEN];
4297ec7c4a9SArd Biesheuvel 	u8 b_0[AES_BLOCK_SIZE];
4301f7d77abSJouni Malinen 
431813d7669SJohannes Berg 	if (info->control.hw_key &&
432077a9154SArik Nemtsov 	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) &&
43317d38fa8SMarek Kwaczynski 	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) &&
43417d38fa8SMarek Kwaczynski 	    !((info->control.hw_key->flags &
43517d38fa8SMarek Kwaczynski 	       IEEE80211_KEY_FLAG_GENERATE_IV_MGMT) &&
43617d38fa8SMarek Kwaczynski 	      ieee80211_is_mgmt(hdr->frame_control))) {
437813d7669SJohannes Berg 		/*
438813d7669SJohannes Berg 		 * hwaccel has no need for preallocated room for CCMP
439813d7669SJohannes Berg 		 * header or MIC fields
440813d7669SJohannes Berg 		 */
44123c0752aSJohannes Berg 		return 0;
442e039fa4aSJohannes Berg 	}
443e039fa4aSJohannes Berg 
444d5184cacSHarvey Harrison 	hdrlen = ieee80211_hdrlen(hdr->frame_control);
445f0706e82SJiri Benc 	len = skb->len - hdrlen;
446f0706e82SJiri Benc 
447813d7669SJohannes Berg 	if (info->control.hw_key)
44823c0752aSJohannes Berg 		tail = 0;
44911a843b7SJohannes Berg 	else
4502b2ba0dbSJouni Malinen 		tail = mic_len;
451f0706e82SJiri Benc 
45223c0752aSJohannes Berg 	if (WARN_ON(skb_tailroom(skb) < tail ||
4534325f6caSJohannes Berg 		    skb_headroom(skb) < IEEE80211_CCMP_HDR_LEN))
454f0706e82SJiri Benc 		return -1;
455f0706e82SJiri Benc 
4564325f6caSJohannes Berg 	pos = skb_push(skb, IEEE80211_CCMP_HDR_LEN);
4574325f6caSJohannes Berg 	memmove(pos, pos + IEEE80211_CCMP_HDR_LEN, hdrlen);
458077a9154SArik Nemtsov 
459077a9154SArik Nemtsov 	/* the HW only needs room for the IV, but not the actual IV */
460e0830f71SArik Nemtsov 	if (info->control.hw_key &&
461e0830f71SArik Nemtsov 	    (info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))
462077a9154SArik Nemtsov 		return 0;
463077a9154SArik Nemtsov 
464f0706e82SJiri Benc 	pos += hdrlen;
465f0706e82SJiri Benc 
466db388a56SJohannes Berg 	pn64 = atomic64_inc_return(&key->conf.tx_pn);
467f0706e82SJiri Benc 
468aba83a0bSJohannes Berg 	pn[5] = pn64;
469aba83a0bSJohannes Berg 	pn[4] = pn64 >> 8;
470aba83a0bSJohannes Berg 	pn[3] = pn64 >> 16;
471aba83a0bSJohannes Berg 	pn[2] = pn64 >> 24;
472aba83a0bSJohannes Berg 	pn[1] = pn64 >> 32;
473aba83a0bSJohannes Berg 	pn[0] = pn64 >> 40;
474f0706e82SJiri Benc 
4758f20fc24SJohannes Berg 	ccmp_pn2hdr(pos, pn, key->conf.keyidx);
476f0706e82SJiri Benc 
477813d7669SJohannes Berg 	/* hwaccel - with software CCMP header */
478813d7669SJohannes Berg 	if (info->control.hw_key)
479f0706e82SJiri Benc 		return 0;
480f0706e82SJiri Benc 
4814325f6caSJohannes Berg 	pos += IEEE80211_CCMP_HDR_LEN;
48230ef7ef9SArd Biesheuvel 	ccmp_special_blocks(skb, pn, b_0, aad);
483f4a067f9SArd Biesheuvel 	return ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len,
4844133da73SXiang Gao 					 skb_put(skb, mic_len));
485f0706e82SJiri Benc }
486f0706e82SJiri Benc 
487f0706e82SJiri Benc 
4889ae54c84SJohannes Berg ieee80211_tx_result
ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data * tx,unsigned int mic_len)4892b2ba0dbSJouni Malinen ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx,
4902b2ba0dbSJouni Malinen 			      unsigned int mic_len)
491f0706e82SJiri Benc {
492252b86c4SJohannes Berg 	struct sk_buff *skb;
493f0706e82SJiri Benc 
4945cf121c3SJohannes Berg 	ieee80211_tx_set_protected(tx);
495f0706e82SJiri Benc 
496252b86c4SJohannes Berg 	skb_queue_walk(&tx->skbs, skb) {
4972b2ba0dbSJouni Malinen 		if (ccmp_encrypt_skb(tx, skb, mic_len) < 0)
4989ae54c84SJohannes Berg 			return TX_DROP;
499252b86c4SJohannes Berg 	}
500f0706e82SJiri Benc 
5019ae54c84SJohannes Berg 	return TX_CONTINUE;
502f0706e82SJiri Benc }
503f0706e82SJiri Benc 
504f0706e82SJiri Benc 
5059ae54c84SJohannes Berg ieee80211_rx_result
ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data * rx,unsigned int mic_len)5062b2ba0dbSJouni Malinen ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx,
5072b2ba0dbSJouni Malinen 			      unsigned int mic_len)
508f0706e82SJiri Benc {
509f0706e82SJiri Benc 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
510f0706e82SJiri Benc 	int hdrlen;
511f0706e82SJiri Benc 	struct ieee80211_key *key = rx->key;
512f0706e82SJiri Benc 	struct sk_buff *skb = rx->skb;
513eb9fb5b8SJohannes Berg 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
5144325f6caSJohannes Berg 	u8 pn[IEEE80211_CCMP_PN_LEN];
515f0706e82SJiri Benc 	int data_len;
5169190252cSJouni Malinen 	int queue;
517f0706e82SJiri Benc 
518d5184cacSHarvey Harrison 	hdrlen = ieee80211_hdrlen(hdr->frame_control);
519f0706e82SJiri Benc 
520fb733336SJouni Malinen 	if (!ieee80211_is_data(hdr->frame_control) &&
521d8ca16dbSJohannes Berg 	    !ieee80211_is_robust_mgmt_frame(skb))
5229ae54c84SJohannes Berg 		return RX_CONTINUE;
523f0706e82SJiri Benc 
524a8286911SJohannes Berg 	if (status->flag & RX_FLAG_DECRYPTED) {
5254325f6caSJohannes Berg 		if (!pskb_may_pull(rx->skb, hdrlen + IEEE80211_CCMP_HDR_LEN))
526a8286911SJohannes Berg 			return RX_DROP_UNUSABLE;
527f980ebc0SSara Sharon 		if (status->flag & RX_FLAG_MIC_STRIPPED)
528f980ebc0SSara Sharon 			mic_len = 0;
529a8286911SJohannes Berg 	} else {
530a8286911SJohannes Berg 		if (skb_linearize(rx->skb))
531a8286911SJohannes Berg 			return RX_DROP_UNUSABLE;
532a8286911SJohannes Berg 	}
533a8286911SJohannes Berg 
53494513069SJohannes Berg 	/* reload hdr - skb might have been reallocated */
53594513069SJohannes Berg 	hdr = (void *)rx->skb->data;
53694513069SJohannes Berg 
537f980ebc0SSara Sharon 	data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN - mic_len;
538f980ebc0SSara Sharon 	if (!rx->sta || data_len < 0)
539f980ebc0SSara Sharon 		return RX_DROP_UNUSABLE;
540f980ebc0SSara Sharon 
541981d94a8SJohannes Berg 	if (!(status->flag & RX_FLAG_PN_VALIDATED)) {
542f631a77bSSara Sharon 		int res;
543f631a77bSSara Sharon 
544c6a1fa12SJohannes Berg 		ccmp_hdr2pn(pn, skb->data + hdrlen);
545f0706e82SJiri Benc 
5469e26297aSJohannes Berg 		queue = rx->security_idx;
5479190252cSJouni Malinen 
548f631a77bSSara Sharon 		res = memcmp(pn, key->u.ccmp.rx_pn[queue],
549f631a77bSSara Sharon 			     IEEE80211_CCMP_PN_LEN);
550f631a77bSSara Sharon 		if (res < 0 ||
551f631a77bSSara Sharon 		    (!res && !(status->flag & RX_FLAG_ALLOW_SAME_PN))) {
552f0706e82SJiri Benc 			key->u.ccmp.replays++;
553baa951a1SJohannes Berg 			return RX_DROP_U_REPLAY;
554f0706e82SJiri Benc 		}
555f0706e82SJiri Benc 
556eb9fb5b8SJohannes Berg 		if (!(status->flag & RX_FLAG_DECRYPTED)) {
5577ec7c4a9SArd Biesheuvel 			u8 aad[2 * AES_BLOCK_SIZE];
5587ec7c4a9SArd Biesheuvel 			u8 b_0[AES_BLOCK_SIZE];
5597848ba7dSJohannes Berg 			/* hardware didn't decrypt/verify MIC */
56030ef7ef9SArd Biesheuvel 			ccmp_special_blocks(skb, pn, b_0, aad);
561f0706e82SJiri Benc 
562f0706e82SJiri Benc 			if (ieee80211_aes_ccm_decrypt(
5637ec7c4a9SArd Biesheuvel 				    key->u.ccmp.tfm, b_0, aad,
5644325f6caSJohannes Berg 				    skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN,
5654325f6caSJohannes Berg 				    data_len,
5664133da73SXiang Gao 				    skb->data + skb->len - mic_len))
567baa951a1SJohannes Berg 				return RX_DROP_U_MIC_FAIL;
568f0706e82SJiri Benc 		}
569f0706e82SJiri Benc 
5704325f6caSJohannes Berg 		memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN);
571bf30ca92SJohannes Berg 		if (unlikely(ieee80211_is_frag(hdr)))
572bf30ca92SJohannes Berg 			memcpy(rx->ccm_gcm.pn, pn, IEEE80211_CCMP_PN_LEN);
573981d94a8SJohannes Berg 	}
574f0706e82SJiri Benc 
575f0706e82SJiri Benc 	/* Remove CCMP header and MIC */
5762b2ba0dbSJouni Malinen 	if (pskb_trim(skb, skb->len - mic_len))
577a8286911SJohannes Berg 		return RX_DROP_UNUSABLE;
5784325f6caSJohannes Berg 	memmove(skb->data + IEEE80211_CCMP_HDR_LEN, skb->data, hdrlen);
5794325f6caSJohannes Berg 	skb_pull(skb, IEEE80211_CCMP_HDR_LEN);
580f0706e82SJiri Benc 
5819ae54c84SJohannes Berg 	return RX_CONTINUE;
582f0706e82SJiri Benc }
583765cb46aSJouni Malinen 
gcmp_special_blocks(struct sk_buff * skb,u8 * pn,u8 * j_0,u8 * aad)58400b9cfa3SJouni Malinen static void gcmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *j_0, u8 *aad)
58500b9cfa3SJouni Malinen {
58697f7a470SJohannes Berg 	struct ieee80211_hdr *hdr = (void *)skb->data;
58700b9cfa3SJouni Malinen 
58800b9cfa3SJouni Malinen 	memcpy(j_0, hdr->addr2, ETH_ALEN);
58900b9cfa3SJouni Malinen 	memcpy(&j_0[ETH_ALEN], pn, IEEE80211_GCMP_PN_LEN);
59000b9cfa3SJouni Malinen 	j_0[13] = 0;
59100b9cfa3SJouni Malinen 	j_0[14] = 0;
59200b9cfa3SJouni Malinen 	j_0[AES_BLOCK_SIZE - 1] = 0x01;
59300b9cfa3SJouni Malinen 
59497f7a470SJohannes Berg 	ccmp_gcmp_aad(skb, aad);
59500b9cfa3SJouni Malinen }
59600b9cfa3SJouni Malinen 
gcmp_pn2hdr(u8 * hdr,const u8 * pn,int key_id)59700b9cfa3SJouni Malinen static inline void gcmp_pn2hdr(u8 *hdr, const u8 *pn, int key_id)
59800b9cfa3SJouni Malinen {
59900b9cfa3SJouni Malinen 	hdr[0] = pn[5];
60000b9cfa3SJouni Malinen 	hdr[1] = pn[4];
60100b9cfa3SJouni Malinen 	hdr[2] = 0;
60200b9cfa3SJouni Malinen 	hdr[3] = 0x20 | (key_id << 6);
60300b9cfa3SJouni Malinen 	hdr[4] = pn[3];
60400b9cfa3SJouni Malinen 	hdr[5] = pn[2];
60500b9cfa3SJouni Malinen 	hdr[6] = pn[1];
60600b9cfa3SJouni Malinen 	hdr[7] = pn[0];
60700b9cfa3SJouni Malinen }
60800b9cfa3SJouni Malinen 
gcmp_hdr2pn(u8 * pn,const u8 * hdr)60900b9cfa3SJouni Malinen static inline void gcmp_hdr2pn(u8 *pn, const u8 *hdr)
61000b9cfa3SJouni Malinen {
61100b9cfa3SJouni Malinen 	pn[0] = hdr[7];
61200b9cfa3SJouni Malinen 	pn[1] = hdr[6];
61300b9cfa3SJouni Malinen 	pn[2] = hdr[5];
61400b9cfa3SJouni Malinen 	pn[3] = hdr[4];
61500b9cfa3SJouni Malinen 	pn[4] = hdr[1];
61600b9cfa3SJouni Malinen 	pn[5] = hdr[0];
61700b9cfa3SJouni Malinen }
61800b9cfa3SJouni Malinen 
gcmp_encrypt_skb(struct ieee80211_tx_data * tx,struct sk_buff * skb)61900b9cfa3SJouni Malinen static int gcmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
62000b9cfa3SJouni Malinen {
62100b9cfa3SJouni Malinen 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
62200b9cfa3SJouni Malinen 	struct ieee80211_key *key = tx->key;
62300b9cfa3SJouni Malinen 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
62400b9cfa3SJouni Malinen 	int hdrlen, len, tail;
62500b9cfa3SJouni Malinen 	u8 *pos;
62600b9cfa3SJouni Malinen 	u8 pn[6];
62700b9cfa3SJouni Malinen 	u64 pn64;
628f4a067f9SArd Biesheuvel 	u8 aad[GCM_AAD_LEN];
62900b9cfa3SJouni Malinen 	u8 j_0[AES_BLOCK_SIZE];
63000b9cfa3SJouni Malinen 
63100b9cfa3SJouni Malinen 	if (info->control.hw_key &&
63200b9cfa3SJouni Malinen 	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) &&
63300b9cfa3SJouni Malinen 	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) &&
63400b9cfa3SJouni Malinen 	    !((info->control.hw_key->flags &
63500b9cfa3SJouni Malinen 	       IEEE80211_KEY_FLAG_GENERATE_IV_MGMT) &&
63600b9cfa3SJouni Malinen 	      ieee80211_is_mgmt(hdr->frame_control))) {
63700b9cfa3SJouni Malinen 		/* hwaccel has no need for preallocated room for GCMP
63800b9cfa3SJouni Malinen 		 * header or MIC fields
63900b9cfa3SJouni Malinen 		 */
64000b9cfa3SJouni Malinen 		return 0;
64100b9cfa3SJouni Malinen 	}
64200b9cfa3SJouni Malinen 
64300b9cfa3SJouni Malinen 	hdrlen = ieee80211_hdrlen(hdr->frame_control);
64400b9cfa3SJouni Malinen 	len = skb->len - hdrlen;
64500b9cfa3SJouni Malinen 
64600b9cfa3SJouni Malinen 	if (info->control.hw_key)
64700b9cfa3SJouni Malinen 		tail = 0;
64800b9cfa3SJouni Malinen 	else
64900b9cfa3SJouni Malinen 		tail = IEEE80211_GCMP_MIC_LEN;
65000b9cfa3SJouni Malinen 
65100b9cfa3SJouni Malinen 	if (WARN_ON(skb_tailroom(skb) < tail ||
65200b9cfa3SJouni Malinen 		    skb_headroom(skb) < IEEE80211_GCMP_HDR_LEN))
65300b9cfa3SJouni Malinen 		return -1;
65400b9cfa3SJouni Malinen 
65500b9cfa3SJouni Malinen 	pos = skb_push(skb, IEEE80211_GCMP_HDR_LEN);
65600b9cfa3SJouni Malinen 	memmove(pos, pos + IEEE80211_GCMP_HDR_LEN, hdrlen);
65700b9cfa3SJouni Malinen 	skb_set_network_header(skb, skb_network_offset(skb) +
65800b9cfa3SJouni Malinen 				    IEEE80211_GCMP_HDR_LEN);
65900b9cfa3SJouni Malinen 
66000b9cfa3SJouni Malinen 	/* the HW only needs room for the IV, but not the actual IV */
66100b9cfa3SJouni Malinen 	if (info->control.hw_key &&
66200b9cfa3SJouni Malinen 	    (info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))
66300b9cfa3SJouni Malinen 		return 0;
66400b9cfa3SJouni Malinen 
66500b9cfa3SJouni Malinen 	pos += hdrlen;
66600b9cfa3SJouni Malinen 
667db388a56SJohannes Berg 	pn64 = atomic64_inc_return(&key->conf.tx_pn);
66800b9cfa3SJouni Malinen 
66900b9cfa3SJouni Malinen 	pn[5] = pn64;
67000b9cfa3SJouni Malinen 	pn[4] = pn64 >> 8;
67100b9cfa3SJouni Malinen 	pn[3] = pn64 >> 16;
67200b9cfa3SJouni Malinen 	pn[2] = pn64 >> 24;
67300b9cfa3SJouni Malinen 	pn[1] = pn64 >> 32;
67400b9cfa3SJouni Malinen 	pn[0] = pn64 >> 40;
67500b9cfa3SJouni Malinen 
67600b9cfa3SJouni Malinen 	gcmp_pn2hdr(pos, pn, key->conf.keyidx);
67700b9cfa3SJouni Malinen 
67800b9cfa3SJouni Malinen 	/* hwaccel - with software GCMP header */
67900b9cfa3SJouni Malinen 	if (info->control.hw_key)
68000b9cfa3SJouni Malinen 		return 0;
68100b9cfa3SJouni Malinen 
68200b9cfa3SJouni Malinen 	pos += IEEE80211_GCMP_HDR_LEN;
68300b9cfa3SJouni Malinen 	gcmp_special_blocks(skb, pn, j_0, aad);
684f4a067f9SArd Biesheuvel 	return ieee80211_aes_gcm_encrypt(key->u.gcmp.tfm, j_0, aad, pos, len,
68500b9cfa3SJouni Malinen 					 skb_put(skb, IEEE80211_GCMP_MIC_LEN));
68600b9cfa3SJouni Malinen }
68700b9cfa3SJouni Malinen 
68800b9cfa3SJouni Malinen ieee80211_tx_result
ieee80211_crypto_gcmp_encrypt(struct ieee80211_tx_data * tx)68900b9cfa3SJouni Malinen ieee80211_crypto_gcmp_encrypt(struct ieee80211_tx_data *tx)
69000b9cfa3SJouni Malinen {
69100b9cfa3SJouni Malinen 	struct sk_buff *skb;
69200b9cfa3SJouni Malinen 
69300b9cfa3SJouni Malinen 	ieee80211_tx_set_protected(tx);
69400b9cfa3SJouni Malinen 
69500b9cfa3SJouni Malinen 	skb_queue_walk(&tx->skbs, skb) {
69600b9cfa3SJouni Malinen 		if (gcmp_encrypt_skb(tx, skb) < 0)
69700b9cfa3SJouni Malinen 			return TX_DROP;
69800b9cfa3SJouni Malinen 	}
69900b9cfa3SJouni Malinen 
70000b9cfa3SJouni Malinen 	return TX_CONTINUE;
70100b9cfa3SJouni Malinen }
70200b9cfa3SJouni Malinen 
70300b9cfa3SJouni Malinen ieee80211_rx_result
ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data * rx)70400b9cfa3SJouni Malinen ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx)
70500b9cfa3SJouni Malinen {
70600b9cfa3SJouni Malinen 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
70700b9cfa3SJouni Malinen 	int hdrlen;
70800b9cfa3SJouni Malinen 	struct ieee80211_key *key = rx->key;
70900b9cfa3SJouni Malinen 	struct sk_buff *skb = rx->skb;
71000b9cfa3SJouni Malinen 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
71100b9cfa3SJouni Malinen 	u8 pn[IEEE80211_GCMP_PN_LEN];
712f980ebc0SSara Sharon 	int data_len, queue, mic_len = IEEE80211_GCMP_MIC_LEN;
71300b9cfa3SJouni Malinen 
71400b9cfa3SJouni Malinen 	hdrlen = ieee80211_hdrlen(hdr->frame_control);
71500b9cfa3SJouni Malinen 
71600b9cfa3SJouni Malinen 	if (!ieee80211_is_data(hdr->frame_control) &&
71700b9cfa3SJouni Malinen 	    !ieee80211_is_robust_mgmt_frame(skb))
71800b9cfa3SJouni Malinen 		return RX_CONTINUE;
71900b9cfa3SJouni Malinen 
72000b9cfa3SJouni Malinen 	if (status->flag & RX_FLAG_DECRYPTED) {
72100b9cfa3SJouni Malinen 		if (!pskb_may_pull(rx->skb, hdrlen + IEEE80211_GCMP_HDR_LEN))
72200b9cfa3SJouni Malinen 			return RX_DROP_UNUSABLE;
723f980ebc0SSara Sharon 		if (status->flag & RX_FLAG_MIC_STRIPPED)
724f980ebc0SSara Sharon 			mic_len = 0;
72500b9cfa3SJouni Malinen 	} else {
72600b9cfa3SJouni Malinen 		if (skb_linearize(rx->skb))
72700b9cfa3SJouni Malinen 			return RX_DROP_UNUSABLE;
72800b9cfa3SJouni Malinen 	}
72900b9cfa3SJouni Malinen 
73094513069SJohannes Berg 	/* reload hdr - skb might have been reallocated */
73194513069SJohannes Berg 	hdr = (void *)rx->skb->data;
73294513069SJohannes Berg 
733f980ebc0SSara Sharon 	data_len = skb->len - hdrlen - IEEE80211_GCMP_HDR_LEN - mic_len;
734f980ebc0SSara Sharon 	if (!rx->sta || data_len < 0)
735f980ebc0SSara Sharon 		return RX_DROP_UNUSABLE;
736f980ebc0SSara Sharon 
737981d94a8SJohannes Berg 	if (!(status->flag & RX_FLAG_PN_VALIDATED)) {
738f631a77bSSara Sharon 		int res;
739f631a77bSSara Sharon 
74000b9cfa3SJouni Malinen 		gcmp_hdr2pn(pn, skb->data + hdrlen);
74100b9cfa3SJouni Malinen 
74200b9cfa3SJouni Malinen 		queue = rx->security_idx;
74300b9cfa3SJouni Malinen 
744f631a77bSSara Sharon 		res = memcmp(pn, key->u.gcmp.rx_pn[queue],
745f631a77bSSara Sharon 			     IEEE80211_GCMP_PN_LEN);
746f631a77bSSara Sharon 		if (res < 0 ||
747f631a77bSSara Sharon 		    (!res && !(status->flag & RX_FLAG_ALLOW_SAME_PN))) {
74800b9cfa3SJouni Malinen 			key->u.gcmp.replays++;
749baa951a1SJohannes Berg 			return RX_DROP_U_REPLAY;
75000b9cfa3SJouni Malinen 		}
75100b9cfa3SJouni Malinen 
75200b9cfa3SJouni Malinen 		if (!(status->flag & RX_FLAG_DECRYPTED)) {
75300b9cfa3SJouni Malinen 			u8 aad[2 * AES_BLOCK_SIZE];
75400b9cfa3SJouni Malinen 			u8 j_0[AES_BLOCK_SIZE];
75500b9cfa3SJouni Malinen 			/* hardware didn't decrypt/verify MIC */
75600b9cfa3SJouni Malinen 			gcmp_special_blocks(skb, pn, j_0, aad);
75700b9cfa3SJouni Malinen 
75800b9cfa3SJouni Malinen 			if (ieee80211_aes_gcm_decrypt(
75900b9cfa3SJouni Malinen 				    key->u.gcmp.tfm, j_0, aad,
76000b9cfa3SJouni Malinen 				    skb->data + hdrlen + IEEE80211_GCMP_HDR_LEN,
76100b9cfa3SJouni Malinen 				    data_len,
762981d94a8SJohannes Berg 				    skb->data + skb->len -
763981d94a8SJohannes Berg 				    IEEE80211_GCMP_MIC_LEN))
764baa951a1SJohannes Berg 				return RX_DROP_U_MIC_FAIL;
76500b9cfa3SJouni Malinen 		}
76600b9cfa3SJouni Malinen 
76700b9cfa3SJouni Malinen 		memcpy(key->u.gcmp.rx_pn[queue], pn, IEEE80211_GCMP_PN_LEN);
768bf30ca92SJohannes Berg 		if (unlikely(ieee80211_is_frag(hdr)))
769bf30ca92SJohannes Berg 			memcpy(rx->ccm_gcm.pn, pn, IEEE80211_CCMP_PN_LEN);
770981d94a8SJohannes Berg 	}
77100b9cfa3SJouni Malinen 
77200b9cfa3SJouni Malinen 	/* Remove GCMP header and MIC */
773f980ebc0SSara Sharon 	if (pskb_trim(skb, skb->len - mic_len))
77400b9cfa3SJouni Malinen 		return RX_DROP_UNUSABLE;
77500b9cfa3SJouni Malinen 	memmove(skb->data + IEEE80211_GCMP_HDR_LEN, skb->data, hdrlen);
77600b9cfa3SJouni Malinen 	skb_pull(skb, IEEE80211_GCMP_HDR_LEN);
77700b9cfa3SJouni Malinen 
77800b9cfa3SJouni Malinen 	return RX_CONTINUE;
77900b9cfa3SJouni Malinen }
78000b9cfa3SJouni Malinen 
bip_aad(struct sk_buff * skb,u8 * aad)781765cb46aSJouni Malinen static void bip_aad(struct sk_buff *skb, u8 *aad)
782765cb46aSJouni Malinen {
78333766368SJouni Malinen 	__le16 mask_fc;
78433766368SJouni Malinen 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
78533766368SJouni Malinen 
786765cb46aSJouni Malinen 	/* BIP AAD: FC(masked) || A1 || A2 || A3 */
787765cb46aSJouni Malinen 
788765cb46aSJouni Malinen 	/* FC type/subtype */
789765cb46aSJouni Malinen 	/* Mask FC Retry, PwrMgt, MoreData flags to zero */
79033766368SJouni Malinen 	mask_fc = hdr->frame_control;
79133766368SJouni Malinen 	mask_fc &= ~cpu_to_le16(IEEE80211_FCTL_RETRY | IEEE80211_FCTL_PM |
79233766368SJouni Malinen 				IEEE80211_FCTL_MOREDATA);
79333766368SJouni Malinen 	put_unaligned(mask_fc, (__le16 *) &aad[0]);
794765cb46aSJouni Malinen 	/* A1 || A2 || A3 */
7952aec9099SJohannes Berg 	memcpy(aad + 2, &hdr->addrs, 3 * ETH_ALEN);
796765cb46aSJouni Malinen }
797765cb46aSJouni Malinen 
798765cb46aSJouni Malinen 
bip_ipn_set64(u8 * d,u64 pn)79975396ae6SJohannes Berg static inline void bip_ipn_set64(u8 *d, u64 pn)
80075396ae6SJohannes Berg {
80175396ae6SJohannes Berg 	*d++ = pn;
80275396ae6SJohannes Berg 	*d++ = pn >> 8;
80375396ae6SJohannes Berg 	*d++ = pn >> 16;
80475396ae6SJohannes Berg 	*d++ = pn >> 24;
80575396ae6SJohannes Berg 	*d++ = pn >> 32;
80675396ae6SJohannes Berg 	*d = pn >> 40;
80775396ae6SJohannes Berg }
80875396ae6SJohannes Berg 
bip_ipn_swap(u8 * d,const u8 * s)809765cb46aSJouni Malinen static inline void bip_ipn_swap(u8 *d, const u8 *s)
810765cb46aSJouni Malinen {
811765cb46aSJouni Malinen 	*d++ = s[5];
812765cb46aSJouni Malinen 	*d++ = s[4];
813765cb46aSJouni Malinen 	*d++ = s[3];
814765cb46aSJouni Malinen 	*d++ = s[2];
815765cb46aSJouni Malinen 	*d++ = s[1];
816765cb46aSJouni Malinen 	*d = s[0];
817765cb46aSJouni Malinen }
818765cb46aSJouni Malinen 
819765cb46aSJouni Malinen 
820765cb46aSJouni Malinen ieee80211_tx_result
ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data * tx)821765cb46aSJouni Malinen ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx)
822765cb46aSJouni Malinen {
823252b86c4SJohannes Berg 	struct sk_buff *skb;
824252b86c4SJohannes Berg 	struct ieee80211_tx_info *info;
825765cb46aSJouni Malinen 	struct ieee80211_key *key = tx->key;
826765cb46aSJouni Malinen 	struct ieee80211_mmie *mmie;
82775396ae6SJohannes Berg 	u8 aad[20];
82875396ae6SJohannes Berg 	u64 pn64;
829765cb46aSJouni Malinen 
830252b86c4SJohannes Berg 	if (WARN_ON(skb_queue_len(&tx->skbs) != 1))
831252b86c4SJohannes Berg 		return TX_DROP;
832252b86c4SJohannes Berg 
833252b86c4SJohannes Berg 	skb = skb_peek(&tx->skbs);
834252b86c4SJohannes Berg 
835252b86c4SJohannes Berg 	info = IEEE80211_SKB_CB(skb);
836252b86c4SJohannes Berg 
837a0b4496aSLorenzo Bianconi 	if (info->control.hw_key &&
838a0b4496aSLorenzo Bianconi 	    !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIE))
839252b86c4SJohannes Berg 		return TX_CONTINUE;
840765cb46aSJouni Malinen 
841765cb46aSJouni Malinen 	if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie)))
842765cb46aSJouni Malinen 		return TX_DROP;
843765cb46aSJouni Malinen 
8444df864c1SJohannes Berg 	mmie = skb_put(skb, sizeof(*mmie));
845765cb46aSJouni Malinen 	mmie->element_id = WLAN_EID_MMIE;
846765cb46aSJouni Malinen 	mmie->length = sizeof(*mmie) - 2;
847765cb46aSJouni Malinen 	mmie->key_id = cpu_to_le16(key->conf.keyidx);
848765cb46aSJouni Malinen 
849765cb46aSJouni Malinen 	/* PN = PN + 1 */
850db388a56SJohannes Berg 	pn64 = atomic64_inc_return(&key->conf.tx_pn);
851765cb46aSJouni Malinen 
85275396ae6SJohannes Berg 	bip_ipn_set64(mmie->sequence_number, pn64);
853765cb46aSJouni Malinen 
854a0b4496aSLorenzo Bianconi 	if (info->control.hw_key)
855a0b4496aSLorenzo Bianconi 		return TX_CONTINUE;
856a0b4496aSLorenzo Bianconi 
857765cb46aSJouni Malinen 	bip_aad(skb, aad);
858765cb46aSJouni Malinen 
859765cb46aSJouni Malinen 	/*
860765cb46aSJouni Malinen 	 * MIC = AES-128-CMAC(IGTK, AAD || Management Frame Body || MMIE, 64)
861765cb46aSJouni Malinen 	 */
86275396ae6SJohannes Berg 	ieee80211_aes_cmac(key->u.aes_cmac.tfm, aad,
86375396ae6SJohannes Berg 			   skb->data + 24, skb->len - 24, mmie->mic);
864765cb46aSJouni Malinen 
865765cb46aSJouni Malinen 	return TX_CONTINUE;
866765cb46aSJouni Malinen }
867765cb46aSJouni Malinen 
86856c52da2SJouni Malinen ieee80211_tx_result
ieee80211_crypto_aes_cmac_256_encrypt(struct ieee80211_tx_data * tx)86956c52da2SJouni Malinen ieee80211_crypto_aes_cmac_256_encrypt(struct ieee80211_tx_data *tx)
87056c52da2SJouni Malinen {
87156c52da2SJouni Malinen 	struct sk_buff *skb;
87256c52da2SJouni Malinen 	struct ieee80211_tx_info *info;
87356c52da2SJouni Malinen 	struct ieee80211_key *key = tx->key;
87456c52da2SJouni Malinen 	struct ieee80211_mmie_16 *mmie;
87556c52da2SJouni Malinen 	u8 aad[20];
87656c52da2SJouni Malinen 	u64 pn64;
87756c52da2SJouni Malinen 
87856c52da2SJouni Malinen 	if (WARN_ON(skb_queue_len(&tx->skbs) != 1))
87956c52da2SJouni Malinen 		return TX_DROP;
88056c52da2SJouni Malinen 
88156c52da2SJouni Malinen 	skb = skb_peek(&tx->skbs);
88256c52da2SJouni Malinen 
88356c52da2SJouni Malinen 	info = IEEE80211_SKB_CB(skb);
88456c52da2SJouni Malinen 
88556c52da2SJouni Malinen 	if (info->control.hw_key)
88656c52da2SJouni Malinen 		return TX_CONTINUE;
88756c52da2SJouni Malinen 
88856c52da2SJouni Malinen 	if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie)))
88956c52da2SJouni Malinen 		return TX_DROP;
89056c52da2SJouni Malinen 
8914df864c1SJohannes Berg 	mmie = skb_put(skb, sizeof(*mmie));
89256c52da2SJouni Malinen 	mmie->element_id = WLAN_EID_MMIE;
89356c52da2SJouni Malinen 	mmie->length = sizeof(*mmie) - 2;
89456c52da2SJouni Malinen 	mmie->key_id = cpu_to_le16(key->conf.keyidx);
89556c52da2SJouni Malinen 
89656c52da2SJouni Malinen 	/* PN = PN + 1 */
897db388a56SJohannes Berg 	pn64 = atomic64_inc_return(&key->conf.tx_pn);
89856c52da2SJouni Malinen 
89956c52da2SJouni Malinen 	bip_ipn_set64(mmie->sequence_number, pn64);
90056c52da2SJouni Malinen 
90156c52da2SJouni Malinen 	bip_aad(skb, aad);
90256c52da2SJouni Malinen 
90356c52da2SJouni Malinen 	/* MIC = AES-256-CMAC(IGTK, AAD || Management Frame Body || MMIE, 128)
90456c52da2SJouni Malinen 	 */
90556c52da2SJouni Malinen 	ieee80211_aes_cmac_256(key->u.aes_cmac.tfm, aad,
90656c52da2SJouni Malinen 			       skb->data + 24, skb->len - 24, mmie->mic);
90756c52da2SJouni Malinen 
90856c52da2SJouni Malinen 	return TX_CONTINUE;
90956c52da2SJouni Malinen }
910765cb46aSJouni Malinen 
911765cb46aSJouni Malinen ieee80211_rx_result
ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data * rx)912765cb46aSJouni Malinen ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx)
913765cb46aSJouni Malinen {
914765cb46aSJouni Malinen 	struct sk_buff *skb = rx->skb;
915eb9fb5b8SJohannes Berg 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
916765cb46aSJouni Malinen 	struct ieee80211_key *key = rx->key;
917765cb46aSJouni Malinen 	struct ieee80211_mmie *mmie;
918765cb46aSJouni Malinen 	u8 aad[20], mic[8], ipn[6];
919765cb46aSJouni Malinen 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
920765cb46aSJouni Malinen 
921765cb46aSJouni Malinen 	if (!ieee80211_is_mgmt(hdr->frame_control))
922765cb46aSJouni Malinen 		return RX_CONTINUE;
923765cb46aSJouni Malinen 
924a8286911SJohannes Berg 	/* management frames are already linear */
925a8286911SJohannes Berg 
926765cb46aSJouni Malinen 	if (skb->len < 24 + sizeof(*mmie))
927765cb46aSJouni Malinen 		return RX_DROP_UNUSABLE;
928765cb46aSJouni Malinen 
929765cb46aSJouni Malinen 	mmie = (struct ieee80211_mmie *)
930765cb46aSJouni Malinen 		(skb->data + skb->len - sizeof(*mmie));
931765cb46aSJouni Malinen 	if (mmie->element_id != WLAN_EID_MMIE ||
932765cb46aSJouni Malinen 	    mmie->length != sizeof(*mmie) - 2)
933baa951a1SJohannes Berg 		return RX_DROP_U_BAD_MMIE; /* Invalid MMIE */
934765cb46aSJouni Malinen 
935765cb46aSJouni Malinen 	bip_ipn_swap(ipn, mmie->sequence_number);
936765cb46aSJouni Malinen 
937765cb46aSJouni Malinen 	if (memcmp(ipn, key->u.aes_cmac.rx_pn, 6) <= 0) {
938765cb46aSJouni Malinen 		key->u.aes_cmac.replays++;
939baa951a1SJohannes Berg 		return RX_DROP_U_REPLAY;
940765cb46aSJouni Malinen 	}
941765cb46aSJouni Malinen 
942eb9fb5b8SJohannes Berg 	if (!(status->flag & RX_FLAG_DECRYPTED)) {
943765cb46aSJouni Malinen 		/* hardware didn't decrypt/verify MIC */
944765cb46aSJouni Malinen 		bip_aad(skb, aad);
94575396ae6SJohannes Berg 		ieee80211_aes_cmac(key->u.aes_cmac.tfm, aad,
946765cb46aSJouni Malinen 				   skb->data + 24, skb->len - 24, mic);
94798c67d18SJason A. Donenfeld 		if (crypto_memneq(mic, mmie->mic, sizeof(mmie->mic))) {
948765cb46aSJouni Malinen 			key->u.aes_cmac.icverrors++;
949baa951a1SJohannes Berg 			return RX_DROP_U_MIC_FAIL;
950765cb46aSJouni Malinen 		}
951765cb46aSJouni Malinen 	}
952765cb46aSJouni Malinen 
953765cb46aSJouni Malinen 	memcpy(key->u.aes_cmac.rx_pn, ipn, 6);
954765cb46aSJouni Malinen 
955765cb46aSJouni Malinen 	/* Remove MMIE */
956765cb46aSJouni Malinen 	skb_trim(skb, skb->len - sizeof(*mmie));
957765cb46aSJouni Malinen 
958765cb46aSJouni Malinen 	return RX_CONTINUE;
959765cb46aSJouni Malinen }
960d32a1028SYoni Divinsky 
96156c52da2SJouni Malinen ieee80211_rx_result
ieee80211_crypto_aes_cmac_256_decrypt(struct ieee80211_rx_data * rx)96256c52da2SJouni Malinen ieee80211_crypto_aes_cmac_256_decrypt(struct ieee80211_rx_data *rx)
96356c52da2SJouni Malinen {
96456c52da2SJouni Malinen 	struct sk_buff *skb = rx->skb;
96556c52da2SJouni Malinen 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
96656c52da2SJouni Malinen 	struct ieee80211_key *key = rx->key;
96756c52da2SJouni Malinen 	struct ieee80211_mmie_16 *mmie;
96856c52da2SJouni Malinen 	u8 aad[20], mic[16], ipn[6];
96956c52da2SJouni Malinen 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
97056c52da2SJouni Malinen 
97156c52da2SJouni Malinen 	if (!ieee80211_is_mgmt(hdr->frame_control))
97256c52da2SJouni Malinen 		return RX_CONTINUE;
97356c52da2SJouni Malinen 
97456c52da2SJouni Malinen 	/* management frames are already linear */
97556c52da2SJouni Malinen 
97656c52da2SJouni Malinen 	if (skb->len < 24 + sizeof(*mmie))
97756c52da2SJouni Malinen 		return RX_DROP_UNUSABLE;
97856c52da2SJouni Malinen 
97956c52da2SJouni Malinen 	mmie = (struct ieee80211_mmie_16 *)
98056c52da2SJouni Malinen 		(skb->data + skb->len - sizeof(*mmie));
98156c52da2SJouni Malinen 	if (mmie->element_id != WLAN_EID_MMIE ||
98256c52da2SJouni Malinen 	    mmie->length != sizeof(*mmie) - 2)
98356c52da2SJouni Malinen 		return RX_DROP_UNUSABLE; /* Invalid MMIE */
98456c52da2SJouni Malinen 
98556c52da2SJouni Malinen 	bip_ipn_swap(ipn, mmie->sequence_number);
98656c52da2SJouni Malinen 
98756c52da2SJouni Malinen 	if (memcmp(ipn, key->u.aes_cmac.rx_pn, 6) <= 0) {
98856c52da2SJouni Malinen 		key->u.aes_cmac.replays++;
989baa951a1SJohannes Berg 		return RX_DROP_U_REPLAY;
99056c52da2SJouni Malinen 	}
99156c52da2SJouni Malinen 
99256c52da2SJouni Malinen 	if (!(status->flag & RX_FLAG_DECRYPTED)) {
99356c52da2SJouni Malinen 		/* hardware didn't decrypt/verify MIC */
99456c52da2SJouni Malinen 		bip_aad(skb, aad);
99556c52da2SJouni Malinen 		ieee80211_aes_cmac_256(key->u.aes_cmac.tfm, aad,
99656c52da2SJouni Malinen 				       skb->data + 24, skb->len - 24, mic);
99798c67d18SJason A. Donenfeld 		if (crypto_memneq(mic, mmie->mic, sizeof(mmie->mic))) {
99856c52da2SJouni Malinen 			key->u.aes_cmac.icverrors++;
999baa951a1SJohannes Berg 			return RX_DROP_U_MIC_FAIL;
100056c52da2SJouni Malinen 		}
100156c52da2SJouni Malinen 	}
100256c52da2SJouni Malinen 
100356c52da2SJouni Malinen 	memcpy(key->u.aes_cmac.rx_pn, ipn, 6);
100456c52da2SJouni Malinen 
100556c52da2SJouni Malinen 	/* Remove MMIE */
100656c52da2SJouni Malinen 	skb_trim(skb, skb->len - sizeof(*mmie));
100756c52da2SJouni Malinen 
100856c52da2SJouni Malinen 	return RX_CONTINUE;
100956c52da2SJouni Malinen }
101056c52da2SJouni Malinen 
1011d32a1028SYoni Divinsky ieee80211_tx_result
ieee80211_crypto_aes_gmac_encrypt(struct ieee80211_tx_data * tx)10128ade538bSJouni Malinen ieee80211_crypto_aes_gmac_encrypt(struct ieee80211_tx_data *tx)
10138ade538bSJouni Malinen {
10148ade538bSJouni Malinen 	struct sk_buff *skb;
10158ade538bSJouni Malinen 	struct ieee80211_tx_info *info;
10168ade538bSJouni Malinen 	struct ieee80211_key *key = tx->key;
10178ade538bSJouni Malinen 	struct ieee80211_mmie_16 *mmie;
10188ade538bSJouni Malinen 	struct ieee80211_hdr *hdr;
1019f4a067f9SArd Biesheuvel 	u8 aad[GMAC_AAD_LEN];
10208ade538bSJouni Malinen 	u64 pn64;
1021f4a067f9SArd Biesheuvel 	u8 nonce[GMAC_NONCE_LEN];
10228ade538bSJouni Malinen 
10238ade538bSJouni Malinen 	if (WARN_ON(skb_queue_len(&tx->skbs) != 1))
10248ade538bSJouni Malinen 		return TX_DROP;
10258ade538bSJouni Malinen 
10268ade538bSJouni Malinen 	skb = skb_peek(&tx->skbs);
10278ade538bSJouni Malinen 
10288ade538bSJouni Malinen 	info = IEEE80211_SKB_CB(skb);
10298ade538bSJouni Malinen 
10308ade538bSJouni Malinen 	if (info->control.hw_key)
10318ade538bSJouni Malinen 		return TX_CONTINUE;
10328ade538bSJouni Malinen 
10338ade538bSJouni Malinen 	if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie)))
10348ade538bSJouni Malinen 		return TX_DROP;
10358ade538bSJouni Malinen 
10364df864c1SJohannes Berg 	mmie = skb_put(skb, sizeof(*mmie));
10378ade538bSJouni Malinen 	mmie->element_id = WLAN_EID_MMIE;
10388ade538bSJouni Malinen 	mmie->length = sizeof(*mmie) - 2;
10398ade538bSJouni Malinen 	mmie->key_id = cpu_to_le16(key->conf.keyidx);
10408ade538bSJouni Malinen 
10418ade538bSJouni Malinen 	/* PN = PN + 1 */
1042db388a56SJohannes Berg 	pn64 = atomic64_inc_return(&key->conf.tx_pn);
10438ade538bSJouni Malinen 
10448ade538bSJouni Malinen 	bip_ipn_set64(mmie->sequence_number, pn64);
10458ade538bSJouni Malinen 
10468ade538bSJouni Malinen 	bip_aad(skb, aad);
10478ade538bSJouni Malinen 
10488ade538bSJouni Malinen 	hdr = (struct ieee80211_hdr *)skb->data;
10498ade538bSJouni Malinen 	memcpy(nonce, hdr->addr2, ETH_ALEN);
10508ade538bSJouni Malinen 	bip_ipn_swap(nonce + ETH_ALEN, mmie->sequence_number);
10518ade538bSJouni Malinen 
10528ade538bSJouni Malinen 	/* MIC = AES-GMAC(IGTK, AAD || Management Frame Body || MMIE, 128) */
10538ade538bSJouni Malinen 	if (ieee80211_aes_gmac(key->u.aes_gmac.tfm, aad, nonce,
10548ade538bSJouni Malinen 			       skb->data + 24, skb->len - 24, mmie->mic) < 0)
10558ade538bSJouni Malinen 		return TX_DROP;
10568ade538bSJouni Malinen 
10578ade538bSJouni Malinen 	return TX_CONTINUE;
10588ade538bSJouni Malinen }
10598ade538bSJouni Malinen 
10608ade538bSJouni Malinen ieee80211_rx_result
ieee80211_crypto_aes_gmac_decrypt(struct ieee80211_rx_data * rx)10618ade538bSJouni Malinen ieee80211_crypto_aes_gmac_decrypt(struct ieee80211_rx_data *rx)
10628ade538bSJouni Malinen {
10638ade538bSJouni Malinen 	struct sk_buff *skb = rx->skb;
10648ade538bSJouni Malinen 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
10658ade538bSJouni Malinen 	struct ieee80211_key *key = rx->key;
10668ade538bSJouni Malinen 	struct ieee80211_mmie_16 *mmie;
1067a71fd9daSJouni Malinen 	u8 aad[GMAC_AAD_LEN], *mic, ipn[6], nonce[GMAC_NONCE_LEN];
10688ade538bSJouni Malinen 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
10698ade538bSJouni Malinen 
10708ade538bSJouni Malinen 	if (!ieee80211_is_mgmt(hdr->frame_control))
10718ade538bSJouni Malinen 		return RX_CONTINUE;
10728ade538bSJouni Malinen 
10738ade538bSJouni Malinen 	/* management frames are already linear */
10748ade538bSJouni Malinen 
10758ade538bSJouni Malinen 	if (skb->len < 24 + sizeof(*mmie))
10768ade538bSJouni Malinen 		return RX_DROP_UNUSABLE;
10778ade538bSJouni Malinen 
10788ade538bSJouni Malinen 	mmie = (struct ieee80211_mmie_16 *)
10798ade538bSJouni Malinen 		(skb->data + skb->len - sizeof(*mmie));
10808ade538bSJouni Malinen 	if (mmie->element_id != WLAN_EID_MMIE ||
10818ade538bSJouni Malinen 	    mmie->length != sizeof(*mmie) - 2)
1082baa951a1SJohannes Berg 		return RX_DROP_U_BAD_MMIE; /* Invalid MMIE */
10838ade538bSJouni Malinen 
10848ade538bSJouni Malinen 	bip_ipn_swap(ipn, mmie->sequence_number);
10858ade538bSJouni Malinen 
10868ade538bSJouni Malinen 	if (memcmp(ipn, key->u.aes_gmac.rx_pn, 6) <= 0) {
10878ade538bSJouni Malinen 		key->u.aes_gmac.replays++;
1088baa951a1SJohannes Berg 		return RX_DROP_U_REPLAY;
10898ade538bSJouni Malinen 	}
10908ade538bSJouni Malinen 
10918ade538bSJouni Malinen 	if (!(status->flag & RX_FLAG_DECRYPTED)) {
10928ade538bSJouni Malinen 		/* hardware didn't decrypt/verify MIC */
10938ade538bSJouni Malinen 		bip_aad(skb, aad);
10948ade538bSJouni Malinen 
10958ade538bSJouni Malinen 		memcpy(nonce, hdr->addr2, ETH_ALEN);
10968ade538bSJouni Malinen 		memcpy(nonce + ETH_ALEN, ipn, 6);
10978ade538bSJouni Malinen 
1098a71fd9daSJouni Malinen 		mic = kmalloc(GMAC_MIC_LEN, GFP_ATOMIC);
1099a71fd9daSJouni Malinen 		if (!mic)
1100a71fd9daSJouni Malinen 			return RX_DROP_UNUSABLE;
11018ade538bSJouni Malinen 		if (ieee80211_aes_gmac(key->u.aes_gmac.tfm, aad, nonce,
11028ade538bSJouni Malinen 				       skb->data + 24, skb->len - 24,
11038ade538bSJouni Malinen 				       mic) < 0 ||
110498c67d18SJason A. Donenfeld 		    crypto_memneq(mic, mmie->mic, sizeof(mmie->mic))) {
11058ade538bSJouni Malinen 			key->u.aes_gmac.icverrors++;
1106a71fd9daSJouni Malinen 			kfree(mic);
1107baa951a1SJohannes Berg 			return RX_DROP_U_MIC_FAIL;
11088ade538bSJouni Malinen 		}
1109a71fd9daSJouni Malinen 		kfree(mic);
11108ade538bSJouni Malinen 	}
11118ade538bSJouni Malinen 
11128ade538bSJouni Malinen 	memcpy(key->u.aes_gmac.rx_pn, ipn, 6);
11138ade538bSJouni Malinen 
11148ade538bSJouni Malinen 	/* Remove MMIE */
11158ade538bSJouni Malinen 	skb_trim(skb, skb->len - sizeof(*mmie));
11168ade538bSJouni Malinen 
11178ade538bSJouni Malinen 	return RX_CONTINUE;
11188ade538bSJouni Malinen }
1119