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/hash.h>
9 #include <crypto/skcipher.h>
10 #include <crypto/utils.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];
223 const struct element
*session
;
227 if (ieee80211_is_reassoc_req(mgmt
->frame_control
)) {
228 capab
= (u8
*)&mgmt
->u
.reassoc_req
.capab_info
;
229 ies
= mgmt
->u
.reassoc_req
.variable
;
231 capab
= (u8
*)&mgmt
->u
.assoc_req
.capab_info
;
232 ies
= mgmt
->u
.assoc_req
.variable
;
235 session
= cfg80211_find_ext_elem(WLAN_EID_EXT_FILS_SESSION
,
236 ies
, skb
->data
+ skb
->len
- ies
);
237 if (!session
|| session
->datalen
!= 1 + 8)
239 /* encrypt after FILS Session element */
240 encr
= (u8
*)session
->data
+ 1 + 8;
242 /* AES-SIV AAD vectors */
244 /* The STA's MAC address */
250 /* The STA's nonce */
251 addr
[2] = assoc_data
->fils_nonces
;
252 len
[2] = FILS_NONCE_LEN
;
254 addr
[3] = &assoc_data
->fils_nonces
[FILS_NONCE_LEN
];
255 len
[3] = FILS_NONCE_LEN
;
256 /* The (Re)Association Request frame from the Capability Information
257 * field to the FILS Session element (both inclusive).
260 len
[4] = encr
- capab
;
262 crypt_len
= skb
->data
+ skb
->len
- encr
;
263 skb_put(skb
, AES_BLOCK_SIZE
);
264 return aes_siv_encrypt(assoc_data
->fils_kek
, assoc_data
->fils_kek_len
,
265 encr
, crypt_len
, 5, addr
, len
, encr
);
268 int fils_decrypt_assoc_resp(struct ieee80211_sub_if_data
*sdata
,
269 u8
*frame
, size_t *frame_len
,
270 struct ieee80211_mgd_assoc_data
*assoc_data
)
272 struct ieee80211_mgmt
*mgmt
= (void *)frame
;
273 u8
*capab
, *ies
, *encr
;
274 const u8
*addr
[5 + 1];
275 const struct element
*session
;
280 if (*frame_len
< 24 + 6)
283 capab
= (u8
*)&mgmt
->u
.assoc_resp
.capab_info
;
284 ies
= mgmt
->u
.assoc_resp
.variable
;
285 session
= cfg80211_find_ext_elem(WLAN_EID_EXT_FILS_SESSION
,
286 ies
, frame
+ *frame_len
- ies
);
287 if (!session
|| session
->datalen
!= 1 + 8) {
289 "No (valid) FILS Session element in (Re)Association Response frame from %pM",
293 /* decrypt after FILS Session element */
294 encr
= (u8
*)session
->data
+ 1 + 8;
296 /* AES-SIV AAD vectors */
301 /* The STA's MAC address */
305 addr
[2] = &assoc_data
->fils_nonces
[FILS_NONCE_LEN
];
306 len
[2] = FILS_NONCE_LEN
;
307 /* The STA's nonce */
308 addr
[3] = assoc_data
->fils_nonces
;
309 len
[3] = FILS_NONCE_LEN
;
310 /* The (Re)Association Response frame from the Capability Information
311 * field to the FILS Session element (both inclusive).
314 len
[4] = encr
- capab
;
316 crypt_len
= frame
+ *frame_len
- encr
;
317 if (crypt_len
< AES_BLOCK_SIZE
) {
319 "Not enough room for AES-SIV data after FILS Session element in (Re)Association Response frame from %pM",
323 res
= aes_siv_decrypt(assoc_data
->fils_kek
, assoc_data
->fils_kek_len
,
324 encr
, crypt_len
, 5, addr
, len
, encr
);
327 "AES-SIV decryption of (Re)Association Response frame from %pM failed",
331 *frame_len
-= AES_BLOCK_SIZE
;