2 * md5drvr - md5 driver code
4 * @(#) $Revision: 13.3 $
5 * @(#) $Id: md5drvr.c,v 13.3 2006/08/14 10:21:59 chongo Exp $
6 * @(#) $Source: /usr/local/src/cmd/hash/RCS/md5drvr.c,v $
8 * This file was written by RSA Data Security.
10 * This file was modified by Landon Curt Noll.
12 * This code has been placed in the public domain. Please do not
13 * copyright this code.
15 * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO
16 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MER-
17 * CHANTABILITY AND FITNESS. IN NO EVENT SHALL LANDON CURT
18 * NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
19 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
20 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
21 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 * chongo (was here) /\oo/\
25 * http://www.isthe.com/chongo/index.html
27 * Share and enjoy! :-)
31 * NOTE: The version information below refers to all md5 code, not
32 * just this file. In particular, this file was created by
35 * Version 1.1: 17 Feb 1990 RLR
36 * Original code written.
38 * Version 1.2: 27 Dec 1990 SRD,AJ,BSK,JT
39 * C reference version.
41 * Version 1.3: 27 Apr 1991 RLR
42 * G modified to have y&~z instead of y&z
43 * FF, GG, HH modified to add in last register done
44 * access pattern: round 2 works mod 5, round 3 works mod 3
45 * distinct additive constant for each step
46 * round 4 added, working mod 7
48 * Version 1.4: 10 Jul 1991 SRD,AJ,BSK,JT
49 * Constant correction.
51 * Version 2.1: 31 Dec 1993 Landon Curt Noll
52 * Modified/Re-wrote md5.c
54 * Version 2.2: 09 Jan 1994 Landon Curt Noll
55 * md5drvr.c and md5dual.c code cloned from shs version 2.5.8 94/01/09
58 * Version 2.3: 10 Jan 1994 Landon Curt Noll
59 * added MUST_ALIGN for Sparc and other RISC architectures
60 * use must_align.c to automatically determine if MUST_ALIGN is required
62 * increased test to 64 megabytes due to increased performance
64 * Version 2.4: non-existent version
66 * Version 2.5: non-existent version
68 * Version 2.6: 10 Jan 1994 Landon Curt Noll
69 * Merged the shs and md5 Makefiles to build both in the same directory
70 * Bumped version to 2.6 to match level to shs
71 * Test suite header now says md5 (not MD5)
72 * Minor performance improvements
74 * Version 2.7: 14 Jan 1994 Landon Curt Noll
76 * chunk is now 64 bytes, block is determined by blocking factor
77 * magic 64 and 64 related values defined in terms of #defines
78 * fixed bit count carry bug
79 * fixed writable strings test bug
81 * Version 2.8: 22 Jan 1994 Landon Curt Noll
83 * count bytes in driver, convert to 64 bit count in final transform
84 * handle read errors and EOF better
85 * prefix strings not multiple of 64 bytes in length do not slow down hash
86 * renumbered exit codes
87 * fixed dual digest split bug
89 * Version 2.9: 05 Feb 1994 Landon Curt Noll
90 * prep for beta release
92 * Version 2.10: 25 Mar 1994 Landon Curt Noll
93 * must_align catchs signal to detect misaligned access
94 * malloc type not declared
96 * Version 2.11: 09 Mar 1995 Landon Curt Noll
97 * Moved stream and file routines to md5io.c.
99 * Version 2.12: 17 Nov 1995 Landon Curt Noll
102 * Version 3.1: non-existent version
104 * Version 4.1: 13 Aug 2006 Landon Curt Noll
105 * Bump to version 4.1 to match the version of SHA and SHA-1 drvr files.
107 * Fixed all known compile warnings.
108 * Allow access to the internal hash transform function (to help code)
109 * that makes direct use of the cryprographic hash source.
110 * Improved the way -v prints version. Now -v prints the RCS version,
111 * not version listed in the above comment.
117 #include <sys/types.h>
118 #include <sys/stat.h>
119 #include <sys/time.h>
120 #include <sys/resource.h>
124 /* size of test in megabytes */
127 /* number of chunks to process */
128 #define TEST_CHUNKS (TEST_MEG*1024*1024/MD5_READSIZE)
130 /* MD5 test suite strings */
131 #define ENTRY(str) {(BYTE *)str, NULL, sizeof(str)-1}
133 BYTE
*ro_data
; /* read only string data or NULL to test */
134 BYTE
*data
; /* data or NULL to test */
135 int len
; /* length of data */
136 } MD5_test_data
[] = {
140 ENTRY("message digest"),
141 ENTRY("abcdefghijklmnopqrstuvwxyz"),
142 ENTRY("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"),
143 ENTRY("12345678901234567890123456789012345678901234567890123456789012345678901234567890")
145 #define MAX_MD5_TEST_DATA ((int)(sizeof(MD5_test_data)/sizeof(MD5_test_data[0])))
147 /* MD5 test filenames */
148 char *MD5_test_file
[] = {
152 #define MAX_MD5_TEST_FILE ((int)(sizeof(MD5_test_file)/sizeof(MD5_test_file[0])))
154 /* where the test files are located by default */
159 /* Prototypes of the static functions */
160 static void MD5Output(char*, int, MD5_CTX
*);
161 static int MD5PreFileRead(char*, BYTE
**);
162 static void MD5TestSuite(void);
163 static void MD5Help(void);
165 /* global variables */
166 static int c_flag
= 0; /* 1 => print C style digest with leading 0x */
167 int i_flag
= 0; /* 1 => process inode & filename */
168 int q_flag
= 0; /* 1 => print the digest only */
169 int dot_zero
= 0; /* 1 => print .0 after the digest */
170 int debug
= 0; /* 1 => add debug */
171 char *program
= "<program name unknown>"; /* our name */
175 * MD5Output - output the digest
178 * str print string after digest, NULL => none
179 * quot 1 => surround str with a double quotes
183 MD5Output(char *str
, int quot
, MD5_CTX
*dig
)
186 * finalize the digest
194 if (str
&& !q_flag
) {
196 printf(" \"%s\"\n", str
);
198 printf(" %s\n", str
);
208 * MD5Print - print a digest in hex
210 * Prints message digest buffer in MD5Info as 40 hexadecimal digits. Order is
211 * from low-order byte to high-order byte of digest. Each byte is printed
212 * with high-order hexadecimal digit first.
214 * If -c, then print a leading "0x". If -i, then print a trailing ".0".
217 MD5Print(MD5_CTX
*MD5Info
)
224 for (i
= 0; i
< 16; i
++) {
225 printf ("%02x", MD5Info
->digest
[i
]);
234 * A time trial routine, to measure the speed of MD5.
236 * Measures user time required to digest TEST_MEG megabytes of characters.
241 BYTE data
[MD5_READSIZE
]; /* test buffer */
242 MD5_CTX MD5Info
; /* hash state */
243 struct rusage start
; /* test start time */
244 struct rusage stop
; /* test end time */
245 double usrsec
; /* duration of test in user seconds */
248 /* initialize test data */
249 for (i
= 0; i
< MD5_READSIZE
; i
++)
250 data
[i
] = (BYTE
)(i
& 0xFF);
254 printf("md5 time trial for %d megabytes of test data ...", TEST_MEG
);
257 getrusage(RUSAGE_SELF
, &start
);
259 /* digest data in MD5_READSIZE byte chunk */
261 for (i
=0; i
< TEST_CHUNKS
; ++i
) {
262 MD5fullUpdate(&MD5Info
, data
, MD5_READSIZE
);
264 MD5COUNT(&MD5Info
, MD5_READSIZE
*TEST_CHUNKS
);
267 /* stop timer, get time difference */
268 getrusage(RUSAGE_SELF
, &stop
);
269 usrsec
= (stop
.ru_utime
.tv_sec
- start
.ru_utime
.tv_sec
) +
270 (double)(stop
.ru_utime
.tv_usec
- start
.ru_utime
.tv_usec
)/1000000.0;
278 printf(" is digest of test data\n");
279 printf("user seconds to process test data: %.2f\n", usrsec
);
280 printf("characters processed per user second: %d\n",
281 (int)((double)TEST_MEG
*1024.0*1024.0/usrsec
));
287 * Runs a standard suite of test data.
292 struct MD5_test
*t
; /* current MD5 test */
293 struct stat buf
; /* stat of a test file */
294 MD5_CTX digest
; /* test digest */
295 char **f
; /* current file being tested */
299 * copy our test strings into writable data
301 for (i
=0, t
=MD5_test_data
; i
< MAX_MD5_TEST_DATA
; ++i
, ++t
) {
302 if (t
->ro_data
!= NULL
) {
303 t
->data
= (BYTE
*)malloc(t
->len
+ 1);
304 if (t
->data
== NULL
) {
305 fprintf(stderr
, "%s: malloc #4 failed\n", program
);
308 strcpy((char *)t
->data
, (char *)t
->ro_data
);
315 puts("md5 test suite results:");
318 * find all of the test files
320 for (i
=0, f
=MD5_test_file
; i
< MAX_MD5_TEST_FILE
; ++i
, ++f
) {
321 if (stat(*f
, &buf
) < 0) {
322 /* no file1 in this directory, cd to the test suite directory */
323 if (chdir(TLIB
) < 0) {
326 "%s: cannot find %s or %s/%s\n", program
, *f
, TLIB
, *f
);
333 * try all combinations of test strings as prefixes and data
335 for (i
=0, t
=MD5_test_data
; i
< MAX_MD5_TEST_DATA
; ++i
, ++t
) {
337 MD5Update(&digest
, t
->data
, t
->len
);
338 MD5COUNT(&digest
, t
->len
);
339 MD5Output((char *)(t
->data
), 1, &digest
);
343 * try the files with all test strings as prefixes
345 for (i
=0, f
=MD5_test_file
; i
< MAX_MD5_TEST_FILE
; ++i
, ++f
) {
347 MD5File(NULL
, 0, *f
, 0, &digest
);
348 MD5Output(*f
, 0, &digest
);
355 * MD5PreFileRead - read and process a prepend file
358 * pre_file form pre_str from file pre_file
359 * buf pointer to pre_str pointer
361 * Returns the length of pre_str, and modifies pre_str to
362 * point at the malloced prepend data.
365 MD5PreFileRead(char *pre_file
, BYTE
**buf
)
367 struct stat statbuf
; /* stat for pre_file */
368 int pre_len
; /* length of pre_file to be used */
369 int bytes
; /* bytes read from pre_file */
370 FILE *pre
; /* pre_file descriptor */
372 /* obtain the length that we will use */
373 if (stat(pre_file
, &statbuf
) < 0) {
374 fprintf(stderr
, "%s: unpable to find prepend file %s\n",
378 pre_len
= statbuf
.st_size
;
379 if (pre_len
> MD5_MAX_PRE_FILE
) {
380 /* don't use beyond MD5_MAX_PRE_FILE in size */
381 pre_len
= MD5_MAX_PRE_FILE
;
384 /* malloc our pre string */
385 *buf
= (BYTE
*)malloc(pre_len
+1);
387 fprintf(stderr
, "%s: malloc #3 failed\n", program
);
391 /* open our pre_file */
392 pre
= fopen(pre_file
, "rb");
394 fprintf(stderr
, "%s: unable to open prepend file %s\n",
399 /* read our pre_file data */
400 bytes
= fread((char *)(*buf
), 1, pre_len
, pre
);
401 if (bytes
!= pre_len
) {
403 "%s: unable to read %d bytes from prepend file %s\n",
404 program
, pre_len
, pre_file
);
408 /* return our length */
414 * MD5Help - print MD5 help message and exit
420 "%s [-cdhiqtx][-p str][-P str][-s str] [file ...]\n",
423 " -c print C style digests with a leading 0x\n");
425 " -d dual digests of even and odd indexed bytes\n");
427 " -h prints this message\n");
429 " -i process inode and filename as well as file data\n");
431 " -p str prepend str to data before digesting\n");
433 " -P str prepend the file 'str' to data before digesting\n");
435 " -q print only the digest\n");
437 " -s str prints digest and contents of string\n");
439 " -t prints time statistics for %dM chars\n", TEST_MEG
);
441 " -v print version\n");
443 " -x execute an extended standard suite of test data\n");
445 " file print digest and name of file\n");
447 " (no args) print digest of stdin\n");
456 main(int argc
, char *argv
[])
458 MD5_CTX digest
; /* our current digest */
459 BYTE
*pre_str
= NULL
; /* pre-process this data first */
460 char *pre_file
= NULL
; /* pre-process this file first */
461 char *data_str
= NULL
; /* data is this string, not a file */
462 UINT pre_str_len
; /* length of pre_str or pre_file */
463 UINT data_str_len
; /* length of data_str */
464 int d_flag
= 0; /* 1 => dual digest mode */
465 int t_flag
= 0; /* 1 => -t was given */
466 int x_flag
= 0; /* 1 => -x was given */
467 extern char *optarg
; /* argument to option */
468 extern int optind
; /* option index */
475 while ((c
= getopt(argc
, argv
, "cdihp:P:qs:tvx")) != -1) {
491 pre_str
= (BYTE
*)optarg
;
506 printf("%s: version %s\n", program
, MD5_VERSION
);
516 if (data_str
&& optind
!= argc
) {
517 fprintf(stderr
, "%s: -s is not compatible with digesting files\n",
521 if (i_flag
&& optind
== argc
) {
522 fprintf(stderr
, "%s: -i works only on filenames\n", program
);
527 * process -x if needed
539 * process -t if needed
547 * process -P or -p if needed
549 if (pre_str
&& pre_file
) {
550 fprintf(stderr
, "%s: -p and -P conflict\n", program
);
554 pre_str_len
= MD5PreFileRead(pre_file
, &pre_str
);
555 } else if (pre_str
) {
556 pre_str_len
= strlen((char *)pre_str
);
562 * if -d, perform dual digest processing instead
565 dualMain(argc
, argv
, pre_str
, pre_str_len
, data_str
);
568 * if no -d, process string, stdin or files
573 * case: digest a string
575 if (data_str
!= NULL
) {
576 data_str_len
= strlen(data_str
);
578 MD5Update(&digest
, pre_str
, pre_str_len
);
579 MD5Update(&digest
, (BYTE
*)data_str
, data_str_len
);
580 MD5COUNT(&digest
, pre_str_len
+ data_str_len
);
581 MD5Output(data_str
, 1, &digest
);
586 } else if (optind
== argc
) {
588 MD5Stream(pre_str
, pre_str_len
, stdin
, &digest
);
589 MD5Output(NULL
, 0, &digest
);
598 for (; optind
< argc
; optind
++) {
600 MD5File(pre_str
, pre_str_len
, argv
[optind
], i_flag
, &digest
);
601 MD5Output(argv
[optind
], 0, &digest
);