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