diff --git a/include/kunit/run-in-irq-context.h b/include/kunit/run-in-irq-context.h
index bfe60d6..b46fa16 100644
--- a/include/kunit/run-in-irq-context.h
+++ b/include/kunit/run-in-irq-context.h
@@ -38,11 +38,13 @@ static enum hrtimer_restart kunit_irq_test_timer_func(struct hrtimer *timer)
 	softirq_calls = atomic_read(&state->softirq_func_calls);
 
 	/*
-	 * If the timer is firing too often for the softirq or task to ever have
-	 * a chance to run, increase the timer interval.  This is needed on very
-	 * slow systems.
+	 * If the hrtimer is running much faster than the bh_work or the task,
+	 * then it is firing too fast and might be starving those contexts as
+	 * well as the actual system timer tick.  Increase the interval.
 	 */
-	if (hardirq_calls >= 20 && (softirq_calls == 0 || task_calls == 0))
+	if (hardirq_calls >= 20 &&
+	    (hardirq_calls / 2 > softirq_calls ||
+	     hardirq_calls / 2 > task_calls))
 		state->interval = ktime_add_ns(state->interval, 250);
 
 	if (!state->func(state->test_specific_state))
@@ -135,6 +137,8 @@ static inline void kunit_run_irq_test(struct kunit *test, bool (*func)(void *),
 	/* Cancel the timer and work. */
 	hrtimer_cancel(&state.timer);
 	flush_work(&state.bh_work);
+	destroy_hrtimer_on_stack(&state.timer);
+	destroy_work_on_stack(&state.bh_work);
 
 	/* Sanity check: the timer and BH functions should have been run. */
 	KUNIT_EXPECT_GT_MSG(test, atomic_read(&state.hardirq_func_calls), 0,
diff --git a/lib/crypto/.kunitconfig b/lib/crypto/.kunitconfig
index 3efc854..60e0a77 100644
--- a/lib/crypto/.kunitconfig
+++ b/lib/crypto/.kunitconfig
@@ -3,6 +3,8 @@ CONFIG_KUNIT=y
 CONFIG_CRYPTO_LIB_ENABLE_ALL_FOR_KUNIT=y
 
 CONFIG_CRYPTO_LIB_AES_CBC_MACS_KUNIT_TEST=y
+CONFIG_CRYPTO_LIB_AES_CCM_KUNIT_TEST=y
+CONFIG_CRYPTO_LIB_AES_GCM_KUNIT_TEST=y
 CONFIG_CRYPTO_LIB_BLAKE2B_KUNIT_TEST=y
 CONFIG_CRYPTO_LIB_BLAKE2S_KUNIT_TEST=y
 CONFIG_CRYPTO_LIB_CHACHA20POLY1305_KUNIT_TEST=y
diff --git a/lib/crypto/aes.c b/lib/crypto/aes.c
index 4222a4c..f154983 100644
--- a/lib/crypto/aes.c
+++ b/lib/crypto/aes.c
@@ -19,7 +19,7 @@
 #include <linux/export.h>
 #include <linux/module.h>
 #include <linux/unaligned.h>
-#include "fips.h"
+#include "fips-aes.h"
 
 static const u8 ____cacheline_aligned aes_sbox[] = {
 	0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
@@ -522,6 +522,26 @@ void aes_decrypt(const struct aes_key *key, u8 out[AES_BLOCK_SIZE],
 }
 EXPORT_SYMBOL(aes_decrypt);
 
+/* FIPS cryptographic algorithm self-test for "bare" AES */
+static void __init aes_fips_test(void)
+{
+	struct aes_key key;
+	u8 data[AES_BLOCK_SIZE];
+
+	if (aes_preparekey(&key, fips_test_key, sizeof(fips_test_key)) != 0)
+		panic("aes: FIPS self-test failed (preparekey)\n");
+
+	aes_encrypt(&key, data, fips_test_data);
+	if (memcmp(fips_test_aes_ecb_ctext, data, sizeof(data)) != 0)
+		panic("aes: FIPS self-test failed (wrong ciphertext)\n");
+
+	aes_decrypt(&key, data, data);
+	if (memcmp(fips_test_data, data, sizeof(data)) != 0)
+		panic("aes: FIPS self-test failed (wrong plaintext)\n");
+
+	memzero_explicit(&key, sizeof(key));
+}
+
 #if IS_ENABLED(CONFIG_CRYPTO_LIB_AES_CBC_MACS)
 
 #ifndef aes_cbcmac_blocks_arch
@@ -717,17 +737,10 @@ void aes_cbcmac_final(struct aes_cbcmac_ctx *ctx, u8 out[AES_BLOCK_SIZE])
 }
 EXPORT_SYMBOL_NS_GPL(aes_cbcmac_final, "CRYPTO_INTERNAL");
 
-/*
- * FIPS cryptographic algorithm self-test for AES-CMAC.  As per the FIPS 140-3
- * Implementation Guidance, a cryptographic algorithm self-test for at least one
- * of AES-GCM, AES-CCM, AES-CMAC, or AES-GMAC is required if any of those modes
- * is implemented.  This fulfills that requirement via AES-CMAC.
- *
- * This is just for FIPS.  The full tests are in the KUnit test suite.
- */
+/* FIPS cryptographic algorithm self-test for AES-CMAC */
 static void __init aes_cmac_fips_test(void)
 {
-	struct aes_cmac_key key;
+	struct aes_cmac_key key __cleanup(aes_cmac_zeroize_key);
 	u8 mac[AES_BLOCK_SIZE];
 
 	if (aes_cmac_preparekey(&key, fips_test_key, sizeof(fips_test_key)) !=
@@ -736,7 +749,6 @@ static void __init aes_cmac_fips_test(void)
 	aes_cmac(&key, fips_test_data, sizeof(fips_test_data), mac);
 	if (memcmp(fips_test_aes_cmac_value, mac, sizeof(mac)) != 0)
 		panic("aes: CMAC FIPS self-test failed (wrong MAC)\n");
-	memzero_explicit(&key, sizeof(key));
 }
 #else /* CONFIG_CRYPTO_LIB_AES_CBC_MACS */
 static inline void aes_cmac_fips_test(void)
@@ -797,7 +809,31 @@ void aes_ecb_decrypt(u8 *dst, const u8 *src, size_t len,
 		aes_decrypt(key, &dst[i], &src[i]);
 }
 EXPORT_SYMBOL_GPL(aes_ecb_decrypt);
-#endif /* CONFIG_CRYPTO_LIB_AES_ECB */
+
+/* FIPS cryptographic algorithm self-test for AES-ECB */
+static void __init aes_ecb_fips_test(void)
+{
+	struct aes_key key;
+	u8 data[sizeof(fips_test_data)];
+
+	if (aes_preparekey(&key, fips_test_key, sizeof(fips_test_key)) != 0)
+		panic("aes: ECB FIPS self-test failed (preparekey)\n");
+
+	aes_ecb_encrypt(data, fips_test_data, sizeof(data), &key);
+	if (memcmp(fips_test_aes_ecb_ctext, data, sizeof(data)) != 0)
+		panic("aes: ECB FIPS self-test failed (wrong ciphertext)\n");
+
+	aes_ecb_decrypt(data, data, sizeof(data), &key);
+	if (memcmp(fips_test_data, data, sizeof(data)) != 0)
+		panic("aes: ECB FIPS self-test failed (wrong plaintext)\n");
+
+	memzero_explicit(&key, sizeof(key));
+}
+#else /* CONFIG_CRYPTO_LIB_AES_ECB */
+static inline void aes_ecb_fips_test(void)
+{
+}
+#endif /* !CONFIG_CRYPTO_LIB_AES_ECB */
 
 #if IS_ENABLED(CONFIG_CRYPTO_LIB_AES_CBC)
 /*
@@ -983,7 +1019,66 @@ void aes_cbc_cts_decrypt(u8 *dst, const u8 *src, size_t len,
 	crypto_xor(pad, iv, AES_BLOCK_SIZE); /* P[n - 1] */
 }
 EXPORT_SYMBOL_GPL(aes_cbc_cts_decrypt);
-#endif /* CONFIG_CRYPTO_LIB_AES_CBC */
+
+/* FIPS cryptographic algorithm self-test for AES-CBC */
+static void __init aes_cbc_fips_test(void)
+{
+	struct aes_key key;
+	u8 iv[AES_BLOCK_SIZE];
+	u8 data[sizeof(fips_test_data)];
+
+	if (aes_preparekey(&key, fips_test_key, sizeof(fips_test_key)) != 0)
+		panic("aes: CBC FIPS self-test failed (preparekey)\n");
+
+	memcpy(iv, fips_test_iv, sizeof(iv));
+	aes_cbc_encrypt(data, fips_test_data, sizeof(data), iv, &key);
+	if (memcmp(fips_test_aes_cbc_ctext, data, sizeof(data)) != 0)
+		panic("aes: CBC FIPS self-test failed (wrong ciphertext)\n");
+
+	memcpy(iv, fips_test_iv, sizeof(iv));
+	aes_cbc_decrypt(data, data, sizeof(data), iv, &key);
+	if (memcmp(fips_test_data, data, sizeof(data)) != 0)
+		panic("aes: CBC FIPS self-test failed (wrong plaintext)\n");
+
+	memzero_explicit(&key, sizeof(key));
+}
+
+/* FIPS cryptographic algorithm self-test for AES-CBC-CTS */
+static void __init aes_cbc_cts_fips_test(void)
+{
+	struct aes_key key;
+	u8 iv[AES_BLOCK_SIZE];
+	const size_t data_len = 2 * AES_BLOCK_SIZE;
+	u8 ptext[2 * AES_BLOCK_SIZE];
+	u8 data[2 * AES_BLOCK_SIZE];
+
+	/* ptext = fips_test_data || fips_test_data */
+	memcpy(ptext, fips_test_data, AES_BLOCK_SIZE);
+	memcpy(&ptext[AES_BLOCK_SIZE], ptext, AES_BLOCK_SIZE);
+
+	if (aes_preparekey(&key, fips_test_key, sizeof(fips_test_key)) != 0)
+		panic("aes: CBC-CTS FIPS self-test failed (preparekey)\n");
+
+	memcpy(iv, fips_test_iv, sizeof(iv));
+	aes_cbc_cts_encrypt(data, ptext, data_len, iv, &key);
+	if (memcmp(fips_test_aes_cbc_cts_ctext, data, data_len) != 0)
+		panic("aes: CBC-CTS FIPS self-test failed (wrong ciphertext)\n");
+
+	memcpy(iv, fips_test_iv, sizeof(iv));
+	aes_cbc_cts_decrypt(data, data, data_len, iv, &key);
+	if (memcmp(ptext, data, data_len) != 0)
+		panic("aes: CBC-CTS FIPS self-test failed (wrong plaintext)\n");
+
+	memzero_explicit(&key, sizeof(key));
+}
+#else /* CONFIG_CRYPTO_LIB_AES_CBC */
+static inline void aes_cbc_fips_test(void)
+{
+}
+static inline void aes_cbc_cts_fips_test(void)
+{
+}
+#endif /* !CONFIG_CRYPTO_LIB_AES_CBC */
 
 #if IS_ENABLED(CONFIG_CRYPTO_LIB_AES_CTR)
 /*
@@ -1078,7 +1173,34 @@ void aes_xctr(u8 *dst, const u8 *src, size_t len, u64 *ctr,
 	memzero_explicit(aes_input, sizeof(aes_input));
 }
 EXPORT_SYMBOL_GPL(aes_xctr);
-#endif /* CONFIG_CRYPTO_LIB_AES_CTR */
+
+/* FIPS cryptographic algorithm self-test for AES-CTR */
+static void __init aes_ctr_fips_test(void)
+{
+	struct aes_enckey key;
+	u8 ctr[AES_BLOCK_SIZE];
+	u8 data[sizeof(fips_test_data)];
+
+	if (aes_prepareenckey(&key, fips_test_key, sizeof(fips_test_key)) != 0)
+		panic("aes: CTR FIPS self-test failed (preparekey)\n");
+
+	memcpy(ctr, fips_test_iv, sizeof(ctr));
+	aes_ctr(data, fips_test_data, sizeof(data), ctr, &key);
+	if (memcmp(fips_test_aes_ctr_ctext, data, sizeof(data)) != 0)
+		panic("aes: CTR FIPS self-test failed (wrong ciphertext)\n");
+
+	memcpy(ctr, fips_test_iv, sizeof(ctr));
+	aes_ctr(data, data, sizeof(data), ctr, &key);
+	if (memcmp(fips_test_data, data, sizeof(data)) != 0)
+		panic("aes: CTR FIPS self-test failed (wrong plaintext)\n");
+
+	memzero_explicit(&key, sizeof(key));
+}
+#else /* CONFIG_CRYPTO_LIB_AES_CTR */
+static inline void aes_ctr_fips_test(void)
+{
+}
+#endif /* !CONFIG_CRYPTO_LIB_AES_CTR */
 
 #if IS_ENABLED(CONFIG_CRYPTO_LIB_AES_XTS)
 int aes_xts_preparekey(struct aes_xts_key *key, const u8 *in_key,
@@ -1307,7 +1429,36 @@ void aes_xts_decrypt(u8 *dst, const u8 *src, size_t len,
 	aes_xts_decrypt_nocts(dst, src, len, tweak, key, cont);
 }
 EXPORT_SYMBOL_GPL(aes_xts_decrypt);
-#endif /* CONFIG_CRYPTO_LIB_AES_XTS */
+
+/* FIPS cryptographic algorithm self-test for AES-XTS */
+static void __init aes_xts_fips_test(void)
+{
+	struct aes_xts_key *key __free(kfree_sensitive) = kmalloc_obj(*key);
+	u8 tweak[AES_BLOCK_SIZE];
+	u8 data[sizeof(fips_test_data)];
+
+	if (key == NULL)
+		panic("aes: XTS FIPS self-test failed (kmalloc)\n");
+
+	if (aes_xts_preparekey(key, fips_test_xts_key,
+			       sizeof(fips_test_xts_key), 0) != 0)
+		panic("aes: XTS FIPS self-test failed (preparekey)\n");
+
+	memcpy(tweak, fips_test_iv, sizeof(tweak));
+	aes_xts_encrypt(data, fips_test_data, sizeof(data), tweak, key, false);
+	if (memcmp(fips_test_aes_xts_ctext, data, sizeof(data)) != 0)
+		panic("aes: XTS FIPS self-test failed (wrong ciphertext)\n");
+
+	memcpy(tweak, fips_test_iv, sizeof(tweak));
+	aes_xts_decrypt(data, data, sizeof(data), tweak, key, false);
+	if (memcmp(fips_test_data, data, sizeof(data)) != 0)
+		panic("aes: XTS FIPS self-test failed (wrong plaintext)\n");
+}
+#else /* CONFIG_CRYPTO_LIB_AES_XTS */
+static inline void aes_xts_fips_test(void)
+{
+}
+#endif /* !CONFIG_CRYPTO_LIB_AES_XTS */
 
 #if IS_ENABLED(CONFIG_CRYPTO_LIB_AES_GCM)
 /*
@@ -1586,7 +1737,37 @@ int aes_gcm_decrypt(u8 *dst, const u8 *src, size_t data_len, const u8 *authtag,
 }
 EXPORT_SYMBOL_GPL(aes_gcm_decrypt);
 
-#endif /* CONFIG_CRYPTO_LIB_AES_GCM */
+/* FIPS cryptographic algorithm self-test for AES-GCM */
+static void __init aes_gcm_fips_test(void)
+{
+	const size_t data_len = sizeof(fips_test_data);
+	u8 buf[sizeof(fips_test_data) + AES_BLOCK_SIZE];
+	struct aes_gcm_key key;
+	int err;
+
+	if (aes_gcm_preparekey(&key, fips_test_key, sizeof(fips_test_key),
+			       AES_BLOCK_SIZE) != 0)
+		panic("aes: GCM FIPS self-test failed (preparekey)\n");
+
+	aes_gcm_encrypt(buf, fips_test_data, data_len, &buf[data_len],
+			fips_test_ad, sizeof(fips_test_ad), fips_test_iv, &key);
+	if (memcmp(fips_test_aes_gcm_ctext_and_tag, buf, sizeof(buf)) != 0)
+		panic("aes: GCM FIPS self-test failed (wrong ciphertext and/or tag)\n");
+
+	err = aes_gcm_decrypt(buf, buf, data_len, &buf[data_len], fips_test_ad,
+			      sizeof(fips_test_ad), fips_test_iv, &key);
+	if (err != 0)
+		panic("aes: GCM FIPS self-test failed (decryption failed)\n");
+	if (memcmp(fips_test_data, buf, data_len) != 0)
+		panic("aes: GCM FIPS self-test failed (wrong plaintext)\n");
+
+	memzero_explicit(&key, sizeof(key));
+}
+#else /* CONFIG_CRYPTO_LIB_AES_GCM */
+static inline void aes_gcm_fips_test(void)
+{
+}
+#endif /* !CONFIG_CRYPTO_LIB_AES_GCM */
 
 #if IS_ENABLED(CONFIG_CRYPTO_LIB_AES_CCM)
 int aes_ccm_preparekey(struct aes_ccm_key *key, const u8 *in_key,
@@ -1898,15 +2079,60 @@ int aes_ccm_decrypt(u8 *dst, const u8 *src, size_t data_len, const u8 *authtag,
 	return err;
 }
 EXPORT_SYMBOL_GPL(aes_ccm_decrypt);
-#endif /* CONFIG_CRYPTO_LIB_AES_CCM */
+
+/* FIPS cryptographic algorithm self-test for AES-CCM */
+static void __init aes_ccm_fips_test(void)
+{
+	const size_t data_len = sizeof(fips_test_data);
+	const size_t nonce_len = 13;
+	u8 buf[sizeof(fips_test_data) + AES_BLOCK_SIZE];
+	struct aes_ccm_key key;
+	int err;
+
+	if (aes_ccm_preparekey(&key, fips_test_key, sizeof(fips_test_key),
+			       AES_BLOCK_SIZE) != 0)
+		panic("aes: CCM FIPS self-test failed (preparekey)\n");
+
+	err = aes_ccm_encrypt(buf, fips_test_data, data_len, &buf[data_len],
+			      fips_test_ad, sizeof(fips_test_ad), fips_test_iv,
+			      nonce_len, &key);
+	if (err != 0)
+		panic("aes: CCM FIPS self-test failed (encryption failed)\n");
+	if (memcmp(fips_test_aes_ccm_ctext_and_tag, buf, sizeof(buf)) != 0)
+		panic("aes: CCM FIPS self-test failed (wrong ciphertext and/or tag)\n");
+
+	err = aes_ccm_decrypt(buf, buf, data_len, &buf[data_len], fips_test_ad,
+			      sizeof(fips_test_ad), fips_test_iv, nonce_len,
+			      &key);
+	if (err != 0)
+		panic("aes: CCM FIPS self-test failed (decryption failed)\n");
+	if (memcmp(fips_test_data, buf, data_len) != 0)
+		panic("aes: CCM FIPS self-test failed (wrong plaintext)\n");
+
+	memzero_explicit(&key, sizeof(key));
+}
+#else /* CONFIG_CRYPTO_LIB_AES_CCM */
+static inline void aes_ccm_fips_test(void)
+{
+}
+#endif /* !CONFIG_CRYPTO_LIB_AES_CCM */
 
 static int __init aes_mod_init(void)
 {
 #ifdef aes_mod_init_arch
 	aes_mod_init_arch();
 #endif
-	if (fips_enabled)
+	if (fips_enabled) {
+		aes_fips_test();
 		aes_cmac_fips_test();
+		aes_ecb_fips_test();
+		aes_cbc_fips_test();
+		aes_cbc_cts_fips_test();
+		aes_ctr_fips_test();
+		aes_xts_fips_test();
+		aes_gcm_fips_test();
+		aes_ccm_fips_test();
+	}
 	return 0;
 }
 subsys_initcall(aes_mod_init);
diff --git a/lib/crypto/fips-aes.h b/lib/crypto/fips-aes.h
new file mode 100644
index 0000000..2a17466
--- /dev/null
+++ b/lib/crypto/fips-aes.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* This file was generated by: gen-fips-testvecs.py */
+/* clang-format off */
+
+#include <linux/fips.h>
+
+static const u8 fips_test_data[] __initconst __maybe_unused = {
+	0x66, 0x69, 0x70, 0x73, 0x20, 0x74, 0x65, 0x73,
+	0x74, 0x20, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00,
+};
+
+static const u8 fips_test_ad[] __initconst __maybe_unused = {
+	0x66, 0x69, 0x70, 0x73, 0x20, 0x74, 0x65, 0x73,
+	0x74, 0x20, 0x61, 0x64, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const u8 fips_test_iv[] __initconst __maybe_unused = {
+	0x66, 0x69, 0x70, 0x73, 0x20, 0x74, 0x65, 0x73,
+	0x74, 0x20, 0x69, 0x76, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const u8 fips_test_key[] __initconst __maybe_unused = {
+	0x66, 0x69, 0x70, 0x73, 0x20, 0x74, 0x65, 0x73,
+	0x74, 0x20, 0x6b, 0x65, 0x79, 0x00, 0x00, 0x00,
+};
+
+static const u8 fips_test_xts_key[] __initconst __maybe_unused = {
+	0x6b, 0x65, 0x79, 0x31, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x6b, 0x65, 0x79, 0x32, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const u8 fips_test_aes_cmac_value[] __initconst __maybe_unused = {
+	0xc5, 0x88, 0x28, 0x55, 0xd7, 0x2c, 0x00, 0xb6,
+	0x6a, 0xa7, 0xfc, 0x82, 0x90, 0x81, 0xcf, 0x18,
+};
+
+static const u8 fips_test_aes_ecb_ctext[] __initconst __maybe_unused = {
+	0x47, 0x76, 0x48, 0xaf, 0x1b, 0xd8, 0x4c, 0xe6,
+	0xb5, 0xa7, 0x20, 0x8d, 0x64, 0x88, 0xbc, 0x3f,
+};
+
+static const u8 fips_test_aes_cbc_ctext[] __initconst __maybe_unused = {
+	0xc8, 0x7d, 0x7c, 0x25, 0xba, 0x15, 0xf7, 0xe1,
+	0x08, 0xa0, 0xd0, 0x7a, 0x20, 0x37, 0xaf, 0x5e,
+};
+
+static const u8 fips_test_aes_cbc_cts_ctext[] __initconst __maybe_unused = {
+	0x36, 0x8e, 0x37, 0xb4, 0x78, 0xe2, 0x88, 0x59,
+	0xd5, 0xe8, 0x17, 0x65, 0x5c, 0xa1, 0x25, 0xe6,
+	0xc8, 0x7d, 0x7c, 0x25, 0xba, 0x15, 0xf7, 0xe1,
+	0x08, 0xa0, 0xd0, 0x7a, 0x20, 0x37, 0xaf, 0x5e,
+};
+
+static const u8 fips_test_aes_ctr_ctext[] __initconst __maybe_unused = {
+	0x95, 0xf4, 0xf4, 0x7a, 0xc8, 0xa2, 0x53, 0x73,
+	0x53, 0x8f, 0x95, 0xfc, 0x18, 0xfe, 0x58, 0x2f,
+};
+
+static const u8 fips_test_aes_xts_ctext[] __initconst __maybe_unused = {
+	0xd4, 0x51, 0x7f, 0x01, 0x14, 0x91, 0x16, 0x29,
+	0x26, 0xbe, 0xec, 0x9b, 0x90, 0xed, 0x59, 0x30,
+};
+
+static const u8 fips_test_aes_gcm_ctext_and_tag[] __initconst __maybe_unused = {
+	0x12, 0x0c, 0x5d, 0x03, 0x32, 0x93, 0x13, 0x44,
+	0x06, 0x35, 0x26, 0x9d, 0xe0, 0xea, 0xbc, 0xe2,
+	0x30, 0xa9, 0xa4, 0x15, 0xc5, 0x3d, 0xb3, 0xf9,
+	0x30, 0x82, 0xdf, 0x9c, 0xd8, 0xc4, 0x3f, 0x2f,
+};
+
+static const u8 fips_test_aes_ccm_ctext_and_tag[] __initconst __maybe_unused = {
+	0x11, 0x8e, 0x01, 0xcb, 0xb5, 0x22, 0x6d, 0xb4,
+	0x66, 0x98, 0x97, 0x1d, 0x35, 0x53, 0x78, 0xdd,
+	0xd1, 0xc5, 0xff, 0xb6, 0x90, 0xcf, 0xb1, 0xf2,
+	0x87, 0x99, 0xd6, 0x1e, 0xd5, 0xd1, 0xed, 0x63,
+};
diff --git a/lib/crypto/fips.h b/lib/crypto/fips-sha.h
similarity index 93%
rename from lib/crypto/fips.h
rename to lib/crypto/fips-sha.h
index 9fc4974..68af7e1 100644
--- a/lib/crypto/fips.h
+++ b/lib/crypto/fips-sha.h
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-or-later */
 /* This file was generated by: gen-fips-testvecs.py */
+/* clang-format off */
 
 #include <linux/fips.h>
 
@@ -43,8 +44,3 @@ static const u8 fips_test_sha3_256_value[] __initconst __maybe_unused = {
 	0xba, 0x9b, 0xb6, 0xaa, 0x32, 0xa7, 0x97, 0x00,
 	0x98, 0xdb, 0xff, 0xe7, 0xc6, 0xde, 0xb5, 0x82,
 };
-
-static const u8 fips_test_aes_cmac_value[] __initconst __maybe_unused = {
-	0xc5, 0x88, 0x28, 0x55, 0xd7, 0x2c, 0x00, 0xb6,
-	0x6a, 0xa7, 0xfc, 0x82, 0x90, 0x81, 0xcf, 0x18,
-};
diff --git a/lib/crypto/sha1.c b/lib/crypto/sha1.c
index daf18c8..b687b89 100644
--- a/lib/crypto/sha1.c
+++ b/lib/crypto/sha1.c
@@ -12,7 +12,7 @@
 #include <linux/string.h>
 #include <linux/unaligned.h>
 #include <linux/wordpart.h>
-#include "fips.h"
+#include "fips-sha.h"
 
 static const struct sha1_block_state sha1_iv = {
 	.h = { SHA1_H0, SHA1_H1, SHA1_H2, SHA1_H3, SHA1_H4 },
diff --git a/lib/crypto/sha256.c b/lib/crypto/sha256.c
index 5d6b77e..e8c346f 100644
--- a/lib/crypto/sha256.c
+++ b/lib/crypto/sha256.c
@@ -17,7 +17,7 @@
 #include <linux/string.h>
 #include <linux/unaligned.h>
 #include <linux/wordpart.h>
-#include "fips.h"
+#include "fips-sha.h"
 
 static const struct sha256_block_state sha224_iv = {
 	.h = {
diff --git a/lib/crypto/sha3.c b/lib/crypto/sha3.c
index 32b7074..286a237 100644
--- a/lib/crypto/sha3.c
+++ b/lib/crypto/sha3.c
@@ -17,7 +17,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/unaligned.h>
-#include "fips.h"
+#include "fips-sha.h"
 
 /*
  * On some 32-bit architectures, such as h8300, GCC ends up using over 1 KB of
diff --git a/lib/crypto/sha512.c b/lib/crypto/sha512.c
index 605eab5..0dd6fb4 100644
--- a/lib/crypto/sha512.c
+++ b/lib/crypto/sha512.c
@@ -17,7 +17,7 @@
 #include <linux/string.h>
 #include <linux/unaligned.h>
 #include <linux/wordpart.h>
-#include "fips.h"
+#include "fips-sha.h"
 
 static const struct sha512_block_state sha384_iv = {
 	.h = {
diff --git a/lib/crypto/tests/Kconfig b/lib/crypto/tests/Kconfig
index bc084dd..e121114 100644
--- a/lib/crypto/tests/Kconfig
+++ b/lib/crypto/tests/Kconfig
@@ -9,6 +9,24 @@ config CRYPTO_LIB_AES_CBC_MACS_KUNIT_TEST
 	  KUnit tests for the AES-CMAC, AES-XCBC-MAC, and AES-CBC-MAC message
 	  authentication codes.
 
+config CRYPTO_LIB_AES_CCM_KUNIT_TEST
+	tristate "KUnit tests for AES-CCM" if !KUNIT_ALL_TESTS
+	depends on KUNIT && CRYPTO_LIB_AES_CCM
+	default KUNIT_ALL_TESTS
+	select CRYPTO_LIB_BENCHMARK_VISIBLE
+	# This test uses BLAKE2s, but that's always built-in.
+	help
+	  KUnit tests for the AES-CCM authenticated encryption algorithm.
+
+config CRYPTO_LIB_AES_GCM_KUNIT_TEST
+	tristate "KUnit tests for AES-GCM" if !KUNIT_ALL_TESTS
+	depends on KUNIT && CRYPTO_LIB_AES_GCM
+	default KUNIT_ALL_TESTS
+	select CRYPTO_LIB_BENCHMARK_VISIBLE
+	# This test uses BLAKE2s, but that's always built-in.
+	help
+	  KUnit tests for the AES-GCM authenticated encryption algorithm.
+
 config CRYPTO_LIB_BLAKE2B_KUNIT_TEST
 	tristate "KUnit tests for BLAKE2b" if !KUNIT_ALL_TESTS
 	depends on KUNIT && CRYPTO_LIB_BLAKE2B
diff --git a/lib/crypto/tests/Makefile b/lib/crypto/tests/Makefile
index a739413..c97c1d7 100644
--- a/lib/crypto/tests/Makefile
+++ b/lib/crypto/tests/Makefile
@@ -1,6 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0-or-later
 
 obj-$(CONFIG_CRYPTO_LIB_AES_CBC_MACS_KUNIT_TEST) += aes_cbc_macs_kunit.o
+obj-$(CONFIG_CRYPTO_LIB_AES_CCM_KUNIT_TEST) += aes_ccm_kunit.o
+obj-$(CONFIG_CRYPTO_LIB_AES_GCM_KUNIT_TEST) += aes_gcm_kunit.o
 obj-$(CONFIG_CRYPTO_LIB_BLAKE2B_KUNIT_TEST) += blake2b_kunit.o
 obj-$(CONFIG_CRYPTO_LIB_BLAKE2S_KUNIT_TEST) += blake2s_kunit.o
 obj-$(CONFIG_CRYPTO_LIB_CHACHA20POLY1305_KUNIT_TEST) += chacha20poly1305_kunit.o
diff --git a/lib/crypto/tests/aead-test-template.h b/lib/crypto/tests/aead-test-template.h
new file mode 100644
index 0000000..0c0138d
--- /dev/null
+++ b/lib/crypto/tests/aead-test-template.h
@@ -0,0 +1,1039 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Shared KUnit test cases for AEAD algorithms, including a benchmark
+ *
+ * Copyright 2026 Google LLC
+ */
+
+/*
+ * This file implements KUnit test cases shared by the different KUnit test
+ * suites for Authenticated Encryption with Associated Data (AEAD) algorithms.
+ *
+ * Test suites including this file must #define the following:
+ *
+ * Data structs:
+ * - AEAD_KEY: name of key struct
+ * - AEAD_CTX: name of context for incremental computation
+ *
+ * Constants:
+ * - AEAD_VALID_KEY_LENS: array of all valid key lengths in bytes
+ * - AEAD_VALID_NONCE_LENS: array of all valid nonce lengths in bytes
+ * - AEAD_VALID_TAG_LENS: array of all valid authtag lengths in bytes
+ * - AEAD_MAX_KEY_LEN: max key length in bytes (assumed to fit on stack)
+ * - AEAD_MAX_NONCE_LEN: max nonce length in bytes (assumed to fit on stack)
+ * - AEAD_MAX_TAG_LEN: max authtag length in bytes (assumed to fit on stack)
+ * - AEAD_MONTE_CARLO_CHECKSUM: checksum of a deterministically generated series
+ *   of (ciphertext, authtag) pairs (see test_aead_monte_carlo())
+ *
+ * Functions:
+ * - AEAD_PREPAREKEY: key preparation
+ * - AEAD_ENCRYPT and AEAD_DECRYPT: one-shot encryption and decryption
+ * - AEAD_INIT, AEAD_AUTH_UPDATE, AEAD_ENCRYPT_UPDATE, AEAD_ENCRYPT_FINAL,
+ *   AEAD_DECRYPT_UPDATE, AEAD_DECRYPT_FINAL: functions for incremental
+ *   encryption and decryption
+ *
+ * Function prototypes and their behavior must match the AES-CCM API.
+ */
+
+#include <crypto/blake2s.h>
+#include <kunit/run-in-irq-context.h>
+#include <kunit/test.h>
+#include <linux/ktime.h>
+#include <linux/preempt.h>
+#include "test-utils.h"
+
+/*
+ * Allocate a KUnit-managed struct AEAD_KEY and prepare it with a random key,
+ * using a random key length and random authentication tag length.
+ */
+static struct AEAD_KEY *aead_alloc_random_key(struct kunit *test,
+					      size_t *tag_len_ret)
+{
+	size_t key_len =
+		AEAD_VALID_KEY_LENS[rand32() % ARRAY_SIZE(AEAD_VALID_KEY_LENS)];
+	size_t tag_len =
+		AEAD_VALID_TAG_LENS[rand32() % ARRAY_SIZE(AEAD_VALID_TAG_LENS)];
+	u8 raw_key[AEAD_MAX_KEY_LEN];
+	struct AEAD_KEY *key = alloc_buf(test, sizeof(*key));
+	int err;
+
+	rand_bytes(raw_key, key_len);
+	err = AEAD_PREPAREKEY(key, raw_key, key_len, tag_len);
+	KUNIT_ASSERT_EQ(test, 0, err);
+	*tag_len_ret = tag_len;
+	return key;
+}
+
+/*
+ * Allocate a KUnit-managed slab buffer of length @len bytes and initialize it
+ * with random data.
+ */
+static u8 *aead_alloc_random_data(struct kunit *test, size_t len)
+{
+	u8 *buf = alloc_buf(test, len);
+
+	rand_bytes(buf, len);
+	return buf;
+}
+
+/*
+ * Allocate a KUnit-managed guarded buffer of length @len bytes and initialize
+ * it with random data.
+ */
+static u8 *aead_alloc_random_data_guarded(struct kunit *test, size_t len)
+{
+	u8 *buf = alloc_guarded_buf(test, len);
+
+	rand_bytes(buf, len);
+	return buf;
+}
+
+/* Process the given associated data using a random incremental strategy. */
+static size_t aead_auth_incrementally(struct AEAD_CTX *ctx, const u8 *ad,
+				      size_t ad_len)
+{
+	size_t num_parts = 0;
+	size_t pos = 0;
+
+	while (rand_bool()) {
+		size_t part_len = rand_length(ad_len - pos);
+
+		AEAD_AUTH_UPDATE(ctx, &ad[pos], part_len);
+		pos += part_len;
+		num_parts++;
+	}
+	if (pos < ad_len || rand_bool()) {
+		AEAD_AUTH_UPDATE(ctx, &ad[pos], ad_len - pos);
+		num_parts++;
+	}
+	return num_parts;
+}
+
+/* Process the given en/decrypted data using a random incremental strategy. */
+static size_t aead_crypt_incrementally(struct AEAD_CTX *ctx, u8 *dst,
+				       const u8 *src, size_t data_len, bool enc)
+{
+	size_t num_parts = 0;
+	size_t pos = 0;
+
+	while (rand_bool()) {
+		size_t part_len = rand_length(data_len - pos);
+
+		if (enc)
+			AEAD_ENCRYPT_UPDATE(ctx, &dst[pos], &src[pos],
+					    part_len);
+		else
+			AEAD_DECRYPT_UPDATE(ctx, &dst[pos], &src[pos],
+					    part_len);
+		pos += part_len;
+		num_parts++;
+	}
+	if (pos < data_len || rand_bool()) {
+		if (enc)
+			AEAD_ENCRYPT_UPDATE(ctx, &dst[pos], &src[pos],
+					    data_len - pos);
+		else
+			AEAD_DECRYPT_UPDATE(ctx, &dst[pos], &src[pos],
+					    data_len - pos);
+		num_parts++;
+	}
+	return num_parts;
+}
+
+struct aead_incremental_info {
+	size_t num_data_parts;
+	size_t num_ad_parts;
+};
+
+static const char *aead_incr_info_str(struct kunit *test,
+				      const struct aead_incremental_info *info)
+{
+	const size_t max_str_len = 64;
+	char *str = alloc_buf(test, max_str_len);
+
+	snprintf(str, max_str_len, "num_data_parts=%zu num_ad_parts=%zu",
+		 info->num_data_parts, info->num_ad_parts);
+	return str;
+}
+
+/*
+ * Encrypt data using a random incremental strategy.
+ * Return information about the incremental strategy used.
+ */
+static struct aead_incremental_info
+aead_encrypt_incrementally(struct kunit *test, struct AEAD_CTX *ctx, u8 *dst,
+			   const u8 *src, size_t data_len, u8 *tag,
+			   const u8 *ad, size_t ad_len, const u8 *nonce,
+			   size_t nonce_len, const struct AEAD_KEY *key)
+{
+	struct aead_incremental_info info;
+	int err;
+
+	err = AEAD_INIT(ctx, data_len, ad_len, nonce, nonce_len, key);
+	KUNIT_ASSERT_EQ(test, 0, err);
+	info.num_ad_parts = aead_auth_incrementally(ctx, ad, ad_len);
+	info.num_data_parts = aead_crypt_incrementally(ctx, dst, src, data_len,
+						       /* enc= */ true);
+	AEAD_ENCRYPT_FINAL(ctx, tag);
+	KUNIT_ASSERT_TRUE_MSG(test, mem_is_zero(ctx, sizeof(*ctx)),
+			      "encrypt_final didn't zeroize context");
+	return info;
+}
+
+/*
+ * Decrypt authentic data using a random incremental strategy.
+ * Return information about the incremental strategy used.
+ */
+static struct aead_incremental_info
+aead_decrypt_incrementally(struct kunit *test, struct AEAD_CTX *ctx, u8 *dst,
+			   const u8 *src, size_t data_len, const u8 *tag,
+			   const u8 *ad, size_t ad_len, const u8 *nonce,
+			   size_t nonce_len, const struct AEAD_KEY *key)
+{
+	struct aead_incremental_info info;
+	int err;
+
+	err = AEAD_INIT(ctx, data_len, ad_len, nonce, nonce_len, key);
+	KUNIT_ASSERT_EQ(test, 0, err);
+	info.num_ad_parts = aead_auth_incrementally(ctx, ad, ad_len);
+	info.num_data_parts = aead_crypt_incrementally(ctx, dst, src, data_len,
+						       /* enc= */ false);
+	err = AEAD_DECRYPT_FINAL(ctx, tag);
+	KUNIT_ASSERT_EQ(test, 0, err);
+	KUNIT_ASSERT_TRUE_MSG(test, mem_is_zero(ctx, sizeof(*ctx)),
+			      "decrypt_final didn't zeroize context");
+	return info;
+}
+
+/* Return true if key_len is declared to be a valid key length. */
+static bool aead_is_key_len_expected_valid(size_t key_len)
+{
+	for (size_t i = 0; i < ARRAY_SIZE(AEAD_VALID_KEY_LENS); i++) {
+		if (AEAD_VALID_KEY_LENS[i] == key_len)
+			return true;
+	}
+	return false;
+}
+
+/* Return true if nonce_len is declared to be a valid nonce length. */
+static bool aead_is_nonce_len_expected_valid(size_t nonce_len)
+{
+	for (size_t i = 0; i < ARRAY_SIZE(AEAD_VALID_NONCE_LENS); i++) {
+		if (AEAD_VALID_NONCE_LENS[i] == nonce_len)
+			return true;
+	}
+	return false;
+}
+
+/* Return true if tag_len is declared to be a valid tag length. */
+static bool aead_is_tag_len_expected_valid(size_t tag_len)
+{
+	for (size_t i = 0; i < ARRAY_SIZE(AEAD_VALID_TAG_LENS); i++) {
+		if (AEAD_VALID_TAG_LENS[i] == tag_len)
+			return true;
+	}
+	return false;
+}
+
+struct aead_basic_validation_test_ctx {
+	struct AEAD_KEY key;
+	struct AEAD_CTX ctx;
+	u8 *raw_key_buf_end;
+	u8 *nonce_buf_end;
+	u8 *tag_buf_end;
+	u8 pt[64]; /* plaintext */
+	u8 ct[64]; /* ciphertext */
+	u8 decrypted[64];
+	u8 ad[16]; /* associated data */
+	u8 *unused_buf;
+	size_t data_len;
+	size_t ad_len;
+};
+
+static struct aead_basic_validation_test_ctx *
+aead_alloc_basic_validation_test_ctx(struct kunit *test)
+{
+	struct aead_basic_validation_test_ctx *ctx =
+		alloc_buf(test, sizeof(*ctx));
+
+	memset(ctx, 0, sizeof(*ctx));
+	ctx->raw_key_buf_end =
+		aead_alloc_random_data_guarded(test, AEAD_MAX_KEY_LEN) +
+		AEAD_MAX_KEY_LEN;
+	ctx->nonce_buf_end =
+		aead_alloc_random_data_guarded(test, AEAD_MAX_NONCE_LEN) +
+		AEAD_MAX_NONCE_LEN;
+	ctx->tag_buf_end =
+		aead_alloc_random_data_guarded(test, AEAD_MAX_TAG_LEN) +
+		AEAD_MAX_TAG_LEN;
+
+	/*
+	 * A pointer to this buffer is passed when passing a length that is
+	 * expected to be invalid.  It should never actually be accessed.
+	 */
+	ctx->unused_buf =
+		alloc_buf(test, max3(AEAD_MAX_KEY_LEN, AEAD_MAX_NONCE_LEN,
+				     AEAD_MAX_TAG_LEN));
+
+	ctx->data_len = sizeof(ctx->pt);
+	ctx->ad_len = sizeof(ctx->ad);
+	return ctx;
+}
+
+/*
+ * Given an expected-valid key_len, nonce_len, and tag_len, verify round-trip
+ * encryption and decryption with them.  Use guarded buffers for each of the raw
+ * key, nonce, and tag to detect any buffer overruns in them.  Also, verify that
+ * every byte of the tag is actually checked.
+ */
+static void aead_do_basic_checks(struct kunit *test,
+				 struct aead_basic_validation_test_ctx *ctx,
+				 size_t key_len, size_t nonce_len,
+				 size_t tag_len)
+{
+	/* Set up exact-size guarded buffers for (raw_key, nonce, tag). */
+	const u8 *raw_key = ctx->raw_key_buf_end - key_len;
+	const u8 *nonce = ctx->nonce_buf_end - nonce_len;
+	u8 *tag = ctx->tag_buf_end - tag_len;
+	int err;
+
+	/* Key preparation should succeed. */
+	err = AEAD_PREPAREKEY(&ctx->key, raw_key, key_len, tag_len);
+	KUNIT_ASSERT_EQ_MSG(test, 0, err,
+			    "key_len=%zu, tag_len=%zu wasn't accepted", key_len,
+			    tag_len);
+
+	/* Encryption should succeed. */
+	err = AEAD_ENCRYPT(ctx->ct, ctx->pt, ctx->data_len, tag, ctx->ad,
+			   ctx->ad_len, nonce, nonce_len, &ctx->key);
+	KUNIT_ASSERT_EQ_MSG(
+		test, 0, err,
+		"Encryption failed with key_len=%zu, nonce_len=%zu, tag_len=%zu",
+		key_len, nonce_len, tag_len);
+
+	/* Decryption should succeed and give the original data. */
+	err = AEAD_DECRYPT(ctx->decrypted, ctx->ct, ctx->data_len, tag, ctx->ad,
+			   ctx->ad_len, nonce, nonce_len, &ctx->key);
+	KUNIT_ASSERT_EQ_MSG(
+		test, 0, err,
+		"Decryption failed with key_len=%zu, nonce_len=%zu, tag_len=%zu",
+		key_len, nonce_len, tag_len);
+	KUNIT_ASSERT_MEMEQ_MSG(
+		test, ctx->pt, ctx->decrypted, ctx->data_len,
+		"Decryption gave wrong output with key_len=%zu, nonce_len=%zu, tag_len=%zu",
+		key_len, nonce_len, tag_len);
+
+	/*
+	 * Every byte of the tag should actually be checked.
+	 * And on authentication failure, the dst buffer should be cleared.
+	 */
+	for (size_t i = 0; i < tag_len; i++) {
+		memset(ctx->decrypted, 0xff, ctx->data_len);
+		tag[i] ^= 1;
+		err = AEAD_DECRYPT(ctx->decrypted, ctx->ct, ctx->data_len, tag,
+				   ctx->ad, ctx->ad_len, nonce, nonce_len,
+				   &ctx->key);
+		KUNIT_ASSERT_EQ_MSG(
+			test, -EBADMSG, err,
+			"Decryption with bad auth tag with key_len=%zu, nonce_len=%zu, tag_len=%zu didn't fail with -EBADMSG",
+			key_len, nonce_len, tag_len);
+		KUNIT_ASSERT_TRUE_MSG(
+			test, mem_is_zero(ctx->decrypted, ctx->data_len),
+			"dst wasn't cleared on authentication failure");
+		tag[i] ^= 1;
+	}
+}
+
+/* Verify that the given expected-invalid key_len is actually rejected. */
+static void
+aead_verify_invalid_key_len(struct kunit *test,
+			    struct aead_basic_validation_test_ctx *ctx,
+			    size_t key_len)
+{
+	int err;
+
+	/*
+	 * The preparekey function should reject the key_len.  It should do so
+	 * before writing to the key struct.
+	 */
+	memset(&ctx->key, 0, sizeof(ctx->key));
+	err = AEAD_PREPAREKEY(&ctx->key, ctx->unused_buf, key_len,
+			      AEAD_MAX_TAG_LEN);
+	KUNIT_ASSERT_EQ_MSG(test, -EINVAL, err,
+			    "key_len=%zu wasn't rejected with -EINVAL",
+			    key_len);
+	KUNIT_ASSERT_TRUE_MSG(
+		test, mem_is_zero(&ctx->key, sizeof(ctx->key)),
+		"Key struct was written to before length validation");
+}
+
+/*
+ * Test that every valid key length is accepted and basic checks pass with it,
+ * and test that invalid key lengths are rejected.
+ */
+static void test_aead_all_key_lens(struct kunit *test)
+{
+	struct aead_basic_validation_test_ctx *ctx =
+		aead_alloc_basic_validation_test_ctx(test);
+
+	for (size_t key_len = 0; key_len <= AEAD_MAX_KEY_LEN; key_len++) {
+		if (aead_is_key_len_expected_valid(key_len))
+			aead_do_basic_checks(test, ctx, key_len,
+					     AEAD_MAX_NONCE_LEN,
+					     AEAD_MAX_TAG_LEN);
+		else
+			aead_verify_invalid_key_len(test, ctx, key_len);
+	}
+	aead_verify_invalid_key_len(test, ctx, AEAD_MAX_KEY_LEN + 1);
+	aead_verify_invalid_key_len(test, ctx, AEAD_MAX_KEY_LEN * 2);
+	aead_verify_invalid_key_len(test, ctx, U32_MAX);
+	aead_verify_invalid_key_len(test, ctx, SIZE_MAX);
+}
+
+/* Verify that the given expected-invalid nonce_len is actually rejected. */
+static void
+aead_verify_invalid_nonce_len(struct kunit *test,
+			      struct aead_basic_validation_test_ctx *ctx,
+			      size_t nonce_len)
+{
+	static const u8 raw_key[AEAD_MAX_KEY_LEN];
+	int err;
+
+	/* Key preparation should succeed, as nonce_len isn't given yet. */
+	err = AEAD_PREPAREKEY(&ctx->key, raw_key, sizeof(raw_key),
+			      AEAD_MAX_TAG_LEN);
+	KUNIT_ASSERT_EQ(test, 0, err);
+
+	/* The init function should reject the nonce_len. */
+	memset(&ctx->ctx, 0, sizeof(ctx->ctx));
+	err = AEAD_INIT(&ctx->ctx, ctx->data_len, ctx->ad_len, ctx->unused_buf,
+			nonce_len, &ctx->key);
+	KUNIT_ASSERT_EQ_MSG(test, -EINVAL, err,
+			    "nonce_len=%zu wasn't rejected with -EINVAL (init)",
+			    nonce_len);
+	KUNIT_ASSERT_TRUE_MSG(
+		test, mem_is_zero(&ctx->ctx, sizeof(ctx->ctx)),
+		"Context struct was written to before length validation");
+
+	/* The encrypt function should reject the nonce_len. */
+	err = AEAD_ENCRYPT(ctx->ct, ctx->pt, ctx->data_len, ctx->unused_buf,
+			   ctx->ad, ctx->ad_len, ctx->unused_buf, nonce_len,
+			   &ctx->key);
+	KUNIT_ASSERT_EQ_MSG(
+		test, -EINVAL, err,
+		"nonce_len=%zu wasn't rejected with -EINVAL (encrypt)",
+		nonce_len);
+
+	/* The decrypt function should reject the nonce_len. */
+	err = AEAD_DECRYPT(ctx->pt, ctx->ct, ctx->data_len, ctx->unused_buf,
+			   ctx->ad, ctx->ad_len, ctx->unused_buf, nonce_len,
+			   &ctx->key);
+	KUNIT_ASSERT_EQ_MSG(
+		test, -EINVAL, err,
+		"nonce_len=%zu wasn't rejected with -EINVAL (decrypt)",
+		nonce_len);
+}
+
+/*
+ * Test that every valid nonce length is accepted and basic checks pass with it,
+ * and test that invalid nonce lengths are rejected.
+ */
+static void test_aead_all_nonce_lens(struct kunit *test)
+{
+	struct aead_basic_validation_test_ctx *ctx =
+		aead_alloc_basic_validation_test_ctx(test);
+
+	for (size_t nonce_len = 0; nonce_len <= AEAD_MAX_NONCE_LEN;
+	     nonce_len++) {
+		if (aead_is_nonce_len_expected_valid(nonce_len))
+			aead_do_basic_checks(test, ctx, AEAD_MAX_KEY_LEN,
+					     nonce_len, AEAD_MAX_TAG_LEN);
+		else
+			aead_verify_invalid_nonce_len(test, ctx, nonce_len);
+	}
+	aead_verify_invalid_nonce_len(test, ctx, AEAD_MAX_NONCE_LEN + 1);
+	aead_verify_invalid_nonce_len(test, ctx, AEAD_MAX_NONCE_LEN * 2);
+	aead_verify_invalid_nonce_len(test, ctx, U32_MAX);
+	aead_verify_invalid_nonce_len(test, ctx, SIZE_MAX);
+}
+
+/* Verify that the given expected-invalid tag_len is actually rejected. */
+static void
+aead_verify_invalid_tag_len(struct kunit *test,
+			    struct aead_basic_validation_test_ctx *ctx,
+			    size_t tag_len)
+{
+	static const u8 raw_key[AEAD_MAX_KEY_LEN];
+	int err;
+
+	/*
+	 * The preparekey function should reject the tag_len.  It should do so
+	 * before writing to the key struct.
+	 */
+	memset(&ctx->key, 0, sizeof(ctx->key));
+	err = AEAD_PREPAREKEY(&ctx->key, raw_key, sizeof(raw_key), tag_len);
+	KUNIT_ASSERT_EQ_MSG(test, -EINVAL, err,
+			    "tag_len=%zu wasn't rejected with -EINVAL",
+			    tag_len);
+	KUNIT_ASSERT_TRUE_MSG(
+		test, mem_is_zero(&ctx->key, sizeof(ctx->key)),
+		"Key struct was written to before length validation");
+}
+
+/*
+ * Test that every valid authentication tag length is accepted and basic checks
+ * pass with it, and test that invalid authentication tag lengths are rejected.
+ */
+static void test_aead_all_tag_lens(struct kunit *test)
+{
+	struct aead_basic_validation_test_ctx *ctx =
+		aead_alloc_basic_validation_test_ctx(test);
+
+	for (size_t tag_len = 0; tag_len <= AEAD_MAX_TAG_LEN; tag_len++) {
+		if (aead_is_tag_len_expected_valid(tag_len))
+			aead_do_basic_checks(test, ctx, AEAD_MAX_KEY_LEN,
+					     AEAD_MAX_NONCE_LEN, tag_len);
+		else
+			aead_verify_invalid_tag_len(test, ctx, tag_len);
+	}
+	aead_verify_invalid_tag_len(test, ctx, AEAD_MAX_TAG_LEN + 1);
+	aead_verify_invalid_tag_len(test, ctx, AEAD_MAX_TAG_LEN * 2);
+	aead_verify_invalid_tag_len(test, ctx, U32_MAX);
+	aead_verify_invalid_tag_len(test, ctx, SIZE_MAX);
+}
+
+/*
+ * Test that one-shot encryption and decryption are consistent with each other
+ * and with incremental encryption and decryption.
+ */
+static void test_aead_incremental_updates(struct kunit *test)
+{
+	const size_t max_data_len = 1024;
+	const size_t max_ad_len = 512;
+	const size_t nonce_len = AEAD_MAX_NONCE_LEN;
+	size_t tag_len;
+	struct AEAD_KEY *key = aead_alloc_random_key(test, &tag_len);
+	struct AEAD_CTX *ctx = alloc_buf(test, sizeof(*ctx));
+	u8 *pt = aead_alloc_random_data(test, max_data_len);
+	u8 *ad = aead_alloc_random_data(test, max_ad_len);
+	u8 *nonce = aead_alloc_random_data(test, nonce_len);
+	u8 *ct = alloc_buf(test, max_data_len);
+	u8 *ct2 = alloc_buf(test, max_data_len);
+	u8 *decrypted = alloc_buf(test, max_data_len);
+	u8 *tag = alloc_buf(test, tag_len);
+	u8 *tag2 = alloc_buf(test, tag_len);
+	int err;
+
+	for (int i = 0; i < 500; i++) {
+		/* Select the lengths to test. */
+		const size_t data_len = rand_length(max_data_len);
+		const size_t ad_len = rand_length(max_ad_len);
+		struct aead_incremental_info incr_info;
+
+		/* Try one-shot encryption and decryption. */
+		err = AEAD_ENCRYPT(ct, pt, data_len, tag, ad, ad_len, nonce,
+				   nonce_len, key);
+		KUNIT_ASSERT_EQ(test, 0, err);
+		err = AEAD_DECRYPT(decrypted, ct, data_len, tag, ad, ad_len,
+				   nonce, nonce_len, key);
+		KUNIT_ASSERT_EQ(test, 0, err);
+		KUNIT_ASSERT_MEMEQ_MSG(
+			test, pt, decrypted, data_len,
+			"Decryption didn't invert encryption; data_len=%zu, ad_len=%zu",
+			data_len, ad_len);
+
+		/* Try incremental encryption and decryption. */
+		incr_info = aead_encrypt_incrementally(test, ctx, ct2, pt,
+						       data_len, tag2, ad,
+						       ad_len, nonce, nonce_len,
+						       key);
+		KUNIT_ASSERT_MEMEQ_MSG(
+			test, ct, ct2, data_len,
+			"One-shot and incremental encryption gave different ciphertexts; data_len=%zu ad_len=%zu %s",
+			data_len, ad_len, aead_incr_info_str(test, &incr_info));
+		KUNIT_ASSERT_MEMEQ_MSG(
+			test, tag, tag2, tag_len,
+			"One-shot and incremental encryption gave different auth tags; data_len=%zu ad_len=%zu %s",
+			data_len, ad_len, aead_incr_info_str(test, &incr_info));
+		incr_info = aead_decrypt_incrementally(test, ctx, decrypted,
+						       ct2, data_len, tag2, ad,
+						       ad_len, nonce, nonce_len,
+						       key);
+		KUNIT_ASSERT_MEMEQ_MSG(
+			test, pt, decrypted, data_len,
+			"One-shot and incremental decryption gave different plaintexts; data_len=%zu ad_len=%zu %s",
+			data_len, ad_len, aead_incr_info_str(test, &incr_info));
+	}
+}
+
+/*
+ * Test using guarded buffers for the plaintext, ciphertext, and associated
+ * data.  This detects out-of-bounds accesses, even in assembly code.
+ *
+ * Note: other test cases cover overrun of raw_key, nonce, and tag.
+ */
+static void test_aead_data_buffer_overruns(struct kunit *test)
+{
+	const size_t max_data_len = 1024;
+	const size_t max_ad_len = 512;
+	const size_t nonce_len = AEAD_MAX_NONCE_LEN;
+	size_t tag_len;
+	struct AEAD_KEY *key = aead_alloc_random_key(test, &tag_len);
+	const u8 *nonce = aead_alloc_random_data(test, nonce_len);
+	const u8 *pt_end = aead_alloc_random_data_guarded(test, max_data_len) +
+			   max_data_len;
+	const u8 *ad_end =
+		aead_alloc_random_data_guarded(test, max_ad_len) + max_ad_len;
+	u8 *ct_end = alloc_guarded_buf(test, max_data_len) + max_data_len;
+	u8 *decrypted_end =
+		alloc_guarded_buf(test, max_data_len) + max_data_len;
+	u8 *tag = alloc_buf(test, tag_len);
+
+	for (int i = 0; i < 200; i++) {
+		/* Select the lengths to test. */
+		const size_t data_len = rand_length(max_data_len);
+		const size_t ad_len = rand_length(max_ad_len);
+		/* Set up exact-size guarded buffers. */
+		const u8 *pt = pt_end - data_len;
+		const u8 *ad = ad_end - ad_len;
+		u8 *ct = ct_end - data_len;
+		u8 *decrypted = decrypted_end - data_len;
+		int err;
+
+		/* Encrypt and decrypt. */
+		err = AEAD_ENCRYPT(ct, pt, data_len, tag, ad, ad_len, nonce,
+				   nonce_len, key);
+		KUNIT_ASSERT_EQ(test, 0, err);
+		err = AEAD_DECRYPT(decrypted, ct, data_len, tag, ad, ad_len,
+				   nonce, nonce_len, key);
+		KUNIT_ASSERT_EQ(test, 0, err);
+		KUNIT_ASSERT_MEMEQ_MSG(
+			test, pt, decrypted, data_len,
+			"Decryption didn't invert encryption; data_len=%zu, ad_len=%zu",
+			data_len, ad_len);
+	}
+}
+
+/*
+ * Test that encryption and decryption produce the same results regardless of
+ * how the buffers are aligned in memory.
+ */
+static void test_aead_alignment_consistency(struct kunit *test)
+{
+	const size_t max_data_len = 4096;
+	const size_t max_ad_len = 4096;
+	const size_t max_offset = 128;
+	const size_t nonce_len = AEAD_MAX_NONCE_LEN;
+	const size_t key_len = AEAD_MAX_KEY_LEN;
+	const size_t tag_len = AEAD_MAX_TAG_LEN;
+	u8 *raw_key1_buf = alloc_buf(test, key_len + max_offset);
+	u8 *raw_key2_buf = alloc_buf(test, key_len + max_offset);
+	u8 *nonce1_buf = alloc_buf(test, nonce_len + max_offset);
+	u8 *nonce2_buf = alloc_buf(test, nonce_len + max_offset);
+	u8 *pt1_buf = alloc_buf(test, max_data_len);
+	u8 *pt2_buf = alloc_buf(test, max_data_len);
+	u8 *ct1_buf = alloc_buf(test, max_data_len);
+	u8 *ct2_buf = alloc_buf(test, max_data_len);
+	u8 *ad1_buf = alloc_buf(test, max_ad_len);
+	u8 *ad2_buf = alloc_buf(test, max_ad_len);
+	u8 *tag1_buf = alloc_buf(test, tag_len + max_offset);
+	u8 *tag2_buf = alloc_buf(test, tag_len + max_offset);
+	struct AEAD_KEY *key = alloc_buf(test, sizeof(*key));
+	int err;
+
+	for (int i = 0; i < 100; i++) {
+		/* Generate lengths. */
+		size_t data_len = rand_length(max_data_len);
+		size_t ad_len = rand_length(max_ad_len);
+
+		/* Generate two sets of alignments. */
+		u8 *raw_key1 = raw_key1_buf + rand_offset(max_offset);
+		u8 *raw_key2 = raw_key2_buf + rand_offset(max_offset);
+		u8 *nonce1 = nonce1_buf + rand_offset(max_offset);
+		u8 *nonce2 = nonce2_buf + rand_offset(max_offset);
+		u8 *pt1 = pt1_buf + rand_offset(max_data_len - data_len);
+		u8 *pt2 = pt2_buf + rand_offset(max_data_len - data_len);
+		u8 *ct1 = ct1_buf + rand_offset(max_data_len - data_len);
+		u8 *ct2 = ct2_buf + rand_offset(max_data_len - data_len);
+		u8 *ad1 = ad1_buf + rand_offset(max_ad_len - ad_len);
+		u8 *ad2 = ad2_buf + rand_offset(max_ad_len - ad_len);
+		u8 *tag1 = tag1_buf + rand_offset(max_offset);
+		u8 *tag2 = tag2_buf + rand_offset(max_offset);
+
+		/*
+		 * Generate inputs in the first set of buffers using the first
+		 * set of alignments.
+		 */
+		rand_bytes(raw_key1, key_len);
+		rand_bytes(nonce1, nonce_len);
+		rand_bytes(pt1, data_len);
+		rand_bytes(ad1, ad_len);
+
+		/*
+		 * Copy the inputs to the second set of buffers using the second
+		 * set of alignments.
+		 */
+		memcpy(raw_key2, raw_key1, key_len);
+		memcpy(nonce2, nonce1, nonce_len);
+		memcpy(pt2, pt1, data_len);
+		memcpy(ad2, ad1, ad_len);
+
+		/* Verify encryption consistency. */
+
+		err = AEAD_PREPAREKEY(key, raw_key1, key_len, tag_len);
+		KUNIT_ASSERT_EQ(test, 0, err);
+		err = AEAD_ENCRYPT(ct1, pt1, data_len, tag1, ad1, ad_len,
+				   nonce1, nonce_len, key);
+		KUNIT_ASSERT_EQ(test, 0, err);
+
+		err = AEAD_PREPAREKEY(key, raw_key2, key_len, tag_len);
+		KUNIT_ASSERT_EQ(test, 0, err);
+		err = AEAD_ENCRYPT(ct2, pt2, data_len, tag2, ad2, ad_len,
+				   nonce2, nonce_len, key);
+		KUNIT_ASSERT_EQ(test, 0, err);
+
+		KUNIT_ASSERT_MEMEQ(test, ct1, ct2, data_len);
+		KUNIT_ASSERT_MEMEQ(test, tag1, tag2, tag_len);
+
+		/* Verify decryption consistency. */
+
+		err = AEAD_PREPAREKEY(key, raw_key1, key_len, tag_len);
+		KUNIT_ASSERT_EQ(test, 0, err);
+		err = AEAD_DECRYPT(pt1, ct1, data_len, tag1, ad1, ad_len,
+				   nonce1, nonce_len, key);
+		KUNIT_ASSERT_EQ(test, 0, err);
+
+		err = AEAD_PREPAREKEY(key, raw_key2, key_len, tag_len);
+		KUNIT_ASSERT_EQ(test, 0, err);
+		err = AEAD_DECRYPT(pt2, ct2, data_len, tag2, ad2, ad_len,
+				   nonce2, nonce_len, key);
+		KUNIT_ASSERT_EQ(test, 0, err);
+
+		KUNIT_ASSERT_MEMEQ(test, pt1, pt2, data_len);
+	}
+}
+
+static void test_aead_inplace(struct kunit *test)
+{
+	const size_t max_data_len = 1024;
+	const size_t max_ad_len = 512;
+	const size_t nonce_len = AEAD_MAX_NONCE_LEN;
+	size_t tag_len;
+	struct AEAD_KEY *key = aead_alloc_random_key(test, &tag_len);
+	u8 *data = aead_alloc_random_data(test, max_data_len + tag_len);
+	u8 *data2 = alloc_buf(test, max_data_len + tag_len);
+	u8 *ad = aead_alloc_random_data(test, max_ad_len);
+	const u8 *nonce = aead_alloc_random_data(test, nonce_len);
+
+	for (int i = 0; i < 100; i++) {
+		size_t data_len = rand_length(max_data_len);
+		size_t ad_len = rand_length(max_ad_len);
+		int err;
+
+		/* Encrypt out-of-place. */
+		err = AEAD_ENCRYPT(data2, data, data_len, data2 + data_len, ad,
+				   ad_len, nonce, nonce_len, key);
+		KUNIT_ASSERT_EQ(test, 0, err);
+
+		/* Encrypt in-place. */
+		err = AEAD_ENCRYPT(data, data, data_len, data + data_len, ad,
+				   ad_len, nonce, nonce_len, key);
+		KUNIT_ASSERT_EQ(test, 0, err);
+
+		/* Compare the results. */
+		KUNIT_ASSERT_MEMEQ(test, data2, data, data_len + tag_len);
+
+		/* Decrypt out-of-place. */
+		err = AEAD_DECRYPT(data2, data, data_len, data + data_len, ad,
+				   ad_len, nonce, nonce_len, key);
+		KUNIT_ASSERT_EQ(test, 0, err);
+
+		/* Decrypt in-place. */
+		err = AEAD_DECRYPT(data, data, data_len, data + data_len, ad,
+				   ad_len, nonce, nonce_len, key);
+		KUNIT_ASSERT_EQ(test, 0, err);
+
+		/* Compare the results. */
+		KUNIT_ASSERT_MEMEQ(test, data2, data, data_len);
+	}
+}
+
+/*
+ * Monte-Carlo test for AEAD algorithms.  This deterministically generates
+ * random AEAD inputs, encrypts them, verifies decryption, and computes and
+ * verifies the checksum of all computed (ciphertext, tag) pairs.
+ */
+static void test_aead_monte_carlo(struct kunit *test)
+{
+	const size_t max_data_len = 1024;
+	const size_t max_ad_len = 293;
+	u8 raw_key[AEAD_MAX_KEY_LEN];
+	u8 nonce[AEAD_MAX_NONCE_LEN];
+	u8 tag[AEAD_MAX_TAG_LEN];
+	u8 *pt = alloc_buf(test, max_data_len);
+	u8 *ct = alloc_buf(test, max_data_len);
+	u8 *decrypted = alloc_buf(test, max_data_len);
+	u8 *ad = alloc_buf(test, max_ad_len);
+	struct AEAD_KEY *key = alloc_buf(test, sizeof(*key));
+	struct blake2s_ctx checksum_ctx;
+	u8 actual_checksum[BLAKE2S_HASH_SIZE];
+	int err;
+
+	blake2s_init(&checksum_ctx, BLAKE2S_HASH_SIZE);
+
+	for (size_t data_len = 0; data_len <= max_data_len; data_len++) {
+		size_t ad_len = data_len % max_ad_len;
+		size_t key_len =
+			AEAD_VALID_KEY_LENS[data_len %
+					    ARRAY_SIZE(AEAD_VALID_KEY_LENS)];
+		size_t nonce_len =
+			AEAD_VALID_NONCE_LENS[data_len %
+					      ARRAY_SIZE(AEAD_VALID_NONCE_LENS)];
+		size_t tag_len =
+			AEAD_VALID_TAG_LENS[data_len %
+					    ARRAY_SIZE(AEAD_VALID_TAG_LENS)];
+
+		rand_bytes_seeded_from_len(pt, data_len);
+		rand_bytes_seeded_from_len(ad, ad_len);
+		rand_bytes_seeded_from_len(raw_key, key_len);
+		rand_bytes_seeded_from_len(nonce, nonce_len);
+
+		err = AEAD_PREPAREKEY(key, raw_key, key_len, tag_len);
+		KUNIT_ASSERT_EQ(test, 0, err);
+
+		err = AEAD_ENCRYPT(ct, pt, data_len, tag, ad, ad_len, nonce,
+				   nonce_len, key);
+		KUNIT_ASSERT_EQ_MSG(
+			test, 0, err,
+			"Encryption failed with data_len=%zu, ad_len=%zu",
+			data_len, ad_len);
+		err = AEAD_DECRYPT(decrypted, ct, data_len, tag, ad, ad_len,
+				   nonce, nonce_len, key);
+		KUNIT_ASSERT_EQ_MSG(
+			test, 0, err,
+			"Decryption failed with data_len=%zu, ad_len=%zu",
+			data_len, ad_len);
+		KUNIT_ASSERT_MEMEQ_MSG(
+			test, pt, decrypted, data_len,
+			"Decryption didn't invert encryption; data_len=%zu, ad_len=%zu",
+			data_len, ad_len);
+
+		blake2s_update(&checksum_ctx, ct, data_len);
+		blake2s_update(&checksum_ctx, tag, tag_len);
+	}
+
+	blake2s_final(&checksum_ctx, actual_checksum);
+	KUNIT_EXPECT_MEMEQ_MSG(test, actual_checksum, AEAD_MONTE_CARLO_CHECKSUM,
+			       BLAKE2S_HASH_SIZE,
+			       "Monte-Carlo checksum mismatch");
+}
+
+#define IRQ_TEST_DATA_LEN 256
+#define IRQ_TEST_NUM_BUFFERS 3 /* matches max concurrency level */
+
+struct aead_irq_test_slot {
+	/* Fields written only at test case initialization time */
+	u8 raw_key[AEAD_MAX_KEY_LEN];
+	u8 nonce[AEAD_MAX_NONCE_LEN];
+	u8 pt[IRQ_TEST_DATA_LEN];
+	u8 ct[IRQ_TEST_DATA_LEN + AEAD_MAX_TAG_LEN];
+	u8 ad[IRQ_TEST_DATA_LEN];
+
+	/* Fields written throughout the test case */
+	struct AEAD_KEY key;
+	u8 scratch_buf[IRQ_TEST_DATA_LEN + AEAD_MAX_TAG_LEN];
+	int phase;
+	atomic_t in_use;
+};
+
+struct aead_irq_test_state {
+	struct aead_irq_test_slot slots[IRQ_TEST_NUM_BUFFERS];
+};
+
+static bool aead_irq_test_func(void *state_)
+{
+	struct aead_irq_test_state *state = state_;
+	struct aead_irq_test_slot *slot;
+	size_t data_len;
+	bool ok = true;
+
+	/*
+	 * Find a free slot.  This should always succeed, since the number of
+	 * slots is equal to the max concurrency level of kunit_run_irq_test().
+	 */
+	for (slot = &state->slots[0];
+	     slot < &state->slots[ARRAY_SIZE(state->slots)]; slot++) {
+		if (atomic_cmpxchg(&slot->in_use, 0, 1) == 0)
+			break;
+	}
+	if (WARN_ON_ONCE(slot == &state->slots[ARRAY_SIZE(state->slots)]))
+		return false;
+	/*
+	 * This execution context now has exclusive access to 'slot'.
+	 * Next, execute the next operation that the slot is set to perform.
+	 */
+
+	data_len = sizeof(slot->pt);
+	if (slot->phase == 0) {
+		/* Phase 0: Prepare slot's key in current context. */
+		ok = ok && AEAD_PREPAREKEY(&slot->key, slot->raw_key,
+					   sizeof(slot->raw_key),
+					   AEAD_MAX_TAG_LEN) == 0;
+	} else if (slot->phase == 1) {
+		/*
+		 * Phase 1: Encrypt plaintext using key that may have been
+		 * prepared in a different context.
+		 */
+		ok = ok && AEAD_ENCRYPT(slot->scratch_buf, slot->pt, data_len,
+					&slot->scratch_buf[data_len], slot->ad,
+					sizeof(slot->ad), slot->nonce,
+					sizeof(slot->nonce), &slot->key) == 0;
+		/* Verify the ciphertext (with concatenated auth tag) matches */
+		ok = ok &&
+		     memcmp(slot->scratch_buf, slot->ct, sizeof(slot->ct)) == 0;
+	} else {
+		/*
+		 * Phase 2: Decrypt ciphertext using key that may have been
+		 * prepared in a different context.
+		 */
+		ok = ok && AEAD_DECRYPT(slot->scratch_buf, slot->ct, data_len,
+					&slot->ct[data_len], slot->ad,
+					sizeof(slot->ad), slot->nonce,
+					sizeof(slot->nonce), &slot->key) == 0;
+		/* Verify the plaintext matches. */
+		ok = ok && memcmp(slot->scratch_buf, slot->pt, data_len) == 0;
+	}
+	slot->phase = (slot->phase + 1) % 3;
+	atomic_set_release(&slot->in_use, 0);
+	return ok;
+}
+
+/*
+ * Test that encryption and decryption produce the correct results in task,
+ * softirq, and hardirq contexts running concurrently -- including with keys
+ * prepared in other contexts.  This is needed to cover fallback code paths that
+ * execute in contexts where FPU or vector registers cannot be used.
+ */
+static void test_aead_interrupt_context(struct kunit *test)
+{
+	struct aead_irq_test_state *state = alloc_buf(test, sizeof(*state));
+
+	memset(state, 0, sizeof(*state));
+
+	/*
+	 * For each slot, generate a set of AEAD inputs: a key, a nonce, a
+	 * plaintext, and some associated data.  Then generate the corresponding
+	 * ciphertext with concatenated auth tag.
+	 */
+	for (int i = 0; i < IRQ_TEST_NUM_BUFFERS; i++) {
+		struct aead_irq_test_slot *slot = &state->slots[i];
+		int err;
+
+		rand_bytes(slot->raw_key, sizeof(slot->raw_key));
+		rand_bytes(slot->nonce, sizeof(slot->nonce));
+		rand_bytes(slot->pt, sizeof(slot->pt));
+		rand_bytes(slot->ad, sizeof(slot->ad));
+		err = AEAD_PREPAREKEY(&slot->key, slot->raw_key,
+				      sizeof(slot->raw_key), AEAD_MAX_TAG_LEN);
+		KUNIT_ASSERT_EQ(test, 0, err);
+		err = AEAD_ENCRYPT(slot->ct, slot->pt, sizeof(slot->pt),
+				   &slot->ct[sizeof(slot->pt)], slot->ad,
+				   sizeof(slot->ad), slot->nonce,
+				   sizeof(slot->nonce), &slot->key);
+		KUNIT_ASSERT_EQ(test, 0, err);
+	}
+
+	kunit_run_irq_test(test, aead_irq_test_func, 100000, state);
+}
+
+/* Benchmark AEAD encryption and decryption on various data lengths. */
+static void benchmark_aead(struct kunit *test)
+{
+	static const size_t data_lens_to_test[] = {
+		16, 64, 128, 256, 512, 1024, 1420, 4096, 16384,
+	};
+	const size_t max_data_len = 16384;
+	const size_t ad_len = 16;
+	const size_t key_len = AEAD_MAX_KEY_LEN;
+	const size_t nonce_len = AEAD_MAX_NONCE_LEN;
+	const size_t tag_len = AEAD_MAX_TAG_LEN;
+	const u8 *raw_key, *nonce, *ad;
+	u8 *pt, *ct, *tag;
+	struct AEAD_KEY *key;
+	int err;
+
+	if (!IS_ENABLED(CONFIG_CRYPTO_LIB_BENCHMARK))
+		kunit_skip(test, "not enabled");
+
+	raw_key = aead_alloc_random_data(test, key_len);
+	nonce = aead_alloc_random_data(test, nonce_len);
+	ad = aead_alloc_random_data(test, ad_len);
+	pt = aead_alloc_random_data(test, max_data_len);
+	ct = alloc_buf(test, max_data_len);
+	tag = alloc_buf(test, tag_len);
+
+	key = alloc_buf(test, sizeof(*key));
+	err = AEAD_PREPAREKEY(key, raw_key, key_len, tag_len);
+	KUNIT_ASSERT_EQ(test, 0, err);
+
+	/* Warm-up */
+	for (size_t i = 0; i < 10000000; i += max_data_len) {
+		err = AEAD_ENCRYPT(ct, pt, max_data_len, tag, ad, ad_len, nonce,
+				   nonce_len, key);
+		KUNIT_ASSERT_EQ(test, 0, err);
+		err = AEAD_DECRYPT(pt, ct, max_data_len, tag, ad, ad_len, nonce,
+				   nonce_len, key);
+		KUNIT_ASSERT_EQ(test, 0, err);
+	}
+
+	for (size_t i = 0; i < ARRAY_SIZE(data_lens_to_test); i++) {
+		size_t data_len = data_lens_to_test[i];
+		size_t num_iters = 10000000 / (data_len + 128);
+		u64 t_enc, t_dec;
+		bool ok = true;
+
+		KUNIT_ASSERT_LE(test, data_len, max_data_len);
+
+		preempt_disable();
+
+		t_enc = ktime_get_ns();
+		for (size_t j = 0; j < num_iters; j++) {
+			err = AEAD_ENCRYPT(ct, pt, data_len, tag, ad, ad_len,
+					   nonce, nonce_len, key);
+			ok &= (err == 0);
+		}
+		t_enc = ktime_get_ns() - t_enc;
+
+		t_dec = ktime_get_ns();
+		for (size_t j = 0; j < num_iters; j++) {
+			err = AEAD_DECRYPT(pt, ct, data_len, tag, ad, ad_len,
+					   nonce, nonce_len, key);
+			ok &= (err == 0);
+		}
+		t_dec = ktime_get_ns() - t_dec;
+
+		preempt_enable();
+
+		KUNIT_ASSERT_TRUE_MSG(test, ok, "data_len=%zu", data_len);
+
+		kunit_info(test, "data_len=%zu: enc %llu MB/s, dec %llu MB/s",
+			   data_len,
+			   div64_u64((u64)data_len * num_iters * 1000,
+				     t_enc ?: 1),
+			   div64_u64((u64)data_len * num_iters * 1000,
+				     t_dec ?: 1));
+	}
+}
+
+/* clang-format off */
+#define AEAD_KUNIT_CASES				\
+	KUNIT_CASE(test_aead_all_key_lens),		\
+	KUNIT_CASE(test_aead_all_nonce_lens),		\
+	KUNIT_CASE(test_aead_all_tag_lens),		\
+	KUNIT_CASE(test_aead_incremental_updates),	\
+	KUNIT_CASE(test_aead_data_buffer_overruns),	\
+	KUNIT_CASE(test_aead_alignment_consistency),	\
+	KUNIT_CASE(test_aead_inplace),			\
+	KUNIT_CASE(test_aead_monte_carlo),		\
+	KUNIT_CASE(test_aead_interrupt_context),	\
+	KUNIT_CASE(benchmark_aead)
diff --git a/lib/crypto/tests/aes_cbc_macs_kunit.c b/lib/crypto/tests/aes_cbc_macs_kunit.c
index ae37452..6afdb3a 100644
--- a/lib/crypto/tests/aes_cbc_macs_kunit.c
+++ b/lib/crypto/tests/aes_cbc_macs_kunit.c
@@ -34,18 +34,9 @@ static void aes_cmac_withtestkey(const u8 *data, size_t data_len,
 static int aes_cbc_macs_suite_init(struct kunit_suite *suite)
 {
 	u8 raw_key[AES_KEYSIZE_256];
-	int err;
 
 	rand_bytes_seeded_from_len(raw_key, sizeof(raw_key));
-	err = aes_cmac_preparekey(&test_key, raw_key, sizeof(raw_key));
-	if (err)
-		return err;
-	return hash_suite_init(suite);
-}
-
-static void aes_cbc_macs_suite_exit(struct kunit_suite *suite)
-{
-	hash_suite_exit(suite);
+	return aes_cmac_preparekey(&test_key, raw_key, sizeof(raw_key));
 }
 
 /* Verify compatibility of the AES-CMAC implementation with RFC 4493. */
@@ -218,7 +209,6 @@ static struct kunit_suite aes_cbc_macs_test_suite = {
 	.name = "aes_cbc_macs",
 	.test_cases = aes_cbc_macs_test_cases,
 	.suite_init = aes_cbc_macs_suite_init,
-	.suite_exit = aes_cbc_macs_suite_exit,
 };
 kunit_test_suite(aes_cbc_macs_test_suite);
 
diff --git a/lib/crypto/tests/aes_ccm_kunit.c b/lib/crypto/tests/aes_ccm_kunit.c
new file mode 100644
index 0000000..e8f2b34
--- /dev/null
+++ b/lib/crypto/tests/aes_ccm_kunit.c
@@ -0,0 +1,356 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * KUnit test suite for AES-CCM
+ *
+ * Copyright 2026 Google LLC
+ */
+#include <crypto/aes-ccm.h>
+#include <crypto/blake2s.h>
+#include "test-utils.h"
+
+/* AES-CCM test vectors from external sources */
+static const struct aes_ccm_testvec {
+	const char *name;
+	const char *key;
+	size_t key_len;
+	const char *nonce;
+	size_t nonce_len;
+	const char *ad;
+	size_t ad_len;
+	const char *ptext;
+	const char *ctext;
+	size_t data_len;
+	const char *tag;
+	size_t tag_len;
+} aes_ccm_testvecs[] = {
+	{
+		.name = "RFC 3610 Packet Vector #1",
+		.key = "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7"
+		       "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf",
+		.key_len = 16,
+		.nonce = "\x00\x00\x00\x03\x02\x01\x00\xa0"
+			 "\xa1\xa2\xa3\xa4\xa5",
+		.nonce_len = 13,
+		.ad = "\x00\x01\x02\x03\x04\x05\x06\x07",
+		.ad_len = 8,
+		.ptext = "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+			 "\x10\x11\x12\x13\x14\x15\x16\x17"
+			 "\x18\x19\x1a\x1b\x1c\x1d\x1e",
+		.ctext = "\x58\x8c\x97\x9a\x61\xc6\x63\xd2"
+			 "\xf0\x66\xd0\xc2\xc0\xf9\x89\x80"
+			 "\x6d\x5f\x6b\x61\xda\xc3\x84",
+		.data_len = 23,
+		.tag = "\x17\xe8\xd1\x2c\xfd\xf9\x26\xe0",
+		.tag_len = 8,
+	},
+	{
+		.name = "RFC 3610 Packet Vector #5",
+		.key = "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7"
+		       "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf",
+		.key_len = 16,
+		.nonce = "\x00\x00\x00\x07\x06\x05\x04\xa0"
+			 "\xa1\xa2\xa3\xa4\xa5",
+		.nonce_len = 13,
+		.ad = "\x00\x01\x02\x03\x04\x05\x06\x07"
+		      "\x08\x09\x0a\x0b",
+		.ad_len = 12,
+		.ptext = "\x0c\x0d\x0e\x0f\x10\x11\x12\x13"
+			 "\x14\x15\x16\x17\x18\x19\x1a\x1b"
+			 "\x1c\x1d\x1e\x1f",
+		.ctext = "\xdc\xf1\xfb\x7b\x5d\x9e\x23\xfb"
+			 "\x9d\x4e\x13\x12\x53\x65\x8a\xd8"
+			 "\x6e\xbd\xca\x3e",
+		.data_len = 20,
+		.tag = "\x51\xe8\x3f\x07\x7d\x9c\x2d\x93",
+		.tag_len = 8,
+	},
+	{
+		.name = "RFC 3610 Packet Vector #9",
+		.key = "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7"
+		       "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf",
+		.key_len = 16,
+		.nonce = "\x00\x00\x00\x0b\x0a\x09\x08\xa0"
+			 "\xa1\xa2\xa3\xa4\xa5",
+		.nonce_len = 13,
+		.ad = "\x00\x01\x02\x03\x04\x05\x06\x07",
+		.ad_len = 8,
+		.ptext = "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+			 "\x10\x11\x12\x13\x14\x15\x16\x17"
+			 "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
+			 "\x20",
+		.ctext = "\x82\x53\x1a\x60\xcc\x24\x94\x5a"
+			 "\x4b\x82\x79\x18\x1a\xb5\xc8\x4d"
+			 "\xf2\x1c\xe7\xf9\xb7\x3f\x42\xe1"
+			 "\x97",
+		.data_len = 25,
+		.tag = "\xea\x9c\x07\xe5\x6b\x5e\xb1\x7e"
+		       "\x5f\x4e",
+		.tag_len = 10,
+	},
+	{
+		.name = "NIST SP 800-38C Example 1",
+		.key = "\x40\x41\x42\x43\x44\x45\x46\x47"
+		       "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
+		.key_len = 16,
+		.nonce = "\x10\x11\x12\x13\x14\x15\x16",
+		.nonce_len = 7,
+		.ad = "\x00\x01\x02\x03\x04\x05\x06\x07",
+		.ad_len = 8,
+		.ptext = "\x20\x21\x22\x23",
+		.ctext = "\x71\x62\x01\x5b",
+		.data_len = 4,
+		.tag = "\x4d\xac\x25\x5d",
+		.tag_len = 4,
+	},
+	{
+		.name = "NIST SP 800-38C Example 2",
+		.key = "\x40\x41\x42\x43\x44\x45\x46\x47"
+		       "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
+		.key_len = 16,
+		.nonce = "\x10\x11\x12\x13\x14\x15\x16\x17",
+		.nonce_len = 8,
+		.ad = "\x00\x01\x02\x03\x04\x05\x06\x07"
+		      "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+		.ad_len = 16,
+		.ptext = "\x20\x21\x22\x23\x24\x25\x26\x27"
+			 "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f",
+		.ctext = "\xd2\xa1\xf0\xe0\x51\xea\x5f\x62"
+			 "\x08\x1a\x77\x92\x07\x3d\x59\x3d",
+		.data_len = 16,
+		.tag = "\x1f\xc6\x4f\xbf\xac\xcd",
+		.tag_len = 6,
+	},
+	{
+		.name = "NIST SP 800-38C Example 3",
+		.key = "\x40\x41\x42\x43\x44\x45\x46\x47"
+		       "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
+		.key_len = 16,
+		.nonce = "\x10\x11\x12\x13\x14\x15\x16\x17"
+			 "\x18\x19\x1a\x1b",
+		.nonce_len = 12,
+		.ad = "\x00\x01\x02\x03\x04\x05\x06\x07"
+		      "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+		      "\x10\x11\x12\x13",
+		.ad_len = 20,
+		.ptext = "\x20\x21\x22\x23\x24\x25\x26\x27"
+			 "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f"
+			 "\x30\x31\x32\x33\x34\x35\x36\x37",
+		.ctext = "\xe3\xb2\x01\xa9\xf5\xb7\x1a\x7a"
+			 "\x9b\x1c\xea\xec\xcd\x97\xe7\x0b"
+			 "\x61\x76\xaa\xd9\xa4\x42\x8a\xa5",
+		.data_len = 24,
+		.tag = "\x48\x43\x92\xfb\xc1\xb0\x99\x51",
+		.tag_len = 8,
+	},
+};
+
+static void test_aes_ccm_one_test_vector(struct kunit *test,
+					 const struct aes_ccm_testvec *tv)
+{
+	u8 *ctext = alloc_buf(test, tv->data_len);
+	u8 *decrypted = alloc_buf(test, tv->data_len);
+	u8 *tag = alloc_buf(test, tv->tag_len);
+	struct aes_ccm_key key;
+	int err;
+
+	err = aes_ccm_preparekey(&key, tv->key, tv->key_len, tv->tag_len);
+	KUNIT_ASSERT_EQ_MSG(test, 0, err, "Failed to prepare key for %s",
+			    tv->name);
+
+	err = aes_ccm_encrypt(ctext, tv->ptext, tv->data_len, tag, tv->ad,
+			      tv->ad_len, tv->nonce, tv->nonce_len, &key);
+	KUNIT_ASSERT_EQ_MSG(test, 0, err, "Encryption failed for %s", tv->name);
+	KUNIT_ASSERT_MEMEQ_MSG(test, tv->ctext, ctext, tv->data_len,
+			       "Wrong ciphertext for %s", tv->name);
+	KUNIT_ASSERT_MEMEQ_MSG(test, tag, tv->tag, tv->tag_len,
+			       "Wrong tag for %s", tv->name);
+
+	err = aes_ccm_decrypt(decrypted, ctext, tv->data_len, tag, tv->ad,
+			      tv->ad_len, tv->nonce, tv->nonce_len, &key);
+	KUNIT_ASSERT_EQ_MSG(test, 0, err, "Decryption failed for %s", tv->name);
+	KUNIT_ASSERT_MEMEQ_MSG(test, tv->ptext, decrypted, tv->data_len,
+			       "Wrong plaintext for %s", tv->name);
+}
+
+static void test_aes_ccm_test_vectors(struct kunit *test)
+{
+	for (size_t i = 0; i < ARRAY_SIZE(aes_ccm_testvecs); i++)
+		test_aes_ccm_one_test_vector(test, &aes_ccm_testvecs[i]);
+}
+
+/*
+ * Test NIST SP 800-38C Example 4, which uses a deterministically-generated
+ * 65536-byte associated data string.
+ */
+static void test_aes_ccm_nist_sp800_38c_example4(struct kunit *test)
+{
+	struct aes_ccm_testvec tv = {
+		.name = "NIST SP 800-38C Example 4",
+		.key_len = 16,
+		.nonce_len = 13,
+		.ad_len = 65536,
+		.ctext = "\x69\x91\x5d\xad\x1e\x84\xc6\x37"
+			 "\x6a\x68\xc2\x96\x7e\x4d\xab\x61"
+			 "\x5a\xe0\xfd\x1f\xae\xc4\x4c\xc4"
+			 "\x84\x82\x85\x29\x46\x3c\xcf\x72",
+		.data_len = 32,
+		.tag = "\xb4\xac\x6b\xec\x93\xe8\x59\x8e"
+		       "\x7f\x0d\xad\xbc\xea\x5b",
+		.tag_len = 14,
+	};
+	u8 *key, *nonce, *ad, *ptext;
+
+	key = alloc_buf(test, tv.key_len);
+	for (size_t i = 0; i < tv.key_len; i++)
+		key[i] = 0x40 + i;
+
+	nonce = alloc_guarded_buf(test, tv.nonce_len);
+	for (size_t i = 0; i < tv.nonce_len; i++)
+		nonce[i] = 0x10 + i;
+
+	ad = alloc_guarded_buf(test, tv.ad_len);
+	for (size_t i = 0; i < tv.ad_len; i++)
+		ad[i] = (u8)i;
+
+	ptext = alloc_guarded_buf(test, tv.data_len);
+	for (size_t i = 0; i < tv.data_len; i++)
+		ptext[i] = 0x20 + i;
+
+	tv.key = key;
+	tv.nonce = nonce;
+	tv.ad = ad;
+	tv.ptext = ptext;
+
+	test_aes_ccm_one_test_vector(test, &tv);
+}
+
+static const size_t aes_ccm_valid_key_lens[] = { 16, 24, 32 };
+#define AEAD_MAX_KEY_LEN 32
+#define AEAD_VALID_KEY_LENS aes_ccm_valid_key_lens
+
+static const size_t aes_ccm_valid_nonce_lens[] = { 7, 8, 9, 10, 11, 12, 13 };
+#define AEAD_MAX_NONCE_LEN 13
+#define AEAD_VALID_NONCE_LENS aes_ccm_valid_nonce_lens
+
+static const size_t aes_ccm_valid_tag_lens[] = { 4, 6, 8, 10, 12, 14, 16 };
+#define AEAD_MAX_TAG_LEN 16
+#define AEAD_VALID_TAG_LENS aes_ccm_valid_tag_lens
+
+#define AEAD_KEY aes_ccm_key
+#define AEAD_CTX aes_ccm_ctx
+#define AEAD_PREPAREKEY aes_ccm_preparekey
+#define AEAD_ENCRYPT aes_ccm_encrypt
+#define AEAD_DECRYPT aes_ccm_decrypt
+
+#define AEAD_INIT aes_ccm_init
+#define AEAD_AUTH_UPDATE aes_ccm_auth_update
+#define AEAD_ENCRYPT_UPDATE aes_ccm_encrypt_update
+#define AEAD_DECRYPT_UPDATE aes_ccm_decrypt_update
+#define AEAD_ENCRYPT_FINAL aes_ccm_encrypt_final
+#define AEAD_DECRYPT_FINAL aes_ccm_decrypt_final
+
+/* This value was generated by gen-aead-testvecs.py. */
+static const u8 aes_ccm_monte_carlo_checksum[BLAKE2S_HASH_SIZE] = {
+	0x70, 0x1c, 0xde, 0xa4, 0xe2, 0x03, 0x50, 0xb2, 0xf5, 0x9e, 0x61,
+	0x66, 0xe4, 0xe5, 0x13, 0x1a, 0x00, 0x95, 0x34, 0x03, 0xb7, 0x61,
+	0x2c, 0xdb, 0xc3, 0x15, 0x36, 0x84, 0x93, 0x7f, 0xb4, 0x5b,
+};
+#define AEAD_MONTE_CARLO_CHECKSUM aes_ccm_monte_carlo_checksum
+
+#include "aead-test-template.h"
+
+/*
+ * Test that for each AES-CCM nonce length, the message length is validated
+ * against the correct corresponding maximum message length.
+ */
+static void test_aes_ccm_data_len_too_large(struct kunit *test)
+{
+	static const struct {
+		size_t nonce_len;
+		u64 max_data_len;
+	} lens[] = {
+		/* clang-format off */
+		{ 7, 0xffffffffffffffff }, /* U64_MAX */
+		{ 8, 0xffffffffffffff },
+		{ 9, 0xffffffffffff },
+		{ 10, 0xffffffffff },
+		{ 11, 0xffffffff },
+		{ 12, 0xffffff },
+		{ 13, 0xffff },
+		/* clang-format on */
+	};
+	u8 nonce[13] = {};
+	u8 raw_key[AES_KEYSIZE_256] = {};
+	int err;
+	struct aes_ccm_key *key = alloc_buf(test, sizeof(*key));
+	struct aes_ccm_ctx ctx;
+
+	err = aes_ccm_preparekey(key, raw_key, sizeof(raw_key), 16);
+	KUNIT_ASSERT_EQ(test, 0, err);
+
+	for (size_t i = 0; i < ARRAY_SIZE(lens); i++) {
+		size_t nonce_len = lens[i].nonce_len;
+		u64 max_data_len = lens[i].max_data_len;
+
+		/* data_len <= max_data_len should be accepted. */
+		err = aes_ccm_init(&ctx, 0, 0, nonce, nonce_len, key);
+		KUNIT_ASSERT_EQ_MSG(
+			test, 0, err,
+			"data_len=0 wasn't accepted with nonce_len=%zu",
+			nonce_len);
+		err = aes_ccm_init(&ctx, max_data_len, 0, nonce, nonce_len,
+				   key);
+		KUNIT_ASSERT_EQ_MSG(
+			test, 0, err,
+			"data_len=%llu wasn't accepted with nonce_len=%zu",
+			max_data_len, nonce_len);
+
+		/* data_len > max_data_len should be rejected. */
+		if (max_data_len == U64_MAX)
+			continue;
+		err = aes_ccm_init(&ctx, max_data_len + 1, 0, nonce, nonce_len,
+				   key);
+		KUNIT_ASSERT_EQ_MSG(
+			test, -EOVERFLOW, err,
+			"data_len=%llu wasn't rejected with -EOVERFLOW with nonce_len=%zu (aes_ccm_init)",
+			max_data_len + 1, nonce_len);
+		if (max_data_len + 1 <= SIZE_MAX) {
+			err = aes_ccm_encrypt(NULL, NULL, max_data_len + 1,
+					      NULL, NULL, 0, nonce, nonce_len,
+					      key);
+			KUNIT_ASSERT_EQ_MSG(
+				test, -EOVERFLOW, err,
+				"data_len=%llu wasn't rejected with -EOVERFLOW with nonce_len=%zu (aes_ccm_encrypt)",
+				max_data_len + 1, nonce_len);
+			err = aes_ccm_decrypt(NULL, NULL, max_data_len + 1,
+					      NULL, NULL, 0, nonce, nonce_len,
+					      key);
+			KUNIT_ASSERT_EQ_MSG(
+				test, -EOVERFLOW, err,
+				"data_len=%llu wasn't rejected with -EOVERFLOW with nonce_len=%zu (aes_ccm_decrypt)",
+				max_data_len + 1, nonce_len);
+		}
+		err = aes_ccm_init(&ctx, U64_MAX, 0, nonce, nonce_len, key);
+		KUNIT_ASSERT_EQ_MSG(
+			test, -EOVERFLOW, err,
+			"data_len=U64_MAX wasn't rejected with -EOVERFLOW with nonce_len=%zu (aes_ccm_init)",
+			nonce_len);
+	}
+}
+
+static struct kunit_case aes_ccm_test_cases[] = {
+	KUNIT_CASE(test_aes_ccm_test_vectors),
+	KUNIT_CASE(test_aes_ccm_nist_sp800_38c_example4),
+	KUNIT_CASE(test_aes_ccm_data_len_too_large),
+	AEAD_KUNIT_CASES,
+	{},
+};
+
+static struct kunit_suite aes_ccm_test_suite = {
+	.name = "aes_ccm",
+	.test_cases = aes_ccm_test_cases,
+};
+kunit_test_suite(aes_ccm_test_suite);
+
+MODULE_DESCRIPTION("KUnit tests and benchmark for AES-CCM");
+MODULE_LICENSE("GPL");
diff --git a/lib/crypto/tests/aes_gcm_kunit.c b/lib/crypto/tests/aes_gcm_kunit.c
new file mode 100644
index 0000000..6959d86
--- /dev/null
+++ b/lib/crypto/tests/aes_gcm_kunit.c
@@ -0,0 +1,472 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * KUnit test suite for AES-GCM
+ *
+ * Copyright 2026 Google LLC
+ */
+#include <crypto/aes-gcm.h>
+#include <crypto/blake2s.h>
+#include "test-utils.h"
+
+/* The kernel's AES-GCM implementation supports only 12-byte nonces. */
+#define AES_GCM_NONCE_LEN 12
+
+/*
+ * AES-GCM test vectors from the original paper "The Galois/Counter Mode of
+ * Operation (GCM)" by McGrew & Viega.  Vectors with nonce lengths other than 12
+ * bytes are excluded.
+ */
+static const struct aes_gcm_testvec {
+	const char *name;
+	const char *key;
+	size_t key_len;
+	const char *nonce;
+	size_t nonce_len;
+	const char *ad;
+	size_t ad_len;
+	const char *ptext;
+	const char *ctext;
+	size_t data_len;
+	const char *tag;
+	size_t tag_len;
+} aes_gcm_testvecs[] = {
+	/* AES-128 Test Vectors */
+	{
+		.name = "McGrew & Viega Test Case 1",
+		.key = "\x00\x00\x00\x00\x00\x00\x00\x00"
+		       "\x00\x00\x00\x00\x00\x00\x00\x00",
+		.key_len = 16,
+		.nonce = "\x00\x00\x00\x00\x00\x00\x00\x00"
+			 "\x00\x00\x00\x00",
+		.nonce_len = 12,
+		.ptext = "",
+		.ctext = "",
+		.data_len = 0,
+		.ad = "",
+		.ad_len = 0,
+		.tag = "\x58\xe2\xfc\xce\xfa\x7e\x30\x61"
+		       "\x36\x7f\x1d\x57\xa4\xe7\x45\x5a",
+		.tag_len = 16,
+	},
+	{
+		.name = "McGrew & Viega Test Case 2",
+		.key = "\x00\x00\x00\x00\x00\x00\x00\x00"
+		       "\x00\x00\x00\x00\x00\x00\x00\x00",
+		.key_len = 16,
+		.nonce = "\x00\x00\x00\x00\x00\x00\x00\x00"
+			 "\x00\x00\x00\x00",
+		.nonce_len = 12,
+		.ptext = "\x00\x00\x00\x00\x00\x00\x00\x00"
+			 "\x00\x00\x00\x00\x00\x00\x00\x00",
+		.ctext = "\x03\x88\xda\xce\x60\xb6\xa3\x92"
+			 "\xf3\x28\xc2\xb9\x71\xb2\xfe\x78",
+		.data_len = 16,
+		.ad = "",
+		.ad_len = 0,
+		.tag = "\xab\x6e\x47\xd4\x2c\xec\x13\xbd"
+		       "\xf5\x3a\x67\xb2\x12\x57\xbd\xdf",
+		.tag_len = 16,
+	},
+	{
+		.name = "McGrew & Viega Test Case 3",
+		.key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c"
+		       "\x6d\x6a\x8f\x94\x67\x30\x83\x08",
+		.key_len = 16,
+		.nonce = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad"
+			 "\xde\xca\xf8\x88",
+		.nonce_len = 12,
+		.ptext = "\xd9\x31\x32\x25\xf8\x84\x06\xe5"
+			 "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a"
+			 "\x86\xa7\xa9\x53\x15\x34\xf7\xda"
+			 "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72"
+			 "\x1c\x3c\x0c\x95\x95\x68\x09\x53"
+			 "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25"
+			 "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57"
+			 "\xba\x63\x7b\x39\x1a\xaf\xd2\x55",
+		.ctext = "\x42\x83\x1e\xc2\x21\x77\x74\x24"
+			 "\x4b\x72\x21\xb7\x84\xd0\xd4\x9c"
+			 "\xe3\xaa\x21\x2f\x2c\x02\xa4\xe0"
+			 "\x35\xc1\x7e\x23\x29\xac\xa1\x2e"
+			 "\x21\xd5\x14\xb2\x54\x66\x93\x1c"
+			 "\x7d\x8f\x6a\x5a\xac\x84\xaa\x05"
+			 "\x1b\xa3\x0b\x39\x6a\x0a\xac\x97"
+			 "\x3d\x58\xe0\x91\x47\x3f\x59\x85",
+		.data_len = 64,
+		.ad = "",
+		.ad_len = 0,
+		.tag = "\x4d\x5c\x2a\xf3\x27\xcd\x64\xa6"
+		       "\x2c\xf3\x5a\xbd\x2b\xa6\xfa\xb4",
+		.tag_len = 16,
+	},
+	{
+		.name = "McGrew & Viega Test Case 4",
+		.key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c"
+		       "\x6d\x6a\x8f\x94\x67\x30\x83\x08",
+		.key_len = 16,
+		.nonce = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad"
+			 "\xde\xca\xf8\x88",
+		.nonce_len = 12,
+		.ptext = "\xd9\x31\x32\x25\xf8\x84\x06\xe5"
+			 "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a"
+			 "\x86\xa7\xa9\x53\x15\x34\xf7\xda"
+			 "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72"
+			 "\x1c\x3c\x0c\x95\x95\x68\x09\x53"
+			 "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25"
+			 "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57"
+			 "\xba\x63\x7b\x39",
+		.ctext = "\x42\x83\x1e\xc2\x21\x77\x74\x24"
+			 "\x4b\x72\x21\xb7\x84\xd0\xd4\x9c"
+			 "\xe3\xaa\x21\x2f\x2c\x02\xa4\xe0"
+			 "\x35\xc1\x7e\x23\x29\xac\xa1\x2e"
+			 "\x21\xd5\x14\xb2\x54\x66\x93\x1c"
+			 "\x7d\x8f\x6a\x5a\xac\x84\xaa\x05"
+			 "\x1b\xa3\x0b\x39\x6a\x0a\xac\x97"
+			 "\x3d\x58\xe0\x91",
+		.data_len = 60,
+		.ad = "\xfe\xed\xfa\xce\xde\xad\xbe\xef"
+		      "\xfe\xed\xfa\xce\xde\xad\xbe\xef"
+		      "\xab\xad\xda\xd2",
+		.ad_len = 20,
+		.tag = "\x5b\xc9\x4f\xbc\x32\x21\xa5\xdb"
+		       "\x94\xfa\xe9\x5a\xe7\x12\x1a\x47",
+		.tag_len = 16,
+	},
+
+	/* AES-192 Test Vectors */
+	{
+		.name = "McGrew & Viega Test Case 7",
+		.key = "\x00\x00\x00\x00\x00\x00\x00\x00"
+		       "\x00\x00\x00\x00\x00\x00\x00\x00"
+		       "\x00\x00\x00\x00\x00\x00\x00\x00",
+		.key_len = 24,
+		.nonce = "\x00\x00\x00\x00\x00\x00\x00\x00"
+			 "\x00\x00\x00\x00",
+		.nonce_len = 12,
+		.ptext = "",
+		.ctext = "",
+		.data_len = 0,
+		.ad = "",
+		.ad_len = 0,
+		.tag = "\xcd\x33\xb2\x8a\xc7\x73\xf7\x4b"
+		       "\xa0\x0e\xd1\xf3\x12\x57\x24\x35",
+		.tag_len = 16,
+	},
+	{
+		.name = "McGrew & Viega Test Case 8",
+		.key = "\x00\x00\x00\x00\x00\x00\x00\x00"
+		       "\x00\x00\x00\x00\x00\x00\x00\x00"
+		       "\x00\x00\x00\x00\x00\x00\x00\x00",
+		.key_len = 24,
+		.nonce = "\x00\x00\x00\x00\x00\x00\x00\x00"
+			 "\x00\x00\x00\x00",
+		.nonce_len = 12,
+		.ptext = "\x00\x00\x00\x00\x00\x00\x00\x00"
+			 "\x00\x00\x00\x00\x00\x00\x00\x00",
+		.ctext = "\x98\xe7\x24\x7c\x07\xf0\xfe\x41"
+			 "\x1c\x26\x7e\x43\x84\xb0\xf6\x00",
+		.data_len = 16,
+		.ad = "",
+		.ad_len = 0,
+		.tag = "\x2f\xf5\x8d\x80\x03\x39\x27\xab"
+		       "\x8e\xf4\xd4\x58\x75\x14\xf0\xfb",
+		.tag_len = 16,
+	},
+	{
+		.name = "McGrew & Viega Test Case 9",
+		.key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c"
+		       "\x6d\x6a\x8f\x94\x67\x30\x83\x08"
+		       "\xfe\xff\xe9\x92\x86\x65\x73\x1c",
+		.key_len = 24,
+		.nonce = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad"
+			 "\xde\xca\xf8\x88",
+		.nonce_len = 12,
+		.ptext = "\xd9\x31\x32\x25\xf8\x84\x06\xe5"
+			 "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a"
+			 "\x86\xa7\xa9\x53\x15\x34\xf7\xda"
+			 "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72"
+			 "\x1c\x3c\x0c\x95\x95\x68\x09\x53"
+			 "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25"
+			 "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57"
+			 "\xba\x63\x7b\x39\x1a\xaf\xd2\x55",
+		.ctext = "\x39\x80\xca\x0b\x3c\x00\xe8\x41"
+			 "\xeb\x06\xfa\xc4\x87\x2a\x27\x57"
+			 "\x85\x9e\x1c\xea\xa6\xef\xd9\x84"
+			 "\x62\x85\x93\xb4\x0c\xa1\xe1\x9c"
+			 "\x7d\x77\x3d\x00\xc1\x44\xc5\x25"
+			 "\xac\x61\x9d\x18\xc8\x4a\x3f\x47"
+			 "\x18\xe2\x44\x8b\x2f\xe3\x24\xd9"
+			 "\xcc\xda\x27\x10\xac\xad\xe2\x56",
+		.data_len = 64,
+		.ad = "",
+		.ad_len = 0,
+		.tag = "\x99\x24\xa7\xc8\x58\x73\x36\xbf"
+		       "\xb1\x18\x02\x4d\xb8\x67\x4a\x14",
+		.tag_len = 16,
+	},
+	{
+		.name = "McGrew & Viega Test Case 10",
+		.key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c"
+		       "\x6d\x6a\x8f\x94\x67\x30\x83\x08"
+		       "\xfe\xff\xe9\x92\x86\x65\x73\x1c",
+		.key_len = 24,
+		.nonce = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad"
+			 "\xde\xca\xf8\x88",
+		.nonce_len = 12,
+		.ptext = "\xd9\x31\x32\x25\xf8\x84\x06\xe5"
+			 "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a"
+			 "\x86\xa7\xa9\x53\x15\x34\xf7\xda"
+			 "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72"
+			 "\x1c\x3c\x0c\x95\x95\x68\x09\x53"
+			 "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25"
+			 "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57"
+			 "\xba\x63\x7b\x39",
+		.ctext = "\x39\x80\xca\x0b\x3c\x00\xe8\x41"
+			 "\xeb\x06\xfa\xc4\x87\x2a\x27\x57"
+			 "\x85\x9e\x1c\xea\xa6\xef\xd9\x84"
+			 "\x62\x85\x93\xb4\x0c\xa1\xe1\x9c"
+			 "\x7d\x77\x3d\x00\xc1\x44\xc5\x25"
+			 "\xac\x61\x9d\x18\xc8\x4a\x3f\x47"
+			 "\x18\xe2\x44\x8b\x2f\xe3\x24\xd9"
+			 "\xcc\xda\x27\x10",
+		.data_len = 60,
+		.ad = "\xfe\xed\xfa\xce\xde\xad\xbe\xef"
+		      "\xfe\xed\xfa\xce\xde\xad\xbe\xef"
+		      "\xab\xad\xda\xd2",
+		.ad_len = 20,
+		.tag = "\x25\x19\x49\x8e\x80\xf1\x47\x8f"
+		       "\x37\xba\x55\xbd\x6d\x27\x61\x8c",
+		.tag_len = 16,
+	},
+
+	/* AES-256 Test Vectors */
+	{
+		.name = "McGrew & Viega Test Case 13",
+		.key = "\x00\x00\x00\x00\x00\x00\x00\x00"
+		       "\x00\x00\x00\x00\x00\x00\x00\x00"
+		       "\x00\x00\x00\x00\x00\x00\x00\x00"
+		       "\x00\x00\x00\x00\x00\x00\x00\x00",
+		.key_len = 32,
+		.nonce = "\x00\x00\x00\x00\x00\x00\x00\x00"
+			 "\x00\x00\x00\x00",
+		.nonce_len = 12,
+		.ptext = "",
+		.ctext = "",
+		.data_len = 0,
+		.ad = "",
+		.ad_len = 0,
+		.tag = "\x53\x0f\x8a\xfb\xc7\x45\x36\xb9"
+		       "\xa9\x63\xb4\xf1\xc4\xcb\x73\x8b",
+		.tag_len = 16,
+	},
+	{
+		.name = "McGrew & Viega Test Case 14",
+		.key = "\x00\x00\x00\x00\x00\x00\x00\x00"
+		       "\x00\x00\x00\x00\x00\x00\x00\x00"
+		       "\x00\x00\x00\x00\x00\x00\x00\x00"
+		       "\x00\x00\x00\x00\x00\x00\x00\x00",
+		.key_len = 32,
+		.nonce = "\x00\x00\x00\x00\x00\x00\x00\x00"
+			 "\x00\x00\x00\x00",
+		.nonce_len = 12,
+		.ptext = "\x00\x00\x00\x00\x00\x00\x00\x00"
+			 "\x00\x00\x00\x00\x00\x00\x00\x00",
+		.ctext = "\xce\xa7\x40\x3d\x4d\x60\x6b\x6e"
+			 "\x07\x4e\xc5\xd3\xba\xf3\x9d\x18",
+		.data_len = 16,
+		.ad = "",
+		.ad_len = 0,
+		.tag = "\xd0\xd1\xc8\xa7\x99\x99\x6b\xf0"
+		       "\x26\x5b\x98\xb5\xd4\x8a\xb9\x19",
+		.tag_len = 16,
+	},
+	{
+		.name = "McGrew & Viega Test Case 15",
+		.key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c"
+		       "\x6d\x6a\x8f\x94\x67\x30\x83\x08"
+		       "\xfe\xff\xe9\x92\x86\x65\x73\x1c"
+		       "\x6d\x6a\x8f\x94\x67\x30\x83\x08",
+		.key_len = 32,
+		.nonce = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad"
+			 "\xde\xca\xf8\x88",
+		.nonce_len = 12,
+		.ptext = "\xd9\x31\x32\x25\xf8\x84\x06\xe5"
+			 "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a"
+			 "\x86\xa7\xa9\x53\x15\x34\xf7\xda"
+			 "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72"
+			 "\x1c\x3c\x0c\x95\x95\x68\x09\x53"
+			 "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25"
+			 "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57"
+			 "\xba\x63\x7b\x39\x1a\xaf\xd2\x55",
+		.ctext = "\x52\x2d\xc1\xf0\x99\x56\x7d\x07"
+			 "\xf4\x7f\x37\xa3\x2a\x84\x42\x7d"
+			 "\x64\x3a\x8c\xdc\xbf\xe5\xc0\xc9"
+			 "\x75\x98\xa2\xbd\x25\x55\xd1\xaa"
+			 "\x8c\xb0\x8e\x48\x59\x0d\xbb\x3d"
+			 "\xa7\xb0\x8b\x10\x56\x82\x88\x38"
+			 "\xc5\xf6\x1e\x63\x93\xba\x7a\x0a"
+			 "\xbc\xc9\xf6\x62\x89\x80\x15\xad",
+		.data_len = 64,
+		.ad = "",
+		.ad_len = 0,
+		.tag = "\xb0\x94\xda\xc5\xd9\x34\x71\xbd"
+		       "\xec\x1a\x50\x22\x70\xe3\xcc\x6c",
+		.tag_len = 16,
+	},
+	{
+		.name = "McGrew & Viega Test Case 16",
+		.key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c"
+		       "\x6d\x6a\x8f\x94\x67\x30\x83\x08"
+		       "\xfe\xff\xe9\x92\x86\x65\x73\x1c"
+		       "\x6d\x6a\x8f\x94\x67\x30\x83\x08",
+		.key_len = 32,
+		.nonce = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad"
+			 "\xde\xca\xf8\x88",
+		.nonce_len = 12,
+		.ptext = "\xd9\x31\x32\x25\xf8\x84\x06\xe5"
+			 "\xa5\x59\x09\xc5\xaf\xf5\x26\x9a"
+			 "\x86\xa7\xa9\x53\x15\x34\xf7\xda"
+			 "\x2e\x4c\x30\x3d\x8a\x31\x8a\x72"
+			 "\x1c\x3c\x0c\x95\x95\x68\x09\x53"
+			 "\x2f\xcf\x0e\x24\x49\xa6\xb5\x25"
+			 "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57"
+			 "\xba\x63\x7b\x39",
+		.ctext = "\x52\x2d\xc1\xf0\x99\x56\x7d\x07"
+			 "\xf4\x7f\x37\xa3\x2a\x84\x42\x7d"
+			 "\x64\x3a\x8c\xdc\xbf\xe5\xc0\xc9"
+			 "\x75\x98\xa2\xbd\x25\x55\xd1\xaa"
+			 "\x8c\xb0\x8e\x48\x59\x0d\xbb\x3d"
+			 "\xa7\xb0\x8b\x10\x56\x82\x88\x38"
+			 "\xc5\xf6\x1e\x63\x93\xba\x7a\x0a"
+			 "\xbc\xc9\xf6\x62",
+		.data_len = 60,
+		.ad = "\xfe\xed\xfa\xce\xde\xad\xbe\xef"
+		      "\xfe\xed\xfa\xce\xde\xad\xbe\xef"
+		      "\xab\xad\xda\xd2",
+		.ad_len = 20,
+		.tag = "\x76\xfc\x6e\xce\x0f\x4e\x17\x68"
+		       "\xcd\xdf\x88\x53\xbb\x2d\x55\x1b",
+		.tag_len = 16,
+	},
+};
+
+static void test_aes_gcm_one_test_vector(struct kunit *test,
+					 const struct aes_gcm_testvec *tv)
+{
+	u8 *ctext = alloc_buf(test, tv->data_len);
+	u8 *decrypted = alloc_buf(test, tv->data_len);
+	u8 *tag = alloc_buf(test, tv->tag_len);
+	struct aes_gcm_key key;
+	int err;
+
+	KUNIT_ASSERT_EQ(test, AES_GCM_NONCE_LEN, tv->nonce_len);
+
+	err = aes_gcm_preparekey(&key, tv->key, tv->key_len, tv->tag_len);
+	KUNIT_ASSERT_EQ_MSG(test, 0, err, "Failed to prepare key for %s",
+			    tv->name);
+
+	aes_gcm_encrypt(ctext, tv->ptext, tv->data_len, tag, tv->ad, tv->ad_len,
+			tv->nonce, &key);
+	KUNIT_ASSERT_MEMEQ_MSG(test, tv->ctext, ctext, tv->data_len,
+			       "Wrong ciphertext for %s", tv->name);
+	KUNIT_ASSERT_MEMEQ_MSG(test, tag, tv->tag, tv->tag_len,
+			       "Wrong tag for %s", tv->name);
+
+	err = aes_gcm_decrypt(decrypted, ctext, tv->data_len, tag, tv->ad,
+			      tv->ad_len, tv->nonce, &key);
+	KUNIT_ASSERT_EQ_MSG(test, 0, err, "Decryption failed for %s", tv->name);
+	KUNIT_ASSERT_MEMEQ_MSG(test, tv->ptext, decrypted, tv->data_len,
+			       "Wrong plaintext for %s", tv->name);
+}
+
+static void test_aes_gcm_test_vectors(struct kunit *test)
+{
+	for (size_t i = 0; i < ARRAY_SIZE(aes_gcm_testvecs); i++)
+		test_aes_gcm_one_test_vector(test, &aes_gcm_testvecs[i]);
+}
+
+static int aes_gcm_init_test(struct aes_gcm_ctx *ctx, u64 data_len, u64 ad_len,
+			     const u8 *nonce, size_t nonce_len,
+			     const struct aes_gcm_key *key)
+{
+	if (nonce_len != AES_GCM_NONCE_LEN)
+		return -EINVAL;
+	/*
+	 * Ignore data_len and ad_len.  Incremental AES-GCM doesn't need them at
+	 * initialization time.
+	 */
+	aes_gcm_init(ctx, nonce, key);
+	return 0;
+}
+
+static int aes_gcm_encrypt_test(u8 *dst, const u8 *src, size_t data_len,
+				u8 *authtag, const u8 *ad, size_t ad_len,
+				const u8 *nonce, size_t nonce_len,
+				const struct aes_gcm_key *key)
+{
+	if (nonce_len != AES_GCM_NONCE_LEN)
+		return -EINVAL;
+	/* aes_gcm_encrypt() returns void. */
+	aes_gcm_encrypt(dst, src, data_len, authtag, ad, ad_len, nonce, key);
+	return 0;
+}
+
+static int aes_gcm_decrypt_test(u8 *dst, const u8 *src, size_t data_len,
+				const u8 *authtag, const u8 *ad, size_t ad_len,
+				const u8 *nonce, size_t nonce_len,
+				const struct aes_gcm_key *key)
+{
+	if (nonce_len != AES_GCM_NONCE_LEN)
+		return -EINVAL;
+	return aes_gcm_decrypt(dst, src, data_len, authtag, ad, ad_len, nonce,
+			       key);
+}
+
+static const size_t aes_gcm_valid_key_lens[] = { 16, 24, 32 };
+#define AEAD_MAX_KEY_LEN 32
+#define AEAD_VALID_KEY_LENS aes_gcm_valid_key_lens
+
+static const size_t aes_gcm_valid_nonce_lens[] = { AES_GCM_NONCE_LEN };
+#define AEAD_MAX_NONCE_LEN AES_GCM_NONCE_LEN
+#define AEAD_VALID_NONCE_LENS aes_gcm_valid_nonce_lens
+
+static const size_t aes_gcm_valid_tag_lens[] = { 4, 8, 12, 13, 14, 15, 16 };
+#define AEAD_MAX_TAG_LEN 16
+#define AEAD_VALID_TAG_LENS aes_gcm_valid_tag_lens
+
+#define AEAD_KEY aes_gcm_key
+#define AEAD_CTX aes_gcm_ctx
+#define AEAD_PREPAREKEY aes_gcm_preparekey
+#define AEAD_ENCRYPT aes_gcm_encrypt_test
+#define AEAD_DECRYPT aes_gcm_decrypt_test
+
+#define AEAD_INIT aes_gcm_init_test
+#define AEAD_AUTH_UPDATE aes_gcm_auth_update
+#define AEAD_ENCRYPT_UPDATE aes_gcm_encrypt_update
+#define AEAD_DECRYPT_UPDATE aes_gcm_decrypt_update
+#define AEAD_ENCRYPT_FINAL aes_gcm_encrypt_final
+#define AEAD_DECRYPT_FINAL aes_gcm_decrypt_final
+
+/* This value was generated by gen-aead-testvecs.py. */
+static const u8 aes_gcm_monte_carlo_checksum[BLAKE2S_HASH_SIZE] = {
+	0x6d, 0xd0, 0x6e, 0x6b, 0xde, 0x3e, 0x92, 0x9f, 0xae, 0x1f, 0xf1,
+	0x84, 0x99, 0x5a, 0x9e, 0x7b, 0xfe, 0x20, 0x9e, 0x22, 0x7c, 0x5f,
+	0x15, 0xb3, 0x59, 0x89, 0xd0, 0xb7, 0x74, 0x5d, 0xd2, 0xa5,
+};
+#define AEAD_MONTE_CARLO_CHECKSUM aes_gcm_monte_carlo_checksum
+
+#include "aead-test-template.h"
+
+static struct kunit_case aes_gcm_test_cases[] = {
+	KUNIT_CASE(test_aes_gcm_test_vectors),
+	AEAD_KUNIT_CASES,
+	{},
+};
+
+static struct kunit_suite aes_gcm_test_suite = {
+	.name = "aes_gcm",
+	.test_cases = aes_gcm_test_cases,
+};
+kunit_test_suite(aes_gcm_test_suite);
+
+MODULE_DESCRIPTION("KUnit tests and benchmark for AES-GCM");
+MODULE_LICENSE("GPL");
diff --git a/lib/crypto/tests/blake2b_kunit.c b/lib/crypto/tests/blake2b_kunit.c
index bc0be7d..e2ce6cf 100644
--- a/lib/crypto/tests/blake2b_kunit.c
+++ b/lib/crypto/tests/blake2b_kunit.c
@@ -41,9 +41,9 @@ static void blake2b_init_default(struct blake2b_ctx *ctx)
 static void test_blake2b_all_key_and_hash_lens(struct kunit *test)
 {
 	const size_t data_len = 100;
-	u8 *data = &test_buf[0];
-	u8 *key = data + data_len;
-	u8 *hash = key + BLAKE2B_KEY_SIZE;
+	u8 *data = alloc_buf(test, data_len);
+	u8 *key = alloc_buf(test, BLAKE2B_KEY_SIZE);
+	u8 *hash = alloc_buf(test, BLAKE2B_HASH_SIZE);
 	struct blake2b_ctx main_ctx;
 	u8 main_hash[BLAKE2B_HASH_SIZE];
 
@@ -68,11 +68,13 @@ static void test_blake2b_all_key_and_hash_lens(struct kunit *test)
 static void test_blake2b_with_guarded_key_buf(struct kunit *test)
 {
 	const size_t data_len = 100;
+	u8 *data = alloc_buf(test, data_len);
+	u8 *guarded_key_buf = alloc_guarded_buf(test, BLAKE2B_KEY_SIZE);
 
-	rand_bytes(test_buf, data_len);
+	rand_bytes(data, data_len);
 	for (int key_len = 0; key_len <= BLAKE2B_KEY_SIZE; key_len++) {
 		u8 key[BLAKE2B_KEY_SIZE];
-		u8 *guarded_key = &test_buf[TEST_BUF_LEN - key_len];
+		u8 *guarded_key = &guarded_key_buf[BLAKE2B_KEY_SIZE - key_len];
 		u8 hash1[BLAKE2B_HASH_SIZE];
 		u8 hash2[BLAKE2B_HASH_SIZE];
 		struct blake2b_ctx ctx;
@@ -80,14 +82,13 @@ static void test_blake2b_with_guarded_key_buf(struct kunit *test)
 		rand_bytes(key, key_len);
 		memcpy(guarded_key, key, key_len);
 
-		blake2b(key, key_len, test_buf, data_len,
-			hash1, BLAKE2B_HASH_SIZE);
-		blake2b(guarded_key, key_len, test_buf, data_len,
-			hash2, BLAKE2B_HASH_SIZE);
+		blake2b(key, key_len, data, data_len, hash1, BLAKE2B_HASH_SIZE);
+		blake2b(guarded_key, key_len, data, data_len, hash2,
+			BLAKE2B_HASH_SIZE);
 		KUNIT_ASSERT_MEMEQ(test, hash1, hash2, BLAKE2B_HASH_SIZE);
 
 		blake2b_init_key(&ctx, BLAKE2B_HASH_SIZE, guarded_key, key_len);
-		blake2b_update(&ctx, test_buf, data_len);
+		blake2b_update(&ctx, data, data_len);
 		blake2b_final(&ctx, hash2);
 		KUNIT_ASSERT_MEMEQ(test, hash1, hash2, BLAKE2B_HASH_SIZE);
 	}
@@ -100,14 +101,16 @@ static void test_blake2b_with_guarded_key_buf(struct kunit *test)
 static void test_blake2b_with_guarded_out_buf(struct kunit *test)
 {
 	const size_t data_len = 100;
+	u8 *data = alloc_buf(test, data_len);
+	u8 *out_buf = alloc_guarded_buf(test, BLAKE2B_HASH_SIZE);
 
-	rand_bytes(test_buf, data_len);
+	rand_bytes(data, data_len);
 	for (int out_len = 1; out_len <= BLAKE2B_HASH_SIZE; out_len++) {
 		u8 hash[BLAKE2B_HASH_SIZE];
-		u8 *guarded_hash = &test_buf[TEST_BUF_LEN - out_len];
+		u8 *guarded_hash = &out_buf[BLAKE2B_HASH_SIZE - out_len];
 
-		blake2b(NULL, 0, test_buf, data_len, hash, out_len);
-		blake2b(NULL, 0, test_buf, data_len, guarded_hash, out_len);
+		blake2b(NULL, 0, data, data_len, hash, out_len);
+		blake2b(NULL, 0, data, data_len, guarded_hash, out_len);
 		KUNIT_ASSERT_MEMEQ(test, hash, guarded_hash, out_len);
 	}
 }
@@ -124,8 +127,6 @@ static struct kunit_case blake2b_test_cases[] = {
 static struct kunit_suite blake2b_test_suite = {
 	.name = "blake2b",
 	.test_cases = blake2b_test_cases,
-	.suite_init = hash_suite_init,
-	.suite_exit = hash_suite_exit,
 };
 kunit_test_suite(blake2b_test_suite);
 
diff --git a/lib/crypto/tests/blake2s_kunit.c b/lib/crypto/tests/blake2s_kunit.c
index 6832d9a..682f695 100644
--- a/lib/crypto/tests/blake2s_kunit.c
+++ b/lib/crypto/tests/blake2s_kunit.c
@@ -41,9 +41,9 @@ static void blake2s_init_default(struct blake2s_ctx *ctx)
 static void test_blake2s_all_key_and_hash_lens(struct kunit *test)
 {
 	const size_t data_len = 100;
-	u8 *data = &test_buf[0];
-	u8 *key = data + data_len;
-	u8 *hash = key + BLAKE2S_KEY_SIZE;
+	u8 *data = alloc_buf(test, data_len);
+	u8 *key = alloc_buf(test, BLAKE2S_KEY_SIZE);
+	u8 *hash = alloc_buf(test, BLAKE2S_HASH_SIZE);
 	struct blake2s_ctx main_ctx;
 	u8 main_hash[BLAKE2S_HASH_SIZE];
 
@@ -68,11 +68,13 @@ static void test_blake2s_all_key_and_hash_lens(struct kunit *test)
 static void test_blake2s_with_guarded_key_buf(struct kunit *test)
 {
 	const size_t data_len = 100;
+	u8 *data = alloc_buf(test, data_len);
+	u8 *guarded_key_buf = alloc_guarded_buf(test, BLAKE2S_KEY_SIZE);
 
-	rand_bytes(test_buf, data_len);
+	rand_bytes(data, data_len);
 	for (int key_len = 0; key_len <= BLAKE2S_KEY_SIZE; key_len++) {
 		u8 key[BLAKE2S_KEY_SIZE];
-		u8 *guarded_key = &test_buf[TEST_BUF_LEN - key_len];
+		u8 *guarded_key = &guarded_key_buf[BLAKE2S_KEY_SIZE - key_len];
 		u8 hash1[BLAKE2S_HASH_SIZE];
 		u8 hash2[BLAKE2S_HASH_SIZE];
 		struct blake2s_ctx ctx;
@@ -80,14 +82,13 @@ static void test_blake2s_with_guarded_key_buf(struct kunit *test)
 		rand_bytes(key, key_len);
 		memcpy(guarded_key, key, key_len);
 
-		blake2s(key, key_len, test_buf, data_len,
-			hash1, BLAKE2S_HASH_SIZE);
-		blake2s(guarded_key, key_len, test_buf, data_len,
-			hash2, BLAKE2S_HASH_SIZE);
+		blake2s(key, key_len, data, data_len, hash1, BLAKE2S_HASH_SIZE);
+		blake2s(guarded_key, key_len, data, data_len, hash2,
+			BLAKE2S_HASH_SIZE);
 		KUNIT_ASSERT_MEMEQ(test, hash1, hash2, BLAKE2S_HASH_SIZE);
 
 		blake2s_init_key(&ctx, BLAKE2S_HASH_SIZE, guarded_key, key_len);
-		blake2s_update(&ctx, test_buf, data_len);
+		blake2s_update(&ctx, data, data_len);
 		blake2s_final(&ctx, hash2);
 		KUNIT_ASSERT_MEMEQ(test, hash1, hash2, BLAKE2S_HASH_SIZE);
 	}
@@ -100,14 +101,16 @@ static void test_blake2s_with_guarded_key_buf(struct kunit *test)
 static void test_blake2s_with_guarded_out_buf(struct kunit *test)
 {
 	const size_t data_len = 100;
+	u8 *data = alloc_buf(test, data_len);
+	u8 *out_buf = alloc_guarded_buf(test, BLAKE2S_HASH_SIZE);
 
-	rand_bytes(test_buf, data_len);
+	rand_bytes(data, data_len);
 	for (int out_len = 1; out_len <= BLAKE2S_HASH_SIZE; out_len++) {
 		u8 hash[BLAKE2S_HASH_SIZE];
-		u8 *guarded_hash = &test_buf[TEST_BUF_LEN - out_len];
+		u8 *guarded_hash = &out_buf[BLAKE2S_HASH_SIZE - out_len];
 
-		blake2s(NULL, 0, test_buf, data_len, hash, out_len);
-		blake2s(NULL, 0, test_buf, data_len, guarded_hash, out_len);
+		blake2s(NULL, 0, data, data_len, hash, out_len);
+		blake2s(NULL, 0, data, data_len, guarded_hash, out_len);
 		KUNIT_ASSERT_MEMEQ(test, hash, guarded_hash, out_len);
 	}
 }
@@ -124,8 +127,6 @@ static struct kunit_case blake2s_test_cases[] = {
 static struct kunit_suite blake2s_test_suite = {
 	.name = "blake2s",
 	.test_cases = blake2s_test_cases,
-	.suite_init = hash_suite_init,
-	.suite_exit = hash_suite_exit,
 };
 kunit_test_suite(blake2s_test_suite);
 
diff --git a/lib/crypto/tests/chacha20poly1305_kunit.c b/lib/crypto/tests/chacha20poly1305_kunit.c
index 97a68fa..d5504d0 100644
--- a/lib/crypto/tests/chacha20poly1305_kunit.c
+++ b/lib/crypto/tests/chacha20poly1305_kunit.c
@@ -11,7 +11,7 @@
 #include <linux/init.h>
 #include <linux/mm.h>
 #include <linux/kernel.h>
-#include <linux/slab.h>
+#include "test-utils.h"
 
 struct chacha20poly1305_testvec {
 	const u8 *input, *output, *assoc, *nonce, *key;
@@ -8890,11 +8890,8 @@ static void test_chacha20poly1305(struct kunit *test)
 	bool ret;
 	struct scatterlist sg_src[3];
 
-	computed_output = kunit_kmalloc(test, MAXIMUM_TEST_BUFFER_LEN,
-					GFP_KERNEL);
-	input = kunit_kmalloc(test, MAXIMUM_TEST_BUFFER_LEN, GFP_KERNEL);
-	KUNIT_ASSERT_NOT_NULL(test, computed_output);
-	KUNIT_ASSERT_NOT_NULL(test, input);
+	computed_output = alloc_buf(test, MAXIMUM_TEST_BUFFER_LEN);
+	input = alloc_buf(test, MAXIMUM_TEST_BUFFER_LEN);
 
 	for (i = 0; i < ARRAY_SIZE(chacha20poly1305_enc_vectors); ++i) {
 		memset(computed_output, 0, MAXIMUM_TEST_BUFFER_LEN);
diff --git a/lib/crypto/tests/ghash_kunit.c b/lib/crypto/tests/ghash_kunit.c
index 68b3837..4d7aa79 100644
--- a/lib/crypto/tests/ghash_kunit.c
+++ b/lib/crypto/tests/ghash_kunit.c
@@ -39,17 +39,18 @@ static void ghash_withtestkey(const u8 *data, size_t len,
  */
 static void test_ghash_allones_key_and_message(struct kunit *test)
 {
+	const size_t max_len = 4096;
+	u8 *data = alloc_buf(test, max_len);
 	struct ghash_key key;
 	struct ghash_ctx hashofhashes_ctx;
 	u8 hash[GHASH_BLOCK_SIZE];
 
-	static_assert(TEST_BUF_LEN >= 4096);
-	memset(test_buf, 0xff, 4096);
+	memset(data, 0xff, max_len);
 
-	ghash_preparekey(&key, test_buf);
+	ghash_preparekey(&key, data);
 	ghash_init(&hashofhashes_ctx, &key);
-	for (size_t len = 0; len <= 4096; len += 16) {
-		ghash(&key, test_buf, len, hash);
+	for (size_t len = 0; len <= max_len; len += 16) {
+		ghash(&key, data, len, hash);
 		ghash_update(&hashofhashes_ctx, hash, sizeof(hash));
 	}
 	ghash_final(&hashofhashes_ctx, hash);
@@ -68,7 +69,7 @@ static void check_key_consistency(struct kunit *test,
 				  const struct ghash_key *key1,
 				  const struct ghash_key *key2)
 {
-	u8 *data = test_buf;
+	u8 *data = alloc_buf(test, MAX_LEN_FOR_KEY_CHECK);
 	u8 hash1[GHASH_BLOCK_SIZE];
 	u8 hash2[GHASH_BLOCK_SIZE];
 
@@ -88,10 +89,10 @@ static void check_key_consistency(struct kunit *test,
 static void test_ghash_with_guarded_key(struct kunit *test)
 {
 	u8 raw_key[GHASH_BLOCK_SIZE];
-	u8 *guarded_raw_key = &test_buf[TEST_BUF_LEN - sizeof(raw_key)];
+	u8 *guarded_raw_key = alloc_guarded_buf(test, sizeof(raw_key));
 	struct ghash_key key1, key2;
 	struct ghash_key *guarded_key =
-		(struct ghash_key *)&test_buf[TEST_BUF_LEN - sizeof(key1)];
+		alloc_guarded_buf(test, sizeof(*guarded_key));
 
 	/* Prepare with regular buffers. */
 	rand_bytes(raw_key, sizeof(raw_key));
@@ -116,14 +117,14 @@ static void test_ghash_with_minimally_aligned_key(struct kunit *test)
 {
 	u8 raw_key[GHASH_BLOCK_SIZE];
 	struct ghash_key key;
+	const size_t align = __alignof__(struct ghash_key);
+	u8 *key_buf = alloc_buf(test, sizeof(struct ghash_key) + 3 * align);
 	struct ghash_key *minaligned_key =
-		(struct ghash_key *)&test_buf[MAX_LEN_FOR_KEY_CHECK +
-					      __alignof__(struct ghash_key)];
+		(struct ghash_key *)(PTR_ALIGN(key_buf, 2 * align) + align);
 
-	KUNIT_ASSERT_TRUE(test, IS_ALIGNED((uintptr_t)minaligned_key,
-					   __alignof__(struct ghash_key)));
-	KUNIT_ASSERT_TRUE(test, !IS_ALIGNED((uintptr_t)minaligned_key,
-					    2 * __alignof__(struct ghash_key)));
+	KUNIT_ASSERT_TRUE(test, IS_ALIGNED((uintptr_t)minaligned_key, align));
+	KUNIT_ASSERT_TRUE(test,
+			  !IS_ALIGNED((uintptr_t)minaligned_key, 2 * align));
 
 	rand_bytes(raw_key, sizeof(raw_key));
 	ghash_preparekey(&key, raw_key);
@@ -164,12 +165,7 @@ static int ghash_suite_init(struct kunit_suite *suite)
 
 	rand_bytes_seeded_from_len(raw_key, sizeof(raw_key));
 	ghash_preparekey(&test_key, raw_key);
-	return hash_suite_init(suite);
-}
-
-static void ghash_suite_exit(struct kunit_suite *suite)
-{
-	hash_suite_exit(suite);
+	return 0;
 }
 
 static struct kunit_case ghash_test_cases[] = {
@@ -186,7 +182,6 @@ static struct kunit_suite ghash_test_suite = {
 	.name = "ghash",
 	.test_cases = ghash_test_cases,
 	.suite_init = ghash_suite_init,
-	.suite_exit = ghash_suite_exit,
 };
 kunit_test_suite(ghash_test_suite);
 
diff --git a/lib/crypto/tests/hash-test-template.h b/lib/crypto/tests/hash-test-template.h
index 61b43e6..bb6eaa5 100644
--- a/lib/crypto/tests/hash-test-template.h
+++ b/lib/crypto/tests/hash-test-template.h
@@ -7,93 +7,7 @@
  */
 #include <kunit/run-in-irq-context.h>
 #include <kunit/test.h>
-#include <linux/vmalloc.h>
-
-/* test_buf is a guarded buffer, i.e. &test_buf[TEST_BUF_LEN] is not mapped. */
-#define TEST_BUF_LEN 16384
-static u8 *test_buf;
-
-static u8 *orig_test_buf;
-
-static u64 random_seed;
-
-/*
- * This is a simple linear congruential generator.  It is used only for testing,
- * which does not require cryptographically secure random numbers.  A hard-coded
- * algorithm is used instead of <linux/prandom.h> so that it matches the
- * algorithm used by the test vector generation script.  This allows the input
- * data in random test vectors to be concisely stored as just the seed.
- */
-static u32 rand32(void)
-{
-	random_seed = (random_seed * 25214903917 + 11) & ((1ULL << 48) - 1);
-	return random_seed >> 16;
-}
-
-static void rand_bytes(u8 *out, size_t len)
-{
-	for (size_t i = 0; i < len; i++)
-		out[i] = rand32();
-}
-
-static void rand_bytes_seeded_from_len(u8 *out, size_t len)
-{
-	random_seed = len;
-	rand_bytes(out, len);
-}
-
-static bool rand_bool(void)
-{
-	return rand32() % 2;
-}
-
-/* Generate a random length, preferring small lengths. */
-static size_t rand_length(size_t max_len)
-{
-	size_t len;
-
-	switch (rand32() % 3) {
-	case 0:
-		len = rand32() % 128;
-		break;
-	case 1:
-		len = rand32() % 3072;
-		break;
-	default:
-		len = rand32();
-		break;
-	}
-	return len % (max_len + 1);
-}
-
-static size_t rand_offset(size_t max_offset)
-{
-	return min(rand32() % 128, max_offset);
-}
-
-static int hash_suite_init(struct kunit_suite *suite)
-{
-	/*
-	 * Allocate the test buffer using vmalloc() with a page-aligned length
-	 * so that it is immediately followed by a guard page.  This allows
-	 * buffer overreads to be detected, even in assembly code.
-	 */
-	size_t alloc_len = round_up(TEST_BUF_LEN, PAGE_SIZE);
-
-	orig_test_buf = vmalloc(alloc_len);
-	if (!orig_test_buf)
-		return -ENOMEM;
-
-	test_buf = orig_test_buf + alloc_len - TEST_BUF_LEN;
-	return 0;
-}
-
-static void hash_suite_exit(struct kunit_suite *suite)
-{
-	vfree(orig_test_buf);
-	orig_test_buf = NULL;
-	test_buf = NULL;
-}
+#include "test-utils.h"
 
 /*
  * Test the hash function against a list of test vectors.
@@ -104,14 +18,16 @@ static void hash_suite_exit(struct kunit_suite *suite)
  */
 static void test_hash_test_vectors(struct kunit *test)
 {
+	const size_t max_len = 16384;
+	u8 *data = alloc_buf(test, max_len);
+
 	for (size_t i = 0; i < ARRAY_SIZE(hash_testvecs); i++) {
 		size_t data_len = hash_testvecs[i].data_len;
 		u8 actual_hash[HASH_SIZE];
 
-		KUNIT_ASSERT_LE(test, data_len, TEST_BUF_LEN);
-		rand_bytes_seeded_from_len(test_buf, data_len);
-
-		HASH(test_buf, data_len, actual_hash);
+		KUNIT_ASSERT_LE(test, data_len, max_len);
+		rand_bytes_seeded_from_len(data, data_len);
+		HASH(data, data_len, actual_hash);
 		KUNIT_ASSERT_MEMEQ_MSG(
 			test, actual_hash, hash_testvecs[i].digest, HASH_SIZE,
 			"Wrong result with test vector %zu; data_len=%zu", i,
@@ -127,14 +43,15 @@ static void test_hash_test_vectors(struct kunit *test)
  */
 static void test_hash_all_lens_up_to_4096(struct kunit *test)
 {
+	const size_t max_len = 4096;
+	u8 *data = alloc_buf(test, max_len);
 	struct HASH_CTX ctx;
 	u8 hash[HASH_SIZE];
 
-	static_assert(TEST_BUF_LEN >= 4096);
-	rand_bytes_seeded_from_len(test_buf, 4096);
+	rand_bytes_seeded_from_len(data, max_len);
 	HASH_INIT(&ctx);
-	for (size_t len = 0; len <= 4096; len++) {
-		HASH(test_buf, len, hash);
+	for (size_t len = 0; len <= max_len; len++) {
+		HASH(data, len, hash);
 		HASH_UPDATE(&ctx, hash, HASH_SIZE);
 	}
 	HASH_FINAL(&ctx, hash);
@@ -147,6 +64,9 @@ static void test_hash_all_lens_up_to_4096(struct kunit *test)
  */
 static void test_hash_incremental_updates(struct kunit *test)
 {
+	const size_t max_len = 16384;
+	u8 *data = alloc_guarded_buf(test, max_len);
+
 	for (int i = 0; i < 1000; i++) {
 		size_t total_len, offset;
 		struct HASH_CTX ctx;
@@ -155,12 +75,12 @@ static void test_hash_incremental_updates(struct kunit *test)
 		size_t num_parts = 0;
 		size_t remaining_len, cur_offset;
 
-		total_len = rand_length(TEST_BUF_LEN);
-		offset = rand_offset(TEST_BUF_LEN - total_len);
-		rand_bytes(&test_buf[offset], total_len);
+		total_len = rand_length(max_len);
+		offset = rand_offset(max_len - total_len);
+		rand_bytes(&data[offset], total_len);
 
 		/* Compute the hash value in one shot. */
-		HASH(&test_buf[offset], total_len, hash1);
+		HASH(&data[offset], total_len, hash1);
 
 		/*
 		 * Compute the hash value incrementally, using a randomly
@@ -172,13 +92,13 @@ static void test_hash_incremental_updates(struct kunit *test)
 		while (rand_bool()) {
 			size_t part_len = rand_length(remaining_len);
 
-			HASH_UPDATE(&ctx, &test_buf[cur_offset], part_len);
+			HASH_UPDATE(&ctx, &data[cur_offset], part_len);
 			num_parts++;
 			cur_offset += part_len;
 			remaining_len -= part_len;
 		}
 		if (remaining_len != 0 || rand_bool()) {
-			HASH_UPDATE(&ctx, &test_buf[cur_offset], remaining_len);
+			HASH_UPDATE(&ctx, &data[cur_offset], remaining_len);
 			num_parts++;
 		}
 		HASH_FINAL(&ctx, hash2);
@@ -197,11 +117,13 @@ static void test_hash_incremental_updates(struct kunit *test)
  */
 static void test_hash_buffer_overruns(struct kunit *test)
 {
-	const size_t max_tested_len = TEST_BUF_LEN - sizeof(struct HASH_CTX);
-	void *const buf_end = &test_buf[TEST_BUF_LEN];
+	const size_t buf_len = 16384;
+	u8 *buf = alloc_guarded_buf(test, buf_len);
+	void *const buf_end = &buf[buf_len];
+	const size_t max_tested_len = buf_len - sizeof(struct HASH_CTX);
 	struct HASH_CTX *guarded_ctx = buf_end - sizeof(*guarded_ctx);
 
-	rand_bytes(test_buf, TEST_BUF_LEN);
+	rand_bytes(buf, buf_len);
 
 	for (int i = 0; i < 100; i++) {
 		size_t len = rand_length(max_tested_len);
@@ -215,14 +137,14 @@ static void test_hash_buffer_overruns(struct kunit *test)
 		HASH_FINAL(&ctx, hash);
 
 		/* Check for overruns of the hash value buffer. */
-		HASH(test_buf, len, buf_end - HASH_SIZE);
+		HASH(buf, len, buf_end - HASH_SIZE);
 		HASH_INIT(&ctx);
-		HASH_UPDATE(&ctx, test_buf, len);
+		HASH_UPDATE(&ctx, buf, len);
 		HASH_FINAL(&ctx, buf_end - HASH_SIZE);
 
-		/* Check for overuns of the hash context. */
+		/* Check for overruns of the hash context. */
 		HASH_INIT(guarded_ctx);
-		HASH_UPDATE(guarded_ctx, test_buf, len);
+		HASH_UPDATE(guarded_ctx, buf, len);
 		HASH_FINAL(guarded_ctx, hash);
 	}
 }
@@ -233,30 +155,32 @@ static void test_hash_buffer_overruns(struct kunit *test)
  */
 static void test_hash_overlaps(struct kunit *test)
 {
-	const size_t max_tested_len = TEST_BUF_LEN - HASH_SIZE;
+	const size_t buf_len = 16384;
+	u8 *buf = alloc_guarded_buf(test, buf_len);
+	const size_t max_tested_len = buf_len - HASH_SIZE;
 	struct HASH_CTX ctx;
 	u8 hash[HASH_SIZE];
 
-	rand_bytes(test_buf, TEST_BUF_LEN);
+	rand_bytes(buf, buf_len);
 
 	for (int i = 0; i < 100; i++) {
 		size_t len = rand_length(max_tested_len);
 		size_t offset = HASH_SIZE + rand_offset(max_tested_len - len);
 		bool left_end = rand_bool();
-		u8 *ovl_hash = left_end ? &test_buf[offset] :
-					  &test_buf[offset + len - HASH_SIZE];
+		u8 *ovl_hash = left_end ? &buf[offset] :
+					  &buf[offset + len - HASH_SIZE];
 
-		HASH(&test_buf[offset], len, hash);
-		HASH(&test_buf[offset], len, ovl_hash);
+		HASH(&buf[offset], len, hash);
+		HASH(&buf[offset], len, ovl_hash);
 		KUNIT_ASSERT_MEMEQ_MSG(
 			test, hash, ovl_hash, HASH_SIZE,
 			"Overlap test 1 failed with len=%zu offset=%zu left_end=%d",
 			len, offset, left_end);
 
 		/* Repeat the above test, but this time use init+update+final */
-		HASH(&test_buf[offset], len, hash);
+		HASH(&buf[offset], len, hash);
 		HASH_INIT(&ctx);
-		HASH_UPDATE(&ctx, &test_buf[offset], len);
+		HASH_UPDATE(&ctx, &buf[offset], len);
 		HASH_FINAL(&ctx, ovl_hash);
 		KUNIT_ASSERT_MEMEQ_MSG(
 			test, hash, ovl_hash, HASH_SIZE,
@@ -264,10 +188,10 @@ static void test_hash_overlaps(struct kunit *test)
 			len, offset, left_end);
 
 		/* Test modifying the source data after it was used. */
-		HASH(&test_buf[offset], len, hash);
+		HASH(&buf[offset], len, hash);
 		HASH_INIT(&ctx);
-		HASH_UPDATE(&ctx, &test_buf[offset], len);
-		rand_bytes(&test_buf[offset], len);
+		HASH_UPDATE(&ctx, &buf[offset], len);
+		rand_bytes(&buf[offset], len);
 		HASH_FINAL(&ctx, ovl_hash);
 		KUNIT_ASSERT_MEMEQ_MSG(
 			test, hash, ovl_hash, HASH_SIZE,
@@ -282,20 +206,22 @@ static void test_hash_overlaps(struct kunit *test)
  */
 static void test_hash_alignment_consistency(struct kunit *test)
 {
+	const size_t max_len = 16384;
+	u8 *data = alloc_guarded_buf(test, max_len);
 	u8 hash1[128 + HASH_SIZE];
 	u8 hash2[128 + HASH_SIZE];
 
 	for (int i = 0; i < 100; i++) {
-		size_t len = rand_length(TEST_BUF_LEN);
-		size_t data_offs1 = rand_offset(TEST_BUF_LEN - len);
-		size_t data_offs2 = rand_offset(TEST_BUF_LEN - len);
+		size_t len = rand_length(max_len);
+		size_t data_offs1 = rand_offset(max_len - len);
+		size_t data_offs2 = rand_offset(max_len - len);
 		size_t hash_offs1 = rand_offset(128);
 		size_t hash_offs2 = rand_offset(128);
 
-		rand_bytes(&test_buf[data_offs1], len);
-		HASH(&test_buf[data_offs1], len, &hash1[hash_offs1]);
-		memmove(&test_buf[data_offs2], &test_buf[data_offs1], len);
-		HASH(&test_buf[data_offs2], len, &hash2[hash_offs2]);
+		rand_bytes(&data[data_offs1], len);
+		HASH(&data[data_offs1], len, &hash1[hash_offs1]);
+		memmove(&data[data_offs2], &data[data_offs1], len);
+		HASH(&data[data_offs2], len, &hash2[hash_offs2]);
 		KUNIT_ASSERT_MEMEQ_MSG(
 			test, &hash1[hash_offs1], &hash2[hash_offs2], HASH_SIZE,
 			"Alignment consistency test failed with len=%zu data_offs=(%zu,%zu) hash_offs=(%zu,%zu)",
@@ -308,11 +234,14 @@ static void test_hash_ctx_zeroization(struct kunit *test)
 {
 	static const u8 zeroes[sizeof(struct HASH_CTX)];
 	struct HASH_CTX ctx;
+	const size_t data_len = 128;
+	u8 *data = alloc_buf(test, data_len);
+	u8 hash[HASH_SIZE];
 
-	rand_bytes(test_buf, 128);
+	rand_bytes(data, data_len);
 	HASH_INIT(&ctx);
-	HASH_UPDATE(&ctx, test_buf, 128);
-	HASH_FINAL(&ctx, test_buf);
+	HASH_UPDATE(&ctx, data, data_len);
+	HASH_FINAL(&ctx, hash);
 	KUNIT_ASSERT_MEMEQ_MSG(test, &ctx, zeroes, sizeof(ctx),
 			       "Hash context was not zeroized by finalization");
 }
@@ -321,6 +250,7 @@ static void test_hash_ctx_zeroization(struct kunit *test)
 #define IRQ_TEST_NUM_BUFFERS 3 /* matches max concurrency level */
 
 struct hash_irq_test1_state {
+	u8 *data;
 	u8 expected_hashes[IRQ_TEST_NUM_BUFFERS][HASH_SIZE];
 	atomic_t seqno;
 };
@@ -336,7 +266,8 @@ static bool hash_irq_test1_func(void *state_)
 	u32 i = (u32)atomic_inc_return(&state->seqno) % IRQ_TEST_NUM_BUFFERS;
 	u8 actual_hash[HASH_SIZE];
 
-	HASH(&test_buf[i * IRQ_TEST_DATA_LEN], IRQ_TEST_DATA_LEN, actual_hash);
+	HASH(&state->data[i * IRQ_TEST_DATA_LEN], IRQ_TEST_DATA_LEN,
+	     actual_hash);
 	return memcmp(actual_hash, state->expected_hashes[i], HASH_SIZE) == 0;
 }
 
@@ -346,12 +277,14 @@ static bool hash_irq_test1_func(void *state_)
  */
 static void test_hash_interrupt_context_1(struct kunit *test)
 {
+	const size_t total_data_len = IRQ_TEST_NUM_BUFFERS * IRQ_TEST_DATA_LEN;
 	struct hash_irq_test1_state state = {};
 
 	/* Prepare some test messages and compute the expected hash of each. */
-	rand_bytes(test_buf, IRQ_TEST_NUM_BUFFERS * IRQ_TEST_DATA_LEN);
+	state.data = alloc_buf(test, total_data_len);
+	rand_bytes(state.data, total_data_len);
 	for (int i = 0; i < IRQ_TEST_NUM_BUFFERS; i++)
-		HASH(&test_buf[i * IRQ_TEST_DATA_LEN], IRQ_TEST_DATA_LEN,
+		HASH(&state.data[i * IRQ_TEST_DATA_LEN], IRQ_TEST_DATA_LEN,
 		     state.expected_hashes[i]);
 
 	kunit_run_irq_test(test, hash_irq_test1_func, 100000, &state);
@@ -365,6 +298,8 @@ struct hash_irq_test2_hash_ctx {
 };
 
 struct hash_irq_test2_state {
+	u8 *data;
+	size_t data_len;
 	struct hash_irq_test2_hash_ctx ctxs[IRQ_TEST_NUM_BUFFERS];
 	u8 expected_hash[HASH_SIZE];
 	u16 update_lens[32];
@@ -397,7 +332,7 @@ static bool hash_irq_test2_func(void *state_)
 		ctx->step++;
 	} else if (ctx->step < state->num_steps - 1) {
 		/* Update step */
-		HASH_UPDATE(&ctx->hash_ctx, &test_buf[ctx->offset],
+		HASH_UPDATE(&ctx->hash_ctx, &state->data[ctx->offset],
 			    state->update_lens[ctx->step - 1]);
 		ctx->offset += state->update_lens[ctx->step - 1];
 		ctx->step++;
@@ -405,7 +340,7 @@ static bool hash_irq_test2_func(void *state_)
 		/* Final step */
 		u8 actual_hash[HASH_SIZE];
 
-		if (WARN_ON_ONCE(ctx->offset != TEST_BUF_LEN))
+		if (WARN_ON_ONCE(ctx->offset != state->data_len))
 			ret = false;
 		HASH_FINAL(&ctx->hash_ctx, actual_hash);
 		if (memcmp(actual_hash, state->expected_hash, HASH_SIZE) != 0)
@@ -426,20 +361,23 @@ static bool hash_irq_test2_func(void *state_)
  */
 static void test_hash_interrupt_context_2(struct kunit *test)
 {
+	const size_t data_len = 16384;
 	struct hash_irq_test2_state *state;
-	int remaining = TEST_BUF_LEN;
+	size_t remaining = data_len;
 
 	state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
 	KUNIT_ASSERT_NOT_NULL(test, state);
+	state->data_len = data_len;
+	state->data = alloc_buf(test, data_len);
 
-	rand_bytes(test_buf, TEST_BUF_LEN);
-	HASH(test_buf, TEST_BUF_LEN, state->expected_hash);
+	rand_bytes(state->data, data_len);
+	HASH(state->data, data_len, state->expected_hash);
 
 	/*
 	 * Generate a list of update lengths to use.  Ensure that it contains
 	 * multiple entries but is limited to a maximum length.
 	 */
-	static_assert(TEST_BUF_LEN / 4096 > 1);
+	KUNIT_ASSERT_GT(test, data_len / 4096, 1);
 	for (state->num_steps = 0;
 	     state->num_steps < ARRAY_SIZE(state->update_lens) - 1 && remaining;
 	     state->num_steps++) {
@@ -485,21 +423,23 @@ static void test_hash_interrupt_context_2(struct kunit *test)
  */
 static void test_hmac(struct kunit *test)
 {
+	const size_t max_data_len = 4096;
+	const size_t max_key_len = 293;
+	const size_t outer_key_len = 32;
+	u8 *data = alloc_guarded_buf(test, max_data_len);
+	u8 *raw_key = alloc_guarded_buf(test, max_key_len);
 	static const u8 zeroes[sizeof(struct HMAC_CTX)];
-	u8 *raw_key;
 	struct HMAC_KEY key;
 	struct HMAC_CTX ctx;
 	u8 mac[HASH_SIZE];
 	u8 mac2[HASH_SIZE];
 
-	static_assert(TEST_BUF_LEN >= 4096 + 293);
-	rand_bytes_seeded_from_len(test_buf, 4096);
-	raw_key = &test_buf[4096];
+	rand_bytes_seeded_from_len(data, max_data_len);
+	rand_bytes_seeded_from_len(raw_key, outer_key_len);
 
-	rand_bytes_seeded_from_len(raw_key, 32);
-	HMAC_PREPAREKEY(&key, raw_key, 32);
+	HMAC_PREPAREKEY(&key, raw_key, outer_key_len);
 	HMAC_INIT(&ctx, &key);
-	for (size_t data_len = 0; data_len <= 4096; data_len++) {
+	for (size_t data_len = 0; data_len <= max_data_len; data_len++) {
 		/*
 		 * Cycle through key lengths as well.  Somewhat arbitrarily go
 		 * up to 293, which is somewhat larger than the largest hash
@@ -507,17 +447,17 @@ static void test_hmac(struct kunit *test)
 		 * hashed down to one block); going higher would not be useful.
 		 * To reduce correlation with data_len, use a prime number here.
 		 */
-		size_t key_len = data_len % 293;
+		size_t key_len = data_len % max_key_len;
 
-		HMAC_UPDATE(&ctx, test_buf, data_len);
+		HMAC_UPDATE(&ctx, data, data_len);
 
 		rand_bytes_seeded_from_len(raw_key, key_len);
-		HMAC_USINGRAWKEY(raw_key, key_len, test_buf, data_len, mac);
+		HMAC_USINGRAWKEY(raw_key, key_len, data, data_len, mac);
 		HMAC_UPDATE(&ctx, mac, HASH_SIZE);
 
 		/* Verify that HMAC() is consistent with HMAC_USINGRAWKEY(). */
 		HMAC_PREPAREKEY(&key, raw_key, key_len);
-		HMAC(&key, test_buf, data_len, mac2);
+		HMAC(&key, data, data_len, mac2);
 		KUNIT_ASSERT_MEMEQ_MSG(
 			test, mac, mac2, HASH_SIZE,
 			"HMAC gave different results with raw and prepared keys");
@@ -540,14 +480,17 @@ static void benchmark_hash(struct kunit *test)
 		1,   16,  64,	127,  128,  200,   256,
 		511, 512, 1024, 3173, 4096, 16384,
 	};
+	const size_t max_len = 16384;
+	u8 *data = alloc_buf(test, max_len);
 	u8 hash[HASH_SIZE];
 
 	if (!IS_ENABLED(CONFIG_CRYPTO_LIB_BENCHMARK))
 		kunit_skip(test, "not enabled");
 
 	/* Warm-up */
-	for (size_t i = 0; i < 10000000; i += TEST_BUF_LEN)
-		HASH(test_buf, TEST_BUF_LEN, hash);
+	memset(data, 0, max_len);
+	for (size_t i = 0; i < 10000000; i += max_len)
+		HASH(data, max_len, hash);
 
 	for (size_t i = 0; i < ARRAY_SIZE(lens_to_test); i++) {
 		size_t len = lens_to_test[i];
@@ -555,11 +498,11 @@ static void benchmark_hash(struct kunit *test)
 		size_t num_iters = 10000000 / (len + 128);
 		u64 t;
 
-		KUNIT_ASSERT_LE(test, len, TEST_BUF_LEN);
+		KUNIT_ASSERT_LE(test, len, max_len);
 		preempt_disable();
 		t = ktime_get_ns();
 		for (size_t j = 0; j < num_iters; j++)
-			HASH(test_buf, len, hash);
+			HASH(data, len, hash);
 		t = ktime_get_ns() - t;
 		preempt_enable();
 		kunit_info(test, "len=%zu: %llu MB/s", len,
diff --git a/lib/crypto/tests/md5_kunit.c b/lib/crypto/tests/md5_kunit.c
index 38bd52c..1598f85 100644
--- a/lib/crypto/tests/md5_kunit.c
+++ b/lib/crypto/tests/md5_kunit.c
@@ -30,8 +30,6 @@ static struct kunit_case hash_test_cases[] = {
 static struct kunit_suite hash_test_suite = {
 	.name = "md5",
 	.test_cases = hash_test_cases,
-	.suite_init = hash_suite_init,
-	.suite_exit = hash_suite_exit,
 };
 kunit_test_suite(hash_test_suite);
 
diff --git a/lib/crypto/tests/mldsa_kunit.c b/lib/crypto/tests/mldsa_kunit.c
index 67f8f93..ec64e10 100644
--- a/lib/crypto/tests/mldsa_kunit.c
+++ b/lib/crypto/tests/mldsa_kunit.c
@@ -8,6 +8,7 @@
 #include <kunit/test.h>
 #include <linux/random.h>
 #include <linux/unaligned.h>
+#include "test-utils.h"
 
 #define Q 8380417 /* The prime q = 2^23 - 2^13 + 1 */
 
@@ -60,14 +61,6 @@ static void do_mldsa_and_assert_success(struct kunit *test,
 	KUNIT_ASSERT_EQ(test, err, 0);
 }
 
-static u8 *kunit_kmemdup_or_fail(struct kunit *test, const u8 *src, size_t len)
-{
-	u8 *dst = kunit_kmalloc(test, len, GFP_KERNEL);
-
-	KUNIT_ASSERT_NOT_NULL(test, dst);
-	return memcpy(dst, src, len);
-}
-
 /*
  * Test that changing coefficients in a valid signature's z vector results in
  * the following behavior from mldsa_verify():
@@ -83,7 +76,7 @@ static u8 *kunit_kmemdup_or_fail(struct kunit *test, const u8 *src, size_t len)
 static void test_mldsa_z_range(struct kunit *test,
 			       const struct mldsa_testvector *tv)
 {
-	u8 *sig = kunit_kmemdup_or_fail(test, tv->sig, tv->sig_len);
+	u8 *sig = memdup_buf(test, tv->sig, tv->sig_len);
 	const int lambda = params[tv->alg].lambda;
 	const s32 gamma1 = params[tv->alg].gamma1;
 	const int beta = params[tv->alg].beta;
@@ -146,7 +139,7 @@ static void test_mldsa_bad_hints(struct kunit *test,
 {
 	const int omega = params[tv->alg].omega;
 	const int k = params[tv->alg].k;
-	u8 *sig = kunit_kmemdup_or_fail(test, tv->sig, tv->sig_len);
+	u8 *sig = memdup_buf(test, tv->sig, tv->sig_len);
 	/* Pointer to the encoded hint vector in the signature */
 	u8 *hintvec = &sig[tv->sig_len - omega - k];
 	u8 h;
@@ -202,9 +195,9 @@ static void test_mldsa_mutation(struct kunit *test,
 	const int msg_len = tv->msg_len;
 	const int pk_len = tv->pk_len;
 	const int num_iter = 200;
-	u8 *sig = kunit_kmemdup_or_fail(test, tv->sig, sig_len);
-	u8 *msg = kunit_kmemdup_or_fail(test, tv->msg, msg_len);
-	u8 *pk = kunit_kmemdup_or_fail(test, tv->pk, pk_len);
+	u8 *sig = memdup_buf(test, tv->sig, sig_len);
+	u8 *msg = memdup_buf(test, tv->msg, msg_len);
+	u8 *pk = memdup_buf(test, tv->pk, pk_len);
 
 	/* Initially the signature is valid. */
 	do_mldsa_and_assert_success(test, tv);
diff --git a/lib/crypto/tests/nh_kunit.c b/lib/crypto/tests/nh_kunit.c
index a8a3c3f..49e0fb3 100644
--- a/lib/crypto/tests/nh_kunit.c
+++ b/lib/crypto/tests/nh_kunit.c
@@ -5,14 +5,13 @@
 #include <crypto/nh.h>
 #include <kunit/test.h>
 #include "nh-testvecs.h"
+#include "test-utils.h"
 
 static void test_nh(struct kunit *test)
 {
-	u32 *key = kunit_kmalloc(test, NH_KEY_BYTES, GFP_KERNEL);
+	u32 *key = memdup_buf(test, nh_test_key, NH_KEY_BYTES);
 	__le64 hash[NH_NUM_PASSES];
 
-	KUNIT_ASSERT_NOT_NULL(test, key);
-	memcpy(key, nh_test_key, NH_KEY_BYTES);
 	le32_to_cpu_array(key, NH_KEY_WORDS);
 
 	nh(key, nh_test_msg, 16, hash);
diff --git a/lib/crypto/tests/poly1305_kunit.c b/lib/crypto/tests/poly1305_kunit.c
index 7ac191b..f3cb624 100644
--- a/lib/crypto/tests/poly1305_kunit.c
+++ b/lib/crypto/tests/poly1305_kunit.c
@@ -46,12 +46,7 @@ static void poly1305_withtestkey(const u8 *data, size_t len,
 static int poly1305_suite_init(struct kunit_suite *suite)
 {
 	rand_bytes_seeded_from_len(test_key, POLY1305_KEY_SIZE);
-	return hash_suite_init(suite);
-}
-
-static void poly1305_suite_exit(struct kunit_suite *suite)
-{
-	hash_suite_exit(suite);
+	return 0;
 }
 
 /*
@@ -81,19 +76,20 @@ static void poly1305_suite_exit(struct kunit_suite *suite)
  */
 static void test_poly1305_allones_keys_and_message(struct kunit *test)
 {
+	const size_t max_len = 4096;
+	u8 *data = alloc_buf(test, max_len);
 	struct poly1305_desc_ctx mac_ctx, macofmacs_ctx;
 	u8 mac[POLY1305_DIGEST_SIZE];
 
-	static_assert(TEST_BUF_LEN >= 4096);
-	memset(test_buf, 0xff, 4096);
+	memset(data, 0xff, max_len);
 
-	poly1305_init(&mac_ctx, test_buf);
-	poly1305_init(&macofmacs_ctx, test_buf);
+	poly1305_init(&mac_ctx, data);
+	poly1305_init(&macofmacs_ctx, data);
 	for (int i = 0; i < 32; i++) {
-		for (size_t len = 0; len <= 4096; len += 16) {
+		for (size_t len = 0; len <= max_len; len += 16) {
 			struct poly1305_desc_ctx tmp_ctx;
 
-			poly1305_update(&mac_ctx, test_buf, len);
+			poly1305_update(&mac_ctx, data, len);
 			tmp_ctx = mac_ctx;
 			poly1305_final(&tmp_ctx, mac);
 			poly1305_update(&macofmacs_ctx, mac,
@@ -157,7 +153,6 @@ static struct kunit_suite poly1305_test_suite = {
 	.name = "poly1305",
 	.test_cases = poly1305_test_cases,
 	.suite_init = poly1305_suite_init,
-	.suite_exit = poly1305_suite_exit,
 };
 kunit_test_suite(poly1305_test_suite);
 
diff --git a/lib/crypto/tests/polyval_kunit.c b/lib/crypto/tests/polyval_kunit.c
index d1f53a6..2835929 100644
--- a/lib/crypto/tests/polyval_kunit.c
+++ b/lib/crypto/tests/polyval_kunit.c
@@ -66,17 +66,18 @@ static void test_polyval_rfc8452_testvec(struct kunit *test)
  */
 static void test_polyval_allones_key_and_message(struct kunit *test)
 {
+	const size_t max_len = 4096;
+	u8 *data = alloc_buf(test, max_len);
 	struct polyval_key key;
 	struct polyval_ctx hashofhashes_ctx;
 	u8 hash[POLYVAL_BLOCK_SIZE];
 
-	static_assert(TEST_BUF_LEN >= 4096);
-	memset(test_buf, 0xff, 4096);
+	memset(data, 0xff, max_len);
 
-	polyval_preparekey(&key, test_buf);
+	polyval_preparekey(&key, data);
 	polyval_init(&hashofhashes_ctx, &key);
-	for (size_t len = 0; len <= 4096; len += 16) {
-		polyval(&key, test_buf, len, hash);
+	for (size_t len = 0; len <= max_len; len += 16) {
+		polyval(&key, data, len, hash);
 		polyval_update(&hashofhashes_ctx, hash, sizeof(hash));
 	}
 	polyval_final(&hashofhashes_ctx, hash);
@@ -95,7 +96,7 @@ static void check_key_consistency(struct kunit *test,
 				  const struct polyval_key *key1,
 				  const struct polyval_key *key2)
 {
-	u8 *data = test_buf;
+	u8 *data = alloc_buf(test, MAX_LEN_FOR_KEY_CHECK);
 	u8 hash1[POLYVAL_BLOCK_SIZE];
 	u8 hash2[POLYVAL_BLOCK_SIZE];
 
@@ -115,10 +116,10 @@ static void check_key_consistency(struct kunit *test,
 static void test_polyval_with_guarded_key(struct kunit *test)
 {
 	u8 raw_key[POLYVAL_BLOCK_SIZE];
-	u8 *guarded_raw_key = &test_buf[TEST_BUF_LEN - sizeof(raw_key)];
+	u8 *guarded_raw_key = alloc_guarded_buf(test, sizeof(raw_key));
 	struct polyval_key key1, key2;
 	struct polyval_key *guarded_key =
-		(struct polyval_key *)&test_buf[TEST_BUF_LEN - sizeof(key1)];
+		alloc_guarded_buf(test, sizeof(*guarded_key));
 
 	/* Prepare with regular buffers. */
 	rand_bytes(raw_key, sizeof(raw_key));
@@ -137,21 +138,20 @@ static void test_polyval_with_guarded_key(struct kunit *test)
 /*
  * Test that polyval_key only needs to be aligned to
  * __alignof__(struct polyval_key), i.e. 8 bytes.  The assembly code may prefer
- * 16-byte or higher alignment, but it musn't require it.
+ * 16-byte or higher alignment, but it mustn't require it.
  */
 static void test_polyval_with_minimally_aligned_key(struct kunit *test)
 {
 	u8 raw_key[POLYVAL_BLOCK_SIZE];
 	struct polyval_key key;
+	const size_t align = __alignof__(struct polyval_key);
+	u8 *key_buf = alloc_buf(test, sizeof(struct polyval_key) + 3 * align);
 	struct polyval_key *minaligned_key =
-		(struct polyval_key *)&test_buf[MAX_LEN_FOR_KEY_CHECK +
-						__alignof__(struct polyval_key)];
+		(struct polyval_key *)(PTR_ALIGN(key_buf, 2 * align) + align);
 
-	KUNIT_ASSERT_TRUE(test, IS_ALIGNED((uintptr_t)minaligned_key,
-					   __alignof__(struct polyval_key)));
+	KUNIT_ASSERT_TRUE(test, IS_ALIGNED((uintptr_t)minaligned_key, align));
 	KUNIT_ASSERT_TRUE(test,
-			  !IS_ALIGNED((uintptr_t)minaligned_key,
-				      2 * __alignof__(struct polyval_key)));
+			  !IS_ALIGNED((uintptr_t)minaligned_key, 2 * align));
 
 	rand_bytes(raw_key, sizeof(raw_key));
 	polyval_preparekey(&key, raw_key);
@@ -192,12 +192,7 @@ static int polyval_suite_init(struct kunit_suite *suite)
 
 	rand_bytes_seeded_from_len(raw_key, sizeof(raw_key));
 	polyval_preparekey(&test_key, raw_key);
-	return hash_suite_init(suite);
-}
-
-static void polyval_suite_exit(struct kunit_suite *suite)
-{
-	hash_suite_exit(suite);
+	return 0;
 }
 
 static struct kunit_case polyval_test_cases[] = {
@@ -215,7 +210,6 @@ static struct kunit_suite polyval_test_suite = {
 	.name = "polyval",
 	.test_cases = polyval_test_cases,
 	.suite_init = polyval_suite_init,
-	.suite_exit = polyval_suite_exit,
 };
 kunit_test_suite(polyval_test_suite);
 
diff --git a/lib/crypto/tests/sha1_kunit.c b/lib/crypto/tests/sha1_kunit.c
index 24ba8d5..27286af 100644
--- a/lib/crypto/tests/sha1_kunit.c
+++ b/lib/crypto/tests/sha1_kunit.c
@@ -30,8 +30,6 @@ static struct kunit_case hash_test_cases[] = {
 static struct kunit_suite hash_test_suite = {
 	.name = "sha1",
 	.test_cases = hash_test_cases,
-	.suite_init = hash_suite_init,
-	.suite_exit = hash_suite_exit,
 };
 kunit_test_suite(hash_test_suite);
 
diff --git a/lib/crypto/tests/sha224_kunit.c b/lib/crypto/tests/sha224_kunit.c
index 962ad46..bcf8b90 100644
--- a/lib/crypto/tests/sha224_kunit.c
+++ b/lib/crypto/tests/sha224_kunit.c
@@ -30,8 +30,6 @@ static struct kunit_case hash_test_cases[] = {
 static struct kunit_suite hash_test_suite = {
 	.name = "sha224",
 	.test_cases = hash_test_cases,
-	.suite_init = hash_suite_init,
-	.suite_exit = hash_suite_exit,
 };
 kunit_test_suite(hash_test_suite);
 
diff --git a/lib/crypto/tests/sha256_kunit.c b/lib/crypto/tests/sha256_kunit.c
index 5dccdee..17daa09 100644
--- a/lib/crypto/tests/sha256_kunit.c
+++ b/lib/crypto/tests/sha256_kunit.c
@@ -4,6 +4,7 @@
  */
 #include <crypto/sha2.h>
 #include "sha256-testvecs.h"
+#include "test-utils.h"
 
 /* Generate the HASH_KUNIT_CASES using hash-test-template.h. */
 #define HASH sha256
@@ -22,26 +23,6 @@
 #define HMAC_USINGRAWKEY hmac_sha256_usingrawkey
 #include "hash-test-template.h"
 
-static void free_guarded_buf(void *buf)
-{
-	vfree(buf);
-}
-
-/*
- * Allocate a KUnit-managed buffer that has length @len bytes immediately
- * followed by an unmapped page, and assert that the allocation succeeds.
- */
-static void *alloc_guarded_buf(struct kunit *test, size_t len)
-{
-	size_t full_len = round_up(len, PAGE_SIZE);
-	void *buf = vmalloc(full_len);
-
-	KUNIT_ASSERT_NOT_NULL(test, buf);
-	KUNIT_ASSERT_EQ(test, 0,
-			kunit_add_action_or_reset(test, free_guarded_buf, buf));
-	return buf + full_len - len;
-}
-
 /*
  * Test for sha256_finup_2x().  Specifically, choose various data lengths and
  * salt lengths, and for each one, verify that sha256_finup_2x() produces the
@@ -105,19 +86,20 @@ static void test_sha256_finup_2x(struct kunit *test)
 static void test_sha256_finup_2x_defaultctx(struct kunit *test)
 {
 	const size_t data_len = 128;
+	u8 *data = alloc_buf(test, 2 * data_len);
 	struct sha256_ctx ctx;
 	u8 hash1_a[SHA256_DIGEST_SIZE];
 	u8 hash2_a[SHA256_DIGEST_SIZE];
 	u8 hash1_b[SHA256_DIGEST_SIZE];
 	u8 hash2_b[SHA256_DIGEST_SIZE];
 
-	rand_bytes(test_buf, 2 * data_len);
+	rand_bytes(data, 2 * data_len);
 
 	sha256_init(&ctx);
-	sha256_finup_2x(&ctx, test_buf, &test_buf[data_len], data_len, hash1_a,
+	sha256_finup_2x(&ctx, data, &data[data_len], data_len, hash1_a,
 			hash2_a);
 
-	sha256_finup_2x(NULL, test_buf, &test_buf[data_len], data_len, hash1_b,
+	sha256_finup_2x(NULL, data, &data[data_len], data_len, hash1_b,
 			hash2_b);
 
 	KUNIT_ASSERT_MEMEQ(test, hash1_a, hash1_b, SHA256_DIGEST_SIZE);
@@ -131,18 +113,19 @@ static void test_sha256_finup_2x_defaultctx(struct kunit *test)
 static void test_sha256_finup_2x_hugelen(struct kunit *test)
 {
 	const size_t data_len = 4 * SHA256_BLOCK_SIZE;
+	u8 *data = alloc_buf(test, data_len);
 	struct sha256_ctx ctx = {};
 	u8 expected_hash[SHA256_DIGEST_SIZE];
 	u8 hash[SHA256_DIGEST_SIZE];
 
-	rand_bytes(test_buf, data_len);
+	rand_bytes(data, data_len);
 	for (size_t align = 0; align < SHA256_BLOCK_SIZE; align++) {
 		sha256_init(&ctx);
 		ctx.ctx.bytecount = 0x123456789abcd00 + align;
 
-		sha256_finup_2x(&ctx, test_buf, test_buf, data_len, hash, hash);
+		sha256_finup_2x(&ctx, data, data, data_len, hash, hash);
 
-		sha256_update(&ctx, test_buf, data_len);
+		sha256_update(&ctx, data, data_len);
 		sha256_final(&ctx, expected_hash);
 
 		KUNIT_ASSERT_MEMEQ(test, hash, expected_hash,
@@ -161,6 +144,7 @@ static void benchmark_sha256_finup_2x(struct kunit *test)
 	static const size_t salt_lens_to_test[] = { 0, 32, 64 };
 	const size_t data_len = 4096;
 	const size_t num_iters = 4096;
+	u8 *data = alloc_buf(test, data_len * 2);
 	struct sha256_ctx ctx;
 	u8 hash1[SHA256_DIGEST_SIZE];
 	u8 hash2[SHA256_DIGEST_SIZE];
@@ -170,12 +154,12 @@ static void benchmark_sha256_finup_2x(struct kunit *test)
 	if (!sha256_finup_2x_is_optimized())
 		kunit_skip(test, "not relevant");
 
-	rand_bytes(test_buf, data_len * 2);
+	rand_bytes(data, data_len * 2);
 
 	/* Warm-up */
 	for (size_t i = 0; i < num_iters; i++)
-		sha256_finup_2x(NULL, &test_buf[0], &test_buf[data_len],
-				data_len, hash1, hash2);
+		sha256_finup_2x(NULL, &data[0], &data[data_len], data_len,
+				hash1, hash2);
 
 	for (size_t i = 0; i < ARRAY_SIZE(salt_lens_to_test); i++) {
 		size_t salt_len = salt_lens_to_test[i];
@@ -186,12 +170,12 @@ static void benchmark_sha256_finup_2x(struct kunit *test)
 		 * not measured; we're just interested in sha256_finup_2x().
 		 */
 		sha256_init(&ctx);
-		sha256_update(&ctx, test_buf, salt_len);
+		sha256_update(&ctx, data, salt_len);
 
 		preempt_disable();
 		t0 = ktime_get_ns();
 		for (size_t j = 0; j < num_iters; j++)
-			sha256_finup_2x(&ctx, &test_buf[0], &test_buf[data_len],
+			sha256_finup_2x(&ctx, &data[0], &data[data_len],
 					data_len, hash1, hash2);
 		t1 = ktime_get_ns();
 		preempt_enable();
@@ -215,8 +199,6 @@ static struct kunit_case hash_test_cases[] = {
 static struct kunit_suite hash_test_suite = {
 	.name = "sha256",
 	.test_cases = hash_test_cases,
-	.suite_init = hash_suite_init,
-	.suite_exit = hash_suite_exit,
 };
 kunit_test_suite(hash_test_suite);
 
diff --git a/lib/crypto/tests/sha384_kunit.c b/lib/crypto/tests/sha384_kunit.c
index e1ef5c9..7640982 100644
--- a/lib/crypto/tests/sha384_kunit.c
+++ b/lib/crypto/tests/sha384_kunit.c
@@ -30,8 +30,6 @@ static struct kunit_case hash_test_cases[] = {
 static struct kunit_suite hash_test_suite = {
 	.name = "sha384",
 	.test_cases = hash_test_cases,
-	.suite_init = hash_suite_init,
-	.suite_exit = hash_suite_exit,
 };
 kunit_test_suite(hash_test_suite);
 
diff --git a/lib/crypto/tests/sha3_kunit.c b/lib/crypto/tests/sha3_kunit.c
index ed5fbe8..81b1130 100644
--- a/lib/crypto/tests/sha3_kunit.c
+++ b/lib/crypto/tests/sha3_kunit.c
@@ -273,12 +273,10 @@ static void test_shake_all_lens_up_to_4096(struct kunit *test)
 {
 	struct sha3_ctx main_ctx;
 	const size_t max_len = 4096;
-	u8 *const in = test_buf;
-	u8 *const out = &test_buf[TEST_BUF_LEN - max_len];
+	u8 *const in = alloc_buf(test, max_len);
+	u8 *const out = alloc_buf(test, max_len);
 	u8 main_hash[SHA3_256_DIGEST_SIZE];
 
-	KUNIT_ASSERT_LE(test, 2 * max_len, TEST_BUF_LEN);
-
 	rand_bytes_seeded_from_len(in, max_len);
 	for (int alg = 0; alg < 2; alg++) {
 		sha3_256_init(&main_ctx);
@@ -309,12 +307,8 @@ static void test_shake_all_lens_up_to_4096(struct kunit *test)
 static void test_shake_multiple_squeezes(struct kunit *test)
 {
 	const size_t max_len = 512;
-	u8 *ref_out;
-
-	KUNIT_ASSERT_GE(test, TEST_BUF_LEN, 2 * max_len);
-
-	ref_out = kunit_kzalloc(test, max_len, GFP_KERNEL);
-	KUNIT_ASSERT_NOT_NULL(test, ref_out);
+	u8 *buf = alloc_buf(test, max_len);
+	u8 *ref_out = alloc_buf(test, max_len);
 
 	for (int i = 0; i < 2000; i++) {
 		const int alg = rand32() % 2;
@@ -322,8 +316,8 @@ static void test_shake_multiple_squeezes(struct kunit *test)
 		const size_t out_len = rand_length(max_len);
 		const size_t in_offs = rand_offset(max_len - in_len);
 		const size_t out_offs = rand_offset(max_len - out_len);
-		u8 *const in = &test_buf[in_offs];
-		u8 *const out = &test_buf[out_offs];
+		u8 *const in = &buf[in_offs];
+		u8 *const out = &buf[out_offs];
 		struct shake_ctx ctx;
 		size_t remaining_len, j, num_parts;
 
@@ -368,16 +362,12 @@ static void test_shake_multiple_squeezes(struct kunit *test)
 static void test_shake_with_guarded_bufs(struct kunit *test)
 {
 	const size_t max_len = 512;
-	u8 *reg_buf;
-
-	KUNIT_ASSERT_GE(test, TEST_BUF_LEN, max_len);
-
-	reg_buf = kunit_kzalloc(test, max_len, GFP_KERNEL);
-	KUNIT_ASSERT_NOT_NULL(test, reg_buf);
+	u8 *buf = alloc_guarded_buf(test, max_len);
+	u8 *reg_buf = alloc_buf(test, max_len);
 
 	for (int alg = 0; alg < 2; alg++) {
 		for (size_t len = 0; len <= max_len; len++) {
-			u8 *guarded_buf = &test_buf[TEST_BUF_LEN - len];
+			u8 *guarded_buf = &buf[max_len - len];
 
 			rand_bytes(reg_buf, len);
 			memcpy(guarded_buf, reg_buf, len);
@@ -413,8 +403,6 @@ static struct kunit_case sha3_test_cases[] = {
 static struct kunit_suite sha3_test_suite = {
 	.name = "sha3",
 	.test_cases = sha3_test_cases,
-	.suite_init = hash_suite_init,
-	.suite_exit = hash_suite_exit,
 };
 kunit_test_suite(sha3_test_suite);
 
diff --git a/lib/crypto/tests/sha512_kunit.c b/lib/crypto/tests/sha512_kunit.c
index 8923e2d..e03926c 100644
--- a/lib/crypto/tests/sha512_kunit.c
+++ b/lib/crypto/tests/sha512_kunit.c
@@ -30,8 +30,6 @@ static struct kunit_case hash_test_cases[] = {
 static struct kunit_suite hash_test_suite = {
 	.name = "sha512",
 	.test_cases = hash_test_cases,
-	.suite_init = hash_suite_init,
-	.suite_exit = hash_suite_exit,
 };
 kunit_test_suite(hash_test_suite);
 
diff --git a/lib/crypto/tests/sm3_kunit.c b/lib/crypto/tests/sm3_kunit.c
index dc8136a..1cba384 100644
--- a/lib/crypto/tests/sm3_kunit.c
+++ b/lib/crypto/tests/sm3_kunit.c
@@ -22,8 +22,6 @@ static struct kunit_case sm3_test_cases[] = {
 static struct kunit_suite sm3_test_suite = {
 	.name = "sm3",
 	.test_cases = sm3_test_cases,
-	.suite_init = hash_suite_init,
-	.suite_exit = hash_suite_exit,
 };
 kunit_test_suite(sm3_test_suite);
 
diff --git a/lib/crypto/tests/test-utils.h b/lib/crypto/tests/test-utils.h
new file mode 100644
index 0000000..73ca21f
--- /dev/null
+++ b/lib/crypto/tests/test-utils.h
@@ -0,0 +1,111 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Test utility functions shared by the crypto library tests.
+ *
+ * For now this is simply a header that's included into the KUnit test suites
+ * that need it.  If this gets too large it could be made its own translation
+ * unit and libcrypto_test_utils module, but that seems overkill for now.
+ */
+#ifndef LIB_CRYPTO_TEST_UTILS_H
+#define LIB_CRYPTO_TEST_UTILS_H
+
+#include <kunit/test.h>
+#include <linux/math.h>
+#include <linux/minmax.h>
+#include <linux/string.h>
+#include <linux/vmalloc.h>
+
+static u64 random_seed;
+
+static __maybe_unused void action_free_guarded_buf(void *buf)
+{
+	vfree(buf);
+}
+
+/*
+ * Allocate a KUnit-managed buffer that has length @size bytes (> 0) immediately
+ * followed by an unmapped page, and assert that the allocation succeeds.
+ */
+static __maybe_unused void *alloc_guarded_buf(struct kunit *test, size_t size)
+{
+	size_t full_size = round_up(size, PAGE_SIZE);
+	void *buf = vmalloc(full_size);
+
+	KUNIT_ASSERT_NOT_NULL(test, buf);
+	KUNIT_ASSERT_EQ(test, 0,
+			kunit_add_action_or_reset(test, action_free_guarded_buf,
+						  buf));
+	return buf + full_size - size;
+}
+
+static __maybe_unused void *alloc_buf(struct kunit *test, size_t size)
+{
+	void *buf = kunit_kmalloc(test, size, GFP_KERNEL);
+
+	KUNIT_ASSERT_NOT_NULL(test, buf);
+	return buf;
+}
+
+static __maybe_unused void *memdup_buf(struct kunit *test, const void *src,
+				       size_t size)
+{
+	void *dst = alloc_buf(test, size);
+
+	return memcpy(dst, src, size);
+}
+
+/*
+ * This is a simple linear congruential generator.  It is used only for testing,
+ * which does not require cryptographically secure random numbers.  A hard-coded
+ * algorithm is used instead of <linux/prandom.h> so that it matches the
+ * algorithm used by the test vector generation script.  This allows the input
+ * data in random test vectors to be concisely stored as just the seed.
+ */
+static __maybe_unused u32 rand32(void)
+{
+	random_seed = (random_seed * 25214903917 + 11) & ((1ULL << 48) - 1);
+	return random_seed >> 16;
+}
+
+static __maybe_unused void rand_bytes(u8 *out, size_t len)
+{
+	for (size_t i = 0; i < len; i++)
+		out[i] = rand32();
+}
+
+static __maybe_unused void rand_bytes_seeded_from_len(u8 *out, size_t len)
+{
+	random_seed = len;
+	rand_bytes(out, len);
+}
+
+static __maybe_unused bool rand_bool(void)
+{
+	return rand32() % 2;
+}
+
+/* Generate a random length, preferring small lengths. */
+static __maybe_unused size_t rand_length(size_t max_len)
+{
+	size_t len;
+
+	switch (rand32() % 3) {
+	case 0:
+		len = rand32() % 128;
+		break;
+	case 1:
+		len = rand32() % 3072;
+		break;
+	default:
+		len = rand32();
+		break;
+	}
+	return len % (max_len + 1);
+}
+
+static __maybe_unused size_t rand_offset(size_t max_offset)
+{
+	return min(rand32() % 128, max_offset);
+}
+
+#endif /* LIB_CRYPTO_TEST_UTILS_H */
diff --git a/scripts/crypto/gen-aead-testvecs.py b/scripts/crypto/gen-aead-testvecs.py
new file mode 100755
index 0000000..d77d646
--- /dev/null
+++ b/scripts/crypto/gen-aead-testvecs.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Script that generates known-good data used in the AEAD tests.
+#
+# Requires that python-cryptography be installed.
+#
+# Copyright 2026 Google LLC
+
+import hashlib
+import sys
+import cryptography.hazmat.primitives.ciphers.aead
+
+
+# Deterministically generate 'length' random bytes.
+def rand_bytes(length):
+    seed = length
+    out = []
+    for _ in range(length):
+        seed = (seed * 25214903917 + 11) % 2**48
+        out.append((seed >> 16) % 256)
+    return bytes(out)
+
+
+# Deterministically generate many different AEAD inputs using exactly the same
+# method that the test uses; encrypt them using an independent implementation of
+# the algorithm; compute the checksum of all the resulting (ciphertext, authtag)
+# pairs concatenated to each other; and print the checksum as a C struct.
+def gen_monte_carlo_checksum(alg):
+    blake2s = hashlib.blake2s()
+    for data_len in range(1025):
+        ad_len = data_len % 293
+        pt = rand_bytes(data_len)
+        ad = rand_bytes(ad_len)
+        if alg == "aes-ccm":
+            key_len = [16, 24, 32][data_len % 3]
+            key = rand_bytes(key_len)
+            nonce = rand_bytes([7, 8, 9, 10, 11, 12, 13][data_len % 7])
+            tag_len = [4, 6, 8, 10, 12, 14, 16][data_len % 7]
+            ccm = cryptography.hazmat.primitives.ciphers.aead.AESCCM(
+                key, tag_length=tag_len
+            )
+            ct_and_tag = ccm.encrypt(nonce, pt, ad)
+        elif alg == "aes-gcm":
+            key_len = [16, 24, 32][data_len % 3]
+            key = rand_bytes(key_len)
+            nonce = rand_bytes(12)
+            tag_len = [4, 8, 12, 13, 14, 15, 16][data_len % 7]
+            gcm = cryptography.hazmat.primitives.ciphers.aead.AESGCM(key)
+            # python-cryptography supports only 16-byte GCM tags.  However, in
+            # GCM, shorter tags are simply truncated.  Do that below.
+            ct_and_tag = gcm.encrypt(nonce, pt, ad)[: data_len + tag_len]
+
+        blake2s.update(ct_and_tag)
+
+    name = f"{alg.replace('-', '_')}_monte_carlo_checksum"
+    value = blake2s.digest()
+    print(f"static const u8 {name}[BLAKE2S_HASH_SIZE] = {{")
+    for i in range(0, len(value), 11):
+        line = "\t" + "".join(f"0x{b:02x}, " for b in value[i : i + 11])
+        print(f"{line.rstrip()}")
+    print("};")
+
+
+if len(sys.argv) != 2 or sys.argv[1] not in ("aes-ccm", "aes-gcm"):
+    sys.stderr.write("Usage: gen-aead-testvecs.py [aes-ccm|aes-gcm]\n")
+    sys.exit(1)
+
+gen_monte_carlo_checksum(sys.argv[1])
diff --git a/scripts/crypto/gen-fips-testvecs.py b/scripts/crypto/gen-fips-testvecs.py
index 9f18bcb..b8c8a78 100755
--- a/scripts/crypto/gen-fips-testvecs.py
+++ b/scripts/crypto/gen-fips-testvecs.py
@@ -1,46 +1,148 @@
 #!/usr/bin/env python3
 # SPDX-License-Identifier: GPL-2.0-or-later
 #
-# Script that generates lib/crypto/fips.h
+# Script that generates lib/crypto/fips-aes.h and lib/crypto/fips-sha.h
 #
 # Requires that python-cryptography be installed.
 #
 # Copyright 2025 Google LLC
 
 import cryptography.hazmat.primitives.ciphers
+import cryptography.hazmat.primitives.ciphers.aead
 import cryptography.hazmat.primitives.cmac
 import hashlib
 import hmac
 
-fips_test_data = b"fips test data\0\0"
-fips_test_key = b"fips test key\0\0\0"
 
-def print_static_u8_array_definition(name, value):
-    print('')
-    print(f'static const u8 {name}[] __initconst __maybe_unused = {{')
+def print_static_u8_array_definition(file, name, value):
+    print("", file=file)
+    print(f"static const u8 {name}[] __initconst __maybe_unused = {{", file=file)
     for i in range(0, len(value), 8):
-        line = '\t' + ''.join(f'0x{b:02x}, ' for b in value[i:i+8])
-        print(f'{line.rstrip()}')
-    print('};')
-
-print('/* SPDX-License-Identifier: GPL-2.0-or-later */')
-print(f'/* This file was generated by: gen-fips-testvecs.py */')
-print()
-print('#include <linux/fips.h>')
-
-print_static_u8_array_definition("fips_test_data", fips_test_data)
-print_static_u8_array_definition("fips_test_key", fips_test_key)
-
-for alg in 'sha1', 'sha256', 'sha512':
-    ctx = hmac.new(fips_test_key, digestmod=alg)
-    ctx.update(fips_test_data)
-    print_static_u8_array_definition(f'fips_test_hmac_{alg}_value', ctx.digest())
-
-print_static_u8_array_definition(f'fips_test_sha3_256_value',
-                                 hashlib.sha3_256(fips_test_data).digest())
-
-aes = cryptography.hazmat.primitives.ciphers.algorithms.AES(fips_test_key)
-aes_cmac = cryptography.hazmat.primitives.cmac.CMAC(aes)
-aes_cmac.update(fips_test_data)
-print_static_u8_array_definition('fips_test_aes_cmac_value',
-                                 aes_cmac.finalize())
+        line = "\t" + "".join(f"0x{b:02x}, " for b in value[i : i + 8])
+        print(f"{line.rstrip()}", file=file)
+    print("};", file=file)
+
+
+def print_header(file):
+    print("/* SPDX-License-Identifier: GPL-2.0-or-later */", file=file)
+    print("/* This file was generated by: gen-fips-testvecs.py */", file=file)
+    print("/* clang-format off */", file=file)
+    print("", file=file)
+    print("#include <linux/fips.h>", file=file)
+
+
+def gen_aes_test_data(file):
+    fips_test_data = b"fips test data\0\0"
+    fips_test_ad = b"fips test ad\0\0\0\0"
+    fips_test_iv = b"fips test iv\0\0\0\0"
+    fips_test_key = b"fips test key\0\0\0"
+    fips_test_xts_key = b"key1" + (b"\0" * 12) + b"key2" + (b"\0" * 12)
+
+    print_header(file)
+    print_static_u8_array_definition(file, "fips_test_data", fips_test_data)
+    print_static_u8_array_definition(file, "fips_test_ad", fips_test_ad)
+    print_static_u8_array_definition(file, "fips_test_iv", fips_test_iv)
+    print_static_u8_array_definition(file, "fips_test_key", fips_test_key)
+    print_static_u8_array_definition(file, "fips_test_xts_key", fips_test_xts_key)
+
+    aes = cryptography.hazmat.primitives.ciphers.algorithms.AES(fips_test_key)
+
+    # AES-CMAC
+    aes_cmac = cryptography.hazmat.primitives.cmac.CMAC(aes)
+    aes_cmac.update(fips_test_data)
+    print_static_u8_array_definition(
+        file, "fips_test_aes_cmac_value", aes_cmac.finalize()
+    )
+
+    # AES-ECB
+    cipher = cryptography.hazmat.primitives.ciphers.Cipher(
+        aes, cryptography.hazmat.primitives.ciphers.modes.ECB()
+    )
+    encryptor = cipher.encryptor()
+    ctext = encryptor.update(fips_test_data) + encryptor.finalize()
+    print_static_u8_array_definition(file, "fips_test_aes_ecb_ctext", ctext)
+
+    # AES-CBC
+    cipher = cryptography.hazmat.primitives.ciphers.Cipher(
+        aes, cryptography.hazmat.primitives.ciphers.modes.CBC(fips_test_iv)
+    )
+    encryptor = cipher.encryptor()
+    ctext = encryptor.update(fips_test_data) + encryptor.finalize()
+    print_static_u8_array_definition(file, "fips_test_aes_cbc_ctext", ctext)
+
+    # AES-CBC-CTS
+    cipher = cryptography.hazmat.primitives.ciphers.Cipher(
+        aes, cryptography.hazmat.primitives.ciphers.modes.CBC(fips_test_iv)
+    )
+    encryptor = cipher.encryptor()
+    ctext = encryptor.update(fips_test_data * 2) + encryptor.finalize()
+    ctext = ctext[16:32] + ctext[0:16]
+    print_static_u8_array_definition(file, "fips_test_aes_cbc_cts_ctext", ctext)
+
+    # AES-CTR
+    cipher = cryptography.hazmat.primitives.ciphers.Cipher(
+        aes, cryptography.hazmat.primitives.ciphers.modes.CTR(fips_test_iv)
+    )
+    encryptor = cipher.encryptor()
+    ctext = encryptor.update(fips_test_data) + encryptor.finalize()
+    print_static_u8_array_definition(file, "fips_test_aes_ctr_ctext", ctext)
+
+    # AES-XTS
+    cipher = cryptography.hazmat.primitives.ciphers.Cipher(
+        cryptography.hazmat.primitives.ciphers.algorithms.AES(fips_test_xts_key),
+        cryptography.hazmat.primitives.ciphers.modes.XTS(fips_test_iv),
+    )
+    encryptor = cipher.encryptor()
+    ctext = encryptor.update(fips_test_data) + encryptor.finalize()
+    print_static_u8_array_definition(file, "fips_test_aes_xts_ctext", ctext)
+
+    # AES-GCM
+    cipher = cryptography.hazmat.primitives.ciphers.aead.AESGCM(fips_test_key)
+    ct_and_tag = cipher.encrypt(
+        nonce=fips_test_iv[:12], data=fips_test_data, associated_data=fips_test_ad
+    )
+    print_static_u8_array_definition(
+        file, "fips_test_aes_gcm_ctext_and_tag", ct_and_tag
+    )
+
+    # AES-CCM
+    cipher = cryptography.hazmat.primitives.ciphers.aead.AESCCM(
+        fips_test_key, tag_length=16
+    )
+    ct_and_tag = cipher.encrypt(
+        nonce=fips_test_iv[:13], data=fips_test_data, associated_data=fips_test_ad
+    )
+    print_static_u8_array_definition(
+        file, "fips_test_aes_ccm_ctext_and_tag", ct_and_tag
+    )
+
+
+def gen_sha_test_data(file):
+    fips_test_data = b"fips test data\0\0"
+    fips_test_key = b"fips test key\0\0\0"
+
+    print_header(file)
+    print_static_u8_array_definition(file, "fips_test_data", fips_test_data)
+    print_static_u8_array_definition(file, "fips_test_key", fips_test_key)
+
+    for alg in "sha1", "sha256", "sha512":
+        ctx = hmac.new(fips_test_key, digestmod=alg)
+        ctx.update(fips_test_data)
+        print_static_u8_array_definition(
+            file, f"fips_test_hmac_{alg}_value", ctx.digest()
+        )
+
+    print_static_u8_array_definition(
+        file, "fips_test_sha3_256_value", hashlib.sha3_256(fips_test_data).digest()
+    )
+
+
+filename = "lib/crypto/fips-aes.h"
+with open(filename, "w") as file:
+    print(f"Generating {filename}")
+    gen_aes_test_data(file)
+
+filename = "lib/crypto/fips-sha.h"
+with open(filename, "w") as file:
+    print(f"Generating {filename}")
+    gen_sha_test_data(file)
