Commit | Line | Data |
---|---|---|
f0706e82 JB |
1 | /* |
2 | * Copyright 2003-2004, Instant802 Networks, Inc. | |
3 | * Copyright 2005-2006, Devicescape Software, Inc. | |
4 | * | |
5 | * 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 | |
7 | * published by the Free Software Foundation. | |
8 | */ | |
9 | ||
172589cc | 10 | #include <linux/kernel.h> |
f0706e82 JB |
11 | #include <linux/types.h> |
12 | #include <linux/crypto.h> | |
13 | #include <linux/err.h> | |
f0706e82 JB |
14 | |
15 | #include <net/mac80211.h> | |
2c8dccc7 | 16 | #include "key.h" |
f0706e82 JB |
17 | #include "aes_ccm.h" |
18 | ||
19 | ||
20 | static void ieee80211_aes_encrypt(struct crypto_cipher *tfm, | |
21 | const u8 pt[16], u8 ct[16]) | |
22 | { | |
23 | crypto_cipher_encrypt_one(tfm, ct, pt); | |
24 | } | |
25 | ||
26 | ||
27 | static inline void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, | |
28 | u8 *b, u8 *s_0, u8 *a) | |
29 | { | |
30 | int i; | |
31 | ||
32 | ieee80211_aes_encrypt(tfm, b_0, b); | |
33 | ||
34 | /* Extra Authenticate-only data (always two AES blocks) */ | |
35 | for (i = 0; i < AES_BLOCK_LEN; i++) | |
36 | aad[i] ^= b[i]; | |
37 | ieee80211_aes_encrypt(tfm, aad, b); | |
38 | ||
39 | aad += AES_BLOCK_LEN; | |
40 | ||
41 | for (i = 0; i < AES_BLOCK_LEN; i++) | |
42 | aad[i] ^= b[i]; | |
43 | ieee80211_aes_encrypt(tfm, aad, a); | |
44 | ||
45 | /* Mask out bits from auth-only-b_0 */ | |
46 | b_0[0] &= 0x07; | |
47 | ||
48 | /* S_0 is used to encrypt T (= MIC) */ | |
49 | b_0[14] = 0; | |
50 | b_0[15] = 0; | |
51 | ieee80211_aes_encrypt(tfm, b_0, s_0); | |
52 | } | |
53 | ||
54 | ||
55 | void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch, | |
56 | u8 *b_0, u8 *aad, u8 *data, size_t data_len, | |
57 | u8 *cdata, u8 *mic) | |
58 | { | |
59 | int i, j, last_len, num_blocks; | |
60 | u8 *pos, *cpos, *b, *s_0, *e; | |
61 | ||
62 | b = scratch; | |
63 | s_0 = scratch + AES_BLOCK_LEN; | |
64 | e = scratch + 2 * AES_BLOCK_LEN; | |
65 | ||
172589cc | 66 | num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN); |
f0706e82 JB |
67 | last_len = data_len % AES_BLOCK_LEN; |
68 | aes_ccm_prepare(tfm, b_0, aad, b, s_0, b); | |
69 | ||
70 | /* Process payload blocks */ | |
71 | pos = data; | |
72 | cpos = cdata; | |
73 | for (j = 1; j <= num_blocks; j++) { | |
74 | int blen = (j == num_blocks && last_len) ? | |
75 | last_len : AES_BLOCK_LEN; | |
76 | ||
77 | /* Authentication followed by encryption */ | |
78 | for (i = 0; i < blen; i++) | |
79 | b[i] ^= pos[i]; | |
80 | ieee80211_aes_encrypt(tfm, b, b); | |
81 | ||
82 | b_0[14] = (j >> 8) & 0xff; | |
83 | b_0[15] = j & 0xff; | |
84 | ieee80211_aes_encrypt(tfm, b_0, e); | |
85 | for (i = 0; i < blen; i++) | |
86 | *cpos++ = *pos++ ^ e[i]; | |
87 | } | |
88 | ||
89 | for (i = 0; i < CCMP_MIC_LEN; i++) | |
90 | mic[i] = b[i] ^ s_0[i]; | |
91 | } | |
92 | ||
93 | ||
94 | int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch, | |
95 | u8 *b_0, u8 *aad, u8 *cdata, size_t data_len, | |
96 | u8 *mic, u8 *data) | |
97 | { | |
98 | int i, j, last_len, num_blocks; | |
99 | u8 *pos, *cpos, *b, *s_0, *a; | |
100 | ||
101 | b = scratch; | |
102 | s_0 = scratch + AES_BLOCK_LEN; | |
103 | a = scratch + 2 * AES_BLOCK_LEN; | |
104 | ||
172589cc | 105 | num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN); |
f0706e82 JB |
106 | last_len = data_len % AES_BLOCK_LEN; |
107 | aes_ccm_prepare(tfm, b_0, aad, b, s_0, a); | |
108 | ||
109 | /* Process payload blocks */ | |
110 | cpos = cdata; | |
111 | pos = data; | |
112 | for (j = 1; j <= num_blocks; j++) { | |
113 | int blen = (j == num_blocks && last_len) ? | |
114 | last_len : AES_BLOCK_LEN; | |
115 | ||
116 | /* Decryption followed by authentication */ | |
117 | b_0[14] = (j >> 8) & 0xff; | |
118 | b_0[15] = j & 0xff; | |
119 | ieee80211_aes_encrypt(tfm, b_0, b); | |
120 | for (i = 0; i < blen; i++) { | |
121 | *pos = *cpos++ ^ b[i]; | |
122 | a[i] ^= *pos++; | |
123 | } | |
124 | ||
125 | ieee80211_aes_encrypt(tfm, a, a); | |
126 | } | |
127 | ||
128 | for (i = 0; i < CCMP_MIC_LEN; i++) { | |
129 | if ((mic[i] ^ s_0[i]) != a[i]) | |
130 | return -1; | |
131 | } | |
132 | ||
133 | return 0; | |
134 | } | |
135 | ||
136 | ||
137 | struct crypto_cipher * ieee80211_aes_key_setup_encrypt(const u8 key[]) | |
138 | { | |
139 | struct crypto_cipher *tfm; | |
140 | ||
141 | tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); | |
142 | if (IS_ERR(tfm)) | |
143 | return NULL; | |
144 | ||
145 | crypto_cipher_setkey(tfm, key, ALG_CCMP_KEY_LEN); | |
146 | ||
147 | return tfm; | |
148 | } | |
149 | ||
150 | ||
151 | void ieee80211_aes_key_free(struct crypto_cipher *tfm) | |
152 | { | |
153 | if (tfm) | |
154 | crypto_free_cipher(tfm); | |
155 | } |