2 * FILS AEAD for (Re)Association Request/Response frames
3 * Copyright 2016, Qualcomm Atheros, Inc.
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.
10 #include <crypto/aes.h>
11 #include <crypto/algapi.h>
12 #include <crypto/hash.h>
13 #include <crypto/skcipher.h>
15 #include "ieee80211_i.h"
17 #include "fils_aead.h"
19 static void gf_mulx(u8
*pad
)
21 u64 a
= get_unaligned_be64(pad
);
22 u64 b
= get_unaligned_be64(pad
+ 8);
24 put_unaligned_be64((a
<< 1) | (b
>> 63), pad
);
25 put_unaligned_be64((b
<< 1) ^ ((a
>> 63) ? 0x87 : 0), pad
+ 8);
28 static int aes_s2v(struct crypto_shash
*tfm
,
29 size_t num_elem
, const u8
*addr
[], size_t len
[], u8
*v
)
31 u8 d
[AES_BLOCK_SIZE
], tmp
[AES_BLOCK_SIZE
] = {};
32 SHASH_DESC_ON_STACK(desc
, tfm
);
37 /* D = AES-CMAC(K, <zero>) */
38 crypto_shash_digest(desc
, tmp
, AES_BLOCK_SIZE
, d
);
40 for (i
= 0; i
< num_elem
- 1; i
++) {
41 /* D = dbl(D) xor AES_CMAC(K, Si) */
43 crypto_shash_digest(desc
, addr
[i
], len
[i
], tmp
);
44 crypto_xor(d
, tmp
, AES_BLOCK_SIZE
);
47 crypto_shash_init(desc
);
49 if (len
[i
] >= AES_BLOCK_SIZE
) {
52 crypto_shash_update(desc
, addr
[i
], len
[i
] - AES_BLOCK_SIZE
);
53 crypto_xor(d
, addr
[i
] + len
[i
] - AES_BLOCK_SIZE
,
57 /* T = dbl(D) xor pad(Sn) */
59 crypto_xor(d
, addr
[i
], len
[i
]);
62 /* V = AES-CMAC(K, T) */
63 crypto_shash_finup(desc
, d
, AES_BLOCK_SIZE
, v
);
68 /* Note: addr[] and len[] needs to have one extra slot at the end. */
69 static int aes_siv_encrypt(const u8
*key
, size_t key_len
,
70 const u8
*plain
, size_t plain_len
,
71 size_t num_elem
, const u8
*addr
[],
72 size_t len
[], u8
*out
)
75 struct crypto_shash
*tfm
;
76 struct crypto_skcipher
*tfm2
;
77 struct skcipher_request
*req
;
79 struct scatterlist src
[1], dst
[1];
82 key_len
/= 2; /* S2V key || CTR key */
84 addr
[num_elem
] = plain
;
85 len
[num_elem
] = plain_len
;
90 tfm
= crypto_alloc_shash("cmac(aes)", 0, 0);
94 res
= crypto_shash_setkey(tfm
, key
, key_len
);
96 res
= aes_s2v(tfm
, num_elem
, addr
, len
, v
);
97 crypto_free_shash(tfm
);
101 /* Use a temporary buffer of the plaintext to handle need for
102 * overwriting this during AES-CTR.
104 tmp
= kmemdup(plain
, plain_len
, GFP_KERNEL
);
108 /* IV for CTR before encrypted data */
109 memcpy(out
, v
, AES_BLOCK_SIZE
);
111 /* Synthetic IV to be used as the initial counter in CTR:
112 * Q = V bitand (1^64 || 0^1 || 1^31 || 0^1 || 1^31)
119 tfm2
= crypto_alloc_skcipher("ctr(aes)", 0, CRYPTO_ALG_ASYNC
);
122 return PTR_ERR(tfm2
);
125 res
= crypto_skcipher_setkey(tfm2
, key
+ key_len
, key_len
);
129 req
= skcipher_request_alloc(tfm2
, GFP_KERNEL
);
135 sg_init_one(src
, tmp
, plain_len
);
136 sg_init_one(dst
, out
+ AES_BLOCK_SIZE
, plain_len
);
137 skcipher_request_set_crypt(req
, src
, dst
, plain_len
, v
);
138 res
= crypto_skcipher_encrypt(req
);
139 skcipher_request_free(req
);
142 crypto_free_skcipher(tfm2
);
146 /* Note: addr[] and len[] needs to have one extra slot at the end. */
147 static int aes_siv_decrypt(const u8
*key
, size_t key_len
,
148 const u8
*iv_crypt
, size_t iv_c_len
,
149 size_t num_elem
, const u8
*addr
[], size_t len
[],
152 struct crypto_shash
*tfm
;
153 struct crypto_skcipher
*tfm2
;
154 struct skcipher_request
*req
;
155 struct scatterlist src
[1], dst
[1];
158 u8 frame_iv
[AES_BLOCK_SIZE
], iv
[AES_BLOCK_SIZE
];
159 u8 check
[AES_BLOCK_SIZE
];
161 crypt_len
= iv_c_len
- AES_BLOCK_SIZE
;
162 key_len
/= 2; /* S2V key || CTR key */
163 addr
[num_elem
] = out
;
164 len
[num_elem
] = crypt_len
;
167 memcpy(iv
, iv_crypt
, AES_BLOCK_SIZE
);
168 memcpy(frame_iv
, iv_crypt
, AES_BLOCK_SIZE
);
170 /* Synthetic IV to be used as the initial counter in CTR:
171 * Q = V bitand (1^64 || 0^1 || 1^31 || 0^1 || 1^31)
178 tfm2
= crypto_alloc_skcipher("ctr(aes)", 0, CRYPTO_ALG_ASYNC
);
180 return PTR_ERR(tfm2
);
182 res
= crypto_skcipher_setkey(tfm2
, key
+ key_len
, key_len
);
184 crypto_free_skcipher(tfm2
);
188 req
= skcipher_request_alloc(tfm2
, GFP_KERNEL
);
190 crypto_free_skcipher(tfm2
);
194 sg_init_one(src
, iv_crypt
+ AES_BLOCK_SIZE
, crypt_len
);
195 sg_init_one(dst
, out
, crypt_len
);
196 skcipher_request_set_crypt(req
, src
, dst
, crypt_len
, iv
);
197 res
= crypto_skcipher_decrypt(req
);
198 skcipher_request_free(req
);
199 crypto_free_skcipher(tfm2
);
205 tfm
= crypto_alloc_shash("cmac(aes)", 0, 0);
209 res
= crypto_shash_setkey(tfm
, key
, key_len
);
211 res
= aes_s2v(tfm
, num_elem
, addr
, len
, check
);
212 crypto_free_shash(tfm
);
215 if (memcmp(check
, frame_iv
, AES_BLOCK_SIZE
) != 0)
220 int fils_encrypt_assoc_req(struct sk_buff
*skb
,
221 struct ieee80211_mgd_assoc_data
*assoc_data
)
223 struct ieee80211_mgmt
*mgmt
= (void *)skb
->data
;
224 u8
*capab
, *ies
, *encr
;
225 const u8
*addr
[5 + 1], *session
;
229 if (ieee80211_is_reassoc_req(mgmt
->frame_control
)) {
230 capab
= (u8
*)&mgmt
->u
.reassoc_req
.capab_info
;
231 ies
= mgmt
->u
.reassoc_req
.variable
;
233 capab
= (u8
*)&mgmt
->u
.assoc_req
.capab_info
;
234 ies
= mgmt
->u
.assoc_req
.variable
;
237 session
= cfg80211_find_ext_ie(WLAN_EID_EXT_FILS_SESSION
,
238 ies
, skb
->data
+ skb
->len
- ies
);
239 if (!session
|| session
[1] != 1 + 8)
241 /* encrypt after FILS Session element */
242 encr
= (u8
*)session
+ 2 + 1 + 8;
244 /* AES-SIV AAD vectors */
246 /* The STA's MAC address */
252 /* The STA's nonce */
253 addr
[2] = assoc_data
->fils_nonces
;
254 len
[2] = FILS_NONCE_LEN
;
256 addr
[3] = &assoc_data
->fils_nonces
[FILS_NONCE_LEN
];
257 len
[3] = FILS_NONCE_LEN
;
258 /* The (Re)Association Request frame from the Capability Information
259 * field to the FILS Session element (both inclusive).
262 len
[4] = encr
- capab
;
264 crypt_len
= skb
->data
+ skb
->len
- encr
;
265 skb_put(skb
, AES_BLOCK_SIZE
);
266 return aes_siv_encrypt(assoc_data
->fils_kek
, assoc_data
->fils_kek_len
,
267 encr
, crypt_len
, 5, addr
, len
, encr
);
270 int fils_decrypt_assoc_resp(struct ieee80211_sub_if_data
*sdata
,
271 u8
*frame
, size_t *frame_len
,
272 struct ieee80211_mgd_assoc_data
*assoc_data
)
274 struct ieee80211_mgmt
*mgmt
= (void *)frame
;
275 u8
*capab
, *ies
, *encr
;
276 const u8
*addr
[5 + 1], *session
;
281 if (*frame_len
< 24 + 6)
284 capab
= (u8
*)&mgmt
->u
.assoc_resp
.capab_info
;
285 ies
= mgmt
->u
.assoc_resp
.variable
;
286 session
= cfg80211_find_ext_ie(WLAN_EID_EXT_FILS_SESSION
,
287 ies
, frame
+ *frame_len
- ies
);
288 if (!session
|| session
[1] != 1 + 8) {
290 "No (valid) FILS Session element in (Re)Association Response frame from %pM",
294 /* decrypt after FILS Session element */
295 encr
= (u8
*)session
+ 2 + 1 + 8;
297 /* AES-SIV AAD vectors */
302 /* The STA's MAC address */
306 addr
[2] = &assoc_data
->fils_nonces
[FILS_NONCE_LEN
];
307 len
[2] = FILS_NONCE_LEN
;
308 /* The STA's nonce */
309 addr
[3] = assoc_data
->fils_nonces
;
310 len
[3] = FILS_NONCE_LEN
;
311 /* The (Re)Association Response frame from the Capability Information
312 * field to the FILS Session element (both inclusive).
315 len
[4] = encr
- capab
;
317 crypt_len
= frame
+ *frame_len
- encr
;
318 if (crypt_len
< AES_BLOCK_SIZE
) {
320 "Not enough room for AES-SIV data after FILS Session element in (Re)Association Response frame from %pM",
324 res
= aes_siv_decrypt(assoc_data
->fils_kek
, assoc_data
->fils_kek_len
,
325 encr
, crypt_len
, 5, addr
, len
, encr
);
328 "AES-SIV decryption of (Re)Association Response frame from %pM failed",
332 *frame_len
-= AES_BLOCK_SIZE
;