xref: /openbmc/linux/net/wireless/lib80211_crypt_tkip.c (revision 4b4193256c8d3bc3a5397b5cd9494c2ad386317d)
121042e41SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2274bfb8dSJohn W. Linville /*
3274bfb8dSJohn W. Linville  * lib80211 crypt: host-based TKIP encryption implementation for lib80211
4274bfb8dSJohn W. Linville  *
5274bfb8dSJohn W. Linville  * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi>
6274bfb8dSJohn W. Linville  * Copyright (c) 2008, John W. Linville <linville@tuxdriver.com>
7274bfb8dSJohn W. Linville  */
8274bfb8dSJohn W. Linville 
9e9c0268fSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10e9c0268fSJoe Perches 
11274bfb8dSJohn W. Linville #include <linux/err.h>
124be29701SArd Biesheuvel #include <linux/fips.h>
13274bfb8dSJohn W. Linville #include <linux/module.h>
14274bfb8dSJohn W. Linville #include <linux/init.h>
15274bfb8dSJohn W. Linville #include <linux/slab.h>
16274bfb8dSJohn W. Linville #include <linux/random.h>
17274bfb8dSJohn W. Linville #include <linux/scatterlist.h>
18274bfb8dSJohn W. Linville #include <linux/skbuff.h>
19274bfb8dSJohn W. Linville #include <linux/netdevice.h>
20274bfb8dSJohn W. Linville #include <linux/mm.h>
21274bfb8dSJohn W. Linville #include <linux/if_ether.h>
22274bfb8dSJohn W. Linville #include <linux/if_arp.h>
23274bfb8dSJohn W. Linville #include <asm/string.h>
24274bfb8dSJohn W. Linville 
25274bfb8dSJohn W. Linville #include <linux/wireless.h>
26274bfb8dSJohn W. Linville #include <linux/ieee80211.h>
27274bfb8dSJohn W. Linville #include <net/iw_handler.h>
28274bfb8dSJohn W. Linville 
294be29701SArd Biesheuvel #include <crypto/arc4.h>
30608fb34cSHerbert Xu #include <crypto/hash.h>
31b802a5d6SJohannes Berg #include <linux/crypto.h>
32274bfb8dSJohn W. Linville #include <linux/crc32.h>
33274bfb8dSJohn W. Linville 
34274bfb8dSJohn W. Linville #include <net/lib80211.h>
35274bfb8dSJohn W. Linville 
36274bfb8dSJohn W. Linville MODULE_AUTHOR("Jouni Malinen");
37274bfb8dSJohn W. Linville MODULE_DESCRIPTION("lib80211 crypt: TKIP");
38274bfb8dSJohn W. Linville MODULE_LICENSE("GPL");
39274bfb8dSJohn W. Linville 
40299af9d3SAndriy Tkachuk #define TKIP_HDR_LEN 8
41299af9d3SAndriy Tkachuk 
42274bfb8dSJohn W. Linville struct lib80211_tkip_data {
43274bfb8dSJohn W. Linville #define TKIP_KEY_LEN 32
44274bfb8dSJohn W. Linville 	u8 key[TKIP_KEY_LEN];
45274bfb8dSJohn W. Linville 	int key_set;
46274bfb8dSJohn W. Linville 
47274bfb8dSJohn W. Linville 	u32 tx_iv32;
48274bfb8dSJohn W. Linville 	u16 tx_iv16;
49274bfb8dSJohn W. Linville 	u16 tx_ttak[5];
50274bfb8dSJohn W. Linville 	int tx_phase1_done;
51274bfb8dSJohn W. Linville 
52274bfb8dSJohn W. Linville 	u32 rx_iv32;
53274bfb8dSJohn W. Linville 	u16 rx_iv16;
54274bfb8dSJohn W. Linville 	u16 rx_ttak[5];
55274bfb8dSJohn W. Linville 	int rx_phase1_done;
56274bfb8dSJohn W. Linville 	u32 rx_iv32_new;
57274bfb8dSJohn W. Linville 	u16 rx_iv16_new;
58274bfb8dSJohn W. Linville 
59274bfb8dSJohn W. Linville 	u32 dot11RSNAStatsTKIPReplays;
60274bfb8dSJohn W. Linville 	u32 dot11RSNAStatsTKIPICVErrors;
61274bfb8dSJohn W. Linville 	u32 dot11RSNAStatsTKIPLocalMICFailures;
62274bfb8dSJohn W. Linville 
63274bfb8dSJohn W. Linville 	int key_idx;
64274bfb8dSJohn W. Linville 
654be29701SArd Biesheuvel 	struct arc4_ctx rx_ctx_arc4;
664be29701SArd Biesheuvel 	struct arc4_ctx tx_ctx_arc4;
67d17504b1SKees Cook 	struct crypto_shash *rx_tfm_michael;
68d17504b1SKees Cook 	struct crypto_shash *tx_tfm_michael;
69274bfb8dSJohn W. Linville 
70274bfb8dSJohn W. Linville 	/* scratch buffers for virt_to_page() (crypto API) */
71274bfb8dSJohn W. Linville 	u8 rx_hdr[16], tx_hdr[16];
72274bfb8dSJohn W. Linville 
73274bfb8dSJohn W. Linville 	unsigned long flags;
74274bfb8dSJohn W. Linville };
75274bfb8dSJohn W. Linville 
lib80211_tkip_set_flags(unsigned long flags,void * priv)76274bfb8dSJohn W. Linville static unsigned long lib80211_tkip_set_flags(unsigned long flags, void *priv)
77274bfb8dSJohn W. Linville {
78274bfb8dSJohn W. Linville 	struct lib80211_tkip_data *_priv = priv;
79274bfb8dSJohn W. Linville 	unsigned long old_flags = _priv->flags;
80274bfb8dSJohn W. Linville 	_priv->flags = flags;
81274bfb8dSJohn W. Linville 	return old_flags;
82274bfb8dSJohn W. Linville }
83274bfb8dSJohn W. Linville 
lib80211_tkip_get_flags(void * priv)84274bfb8dSJohn W. Linville static unsigned long lib80211_tkip_get_flags(void *priv)
85274bfb8dSJohn W. Linville {
86274bfb8dSJohn W. Linville 	struct lib80211_tkip_data *_priv = priv;
87274bfb8dSJohn W. Linville 	return _priv->flags;
88274bfb8dSJohn W. Linville }
89274bfb8dSJohn W. Linville 
lib80211_tkip_init(int key_idx)90274bfb8dSJohn W. Linville static void *lib80211_tkip_init(int key_idx)
91274bfb8dSJohn W. Linville {
92274bfb8dSJohn W. Linville 	struct lib80211_tkip_data *priv;
93274bfb8dSJohn W. Linville 
944be29701SArd Biesheuvel 	if (fips_enabled)
954be29701SArd Biesheuvel 		return NULL;
964be29701SArd Biesheuvel 
97274bfb8dSJohn W. Linville 	priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
98274bfb8dSJohn W. Linville 	if (priv == NULL)
99274bfb8dSJohn W. Linville 		goto fail;
100274bfb8dSJohn W. Linville 
101274bfb8dSJohn W. Linville 	priv->key_idx = key_idx;
102274bfb8dSJohn W. Linville 
103d17504b1SKees Cook 	priv->tx_tfm_michael = crypto_alloc_shash("michael_mic", 0, 0);
104274bfb8dSJohn W. Linville 	if (IS_ERR(priv->tx_tfm_michael)) {
105274bfb8dSJohn W. Linville 		priv->tx_tfm_michael = NULL;
106274bfb8dSJohn W. Linville 		goto fail;
107274bfb8dSJohn W. Linville 	}
108274bfb8dSJohn W. Linville 
109d17504b1SKees Cook 	priv->rx_tfm_michael = crypto_alloc_shash("michael_mic", 0, 0);
110274bfb8dSJohn W. Linville 	if (IS_ERR(priv->rx_tfm_michael)) {
111274bfb8dSJohn W. Linville 		priv->rx_tfm_michael = NULL;
112274bfb8dSJohn W. Linville 		goto fail;
113274bfb8dSJohn W. Linville 	}
114274bfb8dSJohn W. Linville 
115274bfb8dSJohn W. Linville 	return priv;
116274bfb8dSJohn W. Linville 
117274bfb8dSJohn W. Linville       fail:
118274bfb8dSJohn W. Linville 	if (priv) {
119d17504b1SKees Cook 		crypto_free_shash(priv->tx_tfm_michael);
120d17504b1SKees Cook 		crypto_free_shash(priv->rx_tfm_michael);
121274bfb8dSJohn W. Linville 		kfree(priv);
122274bfb8dSJohn W. Linville 	}
123274bfb8dSJohn W. Linville 
124274bfb8dSJohn W. Linville 	return NULL;
125274bfb8dSJohn W. Linville }
126274bfb8dSJohn W. Linville 
lib80211_tkip_deinit(void * priv)127274bfb8dSJohn W. Linville static void lib80211_tkip_deinit(void *priv)
128274bfb8dSJohn W. Linville {
129274bfb8dSJohn W. Linville 	struct lib80211_tkip_data *_priv = priv;
130274bfb8dSJohn W. Linville 	if (_priv) {
131d17504b1SKees Cook 		crypto_free_shash(_priv->tx_tfm_michael);
132d17504b1SKees Cook 		crypto_free_shash(_priv->rx_tfm_michael);
133274bfb8dSJohn W. Linville 	}
134*453431a5SWaiman Long 	kfree_sensitive(priv);
135274bfb8dSJohn W. Linville }
136274bfb8dSJohn W. Linville 
RotR1(u16 val)137274bfb8dSJohn W. Linville static inline u16 RotR1(u16 val)
138274bfb8dSJohn W. Linville {
139274bfb8dSJohn W. Linville 	return (val >> 1) | (val << 15);
140274bfb8dSJohn W. Linville }
141274bfb8dSJohn W. Linville 
Lo8(u16 val)142274bfb8dSJohn W. Linville static inline u8 Lo8(u16 val)
143274bfb8dSJohn W. Linville {
144274bfb8dSJohn W. Linville 	return val & 0xff;
145274bfb8dSJohn W. Linville }
146274bfb8dSJohn W. Linville 
Hi8(u16 val)147274bfb8dSJohn W. Linville static inline u8 Hi8(u16 val)
148274bfb8dSJohn W. Linville {
149274bfb8dSJohn W. Linville 	return val >> 8;
150274bfb8dSJohn W. Linville }
151274bfb8dSJohn W. Linville 
Lo16(u32 val)152274bfb8dSJohn W. Linville static inline u16 Lo16(u32 val)
153274bfb8dSJohn W. Linville {
154274bfb8dSJohn W. Linville 	return val & 0xffff;
155274bfb8dSJohn W. Linville }
156274bfb8dSJohn W. Linville 
Hi16(u32 val)157274bfb8dSJohn W. Linville static inline u16 Hi16(u32 val)
158274bfb8dSJohn W. Linville {
159274bfb8dSJohn W. Linville 	return val >> 16;
160274bfb8dSJohn W. Linville }
161274bfb8dSJohn W. Linville 
Mk16(u8 hi,u8 lo)162274bfb8dSJohn W. Linville static inline u16 Mk16(u8 hi, u8 lo)
163274bfb8dSJohn W. Linville {
164274bfb8dSJohn W. Linville 	return lo | (((u16) hi) << 8);
165274bfb8dSJohn W. Linville }
166274bfb8dSJohn W. Linville 
Mk16_le(__le16 * v)167274bfb8dSJohn W. Linville static inline u16 Mk16_le(__le16 * v)
168274bfb8dSJohn W. Linville {
169274bfb8dSJohn W. Linville 	return le16_to_cpu(*v);
170274bfb8dSJohn W. Linville }
171274bfb8dSJohn W. Linville 
172274bfb8dSJohn W. Linville static const u16 Sbox[256] = {
173274bfb8dSJohn W. Linville 	0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
174274bfb8dSJohn W. Linville 	0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
175274bfb8dSJohn W. Linville 	0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
176274bfb8dSJohn W. Linville 	0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
177274bfb8dSJohn W. Linville 	0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
178274bfb8dSJohn W. Linville 	0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
179274bfb8dSJohn W. Linville 	0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
180274bfb8dSJohn W. Linville 	0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
181274bfb8dSJohn W. Linville 	0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
182274bfb8dSJohn W. Linville 	0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
183274bfb8dSJohn W. Linville 	0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
184274bfb8dSJohn W. Linville 	0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
185274bfb8dSJohn W. Linville 	0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
186274bfb8dSJohn W. Linville 	0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
187274bfb8dSJohn W. Linville 	0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
188274bfb8dSJohn W. Linville 	0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
189274bfb8dSJohn W. Linville 	0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
190274bfb8dSJohn W. Linville 	0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
191274bfb8dSJohn W. Linville 	0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
192274bfb8dSJohn W. Linville 	0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
193274bfb8dSJohn W. Linville 	0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
194274bfb8dSJohn W. Linville 	0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
195274bfb8dSJohn W. Linville 	0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
196274bfb8dSJohn W. Linville 	0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
197274bfb8dSJohn W. Linville 	0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
198274bfb8dSJohn W. Linville 	0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
199274bfb8dSJohn W. Linville 	0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
200274bfb8dSJohn W. Linville 	0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
201274bfb8dSJohn W. Linville 	0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
202274bfb8dSJohn W. Linville 	0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
203274bfb8dSJohn W. Linville 	0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
204274bfb8dSJohn W. Linville 	0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
205274bfb8dSJohn W. Linville };
206274bfb8dSJohn W. Linville 
_S_(u16 v)207274bfb8dSJohn W. Linville static inline u16 _S_(u16 v)
208274bfb8dSJohn W. Linville {
209274bfb8dSJohn W. Linville 	u16 t = Sbox[Hi8(v)];
210274bfb8dSJohn W. Linville 	return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8));
211274bfb8dSJohn W. Linville }
212274bfb8dSJohn W. Linville 
213274bfb8dSJohn W. Linville #define PHASE1_LOOP_COUNT 8
214274bfb8dSJohn W. Linville 
tkip_mixing_phase1(u16 * TTAK,const u8 * TK,const u8 * TA,u32 IV32)215274bfb8dSJohn W. Linville static void tkip_mixing_phase1(u16 * TTAK, const u8 * TK, const u8 * TA,
216274bfb8dSJohn W. Linville 			       u32 IV32)
217274bfb8dSJohn W. Linville {
218274bfb8dSJohn W. Linville 	int i, j;
219274bfb8dSJohn W. Linville 
220274bfb8dSJohn W. Linville 	/* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */
221274bfb8dSJohn W. Linville 	TTAK[0] = Lo16(IV32);
222274bfb8dSJohn W. Linville 	TTAK[1] = Hi16(IV32);
223274bfb8dSJohn W. Linville 	TTAK[2] = Mk16(TA[1], TA[0]);
224274bfb8dSJohn W. Linville 	TTAK[3] = Mk16(TA[3], TA[2]);
225274bfb8dSJohn W. Linville 	TTAK[4] = Mk16(TA[5], TA[4]);
226274bfb8dSJohn W. Linville 
227274bfb8dSJohn W. Linville 	for (i = 0; i < PHASE1_LOOP_COUNT; i++) {
228274bfb8dSJohn W. Linville 		j = 2 * (i & 1);
229274bfb8dSJohn W. Linville 		TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j]));
230274bfb8dSJohn W. Linville 		TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j]));
231274bfb8dSJohn W. Linville 		TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j]));
232274bfb8dSJohn W. Linville 		TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j]));
233274bfb8dSJohn W. Linville 		TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i;
234274bfb8dSJohn W. Linville 	}
235274bfb8dSJohn W. Linville }
236274bfb8dSJohn W. Linville 
tkip_mixing_phase2(u8 * WEPSeed,const u8 * TK,const u16 * TTAK,u16 IV16)237274bfb8dSJohn W. Linville static void tkip_mixing_phase2(u8 * WEPSeed, const u8 * TK, const u16 * TTAK,
238274bfb8dSJohn W. Linville 			       u16 IV16)
239274bfb8dSJohn W. Linville {
240274bfb8dSJohn W. Linville 	/* Make temporary area overlap WEP seed so that the final copy can be
241274bfb8dSJohn W. Linville 	 * avoided on little endian hosts. */
242274bfb8dSJohn W. Linville 	u16 *PPK = (u16 *) & WEPSeed[4];
243274bfb8dSJohn W. Linville 
244274bfb8dSJohn W. Linville 	/* Step 1 - make copy of TTAK and bring in TSC */
245274bfb8dSJohn W. Linville 	PPK[0] = TTAK[0];
246274bfb8dSJohn W. Linville 	PPK[1] = TTAK[1];
247274bfb8dSJohn W. Linville 	PPK[2] = TTAK[2];
248274bfb8dSJohn W. Linville 	PPK[3] = TTAK[3];
249274bfb8dSJohn W. Linville 	PPK[4] = TTAK[4];
250274bfb8dSJohn W. Linville 	PPK[5] = TTAK[4] + IV16;
251274bfb8dSJohn W. Linville 
252274bfb8dSJohn W. Linville 	/* Step 2 - 96-bit bijective mixing using S-box */
253274bfb8dSJohn W. Linville 	PPK[0] += _S_(PPK[5] ^ Mk16_le((__le16 *) & TK[0]));
254274bfb8dSJohn W. Linville 	PPK[1] += _S_(PPK[0] ^ Mk16_le((__le16 *) & TK[2]));
255274bfb8dSJohn W. Linville 	PPK[2] += _S_(PPK[1] ^ Mk16_le((__le16 *) & TK[4]));
256274bfb8dSJohn W. Linville 	PPK[3] += _S_(PPK[2] ^ Mk16_le((__le16 *) & TK[6]));
257274bfb8dSJohn W. Linville 	PPK[4] += _S_(PPK[3] ^ Mk16_le((__le16 *) & TK[8]));
258274bfb8dSJohn W. Linville 	PPK[5] += _S_(PPK[4] ^ Mk16_le((__le16 *) & TK[10]));
259274bfb8dSJohn W. Linville 
260274bfb8dSJohn W. Linville 	PPK[0] += RotR1(PPK[5] ^ Mk16_le((__le16 *) & TK[12]));
261274bfb8dSJohn W. Linville 	PPK[1] += RotR1(PPK[0] ^ Mk16_le((__le16 *) & TK[14]));
262274bfb8dSJohn W. Linville 	PPK[2] += RotR1(PPK[1]);
263274bfb8dSJohn W. Linville 	PPK[3] += RotR1(PPK[2]);
264274bfb8dSJohn W. Linville 	PPK[4] += RotR1(PPK[3]);
265274bfb8dSJohn W. Linville 	PPK[5] += RotR1(PPK[4]);
266274bfb8dSJohn W. Linville 
267274bfb8dSJohn W. Linville 	/* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value
268274bfb8dSJohn W. Linville 	 * WEPSeed[0..2] is transmitted as WEP IV */
269274bfb8dSJohn W. Linville 	WEPSeed[0] = Hi8(IV16);
270274bfb8dSJohn W. Linville 	WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F;
271274bfb8dSJohn W. Linville 	WEPSeed[2] = Lo8(IV16);
272274bfb8dSJohn W. Linville 	WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((__le16 *) & TK[0])) >> 1);
273274bfb8dSJohn W. Linville 
274274bfb8dSJohn W. Linville #ifdef __BIG_ENDIAN
275274bfb8dSJohn W. Linville 	{
276274bfb8dSJohn W. Linville 		int i;
277274bfb8dSJohn W. Linville 		for (i = 0; i < 6; i++)
278274bfb8dSJohn W. Linville 			PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8);
279274bfb8dSJohn W. Linville 	}
280274bfb8dSJohn W. Linville #endif
281274bfb8dSJohn W. Linville }
282274bfb8dSJohn W. Linville 
lib80211_tkip_hdr(struct sk_buff * skb,int hdr_len,u8 * rc4key,int keylen,void * priv)283274bfb8dSJohn W. Linville static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len,
284274bfb8dSJohn W. Linville 			      u8 * rc4key, int keylen, void *priv)
285274bfb8dSJohn W. Linville {
286274bfb8dSJohn W. Linville 	struct lib80211_tkip_data *tkey = priv;
287274bfb8dSJohn W. Linville 	u8 *pos;
288274bfb8dSJohn W. Linville 	struct ieee80211_hdr *hdr;
289274bfb8dSJohn W. Linville 
290274bfb8dSJohn W. Linville 	hdr = (struct ieee80211_hdr *)skb->data;
291274bfb8dSJohn W. Linville 
292299af9d3SAndriy Tkachuk 	if (skb_headroom(skb) < TKIP_HDR_LEN || skb->len < hdr_len)
293274bfb8dSJohn W. Linville 		return -1;
294274bfb8dSJohn W. Linville 
295274bfb8dSJohn W. Linville 	if (rc4key == NULL || keylen < 16)
296274bfb8dSJohn W. Linville 		return -1;
297274bfb8dSJohn W. Linville 
298274bfb8dSJohn W. Linville 	if (!tkey->tx_phase1_done) {
299274bfb8dSJohn W. Linville 		tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2,
300274bfb8dSJohn W. Linville 				   tkey->tx_iv32);
301274bfb8dSJohn W. Linville 		tkey->tx_phase1_done = 1;
302274bfb8dSJohn W. Linville 	}
303274bfb8dSJohn W. Linville 	tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16);
304274bfb8dSJohn W. Linville 
305299af9d3SAndriy Tkachuk 	pos = skb_push(skb, TKIP_HDR_LEN);
306299af9d3SAndriy Tkachuk 	memmove(pos, pos + TKIP_HDR_LEN, hdr_len);
307274bfb8dSJohn W. Linville 	pos += hdr_len;
308274bfb8dSJohn W. Linville 
309274bfb8dSJohn W. Linville 	*pos++ = *rc4key;
310274bfb8dSJohn W. Linville 	*pos++ = *(rc4key + 1);
311274bfb8dSJohn W. Linville 	*pos++ = *(rc4key + 2);
312274bfb8dSJohn W. Linville 	*pos++ = (tkey->key_idx << 6) | (1 << 5) /* Ext IV included */ ;
313274bfb8dSJohn W. Linville 	*pos++ = tkey->tx_iv32 & 0xff;
314274bfb8dSJohn W. Linville 	*pos++ = (tkey->tx_iv32 >> 8) & 0xff;
315274bfb8dSJohn W. Linville 	*pos++ = (tkey->tx_iv32 >> 16) & 0xff;
316274bfb8dSJohn W. Linville 	*pos++ = (tkey->tx_iv32 >> 24) & 0xff;
317274bfb8dSJohn W. Linville 
318274bfb8dSJohn W. Linville 	tkey->tx_iv16++;
319274bfb8dSJohn W. Linville 	if (tkey->tx_iv16 == 0) {
320274bfb8dSJohn W. Linville 		tkey->tx_phase1_done = 0;
321274bfb8dSJohn W. Linville 		tkey->tx_iv32++;
322274bfb8dSJohn W. Linville 	}
323274bfb8dSJohn W. Linville 
324299af9d3SAndriy Tkachuk 	return TKIP_HDR_LEN;
325274bfb8dSJohn W. Linville }
326274bfb8dSJohn W. Linville 
lib80211_tkip_encrypt(struct sk_buff * skb,int hdr_len,void * priv)327274bfb8dSJohn W. Linville static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
328274bfb8dSJohn W. Linville {
329274bfb8dSJohn W. Linville 	struct lib80211_tkip_data *tkey = priv;
330274bfb8dSJohn W. Linville 	int len;
331274bfb8dSJohn W. Linville 	u8 rc4key[16], *pos, *icv;
332274bfb8dSJohn W. Linville 	u32 crc;
333274bfb8dSJohn W. Linville 
334274bfb8dSJohn W. Linville 	if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) {
335e87cc472SJoe Perches 		struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
336e87cc472SJoe Perches 		net_dbg_ratelimited("TKIP countermeasures: dropped TX packet to %pM\n",
337e87cc472SJoe Perches 				    hdr->addr1);
338274bfb8dSJohn W. Linville 		return -1;
339274bfb8dSJohn W. Linville 	}
340274bfb8dSJohn W. Linville 
341274bfb8dSJohn W. Linville 	if (skb_tailroom(skb) < 4 || skb->len < hdr_len)
342274bfb8dSJohn W. Linville 		return -1;
343274bfb8dSJohn W. Linville 
344274bfb8dSJohn W. Linville 	len = skb->len - hdr_len;
345274bfb8dSJohn W. Linville 	pos = skb->data + hdr_len;
346274bfb8dSJohn W. Linville 
347274bfb8dSJohn W. Linville 	if ((lib80211_tkip_hdr(skb, hdr_len, rc4key, 16, priv)) < 0)
348274bfb8dSJohn W. Linville 		return -1;
349274bfb8dSJohn W. Linville 
350274bfb8dSJohn W. Linville 	crc = ~crc32_le(~0, pos, len);
351d0833a6aSAndriy Tkachuk 	icv = skb_put(skb, 4);
352274bfb8dSJohn W. Linville 	icv[0] = crc;
353274bfb8dSJohn W. Linville 	icv[1] = crc >> 8;
354274bfb8dSJohn W. Linville 	icv[2] = crc >> 16;
355274bfb8dSJohn W. Linville 	icv[3] = crc >> 24;
356274bfb8dSJohn W. Linville 
3574be29701SArd Biesheuvel 	arc4_setkey(&tkey->tx_ctx_arc4, rc4key, 16);
3584be29701SArd Biesheuvel 	arc4_crypt(&tkey->tx_ctx_arc4, pos, pos, len + 4);
3594be29701SArd Biesheuvel 
360b802a5d6SJohannes Berg 	return 0;
361274bfb8dSJohn W. Linville }
362274bfb8dSJohn W. Linville 
363274bfb8dSJohn W. Linville /*
364274bfb8dSJohn W. Linville  * deal with seq counter wrapping correctly.
365274bfb8dSJohn W. Linville  * refer to timer_after() for jiffies wrapping handling
366274bfb8dSJohn W. Linville  */
tkip_replay_check(u32 iv32_n,u16 iv16_n,u32 iv32_o,u16 iv16_o)367274bfb8dSJohn W. Linville static inline int tkip_replay_check(u32 iv32_n, u16 iv16_n,
368274bfb8dSJohn W. Linville 				    u32 iv32_o, u16 iv16_o)
369274bfb8dSJohn W. Linville {
370274bfb8dSJohn W. Linville 	if ((s32)iv32_n - (s32)iv32_o < 0 ||
371274bfb8dSJohn W. Linville 	    (iv32_n == iv32_o && iv16_n <= iv16_o))
372274bfb8dSJohn W. Linville 		return 1;
373274bfb8dSJohn W. Linville 	return 0;
374274bfb8dSJohn W. Linville }
375274bfb8dSJohn W. Linville 
lib80211_tkip_decrypt(struct sk_buff * skb,int hdr_len,void * priv)376274bfb8dSJohn W. Linville static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
377274bfb8dSJohn W. Linville {
378274bfb8dSJohn W. Linville 	struct lib80211_tkip_data *tkey = priv;
379274bfb8dSJohn W. Linville 	u8 rc4key[16];
380274bfb8dSJohn W. Linville 	u8 keyidx, *pos;
381274bfb8dSJohn W. Linville 	u32 iv32;
382274bfb8dSJohn W. Linville 	u16 iv16;
383274bfb8dSJohn W. Linville 	struct ieee80211_hdr *hdr;
384274bfb8dSJohn W. Linville 	u8 icv[4];
385274bfb8dSJohn W. Linville 	u32 crc;
386274bfb8dSJohn W. Linville 	int plen;
387274bfb8dSJohn W. Linville 
388274bfb8dSJohn W. Linville 	hdr = (struct ieee80211_hdr *)skb->data;
389274bfb8dSJohn W. Linville 
390274bfb8dSJohn W. Linville 	if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) {
391e87cc472SJoe Perches 		net_dbg_ratelimited("TKIP countermeasures: dropped received packet from %pM\n",
392e87cc472SJoe Perches 				    hdr->addr2);
393274bfb8dSJohn W. Linville 		return -1;
394274bfb8dSJohn W. Linville 	}
395274bfb8dSJohn W. Linville 
396299af9d3SAndriy Tkachuk 	if (skb->len < hdr_len + TKIP_HDR_LEN + 4)
397274bfb8dSJohn W. Linville 		return -1;
398274bfb8dSJohn W. Linville 
399274bfb8dSJohn W. Linville 	pos = skb->data + hdr_len;
400274bfb8dSJohn W. Linville 	keyidx = pos[3];
401274bfb8dSJohn W. Linville 	if (!(keyidx & (1 << 5))) {
402e87cc472SJoe Perches 		net_dbg_ratelimited("TKIP: received packet without ExtIV flag from %pM\n",
403e87cc472SJoe Perches 				    hdr->addr2);
404274bfb8dSJohn W. Linville 		return -2;
405274bfb8dSJohn W. Linville 	}
406274bfb8dSJohn W. Linville 	keyidx >>= 6;
407274bfb8dSJohn W. Linville 	if (tkey->key_idx != keyidx) {
408996bf99cSJohannes Berg 		net_dbg_ratelimited("TKIP: RX tkey->key_idx=%d frame keyidx=%d\n",
409996bf99cSJohannes Berg 				    tkey->key_idx, keyidx);
410274bfb8dSJohn W. Linville 		return -6;
411274bfb8dSJohn W. Linville 	}
412274bfb8dSJohn W. Linville 	if (!tkey->key_set) {
413e87cc472SJoe Perches 		net_dbg_ratelimited("TKIP: received packet from %pM with keyid=%d that does not have a configured key\n",
414e87cc472SJoe Perches 				    hdr->addr2, keyidx);
415274bfb8dSJohn W. Linville 		return -3;
416274bfb8dSJohn W. Linville 	}
417274bfb8dSJohn W. Linville 	iv16 = (pos[0] << 8) | pos[2];
418274bfb8dSJohn W. Linville 	iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24);
419299af9d3SAndriy Tkachuk 	pos += TKIP_HDR_LEN;
420274bfb8dSJohn W. Linville 
421274bfb8dSJohn W. Linville 	if (tkip_replay_check(iv32, iv16, tkey->rx_iv32, tkey->rx_iv16)) {
4226f16bf3bSJohn W. Linville #ifdef CONFIG_LIB80211_DEBUG
423e87cc472SJoe Perches 		net_dbg_ratelimited("TKIP: replay detected: STA=%pM previous TSC %08x%04x received TSC %08x%04x\n",
424e87cc472SJoe Perches 				    hdr->addr2, tkey->rx_iv32, tkey->rx_iv16,
425e87cc472SJoe Perches 				    iv32, iv16);
4266f16bf3bSJohn W. Linville #endif
427274bfb8dSJohn W. Linville 		tkey->dot11RSNAStatsTKIPReplays++;
428274bfb8dSJohn W. Linville 		return -4;
429274bfb8dSJohn W. Linville 	}
430274bfb8dSJohn W. Linville 
431274bfb8dSJohn W. Linville 	if (iv32 != tkey->rx_iv32 || !tkey->rx_phase1_done) {
432274bfb8dSJohn W. Linville 		tkip_mixing_phase1(tkey->rx_ttak, tkey->key, hdr->addr2, iv32);
433274bfb8dSJohn W. Linville 		tkey->rx_phase1_done = 1;
434274bfb8dSJohn W. Linville 	}
435274bfb8dSJohn W. Linville 	tkip_mixing_phase2(rc4key, tkey->key, tkey->rx_ttak, iv16);
436274bfb8dSJohn W. Linville 
437274bfb8dSJohn W. Linville 	plen = skb->len - hdr_len - 12;
438274bfb8dSJohn W. Linville 
4394be29701SArd Biesheuvel 	arc4_setkey(&tkey->rx_ctx_arc4, rc4key, 16);
4404be29701SArd Biesheuvel 	arc4_crypt(&tkey->rx_ctx_arc4, pos, pos, plen + 4);
441274bfb8dSJohn W. Linville 
442274bfb8dSJohn W. Linville 	crc = ~crc32_le(~0, pos, plen);
443274bfb8dSJohn W. Linville 	icv[0] = crc;
444274bfb8dSJohn W. Linville 	icv[1] = crc >> 8;
445274bfb8dSJohn W. Linville 	icv[2] = crc >> 16;
446274bfb8dSJohn W. Linville 	icv[3] = crc >> 24;
447274bfb8dSJohn W. Linville 	if (memcmp(icv, pos + plen, 4) != 0) {
448274bfb8dSJohn W. Linville 		if (iv32 != tkey->rx_iv32) {
449274bfb8dSJohn W. Linville 			/* Previously cached Phase1 result was already lost, so
450274bfb8dSJohn W. Linville 			 * it needs to be recalculated for the next packet. */
451274bfb8dSJohn W. Linville 			tkey->rx_phase1_done = 0;
452274bfb8dSJohn W. Linville 		}
4536f16bf3bSJohn W. Linville #ifdef CONFIG_LIB80211_DEBUG
454e87cc472SJoe Perches 		net_dbg_ratelimited("TKIP: ICV error detected: STA=%pM\n",
455e87cc472SJoe Perches 				    hdr->addr2);
4566f16bf3bSJohn W. Linville #endif
457274bfb8dSJohn W. Linville 		tkey->dot11RSNAStatsTKIPICVErrors++;
458274bfb8dSJohn W. Linville 		return -5;
459274bfb8dSJohn W. Linville 	}
460274bfb8dSJohn W. Linville 
461274bfb8dSJohn W. Linville 	/* Update real counters only after Michael MIC verification has
462274bfb8dSJohn W. Linville 	 * completed */
463274bfb8dSJohn W. Linville 	tkey->rx_iv32_new = iv32;
464274bfb8dSJohn W. Linville 	tkey->rx_iv16_new = iv16;
465274bfb8dSJohn W. Linville 
466274bfb8dSJohn W. Linville 	/* Remove IV and ICV */
467299af9d3SAndriy Tkachuk 	memmove(skb->data + TKIP_HDR_LEN, skb->data, hdr_len);
468299af9d3SAndriy Tkachuk 	skb_pull(skb, TKIP_HDR_LEN);
469274bfb8dSJohn W. Linville 	skb_trim(skb, skb->len - 4);
470274bfb8dSJohn W. Linville 
471274bfb8dSJohn W. Linville 	return keyidx;
472274bfb8dSJohn W. Linville }
473274bfb8dSJohn W. Linville 
michael_mic(struct crypto_shash * tfm_michael,u8 * key,u8 * hdr,u8 * data,size_t data_len,u8 * mic)474d17504b1SKees Cook static int michael_mic(struct crypto_shash *tfm_michael, u8 *key, u8 *hdr,
475274bfb8dSJohn W. Linville 		       u8 *data, size_t data_len, u8 *mic)
476274bfb8dSJohn W. Linville {
477d17504b1SKees Cook 	SHASH_DESC_ON_STACK(desc, tfm_michael);
478608fb34cSHerbert Xu 	int err;
479274bfb8dSJohn W. Linville 
480274bfb8dSJohn W. Linville 	if (tfm_michael == NULL) {
481e9c0268fSJoe Perches 		pr_warn("%s(): tfm_michael == NULL\n", __func__);
482274bfb8dSJohn W. Linville 		return -1;
483274bfb8dSJohn W. Linville 	}
484274bfb8dSJohn W. Linville 
485d17504b1SKees Cook 	desc->tfm = tfm_michael;
486d17504b1SKees Cook 
487d17504b1SKees Cook 	if (crypto_shash_setkey(tfm_michael, key, 8))
488274bfb8dSJohn W. Linville 		return -1;
489274bfb8dSJohn W. Linville 
490d17504b1SKees Cook 	err = crypto_shash_init(desc);
491d17504b1SKees Cook 	if (err)
492d17504b1SKees Cook 		goto out;
493d17504b1SKees Cook 	err = crypto_shash_update(desc, hdr, 16);
494d17504b1SKees Cook 	if (err)
495d17504b1SKees Cook 		goto out;
496d17504b1SKees Cook 	err = crypto_shash_update(desc, data, data_len);
497d17504b1SKees Cook 	if (err)
498d17504b1SKees Cook 		goto out;
499d17504b1SKees Cook 	err = crypto_shash_final(desc, mic);
500d17504b1SKees Cook 
501d17504b1SKees Cook out:
502d17504b1SKees Cook 	shash_desc_zero(desc);
503608fb34cSHerbert Xu 	return err;
504274bfb8dSJohn W. Linville }
505274bfb8dSJohn W. Linville 
michael_mic_hdr(struct sk_buff * skb,u8 * hdr)506274bfb8dSJohn W. Linville static void michael_mic_hdr(struct sk_buff *skb, u8 * hdr)
507274bfb8dSJohn W. Linville {
508274bfb8dSJohn W. Linville 	struct ieee80211_hdr *hdr11;
509274bfb8dSJohn W. Linville 
510274bfb8dSJohn W. Linville 	hdr11 = (struct ieee80211_hdr *)skb->data;
511274bfb8dSJohn W. Linville 
512274bfb8dSJohn W. Linville 	switch (le16_to_cpu(hdr11->frame_control) &
513274bfb8dSJohn W. Linville 		(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
514274bfb8dSJohn W. Linville 	case IEEE80211_FCTL_TODS:
515274bfb8dSJohn W. Linville 		memcpy(hdr, hdr11->addr3, ETH_ALEN);	/* DA */
516274bfb8dSJohn W. Linville 		memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN);	/* SA */
517274bfb8dSJohn W. Linville 		break;
518274bfb8dSJohn W. Linville 	case IEEE80211_FCTL_FROMDS:
519274bfb8dSJohn W. Linville 		memcpy(hdr, hdr11->addr1, ETH_ALEN);	/* DA */
520274bfb8dSJohn W. Linville 		memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN);	/* SA */
521274bfb8dSJohn W. Linville 		break;
522274bfb8dSJohn W. Linville 	case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS:
523274bfb8dSJohn W. Linville 		memcpy(hdr, hdr11->addr3, ETH_ALEN);	/* DA */
524274bfb8dSJohn W. Linville 		memcpy(hdr + ETH_ALEN, hdr11->addr4, ETH_ALEN);	/* SA */
525274bfb8dSJohn W. Linville 		break;
52610f3366bSArnd Bergmann 	default:
527274bfb8dSJohn W. Linville 		memcpy(hdr, hdr11->addr1, ETH_ALEN);	/* DA */
528274bfb8dSJohn W. Linville 		memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN);	/* SA */
529274bfb8dSJohn W. Linville 		break;
530274bfb8dSJohn W. Linville 	}
531274bfb8dSJohn W. Linville 
532274bfb8dSJohn W. Linville 	if (ieee80211_is_data_qos(hdr11->frame_control)) {
5333f6ff6baSJohn W. Linville 		hdr[12] = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(hdr11)))
534274bfb8dSJohn W. Linville 			& IEEE80211_QOS_CTL_TID_MASK;
535274bfb8dSJohn W. Linville 	} else
536274bfb8dSJohn W. Linville 		hdr[12] = 0;		/* priority */
537274bfb8dSJohn W. Linville 
538274bfb8dSJohn W. Linville 	hdr[13] = hdr[14] = hdr[15] = 0;	/* reserved */
539274bfb8dSJohn W. Linville }
540274bfb8dSJohn W. Linville 
lib80211_michael_mic_add(struct sk_buff * skb,int hdr_len,void * priv)541274bfb8dSJohn W. Linville static int lib80211_michael_mic_add(struct sk_buff *skb, int hdr_len,
542274bfb8dSJohn W. Linville 				     void *priv)
543274bfb8dSJohn W. Linville {
544274bfb8dSJohn W. Linville 	struct lib80211_tkip_data *tkey = priv;
545274bfb8dSJohn W. Linville 	u8 *pos;
546274bfb8dSJohn W. Linville 
547274bfb8dSJohn W. Linville 	if (skb_tailroom(skb) < 8 || skb->len < hdr_len) {
548274bfb8dSJohn W. Linville 		printk(KERN_DEBUG "Invalid packet for Michael MIC add "
549274bfb8dSJohn W. Linville 		       "(tailroom=%d hdr_len=%d skb->len=%d)\n",
550274bfb8dSJohn W. Linville 		       skb_tailroom(skb), hdr_len, skb->len);
551274bfb8dSJohn W. Linville 		return -1;
552274bfb8dSJohn W. Linville 	}
553274bfb8dSJohn W. Linville 
554274bfb8dSJohn W. Linville 	michael_mic_hdr(skb, tkey->tx_hdr);
555274bfb8dSJohn W. Linville 	pos = skb_put(skb, 8);
556274bfb8dSJohn W. Linville 	if (michael_mic(tkey->tx_tfm_michael, &tkey->key[16], tkey->tx_hdr,
557274bfb8dSJohn W. Linville 			skb->data + hdr_len, skb->len - 8 - hdr_len, pos))
558274bfb8dSJohn W. Linville 		return -1;
559274bfb8dSJohn W. Linville 
560274bfb8dSJohn W. Linville 	return 0;
561274bfb8dSJohn W. Linville }
562274bfb8dSJohn W. Linville 
lib80211_michael_mic_failure(struct net_device * dev,struct ieee80211_hdr * hdr,int keyidx)563274bfb8dSJohn W. Linville static void lib80211_michael_mic_failure(struct net_device *dev,
564274bfb8dSJohn W. Linville 					  struct ieee80211_hdr *hdr,
565274bfb8dSJohn W. Linville 					  int keyidx)
566274bfb8dSJohn W. Linville {
567274bfb8dSJohn W. Linville 	union iwreq_data wrqu;
568274bfb8dSJohn W. Linville 	struct iw_michaelmicfailure ev;
569274bfb8dSJohn W. Linville 
570274bfb8dSJohn W. Linville 	/* TODO: needed parameters: count, keyid, key type, TSC */
571274bfb8dSJohn W. Linville 	memset(&ev, 0, sizeof(ev));
572274bfb8dSJohn W. Linville 	ev.flags = keyidx & IW_MICFAILURE_KEY_ID;
573274bfb8dSJohn W. Linville 	if (hdr->addr1[0] & 0x01)
574274bfb8dSJohn W. Linville 		ev.flags |= IW_MICFAILURE_GROUP;
575274bfb8dSJohn W. Linville 	else
576274bfb8dSJohn W. Linville 		ev.flags |= IW_MICFAILURE_PAIRWISE;
577274bfb8dSJohn W. Linville 	ev.src_addr.sa_family = ARPHRD_ETHER;
578274bfb8dSJohn W. Linville 	memcpy(ev.src_addr.sa_data, hdr->addr2, ETH_ALEN);
579274bfb8dSJohn W. Linville 	memset(&wrqu, 0, sizeof(wrqu));
580274bfb8dSJohn W. Linville 	wrqu.data.length = sizeof(ev);
581274bfb8dSJohn W. Linville 	wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, (char *)&ev);
582274bfb8dSJohn W. Linville }
583274bfb8dSJohn W. Linville 
lib80211_michael_mic_verify(struct sk_buff * skb,int keyidx,int hdr_len,void * priv)584274bfb8dSJohn W. Linville static int lib80211_michael_mic_verify(struct sk_buff *skb, int keyidx,
585274bfb8dSJohn W. Linville 					int hdr_len, void *priv)
586274bfb8dSJohn W. Linville {
587274bfb8dSJohn W. Linville 	struct lib80211_tkip_data *tkey = priv;
588274bfb8dSJohn W. Linville 	u8 mic[8];
589274bfb8dSJohn W. Linville 
590274bfb8dSJohn W. Linville 	if (!tkey->key_set)
591274bfb8dSJohn W. Linville 		return -1;
592274bfb8dSJohn W. Linville 
593274bfb8dSJohn W. Linville 	michael_mic_hdr(skb, tkey->rx_hdr);
594274bfb8dSJohn W. Linville 	if (michael_mic(tkey->rx_tfm_michael, &tkey->key[24], tkey->rx_hdr,
595274bfb8dSJohn W. Linville 			skb->data + hdr_len, skb->len - 8 - hdr_len, mic))
596274bfb8dSJohn W. Linville 		return -1;
597274bfb8dSJohn W. Linville 	if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) {
598274bfb8dSJohn W. Linville 		struct ieee80211_hdr *hdr;
599274bfb8dSJohn W. Linville 		hdr = (struct ieee80211_hdr *)skb->data;
600274bfb8dSJohn W. Linville 		printk(KERN_DEBUG "%s: Michael MIC verification failed for "
601274bfb8dSJohn W. Linville 		       "MSDU from %pM keyidx=%d\n",
602274bfb8dSJohn W. Linville 		       skb->dev ? skb->dev->name : "N/A", hdr->addr2,
603274bfb8dSJohn W. Linville 		       keyidx);
604274bfb8dSJohn W. Linville 		if (skb->dev)
605274bfb8dSJohn W. Linville 			lib80211_michael_mic_failure(skb->dev, hdr, keyidx);
606274bfb8dSJohn W. Linville 		tkey->dot11RSNAStatsTKIPLocalMICFailures++;
607274bfb8dSJohn W. Linville 		return -1;
608274bfb8dSJohn W. Linville 	}
609274bfb8dSJohn W. Linville 
610274bfb8dSJohn W. Linville 	/* Update TSC counters for RX now that the packet verification has
611274bfb8dSJohn W. Linville 	 * completed. */
612274bfb8dSJohn W. Linville 	tkey->rx_iv32 = tkey->rx_iv32_new;
613274bfb8dSJohn W. Linville 	tkey->rx_iv16 = tkey->rx_iv16_new;
614274bfb8dSJohn W. Linville 
615274bfb8dSJohn W. Linville 	skb_trim(skb, skb->len - 8);
616274bfb8dSJohn W. Linville 
617274bfb8dSJohn W. Linville 	return 0;
618274bfb8dSJohn W. Linville }
619274bfb8dSJohn W. Linville 
lib80211_tkip_set_key(void * key,int len,u8 * seq,void * priv)620274bfb8dSJohn W. Linville static int lib80211_tkip_set_key(void *key, int len, u8 * seq, void *priv)
621274bfb8dSJohn W. Linville {
622274bfb8dSJohn W. Linville 	struct lib80211_tkip_data *tkey = priv;
623274bfb8dSJohn W. Linville 	int keyidx;
624d17504b1SKees Cook 	struct crypto_shash *tfm = tkey->tx_tfm_michael;
6254be29701SArd Biesheuvel 	struct arc4_ctx *tfm2 = &tkey->tx_ctx_arc4;
626d17504b1SKees Cook 	struct crypto_shash *tfm3 = tkey->rx_tfm_michael;
6274be29701SArd Biesheuvel 	struct arc4_ctx *tfm4 = &tkey->rx_ctx_arc4;
628274bfb8dSJohn W. Linville 
629274bfb8dSJohn W. Linville 	keyidx = tkey->key_idx;
630274bfb8dSJohn W. Linville 	memset(tkey, 0, sizeof(*tkey));
631274bfb8dSJohn W. Linville 	tkey->key_idx = keyidx;
632274bfb8dSJohn W. Linville 	tkey->tx_tfm_michael = tfm;
6334be29701SArd Biesheuvel 	tkey->tx_ctx_arc4 = *tfm2;
634274bfb8dSJohn W. Linville 	tkey->rx_tfm_michael = tfm3;
6354be29701SArd Biesheuvel 	tkey->rx_ctx_arc4 = *tfm4;
636274bfb8dSJohn W. Linville 	if (len == TKIP_KEY_LEN) {
637274bfb8dSJohn W. Linville 		memcpy(tkey->key, key, TKIP_KEY_LEN);
638274bfb8dSJohn W. Linville 		tkey->key_set = 1;
639274bfb8dSJohn W. Linville 		tkey->tx_iv16 = 1;	/* TSC is initialized to 1 */
640274bfb8dSJohn W. Linville 		if (seq) {
641274bfb8dSJohn W. Linville 			tkey->rx_iv32 = (seq[5] << 24) | (seq[4] << 16) |
642274bfb8dSJohn W. Linville 			    (seq[3] << 8) | seq[2];
643274bfb8dSJohn W. Linville 			tkey->rx_iv16 = (seq[1] << 8) | seq[0];
644274bfb8dSJohn W. Linville 		}
645274bfb8dSJohn W. Linville 	} else if (len == 0)
646274bfb8dSJohn W. Linville 		tkey->key_set = 0;
647274bfb8dSJohn W. Linville 	else
648274bfb8dSJohn W. Linville 		return -1;
649274bfb8dSJohn W. Linville 
650274bfb8dSJohn W. Linville 	return 0;
651274bfb8dSJohn W. Linville }
652274bfb8dSJohn W. Linville 
lib80211_tkip_get_key(void * key,int len,u8 * seq,void * priv)653274bfb8dSJohn W. Linville static int lib80211_tkip_get_key(void *key, int len, u8 * seq, void *priv)
654274bfb8dSJohn W. Linville {
655274bfb8dSJohn W. Linville 	struct lib80211_tkip_data *tkey = priv;
656274bfb8dSJohn W. Linville 
657274bfb8dSJohn W. Linville 	if (len < TKIP_KEY_LEN)
658274bfb8dSJohn W. Linville 		return -1;
659274bfb8dSJohn W. Linville 
660274bfb8dSJohn W. Linville 	if (!tkey->key_set)
661274bfb8dSJohn W. Linville 		return 0;
662274bfb8dSJohn W. Linville 	memcpy(key, tkey->key, TKIP_KEY_LEN);
663274bfb8dSJohn W. Linville 
664274bfb8dSJohn W. Linville 	if (seq) {
665274bfb8dSJohn W. Linville 		/* Return the sequence number of the last transmitted frame. */
666274bfb8dSJohn W. Linville 		u16 iv16 = tkey->tx_iv16;
667274bfb8dSJohn W. Linville 		u32 iv32 = tkey->tx_iv32;
668274bfb8dSJohn W. Linville 		if (iv16 == 0)
669274bfb8dSJohn W. Linville 			iv32--;
670274bfb8dSJohn W. Linville 		iv16--;
671274bfb8dSJohn W. Linville 		seq[0] = tkey->tx_iv16;
672274bfb8dSJohn W. Linville 		seq[1] = tkey->tx_iv16 >> 8;
673274bfb8dSJohn W. Linville 		seq[2] = tkey->tx_iv32;
674274bfb8dSJohn W. Linville 		seq[3] = tkey->tx_iv32 >> 8;
675274bfb8dSJohn W. Linville 		seq[4] = tkey->tx_iv32 >> 16;
676274bfb8dSJohn W. Linville 		seq[5] = tkey->tx_iv32 >> 24;
677274bfb8dSJohn W. Linville 	}
678274bfb8dSJohn W. Linville 
679274bfb8dSJohn W. Linville 	return TKIP_KEY_LEN;
680274bfb8dSJohn W. Linville }
681274bfb8dSJohn W. Linville 
lib80211_tkip_print_stats(struct seq_file * m,void * priv)6826bbefe86SDavid Howells static void lib80211_tkip_print_stats(struct seq_file *m, void *priv)
683274bfb8dSJohn W. Linville {
684274bfb8dSJohn W. Linville 	struct lib80211_tkip_data *tkip = priv;
6856bbefe86SDavid Howells 	seq_printf(m,
6866bbefe86SDavid Howells 		   "key[%d] alg=TKIP key_set=%d "
687274bfb8dSJohn W. Linville 		   "tx_pn=%02x%02x%02x%02x%02x%02x "
688274bfb8dSJohn W. Linville 		   "rx_pn=%02x%02x%02x%02x%02x%02x "
689274bfb8dSJohn W. Linville 		   "replays=%d icv_errors=%d local_mic_failures=%d\n",
690274bfb8dSJohn W. Linville 		   tkip->key_idx, tkip->key_set,
691274bfb8dSJohn W. Linville 		   (tkip->tx_iv32 >> 24) & 0xff,
692274bfb8dSJohn W. Linville 		   (tkip->tx_iv32 >> 16) & 0xff,
693274bfb8dSJohn W. Linville 		   (tkip->tx_iv32 >> 8) & 0xff,
694274bfb8dSJohn W. Linville 		   tkip->tx_iv32 & 0xff,
695274bfb8dSJohn W. Linville 		   (tkip->tx_iv16 >> 8) & 0xff,
696274bfb8dSJohn W. Linville 		   tkip->tx_iv16 & 0xff,
697274bfb8dSJohn W. Linville 		   (tkip->rx_iv32 >> 24) & 0xff,
698274bfb8dSJohn W. Linville 		   (tkip->rx_iv32 >> 16) & 0xff,
699274bfb8dSJohn W. Linville 		   (tkip->rx_iv32 >> 8) & 0xff,
700274bfb8dSJohn W. Linville 		   tkip->rx_iv32 & 0xff,
701274bfb8dSJohn W. Linville 		   (tkip->rx_iv16 >> 8) & 0xff,
702274bfb8dSJohn W. Linville 		   tkip->rx_iv16 & 0xff,
703274bfb8dSJohn W. Linville 		   tkip->dot11RSNAStatsTKIPReplays,
704274bfb8dSJohn W. Linville 		   tkip->dot11RSNAStatsTKIPICVErrors,
705274bfb8dSJohn W. Linville 		   tkip->dot11RSNAStatsTKIPLocalMICFailures);
706274bfb8dSJohn W. Linville }
707274bfb8dSJohn W. Linville 
708274bfb8dSJohn W. Linville static struct lib80211_crypto_ops lib80211_crypt_tkip = {
709274bfb8dSJohn W. Linville 	.name = "TKIP",
710274bfb8dSJohn W. Linville 	.init = lib80211_tkip_init,
711274bfb8dSJohn W. Linville 	.deinit = lib80211_tkip_deinit,
712274bfb8dSJohn W. Linville 	.encrypt_mpdu = lib80211_tkip_encrypt,
713274bfb8dSJohn W. Linville 	.decrypt_mpdu = lib80211_tkip_decrypt,
714274bfb8dSJohn W. Linville 	.encrypt_msdu = lib80211_michael_mic_add,
715274bfb8dSJohn W. Linville 	.decrypt_msdu = lib80211_michael_mic_verify,
716274bfb8dSJohn W. Linville 	.set_key = lib80211_tkip_set_key,
717274bfb8dSJohn W. Linville 	.get_key = lib80211_tkip_get_key,
718274bfb8dSJohn W. Linville 	.print_stats = lib80211_tkip_print_stats,
719274bfb8dSJohn W. Linville 	.extra_mpdu_prefix_len = 4 + 4,	/* IV + ExtIV */
720274bfb8dSJohn W. Linville 	.extra_mpdu_postfix_len = 4,	/* ICV */
721274bfb8dSJohn W. Linville 	.extra_msdu_postfix_len = 8,	/* MIC */
722274bfb8dSJohn W. Linville 	.get_flags = lib80211_tkip_get_flags,
723274bfb8dSJohn W. Linville 	.set_flags = lib80211_tkip_set_flags,
724274bfb8dSJohn W. Linville 	.owner = THIS_MODULE,
725274bfb8dSJohn W. Linville };
726274bfb8dSJohn W. Linville 
lib80211_crypto_tkip_init(void)727274bfb8dSJohn W. Linville static int __init lib80211_crypto_tkip_init(void)
728274bfb8dSJohn W. Linville {
729274bfb8dSJohn W. Linville 	return lib80211_register_crypto_ops(&lib80211_crypt_tkip);
730274bfb8dSJohn W. Linville }
731274bfb8dSJohn W. Linville 
lib80211_crypto_tkip_exit(void)732274bfb8dSJohn W. Linville static void __exit lib80211_crypto_tkip_exit(void)
733274bfb8dSJohn W. Linville {
734274bfb8dSJohn W. Linville 	lib80211_unregister_crypto_ops(&lib80211_crypt_tkip);
735274bfb8dSJohn W. Linville }
736274bfb8dSJohn W. Linville 
737274bfb8dSJohn W. Linville module_init(lib80211_crypto_tkip_init);
738274bfb8dSJohn W. Linville module_exit(lib80211_crypto_tkip_exit);
739