162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * FILS AEAD for (Re)Association Request/Response frames 462306a36Sopenharmony_ci * Copyright 2016, Qualcomm Atheros, Inc. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <crypto/aes.h> 862306a36Sopenharmony_ci#include <crypto/hash.h> 962306a36Sopenharmony_ci#include <crypto/skcipher.h> 1062306a36Sopenharmony_ci#include <crypto/utils.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "ieee80211_i.h" 1362306a36Sopenharmony_ci#include "aes_cmac.h" 1462306a36Sopenharmony_ci#include "fils_aead.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic void gf_mulx(u8 *pad) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci u64 a = get_unaligned_be64(pad); 1962306a36Sopenharmony_ci u64 b = get_unaligned_be64(pad + 8); 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci put_unaligned_be64((a << 1) | (b >> 63), pad); 2262306a36Sopenharmony_ci put_unaligned_be64((b << 1) ^ ((a >> 63) ? 0x87 : 0), pad + 8); 2362306a36Sopenharmony_ci} 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic int aes_s2v(struct crypto_shash *tfm, 2662306a36Sopenharmony_ci size_t num_elem, const u8 *addr[], size_t len[], u8 *v) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci u8 d[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE] = {}; 2962306a36Sopenharmony_ci SHASH_DESC_ON_STACK(desc, tfm); 3062306a36Sopenharmony_ci size_t i; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci desc->tfm = tfm; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci /* D = AES-CMAC(K, <zero>) */ 3562306a36Sopenharmony_ci crypto_shash_digest(desc, tmp, AES_BLOCK_SIZE, d); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci for (i = 0; i < num_elem - 1; i++) { 3862306a36Sopenharmony_ci /* D = dbl(D) xor AES_CMAC(K, Si) */ 3962306a36Sopenharmony_ci gf_mulx(d); /* dbl */ 4062306a36Sopenharmony_ci crypto_shash_digest(desc, addr[i], len[i], tmp); 4162306a36Sopenharmony_ci crypto_xor(d, tmp, AES_BLOCK_SIZE); 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci crypto_shash_init(desc); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (len[i] >= AES_BLOCK_SIZE) { 4762306a36Sopenharmony_ci /* len(Sn) >= 128 */ 4862306a36Sopenharmony_ci /* T = Sn xorend D */ 4962306a36Sopenharmony_ci crypto_shash_update(desc, addr[i], len[i] - AES_BLOCK_SIZE); 5062306a36Sopenharmony_ci crypto_xor(d, addr[i] + len[i] - AES_BLOCK_SIZE, 5162306a36Sopenharmony_ci AES_BLOCK_SIZE); 5262306a36Sopenharmony_ci } else { 5362306a36Sopenharmony_ci /* len(Sn) < 128 */ 5462306a36Sopenharmony_ci /* T = dbl(D) xor pad(Sn) */ 5562306a36Sopenharmony_ci gf_mulx(d); /* dbl */ 5662306a36Sopenharmony_ci crypto_xor(d, addr[i], len[i]); 5762306a36Sopenharmony_ci d[len[i]] ^= 0x80; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci /* V = AES-CMAC(K, T) */ 6062306a36Sopenharmony_ci crypto_shash_finup(desc, d, AES_BLOCK_SIZE, v); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci return 0; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* Note: addr[] and len[] needs to have one extra slot at the end. */ 6662306a36Sopenharmony_cistatic int aes_siv_encrypt(const u8 *key, size_t key_len, 6762306a36Sopenharmony_ci const u8 *plain, size_t plain_len, 6862306a36Sopenharmony_ci size_t num_elem, const u8 *addr[], 6962306a36Sopenharmony_ci size_t len[], u8 *out) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci u8 v[AES_BLOCK_SIZE]; 7262306a36Sopenharmony_ci struct crypto_shash *tfm; 7362306a36Sopenharmony_ci struct crypto_skcipher *tfm2; 7462306a36Sopenharmony_ci struct skcipher_request *req; 7562306a36Sopenharmony_ci int res; 7662306a36Sopenharmony_ci struct scatterlist src[1], dst[1]; 7762306a36Sopenharmony_ci u8 *tmp; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci key_len /= 2; /* S2V key || CTR key */ 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci addr[num_elem] = plain; 8262306a36Sopenharmony_ci len[num_elem] = plain_len; 8362306a36Sopenharmony_ci num_elem++; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* S2V */ 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci tfm = crypto_alloc_shash("cmac(aes)", 0, 0); 8862306a36Sopenharmony_ci if (IS_ERR(tfm)) 8962306a36Sopenharmony_ci return PTR_ERR(tfm); 9062306a36Sopenharmony_ci /* K1 for S2V */ 9162306a36Sopenharmony_ci res = crypto_shash_setkey(tfm, key, key_len); 9262306a36Sopenharmony_ci if (!res) 9362306a36Sopenharmony_ci res = aes_s2v(tfm, num_elem, addr, len, v); 9462306a36Sopenharmony_ci crypto_free_shash(tfm); 9562306a36Sopenharmony_ci if (res) 9662306a36Sopenharmony_ci return res; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* Use a temporary buffer of the plaintext to handle need for 9962306a36Sopenharmony_ci * overwriting this during AES-CTR. 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_ci tmp = kmemdup(plain, plain_len, GFP_KERNEL); 10262306a36Sopenharmony_ci if (!tmp) 10362306a36Sopenharmony_ci return -ENOMEM; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* IV for CTR before encrypted data */ 10662306a36Sopenharmony_ci memcpy(out, v, AES_BLOCK_SIZE); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* Synthetic IV to be used as the initial counter in CTR: 10962306a36Sopenharmony_ci * Q = V bitand (1^64 || 0^1 || 1^31 || 0^1 || 1^31) 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_ci v[8] &= 0x7f; 11262306a36Sopenharmony_ci v[12] &= 0x7f; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* CTR */ 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci tfm2 = crypto_alloc_skcipher("ctr(aes)", 0, CRYPTO_ALG_ASYNC); 11762306a36Sopenharmony_ci if (IS_ERR(tfm2)) { 11862306a36Sopenharmony_ci kfree(tmp); 11962306a36Sopenharmony_ci return PTR_ERR(tfm2); 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci /* K2 for CTR */ 12262306a36Sopenharmony_ci res = crypto_skcipher_setkey(tfm2, key + key_len, key_len); 12362306a36Sopenharmony_ci if (res) 12462306a36Sopenharmony_ci goto fail; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci req = skcipher_request_alloc(tfm2, GFP_KERNEL); 12762306a36Sopenharmony_ci if (!req) { 12862306a36Sopenharmony_ci res = -ENOMEM; 12962306a36Sopenharmony_ci goto fail; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci sg_init_one(src, tmp, plain_len); 13362306a36Sopenharmony_ci sg_init_one(dst, out + AES_BLOCK_SIZE, plain_len); 13462306a36Sopenharmony_ci skcipher_request_set_crypt(req, src, dst, plain_len, v); 13562306a36Sopenharmony_ci res = crypto_skcipher_encrypt(req); 13662306a36Sopenharmony_ci skcipher_request_free(req); 13762306a36Sopenharmony_cifail: 13862306a36Sopenharmony_ci kfree(tmp); 13962306a36Sopenharmony_ci crypto_free_skcipher(tfm2); 14062306a36Sopenharmony_ci return res; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/* Note: addr[] and len[] needs to have one extra slot at the end. */ 14462306a36Sopenharmony_cistatic int aes_siv_decrypt(const u8 *key, size_t key_len, 14562306a36Sopenharmony_ci const u8 *iv_crypt, size_t iv_c_len, 14662306a36Sopenharmony_ci size_t num_elem, const u8 *addr[], size_t len[], 14762306a36Sopenharmony_ci u8 *out) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct crypto_shash *tfm; 15062306a36Sopenharmony_ci struct crypto_skcipher *tfm2; 15162306a36Sopenharmony_ci struct skcipher_request *req; 15262306a36Sopenharmony_ci struct scatterlist src[1], dst[1]; 15362306a36Sopenharmony_ci size_t crypt_len; 15462306a36Sopenharmony_ci int res; 15562306a36Sopenharmony_ci u8 frame_iv[AES_BLOCK_SIZE], iv[AES_BLOCK_SIZE]; 15662306a36Sopenharmony_ci u8 check[AES_BLOCK_SIZE]; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci crypt_len = iv_c_len - AES_BLOCK_SIZE; 15962306a36Sopenharmony_ci key_len /= 2; /* S2V key || CTR key */ 16062306a36Sopenharmony_ci addr[num_elem] = out; 16162306a36Sopenharmony_ci len[num_elem] = crypt_len; 16262306a36Sopenharmony_ci num_elem++; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci memcpy(iv, iv_crypt, AES_BLOCK_SIZE); 16562306a36Sopenharmony_ci memcpy(frame_iv, iv_crypt, AES_BLOCK_SIZE); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* Synthetic IV to be used as the initial counter in CTR: 16862306a36Sopenharmony_ci * Q = V bitand (1^64 || 0^1 || 1^31 || 0^1 || 1^31) 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_ci iv[8] &= 0x7f; 17162306a36Sopenharmony_ci iv[12] &= 0x7f; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* CTR */ 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci tfm2 = crypto_alloc_skcipher("ctr(aes)", 0, CRYPTO_ALG_ASYNC); 17662306a36Sopenharmony_ci if (IS_ERR(tfm2)) 17762306a36Sopenharmony_ci return PTR_ERR(tfm2); 17862306a36Sopenharmony_ci /* K2 for CTR */ 17962306a36Sopenharmony_ci res = crypto_skcipher_setkey(tfm2, key + key_len, key_len); 18062306a36Sopenharmony_ci if (res) { 18162306a36Sopenharmony_ci crypto_free_skcipher(tfm2); 18262306a36Sopenharmony_ci return res; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci req = skcipher_request_alloc(tfm2, GFP_KERNEL); 18662306a36Sopenharmony_ci if (!req) { 18762306a36Sopenharmony_ci crypto_free_skcipher(tfm2); 18862306a36Sopenharmony_ci return -ENOMEM; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci sg_init_one(src, iv_crypt + AES_BLOCK_SIZE, crypt_len); 19262306a36Sopenharmony_ci sg_init_one(dst, out, crypt_len); 19362306a36Sopenharmony_ci skcipher_request_set_crypt(req, src, dst, crypt_len, iv); 19462306a36Sopenharmony_ci res = crypto_skcipher_decrypt(req); 19562306a36Sopenharmony_ci skcipher_request_free(req); 19662306a36Sopenharmony_ci crypto_free_skcipher(tfm2); 19762306a36Sopenharmony_ci if (res) 19862306a36Sopenharmony_ci return res; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* S2V */ 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci tfm = crypto_alloc_shash("cmac(aes)", 0, 0); 20362306a36Sopenharmony_ci if (IS_ERR(tfm)) 20462306a36Sopenharmony_ci return PTR_ERR(tfm); 20562306a36Sopenharmony_ci /* K1 for S2V */ 20662306a36Sopenharmony_ci res = crypto_shash_setkey(tfm, key, key_len); 20762306a36Sopenharmony_ci if (!res) 20862306a36Sopenharmony_ci res = aes_s2v(tfm, num_elem, addr, len, check); 20962306a36Sopenharmony_ci crypto_free_shash(tfm); 21062306a36Sopenharmony_ci if (res) 21162306a36Sopenharmony_ci return res; 21262306a36Sopenharmony_ci if (memcmp(check, frame_iv, AES_BLOCK_SIZE) != 0) 21362306a36Sopenharmony_ci return -EINVAL; 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ciint fils_encrypt_assoc_req(struct sk_buff *skb, 21862306a36Sopenharmony_ci struct ieee80211_mgd_assoc_data *assoc_data) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt = (void *)skb->data; 22162306a36Sopenharmony_ci u8 *capab, *ies, *encr; 22262306a36Sopenharmony_ci const u8 *addr[5 + 1]; 22362306a36Sopenharmony_ci const struct element *session; 22462306a36Sopenharmony_ci size_t len[5 + 1]; 22562306a36Sopenharmony_ci size_t crypt_len; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (ieee80211_is_reassoc_req(mgmt->frame_control)) { 22862306a36Sopenharmony_ci capab = (u8 *)&mgmt->u.reassoc_req.capab_info; 22962306a36Sopenharmony_ci ies = mgmt->u.reassoc_req.variable; 23062306a36Sopenharmony_ci } else { 23162306a36Sopenharmony_ci capab = (u8 *)&mgmt->u.assoc_req.capab_info; 23262306a36Sopenharmony_ci ies = mgmt->u.assoc_req.variable; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci session = cfg80211_find_ext_elem(WLAN_EID_EXT_FILS_SESSION, 23662306a36Sopenharmony_ci ies, skb->data + skb->len - ies); 23762306a36Sopenharmony_ci if (!session || session->datalen != 1 + 8) 23862306a36Sopenharmony_ci return -EINVAL; 23962306a36Sopenharmony_ci /* encrypt after FILS Session element */ 24062306a36Sopenharmony_ci encr = (u8 *)session->data + 1 + 8; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* AES-SIV AAD vectors */ 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* The STA's MAC address */ 24562306a36Sopenharmony_ci addr[0] = mgmt->sa; 24662306a36Sopenharmony_ci len[0] = ETH_ALEN; 24762306a36Sopenharmony_ci /* The AP's BSSID */ 24862306a36Sopenharmony_ci addr[1] = mgmt->da; 24962306a36Sopenharmony_ci len[1] = ETH_ALEN; 25062306a36Sopenharmony_ci /* The STA's nonce */ 25162306a36Sopenharmony_ci addr[2] = assoc_data->fils_nonces; 25262306a36Sopenharmony_ci len[2] = FILS_NONCE_LEN; 25362306a36Sopenharmony_ci /* The AP's nonce */ 25462306a36Sopenharmony_ci addr[3] = &assoc_data->fils_nonces[FILS_NONCE_LEN]; 25562306a36Sopenharmony_ci len[3] = FILS_NONCE_LEN; 25662306a36Sopenharmony_ci /* The (Re)Association Request frame from the Capability Information 25762306a36Sopenharmony_ci * field to the FILS Session element (both inclusive). 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_ci addr[4] = capab; 26062306a36Sopenharmony_ci len[4] = encr - capab; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci crypt_len = skb->data + skb->len - encr; 26362306a36Sopenharmony_ci skb_put(skb, AES_BLOCK_SIZE); 26462306a36Sopenharmony_ci return aes_siv_encrypt(assoc_data->fils_kek, assoc_data->fils_kek_len, 26562306a36Sopenharmony_ci encr, crypt_len, 5, addr, len, encr); 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ciint fils_decrypt_assoc_resp(struct ieee80211_sub_if_data *sdata, 26962306a36Sopenharmony_ci u8 *frame, size_t *frame_len, 27062306a36Sopenharmony_ci struct ieee80211_mgd_assoc_data *assoc_data) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt = (void *)frame; 27362306a36Sopenharmony_ci u8 *capab, *ies, *encr; 27462306a36Sopenharmony_ci const u8 *addr[5 + 1]; 27562306a36Sopenharmony_ci const struct element *session; 27662306a36Sopenharmony_ci size_t len[5 + 1]; 27762306a36Sopenharmony_ci int res; 27862306a36Sopenharmony_ci size_t crypt_len; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (*frame_len < 24 + 6) 28162306a36Sopenharmony_ci return -EINVAL; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci capab = (u8 *)&mgmt->u.assoc_resp.capab_info; 28462306a36Sopenharmony_ci ies = mgmt->u.assoc_resp.variable; 28562306a36Sopenharmony_ci session = cfg80211_find_ext_elem(WLAN_EID_EXT_FILS_SESSION, 28662306a36Sopenharmony_ci ies, frame + *frame_len - ies); 28762306a36Sopenharmony_ci if (!session || session->datalen != 1 + 8) { 28862306a36Sopenharmony_ci mlme_dbg(sdata, 28962306a36Sopenharmony_ci "No (valid) FILS Session element in (Re)Association Response frame from %pM", 29062306a36Sopenharmony_ci mgmt->sa); 29162306a36Sopenharmony_ci return -EINVAL; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci /* decrypt after FILS Session element */ 29462306a36Sopenharmony_ci encr = (u8 *)session->data + 1 + 8; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* AES-SIV AAD vectors */ 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* The AP's BSSID */ 29962306a36Sopenharmony_ci addr[0] = mgmt->sa; 30062306a36Sopenharmony_ci len[0] = ETH_ALEN; 30162306a36Sopenharmony_ci /* The STA's MAC address */ 30262306a36Sopenharmony_ci addr[1] = mgmt->da; 30362306a36Sopenharmony_ci len[1] = ETH_ALEN; 30462306a36Sopenharmony_ci /* The AP's nonce */ 30562306a36Sopenharmony_ci addr[2] = &assoc_data->fils_nonces[FILS_NONCE_LEN]; 30662306a36Sopenharmony_ci len[2] = FILS_NONCE_LEN; 30762306a36Sopenharmony_ci /* The STA's nonce */ 30862306a36Sopenharmony_ci addr[3] = assoc_data->fils_nonces; 30962306a36Sopenharmony_ci len[3] = FILS_NONCE_LEN; 31062306a36Sopenharmony_ci /* The (Re)Association Response frame from the Capability Information 31162306a36Sopenharmony_ci * field to the FILS Session element (both inclusive). 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_ci addr[4] = capab; 31462306a36Sopenharmony_ci len[4] = encr - capab; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci crypt_len = frame + *frame_len - encr; 31762306a36Sopenharmony_ci if (crypt_len < AES_BLOCK_SIZE) { 31862306a36Sopenharmony_ci mlme_dbg(sdata, 31962306a36Sopenharmony_ci "Not enough room for AES-SIV data after FILS Session element in (Re)Association Response frame from %pM", 32062306a36Sopenharmony_ci mgmt->sa); 32162306a36Sopenharmony_ci return -EINVAL; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci res = aes_siv_decrypt(assoc_data->fils_kek, assoc_data->fils_kek_len, 32462306a36Sopenharmony_ci encr, crypt_len, 5, addr, len, encr); 32562306a36Sopenharmony_ci if (res != 0) { 32662306a36Sopenharmony_ci mlme_dbg(sdata, 32762306a36Sopenharmony_ci "AES-SIV decryption of (Re)Association Response frame from %pM failed", 32862306a36Sopenharmony_ci mgmt->sa); 32962306a36Sopenharmony_ci return res; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci *frame_len -= AES_BLOCK_SIZE; 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci} 334