1 /* 2 * Derived from: 3 * 4 * MDDRIVER.C - test driver for MD2, MD4 and MD5 5 * 6 * $FreeBSD: src/sbin/md5/md5.c,v 1.35 2006/01/17 15:35:57 phk Exp $ 7 */ 8 9 /* 10 * Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All 11 * rights reserved. 12 * 13 * RSA Data Security, Inc. makes no representations concerning either 14 * the merchantability of this software or the suitability of this 15 * software for any particular purpose. It is provided "as is" 16 * without express or implied warranty of any kind. 17 * 18 * These notices must be retained in any copies of any part of this 19 * documentation and/or software. 20 */ 21 22 #include <sys/types.h> 23 #include <sys/mman.h> 24 #include <sys/resource.h> 25 #include <sys/stat.h> 26 #include <sys/time.h> 27 28 #include <err.h> 29 #include <fcntl.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <sysexits.h> 34 #include <time.h> 35 #include <unistd.h> 36 37 #include <openssl/md5.h> 38 #include <openssl/ripemd.h> 39 #include <openssl/sha.h> 40 41 /* 42 * Length of test block, number of test blocks. 43 */ 44 #define TEST_BLOCK_LEN 10000 45 #define TEST_BLOCK_COUNT 100000 46 #define MDTESTCOUNT 8 47 48 static int qflag; 49 static int rflag; 50 static int sflag; 51 52 typedef int (DIGEST_Init)(void *); 53 typedef int (DIGEST_Update)(void *, const unsigned char *, size_t); 54 typedef int (DIGEST_Final)(unsigned char *, void *); 55 56 extern const char *MD5_TestOutput[MDTESTCOUNT]; 57 extern const char *SHA1_TestOutput[MDTESTCOUNT]; 58 extern const char *SHA256_TestOutput[MDTESTCOUNT]; 59 extern const char *SHA512_TestOutput[MDTESTCOUNT]; 60 extern const char *RIPEMD160_TestOutput[MDTESTCOUNT]; 61 62 typedef struct Algorithm_t { 63 const char *progname; 64 const char *name; 65 const char *(*TestOutput)[MDTESTCOUNT]; 66 DIGEST_Init *Init; 67 DIGEST_Update *Update; 68 DIGEST_Final *Final; 69 int Digest_length; 70 } Algorithm_t; 71 72 static void MDString(const Algorithm_t *, const char *); 73 static void MDTimeTrial(const Algorithm_t *); 74 static void MDTestSuite(const Algorithm_t *); 75 static void MDFilter(const Algorithm_t *, int); 76 static void usage(int excode) __dead2; 77 78 typedef union { 79 MD5_CTX md5; 80 SHA_CTX sha1; 81 SHA256_CTX sha256; 82 SHA512_CTX sha512; 83 RIPEMD160_CTX ripemd160; 84 } DIGEST_CTX; 85 86 /* max(MD5_DIGEST_LENGTH, SHA_DIGEST_LENGTH, 87 SHA256_DIGEST_LENGTH, SHA512_DIGEST_LENGTH, 88 RIPEMD160_DIGEST_LENGTH)*2+1 */ 89 #define HEX_DIGEST_LENGTH 129 90 91 /* algorithm function table */ 92 93 static const struct Algorithm_t Algorithm[] = { 94 { "md5", "MD5", 95 &MD5_TestOutput, 96 (DIGEST_Init *)MD5_Init, 97 (DIGEST_Update *)MD5_Update, 98 (DIGEST_Final *)MD5_Final, 99 MD5_DIGEST_LENGTH }, 100 { "sha1", "SHA1", 101 &SHA1_TestOutput, 102 (DIGEST_Init *)SHA1_Init, 103 (DIGEST_Update *)SHA1_Update, 104 (DIGEST_Final *)SHA1_Final, 105 SHA_DIGEST_LENGTH }, 106 { "sha256", "SHA256", 107 &SHA256_TestOutput, 108 (DIGEST_Init *)SHA256_Init, 109 (DIGEST_Update *)SHA256_Update, 110 (DIGEST_Final *)SHA256_Final, 111 SHA256_DIGEST_LENGTH }, 112 { "sha512", "SHA512", 113 &SHA512_TestOutput, 114 (DIGEST_Init *)SHA512_Init, 115 (DIGEST_Update *)SHA512_Update, 116 (DIGEST_Final *)SHA512_Final, 117 SHA512_DIGEST_LENGTH }, 118 { "rmd160", "RMD160", 119 &RIPEMD160_TestOutput, 120 (DIGEST_Init *)RIPEMD160_Init, 121 (DIGEST_Update *)RIPEMD160_Update, 122 (DIGEST_Final *)RIPEMD160_Final, 123 RIPEMD160_DIGEST_LENGTH }, 124 { 0 }, 125 }; 126 127 /* 128 * There is no need to use a huge mmap, just pick something 129 * reasonable. 130 */ 131 #define MAXMMAP (32*1024*1024) 132 133 static char * 134 digestend(const Algorithm_t *alg, DIGEST_CTX *context, char * const buf) 135 { 136 static const char hex[] = "0123456789abcdef"; 137 unsigned char digest[HEX_DIGEST_LENGTH]; 138 int i; 139 140 alg->Final(digest, context); 141 for (i = 0; i < alg->Digest_length; i++) { 142 buf[2*i] = hex[digest[i] >> 4]; 143 buf[2*i+1] = hex[digest[i] & 0x0f]; 144 } 145 buf[2*i] = '\0'; 146 147 return buf; 148 } 149 150 static char * 151 digestdata(const Algorithm_t *alg, const void *data, unsigned int len, 152 char * const buf) 153 { 154 DIGEST_CTX context; 155 156 alg->Init(&context); 157 alg->Update(&context, data, len); 158 return (digestend(alg, &context, buf)); 159 } 160 161 /* 162 * Digest the whole file. Don't rely on the file size (st_size), which may 163 * be zero and meaningless for files on pseudo filesystems (e.g., /proc). 164 * Instead, read the file until EOF to get the whole contents. 165 */ 166 static char * 167 digestbig(const char *fname, char * const buf, const Algorithm_t *alg) 168 { 169 int fd; 170 char *result; 171 unsigned char buffer[4096]; 172 DIGEST_CTX context; 173 ssize_t bytes; 174 175 fd = open(fname, O_RDONLY); 176 if (fd == -1) { 177 warn("can't open %s", fname); 178 return NULL; 179 } 180 181 alg->Init(&context); 182 183 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0) 184 alg->Update(&context, buffer, (size_t)bytes); 185 if (bytes == 0) /* EOF */ 186 result = digestend(alg, &context, buf); 187 else 188 result = NULL; 189 190 close(fd); 191 return result; 192 } 193 194 static char * 195 digestfile(const char *fname, char *buf, const Algorithm_t *alg, 196 off_t *beginp, off_t *endp) 197 { 198 int fd; 199 struct stat st; 200 size_t size, pagesize; 201 char *result = NULL; 202 void *map; 203 DIGEST_CTX context; 204 off_t end = *endp, begin = *beginp; 205 206 fd = open(fname, O_RDONLY); 207 if (fd == -1) { 208 warn("can't open %s", fname); 209 return NULL; 210 } 211 212 if (fstat(fd, &st) == -1) { 213 warn("can't fstat %s after opening", fname); 214 goto cleanup; 215 } 216 217 /* Non-positive end means, it has to be counted from the back: */ 218 if (end <= 0) 219 end += st.st_size; 220 /* Negative begin means, it has to be counted from the back: */ 221 if (begin < 0) 222 begin += st.st_size; 223 224 if (begin < 0 || end < 0 || begin > st.st_size || end > st.st_size) { 225 warnx("%s is %jd bytes long, not large enough for the " 226 "specified offsets [%jd-%jd]", 227 fname, (intmax_t)st.st_size, 228 (intmax_t)*beginp, (intmax_t)*endp); 229 goto cleanup; 230 } 231 if (begin > end) { 232 warnx("%s is %jd bytes long. Begin-offset %jd (%jd) is " 233 "larger than end-offset %jd (%jd)", 234 fname, (intmax_t)st.st_size, (intmax_t)begin, 235 (intmax_t)*beginp, (intmax_t)end, (intmax_t)*endp); 236 goto cleanup; 237 } 238 239 if (*endp <= 0) 240 *endp = end; 241 if (*beginp < 0) 242 *beginp = begin; 243 244 pagesize = getpagesize(); 245 246 alg->Init(&context); 247 248 do { 249 if (end - begin > MAXMMAP) 250 size = MAXMMAP; 251 else 252 size = end - begin; 253 254 map = mmap(NULL, size, PROT_READ, MAP_NOCORE, fd, begin); 255 if (map == MAP_FAILED) { 256 warn("mmaping of %s between %jd and %jd ", 257 fname, (intmax_t)begin, (intmax_t)begin + size); 258 goto cleanup; 259 } 260 /* 261 * Try to give kernel a hint. Not that it 262 * cares at the time of this writing :-( 263 */ 264 if (size > pagesize) 265 madvise(map, size, MADV_SEQUENTIAL); 266 alg->Update(&context, map, size); 267 munmap(map, size); 268 begin += size; 269 } while (begin < end); 270 271 result = digestend(alg, &context, buf); 272 273 cleanup: 274 close(fd); 275 return result; 276 } 277 278 static off_t 279 parseint(const char *arg) 280 { 281 double result; /* Use double to allow things like 0.5Kb */ 282 char *endp; 283 284 result = strtod(arg, &endp); 285 switch (endp[0]) { 286 case 'T': 287 case 't': 288 result *= 1024; 289 /* FALLTHROUGH */ 290 case 'M': 291 case 'm': 292 result *= 1024; 293 /* FALLTHROUGH */ 294 case 'K': 295 case 'k': 296 endp++; 297 if (endp[1] == 'b' || endp[1] == 'B') 298 endp++; 299 result *= 1024; 300 /* FALLTHROUGH */ 301 case '\0': 302 break; 303 default: 304 warnx("%c (%d): unrecognized suffix", endp[0], (int)endp[0]); 305 goto badnumber; 306 } 307 308 if (endp[0] == '\0') 309 return result; 310 311 badnumber: 312 errx(EX_USAGE, "`%s' is not a valid offset.", arg); 313 } 314 315 /* 316 * Main driver. 317 * 318 * Arguments (may be any combination): 319 * -sstring - digests string 320 * -t - runs time trial 321 * -x - runs test script 322 * filename - digests file 323 * (none) - digests standard input 324 */ 325 int 326 main(int argc, char *argv[]) 327 { 328 char *p, buf[HEX_DIGEST_LENGTH]; 329 int ch, failed, useoffsets = 0; 330 off_t begin = 0, end = 0; 331 const char *progname; 332 const Algorithm_t *alg; 333 334 if ((progname = strrchr(argv[0], '/')) == NULL) 335 progname = argv[0]; 336 else 337 progname++; 338 339 for (alg = Algorithm; alg->progname != NULL; alg++) 340 if (strcasecmp(alg->progname, progname) == 0) 341 break; 342 if (alg->progname == NULL) 343 alg = Algorithm; 344 345 failed = 0; 346 while ((ch = getopt(argc, argv, "hb:e:pqrs:tx")) != -1) { 347 switch (ch) { 348 case 'b': 349 begin = parseint(optarg); 350 useoffsets = 1; 351 break; 352 case 'e': 353 end = parseint(optarg); 354 useoffsets = 1; 355 break; 356 case 'p': 357 MDFilter(alg, 1); 358 break; 359 case 'q': 360 qflag = 1; 361 break; 362 case 'r': 363 rflag = 1; 364 break; 365 case 's': 366 sflag = 1; 367 MDString(alg, optarg); 368 break; 369 case 't': 370 MDTimeTrial(alg); 371 break; 372 case 'x': 373 MDTestSuite(alg); 374 break; 375 case 'h': 376 usage(EX_OK); 377 default: 378 usage(EX_USAGE); 379 } 380 } 381 argc -= optind; 382 argv += optind; 383 384 if (*argv) { 385 do { 386 if (useoffsets) 387 p = digestfile(*argv, buf, alg, &begin, &end); 388 else 389 p = digestbig(*argv, buf, alg); 390 if (!p) { 391 /* digestfile() outputs its own diagnostics */ 392 #if 0 393 if (!useoffsets) 394 warn("%s", *argv); 395 #endif 396 failed++; 397 } else { 398 if (qflag) { 399 printf("%s\n", p); 400 } else if (rflag) { 401 if (useoffsets) 402 printf("%s %s[%jd-%jd]\n", 403 p, *argv, 404 (intmax_t)begin, 405 (intmax_t)end); 406 else 407 printf("%s %s\n", p, *argv); 408 } else if (useoffsets) { 409 printf("%s (%s[%jd-%jd]) = %s\n", 410 alg->name, *argv, 411 (intmax_t)begin, 412 (intmax_t)end, 413 p); 414 } else { 415 printf("%s (%s) = %s\n", 416 alg->name, *argv, p); 417 } 418 } 419 } while (*++argv); 420 } else if (!sflag && (optind == 1 || qflag || rflag)) 421 MDFilter(alg, 0); 422 423 if (failed != 0) 424 return (EX_NOINPUT); 425 426 return (0); 427 } 428 429 /* 430 * Digests a string and prints the result. 431 */ 432 static void 433 MDString(const Algorithm_t *alg, const char *string) 434 { 435 size_t len = strlen(string); 436 char buf[HEX_DIGEST_LENGTH]; 437 438 if (qflag) 439 printf("%s\n", digestdata(alg, string, len, buf)); 440 else if (rflag) 441 printf("%s \"%s\"\n", 442 digestdata(alg, string, len, buf), string); 443 else 444 printf("%s (\"%s\") = %s\n", alg->name, string, 445 digestdata(alg, string, len, buf)); 446 } 447 448 /* 449 * Measures the time to digest TEST_BLOCK_COUNT TEST_BLOCK_LEN-byte blocks. 450 */ 451 static void 452 MDTimeTrial(const Algorithm_t *alg) 453 { 454 DIGEST_CTX context; 455 struct rusage before, after; 456 struct timeval total; 457 float seconds; 458 unsigned char block[TEST_BLOCK_LEN]; 459 unsigned int i; 460 char *p, buf[HEX_DIGEST_LENGTH]; 461 462 printf("%s time trial. Digesting %d %d-byte blocks ...", 463 alg->name, TEST_BLOCK_COUNT, TEST_BLOCK_LEN); 464 fflush(stdout); 465 466 /* Initialize block */ 467 for (i = 0; i < TEST_BLOCK_LEN; i++) 468 block[i] = (unsigned char) (i & 0xff); 469 470 /* Start timer */ 471 getrusage(RUSAGE_SELF, &before); 472 473 /* Digest blocks */ 474 alg->Init(&context); 475 for (i = 0; i < TEST_BLOCK_COUNT; i++) 476 alg->Update(&context, block, TEST_BLOCK_LEN); 477 p = digestend(alg, &context, buf); 478 479 /* Stop timer */ 480 getrusage(RUSAGE_SELF, &after); 481 timersub(&after.ru_utime, &before.ru_utime, &total); 482 seconds = total.tv_sec + (float) total.tv_usec / 1000000; 483 484 printf(" done\n"); 485 printf("Digest = %s\n", p); 486 printf("Time = %.3f seconds\n", seconds); 487 printf("Speed = %.3f MiB/second\n", 488 (float) TEST_BLOCK_LEN * TEST_BLOCK_COUNT / seconds / (1 << 20)); 489 } 490 491 /* 492 * Digests a reference suite of strings and prints the results. 493 */ 494 static const char *MDTestInput[MDTESTCOUNT] = { 495 "", 496 "a", 497 "abc", 498 "message digest", 499 "abcdefghijklmnopqrstuvwxyz", 500 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 501 "12345678901234567890123456789012345678901234567890123456789012345678901234567890", 502 ("MD5 has not yet (2001-09-03) been broken, but sufficient attacks have been made " 503 "that its security is in some doubt") 504 }; 505 506 const char *MD5_TestOutput[MDTESTCOUNT] = { 507 "d41d8cd98f00b204e9800998ecf8427e", 508 "0cc175b9c0f1b6a831c399e269772661", 509 "900150983cd24fb0d6963f7d28e17f72", 510 "f96b697d7cb7938d525a2f31aaf161d0", 511 "c3fcd3d76192e4007dfb496cca67e13b", 512 "d174ab98d277d9f5a5611c2c9f419d9f", 513 "57edf4a22be3c955ac49da2e2107b67a", 514 "b50663f41d44d92171cb9976bc118538" 515 }; 516 517 const char *SHA1_TestOutput[MDTESTCOUNT] = { 518 "da39a3ee5e6b4b0d3255bfef95601890afd80709", 519 "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8", 520 "a9993e364706816aba3e25717850c26c9cd0d89d", 521 "c12252ceda8be8994d5fa0290a47231c1d16aae3", 522 "32d10c7b8cf96570ca04ce37f2a19d84240d3a89", 523 "761c457bf73b14d27e9e9265c46f4b4dda11f940", 524 "50abf5706a150990a08b2c5ea40fa0e585554732", 525 "18eca4333979c4181199b7b4fab8786d16cf2846" 526 }; 527 528 const char *SHA256_TestOutput[MDTESTCOUNT] = { 529 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", 530 "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb", 531 "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", 532 "f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650", 533 "71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73", 534 "db4bfcbd4da0cd85a60c3c37d3fbd8805c77f15fc6b1fdfe614ee0a7c8fdb4c0", 535 "f371bc4a311f2b009eef952dd83ca80e2b60026c8e935592d0f9c308453c813e", 536 "e6eae09f10ad4122a0e2a4075761d185a272ebd9f5aa489e998ff2f09cbfdd9f" 537 }; 538 539 const char *SHA512_TestOutput[MDTESTCOUNT] = { 540 "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", 541 "1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75", 542 "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f", 543 "107dbf389d9e9f71a3a95f6c055b9251bc5268c2be16d6c13492ea45b0199f3309e16455ab1e96118e8a905d5597b72038ddb372a89826046de66687bb420e7c", 544 "4dbff86cc2ca1bae1e16468a05cb9881c97f1753bce3619034898faa1aabe429955a1bf8ec483d7421fe3c1646613a59ed5441fb0f321389f77f48a879c7b1f1", 545 "1e07be23c26a86ea37ea810c8ec7809352515a970e9253c26f536cfc7a9996c45c8370583e0a78fa4a90041d71a4ceab7423f19c71b9d5a3e01249f0bebd5894", 546 "72ec1ef1124a45b047e8b7c75a932195135bb61de24ec0d1914042246e0aec3a2354e093d76f3048b456764346900cb130d2a4fd5dd16abb5e30bcb850dee843", 547 "e8a835195e039708b13d9131e025f4441dbdc521ce625f245a436dcd762f54bf5cb298d96235e6c6a304e087ec8189b9512cbdf6427737ea82793460c367b9c3" 548 }; 549 550 const char *RIPEMD160_TestOutput[MDTESTCOUNT] = { 551 "9c1185a5c5e9fc54612808977ee8f548b2258d31", 552 "0bdc9d2d256b3ee9daae347be6f4dc835a467ffe", 553 "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc", 554 "5d0689ef49d2fae572b881b123a85ffa21595f36", 555 "f71c27109c692c1b56bbdceb5b9d2865b3708dbc", 556 "b0e20b6e3116640286ed3a87a5713079b21f5189", 557 "9b752e45573d4b39f4dbd3323cab82bf63326bfb", 558 "5feb69c6bf7c29d95715ad55f57d8ac5b2b7dd32" 559 }; 560 561 static void 562 MDTestSuite(const Algorithm_t *alg) 563 { 564 int i; 565 char buffer[HEX_DIGEST_LENGTH]; 566 567 printf("%s test suite:\n", alg->name); 568 for (i = 0; i < MDTESTCOUNT; i++) { 569 digestdata(alg, MDTestInput[i], strlen(MDTestInput[i]), buffer); 570 printf("%s (\"%s\") = %s", alg->name, MDTestInput[i], buffer); 571 if (strcmp(buffer, (*alg->TestOutput)[i]) == 0) 572 printf(" - verified correct\n"); 573 else 574 printf(" - INCORRECT RESULT!\n"); 575 } 576 } 577 578 /* 579 * Digests the standard input and prints the result. 580 */ 581 static void 582 MDFilter(const Algorithm_t *alg, int tee) 583 { 584 DIGEST_CTX context; 585 unsigned int len; 586 unsigned char buffer[BUFSIZ]; 587 char buf[HEX_DIGEST_LENGTH]; 588 589 alg->Init(&context); 590 while ((len = fread(buffer, 1, BUFSIZ, stdin))) { 591 if (tee && len != fwrite(buffer, 1, len, stdout)) 592 err(1, "stdout"); 593 alg->Update(&context, buffer, len); 594 } 595 printf("%s\n", digestend(alg, &context, buf)); 596 } 597 598 static void 599 usage(int excode) 600 { 601 fprintf(stderr, "usage:\n\t%s [-pqrtx] [-b offset] [-e offset] " 602 "[-s string] [files ...]\n", getprogname()); 603 exit(excode); 604 } 605