1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * FILS AEAD for (Re)Association Request/Response frames
4 * Copyright 2016, Qualcomm Atheros, Inc.
5 */
6
7 #include <crypto/aes-cbc-macs.h>
8 #include <crypto/skcipher.h>
9 #include <crypto/utils.h>
10
11 #include "ieee80211_i.h"
12 #include "fils_aead.h"
13
gf_mulx(u8 * pad)14 static void gf_mulx(u8 *pad)
15 {
16 u64 a = get_unaligned_be64(pad);
17 u64 b = get_unaligned_be64(pad + 8);
18
19 put_unaligned_be64((a << 1) | (b >> 63), pad);
20 put_unaligned_be64((b << 1) ^ ((a >> 63) ? 0x87 : 0), pad + 8);
21 }
22
aes_s2v(const u8 * in_key,size_t key_len,size_t num_elem,const u8 * addr[],size_t len[],u8 * v)23 static int aes_s2v(const u8 *in_key, size_t key_len,
24 size_t num_elem, const u8 *addr[], size_t len[], u8 *v)
25 {
26 u8 d[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE] = {};
27 struct aes_cmac_key key;
28 struct aes_cmac_ctx ctx;
29 size_t i;
30 int res;
31
32 res = aes_cmac_preparekey(&key, in_key, key_len);
33 if (res)
34 return res;
35
36 /* D = AES-CMAC(K, <zero>) */
37 aes_cmac(&key, tmp, AES_BLOCK_SIZE, d);
38
39 for (i = 0; i < num_elem - 1; i++) {
40 /* D = dbl(D) xor AES_CMAC(K, Si) */
41 gf_mulx(d); /* dbl */
42 aes_cmac(&key, addr[i], len[i], tmp);
43 crypto_xor(d, tmp, AES_BLOCK_SIZE);
44 }
45
46 aes_cmac_init(&ctx, &key);
47
48 if (len[i] >= AES_BLOCK_SIZE) {
49 /* len(Sn) >= 128 */
50 /* T = Sn xorend D */
51 aes_cmac_update(&ctx, addr[i], len[i] - AES_BLOCK_SIZE);
52 crypto_xor(d, addr[i] + len[i] - AES_BLOCK_SIZE,
53 AES_BLOCK_SIZE);
54 } else {
55 /* len(Sn) < 128 */
56 /* T = dbl(D) xor pad(Sn) */
57 gf_mulx(d); /* dbl */
58 crypto_xor(d, addr[i], len[i]);
59 d[len[i]] ^= 0x80;
60 }
61 /* V = AES-CMAC(K, T) */
62 aes_cmac_update(&ctx, d, AES_BLOCK_SIZE);
63 aes_cmac_final(&ctx, v);
64
65 memzero_explicit(&key, sizeof(key));
66 return 0;
67 }
68
69 /* Note: addr[] and len[] needs to have one extra slot at the end. */
aes_siv_encrypt(const u8 * key,size_t key_len,const u8 * plain,size_t plain_len,size_t num_elem,const u8 * addr[],size_t len[],u8 * out)70 static int aes_siv_encrypt(const u8 *key, size_t key_len,
71 const u8 *plain, size_t plain_len,
72 size_t num_elem, const u8 *addr[],
73 size_t len[], u8 *out)
74 {
75 u8 v[AES_BLOCK_SIZE];
76 struct crypto_skcipher *tfm2;
77 struct skcipher_request *req;
78 int res;
79 struct scatterlist src[1], dst[1];
80 u8 *tmp;
81
82 key_len /= 2; /* S2V key || CTR key */
83
84 addr[num_elem] = plain;
85 len[num_elem] = plain_len;
86 num_elem++;
87
88 /* S2V */
89 res = aes_s2v(key /* K1 */, key_len, num_elem, addr, len, v);
90 if (res)
91 return res;
92
93 /* Use a temporary buffer of the plaintext to handle need for
94 * overwriting this during AES-CTR.
95 */
96 tmp = kmemdup(plain, plain_len, GFP_KERNEL);
97 if (!tmp)
98 return -ENOMEM;
99
100 /* IV for CTR before encrypted data */
101 memcpy(out, v, AES_BLOCK_SIZE);
102
103 /* Synthetic IV to be used as the initial counter in CTR:
104 * Q = V bitand (1^64 || 0^1 || 1^31 || 0^1 || 1^31)
105 */
106 v[8] &= 0x7f;
107 v[12] &= 0x7f;
108
109 /* CTR */
110
111 tfm2 = crypto_alloc_skcipher("ctr(aes)", 0, CRYPTO_ALG_ASYNC);
112 if (IS_ERR(tfm2)) {
113 kfree(tmp);
114 return PTR_ERR(tfm2);
115 }
116 /* K2 for CTR */
117 res = crypto_skcipher_setkey(tfm2, key + key_len, key_len);
118 if (res)
119 goto fail;
120
121 req = skcipher_request_alloc(tfm2, GFP_KERNEL);
122 if (!req) {
123 res = -ENOMEM;
124 goto fail;
125 }
126
127 sg_init_one(src, tmp, plain_len);
128 sg_init_one(dst, out + AES_BLOCK_SIZE, plain_len);
129 skcipher_request_set_crypt(req, src, dst, plain_len, v);
130 res = crypto_skcipher_encrypt(req);
131 skcipher_request_free(req);
132 fail:
133 kfree(tmp);
134 crypto_free_skcipher(tfm2);
135 return res;
136 }
137
138 /* Note: addr[] and len[] needs to have one extra slot at the end. */
aes_siv_decrypt(const u8 * key,size_t key_len,const u8 * iv_crypt,size_t iv_c_len,size_t num_elem,const u8 * addr[],size_t len[],u8 * out)139 static int aes_siv_decrypt(const u8 *key, size_t key_len,
140 const u8 *iv_crypt, size_t iv_c_len,
141 size_t num_elem, const u8 *addr[], size_t len[],
142 u8 *out)
143 {
144 struct crypto_skcipher *tfm2;
145 struct skcipher_request *req;
146 struct scatterlist src[1], dst[1];
147 size_t crypt_len;
148 int res;
149 u8 frame_iv[AES_BLOCK_SIZE], iv[AES_BLOCK_SIZE];
150 u8 check[AES_BLOCK_SIZE];
151
152 crypt_len = iv_c_len - AES_BLOCK_SIZE;
153 key_len /= 2; /* S2V key || CTR key */
154 addr[num_elem] = out;
155 len[num_elem] = crypt_len;
156 num_elem++;
157
158 memcpy(iv, iv_crypt, AES_BLOCK_SIZE);
159 memcpy(frame_iv, iv_crypt, AES_BLOCK_SIZE);
160
161 /* Synthetic IV to be used as the initial counter in CTR:
162 * Q = V bitand (1^64 || 0^1 || 1^31 || 0^1 || 1^31)
163 */
164 iv[8] &= 0x7f;
165 iv[12] &= 0x7f;
166
167 /* CTR */
168
169 tfm2 = crypto_alloc_skcipher("ctr(aes)", 0, CRYPTO_ALG_ASYNC);
170 if (IS_ERR(tfm2))
171 return PTR_ERR(tfm2);
172 /* K2 for CTR */
173 res = crypto_skcipher_setkey(tfm2, key + key_len, key_len);
174 if (res) {
175 crypto_free_skcipher(tfm2);
176 return res;
177 }
178
179 req = skcipher_request_alloc(tfm2, GFP_KERNEL);
180 if (!req) {
181 crypto_free_skcipher(tfm2);
182 return -ENOMEM;
183 }
184
185 sg_init_one(src, iv_crypt + AES_BLOCK_SIZE, crypt_len);
186 sg_init_one(dst, out, crypt_len);
187 skcipher_request_set_crypt(req, src, dst, crypt_len, iv);
188 res = crypto_skcipher_decrypt(req);
189 skcipher_request_free(req);
190 crypto_free_skcipher(tfm2);
191 if (res)
192 return res;
193
194 /* S2V */
195 res = aes_s2v(key /* K1 */, key_len, num_elem, addr, len, check);
196 if (res)
197 return res;
198 if (memcmp(check, frame_iv, AES_BLOCK_SIZE) != 0)
199 return -EINVAL;
200 return 0;
201 }
202
fils_encrypt_assoc_req(struct sk_buff * skb,struct ieee80211_mgd_assoc_data * assoc_data)203 int fils_encrypt_assoc_req(struct sk_buff *skb,
204 struct ieee80211_mgd_assoc_data *assoc_data)
205 {
206 struct ieee80211_mgmt *mgmt = (void *)skb->data;
207 u8 *capab, *ies, *encr;
208 const u8 *addr[5 + 1];
209 const struct element *session;
210 size_t len[5 + 1];
211 size_t crypt_len;
212
213 if (ieee80211_is_reassoc_req(mgmt->frame_control)) {
214 capab = (u8 *)&mgmt->u.reassoc_req.capab_info;
215 ies = mgmt->u.reassoc_req.variable;
216 } else {
217 capab = (u8 *)&mgmt->u.assoc_req.capab_info;
218 ies = mgmt->u.assoc_req.variable;
219 }
220
221 session = cfg80211_find_ext_elem(WLAN_EID_EXT_FILS_SESSION,
222 ies, skb->data + skb->len - ies);
223 if (!session || session->datalen != 1 + 8)
224 return -EINVAL;
225 /* encrypt after FILS Session element */
226 encr = (u8 *)session->data + 1 + 8;
227
228 /* AES-SIV AAD vectors */
229
230 /* The STA's MAC address */
231 addr[0] = mgmt->sa;
232 len[0] = ETH_ALEN;
233 /* The AP's BSSID */
234 addr[1] = mgmt->da;
235 len[1] = ETH_ALEN;
236 /* The STA's nonce */
237 addr[2] = assoc_data->fils_nonces;
238 len[2] = FILS_NONCE_LEN;
239 /* The AP's nonce */
240 addr[3] = &assoc_data->fils_nonces[FILS_NONCE_LEN];
241 len[3] = FILS_NONCE_LEN;
242 /* The (Re)Association Request frame from the Capability Information
243 * field to the FILS Session element (both inclusive).
244 */
245 addr[4] = capab;
246 len[4] = encr - capab;
247
248 crypt_len = skb->data + skb->len - encr;
249 skb_put(skb, AES_BLOCK_SIZE);
250 return aes_siv_encrypt(assoc_data->fils_kek, assoc_data->fils_kek_len,
251 encr, crypt_len, 5, addr, len, encr);
252 }
253
fils_decrypt_assoc_resp(struct ieee80211_sub_if_data * sdata,u8 * frame,size_t * frame_len,struct ieee80211_mgd_assoc_data * assoc_data)254 int fils_decrypt_assoc_resp(struct ieee80211_sub_if_data *sdata,
255 u8 *frame, size_t *frame_len,
256 struct ieee80211_mgd_assoc_data *assoc_data)
257 {
258 struct ieee80211_mgmt *mgmt = (void *)frame;
259 u8 *capab, *ies, *encr;
260 const u8 *addr[5 + 1];
261 const struct element *session;
262 size_t len[5 + 1];
263 int res;
264 size_t crypt_len;
265
266 if (*frame_len < 24 + 6)
267 return -EINVAL;
268
269 capab = (u8 *)&mgmt->u.assoc_resp.capab_info;
270 ies = mgmt->u.assoc_resp.variable;
271 session = cfg80211_find_ext_elem(WLAN_EID_EXT_FILS_SESSION,
272 ies, frame + *frame_len - ies);
273 if (!session || session->datalen != 1 + 8) {
274 mlme_dbg(sdata,
275 "No (valid) FILS Session element in (Re)Association Response frame from %pM",
276 mgmt->sa);
277 return -EINVAL;
278 }
279 /* decrypt after FILS Session element */
280 encr = (u8 *)session->data + 1 + 8;
281
282 /* AES-SIV AAD vectors */
283
284 /* The AP's BSSID */
285 addr[0] = mgmt->sa;
286 len[0] = ETH_ALEN;
287 /* The STA's MAC address */
288 addr[1] = mgmt->da;
289 len[1] = ETH_ALEN;
290 /* The AP's nonce */
291 addr[2] = &assoc_data->fils_nonces[FILS_NONCE_LEN];
292 len[2] = FILS_NONCE_LEN;
293 /* The STA's nonce */
294 addr[3] = assoc_data->fils_nonces;
295 len[3] = FILS_NONCE_LEN;
296 /* The (Re)Association Response frame from the Capability Information
297 * field to the FILS Session element (both inclusive).
298 */
299 addr[4] = capab;
300 len[4] = encr - capab;
301
302 crypt_len = frame + *frame_len - encr;
303 if (crypt_len < AES_BLOCK_SIZE) {
304 mlme_dbg(sdata,
305 "Not enough room for AES-SIV data after FILS Session element in (Re)Association Response frame from %pM",
306 mgmt->sa);
307 return -EINVAL;
308 }
309 res = aes_siv_decrypt(assoc_data->fils_kek, assoc_data->fils_kek_len,
310 encr, crypt_len, 5, addr, len, encr);
311 if (res != 0) {
312 mlme_dbg(sdata,
313 "AES-SIV decryption of (Re)Association Response frame from %pM failed",
314 mgmt->sa);
315 return res;
316 }
317 *frame_len -= AES_BLOCK_SIZE;
318 return 0;
319 }
320