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