Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mac80211: FILS AEAD protection for station mode association frames

This adds support for encrypting (Re)Association Request frame and
decryption (Re)Association Response frame when using FILS in station
mode.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>

authored by

Jouni Malinen and committed by
Johannes Berg
39404fee dbc0c2cb

+403 -4
+1
net/mac80211/Makefile
··· 19 19 aes_gcm.o \ 20 20 aes_cmac.o \ 21 21 aes_gmac.o \ 22 + fils_aead.o \ 22 23 cfg.o \ 23 24 ethtool.o \ 24 25 rx.o \
+4 -4
net/mac80211/aes_cmac.c
··· 23 23 #define AAD_LEN 20 24 24 25 25 26 - static void gf_mulx(u8 *pad) 26 + void gf_mulx(u8 *pad) 27 27 { 28 28 int i, carry; 29 29 ··· 35 35 pad[AES_BLOCK_SIZE - 1] ^= 0x87; 36 36 } 37 37 38 - static void aes_cmac_vector(struct crypto_cipher *tfm, size_t num_elem, 39 - const u8 *addr[], const size_t *len, u8 *mac, 40 - size_t mac_len) 38 + void aes_cmac_vector(struct crypto_cipher *tfm, size_t num_elem, 39 + const u8 *addr[], const size_t *len, u8 *mac, 40 + size_t mac_len) 41 41 { 42 42 u8 cbc[AES_BLOCK_SIZE], pad[AES_BLOCK_SIZE]; 43 43 const u8 *pos, *end;
+4
net/mac80211/aes_cmac.h
··· 11 11 12 12 #include <linux/crypto.h> 13 13 14 + void gf_mulx(u8 *pad); 15 + void aes_cmac_vector(struct crypto_cipher *tfm, size_t num_elem, 16 + const u8 *addr[], const size_t *len, u8 *mac, 17 + size_t mac_len); 14 18 struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[], 15 19 size_t key_len); 16 20 void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad,
+344
net/mac80211/fils_aead.c
··· 1 + /* 2 + * FILS AEAD for (Re)Association Request/Response frames 3 + * Copyright 2016, Qualcomm Atheros, Inc. 4 + * 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. 8 + */ 9 + 10 + #include <crypto/aes.h> 11 + #include <crypto/algapi.h> 12 + #include <crypto/skcipher.h> 13 + 14 + #include "ieee80211_i.h" 15 + #include "aes_cmac.h" 16 + #include "fils_aead.h" 17 + 18 + static int aes_s2v(struct crypto_cipher *tfm, 19 + size_t num_elem, const u8 *addr[], size_t len[], u8 *v) 20 + { 21 + u8 d[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE]; 22 + size_t i; 23 + const u8 *data[2]; 24 + size_t data_len[2], data_elems; 25 + 26 + /* D = AES-CMAC(K, <zero>) */ 27 + memset(tmp, 0, AES_BLOCK_SIZE); 28 + data[0] = tmp; 29 + data_len[0] = AES_BLOCK_SIZE; 30 + aes_cmac_vector(tfm, 1, data, data_len, d, AES_BLOCK_SIZE); 31 + 32 + for (i = 0; i < num_elem - 1; i++) { 33 + /* D = dbl(D) xor AES_CMAC(K, Si) */ 34 + gf_mulx(d); /* dbl */ 35 + aes_cmac_vector(tfm, 1, &addr[i], &len[i], tmp, 36 + AES_BLOCK_SIZE); 37 + crypto_xor(d, tmp, AES_BLOCK_SIZE); 38 + } 39 + 40 + if (len[i] >= AES_BLOCK_SIZE) { 41 + /* len(Sn) >= 128 */ 42 + size_t j; 43 + const u8 *pos; 44 + 45 + /* T = Sn xorend D */ 46 + 47 + /* Use a temporary buffer to perform xorend on Sn (addr[i]) to 48 + * avoid modifying the const input argument. 49 + */ 50 + data[0] = addr[i]; 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]; 55 + data[1] = tmp; 56 + data_len[1] = AES_BLOCK_SIZE; 57 + data_elems = 2; 58 + } else { 59 + /* len(Sn) < 128 */ 60 + /* T = dbl(D) xor pad(Sn) */ 61 + gf_mulx(d); /* dbl */ 62 + memset(tmp, 0, AES_BLOCK_SIZE); 63 + memcpy(tmp, addr[i], len[i]); 64 + tmp[len[i]] = 0x80; 65 + crypto_xor(d, tmp, AES_BLOCK_SIZE); 66 + data[0] = d; 67 + data_len[0] = sizeof(d); 68 + data_elems = 1; 69 + } 70 + /* V = AES-CMAC(K, T) */ 71 + aes_cmac_vector(tfm, data_elems, data, data_len, v, AES_BLOCK_SIZE); 72 + 73 + return 0; 74 + } 75 + 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) 81 + { 82 + u8 v[AES_BLOCK_SIZE]; 83 + struct crypto_cipher *tfm; 84 + struct crypto_skcipher *tfm2; 85 + struct skcipher_request *req; 86 + int res; 87 + struct scatterlist src[1], dst[1]; 88 + u8 *tmp; 89 + 90 + key_len /= 2; /* S2V key || CTR key */ 91 + 92 + addr[num_elem] = plain; 93 + len[num_elem] = plain_len; 94 + num_elem++; 95 + 96 + /* S2V */ 97 + 98 + tfm = crypto_alloc_cipher("aes", 0, 0); 99 + if (IS_ERR(tfm)) 100 + return PTR_ERR(tfm); 101 + /* K1 for S2V */ 102 + res = crypto_cipher_setkey(tfm, key, key_len); 103 + if (!res) 104 + res = aes_s2v(tfm, num_elem, addr, len, v); 105 + crypto_free_cipher(tfm); 106 + if (res) 107 + return res; 108 + 109 + /* Use a temporary buffer of the plaintext to handle need for 110 + * overwriting this during AES-CTR. 111 + */ 112 + tmp = kmemdup(plain, plain_len, GFP_KERNEL); 113 + if (!tmp) { 114 + res = -ENOMEM; 115 + goto fail; 116 + } 117 + 118 + /* IV for CTR before encrypted data */ 119 + memcpy(out, v, AES_BLOCK_SIZE); 120 + 121 + /* Synthetic IV to be used as the initial counter in CTR: 122 + * Q = V bitand (1^64 || 0^1 || 1^31 || 0^1 || 1^31) 123 + */ 124 + v[8] &= 0x7f; 125 + v[12] &= 0x7f; 126 + 127 + /* CTR */ 128 + 129 + tfm2 = crypto_alloc_skcipher("ctr(aes)", 0, 0); 130 + if (IS_ERR(tfm2)) { 131 + kfree(tmp); 132 + return PTR_ERR(tfm2); 133 + } 134 + /* K2 for CTR */ 135 + res = crypto_skcipher_setkey(tfm2, key + key_len, key_len); 136 + if (res) 137 + goto fail; 138 + 139 + req = skcipher_request_alloc(tfm2, GFP_KERNEL); 140 + if (!req) { 141 + res = -ENOMEM; 142 + goto fail; 143 + } 144 + 145 + sg_init_one(src, tmp, plain_len); 146 + sg_init_one(dst, out + AES_BLOCK_SIZE, plain_len); 147 + skcipher_request_set_crypt(req, src, dst, plain_len, v); 148 + res = crypto_skcipher_encrypt(req); 149 + skcipher_request_free(req); 150 + fail: 151 + kfree(tmp); 152 + crypto_free_skcipher(tfm2); 153 + return res; 154 + } 155 + 156 + /* Note: addr[] and len[] needs to have one extra slot at the end. */ 157 + static int aes_siv_decrypt(const u8 *key, size_t key_len, 158 + const u8 *iv_crypt, size_t iv_c_len, 159 + size_t num_elem, const u8 *addr[], size_t len[], 160 + u8 *out) 161 + { 162 + struct crypto_cipher *tfm; 163 + struct crypto_skcipher *tfm2; 164 + struct skcipher_request *req; 165 + struct scatterlist src[1], dst[1]; 166 + size_t crypt_len; 167 + int res; 168 + u8 frame_iv[AES_BLOCK_SIZE], iv[AES_BLOCK_SIZE]; 169 + u8 check[AES_BLOCK_SIZE]; 170 + 171 + crypt_len = iv_c_len - AES_BLOCK_SIZE; 172 + key_len /= 2; /* S2V key || CTR key */ 173 + addr[num_elem] = out; 174 + len[num_elem] = crypt_len; 175 + num_elem++; 176 + 177 + memcpy(iv, iv_crypt, AES_BLOCK_SIZE); 178 + memcpy(frame_iv, iv_crypt, AES_BLOCK_SIZE); 179 + 180 + /* Synthetic IV to be used as the initial counter in CTR: 181 + * Q = V bitand (1^64 || 0^1 || 1^31 || 0^1 || 1^31) 182 + */ 183 + iv[8] &= 0x7f; 184 + iv[12] &= 0x7f; 185 + 186 + /* CTR */ 187 + 188 + tfm2 = crypto_alloc_skcipher("ctr(aes)", 0, 0); 189 + if (IS_ERR(tfm2)) 190 + return PTR_ERR(tfm2); 191 + /* K2 for CTR */ 192 + res = crypto_skcipher_setkey(tfm2, key + key_len, key_len); 193 + if (res) { 194 + crypto_free_skcipher(tfm2); 195 + return res; 196 + } 197 + 198 + req = skcipher_request_alloc(tfm2, GFP_KERNEL); 199 + if (!req) { 200 + crypto_free_skcipher(tfm2); 201 + return -ENOMEM; 202 + } 203 + 204 + sg_init_one(src, iv_crypt + AES_BLOCK_SIZE, crypt_len); 205 + sg_init_one(dst, out, crypt_len); 206 + skcipher_request_set_crypt(req, src, dst, crypt_len, iv); 207 + res = crypto_skcipher_decrypt(req); 208 + skcipher_request_free(req); 209 + crypto_free_skcipher(tfm2); 210 + if (res) 211 + return res; 212 + 213 + /* S2V */ 214 + 215 + tfm = crypto_alloc_cipher("aes", 0, 0); 216 + if (IS_ERR(tfm)) 217 + return PTR_ERR(tfm); 218 + /* K1 for S2V */ 219 + res = crypto_cipher_setkey(tfm, key, key_len); 220 + if (!res) 221 + res = aes_s2v(tfm, num_elem, addr, len, check); 222 + crypto_free_cipher(tfm); 223 + if (res) 224 + return res; 225 + if (memcmp(check, frame_iv, AES_BLOCK_SIZE) != 0) 226 + return -EINVAL; 227 + return 0; 228 + } 229 + 230 + int fils_encrypt_assoc_req(struct sk_buff *skb, 231 + struct ieee80211_mgd_assoc_data *assoc_data) 232 + { 233 + struct ieee80211_mgmt *mgmt = (void *)skb->data; 234 + u8 *capab, *ies, *encr; 235 + const u8 *addr[5 + 1], *session; 236 + size_t len[5 + 1]; 237 + size_t crypt_len; 238 + 239 + if (ieee80211_is_reassoc_req(mgmt->frame_control)) { 240 + capab = (u8 *)&mgmt->u.reassoc_req.capab_info; 241 + ies = mgmt->u.reassoc_req.variable; 242 + } else { 243 + capab = (u8 *)&mgmt->u.assoc_req.capab_info; 244 + ies = mgmt->u.assoc_req.variable; 245 + } 246 + 247 + session = cfg80211_find_ext_ie(WLAN_EID_EXT_FILS_SESSION, 248 + ies, skb->data + skb->len - ies); 249 + if (!session || session[1] != 1 + 8) 250 + return -EINVAL; 251 + /* encrypt after FILS Session element */ 252 + encr = (u8 *)session + 2 + 1 + 8; 253 + 254 + /* AES-SIV AAD vectors */ 255 + 256 + /* The STA's MAC address */ 257 + addr[0] = mgmt->sa; 258 + len[0] = ETH_ALEN; 259 + /* The AP's BSSID */ 260 + addr[1] = mgmt->da; 261 + len[1] = ETH_ALEN; 262 + /* The STA's nonce */ 263 + addr[2] = assoc_data->fils_nonces; 264 + len[2] = FILS_NONCE_LEN; 265 + /* The AP's nonce */ 266 + addr[3] = &assoc_data->fils_nonces[FILS_NONCE_LEN]; 267 + len[3] = FILS_NONCE_LEN; 268 + /* The (Re)Association Request frame from the Capability Information 269 + * field to the FILS Session element (both inclusive). 270 + */ 271 + addr[4] = capab; 272 + len[4] = encr - capab; 273 + 274 + crypt_len = skb->data + skb->len - encr; 275 + skb_put(skb, AES_BLOCK_SIZE); 276 + return aes_siv_encrypt(assoc_data->fils_kek, assoc_data->fils_kek_len, 277 + encr, crypt_len, 1, addr, len, encr); 278 + } 279 + 280 + int fils_decrypt_assoc_resp(struct ieee80211_sub_if_data *sdata, 281 + u8 *frame, size_t *frame_len, 282 + struct ieee80211_mgd_assoc_data *assoc_data) 283 + { 284 + struct ieee80211_mgmt *mgmt = (void *)frame; 285 + u8 *capab, *ies, *encr; 286 + const u8 *addr[5 + 1], *session; 287 + size_t len[5 + 1]; 288 + int res; 289 + size_t crypt_len; 290 + 291 + if (*frame_len < 24 + 6) 292 + return -EINVAL; 293 + 294 + capab = (u8 *)&mgmt->u.assoc_resp.capab_info; 295 + ies = mgmt->u.assoc_resp.variable; 296 + session = cfg80211_find_ext_ie(WLAN_EID_EXT_FILS_SESSION, 297 + ies, frame + *frame_len - ies); 298 + if (!session || session[1] != 1 + 8) { 299 + mlme_dbg(sdata, 300 + "No (valid) FILS Session element in (Re)Association Response frame from %pM", 301 + mgmt->sa); 302 + return -EINVAL; 303 + } 304 + /* decrypt after FILS Session element */ 305 + encr = (u8 *)session + 2 + 1 + 8; 306 + 307 + /* AES-SIV AAD vectors */ 308 + 309 + /* The AP's BSSID */ 310 + addr[0] = mgmt->sa; 311 + len[0] = ETH_ALEN; 312 + /* The STA's MAC address */ 313 + addr[1] = mgmt->da; 314 + len[1] = ETH_ALEN; 315 + /* The AP's nonce */ 316 + addr[2] = &assoc_data->fils_nonces[FILS_NONCE_LEN]; 317 + len[2] = FILS_NONCE_LEN; 318 + /* The STA's nonce */ 319 + addr[3] = assoc_data->fils_nonces; 320 + len[3] = FILS_NONCE_LEN; 321 + /* The (Re)Association Response frame from the Capability Information 322 + * field to the FILS Session element (both inclusive). 323 + */ 324 + addr[4] = capab; 325 + len[4] = encr - capab; 326 + 327 + crypt_len = frame + *frame_len - encr; 328 + if (crypt_len < AES_BLOCK_SIZE) { 329 + mlme_dbg(sdata, 330 + "Not enough room for AES-SIV data after FILS Session element in (Re)Association Response frame from %pM", 331 + mgmt->sa); 332 + return -EINVAL; 333 + } 334 + res = aes_siv_decrypt(assoc_data->fils_kek, assoc_data->fils_kek_len, 335 + encr, crypt_len, 5, addr, len, encr); 336 + if (res != 0) { 337 + mlme_dbg(sdata, 338 + "AES-SIV decryption of (Re)Association Response frame from %pM failed", 339 + mgmt->sa); 340 + return res; 341 + } 342 + *frame_len -= AES_BLOCK_SIZE; 343 + return 0; 344 + }
+19
net/mac80211/fils_aead.h
··· 1 + /* 2 + * FILS AEAD for (Re)Association Request/Response frames 3 + * Copyright 2016, Qualcomm Atheros, Inc. 4 + * 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. 8 + */ 9 + 10 + #ifndef FILS_AEAD_H 11 + #define FILS_AEAD_H 12 + 13 + int fils_encrypt_assoc_req(struct sk_buff *skb, 14 + struct ieee80211_mgd_assoc_data *assoc_data); 15 + int fils_decrypt_assoc_resp(struct ieee80211_sub_if_data *sdata, 16 + u8 *frame, size_t *frame_len, 17 + struct ieee80211_mgd_assoc_data *assoc_data); 18 + 19 + #endif /* FILS_AEAD_H */
+4
net/mac80211/ieee80211_i.h
··· 401 401 402 402 struct ieee80211_vht_cap ap_vht_cap; 403 403 404 + u8 fils_nonces[2 * FILS_NONCE_LEN]; 405 + u8 fils_kek[FILS_MAX_KEK_LEN]; 406 + size_t fils_kek_len; 407 + 404 408 size_t ie_len; 405 409 u8 ie[]; 406 410 };
+27
net/mac80211/mlme.c
··· 30 30 #include "driver-ops.h" 31 31 #include "rate.h" 32 32 #include "led.h" 33 + #include "fils_aead.h" 33 34 34 35 #define IEEE80211_AUTH_TIMEOUT (HZ / 5) 35 36 #define IEEE80211_AUTH_TIMEOUT_LONG (HZ / 2) ··· 653 652 2 + sizeof(struct ieee80211_ht_cap) + /* HT */ 654 653 2 + sizeof(struct ieee80211_vht_cap) + /* VHT */ 655 654 assoc_data->ie_len + /* extra IEs */ 655 + (assoc_data->fils_kek_len ? 16 /* AES-SIV */ : 0) + 656 656 9, /* WMM */ 657 657 GFP_KERNEL); 658 658 if (!skb) ··· 875 873 noffset = assoc_data->ie_len; 876 874 pos = skb_put(skb, noffset - offset); 877 875 memcpy(pos, assoc_data->ie + offset, noffset - offset); 876 + } 877 + 878 + if (assoc_data->fils_kek_len && 879 + fils_encrypt_assoc_req(skb, assoc_data) < 0) { 880 + dev_kfree_skb(skb); 881 + return; 878 882 } 879 883 880 884 drv_mgd_prepare_tx(local, sdata); ··· 3154 3146 reassoc ? "Rea" : "A", mgmt->sa, 3155 3147 capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14)))); 3156 3148 3149 + if (assoc_data->fils_kek_len && 3150 + fils_decrypt_assoc_resp(sdata, (u8 *)mgmt, &len, assoc_data) < 0) 3151 + return; 3152 + 3157 3153 pos = mgmt->u.assoc_resp.variable; 3158 3154 ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems); 3159 3155 ··· 4718 4706 memcpy(assoc_data->ie, req->ie, req->ie_len); 4719 4707 assoc_data->ie_len = req->ie_len; 4720 4708 } 4709 + 4710 + if (req->fils_kek) { 4711 + /* should already be checked in cfg80211 - so warn */ 4712 + if (WARN_ON(req->fils_kek_len > FILS_MAX_KEK_LEN)) { 4713 + err = -EINVAL; 4714 + goto err_free; 4715 + } 4716 + memcpy(assoc_data->fils_kek, req->fils_kek, 4717 + req->fils_kek_len); 4718 + assoc_data->fils_kek_len = req->fils_kek_len; 4719 + } 4720 + 4721 + if (req->fils_nonces) 4722 + memcpy(assoc_data->fils_nonces, req->fils_nonces, 4723 + 2 * FILS_NONCE_LEN); 4721 4724 4722 4725 assoc_data->bss = req->bss; 4723 4726