blob: 7ea4f2b0770ecbbbbcd9c98cbd9fe690534c2bb8 [file] [log] [blame]
Jeff Garzikb4538722005-05-12 22:48:20 -04001/*
John W. Linville274bfb82008-10-29 11:35:05 -04002 * lib80211 crypt: host-based TKIP encryption implementation for lib80211
Jeff Garzikb4538722005-05-12 22:48:20 -04003 *
Jouni Malinen85d32e72007-03-24 17:15:30 -07004 * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi>
John W. Linville274bfb82008-10-29 11:35:05 -04005 * Copyright (c) 2008, John W. Linville <linville@tuxdriver.com>
Jeff Garzikb4538722005-05-12 22:48:20 -04006 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation. See README and COPYING for
10 * more details.
11 */
12
Joe Perchese9c02682010-11-16 19:56:49 -080013#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14
Herbert Xuf12cc202006-08-22 20:36:13 +100015#include <linux/err.h>
Jeff Garzikb4538722005-05-12 22:48:20 -040016#include <linux/module.h>
17#include <linux/init.h>
18#include <linux/slab.h>
19#include <linux/random.h>
Ralf Baechle11763602007-10-23 20:42:11 +020020#include <linux/scatterlist.h>
Jeff Garzikb4538722005-05-12 22:48:20 -040021#include <linux/skbuff.h>
22#include <linux/netdevice.h>
Al Virod7fe0f22006-12-03 23:15:30 -050023#include <linux/mm.h>
Jeff Garzikb4538722005-05-12 22:48:20 -040024#include <linux/if_ether.h>
25#include <linux/if_arp.h>
26#include <asm/string.h>
27
John W. Linville274bfb82008-10-29 11:35:05 -040028#include <linux/wireless.h>
29#include <linux/ieee80211.h>
30#include <net/iw_handler.h>
Jeff Garzikb4538722005-05-12 22:48:20 -040031
Jeff Garzikb4538722005-05-12 22:48:20 -040032#include <linux/crypto.h>
Jeff Garzikb4538722005-05-12 22:48:20 -040033#include <linux/crc32.h>
34
John W. Linville274bfb82008-10-29 11:35:05 -040035#include <net/lib80211.h>
36
Jeff Garzikb4538722005-05-12 22:48:20 -040037MODULE_AUTHOR("Jouni Malinen");
John W. Linville274bfb82008-10-29 11:35:05 -040038MODULE_DESCRIPTION("lib80211 crypt: TKIP");
Jeff Garzikb4538722005-05-12 22:48:20 -040039MODULE_LICENSE("GPL");
40
Andriy Tkachuk299af9d2010-02-02 16:33:53 +020041#define TKIP_HDR_LEN 8
42
John W. Linville274bfb82008-10-29 11:35:05 -040043struct lib80211_tkip_data {
Jeff Garzikb4538722005-05-12 22:48:20 -040044#define TKIP_KEY_LEN 32
45 u8 key[TKIP_KEY_LEN];
46 int key_set;
47
48 u32 tx_iv32;
49 u16 tx_iv16;
50 u16 tx_ttak[5];
51 int tx_phase1_done;
52
53 u32 rx_iv32;
54 u16 rx_iv16;
55 u16 rx_ttak[5];
56 int rx_phase1_done;
57 u32 rx_iv32_new;
58 u16 rx_iv16_new;
59
60 u32 dot11RSNAStatsTKIPReplays;
61 u32 dot11RSNAStatsTKIPICVErrors;
62 u32 dot11RSNAStatsTKIPLocalMICFailures;
63
64 int key_idx;
65
Jeff Garzik28eb1772006-09-22 20:10:23 -040066 struct crypto_blkcipher *rx_tfm_arc4;
67 struct crypto_hash *rx_tfm_michael;
68 struct crypto_blkcipher *tx_tfm_arc4;
69 struct crypto_hash *tx_tfm_michael;
Jeff Garzikb4538722005-05-12 22:48:20 -040070
71 /* scratch buffers for virt_to_page() (crypto API) */
72 u8 rx_hdr[16], tx_hdr[16];
James Ketrenos20d64712005-09-21 11:53:43 -050073
James Ketrenos6eb6edf2005-09-22 10:34:15 +000074 unsigned long flags;
Jeff Garzikb4538722005-05-12 22:48:20 -040075};
76
John W. Linville274bfb82008-10-29 11:35:05 -040077static unsigned long lib80211_tkip_set_flags(unsigned long flags, void *priv)
James Ketrenos6eb6edf2005-09-22 10:34:15 +000078{
John W. Linville274bfb82008-10-29 11:35:05 -040079 struct lib80211_tkip_data *_priv = priv;
James Ketrenos6eb6edf2005-09-22 10:34:15 +000080 unsigned long old_flags = _priv->flags;
81 _priv->flags = flags;
82 return old_flags;
83}
84
John W. Linville274bfb82008-10-29 11:35:05 -040085static unsigned long lib80211_tkip_get_flags(void *priv)
James Ketrenos6eb6edf2005-09-22 10:34:15 +000086{
John W. Linville274bfb82008-10-29 11:35:05 -040087 struct lib80211_tkip_data *_priv = priv;
James Ketrenos6eb6edf2005-09-22 10:34:15 +000088 return _priv->flags;
89}
90
John W. Linville274bfb82008-10-29 11:35:05 -040091static void *lib80211_tkip_init(int key_idx)
Jeff Garzikb4538722005-05-12 22:48:20 -040092{
John W. Linville274bfb82008-10-29 11:35:05 -040093 struct lib80211_tkip_data *priv;
Jeff Garzikb4538722005-05-12 22:48:20 -040094
Zhu Yi8aa914b2006-01-19 16:22:07 +080095 priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
Jeff Garzikb4538722005-05-12 22:48:20 -040096 if (priv == NULL)
97 goto fail;
James Ketrenos20d64712005-09-21 11:53:43 -050098
Jeff Garzikb4538722005-05-12 22:48:20 -040099 priv->key_idx = key_idx;
100
Jeff Garzik28eb1772006-09-22 20:10:23 -0400101 priv->tx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0,
Herbert Xuf12cc202006-08-22 20:36:13 +1000102 CRYPTO_ALG_ASYNC);
Jeff Garzik28eb1772006-09-22 20:10:23 -0400103 if (IS_ERR(priv->tx_tfm_arc4)) {
Joe Perchese9c02682010-11-16 19:56:49 -0800104 printk(KERN_DEBUG pr_fmt("could not allocate crypto API arc4\n"));
Jeff Garzik18379872006-09-22 21:19:05 -0400105 priv->tx_tfm_arc4 = NULL;
Jeff Garzikb4538722005-05-12 22:48:20 -0400106 goto fail;
107 }
108
Jeff Garzik28eb1772006-09-22 20:10:23 -0400109 priv->tx_tfm_michael = crypto_alloc_hash("michael_mic", 0,
110 CRYPTO_ALG_ASYNC);
111 if (IS_ERR(priv->tx_tfm_michael)) {
Joe Perchese9c02682010-11-16 19:56:49 -0800112 printk(KERN_DEBUG pr_fmt("could not allocate crypto API michael_mic\n"));
Jeff Garzik18379872006-09-22 21:19:05 -0400113 priv->tx_tfm_michael = NULL;
Zhu Yi5a656942006-08-21 11:33:56 +0800114 goto fail;
115 }
116
Jeff Garzik28eb1772006-09-22 20:10:23 -0400117 priv->rx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0,
118 CRYPTO_ALG_ASYNC);
119 if (IS_ERR(priv->rx_tfm_arc4)) {
Joe Perchese9c02682010-11-16 19:56:49 -0800120 printk(KERN_DEBUG pr_fmt("could not allocate crypto API arc4\n"));
Jeff Garzik18379872006-09-22 21:19:05 -0400121 priv->rx_tfm_arc4 = NULL;
Zhu Yi5a656942006-08-21 11:33:56 +0800122 goto fail;
123 }
124
Jeff Garzik28eb1772006-09-22 20:10:23 -0400125 priv->rx_tfm_michael = crypto_alloc_hash("michael_mic", 0,
126 CRYPTO_ALG_ASYNC);
127 if (IS_ERR(priv->rx_tfm_michael)) {
Joe Perchese9c02682010-11-16 19:56:49 -0800128 printk(KERN_DEBUG pr_fmt("could not allocate crypto API michael_mic\n"));
Jeff Garzik18379872006-09-22 21:19:05 -0400129 priv->rx_tfm_michael = NULL;
Jeff Garzikb4538722005-05-12 22:48:20 -0400130 goto fail;
131 }
132
133 return priv;
134
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400135 fail:
Jeff Garzikb4538722005-05-12 22:48:20 -0400136 if (priv) {
Zhu Yi5a656942006-08-21 11:33:56 +0800137 if (priv->tx_tfm_michael)
Jeff Garzik28eb1772006-09-22 20:10:23 -0400138 crypto_free_hash(priv->tx_tfm_michael);
Zhu Yi5a656942006-08-21 11:33:56 +0800139 if (priv->tx_tfm_arc4)
Jeff Garzik28eb1772006-09-22 20:10:23 -0400140 crypto_free_blkcipher(priv->tx_tfm_arc4);
Zhu Yi5a656942006-08-21 11:33:56 +0800141 if (priv->rx_tfm_michael)
Jeff Garzik28eb1772006-09-22 20:10:23 -0400142 crypto_free_hash(priv->rx_tfm_michael);
Zhu Yi5a656942006-08-21 11:33:56 +0800143 if (priv->rx_tfm_arc4)
Jeff Garzik28eb1772006-09-22 20:10:23 -0400144 crypto_free_blkcipher(priv->rx_tfm_arc4);
Jeff Garzikb4538722005-05-12 22:48:20 -0400145 kfree(priv);
146 }
147
148 return NULL;
149}
150
John W. Linville274bfb82008-10-29 11:35:05 -0400151static void lib80211_tkip_deinit(void *priv)
Jeff Garzikb4538722005-05-12 22:48:20 -0400152{
John W. Linville274bfb82008-10-29 11:35:05 -0400153 struct lib80211_tkip_data *_priv = priv;
Zhu Yi5a656942006-08-21 11:33:56 +0800154 if (_priv) {
155 if (_priv->tx_tfm_michael)
Jeff Garzik28eb1772006-09-22 20:10:23 -0400156 crypto_free_hash(_priv->tx_tfm_michael);
Zhu Yi5a656942006-08-21 11:33:56 +0800157 if (_priv->tx_tfm_arc4)
Jeff Garzik28eb1772006-09-22 20:10:23 -0400158 crypto_free_blkcipher(_priv->tx_tfm_arc4);
Zhu Yi5a656942006-08-21 11:33:56 +0800159 if (_priv->rx_tfm_michael)
Jeff Garzik28eb1772006-09-22 20:10:23 -0400160 crypto_free_hash(_priv->rx_tfm_michael);
Zhu Yi5a656942006-08-21 11:33:56 +0800161 if (_priv->rx_tfm_arc4)
Jeff Garzik28eb1772006-09-22 20:10:23 -0400162 crypto_free_blkcipher(_priv->rx_tfm_arc4);
Zhu Yi5a656942006-08-21 11:33:56 +0800163 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400164 kfree(priv);
165}
166
Jeff Garzikb4538722005-05-12 22:48:20 -0400167static inline u16 RotR1(u16 val)
168{
169 return (val >> 1) | (val << 15);
170}
171
Jeff Garzikb4538722005-05-12 22:48:20 -0400172static inline u8 Lo8(u16 val)
173{
174 return val & 0xff;
175}
176
Jeff Garzikb4538722005-05-12 22:48:20 -0400177static inline u8 Hi8(u16 val)
178{
179 return val >> 8;
180}
181
Jeff Garzikb4538722005-05-12 22:48:20 -0400182static inline u16 Lo16(u32 val)
183{
184 return val & 0xffff;
185}
186
Jeff Garzikb4538722005-05-12 22:48:20 -0400187static inline u16 Hi16(u32 val)
188{
189 return val >> 16;
190}
191
Jeff Garzikb4538722005-05-12 22:48:20 -0400192static inline u16 Mk16(u8 hi, u8 lo)
193{
194 return lo | (((u16) hi) << 8);
195}
196
Al Virod9e94d52007-12-29 05:01:07 -0500197static inline u16 Mk16_le(__le16 * v)
Jeff Garzikb4538722005-05-12 22:48:20 -0400198{
199 return le16_to_cpu(*v);
200}
201
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400202static const u16 Sbox[256] = {
Jeff Garzikb4538722005-05-12 22:48:20 -0400203 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
204 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
205 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
206 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
207 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
208 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
209 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
210 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
211 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
212 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
213 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
214 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
215 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
216 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
217 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
218 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
219 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
220 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
221 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
222 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
223 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
224 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
225 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
226 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
227 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
228 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
229 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
230 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
231 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
232 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
233 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
234 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
235};
236
Jeff Garzikb4538722005-05-12 22:48:20 -0400237static inline u16 _S_(u16 v)
238{
239 u16 t = Sbox[Hi8(v)];
240 return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8));
241}
242
Jeff Garzikb4538722005-05-12 22:48:20 -0400243#define PHASE1_LOOP_COUNT 8
244
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400245static void tkip_mixing_phase1(u16 * TTAK, const u8 * TK, const u8 * TA,
246 u32 IV32)
Jeff Garzikb4538722005-05-12 22:48:20 -0400247{
248 int i, j;
249
250 /* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */
251 TTAK[0] = Lo16(IV32);
252 TTAK[1] = Hi16(IV32);
253 TTAK[2] = Mk16(TA[1], TA[0]);
254 TTAK[3] = Mk16(TA[3], TA[2]);
255 TTAK[4] = Mk16(TA[5], TA[4]);
256
257 for (i = 0; i < PHASE1_LOOP_COUNT; i++) {
258 j = 2 * (i & 1);
259 TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j]));
260 TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j]));
261 TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j]));
262 TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j]));
263 TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i;
264 }
265}
266
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400267static void tkip_mixing_phase2(u8 * WEPSeed, const u8 * TK, const u16 * TTAK,
Jeff Garzikb4538722005-05-12 22:48:20 -0400268 u16 IV16)
269{
270 /* Make temporary area overlap WEP seed so that the final copy can be
271 * avoided on little endian hosts. */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400272 u16 *PPK = (u16 *) & WEPSeed[4];
Jeff Garzikb4538722005-05-12 22:48:20 -0400273
274 /* Step 1 - make copy of TTAK and bring in TSC */
275 PPK[0] = TTAK[0];
276 PPK[1] = TTAK[1];
277 PPK[2] = TTAK[2];
278 PPK[3] = TTAK[3];
279 PPK[4] = TTAK[4];
280 PPK[5] = TTAK[4] + IV16;
281
282 /* Step 2 - 96-bit bijective mixing using S-box */
Al Virod9e94d52007-12-29 05:01:07 -0500283 PPK[0] += _S_(PPK[5] ^ Mk16_le((__le16 *) & TK[0]));
284 PPK[1] += _S_(PPK[0] ^ Mk16_le((__le16 *) & TK[2]));
285 PPK[2] += _S_(PPK[1] ^ Mk16_le((__le16 *) & TK[4]));
286 PPK[3] += _S_(PPK[2] ^ Mk16_le((__le16 *) & TK[6]));
287 PPK[4] += _S_(PPK[3] ^ Mk16_le((__le16 *) & TK[8]));
288 PPK[5] += _S_(PPK[4] ^ Mk16_le((__le16 *) & TK[10]));
Jeff Garzikb4538722005-05-12 22:48:20 -0400289
Al Virod9e94d52007-12-29 05:01:07 -0500290 PPK[0] += RotR1(PPK[5] ^ Mk16_le((__le16 *) & TK[12]));
291 PPK[1] += RotR1(PPK[0] ^ Mk16_le((__le16 *) & TK[14]));
Jeff Garzikb4538722005-05-12 22:48:20 -0400292 PPK[2] += RotR1(PPK[1]);
293 PPK[3] += RotR1(PPK[2]);
294 PPK[4] += RotR1(PPK[3]);
295 PPK[5] += RotR1(PPK[4]);
296
297 /* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value
298 * WEPSeed[0..2] is transmitted as WEP IV */
299 WEPSeed[0] = Hi8(IV16);
300 WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F;
301 WEPSeed[2] = Lo8(IV16);
Al Virod9e94d52007-12-29 05:01:07 -0500302 WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((__le16 *) & TK[0])) >> 1);
Jeff Garzikb4538722005-05-12 22:48:20 -0400303
304#ifdef __BIG_ENDIAN
305 {
306 int i;
307 for (i = 0; i < 6; i++)
308 PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8);
309 }
310#endif
311}
312
John W. Linville274bfb82008-10-29 11:35:05 -0400313static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len,
Zhu Yi9184d932006-01-19 16:22:32 +0800314 u8 * rc4key, int keylen, void *priv)
Jeff Garzikb4538722005-05-12 22:48:20 -0400315{
John W. Linville274bfb82008-10-29 11:35:05 -0400316 struct lib80211_tkip_data *tkey = priv;
Zhu Yi9184d932006-01-19 16:22:32 +0800317 u8 *pos;
John W. Linville274bfb82008-10-29 11:35:05 -0400318 struct ieee80211_hdr *hdr;
Jeff Garzikb4538722005-05-12 22:48:20 -0400319
John W. Linville274bfb82008-10-29 11:35:05 -0400320 hdr = (struct ieee80211_hdr *)skb->data;
James Ketrenos20d64712005-09-21 11:53:43 -0500321
Andriy Tkachuk299af9d2010-02-02 16:33:53 +0200322 if (skb_headroom(skb) < TKIP_HDR_LEN || skb->len < hdr_len)
Zhu Yi9184d932006-01-19 16:22:32 +0800323 return -1;
324
325 if (rc4key == NULL || keylen < 16)
326 return -1;
Jeff Garzikb4538722005-05-12 22:48:20 -0400327
Jeff Garzikb4538722005-05-12 22:48:20 -0400328 if (!tkey->tx_phase1_done) {
329 tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2,
330 tkey->tx_iv32);
331 tkey->tx_phase1_done = 1;
332 }
333 tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16);
334
Andriy Tkachuk299af9d2010-02-02 16:33:53 +0200335 pos = skb_push(skb, TKIP_HDR_LEN);
336 memmove(pos, pos + TKIP_HDR_LEN, hdr_len);
Jeff Garzikb4538722005-05-12 22:48:20 -0400337 pos += hdr_len;
Jeff Garzikb4538722005-05-12 22:48:20 -0400338
James Ketrenos31b59ea2005-09-21 11:58:49 -0500339 *pos++ = *rc4key;
340 *pos++ = *(rc4key + 1);
341 *pos++ = *(rc4key + 2);
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400342 *pos++ = (tkey->key_idx << 6) | (1 << 5) /* Ext IV included */ ;
Jeff Garzikb4538722005-05-12 22:48:20 -0400343 *pos++ = tkey->tx_iv32 & 0xff;
344 *pos++ = (tkey->tx_iv32 >> 8) & 0xff;
345 *pos++ = (tkey->tx_iv32 >> 16) & 0xff;
346 *pos++ = (tkey->tx_iv32 >> 24) & 0xff;
347
Zhu Yi9184d932006-01-19 16:22:32 +0800348 tkey->tx_iv16++;
349 if (tkey->tx_iv16 == 0) {
350 tkey->tx_phase1_done = 0;
351 tkey->tx_iv32++;
352 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400353
Andriy Tkachuk299af9d2010-02-02 16:33:53 +0200354 return TKIP_HDR_LEN;
James Ketrenos31b59ea2005-09-21 11:58:49 -0500355}
356
John W. Linville274bfb82008-10-29 11:35:05 -0400357static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
James Ketrenos31b59ea2005-09-21 11:58:49 -0500358{
John W. Linville274bfb82008-10-29 11:35:05 -0400359 struct lib80211_tkip_data *tkey = priv;
Jeff Garzik28eb1772006-09-22 20:10:23 -0400360 struct blkcipher_desc desc = { .tfm = tkey->tx_tfm_arc4 };
James Ketrenos31b59ea2005-09-21 11:58:49 -0500361 int len;
Zhu Yi9184d932006-01-19 16:22:32 +0800362 u8 rc4key[16], *pos, *icv;
363 u32 crc;
James Ketrenos31b59ea2005-09-21 11:58:49 -0500364 struct scatterlist sg;
365
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000366 if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) {
James Ketrenos31b59ea2005-09-21 11:58:49 -0500367 if (net_ratelimit()) {
John W. Linville274bfb82008-10-29 11:35:05 -0400368 struct ieee80211_hdr *hdr =
369 (struct ieee80211_hdr *)skb->data;
Zhu Yi9184d932006-01-19 16:22:32 +0800370 printk(KERN_DEBUG ": TKIP countermeasures: dropped "
Johannes Berge1749612008-10-27 15:59:26 -0700371 "TX packet to %pM\n", hdr->addr1);
James Ketrenos31b59ea2005-09-21 11:58:49 -0500372 }
373 return -1;
374 }
375
376 if (skb_tailroom(skb) < 4 || skb->len < hdr_len)
377 return -1;
378
379 len = skb->len - hdr_len;
380 pos = skb->data + hdr_len;
381
John W. Linville274bfb82008-10-29 11:35:05 -0400382 if ((lib80211_tkip_hdr(skb, hdr_len, rc4key, 16, priv)) < 0)
James Ketrenos31b59ea2005-09-21 11:58:49 -0500383 return -1;
384
Zhu Yi9184d932006-01-19 16:22:32 +0800385 crc = ~crc32_le(~0, pos, len);
Andriy Tkachukd0833a62010-02-02 15:58:53 +0200386 icv = skb_put(skb, 4);
Zhu Yi9184d932006-01-19 16:22:32 +0800387 icv[0] = crc;
388 icv[1] = crc >> 8;
389 icv[2] = crc >> 16;
390 icv[3] = crc >> 24;
391
Jeff Garzik28eb1772006-09-22 20:10:23 -0400392 crypto_blkcipher_setkey(tkey->tx_tfm_arc4, rc4key, 16);
Jens Axboefa05f122007-10-22 19:44:26 +0200393 sg_init_one(&sg, pos, len + 4);
Herbert Xuf12cc202006-08-22 20:36:13 +1000394 return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4);
Zhu Yib4328d82006-08-21 11:33:09 +0800395}
396
Jeff Garzik18379872006-09-22 21:19:05 -0400397/*
398 * deal with seq counter wrapping correctly.
399 * refer to timer_after() for jiffies wrapping handling
400 */
401static inline int tkip_replay_check(u32 iv32_n, u16 iv16_n,
402 u32 iv32_o, u16 iv16_o)
403{
404 if ((s32)iv32_n - (s32)iv32_o < 0 ||
405 (iv32_n == iv32_o && iv16_n <= iv16_o))
406 return 1;
407 return 0;
408}
409
John W. Linville274bfb82008-10-29 11:35:05 -0400410static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
Jeff Garzikb4538722005-05-12 22:48:20 -0400411{
John W. Linville274bfb82008-10-29 11:35:05 -0400412 struct lib80211_tkip_data *tkey = priv;
Jeff Garzik28eb1772006-09-22 20:10:23 -0400413 struct blkcipher_desc desc = { .tfm = tkey->rx_tfm_arc4 };
Jeff Garzikb4538722005-05-12 22:48:20 -0400414 u8 rc4key[16];
415 u8 keyidx, *pos;
416 u32 iv32;
417 u16 iv16;
John W. Linville274bfb82008-10-29 11:35:05 -0400418 struct ieee80211_hdr *hdr;
Jeff Garzikb4538722005-05-12 22:48:20 -0400419 u8 icv[4];
420 u32 crc;
421 struct scatterlist sg;
422 int plen;
423
John W. Linville274bfb82008-10-29 11:35:05 -0400424 hdr = (struct ieee80211_hdr *)skb->data;
James Ketrenos20d64712005-09-21 11:53:43 -0500425
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000426 if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) {
James Ketrenos20d64712005-09-21 11:53:43 -0500427 if (net_ratelimit()) {
Zhu Yi9184d932006-01-19 16:22:32 +0800428 printk(KERN_DEBUG ": TKIP countermeasures: dropped "
Johannes Berge1749612008-10-27 15:59:26 -0700429 "received packet from %pM\n", hdr->addr2);
James Ketrenos20d64712005-09-21 11:53:43 -0500430 }
431 return -1;
432 }
433
Andriy Tkachuk299af9d2010-02-02 16:33:53 +0200434 if (skb->len < hdr_len + TKIP_HDR_LEN + 4)
Jeff Garzikb4538722005-05-12 22:48:20 -0400435 return -1;
436
Jeff Garzikb4538722005-05-12 22:48:20 -0400437 pos = skb->data + hdr_len;
438 keyidx = pos[3];
439 if (!(keyidx & (1 << 5))) {
440 if (net_ratelimit()) {
441 printk(KERN_DEBUG "TKIP: received packet without ExtIV"
Johannes Berge1749612008-10-27 15:59:26 -0700442 " flag from %pM\n", hdr->addr2);
Jeff Garzikb4538722005-05-12 22:48:20 -0400443 }
444 return -2;
445 }
446 keyidx >>= 6;
447 if (tkey->key_idx != keyidx) {
448 printk(KERN_DEBUG "TKIP: RX tkey->key_idx=%d frame "
449 "keyidx=%d priv=%p\n", tkey->key_idx, keyidx, priv);
450 return -6;
451 }
452 if (!tkey->key_set) {
453 if (net_ratelimit()) {
Johannes Berge1749612008-10-27 15:59:26 -0700454 printk(KERN_DEBUG "TKIP: received packet from %pM"
Jeff Garzikb4538722005-05-12 22:48:20 -0400455 " with keyid=%d that does not have a configured"
Johannes Berge1749612008-10-27 15:59:26 -0700456 " key\n", hdr->addr2, keyidx);
Jeff Garzikb4538722005-05-12 22:48:20 -0400457 }
458 return -3;
459 }
460 iv16 = (pos[0] << 8) | pos[2];
461 iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24);
Andriy Tkachuk299af9d2010-02-02 16:33:53 +0200462 pos += TKIP_HDR_LEN;
Jeff Garzikb4538722005-05-12 22:48:20 -0400463
Zhu Yib4328d82006-08-21 11:33:09 +0800464 if (tkip_replay_check(iv32, iv16, tkey->rx_iv32, tkey->rx_iv16)) {
John W. Linville6f16bf32009-03-11 11:05:25 -0400465#ifdef CONFIG_LIB80211_DEBUG
John W. Linville274bfb82008-10-29 11:35:05 -0400466 if (net_ratelimit()) {
467 printk(KERN_DEBUG "TKIP: replay detected: STA=%pM"
Jeff Garzikb4538722005-05-12 22:48:20 -0400468 " previous TSC %08x%04x received TSC "
Johannes Berge1749612008-10-27 15:59:26 -0700469 "%08x%04x\n", hdr->addr2,
Jeff Garzikb4538722005-05-12 22:48:20 -0400470 tkey->rx_iv32, tkey->rx_iv16, iv32, iv16);
471 }
John W. Linville6f16bf32009-03-11 11:05:25 -0400472#endif
Jeff Garzikb4538722005-05-12 22:48:20 -0400473 tkey->dot11RSNAStatsTKIPReplays++;
474 return -4;
475 }
476
477 if (iv32 != tkey->rx_iv32 || !tkey->rx_phase1_done) {
478 tkip_mixing_phase1(tkey->rx_ttak, tkey->key, hdr->addr2, iv32);
479 tkey->rx_phase1_done = 1;
480 }
481 tkip_mixing_phase2(rc4key, tkey->key, tkey->rx_ttak, iv16);
482
483 plen = skb->len - hdr_len - 12;
484
Jeff Garzik28eb1772006-09-22 20:10:23 -0400485 crypto_blkcipher_setkey(tkey->rx_tfm_arc4, rc4key, 16);
Jens Axboefa05f122007-10-22 19:44:26 +0200486 sg_init_one(&sg, pos, plen + 4);
Herbert Xuf12cc202006-08-22 20:36:13 +1000487 if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4)) {
488 if (net_ratelimit()) {
489 printk(KERN_DEBUG ": TKIP: failed to decrypt "
Johannes Berge1749612008-10-27 15:59:26 -0700490 "received packet from %pM\n",
491 hdr->addr2);
Herbert Xuf12cc202006-08-22 20:36:13 +1000492 }
493 return -7;
494 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400495
496 crc = ~crc32_le(~0, pos, plen);
497 icv[0] = crc;
498 icv[1] = crc >> 8;
499 icv[2] = crc >> 16;
500 icv[3] = crc >> 24;
501 if (memcmp(icv, pos + plen, 4) != 0) {
502 if (iv32 != tkey->rx_iv32) {
503 /* Previously cached Phase1 result was already lost, so
504 * it needs to be recalculated for the next packet. */
505 tkey->rx_phase1_done = 0;
506 }
John W. Linville6f16bf32009-03-11 11:05:25 -0400507#ifdef CONFIG_LIB80211_DEBUG
John W. Linville274bfb82008-10-29 11:35:05 -0400508 if (net_ratelimit()) {
509 printk(KERN_DEBUG "TKIP: ICV error detected: STA="
Johannes Berge1749612008-10-27 15:59:26 -0700510 "%pM\n", hdr->addr2);
Jeff Garzikb4538722005-05-12 22:48:20 -0400511 }
John W. Linville6f16bf32009-03-11 11:05:25 -0400512#endif
Jeff Garzikb4538722005-05-12 22:48:20 -0400513 tkey->dot11RSNAStatsTKIPICVErrors++;
514 return -5;
515 }
516
517 /* Update real counters only after Michael MIC verification has
518 * completed */
519 tkey->rx_iv32_new = iv32;
520 tkey->rx_iv16_new = iv16;
521
522 /* Remove IV and ICV */
Andriy Tkachuk299af9d2010-02-02 16:33:53 +0200523 memmove(skb->data + TKIP_HDR_LEN, skb->data, hdr_len);
524 skb_pull(skb, TKIP_HDR_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400525 skb_trim(skb, skb->len - 4);
526
527 return keyidx;
528}
529
Jeff Garzik28eb1772006-09-22 20:10:23 -0400530static int michael_mic(struct crypto_hash *tfm_michael, u8 * key, u8 * hdr,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400531 u8 * data, size_t data_len, u8 * mic)
Jeff Garzikb4538722005-05-12 22:48:20 -0400532{
Herbert Xu35058682006-08-24 19:10:20 +1000533 struct hash_desc desc;
Jeff Garzikb4538722005-05-12 22:48:20 -0400534 struct scatterlist sg[2];
535
Zhu Yi5a656942006-08-21 11:33:56 +0800536 if (tfm_michael == NULL) {
Joe Perchese9c02682010-11-16 19:56:49 -0800537 pr_warn("%s(): tfm_michael == NULL\n", __func__);
Jeff Garzikb4538722005-05-12 22:48:20 -0400538 return -1;
539 }
Jens Axboefa05f122007-10-22 19:44:26 +0200540 sg_init_table(sg, 2);
Jens Axboe642f1492007-10-24 11:20:47 +0200541 sg_set_buf(&sg[0], hdr, 16);
542 sg_set_buf(&sg[1], data, data_len);
Jeff Garzikb4538722005-05-12 22:48:20 -0400543
Jeff Garzik28eb1772006-09-22 20:10:23 -0400544 if (crypto_hash_setkey(tfm_michael, key, 8))
Herbert Xu35058682006-08-24 19:10:20 +1000545 return -1;
Jeff Garzikb4538722005-05-12 22:48:20 -0400546
Jeff Garzik28eb1772006-09-22 20:10:23 -0400547 desc.tfm = tfm_michael;
Herbert Xu35058682006-08-24 19:10:20 +1000548 desc.flags = 0;
549 return crypto_hash_digest(&desc, sg, data_len + 16, mic);
Jeff Garzikb4538722005-05-12 22:48:20 -0400550}
551
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400552static void michael_mic_hdr(struct sk_buff *skb, u8 * hdr)
Jeff Garzikb4538722005-05-12 22:48:20 -0400553{
John W. Linville274bfb82008-10-29 11:35:05 -0400554 struct ieee80211_hdr *hdr11;
Jeff Garzikb4538722005-05-12 22:48:20 -0400555
John W. Linville274bfb82008-10-29 11:35:05 -0400556 hdr11 = (struct ieee80211_hdr *)skb->data;
Zhu Yiea284152006-04-13 17:17:06 +0800557
John W. Linville274bfb82008-10-29 11:35:05 -0400558 switch (le16_to_cpu(hdr11->frame_control) &
Jeff Garzikb4538722005-05-12 22:48:20 -0400559 (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
560 case IEEE80211_FCTL_TODS:
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400561 memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
562 memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
Jeff Garzikb4538722005-05-12 22:48:20 -0400563 break;
564 case IEEE80211_FCTL_FROMDS:
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400565 memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
566 memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */
Jeff Garzikb4538722005-05-12 22:48:20 -0400567 break;
568 case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS:
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400569 memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
570 memcpy(hdr + ETH_ALEN, hdr11->addr4, ETH_ALEN); /* SA */
Jeff Garzikb4538722005-05-12 22:48:20 -0400571 break;
572 case 0:
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400573 memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
574 memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
Jeff Garzikb4538722005-05-12 22:48:20 -0400575 break;
576 }
577
John W. Linville274bfb82008-10-29 11:35:05 -0400578 if (ieee80211_is_data_qos(hdr11->frame_control)) {
John W. Linville3f6ff6b2010-07-20 12:09:11 -0400579 hdr[12] = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(hdr11)))
John W. Linville274bfb82008-10-29 11:35:05 -0400580 & IEEE80211_QOS_CTL_TID_MASK;
Zhu Yiea284152006-04-13 17:17:06 +0800581 } else
582 hdr[12] = 0; /* priority */
583
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400584 hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */
Jeff Garzikb4538722005-05-12 22:48:20 -0400585}
586
John W. Linville274bfb82008-10-29 11:35:05 -0400587static int lib80211_michael_mic_add(struct sk_buff *skb, int hdr_len,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400588 void *priv)
Jeff Garzikb4538722005-05-12 22:48:20 -0400589{
John W. Linville274bfb82008-10-29 11:35:05 -0400590 struct lib80211_tkip_data *tkey = priv;
Jeff Garzikb4538722005-05-12 22:48:20 -0400591 u8 *pos;
592
593 if (skb_tailroom(skb) < 8 || skb->len < hdr_len) {
594 printk(KERN_DEBUG "Invalid packet for Michael MIC add "
595 "(tailroom=%d hdr_len=%d skb->len=%d)\n",
596 skb_tailroom(skb), hdr_len, skb->len);
597 return -1;
598 }
599
600 michael_mic_hdr(skb, tkey->tx_hdr);
601 pos = skb_put(skb, 8);
Zhu Yi5a656942006-08-21 11:33:56 +0800602 if (michael_mic(tkey->tx_tfm_michael, &tkey->key[16], tkey->tx_hdr,
Jeff Garzikb4538722005-05-12 22:48:20 -0400603 skb->data + hdr_len, skb->len - 8 - hdr_len, pos))
604 return -1;
605
606 return 0;
607}
608
John W. Linville274bfb82008-10-29 11:35:05 -0400609static void lib80211_michael_mic_failure(struct net_device *dev,
610 struct ieee80211_hdr *hdr,
James Ketrenosee34af32005-09-21 11:54:36 -0500611 int keyidx)
Jeff Garzikb4538722005-05-12 22:48:20 -0400612{
613 union iwreq_data wrqu;
614 struct iw_michaelmicfailure ev;
615
616 /* TODO: needed parameters: count, keyid, key type, TSC */
617 memset(&ev, 0, sizeof(ev));
618 ev.flags = keyidx & IW_MICFAILURE_KEY_ID;
619 if (hdr->addr1[0] & 0x01)
620 ev.flags |= IW_MICFAILURE_GROUP;
621 else
622 ev.flags |= IW_MICFAILURE_PAIRWISE;
623 ev.src_addr.sa_family = ARPHRD_ETHER;
624 memcpy(ev.src_addr.sa_data, hdr->addr2, ETH_ALEN);
625 memset(&wrqu, 0, sizeof(wrqu));
626 wrqu.data.length = sizeof(ev);
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400627 wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, (char *)&ev);
Jeff Garzikb4538722005-05-12 22:48:20 -0400628}
Jeff Garzikb4538722005-05-12 22:48:20 -0400629
John W. Linville274bfb82008-10-29 11:35:05 -0400630static int lib80211_michael_mic_verify(struct sk_buff *skb, int keyidx,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400631 int hdr_len, void *priv)
Jeff Garzikb4538722005-05-12 22:48:20 -0400632{
John W. Linville274bfb82008-10-29 11:35:05 -0400633 struct lib80211_tkip_data *tkey = priv;
Jeff Garzikb4538722005-05-12 22:48:20 -0400634 u8 mic[8];
635
636 if (!tkey->key_set)
637 return -1;
638
639 michael_mic_hdr(skb, tkey->rx_hdr);
Zhu Yi5a656942006-08-21 11:33:56 +0800640 if (michael_mic(tkey->rx_tfm_michael, &tkey->key[24], tkey->rx_hdr,
Jeff Garzikb4538722005-05-12 22:48:20 -0400641 skb->data + hdr_len, skb->len - 8 - hdr_len, mic))
642 return -1;
643 if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) {
John W. Linville274bfb82008-10-29 11:35:05 -0400644 struct ieee80211_hdr *hdr;
645 hdr = (struct ieee80211_hdr *)skb->data;
Jeff Garzikb4538722005-05-12 22:48:20 -0400646 printk(KERN_DEBUG "%s: Michael MIC verification failed for "
Johannes Berge1749612008-10-27 15:59:26 -0700647 "MSDU from %pM keyidx=%d\n",
648 skb->dev ? skb->dev->name : "N/A", hdr->addr2,
Jeff Garzikb4538722005-05-12 22:48:20 -0400649 keyidx);
650 if (skb->dev)
John W. Linville274bfb82008-10-29 11:35:05 -0400651 lib80211_michael_mic_failure(skb->dev, hdr, keyidx);
Jeff Garzikb4538722005-05-12 22:48:20 -0400652 tkey->dot11RSNAStatsTKIPLocalMICFailures++;
653 return -1;
654 }
655
656 /* Update TSC counters for RX now that the packet verification has
657 * completed. */
658 tkey->rx_iv32 = tkey->rx_iv32_new;
659 tkey->rx_iv16 = tkey->rx_iv16_new;
660
661 skb_trim(skb, skb->len - 8);
662
663 return 0;
664}
665
John W. Linville274bfb82008-10-29 11:35:05 -0400666static int lib80211_tkip_set_key(void *key, int len, u8 * seq, void *priv)
Jeff Garzikb4538722005-05-12 22:48:20 -0400667{
John W. Linville274bfb82008-10-29 11:35:05 -0400668 struct lib80211_tkip_data *tkey = priv;
Jeff Garzikb4538722005-05-12 22:48:20 -0400669 int keyidx;
Jeff Garzik28eb1772006-09-22 20:10:23 -0400670 struct crypto_hash *tfm = tkey->tx_tfm_michael;
671 struct crypto_blkcipher *tfm2 = tkey->tx_tfm_arc4;
672 struct crypto_hash *tfm3 = tkey->rx_tfm_michael;
673 struct crypto_blkcipher *tfm4 = tkey->rx_tfm_arc4;
Jeff Garzikb4538722005-05-12 22:48:20 -0400674
675 keyidx = tkey->key_idx;
676 memset(tkey, 0, sizeof(*tkey));
677 tkey->key_idx = keyidx;
Zhu Yi5a656942006-08-21 11:33:56 +0800678 tkey->tx_tfm_michael = tfm;
679 tkey->tx_tfm_arc4 = tfm2;
680 tkey->rx_tfm_michael = tfm3;
681 tkey->rx_tfm_arc4 = tfm4;
Jeff Garzikb4538722005-05-12 22:48:20 -0400682 if (len == TKIP_KEY_LEN) {
683 memcpy(tkey->key, key, TKIP_KEY_LEN);
684 tkey->key_set = 1;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400685 tkey->tx_iv16 = 1; /* TSC is initialized to 1 */
Jeff Garzikb4538722005-05-12 22:48:20 -0400686 if (seq) {
687 tkey->rx_iv32 = (seq[5] << 24) | (seq[4] << 16) |
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400688 (seq[3] << 8) | seq[2];
Jeff Garzikb4538722005-05-12 22:48:20 -0400689 tkey->rx_iv16 = (seq[1] << 8) | seq[0];
690 }
691 } else if (len == 0)
692 tkey->key_set = 0;
693 else
694 return -1;
695
696 return 0;
697}
698
John W. Linville274bfb82008-10-29 11:35:05 -0400699static int lib80211_tkip_get_key(void *key, int len, u8 * seq, void *priv)
Jeff Garzikb4538722005-05-12 22:48:20 -0400700{
John W. Linville274bfb82008-10-29 11:35:05 -0400701 struct lib80211_tkip_data *tkey = priv;
Jeff Garzikb4538722005-05-12 22:48:20 -0400702
703 if (len < TKIP_KEY_LEN)
704 return -1;
705
706 if (!tkey->key_set)
707 return 0;
708 memcpy(key, tkey->key, TKIP_KEY_LEN);
709
710 if (seq) {
711 /* Return the sequence number of the last transmitted frame. */
712 u16 iv16 = tkey->tx_iv16;
713 u32 iv32 = tkey->tx_iv32;
714 if (iv16 == 0)
715 iv32--;
716 iv16--;
717 seq[0] = tkey->tx_iv16;
718 seq[1] = tkey->tx_iv16 >> 8;
719 seq[2] = tkey->tx_iv32;
720 seq[3] = tkey->tx_iv32 >> 8;
721 seq[4] = tkey->tx_iv32 >> 16;
722 seq[5] = tkey->tx_iv32 >> 24;
723 }
724
725 return TKIP_KEY_LEN;
726}
727
John W. Linville274bfb82008-10-29 11:35:05 -0400728static char *lib80211_tkip_print_stats(char *p, void *priv)
Jeff Garzikb4538722005-05-12 22:48:20 -0400729{
John W. Linville274bfb82008-10-29 11:35:05 -0400730 struct lib80211_tkip_data *tkip = priv;
Jeff Garzikb4538722005-05-12 22:48:20 -0400731 p += sprintf(p, "key[%d] alg=TKIP key_set=%d "
732 "tx_pn=%02x%02x%02x%02x%02x%02x "
733 "rx_pn=%02x%02x%02x%02x%02x%02x "
734 "replays=%d icv_errors=%d local_mic_failures=%d\n",
735 tkip->key_idx, tkip->key_set,
736 (tkip->tx_iv32 >> 24) & 0xff,
737 (tkip->tx_iv32 >> 16) & 0xff,
738 (tkip->tx_iv32 >> 8) & 0xff,
739 tkip->tx_iv32 & 0xff,
740 (tkip->tx_iv16 >> 8) & 0xff,
741 tkip->tx_iv16 & 0xff,
742 (tkip->rx_iv32 >> 24) & 0xff,
743 (tkip->rx_iv32 >> 16) & 0xff,
744 (tkip->rx_iv32 >> 8) & 0xff,
745 tkip->rx_iv32 & 0xff,
746 (tkip->rx_iv16 >> 8) & 0xff,
747 tkip->rx_iv16 & 0xff,
748 tkip->dot11RSNAStatsTKIPReplays,
749 tkip->dot11RSNAStatsTKIPICVErrors,
750 tkip->dot11RSNAStatsTKIPLocalMICFailures);
751 return p;
752}
753
John W. Linville274bfb82008-10-29 11:35:05 -0400754static struct lib80211_crypto_ops lib80211_crypt_tkip = {
James Ketrenos74079fd2005-09-13 17:35:21 -0500755 .name = "TKIP",
John W. Linville274bfb82008-10-29 11:35:05 -0400756 .init = lib80211_tkip_init,
757 .deinit = lib80211_tkip_deinit,
John W. Linville274bfb82008-10-29 11:35:05 -0400758 .encrypt_mpdu = lib80211_tkip_encrypt,
759 .decrypt_mpdu = lib80211_tkip_decrypt,
760 .encrypt_msdu = lib80211_michael_mic_add,
761 .decrypt_msdu = lib80211_michael_mic_verify,
762 .set_key = lib80211_tkip_set_key,
763 .get_key = lib80211_tkip_get_key,
764 .print_stats = lib80211_tkip_print_stats,
James Ketrenos1264fc02005-09-21 11:54:53 -0500765 .extra_mpdu_prefix_len = 4 + 4, /* IV + ExtIV */
766 .extra_mpdu_postfix_len = 4, /* ICV */
767 .extra_msdu_postfix_len = 8, /* MIC */
John W. Linville274bfb82008-10-29 11:35:05 -0400768 .get_flags = lib80211_tkip_get_flags,
769 .set_flags = lib80211_tkip_set_flags,
James Ketrenos74079fd2005-09-13 17:35:21 -0500770 .owner = THIS_MODULE,
Jeff Garzikb4538722005-05-12 22:48:20 -0400771};
772
John W. Linville274bfb82008-10-29 11:35:05 -0400773static int __init lib80211_crypto_tkip_init(void)
Jeff Garzikb4538722005-05-12 22:48:20 -0400774{
John W. Linville274bfb82008-10-29 11:35:05 -0400775 return lib80211_register_crypto_ops(&lib80211_crypt_tkip);
Jeff Garzikb4538722005-05-12 22:48:20 -0400776}
777
John W. Linville274bfb82008-10-29 11:35:05 -0400778static void __exit lib80211_crypto_tkip_exit(void)
Jeff Garzikb4538722005-05-12 22:48:20 -0400779{
John W. Linville274bfb82008-10-29 11:35:05 -0400780 lib80211_unregister_crypto_ops(&lib80211_crypt_tkip);
Jeff Garzikb4538722005-05-12 22:48:20 -0400781}
782
John W. Linville274bfb82008-10-29 11:35:05 -0400783module_init(lib80211_crypto_tkip_init);
784module_exit(lib80211_crypto_tkip_exit);