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