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 __cleanup(aes_cmac_zeroize_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 return 0; 66 } 67 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) 73 { 74 u8 v[AES_BLOCK_SIZE]; 75 struct crypto_skcipher *tfm2; 76 struct skcipher_request *req; 77 int res; 78 struct scatterlist src[1], dst[1]; 79 u8 *tmp; 80 81 key_len /= 2; /* S2V key || CTR key */ 82 83 addr[num_elem] = plain; 84 len[num_elem] = plain_len; 85 num_elem++; 86 87 /* S2V */ 88 res = aes_s2v(key /* K1 */, key_len, num_elem, addr, len, v); 89 if (res) 90 return res; 91 92 /* Use a temporary buffer of the plaintext to handle need for 93 * overwriting this during AES-CTR. 94 */ 95 tmp = kmemdup(plain, plain_len, GFP_KERNEL); 96 if (!tmp) 97 return -ENOMEM; 98 99 /* IV for CTR before encrypted data */ 100 memcpy(out, v, AES_BLOCK_SIZE); 101 102 /* Synthetic IV to be used as the initial counter in CTR: 103 * Q = V bitand (1^64 || 0^1 || 1^31 || 0^1 || 1^31) 104 */ 105 v[8] &= 0x7f; 106 v[12] &= 0x7f; 107 108 /* CTR */ 109 110 tfm2 = crypto_alloc_skcipher("ctr(aes)", 0, CRYPTO_ALG_ASYNC); 111 if (IS_ERR(tfm2)) { 112 kfree(tmp); 113 return PTR_ERR(tfm2); 114 } 115 /* K2 for CTR */ 116 res = crypto_skcipher_setkey(tfm2, key + key_len, key_len); 117 if (res) 118 goto fail; 119 120 req = skcipher_request_alloc(tfm2, GFP_KERNEL); 121 if (!req) { 122 res = -ENOMEM; 123 goto fail; 124 } 125 126 sg_init_one(src, tmp, plain_len); 127 sg_init_one(dst, out + AES_BLOCK_SIZE, plain_len); 128 skcipher_request_set_crypt(req, src, dst, plain_len, v); 129 res = crypto_skcipher_encrypt(req); 130 skcipher_request_free(req); 131 fail: 132 kfree(tmp); 133 crypto_free_skcipher(tfm2); 134 return res; 135 } 136 137 /* Note: addr[] and len[] needs to have one extra slot at the end. */ 138 static int aes_siv_decrypt(const u8 *key, size_t key_len, 139 const u8 *iv_crypt, size_t iv_c_len, 140 size_t num_elem, const u8 *addr[], size_t len[], 141 u8 *out) 142 { 143 struct crypto_skcipher *tfm2; 144 struct skcipher_request *req; 145 struct scatterlist src[1], dst[1]; 146 size_t crypt_len; 147 int res; 148 u8 frame_iv[AES_BLOCK_SIZE], iv[AES_BLOCK_SIZE]; 149 u8 check[AES_BLOCK_SIZE]; 150 151 crypt_len = iv_c_len - AES_BLOCK_SIZE; 152 key_len /= 2; /* S2V key || CTR key */ 153 addr[num_elem] = out; 154 len[num_elem] = crypt_len; 155 num_elem++; 156 157 memcpy(iv, iv_crypt, AES_BLOCK_SIZE); 158 memcpy(frame_iv, iv_crypt, AES_BLOCK_SIZE); 159 160 /* Synthetic IV to be used as the initial counter in CTR: 161 * Q = V bitand (1^64 || 0^1 || 1^31 || 0^1 || 1^31) 162 */ 163 iv[8] &= 0x7f; 164 iv[12] &= 0x7f; 165 166 /* CTR */ 167 168 tfm2 = crypto_alloc_skcipher("ctr(aes)", 0, CRYPTO_ALG_ASYNC); 169 if (IS_ERR(tfm2)) 170 return PTR_ERR(tfm2); 171 /* K2 for CTR */ 172 res = crypto_skcipher_setkey(tfm2, key + key_len, key_len); 173 if (res) { 174 crypto_free_skcipher(tfm2); 175 return res; 176 } 177 178 req = skcipher_request_alloc(tfm2, GFP_KERNEL); 179 if (!req) { 180 crypto_free_skcipher(tfm2); 181 return -ENOMEM; 182 } 183 184 sg_init_one(src, iv_crypt + AES_BLOCK_SIZE, crypt_len); 185 sg_init_one(dst, out, crypt_len); 186 skcipher_request_set_crypt(req, src, dst, crypt_len, iv); 187 res = crypto_skcipher_decrypt(req); 188 skcipher_request_free(req); 189 crypto_free_skcipher(tfm2); 190 if (res) 191 return res; 192 193 /* S2V */ 194 res = aes_s2v(key /* K1 */, key_len, num_elem, addr, len, check); 195 if (res) 196 return res; 197 if (memcmp(check, frame_iv, AES_BLOCK_SIZE) != 0) 198 return -EINVAL; 199 return 0; 200 } 201 202 int fils_encrypt_assoc_req(struct sk_buff *skb, 203 struct ieee80211_mgd_assoc_data *assoc_data) 204 { 205 struct ieee80211_mgmt *mgmt = (void *)skb->data; 206 u8 *capab, *ies, *encr; 207 const u8 *addr[5 + 1]; 208 const struct element *session; 209 size_t len[5 + 1]; 210 size_t crypt_len; 211 212 if (ieee80211_is_reassoc_req(mgmt->frame_control)) { 213 capab = (u8 *)&mgmt->u.reassoc_req.capab_info; 214 ies = mgmt->u.reassoc_req.variable; 215 } else { 216 capab = (u8 *)&mgmt->u.assoc_req.capab_info; 217 ies = mgmt->u.assoc_req.variable; 218 } 219 220 session = cfg80211_find_ext_elem(WLAN_EID_EXT_FILS_SESSION, 221 ies, skb->data + skb->len - ies); 222 if (!session || session->datalen != 1 + 8) 223 return -EINVAL; 224 /* encrypt after FILS Session element */ 225 encr = (u8 *)session->data + 1 + 8; 226 227 /* AES-SIV AAD vectors */ 228 229 /* The STA's MAC address */ 230 addr[0] = mgmt->sa; 231 len[0] = ETH_ALEN; 232 /* The AP's BSSID */ 233 addr[1] = mgmt->da; 234 len[1] = ETH_ALEN; 235 /* The STA's nonce */ 236 addr[2] = assoc_data->fils_nonces; 237 len[2] = FILS_NONCE_LEN; 238 /* The AP's nonce */ 239 addr[3] = &assoc_data->fils_nonces[FILS_NONCE_LEN]; 240 len[3] = FILS_NONCE_LEN; 241 /* The (Re)Association Request frame from the Capability Information 242 * field to the FILS Session element (both inclusive). 243 */ 244 addr[4] = capab; 245 len[4] = encr - capab; 246 247 crypt_len = skb->data + skb->len - encr; 248 skb_put(skb, AES_BLOCK_SIZE); 249 return aes_siv_encrypt(assoc_data->fils_kek, assoc_data->fils_kek_len, 250 encr, crypt_len, 5, addr, len, encr); 251 } 252 253 int fils_decrypt_assoc_resp(struct ieee80211_sub_if_data *sdata, 254 u8 *frame, size_t *frame_len, 255 struct ieee80211_mgd_assoc_data *assoc_data) 256 { 257 struct ieee80211_mgmt *mgmt = (void *)frame; 258 u8 *capab, *ies, *encr; 259 const u8 *addr[5 + 1]; 260 const struct element *session; 261 size_t len[5 + 1]; 262 int res; 263 size_t crypt_len; 264 265 if (*frame_len < 24 + 6) 266 return -EINVAL; 267 268 capab = (u8 *)&mgmt->u.assoc_resp.capab_info; 269 ies = mgmt->u.assoc_resp.variable; 270 session = cfg80211_find_ext_elem(WLAN_EID_EXT_FILS_SESSION, 271 ies, frame + *frame_len - ies); 272 if (!session || session->datalen != 1 + 8) { 273 mlme_dbg(sdata, 274 "No (valid) FILS Session element in (Re)Association Response frame from %pM", 275 mgmt->sa); 276 return -EINVAL; 277 } 278 /* decrypt after FILS Session element */ 279 encr = (u8 *)session->data + 1 + 8; 280 281 /* AES-SIV AAD vectors */ 282 283 /* The AP's BSSID */ 284 addr[0] = mgmt->sa; 285 len[0] = ETH_ALEN; 286 /* The STA's MAC address */ 287 addr[1] = mgmt->da; 288 len[1] = ETH_ALEN; 289 /* The AP's nonce */ 290 addr[2] = &assoc_data->fils_nonces[FILS_NONCE_LEN]; 291 len[2] = FILS_NONCE_LEN; 292 /* The STA's nonce */ 293 addr[3] = assoc_data->fils_nonces; 294 len[3] = FILS_NONCE_LEN; 295 /* The (Re)Association Response frame from the Capability Information 296 * field to the FILS Session element (both inclusive). 297 */ 298 addr[4] = capab; 299 len[4] = encr - capab; 300 301 crypt_len = frame + *frame_len - encr; 302 if (crypt_len < AES_BLOCK_SIZE) { 303 mlme_dbg(sdata, 304 "Not enough room for AES-SIV data after FILS Session element in (Re)Association Response frame from %pM", 305 mgmt->sa); 306 return -EINVAL; 307 } 308 res = aes_siv_decrypt(assoc_data->fils_kek, assoc_data->fils_kek_len, 309 encr, crypt_len, 5, addr, len, encr); 310 if (res != 0) { 311 mlme_dbg(sdata, 312 "AES-SIV decryption of (Re)Association Response frame from %pM failed", 313 mgmt->sa); 314 return res; 315 } 316 *frame_len -= AES_BLOCK_SIZE; 317 return 0; 318 } 319