1/*	$OpenBSD: whirlpool_test.c,v 1.3 2024/04/09 18:12:11 tb Exp $ */
2/*
3 * Copyright (c) 2024 Joshua Sing <joshua@joshuasing.dev>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <openssl/evp.h>
19#include <openssl/whrlpool.h>
20
21#include <stdint.h>
22#include <string.h>
23
24struct whirlpool_test {
25	const uint8_t in[128];
26	const size_t in_len;
27	const uint8_t out[EVP_MAX_MD_SIZE];
28};
29
30static const struct whirlpool_test whirlpool_tests[] = {
31	{
32		.in = "",
33		.in_len = 0,
34		.out = {
35			0x19, 0xfa, 0x61, 0xd7, 0x55, 0x22, 0xa4, 0x66,
36			0x9b, 0x44, 0xe3, 0x9c, 0x1d, 0x2e, 0x17, 0x26,
37			0xc5, 0x30, 0x23, 0x21, 0x30, 0xd4, 0x07, 0xf8,
38			0x9a, 0xfe, 0xe0, 0x96, 0x49, 0x97, 0xf7, 0xa7,
39			0x3e, 0x83, 0xbe, 0x69, 0x8b, 0x28, 0x8f, 0xeb,
40			0xcf, 0x88, 0xe3, 0xe0, 0x3c, 0x4f, 0x07, 0x57,
41			0xea, 0x89, 0x64, 0xe5, 0x9b, 0x63, 0xd9, 0x37,
42			0x08, 0xb1, 0x38, 0xcc, 0x42, 0xa6, 0x6e, 0xb3,
43		},
44	},
45	{
46		.in = "a",
47		.in_len = 1,
48		.out = {
49			0x8a,  0xca,  0x26,  0x02,  0x79,  0x2a,  0xec,  0x6f,
50			0x11,  0xa6,  0x72,  0x06,  0x53,  0x1f,  0xb7,  0xd7,
51			0xf0,  0xdf,  0xf5,  0x94,  0x13,  0x14,  0x5e,  0x69,
52			0x73,  0xc4,  0x50,  0x01,  0xd0,  0x08,  0x7b,  0x42,
53			0xd1,  0x1b,  0xc6,  0x45,  0x41,  0x3a,  0xef,  0xf6,
54			0x3a,  0x42,  0x39,  0x1a,  0x39,  0x14,  0x5a,  0x59,
55			0x1a,  0x92,  0x20,  0x0d,  0x56,  0x01,  0x95,  0xe5,
56			0x3b,  0x47,  0x85,  0x84,  0xfd,  0xae,  0x23,  0x1a,
57		},
58	},
59	{
60		.in = "abc",
61		.in_len = 3,
62		.out = {
63			0x4e,  0x24,  0x48,  0xa4,  0xc6,  0xf4,  0x86,  0xbb,
64			0x16,  0xb6,  0x56,  0x2c,  0x73,  0xb4,  0x02,  0x0b,
65			0xf3,  0x04,  0x3e,  0x3a,  0x73,  0x1b,  0xce,  0x72,
66			0x1a,  0xe1,  0xb3,  0x03,  0xd9,  0x7e,  0x6d,  0x4c,
67			0x71,  0x81,  0xee,  0xbd,  0xb6,  0xc5,  0x7e,  0x27,
68			0x7d,  0x0e,  0x34,  0x95,  0x71,  0x14,  0xcb,  0xd6,
69			0xc7,  0x97,  0xfc,  0x9d,  0x95,  0xd8,  0xb5,  0x82,
70			0xd2,  0x25,  0x29,  0x20,  0x76,  0xd4,  0xee,  0xf5,
71		},
72	},
73	{
74		.in = "message digest",
75		.in_len = 14,
76		.out = {
77			0x37,  0x8c,  0x84,  0xa4,  0x12,  0x6e,  0x2d,  0xc6,
78			0xe5,  0x6d,  0xcc,  0x74,  0x58,  0x37,  0x7a,  0xac,
79			0x83,  0x8d,  0x00,  0x03,  0x22,  0x30,  0xf5,  0x3c,
80			0xe1,  0xf5,  0x70,  0x0c,  0x0f,  0xfb,  0x4d,  0x3b,
81			0x84,  0x21,  0x55,  0x76,  0x59,  0xef,  0x55,  0xc1,
82			0x06,  0xb4,  0xb5,  0x2a,  0xc5,  0xa4,  0xaa,  0xa6,
83			0x92,  0xed,  0x92,  0x00,  0x52,  0x83,  0x8f,  0x33,
84			0x62,  0xe8,  0x6d,  0xbd,  0x37,  0xa8,  0x90,  0x3e,
85		},
86	},
87	{
88		.in = "abcdefghijklmnopqrstuvwxyz",
89		.in_len = 26,
90		.out = {
91			0xf1,  0xd7,  0x54,  0x66,  0x26,  0x36,  0xff,  0xe9,
92			0x2c,  0x82,  0xeb,  0xb9,  0x21,  0x2a,  0x48,  0x4a,
93			0x8d,  0x38,  0x63,  0x1e,  0xad,  0x42,  0x38,  0xf5,
94			0x44,  0x2e,  0xe1,  0x3b,  0x80,  0x54,  0xe4,  0x1b,
95			0x08,  0xbf,  0x2a,  0x92,  0x51,  0xc3,  0x0b,  0x6a,
96			0x0b,  0x8a,  0xae,  0x86,  0x17,  0x7a,  0xb4,  0xa6,
97			0xf6,  0x8f,  0x67,  0x3e,  0x72,  0x07,  0x86,  0x5d,
98			0x5d,  0x98,  0x19,  0xa3,  0xdb,  0xa4,  0xeb,  0x3b,
99		},
100	},
101	{
102		.in = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
103		.in_len = 62,
104		.out = {
105			0xdc,  0x37,  0xe0,  0x08,  0xcf,  0x9e,  0xe6,  0x9b,
106			0xf1,  0x1f,  0x00,  0xed,  0x9a,  0xba,  0x26,  0x90,
107			0x1d,  0xd7,  0xc2,  0x8c,  0xde,  0xc0,  0x66,  0xcc,
108			0x6a,  0xf4,  0x2e,  0x40,  0xf8,  0x2f,  0x3a,  0x1e,
109			0x08,  0xeb,  0xa2,  0x66,  0x29,  0x12,  0x9d,  0x8f,
110			0xb7,  0xcb,  0x57,  0x21,  0x1b,  0x92,  0x81,  0xa6,
111			0x55,  0x17,  0xcc,  0x87,  0x9d,  0x7b,  0x96,  0x21,
112			0x42,  0xc6,  0x5f,  0x5a,  0x7a,  0xf0,  0x14,  0x67,
113		},
114	},
115	{
116		.in = "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
117		.in_len = 80,
118		.out = {
119			0x46,  0x6e,  0xf1,  0x8b,  0xab,  0xb0,  0x15,  0x4d,
120			0x25,  0xb9,  0xd3,  0x8a,  0x64,  0x14,  0xf5,  0xc0,
121			0x87,  0x84,  0x37,  0x2b,  0xcc,  0xb2,  0x04,  0xd6,
122			0x54,  0x9c,  0x4a,  0xfa,  0xdb,  0x60,  0x14,  0x29,
123			0x4d,  0x5b,  0xd8,  0xdf,  0x2a,  0x6c,  0x44,  0xe5,
124			0x38,  0xcd,  0x04,  0x7b,  0x26,  0x81,  0xa5,  0x1a,
125			0x2c,  0x60,  0x48,  0x1e,  0x88,  0xc5,  0xa2,  0x0b,
126			0x2c,  0X2A,  0X80,  0XCF,  0X3A,  0X9A,  0X08,  0X3B,
127		},
128	},
129	{
130		.in = "abcdbcdecdefdefgefghfghighijhijk",
131		.in_len = 32,
132		.out = {
133			0x2a,  0x98,  0x7e,  0xa4,  0x0f,  0x91,  0x70,  0x61,
134			0xf5,  0xd6,  0xf0,  0xa0,  0xe4,  0x64,  0x4f,  0x48,
135			0x8a,  0x7a,  0x5a,  0x52,  0xde,  0xee,  0x65,  0x62,
136			0x07,  0xc5,  0x62,  0xf9,  0x88,  0xe9,  0x5c,  0x69,
137			0x16,  0xbd,  0xc8,  0x03,  0x1b,  0xc5,  0xbe,  0x1b,
138			0x7b,  0x94,  0x76,  0x39,  0xfe,  0x05,  0x0b,  0x56,
139			0x93,  0x9b,  0xaa,  0xa0,  0xad,  0xff,  0x9a,  0xe6,
140			0x74,  0x5b,  0x7b,  0x18,  0x1c,  0x3b,  0xe3,  0xfd,
141		},
142	},
143};
144
145#define N_WHIRLPOOL_TESTS (sizeof(whirlpool_tests) / sizeof(whirlpool_tests[0]))
146
147static int
148whirlpool_test(void)
149{
150	const struct whirlpool_test *wt;
151	EVP_MD_CTX *md_ctx = NULL;
152	const EVP_MD *md = EVP_whirlpool();
153	uint8_t out[EVP_MAX_MD_SIZE];
154	size_t i, l, in_len;
155	int failed = 1;
156
157	if ((md_ctx = EVP_MD_CTX_new()) == NULL) {
158		fprintf(stderr, "FAIL: EVP_MD_CTX_new() failed\n");
159		goto failed;
160	}
161
162	for (i = 0; i < N_WHIRLPOOL_TESTS; i++) {
163		wt = &whirlpool_tests[i];
164
165		/* Digest */
166		memset(out, 0, sizeof(out));
167		WHIRLPOOL(wt->in, wt->in_len, out);
168		if (memcmp(wt->out, out, WHIRLPOOL_DIGEST_LENGTH) != 0) {
169			fprintf(stderr, "FAIL (%zu): digest mismatch\n", i);
170			goto failed;
171		}
172
173		/* EVP single-shot digest */
174		memset(out, 0, sizeof(out));
175		if (!EVP_Digest(wt->in, wt->in_len, out, NULL, md, NULL)) {
176			fprintf(stderr, "FAIL (%zu): EVP_Digest failed\n", i);
177			goto failed;
178		}
179
180		if (memcmp(wt->out, out, WHIRLPOOL_DIGEST_LENGTH) != 0) {
181			fprintf(stderr,
182			    "FAIL (%zu): EVP single-shot mismatch\n", i);
183			goto failed;
184		}
185
186		/* EVP digest */
187		memset(out, 0, sizeof(out));
188		if (!EVP_DigestInit_ex(md_ctx, md, NULL)) {
189			fprintf(stderr,
190			    "FAIL (%zu): EVP_DigestInit_ex failed\n", i);
191			goto failed;
192		}
193
194		for (l = 0; l < wt->in_len;) {
195			in_len = 1;
196			if (wt->in_len > 1)
197				in_len = arc4random_uniform(wt->in_len / 2);
198			if (in_len < 1)
199				in_len = 1;
200			if (in_len > wt->in_len - l)
201				in_len = wt->in_len - l;
202
203			if (!EVP_DigestUpdate(md_ctx, wt->in + l, in_len)) {
204				fprintf(stderr,
205				    "FAIL(%zu, %zu): EVP_DigestUpdate failed\n",
206				    i, l);
207				goto failed;
208			}
209
210			l += in_len;
211		}
212
213		if (!EVP_DigestFinal_ex(md_ctx, out, NULL)) {
214			fprintf(stderr,
215			    "FAIL (%zu): EVP_DigestFinal_ex failed\n",
216			    i);
217			goto failed;
218		}
219
220		if (memcmp(wt->out, out, WHIRLPOOL_DIGEST_LENGTH) != 0) {
221			fprintf(stderr, "FAIL (%zu): EVP mismatch\n", i);
222			goto failed;
223		}
224	}
225
226	failed = 0;
227
228 failed:
229	EVP_MD_CTX_free(md_ctx);
230
231	return failed;
232}
233
234int
235main(int argc, char **argv)
236{
237	int failed = 0;
238
239	failed |= whirlpool_test();
240
241	return failed;
242}
243