Subject: [PATCH 14/15] wifi: mac80211: Use AES-CMAC library in ieee80211_aes_cmac()

From: Eric Biggers <[email-masked]>
Date: Wed, 18 Feb 2026 13:35:00 -0800

Now that AES-CMAC has a library API, convert the mac80211 AES-CMAC
packet authentication code to use it instead of a "cmac(aes)"
crypto_shash.  This has multiple benefits, such as:

- It's faster.  The AES-CMAC code is now called directly, without
  unnecessary overhead such as indirect calls.

- MAC calculation can no longer fail.

- The AES-CMAC key struct is now a fixed size, allowing it to be
  embedded directly into 'struct ieee80211_key' rather than using a
  separate allocation.  Note that although this increases the size of
  the 'u.cmac' field of 'struct ieee80211_key', it doesn't cause it to
  exceed the size of the largest variant of the union 'u'.  Therefore,
  the size of 'struct ieee80211_key' itself is unchanged.

Signed-off-by: Eric Biggers <[email-masked]>
---
 net/mac80211/Kconfig    |  1 +
 net/mac80211/aes_cmac.c | 65 ++++++++---------------------------------
 net/mac80211/aes_cmac.h | 12 +++-----
 net/mac80211/key.c      | 11 ++-----
 net/mac80211/key.h      |  3 +-
 net/mac80211/wpa.c      | 13 +++------
 6 files changed, 26 insertions(+), 79 deletions(-)

diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index cf0f7780fb10..0afbe4f4f976 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -1,10 +1,11 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config MAC80211
 	tristate "Generic IEEE 802.11 Networking Stack (mac80211)"
 	depends on CFG80211
 	select CRYPTO
+	select CRYPTO_LIB_AES_CBC_MACS
 	select CRYPTO_LIB_ARC4
 	select CRYPTO_AES
 	select CRYPTO_CCM
 	select CRYPTO_GCM
 	select CRYPTO_CMAC
diff --git a/net/mac80211/aes_cmac.c b/net/mac80211/aes_cmac.c
index 0827965455dc..55b674ad7d7a 100644
--- a/net/mac80211/aes_cmac.c
+++ b/net/mac80211/aes_cmac.c
@@ -5,80 +5,39 @@
  * Copyright (C) 2020 Intel Corporation
  */
 
 #include <linux/kernel.h>
 #include <linux/types.h>
-#include <linux/crypto.h>
 #include <linux/export.h>
 #include <linux/err.h>
-#include <crypto/aes.h>
+#include <crypto/aes-cbc-macs.h>
 
 #include <net/mac80211.h>
 #include "key.h"
 #include "aes_cmac.h"
 
 #define AAD_LEN 20
 
 static const u8 zero[IEEE80211_CMAC_256_MIC_LEN];
 
-int ieee80211_aes_cmac(struct crypto_shash *tfm, const u8 *aad,
-		       const u8 *data, size_t data_len, u8 *mic,
-		       unsigned int mic_len)
+void ieee80211_aes_cmac(const struct aes_cmac_key *key, const u8 *aad,
+			const u8 *data, size_t data_len, u8 *mic,
+			unsigned int mic_len)
 {
-	int err;
-	SHASH_DESC_ON_STACK(desc, tfm);
+	struct aes_cmac_ctx ctx;
 	u8 out[AES_BLOCK_SIZE];
 	const __le16 *fc;
 
-	desc->tfm = tfm;
-
-	err = crypto_shash_init(desc);
-	if (err)
-		return err;
-	err = crypto_shash_update(desc, aad, AAD_LEN);
-	if (err)
-		return err;
+	aes_cmac_init(&ctx, key);
+	aes_cmac_update(&ctx, aad, AAD_LEN);
 	fc = (const __le16 *)aad;
 	if (ieee80211_is_beacon(*fc)) {
 		/* mask Timestamp field to zero */
-		err = crypto_shash_update(desc, zero, 8);
-		if (err)
-			return err;
-		err = crypto_shash_update(desc, data + 8,
-					  data_len - 8 - mic_len);
-		if (err)
-			return err;
+		aes_cmac_update(&ctx, zero, 8);
+		aes_cmac_update(&ctx, data + 8, data_len - 8 - mic_len);
 	} else {
-		err = crypto_shash_update(desc, data, data_len - mic_len);
-		if (err)
-			return err;
+		aes_cmac_update(&ctx, data, data_len - mic_len);
 	}
-	err = crypto_shash_finup(desc, zero, mic_len, out);
-	if (err)
-		return err;
+	aes_cmac_update(&ctx, zero, mic_len);
+	aes_cmac_final(&ctx, out);
 	memcpy(mic, out, mic_len);
-
-	return 0;
-}
-
-struct crypto_shash *ieee80211_aes_cmac_key_setup(const u8 key[],
-						  size_t key_len)
-{
-	struct crypto_shash *tfm;
-
-	tfm = crypto_alloc_shash("cmac(aes)", 0, 0);
-	if (!IS_ERR(tfm)) {
-		int err = crypto_shash_setkey(tfm, key, key_len);
-
-		if (err) {
-			crypto_free_shash(tfm);
-			return ERR_PTR(err);
-		}
-	}
-
-	return tfm;
-}
-
-void ieee80211_aes_cmac_key_free(struct crypto_shash *tfm)
-{
-	crypto_free_shash(tfm);
 }
diff --git a/net/mac80211/aes_cmac.h b/net/mac80211/aes_cmac.h
index 5f971a8298cb..c7a6df47b327 100644
--- a/net/mac80211/aes_cmac.h
+++ b/net/mac80211/aes_cmac.h
@@ -4,16 +4,12 @@
  */
 
 #ifndef AES_CMAC_H
 #define AES_CMAC_H
 
-#include <linux/crypto.h>
-#include <crypto/hash.h>
+#include <crypto/aes-cbc-macs.h>
 
-struct crypto_shash *ieee80211_aes_cmac_key_setup(const u8 key[],
-						  size_t key_len);
-int ieee80211_aes_cmac(struct crypto_shash *tfm, const u8 *aad,
-		       const u8 *data, size_t data_len, u8 *mic,
-		       unsigned int mic_len);
-void ieee80211_aes_cmac_key_free(struct crypto_shash *tfm);
+void ieee80211_aes_cmac(const struct aes_cmac_key *key, const u8 *aad,
+			const u8 *data, size_t data_len, u8 *mic,
+			unsigned int mic_len);
 
 #endif /* AES_CMAC_H */
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 04c8809173d7..4b8965633df3 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -688,14 +688,13 @@ ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
 					seq[IEEE80211_CMAC_PN_LEN - j - 1];
 		/*
 		 * Initialize AES key state here as an optimization so that
 		 * it does not need to be initialized for every packet.
 		 */
-		key->u.aes_cmac.tfm =
-			ieee80211_aes_cmac_key_setup(key_data, key_len);
-		if (IS_ERR(key->u.aes_cmac.tfm)) {
-			err = PTR_ERR(key->u.aes_cmac.tfm);
+		err = aes_cmac_preparekey(&key->u.aes_cmac.key, key_data,
+					  key_len);
+		if (err) {
 			kfree(key);
 			return ERR_PTR(err);
 		}
 		break;
 	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
@@ -748,14 +747,10 @@ static void ieee80211_key_free_common(struct ieee80211_key *key)
 	switch (key->conf.cipher) {
 	case WLAN_CIPHER_SUITE_CCMP:
 	case WLAN_CIPHER_SUITE_CCMP_256:
 		ieee80211_aes_key_free(key->u.ccmp.tfm);
 		break;
-	case WLAN_CIPHER_SUITE_AES_CMAC:
-	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
-		ieee80211_aes_cmac_key_free(key->u.aes_cmac.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:
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index 1fa0f4f78962..826e4e9387c5 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -10,10 +10,11 @@
 
 #include <linux/types.h>
 #include <linux/list.h>
 #include <linux/crypto.h>
 #include <linux/rcupdate.h>
+#include <crypto/aes-cbc-macs.h>
 #include <crypto/arc4.h>
 #include <net/mac80211.h>
 
 #define NUM_DEFAULT_KEYS 4
 #define NUM_DEFAULT_MGMT_KEYS 2
@@ -91,11 +92,11 @@ struct ieee80211_key {
 			struct crypto_aead *tfm;
 			u32 replays; /* dot11RSNAStatsCCMPReplays */
 		} ccmp;
 		struct {
 			u8 rx_pn[IEEE80211_CMAC_PN_LEN];
-			struct crypto_shash *tfm;
+			struct aes_cmac_key key;
 			u32 replays; /* dot11RSNAStatsCMACReplays */
 			u32 icverrors; /* dot11RSNAStatsCMACICVErrors */
 		} aes_cmac;
 		struct {
 			u8 rx_pn[IEEE80211_GMAC_PN_LEN];
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index fdf98c21d32c..59324b367bdd 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -870,15 +870,12 @@ ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx,
 	if (info->control.hw_key)
 		return TX_CONTINUE;
 
 	bip_aad(skb, aad);
 
-	if (ieee80211_aes_cmac(key->u.aes_cmac.tfm, aad,
-			       skb->data + 24, skb->len - 24,
-			       mmie->mic, mic_len))
-		return TX_DROP;
-
+	ieee80211_aes_cmac(&key->u.aes_cmac.key, aad, skb->data + 24,
+			   skb->len - 24, mmie->mic, mic_len);
 	return TX_CONTINUE;
 }
 
 ieee80211_rx_result
 ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx,
@@ -916,14 +913,12 @@ ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx,
 	}
 
 	if (!(status->flag & RX_FLAG_DECRYPTED)) {
 		/* hardware didn't decrypt/verify MIC */
 		bip_aad(skb, aad);
-		if (ieee80211_aes_cmac(key->u.aes_cmac.tfm, aad,
-				       skb->data + 24, skb->len - 24,
-				       mic, mic_len))
-			return RX_DROP_U_DECRYPT_FAIL;
+		ieee80211_aes_cmac(&key->u.aes_cmac.key, aad, skb->data + 24,
+				   skb->len - 24, mic, mic_len);
 		if (crypto_memneq(mic, mmie->mic, mic_len)) {
 			key->u.aes_cmac.icverrors++;
 			return RX_DROP_U_MIC_FAIL;
 		}
 	}
-- 
2.53.0