xref: /openbsd-src/regress/lib/libcrypto/md/md_test.c (revision f09dd01620813cd80ce38afaf5431cf5c089b2b5)
1 /*	$OpenBSD: md_test.c,v 1.3 2025/01/19 10:17:39 tb Exp $ */
2 /*
3  * Copyright (c) 2022 Joshua Sing <joshua@hypera.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/md4.h>
20 #include <openssl/md5.h>
21 
22 #include <stdint.h>
23 #include <string.h>
24 
25 struct md_test {
26 	const int algorithm;
27 	const uint8_t in[128];
28 	const size_t in_len;
29 	const uint8_t out[EVP_MAX_MD_SIZE];
30 };
31 
32 static const struct md_test md_tests[] = {
33 	/* MD4 (RFC 1320 test vectors)  */
34 	{
35 		.algorithm = NID_md4,
36 		.in = "",
37 		.in_len = 0,
38 		.out = {
39 			0x31, 0xd6, 0xcf, 0xe0, 0xd1, 0x6a, 0xe9, 0x31,
40 			0xb7, 0x3c, 0x59, 0xd7, 0xe0, 0xc0, 0x89, 0xc0,
41 		}
42 	},
43 	{
44 		.algorithm = NID_md4,
45 		.in = "a",
46 		.in_len = 1,
47 		.out = {
48 			0xbd, 0xe5, 0x2c, 0xb3, 0x1d, 0xe3, 0x3e, 0x46,
49 			0x24, 0x5e, 0x05, 0xfb, 0xdb, 0xd6, 0xfb, 0x24,
50 		}
51 	},
52 	{
53 		.algorithm = NID_md4,
54 		.in = "abc",
55 		.in_len = 3,
56 		.out = {
57 			0xa4, 0x48, 0x01, 0x7a, 0xaf, 0x21, 0xd8, 0x52,
58 			0x5f, 0xc1, 0x0a, 0xe8, 0x7a, 0xa6, 0x72, 0x9d,
59 		}
60 	},
61 	{
62 		.algorithm = NID_md4,
63 		.in = "message digest",
64 		.in_len = 14,
65 		.out = {
66 			0xd9, 0x13, 0x0a, 0x81, 0x64, 0x54, 0x9f, 0xe8,
67 			0x18, 0x87, 0x48, 0x06, 0xe1, 0xc7, 0x01, 0x4b,
68 		}
69 	},
70 	{
71 		.algorithm = NID_md4,
72 		.in = "abcdefghijklmnopqrstuvwxyz",
73 		.in_len = 26,
74 		.out = {
75 			0xd7, 0x9e, 0x1c, 0x30, 0x8a, 0xa5, 0xbb, 0xcd,
76 			0xee, 0xa8, 0xed, 0x63, 0xdf, 0x41, 0x2d, 0xa9,
77 		}
78 	},
79 	{
80 		.algorithm = NID_md4,
81 		.in =
82 		    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv"
83 		    "wxyz0123456789",
84 		.in_len = 62,
85 		.out = {
86 			0x04, 0x3f, 0x85, 0x82, 0xf2, 0x41, 0xdb, 0x35,
87 			0x1c, 0xe6, 0x27, 0xe1, 0x53, 0xe7, 0xf0, 0xe4,
88 		}
89 	},
90 	{
91 		.algorithm = NID_md4,
92 		.in =
93 		    "123456789012345678901234567890123456789012345678"
94 		    "90123456789012345678901234567890",
95 		.in_len = 80,
96 		.out = {
97 			0xe3, 0x3b, 0x4d, 0xdc, 0x9c, 0x38, 0xf2, 0x19,
98 			0x9c, 0x3e, 0x7b, 0x16, 0x4f, 0xcc, 0x05, 0x36,
99 		}
100 	},
101 
102 	/* MD5 (RFC 1321 test vectors)  */
103 	{
104 		.algorithm = NID_md5,
105 		.in = "",
106 		.in_len = 0,
107 		.out = {
108 			0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04,
109 			0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e,
110 		}
111 	},
112 	{
113 		.algorithm = NID_md5,
114 		.in = "a",
115 		.in_len = 1,
116 		.out = {
117 			0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8,
118 			0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61,
119 		}
120 	},
121 	{
122 		.algorithm = NID_md5,
123 		.in = "abc",
124 		.in_len = 3,
125 		.out = {
126 			0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0,
127 			0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72,
128 		}
129 	},
130 	{
131 		.algorithm = NID_md5,
132 		.in = "message digest",
133 		.in_len = 14,
134 		.out = {
135 			0xf9, 0x6b, 0x69, 0x7d, 0x7c, 0xb7, 0x93, 0x8d,
136 			0x52, 0x5a, 0x2f, 0x31, 0xaa, 0xf1, 0x61, 0xd0,
137 		}
138 	},
139 	{
140 		.algorithm = NID_md5,
141 		.in = "abcdefghijklmnopqrstuvwxyz",
142 		.in_len = 26,
143 		.out = {
144 			0xc3, 0xfc, 0xd3, 0xd7, 0x61, 0x92, 0xe4, 0x00,
145 			0x7d, 0xfb, 0x49, 0x6c, 0xca, 0x67, 0xe1, 0x3b,
146 		}
147 	},
148 	{
149 		.algorithm = NID_md5,
150 		.in =
151 		    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv"
152 		    "wxyz0123456789",
153 		.in_len = 62,
154 		.out = {
155 			0xd1, 0x74, 0xab, 0x98, 0xd2, 0x77, 0xd9, 0xf5,
156 			0xa5, 0x61, 0x1c, 0x2c, 0x9f, 0x41, 0x9d, 0x9f,
157 		}
158 	},
159 	{
160 		.algorithm = NID_md5,
161 		.in =
162 		    "123456789012345678901234567890123456789012345678"
163 		    "90123456789012345678901234567890",
164 		.in_len = 80,
165 		.out = {
166 			0x57, 0xed, 0xf4, 0xa2, 0x2b, 0xe3, 0xc9, 0x55,
167 			0xac, 0x49, 0xda, 0x2e, 0x21, 0x07, 0xb6, 0x7a,
168 		}
169 	},
170 };
171 
172 #define N_MD_TESTS (sizeof(md_tests) / sizeof(md_tests[0]))
173 
174 typedef unsigned char *(*md_hash_func)(const unsigned char *, size_t,
175     unsigned char *);
176 
177 static int
178 md_hash_from_algorithm(int algorithm, const char **out_label,
179     md_hash_func *out_func, const EVP_MD **out_md, size_t *out_len)
180 {
181 	switch (algorithm) {
182 	case NID_md4:
183 		*out_label = SN_md4;
184 		*out_func = MD4;
185 		*out_md = EVP_md4();
186 		*out_len = MD4_DIGEST_LENGTH;
187 		break;
188 	case NID_md5:
189 		*out_label = SN_md5;
190 		*out_func = MD5;
191 		*out_md = EVP_md5();
192 		*out_len = MD5_DIGEST_LENGTH;
193 		break;
194 	default:
195 		fprintf(stderr, "FAIL: unknown algorithm (%d)\n",
196 			algorithm);
197 		return 0;
198 	}
199 
200 	return 1;
201 }
202 
203 static void
204 hexdump(const unsigned char *buf, size_t len)
205 {
206 	size_t i;
207 
208 	for (i = 1; i <= len; i++)
209 		fprintf(stderr, " 0x%02hhx,%s", buf[i - 1], i % 8 ? "" : "\n");
210 
211 	fprintf(stderr, "\n");
212 }
213 
214 static int
215 md_test(void)
216 {
217 	unsigned char *(*md_func)(const unsigned char *, size_t, unsigned char *);
218 	const struct md_test *st;
219 	EVP_MD_CTX *hash = NULL;
220 	const EVP_MD *md;
221 	uint8_t out[EVP_MAX_MD_SIZE];
222 	size_t in_len, out_len;
223 	size_t i;
224 	const char *label;
225 	int failed = 1;
226 
227 	if ((hash = EVP_MD_CTX_new()) == NULL) {
228 		fprintf(stderr, "FAIL: EVP_MD_CTX_new() failed\n");
229 		goto failed;
230 	}
231 
232 	for (i = 0; i < N_MD_TESTS; i++) {
233 		st = &md_tests[i];
234 		if (!md_hash_from_algorithm(st->algorithm, &label, &md_func,
235 		    &md, &out_len))
236 			goto failed;
237 
238 		/* Digest */
239 		memset(out, 0, sizeof(out));
240 		md_func(st->in, st->in_len, out);
241 		if (memcmp(st->out, out, out_len) != 0) {
242 			fprintf(stderr, "FAIL (%s): mismatch\n", label);
243 			goto failed;
244 		}
245 
246 		/* EVP single-shot digest */
247 		memset(out, 0, sizeof(out));
248 		if (!EVP_Digest(st->in, st->in_len, out, NULL, md, NULL)) {
249 			fprintf(stderr, "FAIL (%s): EVP_Digest failed\n",
250 			    label);
251 			goto failed;
252 		}
253 
254 		if (memcmp(st->out, out, out_len) != 0) {
255 			fprintf(stderr, "FAIL (%s): EVP single-shot mismatch\n",
256 			    label);
257 			goto failed;
258 		}
259 
260 		/* EVP digest */
261 		memset(out, 0, sizeof(out));
262 		if (!EVP_DigestInit_ex(hash, md, NULL)) {
263 			fprintf(stderr, "FAIL (%s): EVP_DigestInit_ex failed\n",
264 			    label);
265 			goto failed;
266 		}
267 
268 		in_len = st->in_len / 2;
269 		if (!EVP_DigestUpdate(hash, st->in, in_len)) {
270 			fprintf(stderr,
271 			    "FAIL (%s): EVP_DigestUpdate first half failed\n",
272 			    label);
273 			goto failed;
274 		}
275 
276 		if (!EVP_DigestUpdate(hash, st->in + in_len,
277 			st->in_len - in_len)) {
278 			fprintf(stderr,
279 			    "FAIL (%s): EVP_DigestUpdate second half failed\n",
280 			    label);
281 			goto failed;
282 		}
283 
284 		if (!EVP_DigestFinal_ex(hash, out, NULL)) {
285 			fprintf(stderr,
286 			    "FAIL (%s): EVP_DigestFinal_ex failed\n",
287 			    label);
288 			goto failed;
289 		}
290 
291 		if (memcmp(st->out, out, out_len) != 0) {
292 			fprintf(stderr, "FAIL (%s): EVP mismatch\n", label);
293 			goto failed;
294 		}
295 	}
296 
297 	failed = 0;
298 
299  failed:
300 	EVP_MD_CTX_free(hash);
301 	return failed;
302 }
303 
304 static int
305 md5_large_test(void)
306 {
307 	MD5_CTX ctx;
308 	uint8_t in[1024];
309 	uint8_t out[EVP_MAX_MD_SIZE];
310 	unsigned int out_len;
311 	size_t in_len;
312 	size_t i;
313 	const char *label;
314 	uint8_t want[] = {
315 		0xd8, 0xbc, 0xae, 0x13, 0xb5, 0x5a, 0xb0, 0xfc,
316 		0x7f, 0x8a, 0xe1, 0x78, 0x27, 0x8d, 0x44, 0x1b,
317 	};
318 	int failed = 1;
319 
320 	memset(in, 'A', sizeof(in));
321 	in_len = sizeof(in);
322 
323 	memset(out, 0, sizeof(out));
324 	out_len = 16;
325 
326 	label = "md5";
327 
328 	MD5_Init(&ctx);
329 
330 	for (i = 0; i < (1<<29) + 1; i += in_len) {
331 		if (!MD5_Update(&ctx, in, in_len)) {
332 			fprintf(stderr, "FAIL (%s): MD5_Update failed\n", label);
333 			goto failed;
334 		}
335 	}
336 	if (!MD5_Final(out, &ctx)) {
337 		fprintf(stderr, "FAIL (%s): MD5_Final failed\n", label);
338 		goto failed;
339 	}
340 
341 	if (memcmp(out, want, out_len) != 0) {
342 		fprintf(stderr, "FAIL (%s): MD5 mismatch\n", label);
343 		hexdump(out, out_len);
344 		goto failed;
345 	}
346 	if (ctx.Nh != 0x1 || ctx.Nl != 0x2000) {
347 		fprintf(stderr, "FAIL (%s): MD5 incorrect bit length\n", label);
348 		goto failed;
349 	}
350 
351 	failed = 0;
352 
353  failed:
354 	return failed;
355 }
356 
357 int
358 main(int argc, char **argv)
359 {
360 	int failed = 0;
361 
362 	failed |= md_test();
363 	failed |= md5_large_test();
364 
365 	return failed;
366 }
367