aboutsummaryrefslogtreecommitdiffstats
path: root/net/ieee80211/ieee80211_crypt_ccmp.c
diff options
context:
space:
mode:
authorJohn W. Linville <linville@tuxdriver.com>2008-10-29 11:35:05 -0400
committerJohn W. Linville <linville@tuxdriver.com>2008-11-21 11:08:17 -0500
commit274bfb8dc5ffa16cb073801bebe76ab7f4e2e73d (patch)
tree04cd3f6a062496911b56737daa6a0858b769ccd6 /net/ieee80211/ieee80211_crypt_ccmp.c
parentdfe1bafdbac1c7b48b636fb7ace799e78170e0d6 (diff)
lib80211: absorb crypto bits from net/ieee80211
These bits are shared already between ipw2x00 and hostap, and could probably be shared both more cleanly and with other drivers. This commit simply relocates the code to lib80211 and adjusts the drivers appropriately. Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/ieee80211/ieee80211_crypt_ccmp.c')
-rw-r--r--net/ieee80211/ieee80211_crypt_ccmp.c492
1 files changed, 0 insertions, 492 deletions
diff --git a/net/ieee80211/ieee80211_crypt_ccmp.c b/net/ieee80211/ieee80211_crypt_ccmp.c
deleted file mode 100644
index bea04af0b482..000000000000
--- a/net/ieee80211/ieee80211_crypt_ccmp.c
+++ /dev/null
@@ -1,492 +0,0 @@
1/*
2 * Host AP crypt: host-based CCMP encryption implementation for Host AP driver
3 *
4 * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation. See README and COPYING for
9 * more details.
10 */
11
12#include <linux/kernel.h>
13#include <linux/err.h>
14#include <linux/module.h>
15#include <linux/init.h>
16#include <linux/slab.h>
17#include <linux/random.h>
18#include <linux/skbuff.h>
19#include <linux/netdevice.h>
20#include <linux/if_ether.h>
21#include <linux/if_arp.h>
22#include <asm/string.h>
23#include <linux/wireless.h>
24
25#include <net/ieee80211.h>
26
27#include <linux/crypto.h>
28
29MODULE_AUTHOR("Jouni Malinen");
30MODULE_DESCRIPTION("Host AP crypt: CCMP");
31MODULE_LICENSE("GPL");
32
33#define AES_BLOCK_LEN 16
34#define CCMP_HDR_LEN 8
35#define CCMP_MIC_LEN 8
36#define CCMP_TK_LEN 16
37#define CCMP_PN_LEN 6
38
39struct ieee80211_ccmp_data {
40 u8 key[CCMP_TK_LEN];
41 int key_set;
42
43 u8 tx_pn[CCMP_PN_LEN];
44 u8 rx_pn[CCMP_PN_LEN];
45
46 u32 dot11RSNAStatsCCMPFormatErrors;
47 u32 dot11RSNAStatsCCMPReplays;
48 u32 dot11RSNAStatsCCMPDecryptErrors;
49
50 int key_idx;
51
52 struct crypto_cipher *tfm;
53
54 /* scratch buffers for virt_to_page() (crypto API) */
55 u8 tx_b0[AES_BLOCK_LEN], tx_b[AES_BLOCK_LEN],
56 tx_e[AES_BLOCK_LEN], tx_s0[AES_BLOCK_LEN];
57 u8 rx_b0[AES_BLOCK_LEN], rx_b[AES_BLOCK_LEN], rx_a[AES_BLOCK_LEN];
58};
59
60static inline void ieee80211_ccmp_aes_encrypt(struct crypto_cipher *tfm,
61 const u8 pt[16], u8 ct[16])
62{
63 crypto_cipher_encrypt_one(tfm, ct, pt);
64}
65
66static void *ieee80211_ccmp_init(int key_idx)
67{
68 struct ieee80211_ccmp_data *priv;
69
70 priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
71 if (priv == NULL)
72 goto fail;
73 priv->key_idx = key_idx;
74
75 priv->tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
76 if (IS_ERR(priv->tfm)) {
77 printk(KERN_DEBUG "ieee80211_crypt_ccmp: could not allocate "
78 "crypto API aes\n");
79 priv->tfm = NULL;
80 goto fail;
81 }
82
83 return priv;
84
85 fail:
86 if (priv) {
87 if (priv->tfm)
88 crypto_free_cipher(priv->tfm);
89 kfree(priv);
90 }
91
92 return NULL;
93}
94
95static void ieee80211_ccmp_deinit(void *priv)
96{
97 struct ieee80211_ccmp_data *_priv = priv;
98 if (_priv && _priv->tfm)
99 crypto_free_cipher(_priv->tfm);
100 kfree(priv);
101}
102
103static inline void xor_block(u8 * b, u8 * a, size_t len)
104{
105 int i;
106 for (i = 0; i < len; i++)
107 b[i] ^= a[i];
108}
109
110static void ccmp_init_blocks(struct crypto_cipher *tfm,
111 struct ieee80211_hdr_4addr *hdr,
112 u8 * pn, size_t dlen, u8 * b0, u8 * auth, u8 * s0)
113{
114 u8 *pos, qc = 0;
115 size_t aad_len;
116 u16 fc;
117 int a4_included, qc_included;
118 u8 aad[2 * AES_BLOCK_LEN];
119
120 fc = le16_to_cpu(hdr->frame_ctl);
121 a4_included = ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
122 (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS));
123 qc_included = ((WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA) &&
124 (WLAN_FC_GET_STYPE(fc) & IEEE80211_STYPE_QOS_DATA));
125 aad_len = 22;
126 if (a4_included)
127 aad_len += 6;
128 if (qc_included) {
129 pos = (u8 *) & hdr->addr4;
130 if (a4_included)
131 pos += 6;
132 qc = *pos & 0x0f;
133 aad_len += 2;
134 }
135
136 /* CCM Initial Block:
137 * Flag (Include authentication header, M=3 (8-octet MIC),
138 * L=1 (2-octet Dlen))
139 * Nonce: 0x00 | A2 | PN
140 * Dlen */
141 b0[0] = 0x59;
142 b0[1] = qc;
143 memcpy(b0 + 2, hdr->addr2, ETH_ALEN);
144 memcpy(b0 + 8, pn, CCMP_PN_LEN);
145 b0[14] = (dlen >> 8) & 0xff;
146 b0[15] = dlen & 0xff;
147
148 /* AAD:
149 * FC with bits 4..6 and 11..13 masked to zero; 14 is always one
150 * A1 | A2 | A3
151 * SC with bits 4..15 (seq#) masked to zero
152 * A4 (if present)
153 * QC (if present)
154 */
155 pos = (u8 *) hdr;
156 aad[0] = 0; /* aad_len >> 8 */
157 aad[1] = aad_len & 0xff;
158 aad[2] = pos[0] & 0x8f;
159 aad[3] = pos[1] & 0xc7;
160 memcpy(aad + 4, hdr->addr1, 3 * ETH_ALEN);
161 pos = (u8 *) & hdr->seq_ctl;
162 aad[22] = pos[0] & 0x0f;
163 aad[23] = 0; /* all bits masked */
164 memset(aad + 24, 0, 8);
165 if (a4_included)
166 memcpy(aad + 24, hdr->addr4, ETH_ALEN);
167 if (qc_included) {
168 aad[a4_included ? 30 : 24] = qc;
169 /* rest of QC masked */
170 }
171
172 /* Start with the first block and AAD */
173 ieee80211_ccmp_aes_encrypt(tfm, b0, auth);
174 xor_block(auth, aad, AES_BLOCK_LEN);
175 ieee80211_ccmp_aes_encrypt(tfm, auth, auth);
176 xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN);
177 ieee80211_ccmp_aes_encrypt(tfm, auth, auth);
178 b0[0] &= 0x07;
179 b0[14] = b0[15] = 0;
180 ieee80211_ccmp_aes_encrypt(tfm, b0, s0);
181}
182
183static int ieee80211_ccmp_hdr(struct sk_buff *skb, int hdr_len,
184 u8 *aeskey, int keylen, void *priv)
185{
186 struct ieee80211_ccmp_data *key = priv;
187 int i;
188 u8 *pos;
189
190 if (skb_headroom(skb) < CCMP_HDR_LEN || skb->len < hdr_len)
191 return -1;
192
193 if (aeskey != NULL && keylen >= CCMP_TK_LEN)
194 memcpy(aeskey, key->key, CCMP_TK_LEN);
195
196 pos = skb_push(skb, CCMP_HDR_LEN);
197 memmove(pos, pos + CCMP_HDR_LEN, hdr_len);
198 pos += hdr_len;
199
200 i = CCMP_PN_LEN - 1;
201 while (i >= 0) {
202 key->tx_pn[i]++;
203 if (key->tx_pn[i] != 0)
204 break;
205 i--;
206 }
207
208 *pos++ = key->tx_pn[5];
209 *pos++ = key->tx_pn[4];
210 *pos++ = 0;
211 *pos++ = (key->key_idx << 6) | (1 << 5) /* Ext IV included */ ;
212 *pos++ = key->tx_pn[3];
213 *pos++ = key->tx_pn[2];
214 *pos++ = key->tx_pn[1];
215 *pos++ = key->tx_pn[0];
216
217 return CCMP_HDR_LEN;
218}
219
220static int ieee80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
221{
222 struct ieee80211_ccmp_data *key = priv;
223 int data_len, i, blocks, last, len;
224 u8 *pos, *mic;
225 struct ieee80211_hdr_4addr *hdr;
226 u8 *b0 = key->tx_b0;
227 u8 *b = key->tx_b;
228 u8 *e = key->tx_e;
229 u8 *s0 = key->tx_s0;
230
231 if (skb_tailroom(skb) < CCMP_MIC_LEN || skb->len < hdr_len)
232 return -1;
233
234 data_len = skb->len - hdr_len;
235 len = ieee80211_ccmp_hdr(skb, hdr_len, NULL, 0, priv);
236 if (len < 0)
237 return -1;
238
239 pos = skb->data + hdr_len + CCMP_HDR_LEN;
240 mic = skb_put(skb, CCMP_MIC_LEN);
241 hdr = (struct ieee80211_hdr_4addr *)skb->data;
242 ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0);
243
244 blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
245 last = data_len % AES_BLOCK_LEN;
246
247 for (i = 1; i <= blocks; i++) {
248 len = (i == blocks && last) ? last : AES_BLOCK_LEN;
249 /* Authentication */
250 xor_block(b, pos, len);
251 ieee80211_ccmp_aes_encrypt(key->tfm, b, b);
252 /* Encryption, with counter */
253 b0[14] = (i >> 8) & 0xff;
254 b0[15] = i & 0xff;
255 ieee80211_ccmp_aes_encrypt(key->tfm, b0, e);
256 xor_block(pos, e, len);
257 pos += len;
258 }
259
260 for (i = 0; i < CCMP_MIC_LEN; i++)
261 mic[i] = b[i] ^ s0[i];
262
263 return 0;
264}
265
266/*
267 * deal with seq counter wrapping correctly.
268 * refer to timer_after() for jiffies wrapping handling
269 */
270static inline int ccmp_replay_check(u8 *pn_n, u8 *pn_o)
271{
272 u32 iv32_n, iv16_n;
273 u32 iv32_o, iv16_o;
274
275 iv32_n = (pn_n[0] << 24) | (pn_n[1] << 16) | (pn_n[2] << 8) | pn_n[3];
276 iv16_n = (pn_n[4] << 8) | pn_n[5];
277
278 iv32_o = (pn_o[0] << 24) | (pn_o[1] << 16) | (pn_o[2] << 8) | pn_o[3];
279 iv16_o = (pn_o[4] << 8) | pn_o[5];
280
281 if ((s32)iv32_n - (s32)iv32_o < 0 ||
282 (iv32_n == iv32_o && iv16_n <= iv16_o))
283 return 1;
284 return 0;
285}
286
287static int ieee80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
288{
289 struct ieee80211_ccmp_data *key = priv;
290 u8 keyidx, *pos;
291 struct ieee80211_hdr_4addr *hdr;
292 u8 *b0 = key->rx_b0;
293 u8 *b = key->rx_b;
294 u8 *a = key->rx_a;
295 u8 pn[6];
296 int i, blocks, last, len;
297 size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN - CCMP_MIC_LEN;
298 u8 *mic = skb->data + skb->len - CCMP_MIC_LEN;
299
300 if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) {
301 key->dot11RSNAStatsCCMPFormatErrors++;
302 return -1;
303 }
304
305 hdr = (struct ieee80211_hdr_4addr *)skb->data;
306 pos = skb->data + hdr_len;
307 keyidx = pos[3];
308 if (!(keyidx & (1 << 5))) {
309 if (net_ratelimit()) {
310 printk(KERN_DEBUG "CCMP: received packet without ExtIV"
311 " flag from %pM\n", hdr->addr2);
312 }
313 key->dot11RSNAStatsCCMPFormatErrors++;
314 return -2;
315 }
316 keyidx >>= 6;
317 if (key->key_idx != keyidx) {
318 printk(KERN_DEBUG "CCMP: RX tkey->key_idx=%d frame "
319 "keyidx=%d priv=%p\n", key->key_idx, keyidx, priv);
320 return -6;
321 }
322 if (!key->key_set) {
323 if (net_ratelimit()) {
324 printk(KERN_DEBUG "CCMP: received packet from %pM"
325 " with keyid=%d that does not have a configured"
326 " key\n", hdr->addr2, keyidx);
327 }
328 return -3;
329 }
330
331 pn[0] = pos[7];
332 pn[1] = pos[6];
333 pn[2] = pos[5];
334 pn[3] = pos[4];
335 pn[4] = pos[1];
336 pn[5] = pos[0];
337 pos += 8;
338
339 if (ccmp_replay_check(pn, key->rx_pn)) {
340 if (ieee80211_ratelimit_debug(IEEE80211_DL_DROP)) {
341 IEEE80211_DEBUG_DROP("CCMP: replay detected: STA=%pM "
342 "previous PN %02x%02x%02x%02x%02x%02x "
343 "received PN %02x%02x%02x%02x%02x%02x\n",
344 hdr->addr2,
345 key->rx_pn[0], key->rx_pn[1], key->rx_pn[2],
346 key->rx_pn[3], key->rx_pn[4], key->rx_pn[5],
347 pn[0], pn[1], pn[2], pn[3], pn[4], pn[5]);
348 }
349 key->dot11RSNAStatsCCMPReplays++;
350 return -4;
351 }
352
353 ccmp_init_blocks(key->tfm, hdr, pn, data_len, b0, a, b);
354 xor_block(mic, b, CCMP_MIC_LEN);
355
356 blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
357 last = data_len % AES_BLOCK_LEN;
358
359 for (i = 1; i <= blocks; i++) {
360 len = (i == blocks && last) ? last : AES_BLOCK_LEN;
361 /* Decrypt, with counter */
362 b0[14] = (i >> 8) & 0xff;
363 b0[15] = i & 0xff;
364 ieee80211_ccmp_aes_encrypt(key->tfm, b0, b);
365 xor_block(pos, b, len);
366 /* Authentication */
367 xor_block(a, pos, len);
368 ieee80211_ccmp_aes_encrypt(key->tfm, a, a);
369 pos += len;
370 }
371
372 if (memcmp(mic, a, CCMP_MIC_LEN) != 0) {
373 if (net_ratelimit()) {
374 printk(KERN_DEBUG "CCMP: decrypt failed: STA="
375 "%pM\n", hdr->addr2);
376 }
377 key->dot11RSNAStatsCCMPDecryptErrors++;
378 return -5;
379 }
380
381 memcpy(key->rx_pn, pn, CCMP_PN_LEN);
382
383 /* Remove hdr and MIC */
384 memmove(skb->data + CCMP_HDR_LEN, skb->data, hdr_len);
385 skb_pull(skb, CCMP_HDR_LEN);
386 skb_trim(skb, skb->len - CCMP_MIC_LEN);
387
388 return keyidx;
389}
390
391static int ieee80211_ccmp_set_key(void *key, int len, u8 * seq, void *priv)
392{
393 struct ieee80211_ccmp_data *data = priv;
394 int keyidx;
395 struct crypto_cipher *tfm = data->tfm;
396
397 keyidx = data->key_idx;
398 memset(data, 0, sizeof(*data));
399 data->key_idx = keyidx;
400 data->tfm = tfm;
401 if (len == CCMP_TK_LEN) {
402 memcpy(data->key, key, CCMP_TK_LEN);
403 data->key_set = 1;
404 if (seq) {
405 data->rx_pn[0] = seq[5];
406 data->rx_pn[1] = seq[4];
407 data->rx_pn[2] = seq[3];
408 data->rx_pn[3] = seq[2];
409 data->rx_pn[4] = seq[1];
410 data->rx_pn[5] = seq[0];
411 }
412 crypto_cipher_setkey(data->tfm, data->key, CCMP_TK_LEN);
413 } else if (len == 0)
414 data->key_set = 0;
415 else
416 return -1;
417
418 return 0;
419}
420
421static int ieee80211_ccmp_get_key(void *key, int len, u8 * seq, void *priv)
422{
423 struct ieee80211_ccmp_data *data = priv;
424
425 if (len < CCMP_TK_LEN)
426 return -1;
427
428 if (!data->key_set)
429 return 0;
430 memcpy(key, data->key, CCMP_TK_LEN);
431
432 if (seq) {
433 seq[0] = data->tx_pn[5];
434 seq[1] = data->tx_pn[4];
435 seq[2] = data->tx_pn[3];
436 seq[3] = data->tx_pn[2];
437 seq[4] = data->tx_pn[1];
438 seq[5] = data->tx_pn[0];
439 }
440
441 return CCMP_TK_LEN;
442}
443
444static char *ieee80211_ccmp_print_stats(char *p, void *priv)
445{
446 struct ieee80211_ccmp_data *ccmp = priv;
447
448 p += sprintf(p, "key[%d] alg=CCMP key_set=%d "
449 "tx_pn=%02x%02x%02x%02x%02x%02x "
450 "rx_pn=%02x%02x%02x%02x%02x%02x "
451 "format_errors=%d replays=%d decrypt_errors=%d\n",
452 ccmp->key_idx, ccmp->key_set,
453 ccmp->tx_pn[0], ccmp->tx_pn[1], ccmp->tx_pn[2],
454 ccmp->tx_pn[3], ccmp->tx_pn[4], ccmp->tx_pn[5],
455 ccmp->rx_pn[0], ccmp->rx_pn[1], ccmp->rx_pn[2],
456 ccmp->rx_pn[3], ccmp->rx_pn[4], ccmp->rx_pn[5],
457 ccmp->dot11RSNAStatsCCMPFormatErrors,
458 ccmp->dot11RSNAStatsCCMPReplays,
459 ccmp->dot11RSNAStatsCCMPDecryptErrors);
460
461 return p;
462}
463
464static struct ieee80211_crypto_ops ieee80211_crypt_ccmp = {
465 .name = "CCMP",
466 .init = ieee80211_ccmp_init,
467 .deinit = ieee80211_ccmp_deinit,
468 .build_iv = ieee80211_ccmp_hdr,
469 .encrypt_mpdu = ieee80211_ccmp_encrypt,
470 .decrypt_mpdu = ieee80211_ccmp_decrypt,
471 .encrypt_msdu = NULL,
472 .decrypt_msdu = NULL,
473 .set_key = ieee80211_ccmp_set_key,
474 .get_key = ieee80211_ccmp_get_key,
475 .print_stats = ieee80211_ccmp_print_stats,
476 .extra_mpdu_prefix_len = CCMP_HDR_LEN,
477 .extra_mpdu_postfix_len = CCMP_MIC_LEN,
478 .owner = THIS_MODULE,
479};
480
481static int __init ieee80211_crypto_ccmp_init(void)
482{
483 return ieee80211_register_crypto_ops(&ieee80211_crypt_ccmp);
484}
485
486static void __exit ieee80211_crypto_ccmp_exit(void)
487{
488 ieee80211_unregister_crypto_ops(&ieee80211_crypt_ccmp);
489}
490
491module_init(ieee80211_crypto_ccmp_init);
492module_exit(ieee80211_crypto_ccmp_exit);