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