diff options
author | Jouni Malinen <jouni@qca.qualcomm.com> | 2016-10-26 17:42:05 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2016-10-27 10:03:25 -0400 |
commit | 39404feee691a2dc524a629ed6eb5cfe5d8898d1 (patch) | |
tree | e56b8589724efff836d7878f5e7793151effb945 | |
parent | dbc0c2cb2f0a02fcb034732cb3d55ea6359b9c8b (diff) |
mac80211: FILS AEAD protection for station mode association frames
This adds support for encrypting (Re)Association Request frame and
decryption (Re)Association Response frame when using FILS in station
mode.
Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r-- | net/mac80211/Makefile | 1 | ||||
-rw-r--r-- | net/mac80211/aes_cmac.c | 8 | ||||
-rw-r--r-- | net/mac80211/aes_cmac.h | 4 | ||||
-rw-r--r-- | net/mac80211/fils_aead.c | 344 | ||||
-rw-r--r-- | net/mac80211/fils_aead.h | 19 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 4 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 27 |
7 files changed, 403 insertions, 4 deletions
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index f9137a8341f4..0b202b343fd4 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile | |||
@@ -19,6 +19,7 @@ mac80211-y := \ | |||
19 | aes_gcm.o \ | 19 | aes_gcm.o \ |
20 | aes_cmac.o \ | 20 | aes_cmac.o \ |
21 | aes_gmac.o \ | 21 | aes_gmac.o \ |
22 | fils_aead.o \ | ||
22 | cfg.o \ | 23 | cfg.o \ |
23 | ethtool.o \ | 24 | ethtool.o \ |
24 | rx.o \ | 25 | rx.o \ |
diff --git a/net/mac80211/aes_cmac.c b/net/mac80211/aes_cmac.c index bdf0790d89cc..d0bd5fff5f0a 100644 --- a/net/mac80211/aes_cmac.c +++ b/net/mac80211/aes_cmac.c | |||
@@ -23,7 +23,7 @@ | |||
23 | #define AAD_LEN 20 | 23 | #define AAD_LEN 20 |
24 | 24 | ||
25 | 25 | ||
26 | static void gf_mulx(u8 *pad) | 26 | void gf_mulx(u8 *pad) |
27 | { | 27 | { |
28 | int i, carry; | 28 | int i, carry; |
29 | 29 | ||
@@ -35,9 +35,9 @@ static void gf_mulx(u8 *pad) | |||
35 | pad[AES_BLOCK_SIZE - 1] ^= 0x87; | 35 | pad[AES_BLOCK_SIZE - 1] ^= 0x87; |
36 | } | 36 | } |
37 | 37 | ||
38 | static void aes_cmac_vector(struct crypto_cipher *tfm, size_t num_elem, | 38 | void aes_cmac_vector(struct crypto_cipher *tfm, size_t num_elem, |
39 | const u8 *addr[], const size_t *len, u8 *mac, | 39 | const u8 *addr[], const size_t *len, u8 *mac, |
40 | size_t mac_len) | 40 | size_t mac_len) |
41 | { | 41 | { |
42 | u8 cbc[AES_BLOCK_SIZE], pad[AES_BLOCK_SIZE]; | 42 | u8 cbc[AES_BLOCK_SIZE], pad[AES_BLOCK_SIZE]; |
43 | const u8 *pos, *end; | 43 | const u8 *pos, *end; |
diff --git a/net/mac80211/aes_cmac.h b/net/mac80211/aes_cmac.h index 3702041f44fd..c827e1d5de8b 100644 --- a/net/mac80211/aes_cmac.h +++ b/net/mac80211/aes_cmac.h | |||
@@ -11,6 +11,10 @@ | |||
11 | 11 | ||
12 | #include <linux/crypto.h> | 12 | #include <linux/crypto.h> |
13 | 13 | ||
14 | void gf_mulx(u8 *pad); | ||
15 | void aes_cmac_vector(struct crypto_cipher *tfm, size_t num_elem, | ||
16 | const u8 *addr[], const size_t *len, u8 *mac, | ||
17 | size_t mac_len); | ||
14 | struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[], | 18 | struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[], |
15 | size_t key_len); | 19 | size_t key_len); |
16 | void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad, | 20 | void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad, |
diff --git a/net/mac80211/fils_aead.c b/net/mac80211/fils_aead.c new file mode 100644 index 000000000000..b81b4f2472cf --- /dev/null +++ b/net/mac80211/fils_aead.c | |||
@@ -0,0 +1,344 @@ | |||
1 | /* | ||
2 | * FILS AEAD for (Re)Association Request/Response frames | ||
3 | * Copyright 2016, Qualcomm Atheros, 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 | |||
10 | #include <crypto/aes.h> | ||
11 | #include <crypto/algapi.h> | ||
12 | #include <crypto/skcipher.h> | ||
13 | |||
14 | #include "ieee80211_i.h" | ||
15 | #include "aes_cmac.h" | ||
16 | #include "fils_aead.h" | ||
17 | |||
18 | static int aes_s2v(struct crypto_cipher *tfm, | ||
19 | size_t num_elem, const u8 *addr[], size_t len[], u8 *v) | ||
20 | { | ||
21 | u8 d[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE]; | ||
22 | size_t i; | ||
23 | const u8 *data[2]; | ||
24 | size_t data_len[2], data_elems; | ||
25 | |||
26 | /* D = AES-CMAC(K, <zero>) */ | ||
27 | memset(tmp, 0, AES_BLOCK_SIZE); | ||
28 | data[0] = tmp; | ||
29 | data_len[0] = AES_BLOCK_SIZE; | ||
30 | aes_cmac_vector(tfm, 1, data, data_len, d, AES_BLOCK_SIZE); | ||
31 | |||
32 | for (i = 0; i < num_elem - 1; i++) { | ||
33 | /* D = dbl(D) xor AES_CMAC(K, Si) */ | ||
34 | gf_mulx(d); /* dbl */ | ||
35 | aes_cmac_vector(tfm, 1, &addr[i], &len[i], tmp, | ||
36 | AES_BLOCK_SIZE); | ||
37 | crypto_xor(d, tmp, AES_BLOCK_SIZE); | ||
38 | } | ||
39 | |||
40 | if (len[i] >= AES_BLOCK_SIZE) { | ||
41 | /* len(Sn) >= 128 */ | ||
42 | size_t j; | ||
43 | const u8 *pos; | ||
44 | |||
45 | /* T = Sn xorend D */ | ||
46 | |||
47 | /* Use a temporary buffer to perform xorend on Sn (addr[i]) to | ||
48 | * avoid modifying the const input argument. | ||
49 | */ | ||
50 | data[0] = addr[i]; | ||
51 | data_len[0] = len[i] - AES_BLOCK_SIZE; | ||
52 | pos = addr[i] + data_len[0]; | ||
53 | for (j = 0; j < AES_BLOCK_SIZE; j++) | ||
54 | tmp[j] = pos[j] ^ d[j]; | ||
55 | data[1] = tmp; | ||
56 | data_len[1] = AES_BLOCK_SIZE; | ||
57 | data_elems = 2; | ||
58 | } else { | ||
59 | /* len(Sn) < 128 */ | ||
60 | /* T = dbl(D) xor pad(Sn) */ | ||
61 | gf_mulx(d); /* dbl */ | ||
62 | memset(tmp, 0, AES_BLOCK_SIZE); | ||
63 | memcpy(tmp, addr[i], len[i]); | ||
64 | tmp[len[i]] = 0x80; | ||
65 | crypto_xor(d, tmp, AES_BLOCK_SIZE); | ||
66 | data[0] = d; | ||
67 | data_len[0] = sizeof(d); | ||
68 | data_elems = 1; | ||
69 | } | ||
70 | /* V = AES-CMAC(K, T) */ | ||
71 | aes_cmac_vector(tfm, data_elems, data, data_len, v, AES_BLOCK_SIZE); | ||
72 | |||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | /* Note: addr[] and len[] needs to have one extra slot at the end. */ | ||
77 | static int aes_siv_encrypt(const u8 *key, size_t key_len, | ||
78 | const u8 *plain, size_t plain_len, | ||
79 | size_t num_elem, const u8 *addr[], | ||
80 | size_t len[], u8 *out) | ||
81 | { | ||
82 | u8 v[AES_BLOCK_SIZE]; | ||
83 | struct crypto_cipher *tfm; | ||
84 | struct crypto_skcipher *tfm2; | ||
85 | struct skcipher_request *req; | ||
86 | int res; | ||
87 | struct scatterlist src[1], dst[1]; | ||
88 | u8 *tmp; | ||
89 | |||
90 | key_len /= 2; /* S2V key || CTR key */ | ||
91 | |||
92 | addr[num_elem] = plain; | ||
93 | len[num_elem] = plain_len; | ||
94 | num_elem++; | ||
95 | |||
96 | /* S2V */ | ||
97 | |||
98 | tfm = crypto_alloc_cipher("aes", 0, 0); | ||
99 | if (IS_ERR(tfm)) | ||
100 | return PTR_ERR(tfm); | ||
101 | /* K1 for S2V */ | ||
102 | res = crypto_cipher_setkey(tfm, key, key_len); | ||
103 | if (!res) | ||
104 | res = aes_s2v(tfm, num_elem, addr, len, v); | ||
105 | crypto_free_cipher(tfm); | ||
106 | if (res) | ||
107 | return res; | ||
108 | |||
109 | /* Use a temporary buffer of the plaintext to handle need for | ||
110 | * overwriting this during AES-CTR. | ||
111 | */ | ||
112 | tmp = kmemdup(plain, plain_len, GFP_KERNEL); | ||
113 | if (!tmp) { | ||
114 | res = -ENOMEM; | ||
115 | goto fail; | ||
116 | } | ||
117 | |||
118 | /* IV for CTR before encrypted data */ | ||
119 | memcpy(out, v, AES_BLOCK_SIZE); | ||
120 | |||
121 | /* Synthetic IV to be used as the initial counter in CTR: | ||
122 | * Q = V bitand (1^64 || 0^1 || 1^31 || 0^1 || 1^31) | ||
123 | */ | ||
124 | v[8] &= 0x7f; | ||
125 | v[12] &= 0x7f; | ||
126 | |||
127 | /* CTR */ | ||
128 | |||
129 | tfm2 = crypto_alloc_skcipher("ctr(aes)", 0, 0); | ||
130 | if (IS_ERR(tfm2)) { | ||
131 | kfree(tmp); | ||
132 | return PTR_ERR(tfm2); | ||
133 | } | ||
134 | /* K2 for CTR */ | ||
135 | res = crypto_skcipher_setkey(tfm2, key + key_len, key_len); | ||
136 | if (res) | ||
137 | goto fail; | ||
138 | |||
139 | req = skcipher_request_alloc(tfm2, GFP_KERNEL); | ||
140 | if (!req) { | ||
141 | res = -ENOMEM; | ||
142 | goto fail; | ||
143 | } | ||
144 | |||
145 | sg_init_one(src, tmp, plain_len); | ||
146 | sg_init_one(dst, out + AES_BLOCK_SIZE, plain_len); | ||
147 | skcipher_request_set_crypt(req, src, dst, plain_len, v); | ||
148 | res = crypto_skcipher_encrypt(req); | ||
149 | skcipher_request_free(req); | ||
150 | fail: | ||
151 | kfree(tmp); | ||
152 | crypto_free_skcipher(tfm2); | ||
153 | return res; | ||
154 | } | ||
155 | |||
156 | /* Note: addr[] and len[] needs to have one extra slot at the end. */ | ||
157 | static int aes_siv_decrypt(const u8 *key, size_t key_len, | ||
158 | const u8 *iv_crypt, size_t iv_c_len, | ||
159 | size_t num_elem, const u8 *addr[], size_t len[], | ||
160 | u8 *out) | ||
161 | { | ||
162 | struct crypto_cipher *tfm; | ||
163 | struct crypto_skcipher *tfm2; | ||
164 | struct skcipher_request *req; | ||
165 | struct scatterlist src[1], dst[1]; | ||
166 | size_t crypt_len; | ||
167 | int res; | ||
168 | u8 frame_iv[AES_BLOCK_SIZE], iv[AES_BLOCK_SIZE]; | ||
169 | u8 check[AES_BLOCK_SIZE]; | ||
170 | |||
171 | crypt_len = iv_c_len - AES_BLOCK_SIZE; | ||
172 | key_len /= 2; /* S2V key || CTR key */ | ||
173 | addr[num_elem] = out; | ||
174 | len[num_elem] = crypt_len; | ||
175 | num_elem++; | ||
176 | |||
177 | memcpy(iv, iv_crypt, AES_BLOCK_SIZE); | ||
178 | memcpy(frame_iv, iv_crypt, AES_BLOCK_SIZE); | ||
179 | |||
180 | /* Synthetic IV to be used as the initial counter in CTR: | ||
181 | * Q = V bitand (1^64 || 0^1 || 1^31 || 0^1 || 1^31) | ||
182 | */ | ||
183 | iv[8] &= 0x7f; | ||
184 | iv[12] &= 0x7f; | ||
185 | |||
186 | /* CTR */ | ||
187 | |||
188 | tfm2 = crypto_alloc_skcipher("ctr(aes)", 0, 0); | ||
189 | if (IS_ERR(tfm2)) | ||
190 | return PTR_ERR(tfm2); | ||
191 | /* K2 for CTR */ | ||
192 | res = crypto_skcipher_setkey(tfm2, key + key_len, key_len); | ||
193 | if (res) { | ||
194 | crypto_free_skcipher(tfm2); | ||
195 | return res; | ||
196 | } | ||
197 | |||
198 | req = skcipher_request_alloc(tfm2, GFP_KERNEL); | ||
199 | if (!req) { | ||
200 | crypto_free_skcipher(tfm2); | ||
201 | return -ENOMEM; | ||
202 | } | ||
203 | |||
204 | sg_init_one(src, iv_crypt + AES_BLOCK_SIZE, crypt_len); | ||
205 | sg_init_one(dst, out, crypt_len); | ||
206 | skcipher_request_set_crypt(req, src, dst, crypt_len, iv); | ||
207 | res = crypto_skcipher_decrypt(req); | ||
208 | skcipher_request_free(req); | ||
209 | crypto_free_skcipher(tfm2); | ||
210 | if (res) | ||
211 | return res; | ||
212 | |||
213 | /* S2V */ | ||
214 | |||
215 | tfm = crypto_alloc_cipher("aes", 0, 0); | ||
216 | if (IS_ERR(tfm)) | ||
217 | return PTR_ERR(tfm); | ||
218 | /* K1 for S2V */ | ||
219 | res = crypto_cipher_setkey(tfm, key, key_len); | ||
220 | if (!res) | ||
221 | res = aes_s2v(tfm, num_elem, addr, len, check); | ||
222 | crypto_free_cipher(tfm); | ||
223 | if (res) | ||
224 | return res; | ||
225 | if (memcmp(check, frame_iv, AES_BLOCK_SIZE) != 0) | ||
226 | return -EINVAL; | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | int fils_encrypt_assoc_req(struct sk_buff *skb, | ||
231 | struct ieee80211_mgd_assoc_data *assoc_data) | ||
232 | { | ||
233 | struct ieee80211_mgmt *mgmt = (void *)skb->data; | ||
234 | u8 *capab, *ies, *encr; | ||
235 | const u8 *addr[5 + 1], *session; | ||
236 | size_t len[5 + 1]; | ||
237 | size_t crypt_len; | ||
238 | |||
239 | if (ieee80211_is_reassoc_req(mgmt->frame_control)) { | ||
240 | capab = (u8 *)&mgmt->u.reassoc_req.capab_info; | ||
241 | ies = mgmt->u.reassoc_req.variable; | ||
242 | } else { | ||
243 | capab = (u8 *)&mgmt->u.assoc_req.capab_info; | ||
244 | ies = mgmt->u.assoc_req.variable; | ||
245 | } | ||
246 | |||
247 | session = cfg80211_find_ext_ie(WLAN_EID_EXT_FILS_SESSION, | ||
248 | ies, skb->data + skb->len - ies); | ||
249 | if (!session || session[1] != 1 + 8) | ||
250 | return -EINVAL; | ||
251 | /* encrypt after FILS Session element */ | ||
252 | encr = (u8 *)session + 2 + 1 + 8; | ||
253 | |||
254 | /* AES-SIV AAD vectors */ | ||
255 | |||
256 | /* The STA's MAC address */ | ||
257 | addr[0] = mgmt->sa; | ||
258 | len[0] = ETH_ALEN; | ||
259 | /* The AP's BSSID */ | ||
260 | addr[1] = mgmt->da; | ||
261 | len[1] = ETH_ALEN; | ||
262 | /* The STA's nonce */ | ||
263 | addr[2] = assoc_data->fils_nonces; | ||
264 | len[2] = FILS_NONCE_LEN; | ||
265 | /* The AP's nonce */ | ||
266 | addr[3] = &assoc_data->fils_nonces[FILS_NONCE_LEN]; | ||
267 | len[3] = FILS_NONCE_LEN; | ||
268 | /* The (Re)Association Request frame from the Capability Information | ||
269 | * field to the FILS Session element (both inclusive). | ||
270 | */ | ||
271 | addr[4] = capab; | ||
272 | len[4] = encr - capab; | ||
273 | |||
274 | crypt_len = skb->data + skb->len - encr; | ||
275 | skb_put(skb, AES_BLOCK_SIZE); | ||
276 | return aes_siv_encrypt(assoc_data->fils_kek, assoc_data->fils_kek_len, | ||
277 | encr, crypt_len, 1, addr, len, encr); | ||
278 | } | ||
279 | |||
280 | int fils_decrypt_assoc_resp(struct ieee80211_sub_if_data *sdata, | ||
281 | u8 *frame, size_t *frame_len, | ||
282 | struct ieee80211_mgd_assoc_data *assoc_data) | ||
283 | { | ||
284 | struct ieee80211_mgmt *mgmt = (void *)frame; | ||
285 | u8 *capab, *ies, *encr; | ||
286 | const u8 *addr[5 + 1], *session; | ||
287 | size_t len[5 + 1]; | ||
288 | int res; | ||
289 | size_t crypt_len; | ||
290 | |||
291 | if (*frame_len < 24 + 6) | ||
292 | return -EINVAL; | ||
293 | |||
294 | capab = (u8 *)&mgmt->u.assoc_resp.capab_info; | ||
295 | ies = mgmt->u.assoc_resp.variable; | ||
296 | session = cfg80211_find_ext_ie(WLAN_EID_EXT_FILS_SESSION, | ||
297 | ies, frame + *frame_len - ies); | ||
298 | if (!session || session[1] != 1 + 8) { | ||
299 | mlme_dbg(sdata, | ||
300 | "No (valid) FILS Session element in (Re)Association Response frame from %pM", | ||
301 | mgmt->sa); | ||
302 | return -EINVAL; | ||
303 | } | ||
304 | /* decrypt after FILS Session element */ | ||
305 | encr = (u8 *)session + 2 + 1 + 8; | ||
306 | |||
307 | /* AES-SIV AAD vectors */ | ||
308 | |||
309 | /* The AP's BSSID */ | ||
310 | addr[0] = mgmt->sa; | ||
311 | len[0] = ETH_ALEN; | ||
312 | /* The STA's MAC address */ | ||
313 | addr[1] = mgmt->da; | ||
314 | len[1] = ETH_ALEN; | ||
315 | /* The AP's nonce */ | ||
316 | addr[2] = &assoc_data->fils_nonces[FILS_NONCE_LEN]; | ||
317 | len[2] = FILS_NONCE_LEN; | ||
318 | /* The STA's nonce */ | ||
319 | addr[3] = assoc_data->fils_nonces; | ||
320 | len[3] = FILS_NONCE_LEN; | ||
321 | /* The (Re)Association Response frame from the Capability Information | ||
322 | * field to the FILS Session element (both inclusive). | ||
323 | */ | ||
324 | addr[4] = capab; | ||
325 | len[4] = encr - capab; | ||
326 | |||
327 | crypt_len = frame + *frame_len - encr; | ||
328 | if (crypt_len < AES_BLOCK_SIZE) { | ||
329 | mlme_dbg(sdata, | ||
330 | "Not enough room for AES-SIV data after FILS Session element in (Re)Association Response frame from %pM", | ||
331 | mgmt->sa); | ||
332 | return -EINVAL; | ||
333 | } | ||
334 | res = aes_siv_decrypt(assoc_data->fils_kek, assoc_data->fils_kek_len, | ||
335 | encr, crypt_len, 5, addr, len, encr); | ||
336 | if (res != 0) { | ||
337 | mlme_dbg(sdata, | ||
338 | "AES-SIV decryption of (Re)Association Response frame from %pM failed", | ||
339 | mgmt->sa); | ||
340 | return res; | ||
341 | } | ||
342 | *frame_len -= AES_BLOCK_SIZE; | ||
343 | return 0; | ||
344 | } | ||
diff --git a/net/mac80211/fils_aead.h b/net/mac80211/fils_aead.h new file mode 100644 index 000000000000..fbc65232f0b3 --- /dev/null +++ b/net/mac80211/fils_aead.h | |||
@@ -0,0 +1,19 @@ | |||
1 | /* | ||
2 | * FILS AEAD for (Re)Association Request/Response frames | ||
3 | * Copyright 2016, Qualcomm Atheros, 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 | |||
10 | #ifndef FILS_AEAD_H | ||
11 | #define FILS_AEAD_H | ||
12 | |||
13 | int fils_encrypt_assoc_req(struct sk_buff *skb, | ||
14 | struct ieee80211_mgd_assoc_data *assoc_data); | ||
15 | int fils_decrypt_assoc_resp(struct ieee80211_sub_if_data *sdata, | ||
16 | u8 *frame, size_t *frame_len, | ||
17 | struct ieee80211_mgd_assoc_data *assoc_data); | ||
18 | |||
19 | #endif /* FILS_AEAD_H */ | ||
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index b4e2b6cf4099..d37a577f63a1 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -401,6 +401,10 @@ struct ieee80211_mgd_assoc_data { | |||
401 | 401 | ||
402 | struct ieee80211_vht_cap ap_vht_cap; | 402 | struct ieee80211_vht_cap ap_vht_cap; |
403 | 403 | ||
404 | u8 fils_nonces[2 * FILS_NONCE_LEN]; | ||
405 | u8 fils_kek[FILS_MAX_KEK_LEN]; | ||
406 | size_t fils_kek_len; | ||
407 | |||
404 | size_t ie_len; | 408 | size_t ie_len; |
405 | u8 ie[]; | 409 | u8 ie[]; |
406 | }; | 410 | }; |
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index bf553797bdff..d157b250ff77 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include "driver-ops.h" | 30 | #include "driver-ops.h" |
31 | #include "rate.h" | 31 | #include "rate.h" |
32 | #include "led.h" | 32 | #include "led.h" |
33 | #include "fils_aead.h" | ||
33 | 34 | ||
34 | #define IEEE80211_AUTH_TIMEOUT (HZ / 5) | 35 | #define IEEE80211_AUTH_TIMEOUT (HZ / 5) |
35 | #define IEEE80211_AUTH_TIMEOUT_LONG (HZ / 2) | 36 | #define IEEE80211_AUTH_TIMEOUT_LONG (HZ / 2) |
@@ -652,6 +653,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) | |||
652 | 2 + sizeof(struct ieee80211_ht_cap) + /* HT */ | 653 | 2 + sizeof(struct ieee80211_ht_cap) + /* HT */ |
653 | 2 + sizeof(struct ieee80211_vht_cap) + /* VHT */ | 654 | 2 + sizeof(struct ieee80211_vht_cap) + /* VHT */ |
654 | assoc_data->ie_len + /* extra IEs */ | 655 | assoc_data->ie_len + /* extra IEs */ |
656 | (assoc_data->fils_kek_len ? 16 /* AES-SIV */ : 0) + | ||
655 | 9, /* WMM */ | 657 | 9, /* WMM */ |
656 | GFP_KERNEL); | 658 | GFP_KERNEL); |
657 | if (!skb) | 659 | if (!skb) |
@@ -875,6 +877,12 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) | |||
875 | memcpy(pos, assoc_data->ie + offset, noffset - offset); | 877 | memcpy(pos, assoc_data->ie + offset, noffset - offset); |
876 | } | 878 | } |
877 | 879 | ||
880 | if (assoc_data->fils_kek_len && | ||
881 | fils_encrypt_assoc_req(skb, assoc_data) < 0) { | ||
882 | dev_kfree_skb(skb); | ||
883 | return; | ||
884 | } | ||
885 | |||
878 | drv_mgd_prepare_tx(local, sdata); | 886 | drv_mgd_prepare_tx(local, sdata); |
879 | 887 | ||
880 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; | 888 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; |
@@ -3146,6 +3154,10 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, | |||
3146 | reassoc ? "Rea" : "A", mgmt->sa, | 3154 | reassoc ? "Rea" : "A", mgmt->sa, |
3147 | capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14)))); | 3155 | capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14)))); |
3148 | 3156 | ||
3157 | if (assoc_data->fils_kek_len && | ||
3158 | fils_decrypt_assoc_resp(sdata, (u8 *)mgmt, &len, assoc_data) < 0) | ||
3159 | return; | ||
3160 | |||
3149 | pos = mgmt->u.assoc_resp.variable; | 3161 | pos = mgmt->u.assoc_resp.variable; |
3150 | ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems); | 3162 | ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems); |
3151 | 3163 | ||
@@ -4707,6 +4719,21 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, | |||
4707 | assoc_data->ie_len = req->ie_len; | 4719 | assoc_data->ie_len = req->ie_len; |
4708 | } | 4720 | } |
4709 | 4721 | ||
4722 | if (req->fils_kek) { | ||
4723 | /* should already be checked in cfg80211 - so warn */ | ||
4724 | if (WARN_ON(req->fils_kek_len > FILS_MAX_KEK_LEN)) { | ||
4725 | err = -EINVAL; | ||
4726 | goto err_free; | ||
4727 | } | ||
4728 | memcpy(assoc_data->fils_kek, req->fils_kek, | ||
4729 | req->fils_kek_len); | ||
4730 | assoc_data->fils_kek_len = req->fils_kek_len; | ||
4731 | } | ||
4732 | |||
4733 | if (req->fils_nonces) | ||
4734 | memcpy(assoc_data->fils_nonces, req->fils_nonces, | ||
4735 | 2 * FILS_NONCE_LEN); | ||
4736 | |||
4710 | assoc_data->bss = req->bss; | 4737 | assoc_data->bss = req->bss; |
4711 | 4738 | ||
4712 | if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) { | 4739 | if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) { |