1 // SPDX-License-Identifier: GPL-2.0-only
3 * FILS AEAD for (Re)Association Request/Response frames
4 * Copyright 2016, Qualcomm Atheros, Inc.
7 #include <crypto/aes.h>
8 #include <crypto/algapi.h>
9 #include <crypto/hash.h>
10 #include <crypto/skcipher.h>
12 #include "ieee80211_i.h"
14 #include "fils_aead.h"
16 static void gf_mulx(u8
*pad
)
18 u64 a
= get_unaligned_be64(pad
);
19 u64 b
= get_unaligned_be64(pad
+ 8);
21 put_unaligned_be64((a
<< 1) | (b
>> 63), pad
);
22 put_unaligned_be64((b
<< 1) ^ ((a
>> 63) ? 0x87 : 0), pad
+ 8);
25 static int aes_s2v(struct crypto_shash
*tfm
,
26 size_t num_elem
, const u8
*addr
[], size_t len
[], u8
*v
)
28 u8 d
[AES_BLOCK_SIZE
], tmp
[AES_BLOCK_SIZE
] = {};
29 SHASH_DESC_ON_STACK(desc
, tfm
);
34 /* D = AES-CMAC(K, <zero>) */
35 crypto_shash_digest(desc
, tmp
, AES_BLOCK_SIZE
, d
);
37 for (i
= 0; i
< num_elem
- 1; i
++) {
38 /* D = dbl(D) xor AES_CMAC(K, Si) */
40 crypto_shash_digest(desc
, addr
[i
], len
[i
], tmp
);
41 crypto_xor(d
, tmp
, AES_BLOCK_SIZE
);
44 crypto_shash_init(desc
);
46 if (len
[i
] >= AES_BLOCK_SIZE
) {
49 crypto_shash_update(desc
, addr
[i
], len
[i
] - AES_BLOCK_SIZE
);
50 crypto_xor(d
, addr
[i
] + len
[i
] - AES_BLOCK_SIZE
,
54 /* T = dbl(D) xor pad(Sn) */
56 crypto_xor(d
, addr
[i
], len
[i
]);
59 /* V = AES-CMAC(K, T) */
60 crypto_shash_finup(desc
, d
, AES_BLOCK_SIZE
, v
);
65 /* Note: addr[] and len[] needs to have one extra slot at the end. */
66 static int aes_siv_encrypt(const u8
*key
, size_t key_len
,
67 const u8
*plain
, size_t plain_len
,
68 size_t num_elem
, const u8
*addr
[],
69 size_t len
[], u8
*out
)
72 struct crypto_shash
*tfm
;
73 struct crypto_skcipher
*tfm2
;
74 struct skcipher_request
*req
;
76 struct scatterlist src
[1], dst
[1];
79 key_len
/= 2; /* S2V key || CTR key */
81 addr
[num_elem
] = plain
;
82 len
[num_elem
] = plain_len
;
87 tfm
= crypto_alloc_shash("cmac(aes)", 0, 0);
91 res
= crypto_shash_setkey(tfm
, key
, key_len
);
93 res
= aes_s2v(tfm
, num_elem
, addr
, len
, v
);
94 crypto_free_shash(tfm
);
98 /* Use a temporary buffer of the plaintext to handle need for
99 * overwriting this during AES-CTR.
101 tmp
= kmemdup(plain
, plain_len
, GFP_KERNEL
);
105 /* IV for CTR before encrypted data */
106 memcpy(out
, v
, AES_BLOCK_SIZE
);
108 /* Synthetic IV to be used as the initial counter in CTR:
109 * Q = V bitand (1^64 || 0^1 || 1^31 || 0^1 || 1^31)
116 tfm2
= crypto_alloc_skcipher("ctr(aes)", 0, CRYPTO_ALG_ASYNC
);
119 return PTR_ERR(tfm2
);
122 res
= crypto_skcipher_setkey(tfm2
, key
+ key_len
, key_len
);
126 req
= skcipher_request_alloc(tfm2
, GFP_KERNEL
);
132 sg_init_one(src
, tmp
, plain_len
);
133 sg_init_one(dst
, out
+ AES_BLOCK_SIZE
, plain_len
);
134 skcipher_request_set_crypt(req
, src
, dst
, plain_len
, v
);
135 res
= crypto_skcipher_encrypt(req
);
136 skcipher_request_free(req
);
139 crypto_free_skcipher(tfm2
);
143 /* Note: addr[] and len[] needs to have one extra slot at the end. */
144 static int aes_siv_decrypt(const u8
*key
, size_t key_len
,
145 const u8
*iv_crypt
, size_t iv_c_len
,
146 size_t num_elem
, const u8
*addr
[], size_t len
[],
149 struct crypto_shash
*tfm
;
150 struct crypto_skcipher
*tfm2
;
151 struct skcipher_request
*req
;
152 struct scatterlist src
[1], dst
[1];
155 u8 frame_iv
[AES_BLOCK_SIZE
], iv
[AES_BLOCK_SIZE
];
156 u8 check
[AES_BLOCK_SIZE
];
158 crypt_len
= iv_c_len
- AES_BLOCK_SIZE
;
159 key_len
/= 2; /* S2V key || CTR key */
160 addr
[num_elem
] = out
;
161 len
[num_elem
] = crypt_len
;
164 memcpy(iv
, iv_crypt
, AES_BLOCK_SIZE
);
165 memcpy(frame_iv
, iv_crypt
, AES_BLOCK_SIZE
);
167 /* Synthetic IV to be used as the initial counter in CTR:
168 * Q = V bitand (1^64 || 0^1 || 1^31 || 0^1 || 1^31)
175 tfm2
= crypto_alloc_skcipher("ctr(aes)", 0, CRYPTO_ALG_ASYNC
);
177 return PTR_ERR(tfm2
);
179 res
= crypto_skcipher_setkey(tfm2
, key
+ key_len
, key_len
);
181 crypto_free_skcipher(tfm2
);
185 req
= skcipher_request_alloc(tfm2
, GFP_KERNEL
);
187 crypto_free_skcipher(tfm2
);
191 sg_init_one(src
, iv_crypt
+ AES_BLOCK_SIZE
, crypt_len
);
192 sg_init_one(dst
, out
, crypt_len
);
193 skcipher_request_set_crypt(req
, src
, dst
, crypt_len
, iv
);
194 res
= crypto_skcipher_decrypt(req
);
195 skcipher_request_free(req
);
196 crypto_free_skcipher(tfm2
);
202 tfm
= crypto_alloc_shash("cmac(aes)", 0, 0);
206 res
= crypto_shash_setkey(tfm
, key
, key_len
);
208 res
= aes_s2v(tfm
, num_elem
, addr
, len
, check
);
209 crypto_free_shash(tfm
);
212 if (memcmp(check
, frame_iv
, AES_BLOCK_SIZE
) != 0)
217 int fils_encrypt_assoc_req(struct sk_buff
*skb
,
218 struct ieee80211_mgd_assoc_data
*assoc_data
)
220 struct ieee80211_mgmt
*mgmt
= (void *)skb
->data
;
221 u8
*capab
, *ies
, *encr
;
222 const u8
*addr
[5 + 1], *session
;
226 if (ieee80211_is_reassoc_req(mgmt
->frame_control
)) {
227 capab
= (u8
*)&mgmt
->u
.reassoc_req
.capab_info
;
228 ies
= mgmt
->u
.reassoc_req
.variable
;
230 capab
= (u8
*)&mgmt
->u
.assoc_req
.capab_info
;
231 ies
= mgmt
->u
.assoc_req
.variable
;
234 session
= cfg80211_find_ext_ie(WLAN_EID_EXT_FILS_SESSION
,
235 ies
, skb
->data
+ skb
->len
- ies
);
236 if (!session
|| session
[1] != 1 + 8)
238 /* encrypt after FILS Session element */
239 encr
= (u8
*)session
+ 2 + 1 + 8;
241 /* AES-SIV AAD vectors */
243 /* The STA's MAC address */
249 /* The STA's nonce */
250 addr
[2] = assoc_data
->fils_nonces
;
251 len
[2] = FILS_NONCE_LEN
;
253 addr
[3] = &assoc_data
->fils_nonces
[FILS_NONCE_LEN
];
254 len
[3] = FILS_NONCE_LEN
;
255 /* The (Re)Association Request frame from the Capability Information
256 * field to the FILS Session element (both inclusive).
259 len
[4] = encr
- capab
;
261 crypt_len
= skb
->data
+ skb
->len
- encr
;
262 skb_put(skb
, AES_BLOCK_SIZE
);
263 return aes_siv_encrypt(assoc_data
->fils_kek
, assoc_data
->fils_kek_len
,
264 encr
, crypt_len
, 5, addr
, len
, encr
);
267 int fils_decrypt_assoc_resp(struct ieee80211_sub_if_data
*sdata
,
268 u8
*frame
, size_t *frame_len
,
269 struct ieee80211_mgd_assoc_data
*assoc_data
)
271 struct ieee80211_mgmt
*mgmt
= (void *)frame
;
272 u8
*capab
, *ies
, *encr
;
273 const u8
*addr
[5 + 1], *session
;
278 if (*frame_len
< 24 + 6)
281 capab
= (u8
*)&mgmt
->u
.assoc_resp
.capab_info
;
282 ies
= mgmt
->u
.assoc_resp
.variable
;
283 session
= cfg80211_find_ext_ie(WLAN_EID_EXT_FILS_SESSION
,
284 ies
, frame
+ *frame_len
- ies
);
285 if (!session
|| session
[1] != 1 + 8) {
287 "No (valid) FILS Session element in (Re)Association Response frame from %pM",
291 /* decrypt after FILS Session element */
292 encr
= (u8
*)session
+ 2 + 1 + 8;
294 /* AES-SIV AAD vectors */
299 /* The STA's MAC address */
303 addr
[2] = &assoc_data
->fils_nonces
[FILS_NONCE_LEN
];
304 len
[2] = FILS_NONCE_LEN
;
305 /* The STA's nonce */
306 addr
[3] = assoc_data
->fils_nonces
;
307 len
[3] = FILS_NONCE_LEN
;
308 /* The (Re)Association Response frame from the Capability Information
309 * field to the FILS Session element (both inclusive).
312 len
[4] = encr
- capab
;
314 crypt_len
= frame
+ *frame_len
- encr
;
315 if (crypt_len
< AES_BLOCK_SIZE
) {
317 "Not enough room for AES-SIV data after FILS Session element in (Re)Association Response frame from %pM",
321 res
= aes_siv_decrypt(assoc_data
->fils_kek
, assoc_data
->fils_kek_len
,
322 encr
, crypt_len
, 5, addr
, len
, encr
);
325 "AES-SIV decryption of (Re)Association Response frame from %pM failed",
329 *frame_len
-= AES_BLOCK_SIZE
;