mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 14:53:58 -04:00
Currently, the standalone GHASH code is coupled with crypto_shash. This has resulted in unnecessary complexity and overhead, as well as the code being unavailable to library code such as the AES-GCM library. Like was done with POLYVAL, it needs to find a new home in lib/crypto/. GHASH and POLYVAL are closely related and can each be implemented in terms of each other. Optimized code for one can be reused with the other. But also since GHASH tends to be difficult to implement directly due to its unnatural bit order, most modern GHASH implementations (including the existing arm, arm64, powerpc, and x86 optimized GHASH code, and the new generic GHASH code I'll be adding) actually reinterpret the GHASH computation as an equivalent POLYVAL computation, pre and post-processing the inputs and outputs to map to/from POLYVAL. Given this close relationship, it makes sense to group the GHASH and POLYVAL code together in the same module. This gives us a wide range of options for implementing them, reusing code between the two and properly utilizing whatever instructions each architecture provides. Thus, GHASH support will be added to the library module that is currently called "polyval". Rename it to an appropriate name: "gf128hash". Rename files, options, functions, etc. where appropriate to reflect the upcoming sharing with GHASH. (Note: polyval_kunit is not renamed, as ghash_kunit will be added alongside it instead.) Acked-by: Ard Biesheuvel <ardb@kernel.org> Link: https://lore.kernel.org/r/20260319061723.1140720-2-ebiggers@kernel.org Signed-off-by: Eric Biggers <ebiggers@kernel.org>
224 lines
6.7 KiB
C
224 lines
6.7 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright 2025 Google LLC
|
|
*/
|
|
#include <crypto/gf128hash.h>
|
|
#include "polyval-testvecs.h"
|
|
|
|
/*
|
|
* A fixed key used when presenting POLYVAL as an unkeyed hash function in order
|
|
* to reuse hash-test-template.h. At the beginning of the test suite, this is
|
|
* initialized to a key prepared from bytes generated from a fixed seed.
|
|
*/
|
|
static struct polyval_key test_key;
|
|
|
|
static void polyval_init_withtestkey(struct polyval_ctx *ctx)
|
|
{
|
|
polyval_init(ctx, &test_key);
|
|
}
|
|
|
|
static void polyval_withtestkey(const u8 *data, size_t len,
|
|
u8 out[POLYVAL_BLOCK_SIZE])
|
|
{
|
|
polyval(&test_key, data, len, out);
|
|
}
|
|
|
|
/* Generate the HASH_KUNIT_CASES using hash-test-template.h. */
|
|
#define HASH polyval_withtestkey
|
|
#define HASH_CTX polyval_ctx
|
|
#define HASH_SIZE POLYVAL_BLOCK_SIZE
|
|
#define HASH_INIT polyval_init_withtestkey
|
|
#define HASH_UPDATE polyval_update
|
|
#define HASH_FINAL polyval_final
|
|
#include "hash-test-template.h"
|
|
|
|
/*
|
|
* Test an example from RFC8452 ("AES-GCM-SIV: Nonce Misuse-Resistant
|
|
* Authenticated Encryption") to ensure compatibility with that.
|
|
*/
|
|
static void test_polyval_rfc8452_testvec(struct kunit *test)
|
|
{
|
|
static const u8 raw_key[POLYVAL_BLOCK_SIZE] =
|
|
"\x31\x07\x28\xd9\x91\x1f\x1f\x38"
|
|
"\x37\xb2\x43\x16\xc3\xfa\xb9\xa0";
|
|
static const u8 data[48] =
|
|
"\x65\x78\x61\x6d\x70\x6c\x65\x00"
|
|
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
|
"\x48\x65\x6c\x6c\x6f\x20\x77\x6f"
|
|
"\x72\x6c\x64\x00\x00\x00\x00\x00"
|
|
"\x38\x00\x00\x00\x00\x00\x00\x00"
|
|
"\x58\x00\x00\x00\x00\x00\x00\x00";
|
|
static const u8 expected_hash[POLYVAL_BLOCK_SIZE] =
|
|
"\xad\x7f\xcf\x0b\x51\x69\x85\x16"
|
|
"\x62\x67\x2f\x3c\x5f\x95\x13\x8f";
|
|
u8 hash[POLYVAL_BLOCK_SIZE];
|
|
struct polyval_key key;
|
|
|
|
polyval_preparekey(&key, raw_key);
|
|
polyval(&key, data, sizeof(data), hash);
|
|
KUNIT_ASSERT_MEMEQ(test, hash, expected_hash, sizeof(hash));
|
|
}
|
|
|
|
/*
|
|
* Test a key and messages containing all one bits. This is useful to detect
|
|
* overflow bugs in implementations that emulate carryless multiplication using
|
|
* a series of standard multiplications with the bits spread out.
|
|
*/
|
|
static void test_polyval_allones_key_and_message(struct kunit *test)
|
|
{
|
|
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);
|
|
|
|
polyval_preparekey(&key, test_buf);
|
|
polyval_init(&hashofhashes_ctx, &key);
|
|
for (size_t len = 0; len <= 4096; len += 16) {
|
|
polyval(&key, test_buf, len, hash);
|
|
polyval_update(&hashofhashes_ctx, hash, sizeof(hash));
|
|
}
|
|
polyval_final(&hashofhashes_ctx, hash);
|
|
KUNIT_ASSERT_MEMEQ(test, hash, polyval_allones_hashofhashes,
|
|
sizeof(hash));
|
|
}
|
|
|
|
#define MAX_LEN_FOR_KEY_CHECK 1024
|
|
|
|
/*
|
|
* Given two prepared keys which should be identical (but may differ in
|
|
* alignment and/or whether they are followed by a guard page or not), verify
|
|
* that they produce consistent results on various data lengths.
|
|
*/
|
|
static void check_key_consistency(struct kunit *test,
|
|
const struct polyval_key *key1,
|
|
const struct polyval_key *key2)
|
|
{
|
|
u8 *data = test_buf;
|
|
u8 hash1[POLYVAL_BLOCK_SIZE];
|
|
u8 hash2[POLYVAL_BLOCK_SIZE];
|
|
|
|
rand_bytes(data, MAX_LEN_FOR_KEY_CHECK);
|
|
KUNIT_ASSERT_MEMEQ(test, key1, key2, sizeof(*key1));
|
|
|
|
for (int i = 0; i < 100; i++) {
|
|
size_t len = rand_length(MAX_LEN_FOR_KEY_CHECK);
|
|
|
|
polyval(key1, data, len, hash1);
|
|
polyval(key2, data, len, hash2);
|
|
KUNIT_ASSERT_MEMEQ(test, hash1, hash2, sizeof(hash1));
|
|
}
|
|
}
|
|
|
|
/* Test that no buffer overreads occur on either raw_key or polyval_key. */
|
|
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)];
|
|
struct polyval_key key1, key2;
|
|
struct polyval_key *guarded_key =
|
|
(struct polyval_key *)&test_buf[TEST_BUF_LEN - sizeof(key1)];
|
|
|
|
/* Prepare with regular buffers. */
|
|
rand_bytes(raw_key, sizeof(raw_key));
|
|
polyval_preparekey(&key1, raw_key);
|
|
|
|
/* Prepare with guarded raw_key, then check that it works. */
|
|
memcpy(guarded_raw_key, raw_key, sizeof(raw_key));
|
|
polyval_preparekey(&key2, guarded_raw_key);
|
|
check_key_consistency(test, &key1, &key2);
|
|
|
|
/* Prepare guarded polyval_key, then check that it works. */
|
|
polyval_preparekey(guarded_key, raw_key);
|
|
check_key_consistency(test, &key1, guarded_key);
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
static void test_polyval_with_minimally_aligned_key(struct kunit *test)
|
|
{
|
|
u8 raw_key[POLYVAL_BLOCK_SIZE];
|
|
struct polyval_key key;
|
|
struct polyval_key *minaligned_key =
|
|
(struct polyval_key *)&test_buf[MAX_LEN_FOR_KEY_CHECK +
|
|
__alignof__(struct polyval_key)];
|
|
|
|
KUNIT_ASSERT_TRUE(test, IS_ALIGNED((uintptr_t)minaligned_key,
|
|
__alignof__(struct polyval_key)));
|
|
KUNIT_ASSERT_TRUE(test,
|
|
!IS_ALIGNED((uintptr_t)minaligned_key,
|
|
2 * __alignof__(struct polyval_key)));
|
|
|
|
rand_bytes(raw_key, sizeof(raw_key));
|
|
polyval_preparekey(&key, raw_key);
|
|
polyval_preparekey(minaligned_key, raw_key);
|
|
check_key_consistency(test, &key, minaligned_key);
|
|
}
|
|
|
|
struct polyval_irq_test_state {
|
|
struct polyval_key expected_key;
|
|
u8 raw_key[POLYVAL_BLOCK_SIZE];
|
|
};
|
|
|
|
static bool polyval_irq_test_func(void *state_)
|
|
{
|
|
struct polyval_irq_test_state *state = state_;
|
|
struct polyval_key key;
|
|
|
|
polyval_preparekey(&key, state->raw_key);
|
|
return memcmp(&key, &state->expected_key, sizeof(key)) == 0;
|
|
}
|
|
|
|
/*
|
|
* Test that polyval_preparekey() produces the same output regardless of whether
|
|
* FPU or vector registers are usable when it is called.
|
|
*/
|
|
static void test_polyval_preparekey_in_irqs(struct kunit *test)
|
|
{
|
|
struct polyval_irq_test_state state;
|
|
|
|
rand_bytes(state.raw_key, sizeof(state.raw_key));
|
|
polyval_preparekey(&state.expected_key, state.raw_key);
|
|
kunit_run_irq_test(test, polyval_irq_test_func, 200000, &state);
|
|
}
|
|
|
|
static int polyval_suite_init(struct kunit_suite *suite)
|
|
{
|
|
u8 raw_key[POLYVAL_BLOCK_SIZE];
|
|
|
|
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);
|
|
}
|
|
|
|
static struct kunit_case polyval_test_cases[] = {
|
|
HASH_KUNIT_CASES,
|
|
KUNIT_CASE(test_polyval_rfc8452_testvec),
|
|
KUNIT_CASE(test_polyval_allones_key_and_message),
|
|
KUNIT_CASE(test_polyval_with_guarded_key),
|
|
KUNIT_CASE(test_polyval_with_minimally_aligned_key),
|
|
KUNIT_CASE(test_polyval_preparekey_in_irqs),
|
|
KUNIT_CASE(benchmark_hash),
|
|
{},
|
|
};
|
|
|
|
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);
|
|
|
|
MODULE_DESCRIPTION("KUnit tests and benchmark for POLYVAL");
|
|
MODULE_LICENSE("GPL");
|