md5.c revision 121914
1/*
2 * Derived from:
3 *
4 * MDDRIVER.C - test driver for MD2, MD4 and MD5
5 */
6
7/*
8 *  Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All
9 *  rights reserved.
10 *
11 *  RSA Data Security, Inc. makes no representations concerning either
12 *  the merchantability of this software or the suitability of this
13 *  software for any particular purpose. It is provided "as is"
14 *  without express or implied warranty of any kind.
15 *
16 *  These notices must be retained in any copies of any part of this
17 *  documentation and/or software.
18 */
19
20#include <sys/cdefs.h>
21__FBSDID("$FreeBSD: head/sbin/md5/md5.c 121914 2003-11-02 23:12:08Z se $");
22
23#include <sys/types.h>
24#include <sys/time.h>
25#include <sys/resource.h>
26#include <err.h>
27#include <md5.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <time.h>
32#include <unistd.h>
33
34/*
35 * Length of test block, number of test blocks.
36 */
37#define TEST_BLOCK_LEN 10000
38#define TEST_BLOCK_COUNT 100000
39
40int qflag;
41int rflag;
42int sflag;
43
44static void MDString(const char *);
45static void MDTimeTrial(void);
46static void MDTestSuite(void);
47static void MDFilter(int);
48static void usage(void);
49
50/* Main driver.
51
52Arguments (may be any combination):
53  -sstring - digests string
54  -t       - runs time trial
55  -x       - runs test script
56  filename - digests file
57  (none)   - digests standard input
58 */
59int
60main(int argc, char *argv[])
61{
62	int     ch;
63	char   *p;
64	char	buf[33];
65	int     failed;
66
67	failed = 0;
68	while ((ch = getopt(argc, argv, "pqrs:tx")) != -1)
69		switch (ch) {
70		case 'p':
71			MDFilter(1);
72			break;
73		case 'q':
74			qflag = 1;
75			break;
76		case 'r':
77			rflag = 1;
78			break;
79		case 's':
80			sflag = 1;
81			MDString(optarg);
82			break;
83		case 't':
84			MDTimeTrial();
85			break;
86		case 'x':
87			MDTestSuite();
88			break;
89		default:
90			usage();
91		}
92	argc -= optind;
93	argv += optind;
94
95	if (*argv) {
96		do {
97			p = MD5File(*argv, buf);
98			if (!p) {
99				warn("%s", *argv);
100				failed++;
101			} else {
102				if (qflag)
103					printf("%s\n", p);
104				else if (rflag)
105					printf("%s %s\n", p, *argv);
106				else
107					printf("MD5 (%s) = %s\n", *argv, p);
108			}
109		} while (*++argv);
110	} else if (!sflag && (optind == 1 || qflag || rflag))
111		MDFilter(0);
112
113	if (failed != 0)
114		return (1);
115
116	return (0);
117}
118/*
119 * Digests a string and prints the result.
120 */
121static void
122MDString(const char *string)
123{
124	size_t len = strlen(string);
125	char buf[33];
126
127	if (qflag)
128		printf("%s\n", MD5Data(string, len, buf));
129	else if (rflag)
130		printf("%s \"%s\"\n", MD5Data(string, len, buf), string);
131	else
132		printf("MD5 (\"%s\") = %s\n", string, MD5Data(string, len, buf));
133}
134/*
135 * Measures the time to digest TEST_BLOCK_COUNT TEST_BLOCK_LEN-byte blocks.
136 */
137static void
138MDTimeTrial(void)
139{
140	MD5_CTX context;
141	struct rusage before, after;
142	struct timeval total;
143	float seconds;
144	unsigned char block[TEST_BLOCK_LEN];
145	unsigned int i;
146	char   *p, buf[33];
147
148	printf
149	    ("MD5 time trial. Digesting %d %d-byte blocks ...",
150	    TEST_BLOCK_COUNT, TEST_BLOCK_LEN);
151	fflush(stdout);
152
153	/* Initialize block */
154	for (i = 0; i < TEST_BLOCK_LEN; i++)
155		block[i] = (unsigned char) (i & 0xff);
156
157	/* Start timer */
158	getrusage(0, &before);
159
160	/* Digest blocks */
161	MD5Init(&context);
162	for (i = 0; i < TEST_BLOCK_COUNT; i++)
163		MD5Update(&context, block, TEST_BLOCK_LEN);
164	p = MD5End(&context,buf);
165
166	/* Stop timer */
167	getrusage(0, &after);
168	timersub(&after.ru_utime, &before.ru_utime, &total);
169	seconds = total.tv_sec + (float) total.tv_usec / 1000000;
170
171	printf(" done\n");
172	printf("Digest = %s", p);
173	printf("\nTime = %f seconds\n", seconds);
174	printf
175	    ("Speed = %f bytes/second\n",
176	    (float) TEST_BLOCK_LEN * (float) TEST_BLOCK_COUNT / seconds);
177}
178/*
179 * Digests a reference suite of strings and prints the results.
180 */
181
182#define MD5TESTCOUNT 8
183
184char *MDTestInput[] = {
185	"",
186	"a",
187	"abc",
188	"message digest",
189	"abcdefghijklmnopqrstuvwxyz",
190	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
191	"12345678901234567890123456789012345678901234567890123456789012345678901234567890",
192	"MD5 has not yet (2001-09-03) been broken, but sufficient attacks have been made that its security is in some doubt"
193};
194
195char *MDTestOutput[MD5TESTCOUNT] = {
196	"d41d8cd98f00b204e9800998ecf8427e",
197	"0cc175b9c0f1b6a831c399e269772661",
198	"900150983cd24fb0d6963f7d28e17f72",
199	"f96b697d7cb7938d525a2f31aaf161d0",
200	"c3fcd3d76192e4007dfb496cca67e13b",
201	"d174ab98d277d9f5a5611c2c9f419d9f",
202	"57edf4a22be3c955ac49da2e2107b67a",
203	"b50663f41d44d92171cb9976bc118538"
204};
205
206static void
207MDTestSuite(void)
208{
209	int i;
210	char buffer[33];
211
212	printf("MD5 test suite:\n");
213	for (i = 0; i < MD5TESTCOUNT; i++) {
214		MD5Data(MDTestInput[i], strlen(MDTestInput[i]), buffer);
215		printf("MD5 (\"%s\") = %s", MDTestInput[i], buffer);
216		if (strcmp(buffer, MDTestOutput[i]) == 0)
217			printf(" - verified correct\n");
218		else
219			printf(" - INCORRECT RESULT!\n");
220	}
221}
222
223/*
224 * Digests the standard input and prints the result.
225 */
226static void
227MDFilter(int tee)
228{
229	MD5_CTX context;
230	unsigned int len;
231	unsigned char buffer[BUFSIZ];
232	char buf[33];
233
234	MD5Init(&context);
235	while ((len = fread(buffer, 1, BUFSIZ, stdin))) {
236		if (tee && len != fwrite(buffer, 1, len, stdout))
237			err(1, "stdout");
238		MD5Update(&context, buffer, len);
239	}
240	printf("%s\n", MD5End(&context,buf));
241}
242
243static void
244usage(void)
245{
246
247	fprintf(stderr, "usage: md5 [-pqrtx] [-s string] [files ...]\n");
248	exit(1);
249}
250