summaryrefslogtreecommitdiff
path: root/lib/crypto/tests/polyval_kunit.c
blob: e59f598c15721d0ceb397142ef78915ddba46a31 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright 2025 Google LLC
 */
#include <crypto/polyval.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, 20000, &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");