diff options
author | Ard Biesheuvel <ard.biesheuvel@linaro.org> | 2013-10-10 03:55:20 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2013-10-11 09:38:20 -0400 |
commit | 7ec7c4a9a686c608315739ab6a2b0527a240883c (patch) | |
tree | e3f5ecc10414b632be93bef3f4e0494733113795 /net/mac80211 | |
parent | fa1fb9cb1c734204018d2b4e6f38c4a9b4146612 (diff) |
mac80211: port CCMP to cryptoapi's CCM driver
Use the generic CCM aead chaining mode driver rather than a local
implementation that sits right on top of the core AES cipher.
This allows the use of accelerated implementations of either
CCM as a whole or the CTR mode which it encapsulates.
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/Kconfig | 1 | ||||
-rw-r--r-- | net/mac80211/aes_ccm.c | 169 | ||||
-rw-r--r-- | net/mac80211/aes_ccm.h | 14 | ||||
-rw-r--r-- | net/mac80211/key.h | 2 | ||||
-rw-r--r-- | net/mac80211/wpa.c | 44 |
5 files changed, 84 insertions, 146 deletions
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 62535fe9f570..dc31ec3db404 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig | |||
@@ -4,6 +4,7 @@ config MAC80211 | |||
4 | select CRYPTO | 4 | select CRYPTO |
5 | select CRYPTO_ARC4 | 5 | select CRYPTO_ARC4 |
6 | select CRYPTO_AES | 6 | select CRYPTO_AES |
7 | select CRYPTO_CCM | ||
7 | select CRC32 | 8 | select CRC32 |
8 | select AVERAGE | 9 | select AVERAGE |
9 | ---help--- | 10 | ---help--- |
diff --git a/net/mac80211/aes_ccm.c b/net/mac80211/aes_ccm.c index be7614b9ed27..7c7df475a401 100644 --- a/net/mac80211/aes_ccm.c +++ b/net/mac80211/aes_ccm.c | |||
@@ -2,6 +2,8 @@ | |||
2 | * Copyright 2003-2004, Instant802 Networks, Inc. | 2 | * Copyright 2003-2004, Instant802 Networks, Inc. |
3 | * Copyright 2005-2006, Devicescape Software, Inc. | 3 | * Copyright 2005-2006, Devicescape Software, Inc. |
4 | * | 4 | * |
5 | * Rewrite: Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@linaro.org> | ||
6 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License version 2 as | 8 | * it under the terms of the GNU General Public License version 2 as |
7 | * published by the Free Software Foundation. | 9 | * published by the Free Software Foundation. |
@@ -17,134 +19,75 @@ | |||
17 | #include "key.h" | 19 | #include "key.h" |
18 | #include "aes_ccm.h" | 20 | #include "aes_ccm.h" |
19 | 21 | ||
20 | static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *scratch, u8 *a) | 22 | void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, |
23 | u8 *data, size_t data_len, u8 *mic) | ||
21 | { | 24 | { |
22 | int i; | 25 | struct scatterlist assoc, pt, ct[2]; |
23 | u8 *b_0, *aad, *b, *s_0; | 26 | struct { |
24 | 27 | struct aead_request req; | |
25 | b_0 = scratch + 3 * AES_BLOCK_SIZE; | 28 | u8 priv[crypto_aead_reqsize(tfm)]; |
26 | aad = scratch + 4 * AES_BLOCK_SIZE; | 29 | } aead_req; |
27 | b = scratch; | ||
28 | s_0 = scratch + AES_BLOCK_SIZE; | ||
29 | |||
30 | crypto_cipher_encrypt_one(tfm, b, b_0); | ||
31 | 30 | ||
32 | /* Extra Authenticate-only data (always two AES blocks) */ | 31 | memset(&aead_req, 0, sizeof(aead_req)); |
33 | for (i = 0; i < AES_BLOCK_SIZE; i++) | ||
34 | aad[i] ^= b[i]; | ||
35 | crypto_cipher_encrypt_one(tfm, b, aad); | ||
36 | 32 | ||
37 | aad += AES_BLOCK_SIZE; | 33 | sg_init_one(&pt, data, data_len); |
34 | sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad)); | ||
35 | sg_init_table(ct, 2); | ||
36 | sg_set_buf(&ct[0], data, data_len); | ||
37 | sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN); | ||
38 | 38 | ||
39 | for (i = 0; i < AES_BLOCK_SIZE; i++) | 39 | aead_request_set_tfm(&aead_req.req, tfm); |
40 | aad[i] ^= b[i]; | 40 | aead_request_set_assoc(&aead_req.req, &assoc, assoc.length); |
41 | crypto_cipher_encrypt_one(tfm, a, aad); | 41 | aead_request_set_crypt(&aead_req.req, &pt, ct, data_len, b_0); |
42 | 42 | ||
43 | /* Mask out bits from auth-only-b_0 */ | 43 | crypto_aead_encrypt(&aead_req.req); |
44 | b_0[0] &= 0x07; | ||
45 | |||
46 | /* S_0 is used to encrypt T (= MIC) */ | ||
47 | b_0[14] = 0; | ||
48 | b_0[15] = 0; | ||
49 | crypto_cipher_encrypt_one(tfm, s_0, b_0); | ||
50 | } | 44 | } |
51 | 45 | ||
52 | 46 | int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, | |
53 | void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch, | 47 | u8 *data, size_t data_len, u8 *mic) |
54 | u8 *data, size_t data_len, | ||
55 | u8 *cdata, u8 *mic) | ||
56 | { | 48 | { |
57 | int i, j, last_len, num_blocks; | 49 | struct scatterlist assoc, pt, ct[2]; |
58 | u8 *pos, *cpos, *b, *s_0, *e, *b_0; | 50 | struct { |
59 | 51 | struct aead_request req; | |
60 | b = scratch; | 52 | u8 priv[crypto_aead_reqsize(tfm)]; |
61 | s_0 = scratch + AES_BLOCK_SIZE; | 53 | } aead_req; |
62 | e = scratch + 2 * AES_BLOCK_SIZE; | 54 | |
63 | b_0 = scratch + 3 * AES_BLOCK_SIZE; | 55 | memset(&aead_req, 0, sizeof(aead_req)); |
64 | 56 | ||
65 | num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE); | 57 | sg_init_one(&pt, data, data_len); |
66 | last_len = data_len % AES_BLOCK_SIZE; | 58 | sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad)); |
67 | aes_ccm_prepare(tfm, scratch, b); | 59 | sg_init_table(ct, 2); |
68 | 60 | sg_set_buf(&ct[0], data, data_len); | |
69 | /* Process payload blocks */ | 61 | sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN); |
70 | pos = data; | 62 | |
71 | cpos = cdata; | 63 | aead_request_set_tfm(&aead_req.req, tfm); |
72 | for (j = 1; j <= num_blocks; j++) { | 64 | aead_request_set_assoc(&aead_req.req, &assoc, assoc.length); |
73 | int blen = (j == num_blocks && last_len) ? | 65 | aead_request_set_crypt(&aead_req.req, ct, &pt, |
74 | last_len : AES_BLOCK_SIZE; | 66 | data_len + IEEE80211_CCMP_MIC_LEN, b_0); |
75 | 67 | ||
76 | /* Authentication followed by encryption */ | 68 | return crypto_aead_decrypt(&aead_req.req); |
77 | for (i = 0; i < blen; i++) | ||
78 | b[i] ^= pos[i]; | ||
79 | crypto_cipher_encrypt_one(tfm, b, b); | ||
80 | |||
81 | b_0[14] = (j >> 8) & 0xff; | ||
82 | b_0[15] = j & 0xff; | ||
83 | crypto_cipher_encrypt_one(tfm, e, b_0); | ||
84 | for (i = 0; i < blen; i++) | ||
85 | *cpos++ = *pos++ ^ e[i]; | ||
86 | } | ||
87 | |||
88 | for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++) | ||
89 | mic[i] = b[i] ^ s_0[i]; | ||
90 | } | 69 | } |
91 | 70 | ||
92 | 71 | struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[]) | |
93 | int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch, | ||
94 | u8 *cdata, size_t data_len, u8 *mic, u8 *data) | ||
95 | { | 72 | { |
96 | int i, j, last_len, num_blocks; | 73 | struct crypto_aead *tfm; |
97 | u8 *pos, *cpos, *b, *s_0, *a, *b_0; | 74 | int err; |
98 | |||
99 | b = scratch; | ||
100 | s_0 = scratch + AES_BLOCK_SIZE; | ||
101 | a = scratch + 2 * AES_BLOCK_SIZE; | ||
102 | b_0 = scratch + 3 * AES_BLOCK_SIZE; | ||
103 | |||
104 | num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE); | ||
105 | last_len = data_len % AES_BLOCK_SIZE; | ||
106 | aes_ccm_prepare(tfm, scratch, a); | ||
107 | |||
108 | /* Process payload blocks */ | ||
109 | cpos = cdata; | ||
110 | pos = data; | ||
111 | for (j = 1; j <= num_blocks; j++) { | ||
112 | int blen = (j == num_blocks && last_len) ? | ||
113 | last_len : AES_BLOCK_SIZE; | ||
114 | |||
115 | /* Decryption followed by authentication */ | ||
116 | b_0[14] = (j >> 8) & 0xff; | ||
117 | b_0[15] = j & 0xff; | ||
118 | crypto_cipher_encrypt_one(tfm, b, b_0); | ||
119 | for (i = 0; i < blen; i++) { | ||
120 | *pos = *cpos++ ^ b[i]; | ||
121 | a[i] ^= *pos++; | ||
122 | } | ||
123 | crypto_cipher_encrypt_one(tfm, a, a); | ||
124 | } | ||
125 | |||
126 | for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++) { | ||
127 | if ((mic[i] ^ s_0[i]) != a[i]) | ||
128 | return -1; | ||
129 | } | ||
130 | |||
131 | return 0; | ||
132 | } | ||
133 | 75 | ||
76 | tfm = crypto_alloc_aead("ccm(aes)", 0, CRYPTO_ALG_ASYNC); | ||
77 | if (IS_ERR(tfm)) | ||
78 | return tfm; | ||
134 | 79 | ||
135 | struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[]) | 80 | err = crypto_aead_setkey(tfm, key, WLAN_KEY_LEN_CCMP); |
136 | { | 81 | if (!err) |
137 | struct crypto_cipher *tfm; | 82 | err = crypto_aead_setauthsize(tfm, IEEE80211_CCMP_MIC_LEN); |
83 | if (!err) | ||
84 | return tfm; | ||
138 | 85 | ||
139 | tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); | 86 | crypto_free_aead(tfm); |
140 | if (!IS_ERR(tfm)) | 87 | return ERR_PTR(err); |
141 | crypto_cipher_setkey(tfm, key, WLAN_KEY_LEN_CCMP); | ||
142 | |||
143 | return tfm; | ||
144 | } | 88 | } |
145 | 89 | ||
146 | 90 | void ieee80211_aes_key_free(struct crypto_aead *tfm) | |
147 | void ieee80211_aes_key_free(struct crypto_cipher *tfm) | ||
148 | { | 91 | { |
149 | crypto_free_cipher(tfm); | 92 | crypto_free_aead(tfm); |
150 | } | 93 | } |
diff --git a/net/mac80211/aes_ccm.h b/net/mac80211/aes_ccm.h index 5b7d744e2370..2c7ab1948a2e 100644 --- a/net/mac80211/aes_ccm.h +++ b/net/mac80211/aes_ccm.h | |||
@@ -12,13 +12,11 @@ | |||
12 | 12 | ||
13 | #include <linux/crypto.h> | 13 | #include <linux/crypto.h> |
14 | 14 | ||
15 | struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[]); | 15 | struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[]); |
16 | void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch, | 16 | void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, |
17 | u8 *data, size_t data_len, | 17 | u8 *data, size_t data_len, u8 *mic); |
18 | u8 *cdata, u8 *mic); | 18 | int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, |
19 | int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch, | 19 | u8 *data, size_t data_len, u8 *mic); |
20 | u8 *cdata, size_t data_len, | 20 | void ieee80211_aes_key_free(struct crypto_aead *tfm); |
21 | u8 *mic, u8 *data); | ||
22 | void ieee80211_aes_key_free(struct crypto_cipher *tfm); | ||
23 | 21 | ||
24 | #endif /* AES_CCM_H */ | 22 | #endif /* AES_CCM_H */ |
diff --git a/net/mac80211/key.h b/net/mac80211/key.h index 036d57e76a5e..aaae0ed37004 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h | |||
@@ -83,7 +83,7 @@ struct ieee80211_key { | |||
83 | * Management frames. | 83 | * Management frames. |
84 | */ | 84 | */ |
85 | u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN]; | 85 | u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN]; |
86 | struct crypto_cipher *tfm; | 86 | struct crypto_aead *tfm; |
87 | u32 replays; /* dot11RSNAStatsCCMPReplays */ | 87 | u32 replays; /* dot11RSNAStatsCCMPReplays */ |
88 | } ccmp; | 88 | } ccmp; |
89 | struct { | 89 | struct { |
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index c9edfcb7a13b..d65728220763 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c | |||
@@ -301,22 +301,16 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx) | |||
301 | } | 301 | } |
302 | 302 | ||
303 | 303 | ||
304 | static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch, | 304 | static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad, |
305 | int encrypted) | 305 | int encrypted) |
306 | { | 306 | { |
307 | __le16 mask_fc; | 307 | __le16 mask_fc; |
308 | int a4_included, mgmt; | 308 | int a4_included, mgmt; |
309 | u8 qos_tid; | 309 | u8 qos_tid; |
310 | u8 *b_0, *aad; | 310 | u16 len_a; |
311 | u16 data_len, len_a; | ||
312 | unsigned int hdrlen; | 311 | unsigned int hdrlen; |
313 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; | 312 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; |
314 | 313 | ||
315 | memset(scratch, 0, 6 * AES_BLOCK_SIZE); | ||
316 | |||
317 | b_0 = scratch + 3 * AES_BLOCK_SIZE; | ||
318 | aad = scratch + 4 * AES_BLOCK_SIZE; | ||
319 | |||
320 | /* | 314 | /* |
321 | * Mask FC: zero subtype b4 b5 b6 (if not mgmt) | 315 | * Mask FC: zero subtype b4 b5 b6 (if not mgmt) |
322 | * Retry, PwrMgt, MoreData; set Protected | 316 | * Retry, PwrMgt, MoreData; set Protected |
@@ -338,20 +332,21 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch, | |||
338 | else | 332 | else |
339 | qos_tid = 0; | 333 | qos_tid = 0; |
340 | 334 | ||
341 | data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN; | 335 | /* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC |
342 | if (encrypted) | 336 | * mode authentication are not allowed to collide, yet both are derived |
343 | data_len -= IEEE80211_CCMP_MIC_LEN; | 337 | * from this vector b_0. We only set L := 1 here to indicate that the |
338 | * data size can be represented in (L+1) bytes. The CCM layer will take | ||
339 | * care of storing the data length in the top (L+1) bytes and setting | ||
340 | * and clearing the other bits as is required to derive the two IVs. | ||
341 | */ | ||
342 | b_0[0] = 0x1; | ||
344 | 343 | ||
345 | /* First block, b_0 */ | ||
346 | b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */ | ||
347 | /* Nonce: Nonce Flags | A2 | PN | 344 | /* Nonce: Nonce Flags | A2 | PN |
348 | * Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7) | 345 | * Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7) |
349 | */ | 346 | */ |
350 | b_0[1] = qos_tid | (mgmt << 4); | 347 | b_0[1] = qos_tid | (mgmt << 4); |
351 | memcpy(&b_0[2], hdr->addr2, ETH_ALEN); | 348 | memcpy(&b_0[2], hdr->addr2, ETH_ALEN); |
352 | memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN); | 349 | memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN); |
353 | /* l(m) */ | ||
354 | put_unaligned_be16(data_len, &b_0[14]); | ||
355 | 350 | ||
356 | /* AAD (extra authenticate-only data) / masked 802.11 header | 351 | /* AAD (extra authenticate-only data) / masked 802.11 header |
357 | * FC | A1 | A2 | A3 | SC | [A4] | [QC] */ | 352 | * FC | A1 | A2 | A3 | SC | [A4] | [QC] */ |
@@ -407,7 +402,8 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) | |||
407 | u8 *pos; | 402 | u8 *pos; |
408 | u8 pn[6]; | 403 | u8 pn[6]; |
409 | u64 pn64; | 404 | u64 pn64; |
410 | u8 scratch[6 * AES_BLOCK_SIZE]; | 405 | u8 aad[2 * AES_BLOCK_SIZE]; |
406 | u8 b_0[AES_BLOCK_SIZE]; | ||
411 | 407 | ||
412 | if (info->control.hw_key && | 408 | if (info->control.hw_key && |
413 | !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) && | 409 | !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) && |
@@ -460,9 +456,9 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) | |||
460 | return 0; | 456 | return 0; |
461 | 457 | ||
462 | pos += IEEE80211_CCMP_HDR_LEN; | 458 | pos += IEEE80211_CCMP_HDR_LEN; |
463 | ccmp_special_blocks(skb, pn, scratch, 0); | 459 | ccmp_special_blocks(skb, pn, b_0, aad, 0); |
464 | ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, scratch, pos, len, | 460 | ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len, |
465 | pos, skb_put(skb, IEEE80211_CCMP_MIC_LEN)); | 461 | skb_put(skb, IEEE80211_CCMP_MIC_LEN)); |
466 | 462 | ||
467 | return 0; | 463 | return 0; |
468 | } | 464 | } |
@@ -525,16 +521,16 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx) | |||
525 | } | 521 | } |
526 | 522 | ||
527 | if (!(status->flag & RX_FLAG_DECRYPTED)) { | 523 | if (!(status->flag & RX_FLAG_DECRYPTED)) { |
528 | u8 scratch[6 * AES_BLOCK_SIZE]; | 524 | u8 aad[2 * AES_BLOCK_SIZE]; |
525 | u8 b_0[AES_BLOCK_SIZE]; | ||
529 | /* hardware didn't decrypt/verify MIC */ | 526 | /* hardware didn't decrypt/verify MIC */ |
530 | ccmp_special_blocks(skb, pn, scratch, 1); | 527 | ccmp_special_blocks(skb, pn, b_0, aad, 1); |
531 | 528 | ||
532 | if (ieee80211_aes_ccm_decrypt( | 529 | if (ieee80211_aes_ccm_decrypt( |
533 | key->u.ccmp.tfm, scratch, | 530 | key->u.ccmp.tfm, b_0, aad, |
534 | skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN, | 531 | skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN, |
535 | data_len, | 532 | data_len, |
536 | skb->data + skb->len - IEEE80211_CCMP_MIC_LEN, | 533 | skb->data + skb->len - IEEE80211_CCMP_MIC_LEN)) |
537 | skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN)) | ||
538 | return RX_DROP_UNUSABLE; | 534 | return RX_DROP_UNUSABLE; |
539 | } | 535 | } |
540 | 536 | ||