2 * md5dual - md5 dual digest code
4 * @(#) $Revision: 13.1 $
5 * @(#) $Id: md5dual.c,v 13.1 2006/08/14 03:16:33 chongo Exp $
6 * @(#) $Source: /usr/local/src/cmd/hash/RCS/md5dual.c,v $
8 * Split our data into even and odd byte index streams, digest them both
9 * and output the digests, space separated on a line with a 0x prefix.
11 * This file was written by Landon Curt Noll.
13 * This code has been placed in the public domain. Please do not
14 * copyright this code.
16 * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO
17 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MER-
18 * CHANTABILITY AND FITNESS. IN NO EVENT SHALL LANDON CURT
19 * NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
20 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
21 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
22 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
23 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 * chongo (was here) /\oo/\
26 * http://www.isthe.com/chongo/index.html
28 * Share and enjoy! :-)
30 * See md5drvr.c for version and modification history.
33 char *MD5dual_what
="%Z%"; /* #(@) if checked in */
41 /* static declarations */
42 static void dualData(BYTE
*, UINT
, BYTE
*, UINT
, MD5_CTX
*, MD5_CTX
*);
43 static void dualStream(BYTE
*, UINT
, FILE*, MD5_CTX
*, MD5_CTX
*);
44 static void dualFile(BYTE
*, UINT
, char*, MD5_CTX
*, MD5_CTX
*);
45 static void dualOutput(char*, int, MD5_CTX
*, MD5_CTX
*);
47 /* dual test suite strings */
48 #define ENTRY(str) {(BYTE *)str, NULL, sizeof(str)-1}
50 BYTE
*ro_data
; /* read only string data or NULL to test */
51 BYTE
*data
; /* data or NULL to test */
52 int len
; /* length of data */
53 } dual_test_data
[] = {
58 ENTRY("aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz"),
59 ENTRY("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"),
60 ENTRY("chongo <Ich bin, du bist, aber ein Yit ist nicht!!! :-)> /\\../\\"),
61 ENTRY("123456789 123456789 123456789 123456789 123456789 123456789 1234"),
62 ENTRY("a123456789 123456789 123456789 123456789 123456789 123456789 1234")
64 #define MAX_DUAL_TEST_DATA (sizeof(dual_test_data)/sizeof(dual_test_data[0]))
66 /* dual test filenames */
67 char *dual_test_file
[] = {
73 #define MAX_DUAL_TEST_FILE (sizeof(dual_test_file)/sizeof(dual_test_file[0]))
75 /* where the test files are located by default */
82 * dualData - divide data into alternating bytes and digest both halves
85 * pre_str string prefix or NULL
86 * pre_len length of pre_str
87 * inString string to digest
88 * in_len length of inString
89 * even even byte digest
93 dualData(BYTE
*pre_str
, UINT pre_len
, BYTE
*inString
,
94 UINT in_len
, MD5_CTX
*even
, MD5_CTX
*odd
)
96 int len
; /* total length of pre_str and inString */
97 BYTE
*even_buf
; /* even byte array */
98 BYTE
*odd_buf
; /* odd byte array */
99 int indx
; /* byte stream index */
106 len
= (pre_str
== NULL
) ? 0 : pre_len
;
107 len
+= (inString
== NULL
) ? 0 : in_len
;
108 /* no strings, quick return */
112 /* only 1 byte, process now and return */
115 MD5Update(even
, (BYTE
*)pre_str
, 1);
118 MD5Update(odd
, (BYTE
*)inString
, 1);
125 * malloc both string halves
127 odd_buf
= (BYTE
*)malloc(len
/2);
128 if (odd_buf
== NULL
) {
129 fprintf(stderr
, "%s: bad malloc #1\n", program
);
132 even_buf
= (BYTE
*)malloc((len
+1)/2);
133 if (even_buf
== NULL
) {
134 fprintf(stderr
, "%s: bad malloc #2\n", program
);
139 * divide the pre-string
142 if (pre_str
!= NULL
) {
143 for (p
=pre_str
, i
=0; i
< pre_len
; ++indx
, ++i
, ++p
) {
145 odd_buf
[indx
>>1] = *p
;
147 even_buf
[indx
>>1] = *p
;
155 if (inString
!= NULL
) {
156 for (p
=inString
, i
=0; i
< in_len
; ++indx
, ++i
, ++p
) {
158 odd_buf
[indx
>>1] = *p
;
160 even_buf
[indx
>>1] = *p
;
168 MD5Update(even
, even_buf
, (len
+1)/2);
169 MD5COUNT(even
, (len
+1)/2);
170 MD5Update(odd
, odd_buf
, len
/2);
171 MD5COUNT(odd
, len
/2);
182 * dualStream - divide a Stream into alternating bytes and digest both halves
185 * pre_str data prefix or NULL
186 * pre_len length of pre_str
187 * stream the stream to process
188 * even even byte digest
189 * odd odd byte digest
192 dualStream(BYTE
*pre_str
, UINT pre_len
, FILE *stream
,
193 MD5_CTX
*even
, MD5_CTX
*odd
)
195 BYTE data
[2*MD5_READSIZE
]; /* our read buffer */
196 BYTE even_buf
[MD5_READSIZE
]; /* even half */
197 BYTE odd_buf
[MD5_READSIZE
]; /* off half */
198 int bytes
; /* bytes last read */
199 int epartial
; /* 1 => even partial chunk */
200 int opartial
; /* 1 => odd partial chunk */
201 int elen
; /* length of even_buf */
202 int olen
; /* length of odd_buf */
207 * pre-process prefix if needed
209 if (pre_str
!= NULL
) {
210 dualData(pre_str
, pre_len
, NULL
, 0, even
, odd
);
214 * determine if either half has a partial chunk
216 if (even
->datalen
> 0) {
221 if (odd
->datalen
> 0) {
228 * process the contents of the file
230 while ((bytes
= fread((char *)data
, 1, MD5_READSIZE
*2, stream
)) > 0) {
233 * split bytes into two halves
235 for (i
=0, olen
=0, p
=(BYTE
*)data
; i
< bytes
-1; i
+=2, ++olen
, p
+=2) {
237 odd_buf
[olen
] = *(p
+1);
240 even_buf
[olen
] = data
[bytes
-1];
250 if (even
->datalen
== 0 && (elen
&(MD5_CHUNKSIZE
-1)) == 0) {
251 MD5fullUpdate(even
, even_buf
, elen
);
254 MD5Update(even
, even_buf
, elen
);
256 } else if ((elen
&(MD5_CHUNKSIZE
-1)) == 0) {
257 MD5fullUpdate(even
, even_buf
, elen
);
259 MD5Update(even
, even_buf
, elen
);
262 MD5COUNT(even
, elen
);
268 if (odd
->datalen
== 0 && (olen
&(MD5_CHUNKSIZE
-1)) == 0) {
269 MD5fullUpdate(odd
, odd_buf
, olen
);
272 MD5Update(odd
, odd_buf
, olen
);
274 } else if ((olen
&(MD5_CHUNKSIZE
-1)) == 0) {
275 MD5fullUpdate(odd
, odd_buf
, olen
);
277 MD5Update(odd
, odd_buf
, olen
);
286 * dualFile - divide a file into alternating bytes and digest both halves
289 dualFile(pre_str
, pre_len
, filename
, even
, odd
)
290 BYTE
*pre_str
; /* string prefix or NULL */
291 UINT pre_len
; /* length of pre_str */
292 char *filename
; /* the filename to process */
293 MD5_CTX
*even
; /* even byte digest */
294 MD5_CTX
*odd
; /* odd byte digest */
296 FILE *inFile
; /* the open file stream */
297 struct stat buf
; /* stat or lstat of file */
298 struct md5_stat hashbuf
; /* stat data to digest */
299 struct md5_stat hashlbuf
; /* lstat data to digest */
304 inFile
= fopen(filename
, "rb");
305 if (inFile
== NULL
) {
306 fprintf(stderr
, "%s: cannot open %s: ", program
, filename
);
312 * pre-process prefix if needed
314 if (pre_str
== NULL
) {
316 dualData(NULL
, 0, (BYTE
*)filename
, strlen(filename
), even
, odd
);
320 dualData(pre_str
, pre_len
, (BYTE
*)filename
, strlen(filename
),
323 dualData(pre_str
, pre_len
, NULL
, 0, even
, odd
);
328 * digest file stat and lstat
331 if (fstat(fileno(inFile
), &buf
) < 0) {
332 printf("%s can't be stated.\n", filename
);
335 hashbuf
.stat_dev
= buf
.st_dev
;
336 hashbuf
.stat_ino
= buf
.st_ino
;
337 hashbuf
.stat_mode
= buf
.st_mode
;
338 hashbuf
.stat_nlink
= buf
.st_nlink
;
339 hashbuf
.stat_uid
= buf
.st_uid
;
340 hashbuf
.stat_gid
= buf
.st_gid
;
341 hashbuf
.stat_size
= buf
.st_size
;
342 hashbuf
.stat_mtime
= buf
.st_mtime
;
343 hashbuf
.stat_ctime
= buf
.st_ctime
;
344 if (lstat(filename
, &buf
) < 0) {
345 printf("%s can't be lstated.\n", filename
);
348 hashlbuf
.stat_dev
= buf
.st_dev
;
349 hashlbuf
.stat_ino
= buf
.st_ino
;
350 hashlbuf
.stat_mode
= buf
.st_mode
;
351 hashlbuf
.stat_nlink
= buf
.st_nlink
;
352 hashlbuf
.stat_uid
= buf
.st_uid
;
353 hashlbuf
.stat_gid
= buf
.st_gid
;
354 hashlbuf
.stat_size
= buf
.st_size
;
355 hashlbuf
.stat_mtime
= buf
.st_mtime
;
356 hashlbuf
.stat_ctime
= buf
.st_ctime
;
357 dualData((BYTE
*)&hashbuf
, sizeof(hashbuf
), (BYTE
*)&hashlbuf
,
358 sizeof(hashlbuf
), even
, odd
);
361 * pad both halves with zeros to process file data faster
363 if (even
->datalen
> 0) {
364 MD5Update(even
, (BYTE
*)md5_zero
, MD5_CHUNKSIZE
- even
->datalen
);
365 MD5COUNT(even
, MD5_CHUNKSIZE
- even
->datalen
);
367 if (odd
->datalen
> 0) {
368 MD5Update(odd
, (BYTE
*)md5_zero
, MD5_CHUNKSIZE
- odd
->datalen
);
369 MD5COUNT(odd
, MD5_CHUNKSIZE
- odd
->datalen
);
374 * process the data stream
376 dualStream(NULL
, 0, inFile
, even
, odd
);
382 * dualOutput - output the dual digests
385 * str print string after digest, NULL => none
386 * quot 1 => surround str with a double quotes
387 * even even byte digest
388 * odd odd byte digest
391 dualOutput(char *str
, int quot
, MD5_CTX
*even
, MD5_CTX
*odd
)
400 * print the 320 bit hex value
405 if (str
&& !q_flag
) {
407 printf(" \"%s\"\n", str
);
409 printf(" %s\n", str
);
418 * dualTest - MD5 dual test suite
423 struct dual_test
*t
; /* current dual test */
424 struct dual_test
*p
; /* current dual pre-string test */
425 struct stat buf
; /* stat of a test file */
426 MD5_CTX even_dig
; /* even byte digest */
427 MD5_CTX odd_dig
; /* odd byte digest */
428 char **f
; /* current file being tested */
433 * copy our test strings into writable data
435 for (i
=0, t
=dual_test_data
; i
< MAX_DUAL_TEST_DATA
; ++i
, ++t
) {
436 if (t
->ro_data
!= NULL
) {
437 t
->data
= (BYTE
*)malloc(t
->len
+ 1);
438 if (t
->data
== NULL
) {
439 fprintf(stderr
, "%s: malloc #5 failed\n", program
);
442 strcpy((char *)t
->data
, (char *)t
->ro_data
);
447 * find all of the test files
449 for (i
=0, f
=dual_test_file
; i
< MAX_DUAL_TEST_FILE
; ++i
, ++f
) {
450 if (stat(*f
, &buf
) < 0) {
451 /* no file1 in this directory, cd to the test suite directory */
452 if (chdir(TLIB
) < 0) {
455 "%s: cannot find %s or %s/%s\n", program
, *f
, TLIB
, *f
);
462 * try all combinations of test strings as prefixes and data
464 for (i
=0, t
=dual_test_data
; i
< MAX_DUAL_TEST_DATA
; ++i
, ++t
) {
465 for (j
=0, p
=dual_test_data
; j
< MAX_DUAL_TEST_DATA
; ++j
, ++p
) {
466 printf("pre:%d data:%d\n", i
, j
);
469 dualData(p
->data
, p
->len
, t
->data
, t
->len
, &even_dig
, &odd_dig
);
470 dualOutput(NULL
, 0, &even_dig
, &odd_dig
);
475 * try the files with all test strings as prefixes
477 for (i
=0, p
=dual_test_data
; i
< MAX_DUAL_TEST_DATA
; ++i
, ++p
) {
478 for (j
=0, f
=dual_test_file
; j
< MAX_DUAL_TEST_FILE
; ++j
, ++f
) {
479 printf("pre:%d file:%s\n", i
, *f
);
482 dualFile(p
->data
, p
->len
, *f
, &even_dig
, &odd_dig
);
483 dualOutput(NULL
, 0, &even_dig
, &odd_dig
);
491 * dualMain - main driver of MD5 dual routines
494 * argc arg count left after getopt
495 * argv args left after getopt
496 * pre_str pre-process this data first
497 * pre_len length of pre_str
498 * data_str data is this string, not a file
501 dualMain(argc
, argv
, pre_str
, pre_len
, data_str
)
502 int argc
; /* arg count left after getopt */
503 char **argv
; /* args left after getopt */
504 BYTE
*pre_str
; /* pre-process this data first */
505 UINT pre_len
; /* length of pre_str */
506 char *data_str
; /* data is this string, not a file */
508 extern int optind
; /* option index */
509 MD5_CTX even_dig
; /* even byte digest */
510 MD5_CTX odd_dig
; /* odd byte digest */
513 * case: initialize both halves
521 if (data_str
!= NULL
) {
522 dualData(pre_str
, pre_len
, (BYTE
*)data_str
, strlen(data_str
),
523 &even_dig
, &odd_dig
);
524 dualOutput(data_str
, 1, &even_dig
, &odd_dig
);
529 } else if (optind
== argc
) {
530 dualStream(pre_str
, pre_len
, stdin
, &even_dig
, &odd_dig
);
531 dualOutput(NULL
, 0, &even_dig
, &odd_dig
);
540 for (; optind
< argc
; optind
++) {
541 dualFile(pre_str
, pre_len
, argv
[optind
], &even_dig
, &odd_dig
);
542 dualOutput(argv
[optind
], 0, &even_dig
, &odd_dig
);