[PATCH 24/33] wifi: mac80211: Use AES-GCM library for GMAC suite
From: Eric Biggers
Date: Tue Jul 07 2026 - 01:41:51 EST
Now that there's a library API for AES-GCM (of which AES-GMAC is a
special case), for implementing the GMAC cipher suite use it instead of
a "gcm(aes)" crypto_aead. This significantly simplifies the code and
eliminates per-skb heap allocations.
As a bonus, ieee80211_crypto_aes_gmac_decrypt() no longer needs to
allocate 'mic' on the heap, since it no longer needs to be representable
as a scatterlist for crypto_aead.
Signed-off-by: Eric Biggers <ebiggers@xxxxxxxxxx>
---
net/mac80211/Kconfig | 1 +
net/mac80211/aes_gmac.c | 85 ++++++++---------------------------------
net/mac80211/aes_gmac.h | 10 ++---
net/mac80211/key.c | 11 ++----
net/mac80211/key.h | 3 +-
net/mac80211/wpa.c | 12 ++----
6 files changed, 29 insertions(+), 93 deletions(-)
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index 8fe97e63ff39..32808c5de0fb 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -5,6 +5,7 @@ config MAC80211
select CRYPTO
select CRYPTO_LIB_AES_CBC_MACS
select CRYPTO_LIB_AES_CTR
+ select CRYPTO_LIB_AES_GCM
select CRYPTO_LIB_ARC4
select CRYPTO_AES
select CRYPTO_CCM
diff --git a/net/mac80211/aes_gmac.c b/net/mac80211/aes_gmac.c
index 811a83d8d525..722d8983bb5c 100644
--- a/net/mac80211/aes_gmac.c
+++ b/net/mac80211/aes_gmac.c
@@ -6,89 +6,34 @@
#include <linux/kernel.h>
#include <linux/types.h>
-#include <linux/err.h>
-#include <crypto/aead.h>
-#include <crypto/aes.h>
+#include <crypto/aes-gcm.h>
#include <net/mac80211.h>
-#include "key.h"
#include "aes_gmac.h"
-int ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce,
- const u8 *data, size_t data_len, u8 *mic)
+int ieee80211_aes_gmac(const struct aes_gcm_key *key, const u8 *aad,
+ const u8 *nonce, const u8 *data, size_t data_len,
+ u8 *mic)
{
- struct scatterlist sg[5];
- u8 *zero, *__aad, iv[AES_BLOCK_SIZE];
- struct aead_request *aead_req;
- int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm);
+ static const u8 zero[IEEE80211_GMAC_MIC_LEN];
+ struct aes_gcm_ctx ctx;
const __le16 *fc;
- int ret;
if (data_len < IEEE80211_GMAC_MIC_LEN)
return -EINVAL;
- aead_req = kzalloc(reqsize + IEEE80211_GMAC_MIC_LEN + GMAC_AAD_LEN,
- GFP_ATOMIC);
- if (!aead_req)
- return -ENOMEM;
-
- zero = (u8 *)aead_req + reqsize;
- __aad = zero + IEEE80211_GMAC_MIC_LEN;
- memcpy(__aad, aad, GMAC_AAD_LEN);
+ aes_gcm_init(&ctx, nonce, key);
+ aes_gcm_auth_update(&ctx, aad, GMAC_AAD_LEN);
fc = (const __le16 *)aad;
if (ieee80211_is_beacon(*fc)) {
/* mask Timestamp field to zero */
- sg_init_table(sg, 5);
- sg_set_buf(&sg[0], __aad, GMAC_AAD_LEN);
- sg_set_buf(&sg[1], zero, 8);
- sg_set_buf(&sg[2], data + 8,
- data_len - 8 - IEEE80211_GMAC_MIC_LEN);
- sg_set_buf(&sg[3], zero, IEEE80211_GMAC_MIC_LEN);
- sg_set_buf(&sg[4], mic, IEEE80211_GMAC_MIC_LEN);
- } else {
- sg_init_table(sg, 4);
- sg_set_buf(&sg[0], __aad, GMAC_AAD_LEN);
- sg_set_buf(&sg[1], data, data_len - IEEE80211_GMAC_MIC_LEN);
- sg_set_buf(&sg[2], zero, IEEE80211_GMAC_MIC_LEN);
- sg_set_buf(&sg[3], mic, IEEE80211_GMAC_MIC_LEN);
+ aes_gcm_auth_update(&ctx, zero, 8);
+ data += 8;
+ data_len -= 8;
}
-
- memcpy(iv, nonce, GMAC_NONCE_LEN);
- memset(iv + GMAC_NONCE_LEN, 0, sizeof(iv) - GMAC_NONCE_LEN);
- iv[AES_BLOCK_SIZE - 1] = 0x01;
-
- aead_request_set_tfm(aead_req, tfm);
- aead_request_set_crypt(aead_req, sg, sg, 0, iv);
- aead_request_set_ad(aead_req, GMAC_AAD_LEN + data_len);
-
- ret = crypto_aead_encrypt(aead_req);
- kfree_sensitive(aead_req);
-
- return ret;
-}
-
-struct crypto_aead *ieee80211_aes_gmac_key_setup(const u8 key[],
- size_t key_len)
-{
- struct crypto_aead *tfm;
- int err;
-
- tfm = crypto_alloc_aead("gcm(aes)", 0, CRYPTO_ALG_ASYNC);
- if (IS_ERR(tfm))
- return tfm;
-
- err = crypto_aead_setkey(tfm, key, key_len);
- if (!err)
- err = crypto_aead_setauthsize(tfm, IEEE80211_GMAC_MIC_LEN);
- if (!err)
- return tfm;
-
- crypto_free_aead(tfm);
- return ERR_PTR(err);
-}
-
-void ieee80211_aes_gmac_key_free(struct crypto_aead *tfm)
-{
- crypto_free_aead(tfm);
+ aes_gcm_auth_update(&ctx, data, data_len - IEEE80211_GMAC_MIC_LEN);
+ aes_gcm_auth_update(&ctx, zero, IEEE80211_GMAC_MIC_LEN);
+ aes_gcm_encrypt_final(&ctx, mic);
+ return 0;
}
diff --git a/net/mac80211/aes_gmac.h b/net/mac80211/aes_gmac.h
index 206136b60bca..f31bdfbecf3c 100644
--- a/net/mac80211/aes_gmac.h
+++ b/net/mac80211/aes_gmac.h
@@ -6,15 +6,13 @@
#ifndef AES_GMAC_H
#define AES_GMAC_H
-#include <linux/crypto.h>
+#include <crypto/aes-gcm.h>
#define GMAC_AAD_LEN 20
#define GMAC_NONCE_LEN 12
-struct crypto_aead *ieee80211_aes_gmac_key_setup(const u8 key[],
- size_t key_len);
-int ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce,
- const u8 *data, size_t data_len, u8 *mic);
-void ieee80211_aes_gmac_key_free(struct crypto_aead *tfm);
+int ieee80211_aes_gmac(const struct aes_gcm_key *key, const u8 *aad,
+ const u8 *nonce, const u8 *data, size_t data_len,
+ u8 *mic);
#endif /* AES_GMAC_H */
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index f45e792abede..48404097e4f1 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -711,10 +711,9 @@ ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
/* Initialize AES key state here as an optimization so that
* it does not need to be initialized for every packet.
*/
- key->u.aes_gmac.tfm =
- ieee80211_aes_gmac_key_setup(key_data, key_len);
- if (IS_ERR(key->u.aes_gmac.tfm)) {
- err = PTR_ERR(key->u.aes_gmac.tfm);
+ err = aes_gcm_preparekey(&key->u.aes_gmac.key, key_data,
+ key_len, IEEE80211_GMAC_MIC_LEN);
+ if (err) {
kfree(key);
return ERR_PTR(err);
}
@@ -752,10 +751,6 @@ static void ieee80211_key_free_common(struct ieee80211_key *key)
case WLAN_CIPHER_SUITE_CCMP_256:
ieee80211_aes_key_free(key->u.ccmp.tfm);
break;
- case WLAN_CIPHER_SUITE_BIP_GMAC_128:
- case WLAN_CIPHER_SUITE_BIP_GMAC_256:
- ieee80211_aes_gmac_key_free(key->u.aes_gmac.tfm);
- break;
case WLAN_CIPHER_SUITE_GCMP:
case WLAN_CIPHER_SUITE_GCMP_256:
ieee80211_aes_gcm_key_free(key->u.gcmp.tfm);
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index 826e4e9387c5..d2dd2a76fa25 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -13,6 +13,7 @@
#include <linux/crypto.h>
#include <linux/rcupdate.h>
#include <crypto/aes-cbc-macs.h>
+#include <crypto/aes-gcm.h>
#include <crypto/arc4.h>
#include <net/mac80211.h>
@@ -100,7 +101,7 @@ struct ieee80211_key {
} aes_cmac;
struct {
u8 rx_pn[IEEE80211_GMAC_PN_LEN];
- struct crypto_aead *tfm;
+ struct aes_gcm_key key;
u32 replays; /* dot11RSNAStatsCMACReplays */
u32 icverrors; /* dot11RSNAStatsCMACICVErrors */
} aes_gmac;
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index be3a2e95303c..eb4a98537395 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -1007,7 +1007,7 @@ ieee80211_crypto_aes_gmac_encrypt(struct ieee80211_tx_data *tx)
bip_ipn_swap(nonce + ETH_ALEN, mmie->sequence_number);
/* MIC = AES-GMAC(IGTK, AAD || Management Frame Body || MMIE, 128) */
- if (ieee80211_aes_gmac(key->u.aes_gmac.tfm, aad, nonce,
+ if (ieee80211_aes_gmac(&key->u.aes_gmac.key, aad, nonce,
skb->data + 24, skb->len - 24, mmie->mic) < 0)
return TX_DROP;
@@ -1021,7 +1021,8 @@ ieee80211_crypto_aes_gmac_decrypt(struct ieee80211_rx_data *rx)
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_key *key = rx->key;
struct ieee80211_mmie_16 *mmie;
- u8 aad[GMAC_AAD_LEN], *mic, ipn[6], nonce[GMAC_NONCE_LEN];
+ u8 aad[GMAC_AAD_LEN], ipn[6], nonce[GMAC_NONCE_LEN];
+ u8 mic[IEEE80211_GMAC_MIC_LEN];
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
if (!ieee80211_is_mgmt(hdr->frame_control))
@@ -1052,18 +1053,13 @@ ieee80211_crypto_aes_gmac_decrypt(struct ieee80211_rx_data *rx)
memcpy(nonce, hdr->addr2, ETH_ALEN);
memcpy(nonce + ETH_ALEN, ipn, 6);
- mic = kmalloc(IEEE80211_GMAC_MIC_LEN, GFP_ATOMIC);
- if (!mic)
- return RX_DROP_U_OOM;
- if (ieee80211_aes_gmac(key->u.aes_gmac.tfm, aad, nonce,
+ if (ieee80211_aes_gmac(&key->u.aes_gmac.key, aad, nonce,
skb->data + 24, skb->len - 24,
mic) < 0 ||
crypto_memneq(mic, mmie->mic, sizeof(mmie->mic))) {
key->u.aes_gmac.icverrors++;
- kfree(mic);
return RX_DROP_U_MIC_FAIL;
}
- kfree(mic);
}
memcpy(key->u.aes_gmac.rx_pn, ipn, 6);
--
2.54.0