All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/5] lib/crypto: KUnit tests for AES-CCM and AES-GCM
@ 2026-07-30  1:32 Eric Biggers
  2026-07-30  1:32 ` [PATCH 1/5] lib/crypto: tests: Create test-utils.h Eric Biggers
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Eric Biggers @ 2026-07-30  1:32 UTC (permalink / raw)
  To: linux-crypto
  Cc: linux-kernel, Ard Biesheuvel, Jason A . Donenfeld, Herbert Xu,
	Thomas Huth, Eric Biggers

This series applies to libcrypto-next.  It can also be retrieved from:

    git fetch https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux.git gcm-ccm-kunit-v1

This series adds comprehensive KUnit test suites for the AES-CCM and
AES-GCM library APIs.

This is a follow-up to the series 
"[PATCH v2 00/13] Library APIs for AES encryption modes"
(https://lore.kernel.org/linux-crypto/20260715221153.246410-1-ebiggers@kernel.org/)

Eric Biggers (5):
  lib/crypto: tests: Create test-utils.h
  lib/crypto: tests: Use per-test-case buffers in hash tests
  lib/crypto: tests: Add aead-test-template.h
  lib/crypto: tests: Add KUnit test suite for AES-CCM
  lib/crypto: tests: Add KUnit test suite for AES-GCM

 lib/crypto/.kunitconfig                   |    2 +
 lib/crypto/tests/Kconfig                  |   18 +
 lib/crypto/tests/Makefile                 |    2 +
 lib/crypto/tests/aead-test-template.h     | 1039 +++++++++++++++++++++
 lib/crypto/tests/aes_cbc_macs_kunit.c     |   12 +-
 lib/crypto/tests/aes_ccm_kunit.c          |  352 +++++++
 lib/crypto/tests/aes_gcm_kunit.c          |  472 ++++++++++
 lib/crypto/tests/blake2b_kunit.c          |   33 +-
 lib/crypto/tests/blake2s_kunit.c          |   33 +-
 lib/crypto/tests/chacha20poly1305_kunit.c |    9 +-
 lib/crypto/tests/ghash_kunit.c            |   37 +-
 lib/crypto/tests/hash-test-template.h     |  249 ++---
 lib/crypto/tests/md5_kunit.c              |    2 -
 lib/crypto/tests/mldsa_kunit.c            |   19 +-
 lib/crypto/tests/nh_kunit.c               |    5 +-
 lib/crypto/tests/poly1305_kunit.c         |   21 +-
 lib/crypto/tests/polyval_kunit.c          |   38 +-
 lib/crypto/tests/sha1_kunit.c             |    2 -
 lib/crypto/tests/sha224_kunit.c           |    2 -
 lib/crypto/tests/sha256_kunit.c           |   48 +-
 lib/crypto/tests/sha384_kunit.c           |    2 -
 lib/crypto/tests/sha3_kunit.c             |   30 +-
 lib/crypto/tests/sha512_kunit.c           |    2 -
 lib/crypto/tests/sm3_kunit.c              |    2 -
 lib/crypto/tests/test-utils.h             |  111 +++
 scripts/crypto/gen-aead-testvecs.py       |   69 ++
 26 files changed, 2271 insertions(+), 340 deletions(-)
 create mode 100644 lib/crypto/tests/aead-test-template.h
 create mode 100644 lib/crypto/tests/aes_ccm_kunit.c
 create mode 100644 lib/crypto/tests/aes_gcm_kunit.c
 create mode 100644 lib/crypto/tests/test-utils.h
 create mode 100755 scripts/crypto/gen-aead-testvecs.py


base-commit: f70ad727d1d60a4dfcb1a22152d4169f9a205af9
-- 
2.55.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/5] lib/crypto: tests: Create test-utils.h
  2026-07-30  1:32 [PATCH 0/5] lib/crypto: KUnit tests for AES-CCM and AES-GCM Eric Biggers
@ 2026-07-30  1:32 ` Eric Biggers
  2026-07-30  1:32 ` [PATCH 2/5] lib/crypto: tests: Use per-test-case buffers in hash tests Eric Biggers
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Eric Biggers @ 2026-07-30  1:32 UTC (permalink / raw)
  To: linux-crypto
  Cc: linux-kernel, Ard Biesheuvel, Jason A . Donenfeld, Herbert Xu,
	Thomas Huth, Eric Biggers

Move hash-test-template's test RNG code and the buffer allocation code
from various test suites into a header test-utils.h.  This allows them
to be shared by other test suites.

Signed-off-by: Eric Biggers <ebiggers@kernel.org>
---
 lib/crypto/tests/chacha20poly1305_kunit.c |   9 +-
 lib/crypto/tests/hash-test-template.h     |  58 +----------
 lib/crypto/tests/mldsa_kunit.c            |  19 ++--
 lib/crypto/tests/nh_kunit.c               |   5 +-
 lib/crypto/tests/sha256_kunit.c           |  21 +---
 lib/crypto/tests/test-utils.h             | 111 ++++++++++++++++++++++
 6 files changed, 124 insertions(+), 99 deletions(-)
 create mode 100644 lib/crypto/tests/test-utils.h

diff --git a/lib/crypto/tests/chacha20poly1305_kunit.c b/lib/crypto/tests/chacha20poly1305_kunit.c
index 97a68fab88a7..d5504d0f4ad7 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/hash-test-template.h b/lib/crypto/tests/hash-test-template.h
index 61b43e62779f..e6ab1dc8f9d7 100644
--- a/lib/crypto/tests/hash-test-template.h
+++ b/lib/crypto/tests/hash-test-template.h
@@ -8,69 +8,13 @@
 #include <kunit/run-in-irq-context.h>
 #include <kunit/test.h>
 #include <linux/vmalloc.h>
+#include "test-utils.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)
 {
 	/*
diff --git a/lib/crypto/tests/mldsa_kunit.c b/lib/crypto/tests/mldsa_kunit.c
index 67f8f93e3dc6..ec64e10c97f2 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 a8a3c3f345cb..49e0fb3dd294 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/sha256_kunit.c b/lib/crypto/tests/sha256_kunit.c
index 5dccdee79693..8758db3673c7 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
diff --git a/lib/crypto/tests/test-utils.h b/lib/crypto/tests/test-utils.h
new file mode 100644
index 000000000000..73ca21f9176e
--- /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 */

base-commit: f70ad727d1d60a4dfcb1a22152d4169f9a205af9
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/5] lib/crypto: tests: Use per-test-case buffers in hash tests
  2026-07-30  1:32 [PATCH 0/5] lib/crypto: KUnit tests for AES-CCM and AES-GCM Eric Biggers
  2026-07-30  1:32 ` [PATCH 1/5] lib/crypto: tests: Create test-utils.h Eric Biggers
@ 2026-07-30  1:32 ` Eric Biggers
  2026-07-30  1:32 ` [PATCH 3/5] lib/crypto: tests: Add aead-test-template.h Eric Biggers
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Eric Biggers @ 2026-07-30  1:32 UTC (permalink / raw)
  To: linux-crypto
  Cc: linux-kernel, Ard Biesheuvel, Jason A . Donenfeld, Herbert Xu,
	Thomas Huth, Eric Biggers

Make the test cases more self-contained by replacing the test suite
scoped guarded buffer ('test_buf') with buffers allocated by each test
case using the helper functions in test-utils.h.

Signed-off-by: Eric Biggers <ebiggers@kernel.org>
---
 lib/crypto/tests/aes_cbc_macs_kunit.c |  12 +-
 lib/crypto/tests/blake2b_kunit.c      |  33 ++---
 lib/crypto/tests/blake2s_kunit.c      |  33 ++---
 lib/crypto/tests/ghash_kunit.c        |  37 +++--
 lib/crypto/tests/hash-test-template.h | 191 +++++++++++++-------------
 lib/crypto/tests/md5_kunit.c          |   2 -
 lib/crypto/tests/poly1305_kunit.c     |  21 ++-
 lib/crypto/tests/polyval_kunit.c      |  38 +++--
 lib/crypto/tests/sha1_kunit.c         |   2 -
 lib/crypto/tests/sha224_kunit.c       |   2 -
 lib/crypto/tests/sha256_kunit.c       |  27 ++--
 lib/crypto/tests/sha384_kunit.c       |   2 -
 lib/crypto/tests/sha3_kunit.c         |  30 ++--
 lib/crypto/tests/sha512_kunit.c       |   2 -
 lib/crypto/tests/sm3_kunit.c          |   2 -
 15 files changed, 193 insertions(+), 241 deletions(-)

diff --git a/lib/crypto/tests/aes_cbc_macs_kunit.c b/lib/crypto/tests/aes_cbc_macs_kunit.c
index ae3745212f03..6afdb3a04c17 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/blake2b_kunit.c b/lib/crypto/tests/blake2b_kunit.c
index bc0be7da1e76..e2ce6cf3a99d 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 6832d9aa7b82..682f695f8a09 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/ghash_kunit.c b/lib/crypto/tests/ghash_kunit.c
index 68b3837a3607..4d7aa79b3cb2 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 e6ab1dc8f9d7..bb6eaa509d0f 100644
--- a/lib/crypto/tests/hash-test-template.h
+++ b/lib/crypto/tests/hash-test-template.h
@@ -7,38 +7,8 @@
  */
 #include <kunit/run-in-irq-context.h>
 #include <kunit/test.h>
-#include <linux/vmalloc.h>
 #include "test-utils.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 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;
-}
-
 /*
  * Test the hash function against a list of test vectors.
  *
@@ -48,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,
@@ -71,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);
@@ -91,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;
@@ -99,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
@@ -116,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);
@@ -141,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);
@@ -159,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);
 	}
 }
@@ -177,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,
@@ -208,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,
@@ -226,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)",
@@ -252,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");
 }
@@ -265,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;
 };
@@ -280,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;
 }
 
@@ -290,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);
@@ -309,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];
@@ -341,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++;
@@ -349,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)
@@ -370,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++) {
@@ -429,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
@@ -451,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");
@@ -484,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];
@@ -499,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 38bd52c25ae3..1598f8585b45 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/poly1305_kunit.c b/lib/crypto/tests/poly1305_kunit.c
index 7ac191bd96b6..f3cb6245bc29 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 d1f53a690ab8..2835929e890c 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 24ba8d5669c8..27286afaa407 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 962ad46b9c99..bcf8b90f9ae2 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 8758db3673c7..17daa09adfe7 100644
--- a/lib/crypto/tests/sha256_kunit.c
+++ b/lib/crypto/tests/sha256_kunit.c
@@ -86,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);
@@ -112,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,
@@ -142,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];
@@ -151,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];
@@ -167,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();
@@ -196,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 e1ef5c995bb6..76409822d965 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 ed5fbe80337f..81b113079755 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 8923e2d7d3d4..e03926c24403 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 dc8136acdff6..1cba384946e9 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);
 
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/5] lib/crypto: tests: Add aead-test-template.h
  2026-07-30  1:32 [PATCH 0/5] lib/crypto: KUnit tests for AES-CCM and AES-GCM Eric Biggers
  2026-07-30  1:32 ` [PATCH 1/5] lib/crypto: tests: Create test-utils.h Eric Biggers
  2026-07-30  1:32 ` [PATCH 2/5] lib/crypto: tests: Use per-test-case buffers in hash tests Eric Biggers
@ 2026-07-30  1:32 ` Eric Biggers
  2026-07-30  1:32 ` [PATCH 4/5] lib/crypto: tests: Add KUnit test suite for AES-CCM Eric Biggers
  2026-07-30  1:33 ` [PATCH 5/5] lib/crypto: tests: Add KUnit test suite for AES-GCM Eric Biggers
  4 siblings, 0 replies; 6+ messages in thread
From: Eric Biggers @ 2026-07-30  1:32 UTC (permalink / raw)
  To: linux-crypto
  Cc: linux-kernel, Ard Biesheuvel, Jason A . Donenfeld, Herbert Xu,
	Thomas Huth, Eric Biggers

Add aead-test-template.h which contains shared test logic for AEAD
algorithms.  It's similar to the existing hash-test-template.h, which
contains shared test logic for hash algorithms.

The initial use cases for this will be AES-CCM and AES-GCM.

It may be used by the existing ChaCha20Poly1305 test suite in the future
as well, though the ChaCha20Poly1305 API currently lacks some of the
functionality that the "template" expects to be present.

Signed-off-by: Eric Biggers <ebiggers@kernel.org>
---
 lib/crypto/tests/aead-test-template.h | 1039 +++++++++++++++++++++++++
 1 file changed, 1039 insertions(+)
 create mode 100644 lib/crypto/tests/aead-test-template.h

diff --git a/lib/crypto/tests/aead-test-template.h b/lib/crypto/tests/aead-test-template.h
new file mode 100644
index 000000000000..0c0138d2fa8a
--- /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)
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 4/5] lib/crypto: tests: Add KUnit test suite for AES-CCM
  2026-07-30  1:32 [PATCH 0/5] lib/crypto: KUnit tests for AES-CCM and AES-GCM Eric Biggers
                   ` (2 preceding siblings ...)
  2026-07-30  1:32 ` [PATCH 3/5] lib/crypto: tests: Add aead-test-template.h Eric Biggers
@ 2026-07-30  1:32 ` Eric Biggers
  2026-07-30  1:33 ` [PATCH 5/5] lib/crypto: tests: Add KUnit test suite for AES-GCM Eric Biggers
  4 siblings, 0 replies; 6+ messages in thread
From: Eric Biggers @ 2026-07-30  1:32 UTC (permalink / raw)
  To: linux-crypto
  Cc: linux-kernel, Ard Biesheuvel, Jason A . Donenfeld, Herbert Xu,
	Thomas Huth, Eric Biggers

Add a KUnit test suite for the AES-CCM library API.

It consists of:

- All the shared test cases from aead-test-template.h.  These include
  extensive consistency tests, a "Monte-Carlo test", and a benchmark.

- Tests against hardcoded AES-CCM test vectors from external sources.

- Tests for CCM-specific message length validation.

To generate the expected aes_ccm_monte_carlo_checksum[] value, add a
script gen-aead-testvecs.py which computes it using python-cryptography.

Signed-off-by: Eric Biggers <ebiggers@kernel.org>
---
 lib/crypto/.kunitconfig             |   1 +
 lib/crypto/tests/Kconfig            |   9 +
 lib/crypto/tests/Makefile           |   1 +
 lib/crypto/tests/aes_ccm_kunit.c    | 352 ++++++++++++++++++++++++++++
 scripts/crypto/gen-aead-testvecs.py |  60 +++++
 5 files changed, 423 insertions(+)
 create mode 100644 lib/crypto/tests/aes_ccm_kunit.c
 create mode 100755 scripts/crypto/gen-aead-testvecs.py

diff --git a/lib/crypto/.kunitconfig b/lib/crypto/.kunitconfig
index 3efc854a2c08..f0237e602f28 100644
--- a/lib/crypto/.kunitconfig
+++ b/lib/crypto/.kunitconfig
@@ -3,6 +3,7 @@ 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_BLAKE2B_KUNIT_TEST=y
 CONFIG_CRYPTO_LIB_BLAKE2S_KUNIT_TEST=y
 CONFIG_CRYPTO_LIB_CHACHA20POLY1305_KUNIT_TEST=y
diff --git a/lib/crypto/tests/Kconfig b/lib/crypto/tests/Kconfig
index bc084dde424f..320236e996b1 100644
--- a/lib/crypto/tests/Kconfig
+++ b/lib/crypto/tests/Kconfig
@@ -9,6 +9,15 @@ 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_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 a739413500b6..085852ba008b 100644
--- a/lib/crypto/tests/Makefile
+++ b/lib/crypto/tests/Makefile
@@ -1,6 +1,7 @@
 # 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_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/aes_ccm_kunit.c b/lib/crypto/tests/aes_ccm_kunit.c
new file mode 100644
index 000000000000..b39df319450d
--- /dev/null
+++ b/lib/crypto/tests/aes_ccm_kunit.c
@@ -0,0 +1,352 @@
+// 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 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);
+		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/scripts/crypto/gen-aead-testvecs.py b/scripts/crypto/gen-aead-testvecs.py
new file mode 100755
index 000000000000..048043735819
--- /dev/null
+++ b/scripts/crypto/gen-aead-testvecs.py
@@ -0,0 +1,60 @@
+#!/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)
+
+        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"):
+    sys.stderr.write("Usage: gen-aead-testvecs.py [aes-ccm]\n")
+    sys.exit(1)
+
+gen_monte_carlo_checksum(sys.argv[1])
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 5/5] lib/crypto: tests: Add KUnit test suite for AES-GCM
  2026-07-30  1:32 [PATCH 0/5] lib/crypto: KUnit tests for AES-CCM and AES-GCM Eric Biggers
                   ` (3 preceding siblings ...)
  2026-07-30  1:32 ` [PATCH 4/5] lib/crypto: tests: Add KUnit test suite for AES-CCM Eric Biggers
@ 2026-07-30  1:33 ` Eric Biggers
  4 siblings, 0 replies; 6+ messages in thread
From: Eric Biggers @ 2026-07-30  1:33 UTC (permalink / raw)
  To: linux-crypto
  Cc: linux-kernel, Ard Biesheuvel, Jason A . Donenfeld, Herbert Xu,
	Thomas Huth, Eric Biggers

Add a KUnit test suite for the AES-GCM library API.

It consists of:

- All the shared test cases from aead-test-template.h.  These include
  extensive consistency tests, a "Monte-Carlo test", and a benchmark.

- Tests against hardcoded AES-GCM test vectors from external sources.

Signed-off-by: Eric Biggers <ebiggers@kernel.org>
---
 lib/crypto/.kunitconfig             |   1 +
 lib/crypto/tests/Kconfig            |   9 +
 lib/crypto/tests/Makefile           |   1 +
 lib/crypto/tests/aes_gcm_kunit.c    | 472 ++++++++++++++++++++++++++++
 scripts/crypto/gen-aead-testvecs.py |  13 +-
 5 files changed, 494 insertions(+), 2 deletions(-)
 create mode 100644 lib/crypto/tests/aes_gcm_kunit.c

diff --git a/lib/crypto/.kunitconfig b/lib/crypto/.kunitconfig
index f0237e602f28..60e0a77f9889 100644
--- a/lib/crypto/.kunitconfig
+++ b/lib/crypto/.kunitconfig
@@ -4,6 +4,7 @@ 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/tests/Kconfig b/lib/crypto/tests/Kconfig
index 320236e996b1..e121114624da 100644
--- a/lib/crypto/tests/Kconfig
+++ b/lib/crypto/tests/Kconfig
@@ -18,6 +18,15 @@ config CRYPTO_LIB_AES_CCM_KUNIT_TEST
 	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 085852ba008b..c97c1d78784a 100644
--- a/lib/crypto/tests/Makefile
+++ b/lib/crypto/tests/Makefile
@@ -2,6 +2,7 @@
 
 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/aes_gcm_kunit.c b/lib/crypto/tests/aes_gcm_kunit.c
new file mode 100644
index 000000000000..6959d86b316a
--- /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/scripts/crypto/gen-aead-testvecs.py b/scripts/crypto/gen-aead-testvecs.py
index 048043735819..d77d646f75b5 100755
--- a/scripts/crypto/gen-aead-testvecs.py
+++ b/scripts/crypto/gen-aead-testvecs.py
@@ -41,6 +41,15 @@ def gen_monte_carlo_checksum(alg):
                 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)
 
@@ -53,8 +62,8 @@ def gen_monte_carlo_checksum(alg):
     print("};")
 
 
-if len(sys.argv) != 2 or sys.argv[1] not in ("aes-ccm"):
-    sys.stderr.write("Usage: gen-aead-testvecs.py [aes-ccm]\n")
+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])
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-07-30  1:35 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-30  1:32 [PATCH 0/5] lib/crypto: KUnit tests for AES-CCM and AES-GCM Eric Biggers
2026-07-30  1:32 ` [PATCH 1/5] lib/crypto: tests: Create test-utils.h Eric Biggers
2026-07-30  1:32 ` [PATCH 2/5] lib/crypto: tests: Use per-test-case buffers in hash tests Eric Biggers
2026-07-30  1:32 ` [PATCH 3/5] lib/crypto: tests: Add aead-test-template.h Eric Biggers
2026-07-30  1:32 ` [PATCH 4/5] lib/crypto: tests: Add KUnit test suite for AES-CCM Eric Biggers
2026-07-30  1:33 ` [PATCH 5/5] lib/crypto: tests: Add KUnit test suite for AES-GCM Eric Biggers

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.