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 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 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. */ 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. */ 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 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 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