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/skcipher.h>
14 #include "ieee80211_i.h"
16 #include "fils_aead.h"
18 static int aes_s2v(struct crypto_cipher
*tfm
,
19 size_t num_elem
, const u8
*addr
[], size_t len
[], u8
*v
)
21 u8 d
[AES_BLOCK_SIZE
], tmp
[AES_BLOCK_SIZE
];
24 size_t data_len
[2], data_elems
;
26 /* D = AES-CMAC(K, <zero>) */
27 memset(tmp
, 0, AES_BLOCK_SIZE
);
29 data_len
[0] = AES_BLOCK_SIZE
;
30 aes_cmac_vector(tfm
, 1, data
, data_len
, d
, AES_BLOCK_SIZE
);
32 for (i
= 0; i
< num_elem
- 1; i
++) {
33 /* D = dbl(D) xor AES_CMAC(K, Si) */
35 aes_cmac_vector(tfm
, 1, &addr
[i
], &len
[i
], tmp
,
37 crypto_xor(d
, tmp
, AES_BLOCK_SIZE
);
40 if (len
[i
] >= AES_BLOCK_SIZE
) {
47 /* Use a temporary buffer to perform xorend on Sn (addr[i]) to
48 * avoid modifying the const input argument.
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
];
56 data_len
[1] = AES_BLOCK_SIZE
;
60 /* T = dbl(D) xor pad(Sn) */
62 memset(tmp
, 0, AES_BLOCK_SIZE
);
63 memcpy(tmp
, addr
[i
], len
[i
]);
65 crypto_xor(d
, tmp
, AES_BLOCK_SIZE
);
67 data_len
[0] = sizeof(d
);
70 /* V = AES-CMAC(K, T) */
71 aes_cmac_vector(tfm
, data_elems
, data
, data_len
, v
, AES_BLOCK_SIZE
);
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
)
83 struct crypto_cipher
*tfm
;
84 struct crypto_skcipher
*tfm2
;
85 struct skcipher_request
*req
;
87 struct scatterlist src
[1], dst
[1];
90 key_len
/= 2; /* S2V key || CTR key */
92 addr
[num_elem
] = plain
;
93 len
[num_elem
] = plain_len
;
98 tfm
= crypto_alloc_cipher("aes", 0, 0);
102 res
= crypto_cipher_setkey(tfm
, key
, key_len
);
104 res
= aes_s2v(tfm
, num_elem
, addr
, len
, v
);
105 crypto_free_cipher(tfm
);
109 /* Use a temporary buffer of the plaintext to handle need for
110 * overwriting this during AES-CTR.
112 tmp
= kmemdup(plain
, plain_len
, GFP_KERNEL
);
116 /* IV for CTR before encrypted data */
117 memcpy(out
, v
, AES_BLOCK_SIZE
);
119 /* Synthetic IV to be used as the initial counter in CTR:
120 * Q = V bitand (1^64 || 0^1 || 1^31 || 0^1 || 1^31)
127 tfm2
= crypto_alloc_skcipher("ctr(aes)", 0, 0);
130 return PTR_ERR(tfm2
);
133 res
= crypto_skcipher_setkey(tfm2
, key
+ key_len
, key_len
);
137 req
= skcipher_request_alloc(tfm2
, GFP_KERNEL
);
143 sg_init_one(src
, tmp
, plain_len
);
144 sg_init_one(dst
, out
+ AES_BLOCK_SIZE
, plain_len
);
145 skcipher_request_set_crypt(req
, src
, dst
, plain_len
, v
);
146 res
= crypto_skcipher_encrypt(req
);
147 skcipher_request_free(req
);
150 crypto_free_skcipher(tfm2
);
154 /* Note: addr[] and len[] needs to have one extra slot at the end. */
155 static int aes_siv_decrypt(const u8
*key
, size_t key_len
,
156 const u8
*iv_crypt
, size_t iv_c_len
,
157 size_t num_elem
, const u8
*addr
[], size_t len
[],
160 struct crypto_cipher
*tfm
;
161 struct crypto_skcipher
*tfm2
;
162 struct skcipher_request
*req
;
163 struct scatterlist src
[1], dst
[1];
166 u8 frame_iv
[AES_BLOCK_SIZE
], iv
[AES_BLOCK_SIZE
];
167 u8 check
[AES_BLOCK_SIZE
];
169 crypt_len
= iv_c_len
- AES_BLOCK_SIZE
;
170 key_len
/= 2; /* S2V key || CTR key */
171 addr
[num_elem
] = out
;
172 len
[num_elem
] = crypt_len
;
175 memcpy(iv
, iv_crypt
, AES_BLOCK_SIZE
);
176 memcpy(frame_iv
, iv_crypt
, AES_BLOCK_SIZE
);
178 /* Synthetic IV to be used as the initial counter in CTR:
179 * Q = V bitand (1^64 || 0^1 || 1^31 || 0^1 || 1^31)
186 tfm2
= crypto_alloc_skcipher("ctr(aes)", 0, 0);
188 return PTR_ERR(tfm2
);
190 res
= crypto_skcipher_setkey(tfm2
, key
+ key_len
, key_len
);
192 crypto_free_skcipher(tfm2
);
196 req
= skcipher_request_alloc(tfm2
, GFP_KERNEL
);
198 crypto_free_skcipher(tfm2
);
202 sg_init_one(src
, iv_crypt
+ AES_BLOCK_SIZE
, crypt_len
);
203 sg_init_one(dst
, out
, crypt_len
);
204 skcipher_request_set_crypt(req
, src
, dst
, crypt_len
, iv
);
205 res
= crypto_skcipher_decrypt(req
);
206 skcipher_request_free(req
);
207 crypto_free_skcipher(tfm2
);
213 tfm
= crypto_alloc_cipher("aes", 0, 0);
217 res
= crypto_cipher_setkey(tfm
, key
, key_len
);
219 res
= aes_s2v(tfm
, num_elem
, addr
, len
, check
);
220 crypto_free_cipher(tfm
);
223 if (memcmp(check
, frame_iv
, AES_BLOCK_SIZE
) != 0)
228 int fils_encrypt_assoc_req(struct sk_buff
*skb
,
229 struct ieee80211_mgd_assoc_data
*assoc_data
)
231 struct ieee80211_mgmt
*mgmt
= (void *)skb
->data
;
232 u8
*capab
, *ies
, *encr
;
233 const u8
*addr
[5 + 1], *session
;
237 if (ieee80211_is_reassoc_req(mgmt
->frame_control
)) {
238 capab
= (u8
*)&mgmt
->u
.reassoc_req
.capab_info
;
239 ies
= mgmt
->u
.reassoc_req
.variable
;
241 capab
= (u8
*)&mgmt
->u
.assoc_req
.capab_info
;
242 ies
= mgmt
->u
.assoc_req
.variable
;
245 session
= cfg80211_find_ext_ie(WLAN_EID_EXT_FILS_SESSION
,
246 ies
, skb
->data
+ skb
->len
- ies
);
247 if (!session
|| session
[1] != 1 + 8)
249 /* encrypt after FILS Session element */
250 encr
= (u8
*)session
+ 2 + 1 + 8;
252 /* AES-SIV AAD vectors */
254 /* The STA's MAC address */
260 /* The STA's nonce */
261 addr
[2] = assoc_data
->fils_nonces
;
262 len
[2] = FILS_NONCE_LEN
;
264 addr
[3] = &assoc_data
->fils_nonces
[FILS_NONCE_LEN
];
265 len
[3] = FILS_NONCE_LEN
;
266 /* The (Re)Association Request frame from the Capability Information
267 * field to the FILS Session element (both inclusive).
270 len
[4] = encr
- capab
;
272 crypt_len
= skb
->data
+ skb
->len
- encr
;
273 skb_put(skb
, AES_BLOCK_SIZE
);
274 return aes_siv_encrypt(assoc_data
->fils_kek
, assoc_data
->fils_kek_len
,
275 encr
, crypt_len
, 1, addr
, len
, encr
);
278 int fils_decrypt_assoc_resp(struct ieee80211_sub_if_data
*sdata
,
279 u8
*frame
, size_t *frame_len
,
280 struct ieee80211_mgd_assoc_data
*assoc_data
)
282 struct ieee80211_mgmt
*mgmt
= (void *)frame
;
283 u8
*capab
, *ies
, *encr
;
284 const u8
*addr
[5 + 1], *session
;
289 if (*frame_len
< 24 + 6)
292 capab
= (u8
*)&mgmt
->u
.assoc_resp
.capab_info
;
293 ies
= mgmt
->u
.assoc_resp
.variable
;
294 session
= cfg80211_find_ext_ie(WLAN_EID_EXT_FILS_SESSION
,
295 ies
, frame
+ *frame_len
- ies
);
296 if (!session
|| session
[1] != 1 + 8) {
298 "No (valid) FILS Session element in (Re)Association Response frame from %pM",
302 /* decrypt after FILS Session element */
303 encr
= (u8
*)session
+ 2 + 1 + 8;
305 /* AES-SIV AAD vectors */
310 /* The STA's MAC address */
314 addr
[2] = &assoc_data
->fils_nonces
[FILS_NONCE_LEN
];
315 len
[2] = FILS_NONCE_LEN
;
316 /* The STA's nonce */
317 addr
[3] = assoc_data
->fils_nonces
;
318 len
[3] = FILS_NONCE_LEN
;
319 /* The (Re)Association Response frame from the Capability Information
320 * field to the FILS Session element (both inclusive).
323 len
[4] = encr
- capab
;
325 crypt_len
= frame
+ *frame_len
- encr
;
326 if (crypt_len
< AES_BLOCK_SIZE
) {
328 "Not enough room for AES-SIV data after FILS Session element in (Re)Association Response frame from %pM",
332 res
= aes_siv_decrypt(assoc_data
->fils_kek
, assoc_data
->fils_kek_len
,
333 encr
, crypt_len
, 5, addr
, len
, encr
);
336 "AES-SIV decryption of (Re)Association Response frame from %pM failed",
340 *frame_len
-= AES_BLOCK_SIZE
;