Skip to contentSkip to conversation

KUnit tests for SHA-2 and Poly1305

PATCH In review Eric Biggers 2 participants 6 messages Jul 9, 2025 – Jul 14, 2025

Subsystems (2)
lore
Share & tools
lore

Eric Biggers

author maintainer
#0 lore raw

v5 cover KUnit tests for SHA-2 and Poly1305

This series is also available at:

git fetch https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux.git libcrypto-kunit-v5

This series adds the first KUnit tests for lib/crypto/, including tests for SHA-2 and Poly1305.

Changed in v5:
- Moved hash_irq_test2_state from stack to heap
(fixes https://lore.kernel.org/r/[email protected])
- Replaced HRTIMER_MODE_REL with HRTIMER_MODE_REL_HARD to make hardirq context be used on CONFIG_PREEMPT_RT (fixes a WARN_ON_ONCE).
- Removed an unnecessary initialization in hash_irq_test2_func()
- Added comments that explain kconfig option naming
- Added link to Poly1305 paper

Changed in v4:
- Added Poly1305 tests.
- Split the addition of hash-test-template.h and gen-hash-testvecs.py into a separate patch.
- Added two more test cases to hash-test-template.h (test_hash_all_lens_up_to_4096 and test_hash_interrupt_context_2).
- Simplified test_hmac to use a single consolidated test vector.
- Lots of other cleanups.

Changed in v3:
- Split from SHA-512 and SHA-256 series, as per the request from Linus that the tests be kept last on the branch.

Eric Biggers (4):
lib/crypto: tests: Add hash-test-template.h and gen-hash-testvecs.py lib/crypto: tests: Add KUnit tests for SHA-224 and SHA-256 lib/crypto: tests: Add KUnit tests for SHA-384 and SHA-512 lib/crypto: tests: Add KUnit tests for Poly1305

 lib/crypto/Kconfig                    |   2 +
 lib/crypto/Makefile                   |   2 +
 lib/crypto/tests/Kconfig              |  50 ++
 lib/crypto/tests/Makefile             |   5 +
 lib/crypto/tests/hash-test-template.h | 683 ++++++++++++++++++++++++++
 lib/crypto/tests/poly1305-testvecs.h  | 186 +++++++
 lib/crypto/tests/poly1305_kunit.c     | 165 +++++++
 lib/crypto/tests/sha224-testvecs.h    | 238 +++++++++
 lib/crypto/tests/sha224_kunit.c       |  39 ++
 lib/crypto/tests/sha256-testvecs.h    | 238 +++++++++
 lib/crypto/tests/sha256_kunit.c       |  39 ++
 lib/crypto/tests/sha384-testvecs.h    | 290 +++++++++++
 lib/crypto/tests/sha384_kunit.c       |  39 ++
 lib/crypto/tests/sha512-testvecs.h    | 342 +++++++++++++
 lib/crypto/tests/sha512_kunit.c       |  39 ++
 scripts/crypto/gen-hash-testvecs.py   | 147 ++++++
 16 files changed, 2504 insertions(+)
 create mode 100644 lib/crypto/tests/Kconfig
 create mode 100644 lib/crypto/tests/Makefile
 create mode 100644 lib/crypto/tests/hash-test-template.h
 create mode 100644 lib/crypto/tests/poly1305-testvecs.h
 create mode 100644 lib/crypto/tests/poly1305_kunit.c
 create mode 100644 lib/crypto/tests/sha224-testvecs.h
 create mode 100644 lib/crypto/tests/sha224_kunit.c
 create mode 100644 lib/crypto/tests/sha256-testvecs.h
 create mode 100644 lib/crypto/tests/sha256_kunit.c
 create mode 100644 lib/crypto/tests/sha384-testvecs.h
 create mode 100644 lib/crypto/tests/sha384_kunit.c
 create mode 100644 lib/crypto/tests/sha512-testvecs.h
 create mode 100644 lib/crypto/tests/sha512_kunit.c
 create mode 100755 scripts/crypto/gen-hash-testvecs.py
Signature · 1 line
2.50.1
author maintainer
#1 lore raw

PATCH v5 1/4 lib/crypto: tests: Add hash-test-template.h and gen-hash-testvecs.py

Add hash-test-template.h which generates the following KUnit test cases for hash functions:

test_hash_test_vectors test_hash_all_lens_up_to_4096 test_hash_incremental_updates test_hash_buffer_overruns test_hash_overlaps test_hash_alignment_consistency test_hash_ctx_zeroization test_hash_interrupt_context_1 test_hash_interrupt_context_2
test_hmac (when HMAC is supported) benchmark_hash (when CONFIG_CRYPTO_LIB_BENCHMARK=y)

The initial use cases for this will be sha224_kunit, sha256_kunit, sha384_kunit, sha512_kunit, and poly1305_kunit.

Add a Python script gen-hash-testvecs.py which generates the test vectors required by test_hash_test_vectors, test_hash_all_lens_up_to_4096, and test_hmac.

---
 lib/crypto/tests/hash-test-template.h | 683 ++++++++++++++++++++++++++
 scripts/crypto/gen-hash-testvecs.py   | 102 ++++
 2 files changed, 785 insertions(+)
 create mode 100644 lib/crypto/tests/hash-test-template.h
 create mode 100755 scripts/crypto/gen-hash-testvecs.py
lib/crypto/tests/hash-test-template.h new file +683 -0
new file mode 100644
index 0000000000000..ffee1741a1b36
@@ -0,0 +1,683 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Test cases for hash functions, including a benchmark.  This is included by
+ * KUnit test suites that want to use it.  See sha512_kunit.c for an example.
+ *
+ * Copyright 2025 Google LLC
+ */
+#include <kunit/test.h>
+#include <linux/hrtimer.h>
+#include <linux/timekeeping.h>
+#include <linux/vmalloc.h>
+#include <linux/workqueue.h>
+
+/* test_buf is a guarded buffer, i.e. &test_buf[TEST_BUF_LEN] is not mapped. */
+#define TEST_BUF_LEN 16384
+static u8 *test_buf;
+
+static u8 *orig_test_buf;
+
+static u64 random_seed;
+
+/*
+ * This is a simple linear congruential generator.  It is used only for testing,
+ * which does not require cryptographically secure random numbers.  A hard-coded
+ * algorithm is used instead of <linux/prandom.h> so that it matches the
+ * algorithm used by the test vector generation script.  This allows the input
+ * data in random test vectors to be concisely stored as just the seed.
+ */
+static u32 rand32(void)
+{
+	random_seed = (random_seed * 25214903917 + 11) & ((1ULL << 48) - 1);
+	return random_seed >> 16;
+}
+
+static void rand_bytes(u8 *out, size_t len)
+{
+	for (size_t i = 0; i < len; i++)
+		out[i] = rand32();
+}
+
+static void rand_bytes_seeded_from_len(u8 *out, size_t len)
+{
+	random_seed = len;
+	rand_bytes(out, len);
+}
+
+static bool rand_bool(void)
+{
+	return rand32() % 2;
+}
+
+/* Generate a random length, preferring small lengths. */
+static size_t rand_length(size_t max_len)
+{
+	size_t len;
+
+	switch (rand32() % 3) {
+	case 0:
+		len = rand32() % 128;
+		break;
+	case 1:
+		len = rand32() % 3072;
+		break;
+	default:
+		len = rand32();
+		break;
+	}
+	return len % (max_len + 1);
+}
+
+static size_t rand_offset(size_t max_offset)
+{
+	return min(rand32() % 128, max_offset);
+}
+
+static int hash_suite_init(struct kunit_suite *suite)
+{
+	/*
+	 * Allocate the test buffer using vmalloc() with a page-aligned length
+	 * so that it is immediately followed by a guard page.  This allows
+	 * buffer overreads to be detected, even in assembly code.
+	 */
+	size_t alloc_len = round_up(TEST_BUF_LEN, PAGE_SIZE);
+
+	orig_test_buf = vmalloc(alloc_len);
+	if (!orig_test_buf)
+		return -ENOMEM;
+
+	test_buf = orig_test_buf + alloc_len - TEST_BUF_LEN;
+	return 0;
+}
+
+static void hash_suite_exit(struct kunit_suite *suite)
+{
+	vfree(orig_test_buf);
+	orig_test_buf = NULL;
+	test_buf = NULL;
+}
+
+/*
+ * Test the hash function against a list of test vectors.
+ *
+ * Note that it's only necessary to run each test vector in one way (e.g.,
+ * one-shot instead of incremental), since consistency between different ways of
+ * using the APIs is verified by other test cases.
+ */
+static void test_hash_test_vectors(struct kunit *test)
+{
+	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_MEMEQ_MSG(
+			test, actual_hash, hash_testvecs[i].digest, HASH_SIZE,
+			"Wrong result with test vector %zu; data_len=%zu", i,
+			data_len);
+	}
+}
+
+/*
+ * Test that the hash function produces correct results for *every* length up to
+ * 4096 bytes.  To do this, generate seeded random data, then calculate a hash
+ * value for each length 0..4096, then hash the hash values.  Verify just the
+ * final hash value, which should match only when all hash values were correct.
+ */
+static void test_hash_all_lens_up_to_4096(struct kunit *test)
+{
+	struct HASH_CTX ctx;
+	u8 hash[HASH_SIZE];
+
+	static_assert(TEST_BUF_LEN >= 4096);
+	rand_bytes_seeded_from_len(test_buf, 4096);
+	HASH_INIT(&ctx);
+	for (size_t len = 0; len <= 4096; len++) {
+		HASH(test_buf, len, hash);
+		HASH_UPDATE(&ctx, hash, HASH_SIZE);
+	}
+	HASH_FINAL(&ctx, hash);
+	KUNIT_ASSERT_MEMEQ(test, hash, hash_testvec_consolidated, HASH_SIZE);
+}
+
+/*
+ * Test that the hash function produces the same result with a one-shot
+ * computation as it does with an incremental computation.
+ */
+static void test_hash_incremental_updates(struct kunit *test)
+{
+	for (int i = 0; i < 1000; i++) {
+		size_t total_len, offset;
+		struct HASH_CTX ctx;
+		u8 hash1[HASH_SIZE];
+		u8 hash2[HASH_SIZE];
+		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);
+
+		/* Compute the hash value in one shot. */
+		HASH(&test_buf[offset], total_len, hash1);
+
+		/*
+		 * Compute the hash value incrementally, using a randomly
+		 * selected sequence of update lengths that sum to total_len.
+		 */
+		HASH_INIT(&ctx);
+		remaining_len = total_len;
+		cur_offset = offset;
+		while (rand_bool()) {
+			size_t part_len = rand_length(remaining_len);
+
+			HASH_UPDATE(&ctx, &test_buf[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);
+			num_parts++;
+		}
+		HASH_FINAL(&ctx, hash2);
+
+		/* Verify that the two hash values are the same. */
+		KUNIT_ASSERT_MEMEQ_MSG(
+			test, hash1, hash2, HASH_SIZE,
+			"Incremental test failed with total_len=%zu num_parts=%zu offset=%zu",
+			total_len, num_parts, offset);
+	}
+}
+
+/*
+ * Test that the hash function does not overrun any buffers.  Uses a guard page
+ * to catch buffer overruns even if they occur in assembly code.
+ */
+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];
+	struct HASH_CTX *guarded_ctx = buf_end - sizeof(*guarded_ctx);
+
+	rand_bytes(test_buf, TEST_BUF_LEN);
+
+	for (int i = 0; i < 100; i++) {
+		size_t len = rand_length(max_tested_len);
+		struct HASH_CTX ctx;
+		u8 hash[HASH_SIZE];
+
+		/* Check for overruns of the data buffer. */
+		HASH(buf_end - len, len, hash);
+		HASH_INIT(&ctx);
+		HASH_UPDATE(&ctx, buf_end - len, len);
+		HASH_FINAL(&ctx, hash);
+
+		/* Check for overruns of the hash value buffer. */
+		HASH(test_buf, len, buf_end - HASH_SIZE);
+		HASH_INIT(&ctx);
+		HASH_UPDATE(&ctx, test_buf, len);
+		HASH_FINAL(&ctx, buf_end - HASH_SIZE);
+
+		/* Check for overuns of the hash context. */
+		HASH_INIT(guarded_ctx);
+		HASH_UPDATE(guarded_ctx, test_buf, len);
+		HASH_FINAL(guarded_ctx, hash);
+	}
+}
+
+/*
+ * Test that the caller is permitted to alias the output digest and source data
+ * buffer, and also modify the source data buffer after it has been used.
+ */
+static void test_hash_overlaps(struct kunit *test)
+{
+	const size_t max_tested_len = TEST_BUF_LEN - HASH_SIZE;
+	struct HASH_CTX ctx;
+	u8 hash[HASH_SIZE];
+
+	rand_bytes(test_buf, TEST_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];
+
+		HASH(&test_buf[offset], len, hash);
+		HASH(&test_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_INIT(&ctx);
+		HASH_UPDATE(&ctx, &test_buf[offset], len);
+		HASH_FINAL(&ctx, ovl_hash);
+		KUNIT_ASSERT_MEMEQ_MSG(
+			test, hash, ovl_hash, HASH_SIZE,
+			"Overlap test 2 failed with len=%zu offset=%zu left_end=%d",
+			len, offset, left_end);
+
+		/* Test modifying the source data after it was used. */
+		HASH(&test_buf[offset], len, hash);
+		HASH_INIT(&ctx);
+		HASH_UPDATE(&ctx, &test_buf[offset], len);
+		rand_bytes(&test_buf[offset], len);
+		HASH_FINAL(&ctx, ovl_hash);
+		KUNIT_ASSERT_MEMEQ_MSG(
+			test, hash, ovl_hash, HASH_SIZE,
+			"Overlap test 3 failed with len=%zu offset=%zu left_end=%d",
+			len, offset, left_end);
+	}
+}
+
+/*
+ * Test that if the same data is hashed at different alignments in memory, the
+ * results are the same.
+ */
+static void test_hash_alignment_consistency(struct kunit *test)
+{
+	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 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]);
+		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)",
+			len, data_offs1, data_offs2, hash_offs1, hash_offs2);
+	}
+}
+
+/* Test that HASH_FINAL zeroizes the context. */
+static void test_hash_ctx_zeroization(struct kunit *test)
+{
+	static const u8 zeroes[sizeof(struct HASH_CTX)];
+	struct HASH_CTX ctx;
+
+	rand_bytes(test_buf, 128);
+	HASH_INIT(&ctx);
+	HASH_UPDATE(&ctx, test_buf, 128);
+	HASH_FINAL(&ctx, test_buf);
+	KUNIT_ASSERT_MEMEQ_MSG(test, &ctx, zeroes, sizeof(ctx),
+			       "Hash context was not zeroized by finalization");
+}
+
+#define IRQ_TEST_HRTIMER_INTERVAL us_to_ktime(5)
+
+struct hash_irq_test_state {
+	bool (*func)(void *test_specific_state);
+	void *test_specific_state;
+	bool task_func_reported_failure;
+	bool hardirq_func_reported_failure;
+	bool softirq_func_reported_failure;
+	unsigned long hardirq_func_calls;
+	unsigned long softirq_func_calls;
+	struct hrtimer timer;
+	struct work_struct bh_work;
+};
+
+static enum hrtimer_restart hash_irq_test_timer_func(struct hrtimer *timer)
+{
+	struct hash_irq_test_state *state =
+		container_of(timer, typeof(*state), timer);
+
+	WARN_ON_ONCE(!in_hardirq());
+	state->hardirq_func_calls++;
+
+	if (!state->func(state->test_specific_state))
+		state->hardirq_func_reported_failure = true;
+
+	hrtimer_forward_now(&state->timer, IRQ_TEST_HRTIMER_INTERVAL);
+	queue_work(system_bh_wq, &state->bh_work);
+	return HRTIMER_RESTART;
+}
+
+static void hash_irq_test_bh_work_func(struct work_struct *work)
+{
+	struct hash_irq_test_state *state =
+		container_of(work, typeof(*state), bh_work);
+
+	WARN_ON_ONCE(!in_serving_softirq());
+	state->softirq_func_calls++;
+
+	if (!state->func(state->test_specific_state))
+		state->softirq_func_reported_failure = true;
+}
+
+/*
+ * Helper function which repeatedly runs the given @func in task, softirq, and
+ * hardirq context concurrently, and reports a failure to KUnit if any
+ * invocation of @func in any context returns false.  @func is passed
+ * @test_specific_state as its argument.  At most 3 invocations of @func will
+ * run concurrently: one in each of task, softirq, and hardirq context.
+ *
+ * The main purpose of this interrupt context testing is to validate fallback
+ * code paths that run in contexts where the normal code path cannot be used,
+ * typically due to the FPU or vector registers already being in-use in kernel
+ * mode.  These code paths aren't covered when the test code is executed only by
+ * the KUnit test runner thread in task context.  The reason for the concurrency
+ * is because merely using hardirq context is not sufficient to reach a fallback
+ * code path on some architectures; the hardirq actually has to occur while the
+ * FPU or vector unit was already in-use in kernel mode.
+ *
+ * Another purpose of this testing is to detect issues with the architecture's
+ * irq_fpu_usable() and kernel_fpu_begin/end() or equivalent functions,
+ * especially in softirq context when the softirq may have interrupted a task
+ * already using kernel-mode FPU or vector (if the arch didn't prevent that).
+ * Crypto functions are often executed in softirqs, so this is important.
+ */
+static void run_irq_test(struct kunit *test, bool (*func)(void *),
+			 int max_iterations, void *test_specific_state)
+{
+	struct hash_irq_test_state state = {
+		.func = func,
+		.test_specific_state = test_specific_state,
+	};
+	unsigned long end_jiffies;
+
+	/*
+	 * Set up a hrtimer (the way we access hardirq context) and a work
+	 * struct for the BH workqueue (the way we access softirq context).
+	 */
+	hrtimer_setup_on_stack(&state.timer, hash_irq_test_timer_func,
+			       CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
+	INIT_WORK(&state.bh_work, hash_irq_test_bh_work_func);
+
+	/* Run for up to max_iterations or 1 second, whichever comes first. */
+	end_jiffies = jiffies + HZ;
+	hrtimer_start(&state.timer, IRQ_TEST_HRTIMER_INTERVAL,
+		      HRTIMER_MODE_REL_HARD);
+	for (int i = 0; i < max_iterations && !time_after(jiffies, end_jiffies);
+	     i++) {
+		if (!func(test_specific_state))
+			state.task_func_reported_failure = true;
+	}
+
+	/* Cancel the timer and work. */
+	hrtimer_cancel(&state.timer);
+	flush_work(&state.bh_work);
+
+	/* Sanity check: the timer and BH functions should have been run. */
+	KUNIT_EXPECT_GT_MSG(test, state.hardirq_func_calls, 0,
+			    "Timer function was not called");
+	KUNIT_EXPECT_GT_MSG(test, state.softirq_func_calls, 0,
+			    "BH work function was not called");
+
+	/* Check for incorrect hash values reported from any context. */
+	KUNIT_EXPECT_FALSE_MSG(
+		test, state.task_func_reported_failure,
+		"Incorrect hash values reported from task context");
+	KUNIT_EXPECT_FALSE_MSG(
+		test, state.hardirq_func_reported_failure,
+		"Incorrect hash values reported from hardirq context");
+	KUNIT_EXPECT_FALSE_MSG(
+		test, state.softirq_func_reported_failure,
+		"Incorrect hash values reported from softirq context");
+}
+
+#define IRQ_TEST_DATA_LEN 256
+#define IRQ_TEST_NUM_BUFFERS 3 /* matches max concurrency level */
+
+struct hash_irq_test1_state {
+	u8 expected_hashes[IRQ_TEST_NUM_BUFFERS][HASH_SIZE];
+	atomic_t seqno;
+};
+
+/*
+ * Compute the hash of one of the test messages and verify that it matches the
+ * expected hash from @state->expected_hashes.  To increase the chance of
+ * detecting problems, cycle through multiple messages.
+ */
+static bool hash_irq_test1_func(void *state_)
+{
+	struct hash_irq_test1_state *state = 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);
+	return memcmp(actual_hash, state->expected_hashes[i], HASH_SIZE) == 0;
+}
+
+/*
+ * Test that if hashes are computed in task, softirq, and hardirq context
+ * concurrently, then all results are as expected.
+ */
+static void test_hash_interrupt_context_1(struct kunit *test)
+{
+	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);
+	for (int i = 0; i < IRQ_TEST_NUM_BUFFERS; i++)
+		HASH(&test_buf[i * IRQ_TEST_DATA_LEN], IRQ_TEST_DATA_LEN,
+		     state.expected_hashes[i]);
+
+	run_irq_test(test, hash_irq_test1_func, 100000, &state);
+}
+
+struct hash_irq_test2_hash_ctx {
+	struct HASH_CTX hash_ctx;
+	atomic_t in_use;
+	int offset;
+	int step;
+};
+
+struct hash_irq_test2_state {
+	struct hash_irq_test2_hash_ctx ctxs[IRQ_TEST_NUM_BUFFERS];
+	u8 expected_hash[HASH_SIZE];
+	u16 update_lens[32];
+	int num_steps;
+};
+
+static bool hash_irq_test2_func(void *state_)
+{
+	struct hash_irq_test2_state *state = state_;
+	struct hash_irq_test2_hash_ctx *ctx;
+	bool ret = true;
+
+	for (ctx = &state->ctxs[0]; ctx < &state->ctxs[ARRAY_SIZE(state->ctxs)];
+	     ctx++) {
+		if (atomic_cmpxchg(&ctx->in_use, 0, 1) == 0)
+			break;
+	}
+	if (WARN_ON_ONCE(ctx == &state->ctxs[ARRAY_SIZE(state->ctxs)])) {
+		/*
+		 * This should never happen, as the number of contexts is equal
+		 * to the maximum concurrency level of run_irq_test().
+		 */
+		return false;
+	}
+
+	if (ctx->step == 0) {
+		/* Init step */
+		HASH_INIT(&ctx->hash_ctx);
+		ctx->offset = 0;
+		ctx->step++;
+	} else if (ctx->step < state->num_steps - 1) {
+		/* Update step */
+		HASH_UPDATE(&ctx->hash_ctx, &test_buf[ctx->offset],
+			    state->update_lens[ctx->step - 1]);
+		ctx->offset += state->update_lens[ctx->step - 1];
+		ctx->step++;
+	} else {
+		/* Final step */
+		u8 actual_hash[HASH_SIZE];
+
+		if (WARN_ON_ONCE(ctx->offset != TEST_BUF_LEN))
+			ret = false;
+		HASH_FINAL(&ctx->hash_ctx, actual_hash);
+		if (memcmp(actual_hash, state->expected_hash, HASH_SIZE) != 0)
+			ret = false;
+		ctx->step = 0;
+	}
+	atomic_set_release(&ctx->in_use, 0);
+	return ret;
+}
+
+/*
+ * Test that if hashes are computed in task, softirq, and hardirq context
+ * concurrently, *including doing different parts of the same incremental
+ * computation in different contexts*, then all results are as expected.
+ * Besides detecting bugs similar to those that test_hash_interrupt_context_1
+ * can detect, this test case can also detect bugs where hash function
+ * implementations don't correctly handle these mixed incremental computations.
+ */
+static void test_hash_interrupt_context_2(struct kunit *test)
+{
+	struct hash_irq_test2_state *state;
+	int remaining = TEST_BUF_LEN;
+
+	state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, state);
+
+	rand_bytes(test_buf, TEST_BUF_LEN);
+	HASH(test_buf, TEST_BUF_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);
+	for (state->num_steps = 0;
+	     state->num_steps < ARRAY_SIZE(state->update_lens) - 1 && remaining;
+	     state->num_steps++) {
+		state->update_lens[state->num_steps] =
+			rand_length(min(remaining, 4096));
+		remaining -= state->update_lens[state->num_steps];
+	}
+	if (remaining)
+		state->update_lens[state->num_steps++] = remaining;
+	state->num_steps += 2; /* for init and final */
+
+	run_irq_test(test, hash_irq_test2_func, 250000, state);
+}
+
+#define UNKEYED_HASH_KUNIT_CASES                     \
+	KUNIT_CASE(test_hash_test_vectors),          \
+	KUNIT_CASE(test_hash_all_lens_up_to_4096),   \
+	KUNIT_CASE(test_hash_incremental_updates),   \
+	KUNIT_CASE(test_hash_buffer_overruns),       \
+	KUNIT_CASE(test_hash_overlaps),              \
+	KUNIT_CASE(test_hash_alignment_consistency), \
+	KUNIT_CASE(test_hash_ctx_zeroization),       \
+	KUNIT_CASE(test_hash_interrupt_context_1),   \
+	KUNIT_CASE(test_hash_interrupt_context_2)
+/* benchmark_hash is omitted so that the suites can put it last. */
+
+#ifdef HMAC
+/*
+ * Test the corresponding HMAC variant.
+ *
+ * This test case is fairly short, since HMAC is just a simple C wrapper around
+ * the underlying unkeyed hash function, which is already well-tested by the
+ * other test cases.  It's not useful to test things like data alignment or
+ * interrupt context again for HMAC, nor to have a long list of test vectors.
+ *
+ * Thus, just do a single consolidated test, which covers all data lengths up to
+ * 4096 bytes and all key lengths up to 292 bytes.  For each data length, select
+ * a key length, generate the inputs from a seed, and compute the HMAC value.
+ * Concatenate all these HMAC values together, and compute the HMAC of that.
+ * Verify that value.  If this fails, then the HMAC implementation is wrong.
+ * This won't show which specific input failed, but that should be fine.  Any
+ * failure would likely be non-input-specific or also show in the unkeyed tests.
+ */
+static void test_hmac(struct kunit *test)
+{
+	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(raw_key, 32);
+	HMAC_PREPAREKEY(&key, raw_key, 32);
+	HMAC_INIT(&ctx, &key);
+	for (size_t data_len = 0; data_len <= 4096; data_len++) {
+		/*
+		 * Cycle through key lengths as well.  Somewhat arbitrarily go
+		 * up to 293, which is somewhat larger than the largest hash
+		 * block size (which is the size at which the key starts being
+		 * 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;
+
+		HMAC_UPDATE(&ctx, test_buf, data_len);
+
+		rand_bytes_seeded_from_len(raw_key, key_len);
+		HMAC_USINGRAWKEY(raw_key, key_len, test_buf, 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);
+		KUNIT_ASSERT_MEMEQ_MSG(
+			test, mac, mac2, HASH_SIZE,
+			"HMAC gave different results with raw and prepared keys");
+	}
+	HMAC_FINAL(&ctx, mac);
+	KUNIT_EXPECT_MEMEQ_MSG(test, mac, hmac_testvec_consolidated, HASH_SIZE,
+			       "HMAC gave wrong result");
+	KUNIT_EXPECT_MEMEQ_MSG(test, &ctx, zeroes, sizeof(ctx),
+			       "HMAC context was not zeroized by finalization");
+}
+#define HASH_KUNIT_CASES UNKEYED_HASH_KUNIT_CASES, KUNIT_CASE(test_hmac)
+#else
+#define HASH_KUNIT_CASES UNKEYED_HASH_KUNIT_CASES
+#endif
+
+/* Benchmark the hash function on various data lengths. */
+static void benchmark_hash(struct kunit *test)
+{
+	static const size_t lens_to_test[] = {
+		1,   16,  64,	127,  128,  200,   256,
+		511, 512, 1024, 3173, 4096, 16384,
+	};
+	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);
+
+	for (size_t i = 0; i < ARRAY_SIZE(lens_to_test); i++) {
+		size_t len = lens_to_test[i];
+		/* The '+ 128' tries to account for per-message overhead. */
+		size_t num_iters = 10000000 / (len + 128);
+		u64 t;
+
+		KUNIT_ASSERT_LE(test, len, TEST_BUF_LEN);
+		preempt_disable();
+		t = ktime_get_ns();
+		for (size_t j = 0; j < num_iters; j++)
+			HASH(test_buf, len, hash);
+		t = ktime_get_ns() - t;
+		preempt_enable();
+		kunit_info(test, "len=%zu: %llu MB/s", len,
+			   div64_u64((u64)len * num_iters * 1000, t ?: 1));
+	}
+}
scripts/crypto/gen-hash-testvecs.py new file +102 -0

102 lines not on this page · raw message

Signature · 1 line
2.50.1
author maintainer
#2 lore raw

PATCH v5 2/4 lib/crypto: tests: Add KUnit tests for SHA-224 and SHA-256

In reply to Eric Biggers

Add KUnit test suites for the SHA-224 and SHA-256 library functions, including the corresponding HMAC support. The core test logic is in the previously-added hash-test-template.h. This commit just adds the actual KUnit suites, and it adds the generated test vectors to the tree so that gen-hash-testvecs.py won't have to be run at build time.

---
 lib/crypto/Kconfig                 |   2 +
 lib/crypto/Makefile                |   2 +
 lib/crypto/tests/Kconfig           |  29 ++++
 lib/crypto/tests/Makefile          |   3 +
 lib/crypto/tests/sha224-testvecs.h | 238 +++++++++++++++++++++++++++++
 lib/crypto/tests/sha224_kunit.c    |  39 +++++
 lib/crypto/tests/sha256-testvecs.h | 238 +++++++++++++++++++++++++++++
 lib/crypto/tests/sha256_kunit.c    |  39 +++++
 8 files changed, 590 insertions(+)
 create mode 100644 lib/crypto/tests/Kconfig
 create mode 100644 lib/crypto/tests/Makefile
 create mode 100644 lib/crypto/tests/sha224-testvecs.h
 create mode 100644 lib/crypto/tests/sha224_kunit.c
 create mode 100644 lib/crypto/tests/sha256-testvecs.h
 create mode 100644 lib/crypto/tests/sha256_kunit.c
lib/crypto/Kconfig +2 -0
index ee7e1e27ea6a9..c2a28a50ac576 100644
@@ -178,10 +178,12 @@ config CRYPTO_LIB_SHA512_ARCH
 	default y if X86_64
 
 config CRYPTO_LIB_SM3
 	tristate
 
+source "lib/crypto/tests/Kconfig"
+
 if !KMSAN # avoid false positives from assembly
 if ARM
 source "lib/crypto/arm/Kconfig"
 endif
 if ARM64
lib/crypto/Makefile +2 -0
index 533bb1533e19b..8aaec39b85608 100644
@@ -6,10 +6,12 @@ quiet_cmd_perlasm = PERLASM $@
       cmd_perlasm = $(PERL) $(<) > $(@)
 
 quiet_cmd_perlasm_with_args = PERLASM $@
       cmd_perlasm_with_args = $(PERL) $(<) void $(@)
 
+obj-$(CONFIG_KUNIT)				+= tests/
+
 obj-$(CONFIG_CRYPTO_HASH_INFO)			+= hash_info.o
 
 obj-$(CONFIG_CRYPTO_LIB_UTILS)			+= libcryptoutils.o
 libcryptoutils-y				:= memneq.o utils.o
 
lib/crypto/tests/Kconfig new file +29 -0
new file mode 100644
index 0000000000000..455d05f61ac3e
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# Option is named *_SHA256_KUNIT_TEST, though both SHA-224 and SHA-256 tests are
+# included, for consistency with the naming used elsewhere (e.g. CRYPTO_SHA256).
+config CRYPTO_LIB_SHA256_KUNIT_TEST
+	tristate "KUnit tests for SHA-224 and SHA-256" if !KUNIT_ALL_TESTS
+	depends on KUNIT
+	default KUNIT_ALL_TESTS || CRYPTO_SELFTESTS
+	select CRYPTO_LIB_BENCHMARK_VISIBLE
+	select CRYPTO_LIB_SHA256
+	help
+	  KUnit tests for the SHA-224 and SHA-256 cryptographic hash functions
+	  and their corresponding HMACs.
+
+config CRYPTO_LIB_BENCHMARK_VISIBLE
+	bool
+
+config CRYPTO_LIB_BENCHMARK
+	bool "Include benchmarks in KUnit tests for cryptographic functions"
+	depends on CRYPTO_LIB_BENCHMARK_VISIBLE
+	help
+	  Include benchmarks in the KUnit tests for cryptographic functions.
+	  The benchmark results are printed to the kernel log when the
+	  corresponding KUnit test suite runs.
+
+	  This is useful for evaluating the performance of the cryptographic
+	  functions.  However, it will increase the runtime of the KUnit tests.
+
+	  If you're only interested in correctness testing, leave this disabled.
lib/crypto/tests/Makefile new file +3 -0
new file mode 100644
index 0000000000000..a0e5e117dc33e
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+obj-$(CONFIG_CRYPTO_LIB_SHA256_KUNIT_TEST) += sha224_kunit.o sha256_kunit.o
lib/crypto/tests/sha224-testvecs.h new file +238 -0

238 lines not on this page · raw message

lib/crypto/tests/sha224_kunit.c new file +39 -0
new file mode 100644
index 0000000000000..962ad46b9c994
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2025 Google LLC
+ */
+#include <crypto/sha2.h>
+#include "sha224-testvecs.h"
+
+#define HASH sha224
+#define HASH_CTX sha224_ctx
+#define HASH_SIZE SHA224_DIGEST_SIZE
+#define HASH_INIT sha224_init
+#define HASH_UPDATE sha224_update
+#define HASH_FINAL sha224_final
+#define HMAC_KEY hmac_sha224_key
+#define HMAC_CTX hmac_sha224_ctx
+#define HMAC_PREPAREKEY hmac_sha224_preparekey
+#define HMAC_INIT hmac_sha224_init
+#define HMAC_UPDATE hmac_sha224_update
+#define HMAC_FINAL hmac_sha224_final
+#define HMAC hmac_sha224
+#define HMAC_USINGRAWKEY hmac_sha224_usingrawkey
+#include "hash-test-template.h"
+
+static struct kunit_case hash_test_cases[] = {
+	HASH_KUNIT_CASES,
+	KUNIT_CASE(benchmark_hash),
+	{},
+};
+
+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);
+
+MODULE_DESCRIPTION("KUnit tests and benchmark for SHA-224 and HMAC-SHA224");
+MODULE_LICENSE("GPL");
lib/crypto/tests/sha256-testvecs.h new file +238 -0

238 lines not on this page · raw message

lib/crypto/tests/sha256_kunit.c new file +39 -0
new file mode 100644
index 0000000000000..1cd4caee6010d
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2025 Google LLC
+ */
+#include <crypto/sha2.h>
+#include "sha256-testvecs.h"
+
+#define HASH sha256
+#define HASH_CTX sha256_ctx
+#define HASH_SIZE SHA256_DIGEST_SIZE
+#define HASH_INIT sha256_init
+#define HASH_UPDATE sha256_update
+#define HASH_FINAL sha256_final
+#define HMAC_KEY hmac_sha256_key
+#define HMAC_CTX hmac_sha256_ctx
+#define HMAC_PREPAREKEY hmac_sha256_preparekey
+#define HMAC_INIT hmac_sha256_init
+#define HMAC_UPDATE hmac_sha256_update
+#define HMAC_FINAL hmac_sha256_final
+#define HMAC hmac_sha256
+#define HMAC_USINGRAWKEY hmac_sha256_usingrawkey
+#include "hash-test-template.h"
+
+static struct kunit_case hash_test_cases[] = {
+	HASH_KUNIT_CASES,
+	KUNIT_CASE(benchmark_hash),
+	{},
+};
+
+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);
+
+MODULE_DESCRIPTION("KUnit tests and benchmark for SHA-256 and HMAC-SHA256");
+MODULE_LICENSE("GPL");
Signature · 1 line
2.50.1
author maintainer
#3 lore raw

PATCH v5 3/4 lib/crypto: tests: Add KUnit tests for SHA-384 and SHA-512

In reply to Eric Biggers

Add KUnit test suites for the SHA-384 and SHA-512 library functions, including the corresponding HMAC support. The core test logic is in the previously-added hash-test-template.h. This commit just adds the actual KUnit suites, and it adds the generated test vectors to the tree so that gen-hash-testvecs.py won't have to be run at build time.

---
 lib/crypto/tests/Kconfig           |  12 +
 lib/crypto/tests/Makefile          |   1 +
 lib/crypto/tests/sha384-testvecs.h | 290 ++++++++++++++++++++++++
 lib/crypto/tests/sha384_kunit.c    |  39 ++++
 lib/crypto/tests/sha512-testvecs.h | 342 +++++++++++++++++++++++++++++
 lib/crypto/tests/sha512_kunit.c    |  39 ++++
 6 files changed, 723 insertions(+)
 create mode 100644 lib/crypto/tests/sha384-testvecs.h
 create mode 100644 lib/crypto/tests/sha384_kunit.c
 create mode 100644 lib/crypto/tests/sha512-testvecs.h
 create mode 100644 lib/crypto/tests/sha512_kunit.c
lib/crypto/tests/Kconfig +12 -0
index 455d05f61ac3e..5005aeac736d6 100644
@@ -10,10 +10,22 @@ config CRYPTO_LIB_SHA256_KUNIT_TEST
 	select CRYPTO_LIB_SHA256
 	help
 	  KUnit tests for the SHA-224 and SHA-256 cryptographic hash functions
 	  and their corresponding HMACs.
 
+# Option is named *_SHA512_KUNIT_TEST, though both SHA-384 and SHA-512 tests are
+# included, for consistency with the naming used elsewhere (e.g. CRYPTO_SHA512).
+config CRYPTO_LIB_SHA512_KUNIT_TEST
+	tristate "KUnit tests for SHA-384 and SHA-512" if !KUNIT_ALL_TESTS
+	depends on KUNIT
+	default KUNIT_ALL_TESTS || CRYPTO_SELFTESTS
+	select CRYPTO_LIB_BENCHMARK_VISIBLE
+	select CRYPTO_LIB_SHA512
+	help
+	  KUnit tests for the SHA-384 and SHA-512 cryptographic hash functions
+	  and their corresponding HMACs.
+
 config CRYPTO_LIB_BENCHMARK_VISIBLE
 	bool
 
 config CRYPTO_LIB_BENCHMARK
 	bool "Include benchmarks in KUnit tests for cryptographic functions"
lib/crypto/tests/Makefile +1 -0
index a0e5e117dc33e..a963429061dc5 100644
@@ -1,3 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0-or-later
 
 obj-$(CONFIG_CRYPTO_LIB_SHA256_KUNIT_TEST) += sha224_kunit.o sha256_kunit.o
+obj-$(CONFIG_CRYPTO_LIB_SHA512_KUNIT_TEST) += sha384_kunit.o sha512_kunit.o
lib/crypto/tests/sha384-testvecs.h new file +290 -0

290 lines not on this page · raw message

lib/crypto/tests/sha384_kunit.c new file +39 -0
new file mode 100644
index 0000000000000..e1ef5c995bb61
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2025 Google LLC
+ */
+#include <crypto/sha2.h>
+#include "sha384-testvecs.h"
+
+#define HASH sha384
+#define HASH_CTX sha384_ctx
+#define HASH_SIZE SHA384_DIGEST_SIZE
+#define HASH_INIT sha384_init
+#define HASH_UPDATE sha384_update
+#define HASH_FINAL sha384_final
+#define HMAC_KEY hmac_sha384_key
+#define HMAC_CTX hmac_sha384_ctx
+#define HMAC_PREPAREKEY hmac_sha384_preparekey
+#define HMAC_INIT hmac_sha384_init
+#define HMAC_UPDATE hmac_sha384_update
+#define HMAC_FINAL hmac_sha384_final
+#define HMAC hmac_sha384
+#define HMAC_USINGRAWKEY hmac_sha384_usingrawkey
+#include "hash-test-template.h"
+
+static struct kunit_case hash_test_cases[] = {
+	HASH_KUNIT_CASES,
+	KUNIT_CASE(benchmark_hash),
+	{},
+};
+
+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);
+
+MODULE_DESCRIPTION("KUnit tests and benchmark for SHA-384 and HMAC-SHA384");
+MODULE_LICENSE("GPL");
lib/crypto/tests/sha512-testvecs.h new file +342 -0

342 lines not on this page · raw message

lib/crypto/tests/sha512_kunit.c new file +39 -0
new file mode 100644
index 0000000000000..8923e2d7d3d43
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2025 Google LLC
+ */
+#include <crypto/sha2.h>
+#include "sha512-testvecs.h"
+
+#define HASH sha512
+#define HASH_CTX sha512_ctx
+#define HASH_SIZE SHA512_DIGEST_SIZE
+#define HASH_INIT sha512_init
+#define HASH_UPDATE sha512_update
+#define HASH_FINAL sha512_final
+#define HMAC_KEY hmac_sha512_key
+#define HMAC_CTX hmac_sha512_ctx
+#define HMAC_PREPAREKEY hmac_sha512_preparekey
+#define HMAC_INIT hmac_sha512_init
+#define HMAC_UPDATE hmac_sha512_update
+#define HMAC_FINAL hmac_sha512_final
+#define HMAC hmac_sha512
+#define HMAC_USINGRAWKEY hmac_sha512_usingrawkey
+#include "hash-test-template.h"
+
+static struct kunit_case hash_test_cases[] = {
+	HASH_KUNIT_CASES,
+	KUNIT_CASE(benchmark_hash),
+	{},
+};
+
+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);
+
+MODULE_DESCRIPTION("KUnit tests and benchmark for SHA-512 and HMAC-SHA512");
+MODULE_LICENSE("GPL");
Signature · 1 line
2.50.1
author maintainer
#4 lore raw

PATCH v5 4/4 lib/crypto: tests: Add KUnit tests for Poly1305

In reply to Eric Biggers

Add a KUnit test suite for the Poly1305 functions. Most of its test cases are instantiated from hash-test-template.h, which is also used by the SHA-2 tests. A couple additional test cases are also included to test edge cases specific to Poly1305.

---
 lib/crypto/tests/Kconfig             |   9 ++
 lib/crypto/tests/Makefile            |   1 +
 lib/crypto/tests/poly1305-testvecs.h | 186 +++++++++++++++++++++++++++
 lib/crypto/tests/poly1305_kunit.c    | 165 ++++++++++++++++++++++++
 scripts/crypto/gen-hash-testvecs.py  |  49 ++++++-
 5 files changed, 408 insertions(+), 2 deletions(-)
 create mode 100644 lib/crypto/tests/poly1305-testvecs.h
 create mode 100644 lib/crypto/tests/poly1305_kunit.c
lib/crypto/tests/Kconfig +9 -0
index 5005aeac736d6..65f462754419d 100644
@@ -1,7 +1,16 @@
 # SPDX-License-Identifier: GPL-2.0-or-later
 
+config CRYPTO_LIB_POLY1305_KUNIT_TEST
+	tristate "KUnit tests for Poly1305" if !KUNIT_ALL_TESTS
+	depends on KUNIT
+	default KUNIT_ALL_TESTS || CRYPTO_SELFTESTS
+	select CRYPTO_LIB_BENCHMARK_VISIBLE
+	select CRYPTO_LIB_POLY1305
+	help
+	  KUnit tests for the Poly1305 library functions.
+
 # Option is named *_SHA256_KUNIT_TEST, though both SHA-224 and SHA-256 tests are
 # included, for consistency with the naming used elsewhere (e.g. CRYPTO_SHA256).
 config CRYPTO_LIB_SHA256_KUNIT_TEST
 	tristate "KUnit tests for SHA-224 and SHA-256" if !KUNIT_ALL_TESTS
 	depends on KUNIT
lib/crypto/tests/Makefile +1 -0
index a963429061dc5..d33f6d85ecaa8 100644
@@ -1,4 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0-or-later
 
+obj-$(CONFIG_CRYPTO_LIB_POLY1305_KUNIT_TEST) += poly1305_kunit.o
 obj-$(CONFIG_CRYPTO_LIB_SHA256_KUNIT_TEST) += sha224_kunit.o sha256_kunit.o
 obj-$(CONFIG_CRYPTO_LIB_SHA512_KUNIT_TEST) += sha384_kunit.o sha512_kunit.o
lib/crypto/tests/poly1305-testvecs.h new file +186 -0

186 lines not on this page · raw message

lib/crypto/tests/poly1305_kunit.c new file +165 -0

165 lines not on this page · raw message

scripts/crypto/gen-hash-testvecs.py +47 -2

74 lines not on this page · raw message

Signature · 1 line
2.50.1
maintainer Acked-by on the v5 series
#5 lore raw
In reply to Eric Biggers
Quoted 30 lines · Eric Biggers

This series is also available at:

git fetch https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux.git libcrypto-kunit-v5

This series adds the first KUnit tests for lib/crypto/, including tests for SHA-2 and Poly1305.

Changed in v5:
- Moved hash_irq_test2_state from stack to heap
(fixes https://lore.kernel.org/r/[email protected])
- Replaced HRTIMER_MODE_REL with HRTIMER_MODE_REL_HARD to make hardirq context be used on CONFIG_PREEMPT_RT (fixes a WARN_ON_ONCE).
- Removed an unnecessary initialization in hash_irq_test2_func()
- Added comments that explain kconfig option naming
- Added link to Poly1305 paper

Changed in v4:
- Added Poly1305 tests.
- Split the addition of hash-test-template.h and gen-hash-testvecs.py into a separate patch.
- Added two more test cases to hash-test-template.h (test_hash_all_lens_up_to_4096 and test_hash_interrupt_context_2).
- Simplified test_hmac to use a single consolidated test vector.
- Lots of other cleanups.

Changed in v3:
- Split from SHA-512 and SHA-256 series, as per the request from Linus that the tests be kept last on the branch.

Quoted 6 lines

Eric Biggers (4):
lib/crypto: tests: Add hash-test-template.h and gen-hash-testvecs.py lib/crypto: tests: Add KUnit tests for SHA-224 and SHA-256 lib/crypto: tests: Add KUnit tests for SHA-384 and SHA-512 lib/crypto: tests: Add KUnit tests for Poly1305