xref: /openbsd-src/regress/lib/libcrypto/md/md_test.c (revision f09dd01620813cd80ce38afaf5431cf5c089b2b5)
1*f09dd016Stb /*	$OpenBSD: md_test.c,v 1.3 2025/01/19 10:17:39 tb Exp $ */
2080aa9e4Stb /*
3080aa9e4Stb  * Copyright (c) 2022 Joshua Sing <joshua@hypera.dev>
4080aa9e4Stb  *
5080aa9e4Stb  * Permission to use, copy, modify, and distribute this software for any
6080aa9e4Stb  * purpose with or without fee is hereby granted, provided that the above
7080aa9e4Stb  * copyright notice and this permission notice appear in all copies.
8080aa9e4Stb  *
9080aa9e4Stb  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10080aa9e4Stb  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11080aa9e4Stb  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12080aa9e4Stb  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13080aa9e4Stb  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14080aa9e4Stb  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15080aa9e4Stb  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16080aa9e4Stb  */
17080aa9e4Stb 
18080aa9e4Stb #include <openssl/evp.h>
19080aa9e4Stb #include <openssl/md4.h>
20080aa9e4Stb #include <openssl/md5.h>
21080aa9e4Stb 
22080aa9e4Stb #include <stdint.h>
23080aa9e4Stb #include <string.h>
24080aa9e4Stb 
25080aa9e4Stb struct md_test {
26080aa9e4Stb 	const int algorithm;
27080aa9e4Stb 	const uint8_t in[128];
28080aa9e4Stb 	const size_t in_len;
29080aa9e4Stb 	const uint8_t out[EVP_MAX_MD_SIZE];
30080aa9e4Stb };
31080aa9e4Stb 
32080aa9e4Stb static const struct md_test md_tests[] = {
33080aa9e4Stb 	/* MD4 (RFC 1320 test vectors)  */
34080aa9e4Stb 	{
35080aa9e4Stb 		.algorithm = NID_md4,
36080aa9e4Stb 		.in = "",
37080aa9e4Stb 		.in_len = 0,
38080aa9e4Stb 		.out = {
39080aa9e4Stb 			0x31, 0xd6, 0xcf, 0xe0, 0xd1, 0x6a, 0xe9, 0x31,
40080aa9e4Stb 			0xb7, 0x3c, 0x59, 0xd7, 0xe0, 0xc0, 0x89, 0xc0,
41080aa9e4Stb 		}
42080aa9e4Stb 	},
43080aa9e4Stb 	{
44080aa9e4Stb 		.algorithm = NID_md4,
45080aa9e4Stb 		.in = "a",
46080aa9e4Stb 		.in_len = 1,
47080aa9e4Stb 		.out = {
48080aa9e4Stb 			0xbd, 0xe5, 0x2c, 0xb3, 0x1d, 0xe3, 0x3e, 0x46,
49080aa9e4Stb 			0x24, 0x5e, 0x05, 0xfb, 0xdb, 0xd6, 0xfb, 0x24,
50080aa9e4Stb 		}
51080aa9e4Stb 	},
52080aa9e4Stb 	{
53080aa9e4Stb 		.algorithm = NID_md4,
54080aa9e4Stb 		.in = "abc",
55080aa9e4Stb 		.in_len = 3,
56080aa9e4Stb 		.out = {
57080aa9e4Stb 			0xa4, 0x48, 0x01, 0x7a, 0xaf, 0x21, 0xd8, 0x52,
58080aa9e4Stb 			0x5f, 0xc1, 0x0a, 0xe8, 0x7a, 0xa6, 0x72, 0x9d,
59080aa9e4Stb 		}
60080aa9e4Stb 	},
61080aa9e4Stb 	{
62080aa9e4Stb 		.algorithm = NID_md4,
63080aa9e4Stb 		.in = "message digest",
64080aa9e4Stb 		.in_len = 14,
65080aa9e4Stb 		.out = {
66080aa9e4Stb 			0xd9, 0x13, 0x0a, 0x81, 0x64, 0x54, 0x9f, 0xe8,
67080aa9e4Stb 			0x18, 0x87, 0x48, 0x06, 0xe1, 0xc7, 0x01, 0x4b,
68080aa9e4Stb 		}
69080aa9e4Stb 	},
70080aa9e4Stb 	{
71080aa9e4Stb 		.algorithm = NID_md4,
72080aa9e4Stb 		.in = "abcdefghijklmnopqrstuvwxyz",
73080aa9e4Stb 		.in_len = 26,
74080aa9e4Stb 		.out = {
75080aa9e4Stb 			0xd7, 0x9e, 0x1c, 0x30, 0x8a, 0xa5, 0xbb, 0xcd,
76080aa9e4Stb 			0xee, 0xa8, 0xed, 0x63, 0xdf, 0x41, 0x2d, 0xa9,
77080aa9e4Stb 		}
78080aa9e4Stb 	},
79080aa9e4Stb 	{
80080aa9e4Stb 		.algorithm = NID_md4,
81080aa9e4Stb 		.in =
82080aa9e4Stb 		    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv"
83080aa9e4Stb 		    "wxyz0123456789",
84080aa9e4Stb 		.in_len = 62,
85080aa9e4Stb 		.out = {
86080aa9e4Stb 			0x04, 0x3f, 0x85, 0x82, 0xf2, 0x41, 0xdb, 0x35,
87080aa9e4Stb 			0x1c, 0xe6, 0x27, 0xe1, 0x53, 0xe7, 0xf0, 0xe4,
88080aa9e4Stb 		}
89080aa9e4Stb 	},
90080aa9e4Stb 	{
91080aa9e4Stb 		.algorithm = NID_md4,
92080aa9e4Stb 		.in =
93080aa9e4Stb 		    "123456789012345678901234567890123456789012345678"
94080aa9e4Stb 		    "90123456789012345678901234567890",
95080aa9e4Stb 		.in_len = 80,
96080aa9e4Stb 		.out = {
97080aa9e4Stb 			0xe3, 0x3b, 0x4d, 0xdc, 0x9c, 0x38, 0xf2, 0x19,
98080aa9e4Stb 			0x9c, 0x3e, 0x7b, 0x16, 0x4f, 0xcc, 0x05, 0x36,
99080aa9e4Stb 		}
100080aa9e4Stb 	},
101080aa9e4Stb 
102080aa9e4Stb 	/* MD5 (RFC 1321 test vectors)  */
103080aa9e4Stb 	{
104080aa9e4Stb 		.algorithm = NID_md5,
105080aa9e4Stb 		.in = "",
106080aa9e4Stb 		.in_len = 0,
107080aa9e4Stb 		.out = {
108080aa9e4Stb 			0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04,
109080aa9e4Stb 			0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e,
110080aa9e4Stb 		}
111080aa9e4Stb 	},
112080aa9e4Stb 	{
113080aa9e4Stb 		.algorithm = NID_md5,
114080aa9e4Stb 		.in = "a",
115080aa9e4Stb 		.in_len = 1,
116080aa9e4Stb 		.out = {
117080aa9e4Stb 			0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8,
118080aa9e4Stb 			0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61,
119080aa9e4Stb 		}
120080aa9e4Stb 	},
121080aa9e4Stb 	{
122080aa9e4Stb 		.algorithm = NID_md5,
123080aa9e4Stb 		.in = "abc",
124080aa9e4Stb 		.in_len = 3,
125080aa9e4Stb 		.out = {
126080aa9e4Stb 			0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0,
127080aa9e4Stb 			0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72,
128080aa9e4Stb 		}
129080aa9e4Stb 	},
130080aa9e4Stb 	{
131080aa9e4Stb 		.algorithm = NID_md5,
132080aa9e4Stb 		.in = "message digest",
133080aa9e4Stb 		.in_len = 14,
134080aa9e4Stb 		.out = {
135080aa9e4Stb 			0xf9, 0x6b, 0x69, 0x7d, 0x7c, 0xb7, 0x93, 0x8d,
136080aa9e4Stb 			0x52, 0x5a, 0x2f, 0x31, 0xaa, 0xf1, 0x61, 0xd0,
137080aa9e4Stb 		}
138080aa9e4Stb 	},
139080aa9e4Stb 	{
140080aa9e4Stb 		.algorithm = NID_md5,
141080aa9e4Stb 		.in = "abcdefghijklmnopqrstuvwxyz",
142080aa9e4Stb 		.in_len = 26,
143080aa9e4Stb 		.out = {
144080aa9e4Stb 			0xc3, 0xfc, 0xd3, 0xd7, 0x61, 0x92, 0xe4, 0x00,
145080aa9e4Stb 			0x7d, 0xfb, 0x49, 0x6c, 0xca, 0x67, 0xe1, 0x3b,
146080aa9e4Stb 		}
147080aa9e4Stb 	},
148080aa9e4Stb 	{
149080aa9e4Stb 		.algorithm = NID_md5,
150080aa9e4Stb 		.in =
151080aa9e4Stb 		    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv"
152080aa9e4Stb 		    "wxyz0123456789",
153080aa9e4Stb 		.in_len = 62,
154080aa9e4Stb 		.out = {
155080aa9e4Stb 			0xd1, 0x74, 0xab, 0x98, 0xd2, 0x77, 0xd9, 0xf5,
156080aa9e4Stb 			0xa5, 0x61, 0x1c, 0x2c, 0x9f, 0x41, 0x9d, 0x9f,
157080aa9e4Stb 		}
158080aa9e4Stb 	},
159080aa9e4Stb 	{
160080aa9e4Stb 		.algorithm = NID_md5,
161080aa9e4Stb 		.in =
162080aa9e4Stb 		    "123456789012345678901234567890123456789012345678"
163080aa9e4Stb 		    "90123456789012345678901234567890",
164080aa9e4Stb 		.in_len = 80,
165080aa9e4Stb 		.out = {
166080aa9e4Stb 			0x57, 0xed, 0xf4, 0xa2, 0x2b, 0xe3, 0xc9, 0x55,
167080aa9e4Stb 			0xac, 0x49, 0xda, 0x2e, 0x21, 0x07, 0xb6, 0x7a,
168080aa9e4Stb 		}
169080aa9e4Stb 	},
170080aa9e4Stb };
171080aa9e4Stb 
172080aa9e4Stb #define N_MD_TESTS (sizeof(md_tests) / sizeof(md_tests[0]))
173080aa9e4Stb 
174080aa9e4Stb typedef unsigned char *(*md_hash_func)(const unsigned char *, size_t,
175080aa9e4Stb     unsigned char *);
176080aa9e4Stb 
177080aa9e4Stb static int
178080aa9e4Stb md_hash_from_algorithm(int algorithm, const char **out_label,
179080aa9e4Stb     md_hash_func *out_func, const EVP_MD **out_md, size_t *out_len)
180080aa9e4Stb {
181080aa9e4Stb 	switch (algorithm) {
182080aa9e4Stb 	case NID_md4:
183080aa9e4Stb 		*out_label = SN_md4;
184080aa9e4Stb 		*out_func = MD4;
185080aa9e4Stb 		*out_md = EVP_md4();
186080aa9e4Stb 		*out_len = MD4_DIGEST_LENGTH;
187080aa9e4Stb 		break;
188080aa9e4Stb 	case NID_md5:
189080aa9e4Stb 		*out_label = SN_md5;
190080aa9e4Stb 		*out_func = MD5;
191080aa9e4Stb 		*out_md = EVP_md5();
192080aa9e4Stb 		*out_len = MD5_DIGEST_LENGTH;
193080aa9e4Stb 		break;
194080aa9e4Stb 	default:
195080aa9e4Stb 		fprintf(stderr, "FAIL: unknown algorithm (%d)\n",
196080aa9e4Stb 			algorithm);
197080aa9e4Stb 		return 0;
198080aa9e4Stb 	}
199080aa9e4Stb 
200080aa9e4Stb 	return 1;
201080aa9e4Stb }
202080aa9e4Stb 
2039c7bd2b0Sjsing static void
2049c7bd2b0Sjsing hexdump(const unsigned char *buf, size_t len)
2059c7bd2b0Sjsing {
2069c7bd2b0Sjsing 	size_t i;
2079c7bd2b0Sjsing 
2089c7bd2b0Sjsing 	for (i = 1; i <= len; i++)
2099c7bd2b0Sjsing 		fprintf(stderr, " 0x%02hhx,%s", buf[i - 1], i % 8 ? "" : "\n");
2109c7bd2b0Sjsing 
2119c7bd2b0Sjsing 	fprintf(stderr, "\n");
2129c7bd2b0Sjsing }
2139c7bd2b0Sjsing 
214080aa9e4Stb static int
215080aa9e4Stb md_test(void)
216080aa9e4Stb {
217080aa9e4Stb 	unsigned char *(*md_func)(const unsigned char *, size_t, unsigned char *);
218080aa9e4Stb 	const struct md_test *st;
219080aa9e4Stb 	EVP_MD_CTX *hash = NULL;
220080aa9e4Stb 	const EVP_MD *md;
221080aa9e4Stb 	uint8_t out[EVP_MAX_MD_SIZE];
222080aa9e4Stb 	size_t in_len, out_len;
223080aa9e4Stb 	size_t i;
224080aa9e4Stb 	const char *label;
225080aa9e4Stb 	int failed = 1;
226080aa9e4Stb 
227080aa9e4Stb 	if ((hash = EVP_MD_CTX_new()) == NULL) {
228080aa9e4Stb 		fprintf(stderr, "FAIL: EVP_MD_CTX_new() failed\n");
229080aa9e4Stb 		goto failed;
230080aa9e4Stb 	}
231080aa9e4Stb 
232080aa9e4Stb 	for (i = 0; i < N_MD_TESTS; i++) {
233080aa9e4Stb 		st = &md_tests[i];
234080aa9e4Stb 		if (!md_hash_from_algorithm(st->algorithm, &label, &md_func,
235080aa9e4Stb 		    &md, &out_len))
236080aa9e4Stb 			goto failed;
237080aa9e4Stb 
238080aa9e4Stb 		/* Digest */
239080aa9e4Stb 		memset(out, 0, sizeof(out));
240080aa9e4Stb 		md_func(st->in, st->in_len, out);
241080aa9e4Stb 		if (memcmp(st->out, out, out_len) != 0) {
242080aa9e4Stb 			fprintf(stderr, "FAIL (%s): mismatch\n", label);
243080aa9e4Stb 			goto failed;
244080aa9e4Stb 		}
245080aa9e4Stb 
246080aa9e4Stb 		/* EVP single-shot digest */
247080aa9e4Stb 		memset(out, 0, sizeof(out));
248080aa9e4Stb 		if (!EVP_Digest(st->in, st->in_len, out, NULL, md, NULL)) {
249080aa9e4Stb 			fprintf(stderr, "FAIL (%s): EVP_Digest failed\n",
250080aa9e4Stb 			    label);
251080aa9e4Stb 			goto failed;
252080aa9e4Stb 		}
253080aa9e4Stb 
254080aa9e4Stb 		if (memcmp(st->out, out, out_len) != 0) {
255080aa9e4Stb 			fprintf(stderr, "FAIL (%s): EVP single-shot mismatch\n",
256080aa9e4Stb 			    label);
257080aa9e4Stb 			goto failed;
258080aa9e4Stb 		}
259080aa9e4Stb 
260080aa9e4Stb 		/* EVP digest */
261080aa9e4Stb 		memset(out, 0, sizeof(out));
262080aa9e4Stb 		if (!EVP_DigestInit_ex(hash, md, NULL)) {
263080aa9e4Stb 			fprintf(stderr, "FAIL (%s): EVP_DigestInit_ex failed\n",
264080aa9e4Stb 			    label);
265080aa9e4Stb 			goto failed;
266080aa9e4Stb 		}
267080aa9e4Stb 
268080aa9e4Stb 		in_len = st->in_len / 2;
269080aa9e4Stb 		if (!EVP_DigestUpdate(hash, st->in, in_len)) {
270080aa9e4Stb 			fprintf(stderr,
271080aa9e4Stb 			    "FAIL (%s): EVP_DigestUpdate first half failed\n",
272080aa9e4Stb 			    label);
273080aa9e4Stb 			goto failed;
274080aa9e4Stb 		}
275080aa9e4Stb 
276080aa9e4Stb 		if (!EVP_DigestUpdate(hash, st->in + in_len,
277080aa9e4Stb 			st->in_len - in_len)) {
278080aa9e4Stb 			fprintf(stderr,
279080aa9e4Stb 			    "FAIL (%s): EVP_DigestUpdate second half failed\n",
280080aa9e4Stb 			    label);
281080aa9e4Stb 			goto failed;
282080aa9e4Stb 		}
283080aa9e4Stb 
284080aa9e4Stb 		if (!EVP_DigestFinal_ex(hash, out, NULL)) {
285080aa9e4Stb 			fprintf(stderr,
286080aa9e4Stb 			    "FAIL (%s): EVP_DigestFinal_ex failed\n",
287080aa9e4Stb 			    label);
288080aa9e4Stb 			goto failed;
289080aa9e4Stb 		}
290080aa9e4Stb 
291080aa9e4Stb 		if (memcmp(st->out, out, out_len) != 0) {
292080aa9e4Stb 			fprintf(stderr, "FAIL (%s): EVP mismatch\n", label);
293080aa9e4Stb 			goto failed;
294080aa9e4Stb 		}
295080aa9e4Stb 	}
296080aa9e4Stb 
297080aa9e4Stb 	failed = 0;
298080aa9e4Stb 
299080aa9e4Stb  failed:
300080aa9e4Stb 	EVP_MD_CTX_free(hash);
301080aa9e4Stb 	return failed;
302080aa9e4Stb }
303080aa9e4Stb 
3049c7bd2b0Sjsing static int
3059c7bd2b0Sjsing md5_large_test(void)
3069c7bd2b0Sjsing {
3079c7bd2b0Sjsing 	MD5_CTX ctx;
3089c7bd2b0Sjsing 	uint8_t in[1024];
3099c7bd2b0Sjsing 	uint8_t out[EVP_MAX_MD_SIZE];
3109c7bd2b0Sjsing 	unsigned int out_len;
3119c7bd2b0Sjsing 	size_t in_len;
3129c7bd2b0Sjsing 	size_t i;
3139c7bd2b0Sjsing 	const char *label;
3149c7bd2b0Sjsing 	uint8_t want[] = {
3159c7bd2b0Sjsing 		0xd8, 0xbc, 0xae, 0x13, 0xb5, 0x5a, 0xb0, 0xfc,
3169c7bd2b0Sjsing 		0x7f, 0x8a, 0xe1, 0x78, 0x27, 0x8d, 0x44, 0x1b,
3179c7bd2b0Sjsing 	};
3189c7bd2b0Sjsing 	int failed = 1;
3199c7bd2b0Sjsing 
3209c7bd2b0Sjsing 	memset(in, 'A', sizeof(in));
3219c7bd2b0Sjsing 	in_len = sizeof(in);
3229c7bd2b0Sjsing 
3239c7bd2b0Sjsing 	memset(out, 0, sizeof(out));
3249c7bd2b0Sjsing 	out_len = 16;
3259c7bd2b0Sjsing 
3269c7bd2b0Sjsing 	label = "md5";
3279c7bd2b0Sjsing 
3289c7bd2b0Sjsing 	MD5_Init(&ctx);
3299c7bd2b0Sjsing 
330*f09dd016Stb 	for (i = 0; i < (1<<29) + 1; i += in_len) {
3319c7bd2b0Sjsing 		if (!MD5_Update(&ctx, in, in_len)) {
3329c7bd2b0Sjsing 			fprintf(stderr, "FAIL (%s): MD5_Update failed\n", label);
3339c7bd2b0Sjsing 			goto failed;
3349c7bd2b0Sjsing 		}
3359c7bd2b0Sjsing 	}
3369c7bd2b0Sjsing 	if (!MD5_Final(out, &ctx)) {
3379c7bd2b0Sjsing 		fprintf(stderr, "FAIL (%s): MD5_Final failed\n", label);
3389c7bd2b0Sjsing 		goto failed;
3399c7bd2b0Sjsing 	}
3409c7bd2b0Sjsing 
3419c7bd2b0Sjsing 	if (memcmp(out, want, out_len) != 0) {
3429c7bd2b0Sjsing 		fprintf(stderr, "FAIL (%s): MD5 mismatch\n", label);
3439c7bd2b0Sjsing 		hexdump(out, out_len);
3449c7bd2b0Sjsing 		goto failed;
3459c7bd2b0Sjsing 	}
3469c7bd2b0Sjsing 	if (ctx.Nh != 0x1 || ctx.Nl != 0x2000) {
3479c7bd2b0Sjsing 		fprintf(stderr, "FAIL (%s): MD5 incorrect bit length\n", label);
3489c7bd2b0Sjsing 		goto failed;
3499c7bd2b0Sjsing 	}
3509c7bd2b0Sjsing 
3519c7bd2b0Sjsing 	failed = 0;
3529c7bd2b0Sjsing 
3539c7bd2b0Sjsing  failed:
3549c7bd2b0Sjsing 	return failed;
3559c7bd2b0Sjsing }
3569c7bd2b0Sjsing 
357080aa9e4Stb int
358080aa9e4Stb main(int argc, char **argv)
359080aa9e4Stb {
360080aa9e4Stb 	int failed = 0;
361080aa9e4Stb 
362080aa9e4Stb 	failed |= md_test();
3639c7bd2b0Sjsing 	failed |= md5_large_test();
364080aa9e4Stb 
365080aa9e4Stb 	return failed;
366080aa9e4Stb }
367