blob: 0fe40510e2cb38a284d8ae50f6a0b42a9b458809 [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
Herbert Xuf12cc202006-08-22 20:36:13 +100013#include <linux/err.h>
Jeff Garzikb4538722005-05-12 22:48:20 -040014#include <linux/module.h>
15#include <linux/init.h>
16#include <linux/slab.h>
17#include <linux/random.h>
Ralf Baechle11763602007-10-23 20:42:11 +020018#include <linux/scatterlist.h>
Jeff Garzikb4538722005-05-12 22:48:20 -040019#include <linux/skbuff.h>
20#include <linux/netdevice.h>
Al Virod7fe0f22006-12-03 23:15:30 -050021#include <linux/mm.h>
Jeff Garzikb4538722005-05-12 22:48:20 -040022#include <linux/if_ether.h>
23#include <linux/if_arp.h>
24#include <asm/string.h>
25
John W. Linville274bfb82008-10-29 11:35:05 -040026#include <linux/wireless.h>
27#include <linux/ieee80211.h>
28#include <net/iw_handler.h>
Jeff Garzikb4538722005-05-12 22:48:20 -040029
Jeff Garzikb4538722005-05-12 22:48:20 -040030#include <linux/crypto.h>
Jeff Garzikb4538722005-05-12 22:48:20 -040031#include <linux/crc32.h>
32
John W. Linville274bfb82008-10-29 11:35:05 -040033#include <net/lib80211.h>
34
Jeff Garzikb4538722005-05-12 22:48:20 -040035MODULE_AUTHOR("Jouni Malinen");
John W. Linville274bfb82008-10-29 11:35:05 -040036MODULE_DESCRIPTION("lib80211 crypt: TKIP");
Jeff Garzikb4538722005-05-12 22:48:20 -040037MODULE_LICENSE("GPL");
38
Andriy Tkachuk299af9d2010-02-02 16:33:53 +020039#define TKIP_HDR_LEN 8
40
John W. Linville274bfb82008-10-29 11:35:05 -040041struct lib80211_tkip_data {
Jeff Garzikb4538722005-05-12 22:48:20 -040042#define TKIP_KEY_LEN 32
43 u8 key[TKIP_KEY_LEN];
44 int key_set;
45
46 u32 tx_iv32;
47 u16 tx_iv16;
48 u16 tx_ttak[5];
49 int tx_phase1_done;
50
51 u32 rx_iv32;
52 u16 rx_iv16;
53 u16 rx_ttak[5];
54 int rx_phase1_done;
55 u32 rx_iv32_new;
56 u16 rx_iv16_new;
57
58 u32 dot11RSNAStatsTKIPReplays;
59 u32 dot11RSNAStatsTKIPICVErrors;
60 u32 dot11RSNAStatsTKIPLocalMICFailures;
61
62 int key_idx;
63
Jeff Garzik28eb1772006-09-22 20:10:23 -040064 struct crypto_blkcipher *rx_tfm_arc4;
65 struct crypto_hash *rx_tfm_michael;
66 struct crypto_blkcipher *tx_tfm_arc4;
67 struct crypto_hash *tx_tfm_michael;
Jeff Garzikb4538722005-05-12 22:48:20 -040068
69 /* scratch buffers for virt_to_page() (crypto API) */
70 u8 rx_hdr[16], tx_hdr[16];
James Ketrenos20d64712005-09-21 11:53:43 -050071
James Ketrenos6eb6edf2005-09-22 10:34:15 +000072 unsigned long flags;
Jeff Garzikb4538722005-05-12 22:48:20 -040073};
74
John W. Linville274bfb82008-10-29 11:35:05 -040075static unsigned long lib80211_tkip_set_flags(unsigned long flags, void *priv)
James Ketrenos6eb6edf2005-09-22 10:34:15 +000076{
John W. Linville274bfb82008-10-29 11:35:05 -040077 struct lib80211_tkip_data *_priv = priv;
James Ketrenos6eb6edf2005-09-22 10:34:15 +000078 unsigned long old_flags = _priv->flags;
79 _priv->flags = flags;
80 return old_flags;
81}
82
John W. Linville274bfb82008-10-29 11:35:05 -040083static unsigned long lib80211_tkip_get_flags(void *priv)
James Ketrenos6eb6edf2005-09-22 10:34:15 +000084{
John W. Linville274bfb82008-10-29 11:35:05 -040085 struct lib80211_tkip_data *_priv = priv;
James Ketrenos6eb6edf2005-09-22 10:34:15 +000086 return _priv->flags;
87}
88
John W. Linville274bfb82008-10-29 11:35:05 -040089static void *lib80211_tkip_init(int key_idx)
Jeff Garzikb4538722005-05-12 22:48:20 -040090{
John W. Linville274bfb82008-10-29 11:35:05 -040091 struct lib80211_tkip_data *priv;
Jeff Garzikb4538722005-05-12 22:48:20 -040092
Zhu Yi8aa914b2006-01-19 16:22:07 +080093 priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
Jeff Garzikb4538722005-05-12 22:48:20 -040094 if (priv == NULL)
95 goto fail;
James Ketrenos20d64712005-09-21 11:53:43 -050096
Jeff Garzikb4538722005-05-12 22:48:20 -040097 priv->key_idx = key_idx;
98
Jeff Garzik28eb1772006-09-22 20:10:23 -040099 priv->tx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0,
Herbert Xuf12cc202006-08-22 20:36:13 +1000100 CRYPTO_ALG_ASYNC);
Jeff Garzik28eb1772006-09-22 20:10:23 -0400101 if (IS_ERR(priv->tx_tfm_arc4)) {
John W. Linville274bfb82008-10-29 11:35:05 -0400102 printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate "
Jeff Garzikb4538722005-05-12 22:48:20 -0400103 "crypto API arc4\n");
Jeff Garzik18379872006-09-22 21:19:05 -0400104 priv->tx_tfm_arc4 = NULL;
Jeff Garzikb4538722005-05-12 22:48:20 -0400105 goto fail;
106 }
107
Jeff Garzik28eb1772006-09-22 20:10:23 -0400108 priv->tx_tfm_michael = crypto_alloc_hash("michael_mic", 0,
109 CRYPTO_ALG_ASYNC);
110 if (IS_ERR(priv->tx_tfm_michael)) {
John W. Linville274bfb82008-10-29 11:35:05 -0400111 printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate "
Zhu Yi5a656942006-08-21 11:33:56 +0800112 "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)) {
John W. Linville274bfb82008-10-29 11:35:05 -0400120 printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate "
Zhu Yi5a656942006-08-21 11:33:56 +0800121 "crypto API arc4\n");
Jeff Garzik18379872006-09-22 21:19:05 -0400122 priv->rx_tfm_arc4 = NULL;
Zhu Yi5a656942006-08-21 11:33:56 +0800123 goto fail;
124 }
125
Jeff Garzik28eb1772006-09-22 20:10:23 -0400126 priv->rx_tfm_michael = crypto_alloc_hash("michael_mic", 0,
127 CRYPTO_ALG_ASYNC);
128 if (IS_ERR(priv->rx_tfm_michael)) {
John W. Linville274bfb82008-10-29 11:35:05 -0400129 printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate "
Jeff Garzikb4538722005-05-12 22:48:20 -0400130 "crypto API michael_mic\n");
Jeff Garzik18379872006-09-22 21:19:05 -0400131 priv->rx_tfm_michael = NULL;
Jeff Garzikb4538722005-05-12 22:48:20 -0400132 goto fail;
133 }
134
135 return priv;
136
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400137 fail:
Jeff Garzikb4538722005-05-12 22:48:20 -0400138 if (priv) {
Zhu Yi5a656942006-08-21 11:33:56 +0800139 if (priv->tx_tfm_michael)
Jeff Garzik28eb1772006-09-22 20:10:23 -0400140 crypto_free_hash(priv->tx_tfm_michael);
Zhu Yi5a656942006-08-21 11:33:56 +0800141 if (priv->tx_tfm_arc4)
Jeff Garzik28eb1772006-09-22 20:10:23 -0400142 crypto_free_blkcipher(priv->tx_tfm_arc4);
Zhu Yi5a656942006-08-21 11:33:56 +0800143 if (priv->rx_tfm_michael)
Jeff Garzik28eb1772006-09-22 20:10:23 -0400144 crypto_free_hash(priv->rx_tfm_michael);
Zhu Yi5a656942006-08-21 11:33:56 +0800145 if (priv->rx_tfm_arc4)
Jeff Garzik28eb1772006-09-22 20:10:23 -0400146 crypto_free_blkcipher(priv->rx_tfm_arc4);
Jeff Garzikb4538722005-05-12 22:48:20 -0400147 kfree(priv);
148 }
149
150 return NULL;
151}
152
John W. Linville274bfb82008-10-29 11:35:05 -0400153static void lib80211_tkip_deinit(void *priv)
Jeff Garzikb4538722005-05-12 22:48:20 -0400154{
John W. Linville274bfb82008-10-29 11:35:05 -0400155 struct lib80211_tkip_data *_priv = priv;
Zhu Yi5a656942006-08-21 11:33:56 +0800156 if (_priv) {
157 if (_priv->tx_tfm_michael)
Jeff Garzik28eb1772006-09-22 20:10:23 -0400158 crypto_free_hash(_priv->tx_tfm_michael);
Zhu Yi5a656942006-08-21 11:33:56 +0800159 if (_priv->tx_tfm_arc4)
Jeff Garzik28eb1772006-09-22 20:10:23 -0400160 crypto_free_blkcipher(_priv->tx_tfm_arc4);
Zhu Yi5a656942006-08-21 11:33:56 +0800161 if (_priv->rx_tfm_michael)
Jeff Garzik28eb1772006-09-22 20:10:23 -0400162 crypto_free_hash(_priv->rx_tfm_michael);
Zhu Yi5a656942006-08-21 11:33:56 +0800163 if (_priv->rx_tfm_arc4)
Jeff Garzik28eb1772006-09-22 20:10:23 -0400164 crypto_free_blkcipher(_priv->rx_tfm_arc4);
Zhu Yi5a656942006-08-21 11:33:56 +0800165 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400166 kfree(priv);
167}
168
Jeff Garzikb4538722005-05-12 22:48:20 -0400169static inline u16 RotR1(u16 val)
170{
171 return (val >> 1) | (val << 15);
172}
173
Jeff Garzikb4538722005-05-12 22:48:20 -0400174static inline u8 Lo8(u16 val)
175{
176 return val & 0xff;
177}
178
Jeff Garzikb4538722005-05-12 22:48:20 -0400179static inline u8 Hi8(u16 val)
180{
181 return val >> 8;
182}
183
Jeff Garzikb4538722005-05-12 22:48:20 -0400184static inline u16 Lo16(u32 val)
185{
186 return val & 0xffff;
187}
188
Jeff Garzikb4538722005-05-12 22:48:20 -0400189static inline u16 Hi16(u32 val)
190{
191 return val >> 16;
192}
193
Jeff Garzikb4538722005-05-12 22:48:20 -0400194static inline u16 Mk16(u8 hi, u8 lo)
195{
196 return lo | (((u16) hi) << 8);
197}
198
Al Virod9e94d52007-12-29 05:01:07 -0500199static inline u16 Mk16_le(__le16 * v)
Jeff Garzikb4538722005-05-12 22:48:20 -0400200{
201 return le16_to_cpu(*v);
202}
203
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400204static const u16 Sbox[256] = {
Jeff Garzikb4538722005-05-12 22:48:20 -0400205 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
206 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
207 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
208 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
209 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
210 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
211 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
212 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
213 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
214 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
215 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
216 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
217 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
218 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
219 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
220 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
221 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
222 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
223 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
224 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
225 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
226 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
227 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
228 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
229 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
230 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
231 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
232 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
233 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
234 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
235 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
236 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
237};
238
Jeff Garzikb4538722005-05-12 22:48:20 -0400239static inline u16 _S_(u16 v)
240{
241 u16 t = Sbox[Hi8(v)];
242 return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8));
243}
244
Jeff Garzikb4538722005-05-12 22:48:20 -0400245#define PHASE1_LOOP_COUNT 8
246
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400247static void tkip_mixing_phase1(u16 * TTAK, const u8 * TK, const u8 * TA,
248 u32 IV32)
Jeff Garzikb4538722005-05-12 22:48:20 -0400249{
250 int i, j;
251
252 /* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */
253 TTAK[0] = Lo16(IV32);
254 TTAK[1] = Hi16(IV32);
255 TTAK[2] = Mk16(TA[1], TA[0]);
256 TTAK[3] = Mk16(TA[3], TA[2]);
257 TTAK[4] = Mk16(TA[5], TA[4]);
258
259 for (i = 0; i < PHASE1_LOOP_COUNT; i++) {
260 j = 2 * (i & 1);
261 TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j]));
262 TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j]));
263 TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j]));
264 TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j]));
265 TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i;
266 }
267}
268
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400269static void tkip_mixing_phase2(u8 * WEPSeed, const u8 * TK, const u16 * TTAK,
Jeff Garzikb4538722005-05-12 22:48:20 -0400270 u16 IV16)
271{
272 /* Make temporary area overlap WEP seed so that the final copy can be
273 * avoided on little endian hosts. */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400274 u16 *PPK = (u16 *) & WEPSeed[4];
Jeff Garzikb4538722005-05-12 22:48:20 -0400275
276 /* Step 1 - make copy of TTAK and bring in TSC */
277 PPK[0] = TTAK[0];
278 PPK[1] = TTAK[1];
279 PPK[2] = TTAK[2];
280 PPK[3] = TTAK[3];
281 PPK[4] = TTAK[4];
282 PPK[5] = TTAK[4] + IV16;
283
284 /* Step 2 - 96-bit bijective mixing using S-box */
Al Virod9e94d52007-12-29 05:01:07 -0500285 PPK[0] += _S_(PPK[5] ^ Mk16_le((__le16 *) & TK[0]));
286 PPK[1] += _S_(PPK[0] ^ Mk16_le((__le16 *) & TK[2]));
287 PPK[2] += _S_(PPK[1] ^ Mk16_le((__le16 *) & TK[4]));
288 PPK[3] += _S_(PPK[2] ^ Mk16_le((__le16 *) & TK[6]));
289 PPK[4] += _S_(PPK[3] ^ Mk16_le((__le16 *) & TK[8]));
290 PPK[5] += _S_(PPK[4] ^ Mk16_le((__le16 *) & TK[10]));
Jeff Garzikb4538722005-05-12 22:48:20 -0400291
Al Virod9e94d52007-12-29 05:01:07 -0500292 PPK[0] += RotR1(PPK[5] ^ Mk16_le((__le16 *) & TK[12]));
293 PPK[1] += RotR1(PPK[0] ^ Mk16_le((__le16 *) & TK[14]));
Jeff Garzikb4538722005-05-12 22:48:20 -0400294 PPK[2] += RotR1(PPK[1]);
295 PPK[3] += RotR1(PPK[2]);
296 PPK[4] += RotR1(PPK[3]);
297 PPK[5] += RotR1(PPK[4]);
298
299 /* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value
300 * WEPSeed[0..2] is transmitted as WEP IV */
301 WEPSeed[0] = Hi8(IV16);
302 WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F;
303 WEPSeed[2] = Lo8(IV16);
Al Virod9e94d52007-12-29 05:01:07 -0500304 WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((__le16 *) & TK[0])) >> 1);
Jeff Garzikb4538722005-05-12 22:48:20 -0400305
306#ifdef __BIG_ENDIAN
307 {
308 int i;
309 for (i = 0; i < 6; i++)
310 PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8);
311 }
312#endif
313}
314
John W. Linville274bfb82008-10-29 11:35:05 -0400315static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len,
Zhu Yi9184d932006-01-19 16:22:32 +0800316 u8 * rc4key, int keylen, void *priv)
Jeff Garzikb4538722005-05-12 22:48:20 -0400317{
John W. Linville274bfb82008-10-29 11:35:05 -0400318 struct lib80211_tkip_data *tkey = priv;
Zhu Yi9184d932006-01-19 16:22:32 +0800319 u8 *pos;
John W. Linville274bfb82008-10-29 11:35:05 -0400320 struct ieee80211_hdr *hdr;
Jeff Garzikb4538722005-05-12 22:48:20 -0400321
John W. Linville274bfb82008-10-29 11:35:05 -0400322 hdr = (struct ieee80211_hdr *)skb->data;
James Ketrenos20d64712005-09-21 11:53:43 -0500323
Andriy Tkachuk299af9d2010-02-02 16:33:53 +0200324 if (skb_headroom(skb) < TKIP_HDR_LEN || skb->len < hdr_len)
Zhu Yi9184d932006-01-19 16:22:32 +0800325 return -1;
326
327 if (rc4key == NULL || keylen < 16)
328 return -1;
Jeff Garzikb4538722005-05-12 22:48:20 -0400329
Jeff Garzikb4538722005-05-12 22:48:20 -0400330 if (!tkey->tx_phase1_done) {
331 tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2,
332 tkey->tx_iv32);
333 tkey->tx_phase1_done = 1;
334 }
335 tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16);
336
Andriy Tkachuk299af9d2010-02-02 16:33:53 +0200337 pos = skb_push(skb, TKIP_HDR_LEN);
338 memmove(pos, pos + TKIP_HDR_LEN, hdr_len);
Jeff Garzikb4538722005-05-12 22:48:20 -0400339 pos += hdr_len;
Jeff Garzikb4538722005-05-12 22:48:20 -0400340
James Ketrenos31b59ea2005-09-21 11:58:49 -0500341 *pos++ = *rc4key;
342 *pos++ = *(rc4key + 1);
343 *pos++ = *(rc4key + 2);
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400344 *pos++ = (tkey->key_idx << 6) | (1 << 5) /* Ext IV included */ ;
Jeff Garzikb4538722005-05-12 22:48:20 -0400345 *pos++ = tkey->tx_iv32 & 0xff;
346 *pos++ = (tkey->tx_iv32 >> 8) & 0xff;
347 *pos++ = (tkey->tx_iv32 >> 16) & 0xff;
348 *pos++ = (tkey->tx_iv32 >> 24) & 0xff;
349
Zhu Yi9184d932006-01-19 16:22:32 +0800350 tkey->tx_iv16++;
351 if (tkey->tx_iv16 == 0) {
352 tkey->tx_phase1_done = 0;
353 tkey->tx_iv32++;
354 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400355
Andriy Tkachuk299af9d2010-02-02 16:33:53 +0200356 return TKIP_HDR_LEN;
James Ketrenos31b59ea2005-09-21 11:58:49 -0500357}
358
John W. Linville274bfb82008-10-29 11:35:05 -0400359static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
James Ketrenos31b59ea2005-09-21 11:58:49 -0500360{
John W. Linville274bfb82008-10-29 11:35:05 -0400361 struct lib80211_tkip_data *tkey = priv;
Jeff Garzik28eb1772006-09-22 20:10:23 -0400362 struct blkcipher_desc desc = { .tfm = tkey->tx_tfm_arc4 };
James Ketrenos31b59ea2005-09-21 11:58:49 -0500363 int len;
Zhu Yi9184d932006-01-19 16:22:32 +0800364 u8 rc4key[16], *pos, *icv;
365 u32 crc;
James Ketrenos31b59ea2005-09-21 11:58:49 -0500366 struct scatterlist sg;
367
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000368 if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) {
James Ketrenos31b59ea2005-09-21 11:58:49 -0500369 if (net_ratelimit()) {
John W. Linville274bfb82008-10-29 11:35:05 -0400370 struct ieee80211_hdr *hdr =
371 (struct ieee80211_hdr *)skb->data;
Zhu Yi9184d932006-01-19 16:22:32 +0800372 printk(KERN_DEBUG ": TKIP countermeasures: dropped "
Johannes Berge1749612008-10-27 15:59:26 -0700373 "TX packet to %pM\n", hdr->addr1);
James Ketrenos31b59ea2005-09-21 11:58:49 -0500374 }
375 return -1;
376 }
377
378 if (skb_tailroom(skb) < 4 || skb->len < hdr_len)
379 return -1;
380
381 len = skb->len - hdr_len;
382 pos = skb->data + hdr_len;
383
John W. Linville274bfb82008-10-29 11:35:05 -0400384 if ((lib80211_tkip_hdr(skb, hdr_len, rc4key, 16, priv)) < 0)
James Ketrenos31b59ea2005-09-21 11:58:49 -0500385 return -1;
386
Zhu Yi9184d932006-01-19 16:22:32 +0800387 crc = ~crc32_le(~0, pos, len);
Andriy Tkachukd0833a62010-02-02 15:58:53 +0200388 icv = skb_put(skb, 4);
Zhu Yi9184d932006-01-19 16:22:32 +0800389 icv[0] = crc;
390 icv[1] = crc >> 8;
391 icv[2] = crc >> 16;
392 icv[3] = crc >> 24;
393
Jeff Garzik28eb1772006-09-22 20:10:23 -0400394 crypto_blkcipher_setkey(tkey->tx_tfm_arc4, rc4key, 16);
Jens Axboefa05f122007-10-22 19:44:26 +0200395 sg_init_one(&sg, pos, len + 4);
Herbert Xuf12cc202006-08-22 20:36:13 +1000396 return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4);
Zhu Yib4328d82006-08-21 11:33:09 +0800397}
398
Jeff Garzik18379872006-09-22 21:19:05 -0400399/*
400 * deal with seq counter wrapping correctly.
401 * refer to timer_after() for jiffies wrapping handling
402 */
403static inline int tkip_replay_check(u32 iv32_n, u16 iv16_n,
404 u32 iv32_o, u16 iv16_o)
405{
406 if ((s32)iv32_n - (s32)iv32_o < 0 ||
407 (iv32_n == iv32_o && iv16_n <= iv16_o))
408 return 1;
409 return 0;
410}
411
John W. Linville274bfb82008-10-29 11:35:05 -0400412static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
Jeff Garzikb4538722005-05-12 22:48:20 -0400413{
John W. Linville274bfb82008-10-29 11:35:05 -0400414 struct lib80211_tkip_data *tkey = priv;
Jeff Garzik28eb1772006-09-22 20:10:23 -0400415 struct blkcipher_desc desc = { .tfm = tkey->rx_tfm_arc4 };
Jeff Garzikb4538722005-05-12 22:48:20 -0400416 u8 rc4key[16];
417 u8 keyidx, *pos;
418 u32 iv32;
419 u16 iv16;
John W. Linville274bfb82008-10-29 11:35:05 -0400420 struct ieee80211_hdr *hdr;
Jeff Garzikb4538722005-05-12 22:48:20 -0400421 u8 icv[4];
422 u32 crc;
423 struct scatterlist sg;
424 int plen;
425
John W. Linville274bfb82008-10-29 11:35:05 -0400426 hdr = (struct ieee80211_hdr *)skb->data;
James Ketrenos20d64712005-09-21 11:53:43 -0500427
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000428 if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) {
James Ketrenos20d64712005-09-21 11:53:43 -0500429 if (net_ratelimit()) {
Zhu Yi9184d932006-01-19 16:22:32 +0800430 printk(KERN_DEBUG ": TKIP countermeasures: dropped "
Johannes Berge1749612008-10-27 15:59:26 -0700431 "received packet from %pM\n", hdr->addr2);
James Ketrenos20d64712005-09-21 11:53:43 -0500432 }
433 return -1;
434 }
435
Andriy Tkachuk299af9d2010-02-02 16:33:53 +0200436 if (skb->len < hdr_len + TKIP_HDR_LEN + 4)
Jeff Garzikb4538722005-05-12 22:48:20 -0400437 return -1;
438
Jeff Garzikb4538722005-05-12 22:48:20 -0400439 pos = skb->data + hdr_len;
440 keyidx = pos[3];
441 if (!(keyidx & (1 << 5))) {
442 if (net_ratelimit()) {
443 printk(KERN_DEBUG "TKIP: received packet without ExtIV"
Johannes Berge1749612008-10-27 15:59:26 -0700444 " flag from %pM\n", hdr->addr2);
Jeff Garzikb4538722005-05-12 22:48:20 -0400445 }
446 return -2;
447 }
448 keyidx >>= 6;
449 if (tkey->key_idx != keyidx) {
450 printk(KERN_DEBUG "TKIP: RX tkey->key_idx=%d frame "
451 "keyidx=%d priv=%p\n", tkey->key_idx, keyidx, priv);
452 return -6;
453 }
454 if (!tkey->key_set) {
455 if (net_ratelimit()) {
Johannes Berge1749612008-10-27 15:59:26 -0700456 printk(KERN_DEBUG "TKIP: received packet from %pM"
Jeff Garzikb4538722005-05-12 22:48:20 -0400457 " with keyid=%d that does not have a configured"
Johannes Berge1749612008-10-27 15:59:26 -0700458 " key\n", hdr->addr2, keyidx);
Jeff Garzikb4538722005-05-12 22:48:20 -0400459 }
460 return -3;
461 }
462 iv16 = (pos[0] << 8) | pos[2];
463 iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24);
Andriy Tkachuk299af9d2010-02-02 16:33:53 +0200464 pos += TKIP_HDR_LEN;
Jeff Garzikb4538722005-05-12 22:48:20 -0400465
Zhu Yib4328d82006-08-21 11:33:09 +0800466 if (tkip_replay_check(iv32, iv16, tkey->rx_iv32, tkey->rx_iv16)) {
John W. Linville6f16bf32009-03-11 11:05:25 -0400467#ifdef CONFIG_LIB80211_DEBUG
John W. Linville274bfb82008-10-29 11:35:05 -0400468 if (net_ratelimit()) {
469 printk(KERN_DEBUG "TKIP: replay detected: STA=%pM"
Jeff Garzikb4538722005-05-12 22:48:20 -0400470 " previous TSC %08x%04x received TSC "
Johannes Berge1749612008-10-27 15:59:26 -0700471 "%08x%04x\n", hdr->addr2,
Jeff Garzikb4538722005-05-12 22:48:20 -0400472 tkey->rx_iv32, tkey->rx_iv16, iv32, iv16);
473 }
John W. Linville6f16bf32009-03-11 11:05:25 -0400474#endif
Jeff Garzikb4538722005-05-12 22:48:20 -0400475 tkey->dot11RSNAStatsTKIPReplays++;
476 return -4;
477 }
478
479 if (iv32 != tkey->rx_iv32 || !tkey->rx_phase1_done) {
480 tkip_mixing_phase1(tkey->rx_ttak, tkey->key, hdr->addr2, iv32);
481 tkey->rx_phase1_done = 1;
482 }
483 tkip_mixing_phase2(rc4key, tkey->key, tkey->rx_ttak, iv16);
484
485 plen = skb->len - hdr_len - 12;
486
Jeff Garzik28eb1772006-09-22 20:10:23 -0400487 crypto_blkcipher_setkey(tkey->rx_tfm_arc4, rc4key, 16);
Jens Axboefa05f122007-10-22 19:44:26 +0200488 sg_init_one(&sg, pos, plen + 4);
Herbert Xuf12cc202006-08-22 20:36:13 +1000489 if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4)) {
490 if (net_ratelimit()) {
491 printk(KERN_DEBUG ": TKIP: failed to decrypt "
Johannes Berge1749612008-10-27 15:59:26 -0700492 "received packet from %pM\n",
493 hdr->addr2);
Herbert Xuf12cc202006-08-22 20:36:13 +1000494 }
495 return -7;
496 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400497
498 crc = ~crc32_le(~0, pos, plen);
499 icv[0] = crc;
500 icv[1] = crc >> 8;
501 icv[2] = crc >> 16;
502 icv[3] = crc >> 24;
503 if (memcmp(icv, pos + plen, 4) != 0) {
504 if (iv32 != tkey->rx_iv32) {
505 /* Previously cached Phase1 result was already lost, so
506 * it needs to be recalculated for the next packet. */
507 tkey->rx_phase1_done = 0;
508 }
John W. Linville6f16bf32009-03-11 11:05:25 -0400509#ifdef CONFIG_LIB80211_DEBUG
John W. Linville274bfb82008-10-29 11:35:05 -0400510 if (net_ratelimit()) {
511 printk(KERN_DEBUG "TKIP: ICV error detected: STA="
Johannes Berge1749612008-10-27 15:59:26 -0700512 "%pM\n", hdr->addr2);
Jeff Garzikb4538722005-05-12 22:48:20 -0400513 }
John W. Linville6f16bf32009-03-11 11:05:25 -0400514#endif
Jeff Garzikb4538722005-05-12 22:48:20 -0400515 tkey->dot11RSNAStatsTKIPICVErrors++;
516 return -5;
517 }
518
519 /* Update real counters only after Michael MIC verification has
520 * completed */
521 tkey->rx_iv32_new = iv32;
522 tkey->rx_iv16_new = iv16;
523
524 /* Remove IV and ICV */
Andriy Tkachuk299af9d2010-02-02 16:33:53 +0200525 memmove(skb->data + TKIP_HDR_LEN, skb->data, hdr_len);
526 skb_pull(skb, TKIP_HDR_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400527 skb_trim(skb, skb->len - 4);
528
529 return keyidx;
530}
531
Jeff Garzik28eb1772006-09-22 20:10:23 -0400532static int michael_mic(struct crypto_hash *tfm_michael, u8 * key, u8 * hdr,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400533 u8 * data, size_t data_len, u8 * mic)
Jeff Garzikb4538722005-05-12 22:48:20 -0400534{
Herbert Xu35058682006-08-24 19:10:20 +1000535 struct hash_desc desc;
Jeff Garzikb4538722005-05-12 22:48:20 -0400536 struct scatterlist sg[2];
537
Zhu Yi5a656942006-08-21 11:33:56 +0800538 if (tfm_michael == NULL) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400539 printk(KERN_WARNING "michael_mic: tfm_michael == NULL\n");
540 return -1;
541 }
Jens Axboefa05f122007-10-22 19:44:26 +0200542 sg_init_table(sg, 2);
Jens Axboe642f1492007-10-24 11:20:47 +0200543 sg_set_buf(&sg[0], hdr, 16);
544 sg_set_buf(&sg[1], data, data_len);
Jeff Garzikb4538722005-05-12 22:48:20 -0400545
Jeff Garzik28eb1772006-09-22 20:10:23 -0400546 if (crypto_hash_setkey(tfm_michael, key, 8))
Herbert Xu35058682006-08-24 19:10:20 +1000547 return -1;
Jeff Garzikb4538722005-05-12 22:48:20 -0400548
Jeff Garzik28eb1772006-09-22 20:10:23 -0400549 desc.tfm = tfm_michael;
Herbert Xu35058682006-08-24 19:10:20 +1000550 desc.flags = 0;
551 return crypto_hash_digest(&desc, sg, data_len + 16, mic);
Jeff Garzikb4538722005-05-12 22:48:20 -0400552}
553
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400554static void michael_mic_hdr(struct sk_buff *skb, u8 * hdr)
Jeff Garzikb4538722005-05-12 22:48:20 -0400555{
John W. Linville274bfb82008-10-29 11:35:05 -0400556 struct ieee80211_hdr *hdr11;
Jeff Garzikb4538722005-05-12 22:48:20 -0400557
John W. Linville274bfb82008-10-29 11:35:05 -0400558 hdr11 = (struct ieee80211_hdr *)skb->data;
Zhu Yiea284152006-04-13 17:17:06 +0800559
John W. Linville274bfb82008-10-29 11:35:05 -0400560 switch (le16_to_cpu(hdr11->frame_control) &
Jeff Garzikb4538722005-05-12 22:48:20 -0400561 (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
562 case IEEE80211_FCTL_TODS:
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400563 memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
564 memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
Jeff Garzikb4538722005-05-12 22:48:20 -0400565 break;
566 case IEEE80211_FCTL_FROMDS:
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400567 memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
568 memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */
Jeff Garzikb4538722005-05-12 22:48:20 -0400569 break;
570 case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS:
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400571 memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
572 memcpy(hdr + ETH_ALEN, hdr11->addr4, ETH_ALEN); /* SA */
Jeff Garzikb4538722005-05-12 22:48:20 -0400573 break;
574 case 0:
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400575 memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
576 memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
Jeff Garzikb4538722005-05-12 22:48:20 -0400577 break;
578 }
579
John W. Linville274bfb82008-10-29 11:35:05 -0400580 if (ieee80211_is_data_qos(hdr11->frame_control)) {
John W. Linville3f6ff6b2010-07-20 12:09:11 -0400581 hdr[12] = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(hdr11)))
John W. Linville274bfb82008-10-29 11:35:05 -0400582 & IEEE80211_QOS_CTL_TID_MASK;
Zhu Yiea284152006-04-13 17:17:06 +0800583 } else
584 hdr[12] = 0; /* priority */
585
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400586 hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */
Jeff Garzikb4538722005-05-12 22:48:20 -0400587}
588
John W. Linville274bfb82008-10-29 11:35:05 -0400589static int lib80211_michael_mic_add(struct sk_buff *skb, int hdr_len,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400590 void *priv)
Jeff Garzikb4538722005-05-12 22:48:20 -0400591{
John W. Linville274bfb82008-10-29 11:35:05 -0400592 struct lib80211_tkip_data *tkey = priv;
Jeff Garzikb4538722005-05-12 22:48:20 -0400593 u8 *pos;
594
595 if (skb_tailroom(skb) < 8 || skb->len < hdr_len) {
596 printk(KERN_DEBUG "Invalid packet for Michael MIC add "
597 "(tailroom=%d hdr_len=%d skb->len=%d)\n",
598 skb_tailroom(skb), hdr_len, skb->len);
599 return -1;
600 }
601
602 michael_mic_hdr(skb, tkey->tx_hdr);
603 pos = skb_put(skb, 8);
Zhu Yi5a656942006-08-21 11:33:56 +0800604 if (michael_mic(tkey->tx_tfm_michael, &tkey->key[16], tkey->tx_hdr,
Jeff Garzikb4538722005-05-12 22:48:20 -0400605 skb->data + hdr_len, skb->len - 8 - hdr_len, pos))
606 return -1;
607
608 return 0;
609}
610
John W. Linville274bfb82008-10-29 11:35:05 -0400611static void lib80211_michael_mic_failure(struct net_device *dev,
612 struct ieee80211_hdr *hdr,
James Ketrenosee34af32005-09-21 11:54:36 -0500613 int keyidx)
Jeff Garzikb4538722005-05-12 22:48:20 -0400614{
615 union iwreq_data wrqu;
616 struct iw_michaelmicfailure ev;
617
618 /* TODO: needed parameters: count, keyid, key type, TSC */
619 memset(&ev, 0, sizeof(ev));
620 ev.flags = keyidx & IW_MICFAILURE_KEY_ID;
621 if (hdr->addr1[0] & 0x01)
622 ev.flags |= IW_MICFAILURE_GROUP;
623 else
624 ev.flags |= IW_MICFAILURE_PAIRWISE;
625 ev.src_addr.sa_family = ARPHRD_ETHER;
626 memcpy(ev.src_addr.sa_data, hdr->addr2, ETH_ALEN);
627 memset(&wrqu, 0, sizeof(wrqu));
628 wrqu.data.length = sizeof(ev);
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400629 wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, (char *)&ev);
Jeff Garzikb4538722005-05-12 22:48:20 -0400630}
Jeff Garzikb4538722005-05-12 22:48:20 -0400631
John W. Linville274bfb82008-10-29 11:35:05 -0400632static int lib80211_michael_mic_verify(struct sk_buff *skb, int keyidx,
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400633 int hdr_len, void *priv)
Jeff Garzikb4538722005-05-12 22:48:20 -0400634{
John W. Linville274bfb82008-10-29 11:35:05 -0400635 struct lib80211_tkip_data *tkey = priv;
Jeff Garzikb4538722005-05-12 22:48:20 -0400636 u8 mic[8];
637
638 if (!tkey->key_set)
639 return -1;
640
641 michael_mic_hdr(skb, tkey->rx_hdr);
Zhu Yi5a656942006-08-21 11:33:56 +0800642 if (michael_mic(tkey->rx_tfm_michael, &tkey->key[24], tkey->rx_hdr,
Jeff Garzikb4538722005-05-12 22:48:20 -0400643 skb->data + hdr_len, skb->len - 8 - hdr_len, mic))
644 return -1;
645 if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) {
John W. Linville274bfb82008-10-29 11:35:05 -0400646 struct ieee80211_hdr *hdr;
647 hdr = (struct ieee80211_hdr *)skb->data;
Jeff Garzikb4538722005-05-12 22:48:20 -0400648 printk(KERN_DEBUG "%s: Michael MIC verification failed for "
Johannes Berge1749612008-10-27 15:59:26 -0700649 "MSDU from %pM keyidx=%d\n",
650 skb->dev ? skb->dev->name : "N/A", hdr->addr2,
Jeff Garzikb4538722005-05-12 22:48:20 -0400651 keyidx);
652 if (skb->dev)
John W. Linville274bfb82008-10-29 11:35:05 -0400653 lib80211_michael_mic_failure(skb->dev, hdr, keyidx);
Jeff Garzikb4538722005-05-12 22:48:20 -0400654 tkey->dot11RSNAStatsTKIPLocalMICFailures++;
655 return -1;
656 }
657
658 /* Update TSC counters for RX now that the packet verification has
659 * completed. */
660 tkey->rx_iv32 = tkey->rx_iv32_new;
661 tkey->rx_iv16 = tkey->rx_iv16_new;
662
663 skb_trim(skb, skb->len - 8);
664
665 return 0;
666}
667
John W. Linville274bfb82008-10-29 11:35:05 -0400668static int lib80211_tkip_set_key(void *key, int len, u8 * seq, void *priv)
Jeff Garzikb4538722005-05-12 22:48:20 -0400669{
John W. Linville274bfb82008-10-29 11:35:05 -0400670 struct lib80211_tkip_data *tkey = priv;
Jeff Garzikb4538722005-05-12 22:48:20 -0400671 int keyidx;
Jeff Garzik28eb1772006-09-22 20:10:23 -0400672 struct crypto_hash *tfm = tkey->tx_tfm_michael;
673 struct crypto_blkcipher *tfm2 = tkey->tx_tfm_arc4;
674 struct crypto_hash *tfm3 = tkey->rx_tfm_michael;
675 struct crypto_blkcipher *tfm4 = tkey->rx_tfm_arc4;
Jeff Garzikb4538722005-05-12 22:48:20 -0400676
677 keyidx = tkey->key_idx;
678 memset(tkey, 0, sizeof(*tkey));
679 tkey->key_idx = keyidx;
Zhu Yi5a656942006-08-21 11:33:56 +0800680 tkey->tx_tfm_michael = tfm;
681 tkey->tx_tfm_arc4 = tfm2;
682 tkey->rx_tfm_michael = tfm3;
683 tkey->rx_tfm_arc4 = tfm4;
Jeff Garzikb4538722005-05-12 22:48:20 -0400684 if (len == TKIP_KEY_LEN) {
685 memcpy(tkey->key, key, TKIP_KEY_LEN);
686 tkey->key_set = 1;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400687 tkey->tx_iv16 = 1; /* TSC is initialized to 1 */
Jeff Garzikb4538722005-05-12 22:48:20 -0400688 if (seq) {
689 tkey->rx_iv32 = (seq[5] << 24) | (seq[4] << 16) |
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400690 (seq[3] << 8) | seq[2];
Jeff Garzikb4538722005-05-12 22:48:20 -0400691 tkey->rx_iv16 = (seq[1] << 8) | seq[0];
692 }
693 } else if (len == 0)
694 tkey->key_set = 0;
695 else
696 return -1;
697
698 return 0;
699}
700
John W. Linville274bfb82008-10-29 11:35:05 -0400701static int lib80211_tkip_get_key(void *key, int len, u8 * seq, void *priv)
Jeff Garzikb4538722005-05-12 22:48:20 -0400702{
John W. Linville274bfb82008-10-29 11:35:05 -0400703 struct lib80211_tkip_data *tkey = priv;
Jeff Garzikb4538722005-05-12 22:48:20 -0400704
705 if (len < TKIP_KEY_LEN)
706 return -1;
707
708 if (!tkey->key_set)
709 return 0;
710 memcpy(key, tkey->key, TKIP_KEY_LEN);
711
712 if (seq) {
713 /* Return the sequence number of the last transmitted frame. */
714 u16 iv16 = tkey->tx_iv16;
715 u32 iv32 = tkey->tx_iv32;
716 if (iv16 == 0)
717 iv32--;
718 iv16--;
719 seq[0] = tkey->tx_iv16;
720 seq[1] = tkey->tx_iv16 >> 8;
721 seq[2] = tkey->tx_iv32;
722 seq[3] = tkey->tx_iv32 >> 8;
723 seq[4] = tkey->tx_iv32 >> 16;
724 seq[5] = tkey->tx_iv32 >> 24;
725 }
726
727 return TKIP_KEY_LEN;
728}
729
John W. Linville274bfb82008-10-29 11:35:05 -0400730static char *lib80211_tkip_print_stats(char *p, void *priv)
Jeff Garzikb4538722005-05-12 22:48:20 -0400731{
John W. Linville274bfb82008-10-29 11:35:05 -0400732 struct lib80211_tkip_data *tkip = priv;
Jeff Garzikb4538722005-05-12 22:48:20 -0400733 p += sprintf(p, "key[%d] alg=TKIP key_set=%d "
734 "tx_pn=%02x%02x%02x%02x%02x%02x "
735 "rx_pn=%02x%02x%02x%02x%02x%02x "
736 "replays=%d icv_errors=%d local_mic_failures=%d\n",
737 tkip->key_idx, tkip->key_set,
738 (tkip->tx_iv32 >> 24) & 0xff,
739 (tkip->tx_iv32 >> 16) & 0xff,
740 (tkip->tx_iv32 >> 8) & 0xff,
741 tkip->tx_iv32 & 0xff,
742 (tkip->tx_iv16 >> 8) & 0xff,
743 tkip->tx_iv16 & 0xff,
744 (tkip->rx_iv32 >> 24) & 0xff,
745 (tkip->rx_iv32 >> 16) & 0xff,
746 (tkip->rx_iv32 >> 8) & 0xff,
747 tkip->rx_iv32 & 0xff,
748 (tkip->rx_iv16 >> 8) & 0xff,
749 tkip->rx_iv16 & 0xff,
750 tkip->dot11RSNAStatsTKIPReplays,
751 tkip->dot11RSNAStatsTKIPICVErrors,
752 tkip->dot11RSNAStatsTKIPLocalMICFailures);
753 return p;
754}
755
John W. Linville274bfb82008-10-29 11:35:05 -0400756static struct lib80211_crypto_ops lib80211_crypt_tkip = {
James Ketrenos74079fd2005-09-13 17:35:21 -0500757 .name = "TKIP",
John W. Linville274bfb82008-10-29 11:35:05 -0400758 .init = lib80211_tkip_init,
759 .deinit = lib80211_tkip_deinit,
John W. Linville274bfb82008-10-29 11:35:05 -0400760 .encrypt_mpdu = lib80211_tkip_encrypt,
761 .decrypt_mpdu = lib80211_tkip_decrypt,
762 .encrypt_msdu = lib80211_michael_mic_add,
763 .decrypt_msdu = lib80211_michael_mic_verify,
764 .set_key = lib80211_tkip_set_key,
765 .get_key = lib80211_tkip_get_key,
766 .print_stats = lib80211_tkip_print_stats,
James Ketrenos1264fc02005-09-21 11:54:53 -0500767 .extra_mpdu_prefix_len = 4 + 4, /* IV + ExtIV */
768 .extra_mpdu_postfix_len = 4, /* ICV */
769 .extra_msdu_postfix_len = 8, /* MIC */
John W. Linville274bfb82008-10-29 11:35:05 -0400770 .get_flags = lib80211_tkip_get_flags,
771 .set_flags = lib80211_tkip_set_flags,
James Ketrenos74079fd2005-09-13 17:35:21 -0500772 .owner = THIS_MODULE,
Jeff Garzikb4538722005-05-12 22:48:20 -0400773};
774
John W. Linville274bfb82008-10-29 11:35:05 -0400775static int __init lib80211_crypto_tkip_init(void)
Jeff Garzikb4538722005-05-12 22:48:20 -0400776{
John W. Linville274bfb82008-10-29 11:35:05 -0400777 return lib80211_register_crypto_ops(&lib80211_crypt_tkip);
Jeff Garzikb4538722005-05-12 22:48:20 -0400778}
779
John W. Linville274bfb82008-10-29 11:35:05 -0400780static void __exit lib80211_crypto_tkip_exit(void)
Jeff Garzikb4538722005-05-12 22:48:20 -0400781{
John W. Linville274bfb82008-10-29 11:35:05 -0400782 lib80211_unregister_crypto_ops(&lib80211_crypt_tkip);
Jeff Garzikb4538722005-05-12 22:48:20 -0400783}
784
John W. Linville274bfb82008-10-29 11:35:05 -0400785module_init(lib80211_crypto_tkip_init);
786module_exit(lib80211_crypto_tkip_exit);