diff options
Diffstat (limited to 'text_cmds/md5')
-rw-r--r-- | text_cmds/md5/commoncrypto.c | 108 | ||||
-rw-r--r-- | text_cmds/md5/commoncrypto.h | 8 | ||||
-rw-r--r-- | text_cmds/md5/md5.1 | 95 | ||||
-rw-r--r-- | text_cmds/md5/md5.c | 419 |
4 files changed, 630 insertions, 0 deletions
diff --git a/text_cmds/md5/commoncrypto.c b/text_cmds/md5/commoncrypto.c new file mode 100644 index 0000000..3551afe --- /dev/null +++ b/text_cmds/md5/commoncrypto.c @@ -0,0 +1,108 @@ +/* Generic CommonDigest wrappers to match the semantics of libmd. */ + +#include <dispatch/dispatch.h> +#include <os/assumes.h> +#include <errno.h> +#include <fcntl.h> + +#include "commoncrypto.h" + +#define CHUNK_SIZE (10 * 1024 * 1024) + +char * +Digest_End(CCDigestRef ctx, char *buf) +{ + static const char hex[] = "0123456789abcdef"; + uint8_t digest[32]; // SHA256 is the biggest + size_t i, length; + + (void)os_assumes_zero(CCDigestFinal(ctx, digest)); + length = CCDigestOutputSize(ctx); + os_assert(length <= sizeof(digest)); + for (i = 0; i < length; i++) { + buf[i+i] = hex[digest[i] >> 4]; + buf[i+i+1] = hex[digest[i] & 0x0f]; + } + buf[i+i] = '\0'; + return buf; +} + +char * +Digest_Data(CCDigestAlg algorithm, const void *data, size_t len, char *buf) +{ + CCDigestCtx ctx; + + (void)os_assumes_zero(CCDigestInit(algorithm, &ctx)); + (void)os_assumes_zero(CCDigestUpdate(&ctx, data, len)); + return Digest_End(&ctx, buf); +} + +char * +Digest_File(CCDigestAlg algorithm, const char *filename, char *buf) +{ + int fd; + __block CCDigestCtx ctx; + dispatch_queue_t queue; + dispatch_semaphore_t sema; + dispatch_io_t io; + __block int s_error = 0; + __block bool eof = false; + off_t chunk_offset; + + /* dispatch_io_create_with_path requires an absolute path */ + fd = open(filename, O_RDONLY); + if (fd < 0) { + return NULL; + } + + (void)fcntl(fd, F_NOCACHE, 1); + + (void)os_assumes_zero(CCDigestInit(algorithm, &ctx)); + + queue = dispatch_queue_create("com.apple.mtree.io", NULL); + os_assert(queue); + sema = dispatch_semaphore_create(0); + os_assert(sema); + + io = dispatch_io_create(DISPATCH_IO_STREAM, fd, queue, ^(int error) { + if (error != 0) { + s_error = error; + } + (void)close(fd); + (void)dispatch_semaphore_signal(sema); + }); + os_assert(io); + for (chunk_offset = 0; eof == false && s_error == 0; chunk_offset += CHUNK_SIZE) { + dispatch_io_read(io, chunk_offset, CHUNK_SIZE, queue, ^(bool done, dispatch_data_t data, int error) { + if (data != NULL) { + (void)dispatch_data_apply(data, ^(__unused dispatch_data_t region, __unused size_t offset, const void *buffer, size_t size) { + (void)os_assumes_zero(CCDigestUpdate(&ctx, buffer, size)); + return (bool)true; + }); + } + + if (error != 0) { + s_error = error; + } + + if (done) { + eof = (data == dispatch_data_empty); + dispatch_semaphore_signal(sema); + } + }); + dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); + } + dispatch_release(io); // it will close on its own + + (void)dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); + + dispatch_release(queue); + dispatch_release(sema); + + if (s_error != 0) { + errno = s_error; + return NULL; + } + + return Digest_End(&ctx, buf); +} diff --git a/text_cmds/md5/commoncrypto.h b/text_cmds/md5/commoncrypto.h new file mode 100644 index 0000000..ed10d9a --- /dev/null +++ b/text_cmds/md5/commoncrypto.h @@ -0,0 +1,8 @@ +#include <CommonCrypto/CommonDigest.h> +#include <CommonCrypto/CommonDigestSPI.h> + +char *Digest_End(CCDigestRef, char *); + +char *Digest_Data(CCDigestAlg, const void *, size_t, char *); + +char *Digest_File(CCDigestAlg, const char *, char *); diff --git a/text_cmds/md5/md5.1 b/text_cmds/md5/md5.1 new file mode 100644 index 0000000..01df82b --- /dev/null +++ b/text_cmds/md5/md5.1 @@ -0,0 +1,95 @@ +.\" $FreeBSD: src/sbin/md5/md5.1,v 1.24 2005/03/10 09:56:39 cperciva Exp $ +.Dd June 6, 2004 +.Dt MD5 1 +.Os +.Sh NAME +.Nm md5 +.Nd calculate a message-digest fingerprint (checksum) for a file +.Sh SYNOPSIS +.Nm md5 +.Op Fl pqrtx +.Op Fl s Ar string +.Op Ar +.Sh DESCRIPTION +The +.Nm +utility takes as input a message of arbitrary length and produces as +output a +.Dq fingerprint +or +.Dq message digest +of the input. +It is conjectured that it is computationally infeasible to +produce two messages having the same message digest, or to produce any +message having a given prespecified target message digest. +The +.Tn MD5 +algorithm is intended for digital signature applications, where a +large file must be +.Dq compressed +in a secure manner before being encrypted with a private +(secret) +key under a public-key cryptosystem such as +.Tn RSA . +.Pp +.Tn MD5's +designer Ron Rivest has stated "md5 and sha1 are both clearly broken (in terms +of collision-resistance)". +So +.Tn MD5 +should be avoided when creating new protocols, or implementing protocols with better options. +.Tn SHA256 +and +.Tn SHA512 +are better options as they have been more resilient to attacks (as of 2009). +.Pp +The following options may be used in any combination and must +precede any files named on the command line. +The hexadecimal checksum of each file listed on the command line is printed +after the options are processed. +.Bl -tag -width indent +.It Fl s Ar string +Print a checksum of the given +.Ar string . +.It Fl p +Echo stdin to stdout and append the checksum to stdout. +.It Fl q +Quiet mode - only the checksum is printed out. +Overrides the +.Fl r +option. +.It Fl r +Reverses the format of the output. +This helps with visual diffs. +Does nothing +when combined with the +.Fl ptx +options. +.It Fl t +Run a built-in time trial. +.It Fl x +Run a built-in test script. +.El +.Sh EXIT STATUS +The +.Nm +utility exits 0 on success, +and 1 if at least one of the input files could not be read. +.Sh SEE ALSO +.Xr cksum 1 , +.Xr md5 3 , +.Xr ripemd 3 , +.Xr sha 3 , +.Xr CC_SHA256_Init 3 +.Rs +.%A R. Rivest +.%T The MD5 Message-Digest Algorithm +.%O RFC1321 +.Rs +.%A Vlastimil Klima +.%T Finding MD5 Collisions - a Toy For a Notebook +.%O Cryptology ePrint Archive: Report 2005/075 +.Re +.Sh ACKNOWLEDGMENTS +This program is placed in the public domain for free general use by +RSA Data Security. diff --git a/text_cmds/md5/md5.c b/text_cmds/md5/md5.c new file mode 100644 index 0000000..9be53d3 --- /dev/null +++ b/text_cmds/md5/md5.c @@ -0,0 +1,419 @@ +/* + * Derived from: + * + * MDDRIVER.C - test driver for MD2, MD4 and MD5 + */ + +/* + * Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All + * rights reserved. + * + * RSA Data Security, Inc. makes no representations concerning either + * the merchantability of this software or the suitability of this + * software for any particular purpose. It is provided "as is" + * without express or implied warranty of any kind. + * + * These notices must be retained in any copies of any part of this + * documentation and/or software. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/sbin/md5/md5.c,v 1.34 2005/03/09 19:23:04 cperciva Exp $"); + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <err.h> +#ifndef __APPLE__ +#include <md5.h> +#include <ripemd.h> +#include <sha.h> +#include <sha256.h> +#endif /* !__APPLE__ */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sysexits.h> + +#ifdef __APPLE__ +#include "commoncrypto.h" +#endif /* __APPLE__ */ + +/* + * Length of test block, number of test blocks. + */ +#define TEST_BLOCK_LEN 10000 +#define TEST_BLOCK_COUNT 100000 +#define MDTESTCOUNT 8 + +int qflag; +int rflag; +int sflag; + +typedef void (DIGEST_Init)(void *); +typedef void (DIGEST_Update)(void *, const unsigned char *, size_t); +typedef char *(DIGEST_End)(void *, char *); + +extern const char *MD5TestOutput[MDTESTCOUNT]; +extern const char *SHA1_TestOutput[MDTESTCOUNT]; +extern const char *SHA256_TestOutput[MDTESTCOUNT]; +extern const char *RIPEMD160_TestOutput[MDTESTCOUNT]; + +typedef struct Algorithm_t { + const char *progname; + const char *name; + const char *(*TestOutput)[MDTESTCOUNT]; +#ifdef __APPLE__ + CCDigestAlg algorithm; +#else /* !__APPLE__ */ + DIGEST_Init *Init; + DIGEST_Update *Update; + DIGEST_End *End; + char *(*Data)(const unsigned char *, unsigned int, char *); + char *(*File)(const char *, char *); +#endif /* __APPLE__ */ +} Algorithm_t; + +#ifndef __APPLE__ +static void MD5_Update(MD5_CTX *, const unsigned char *, size_t); +#endif /* !__APPLE__ */ +static void MDString(Algorithm_t *, const char *); +static void MDTimeTrial(Algorithm_t *); +static void MDTestSuite(Algorithm_t *); +static void MDFilter(Algorithm_t *, int); +static void usage(Algorithm_t *); + +#ifdef __APPLE__ +typedef CCDigestCtx DIGEST_CTX; +#else /* !__APPLE__ */ +typedef union { + MD5_CTX md5; + SHA1_CTX sha1; + SHA256_CTX sha256; + RIPEMD160_CTX ripemd160; +} DIGEST_CTX; +#endif /* __APPLE__ */ + +/* max(MD5_DIGEST_LENGTH, SHA_DIGEST_LENGTH, + SHA256_DIGEST_LENGTH, RIPEMD160_DIGEST_LENGTH)*2+1 */ +#define HEX_DIGEST_LENGTH 65 + +/* algorithm function table */ + +struct Algorithm_t Algorithm[] = { +#ifdef __APPLE__ + { "md5", "MD5", &MD5TestOutput, kCCDigestMD5, }, + { "sha1", "SHA1", &SHA1_TestOutput, kCCDigestSHA1 }, + { "sha256", "SHA256", &SHA256_TestOutput, kCCDigestSHA256 }, + { "rmd160", "RMD160", &RIPEMD160_TestOutput, kCCDigestRMD160 }, +#else + { "md5", "MD5", &MD5TestOutput, (DIGEST_Init*)&MD5Init, + (DIGEST_Update*)&MD5_Update, (DIGEST_End*)&MD5End, + &MD5Data, &MD5File }, + { "sha1", "SHA1", &SHA1_TestOutput, (DIGEST_Init*)&SHA1_Init, + (DIGEST_Update*)&SHA1_Update, (DIGEST_End*)&SHA1_End, + &SHA1_Data, &SHA1_File }, + { "sha256", "SHA256", &SHA256_TestOutput, (DIGEST_Init*)&SHA256_Init, + (DIGEST_Update*)&SHA256_Update, (DIGEST_End*)&SHA256_End, + &SHA256_Data, &SHA256_File }, + { "rmd160", "RMD160", &RIPEMD160_TestOutput, + (DIGEST_Init*)&RIPEMD160_Init, (DIGEST_Update*)&RIPEMD160_Update, + (DIGEST_End*)&RIPEMD160_End, &RIPEMD160_Data, &RIPEMD160_File } +#endif +}; + +#ifndef __APPLE__ +static void +MD5_Update(MD5_CTX *c, const unsigned char *data, size_t len) +{ + MD5Update(c, data, len); +} +#endif /* !__APPLE__ */ + +/* Main driver. + +Arguments (may be any combination): + -sstring - digests string + -t - runs time trial + -x - runs test script + filename - digests file + (none) - digests standard input + */ +int +main(int argc, char *argv[]) +{ + int ch; + char *p; + char buf[HEX_DIGEST_LENGTH]; + int failed=0; + unsigned digest=0; + const char* progname; + + if(*argv) { + if ((progname = strrchr(argv[0], '/')) == NULL) + progname = argv[0]; + else + progname++; + + for (digest = 0; digest < sizeof(Algorithm)/sizeof(*Algorithm); digest++) + if (strcasecmp(Algorithm[digest].progname, progname) == 0) + break; + + if (digest == sizeof(Algorithm)/sizeof(*Algorithm)) + digest = 0; + } + + while ((ch = getopt(argc, argv, "pqrs:tx")) != -1) + switch (ch) { + case 'p': + MDFilter(&Algorithm[digest], 1); + break; + case 'q': + qflag = 1; + break; + case 'r': + rflag = 1; + break; + case 's': + sflag = 1; + MDString(&Algorithm[digest], optarg); + break; + case 't': + MDTimeTrial(&Algorithm[digest]); + break; + case 'x': + MDTestSuite(&Algorithm[digest]); + break; + default: + usage(&Algorithm[digest]); + } + argc -= optind; + argv += optind; + + if (*argv) { + do { +#ifdef __APPLE__ + p = Digest_File(Algorithm[digest].algorithm, *argv, buf); +#else + p = Algorithm[digest].File(*argv, buf); +#endif + if (!p) { + warn("%s", *argv); + failed++; + } else { + if (qflag) + printf("%s\n", p); + else if (rflag) + printf("%s %s\n", p, *argv); + else + printf("%s (%s) = %s\n", Algorithm[digest].name, *argv, p); + } + } while (*++argv); + } else if (!sflag && (optind == 1 || qflag || rflag)) + MDFilter(&Algorithm[digest], 0); + + if (failed != 0) + return (1); + + return (0); +} +/* + * Digests a string and prints the result. + */ +static void +MDString(Algorithm_t *alg, const char *string) +{ + size_t len = strlen(string); + char buf[HEX_DIGEST_LENGTH]; + + if (qflag) +#ifdef __APPLE__ + printf("%s\n", Digest_Data(alg->algorithm, string, len, buf)); + else if (rflag) + printf("%s \"%s\"\n", Digest_Data(alg->algorithm, string, len, buf), string); + else + printf("%s (\"%s\") = %s\n", alg->name, string, Digest_Data(alg->algorithm, string, len, buf)); +#else /* !__APPLE__ */ + printf("%s\n", alg->Data(string, len, buf)); + else if (rflag) + printf("%s \"%s\"\n", alg->Data(string, len, buf), string); + else + printf("%s (\"%s\") = %s\n", alg->name, string, alg->Data(string, len, buf)); +#endif /* __APPLE__ */ +} +/* + * Measures the time to digest TEST_BLOCK_COUNT TEST_BLOCK_LEN-byte blocks. + */ +static void +MDTimeTrial(Algorithm_t *alg) +{ + DIGEST_CTX context; + struct rusage before, after; + struct timeval total; + float seconds; + unsigned char block[TEST_BLOCK_LEN]; + unsigned int i; + char *p, buf[HEX_DIGEST_LENGTH]; + + printf + ("%s time trial. Digesting %d %d-byte blocks ...", + alg->name, TEST_BLOCK_COUNT, TEST_BLOCK_LEN); + fflush(stdout); + + /* Initialize block */ + for (i = 0; i < TEST_BLOCK_LEN; i++) + block[i] = (unsigned char) (i & 0xff); + + /* Start timer */ + getrusage(0, &before); + + /* Digest blocks */ +#ifdef __APPLE__ + CCDigestInit(alg->algorithm, &context); + for (i = 0; i < TEST_BLOCK_COUNT; i++) + CCDigestUpdate(&context, block, TEST_BLOCK_LEN); + p = Digest_End(&context, buf); +#else + alg->Init(&context); + for (i = 0; i < TEST_BLOCK_COUNT; i++) + alg->Update(&context, block, TEST_BLOCK_LEN); + p = alg->End(&context, buf); +#endif + + /* Stop timer */ + getrusage(0, &after); + timersub(&after.ru_utime, &before.ru_utime, &total); + seconds = total.tv_sec + (float) total.tv_usec / 1000000; + + printf(" done\n"); + printf("Digest = %s", p); + printf("\nTime = %f seconds\n", seconds); + printf + ("Speed = %f bytes/second\n", + (float) TEST_BLOCK_LEN * (float) TEST_BLOCK_COUNT / seconds); +} +/* + * Digests a reference suite of strings and prints the results. + */ + +const char *MDTestInput[MDTESTCOUNT] = { + "", + "a", + "abc", + "message digest", + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + "12345678901234567890123456789012345678901234567890123456789012345678901234567890", + "MD5 has not yet (2001-09-03) been broken, but sufficient attacks have been made \ +that its security is in some doubt" +}; + +const char *MD5TestOutput[MDTESTCOUNT] = { + "d41d8cd98f00b204e9800998ecf8427e", + "0cc175b9c0f1b6a831c399e269772661", + "900150983cd24fb0d6963f7d28e17f72", + "f96b697d7cb7938d525a2f31aaf161d0", + "c3fcd3d76192e4007dfb496cca67e13b", + "d174ab98d277d9f5a5611c2c9f419d9f", + "57edf4a22be3c955ac49da2e2107b67a", + "b50663f41d44d92171cb9976bc118538" +}; + +const char *SHA1_TestOutput[MDTESTCOUNT] = { + "da39a3ee5e6b4b0d3255bfef95601890afd80709", + "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8", + "a9993e364706816aba3e25717850c26c9cd0d89d", + "c12252ceda8be8994d5fa0290a47231c1d16aae3", + "32d10c7b8cf96570ca04ce37f2a19d84240d3a89", + "761c457bf73b14d27e9e9265c46f4b4dda11f940", + "50abf5706a150990a08b2c5ea40fa0e585554732", + "18eca4333979c4181199b7b4fab8786d16cf2846" +}; + +const char *SHA256_TestOutput[MDTESTCOUNT] = { + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb", + "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", + "f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650", + "71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73", + "db4bfcbd4da0cd85a60c3c37d3fbd8805c77f15fc6b1fdfe614ee0a7c8fdb4c0", + "f371bc4a311f2b009eef952dd83ca80e2b60026c8e935592d0f9c308453c813e", + "e6eae09f10ad4122a0e2a4075761d185a272ebd9f5aa489e998ff2f09cbfdd9f" +}; + +const char *RIPEMD160_TestOutput[MDTESTCOUNT] = { + "9c1185a5c5e9fc54612808977ee8f548b2258d31", + "0bdc9d2d256b3ee9daae347be6f4dc835a467ffe", + "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc", + "5d0689ef49d2fae572b881b123a85ffa21595f36", + "f71c27109c692c1b56bbdceb5b9d2865b3708dbc", + "b0e20b6e3116640286ed3a87a5713079b21f5189", + "9b752e45573d4b39f4dbd3323cab82bf63326bfb", + "5feb69c6bf7c29d95715ad55f57d8ac5b2b7dd32" +}; + +static void +MDTestSuite(Algorithm_t *alg) +{ + int i; + char buffer[HEX_DIGEST_LENGTH]; + + printf("%s test suite:\n", alg->name); + for (i = 0; i < MDTESTCOUNT; i++) { +#ifdef __APPLE__ + Digest_Data(alg->algorithm, MDTestInput[i], strlen(MDTestInput[i]), buffer); +#else + (*alg->Data)(MDTestInput[i], strlen(MDTestInput[i]), buffer); +#endif + printf("%s (\"%s\") = %s", alg->name, MDTestInput[i], buffer); + if (strcmp(buffer, (*alg->TestOutput)[i]) == 0) + printf(" - verified correct\n"); + else + printf(" - INCORRECT RESULT!\n"); + } +} + +/* + * Digests the standard input and prints the result. + */ +static void +MDFilter(Algorithm_t *alg, int tee) +{ + DIGEST_CTX context; + unsigned int len; + unsigned char buffer[BUFSIZ]; + char buf[HEX_DIGEST_LENGTH]; + +#ifdef __APPLE__ + CCDigestInit(alg->algorithm, &context); +#else + alg->Init(&context); +#endif + while ((len = fread(buffer, 1, BUFSIZ, stdin))) { + if (tee && len != fwrite(buffer, 1, len, stdout)) + err(1, "stdout"); +#ifdef __APPLE__ + CCDigestUpdate(&context, buffer, len); +#else + alg->Update(&context, buffer, len); +#endif + } + if (ferror(stdin)) { + errx(EX_IOERR, NULL); + } +#ifdef __APPLE__ + printf("%s\n", Digest_End(&context, buf)); +#else + printf("%s\n", alg->End(&context, buf)); +#endif +} + +static void +usage(Algorithm_t *alg) +{ + + fprintf(stderr, "usage: %s [-pqrtx] [-s string] [files ...]\n", alg->progname); + exit(1); +} |