md5.c revision 110840
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#ifndef lint
21static const char rcsid[] =
22  "$FreeBSD: head/sbin/md5/md5.c 110840 2003-02-14 04:48:06Z silby $";
23#endif /* not lint */
24
25#include <sys/types.h>
26#include <sys/time.h>
27#include <sys/resource.h>
28#include <err.h>
29#include <md5.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <time.h>
34#include <unistd.h>
35
36/*
37 * Length of test block, number of test blocks.
38 */
39#define TEST_BLOCK_LEN 10000
40#define TEST_BLOCK_COUNT 100000
41
42int qflag;
43int rflag;
44int sflag;
45
46static void MDString(const char *);
47static void MDTimeTrial(void);
48static void MDTestSuite(void);
49static void MDFilter(int);
50static void usage(void);
51
52/* Main driver.
53
54Arguments (may be any combination):
55  -sstring - digests string
56  -t       - runs time trial
57  -x       - runs test script
58  filename - digests file
59  (none)   - digests standard input
60 */
61int
62main(int argc, char *argv[])
63{
64	int     ch;
65	char   *p;
66	char	buf[33];
67
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			else
101				if (qflag)
102					printf("%s\n", p);
103				else if (rflag)
104					printf("%s %s\n", p, *argv);
105				else
106					printf("MD5 (%s) = %s\n", *argv, p);
107		} while (*++argv);
108	} else if (!sflag && (optind == 1 || qflag || rflag))
109		MDFilter(0);
110
111	return (0);
112}
113/*
114 * Digests a string and prints the result.
115 */
116static void
117MDString(const char *string)
118{
119	size_t len = strlen(string);
120	char buf[33];
121
122	if (qflag)
123		printf("%s\n", MD5Data(string, len, buf));
124	else if (rflag)
125		printf("%s \"%s\"\n", MD5Data(string, len, buf), string);
126	else
127		printf("MD5 (\"%s\") = %s\n", string, MD5Data(string, len, buf));
128}
129/*
130 * Measures the time to digest TEST_BLOCK_COUNT TEST_BLOCK_LEN-byte blocks.
131 */
132static void
133MDTimeTrial(void)
134{
135	MD5_CTX context;
136	struct rusage before, after;
137	struct timeval total;
138	float seconds;
139	unsigned char block[TEST_BLOCK_LEN];
140	unsigned int i;
141	char   *p, buf[33];
142
143	printf
144	    ("MD5 time trial. Digesting %d %d-byte blocks ...",
145	    TEST_BLOCK_COUNT, TEST_BLOCK_LEN);
146	fflush(stdout);
147
148	/* Initialize block */
149	for (i = 0; i < TEST_BLOCK_LEN; i++)
150		block[i] = (unsigned char) (i & 0xff);
151
152	/* Start timer */
153	getrusage(0, &before);
154
155	/* Digest blocks */
156	MD5Init(&context);
157	for (i = 0; i < TEST_BLOCK_COUNT; i++)
158		MD5Update(&context, block, TEST_BLOCK_LEN);
159	p = MD5End(&context,buf);
160
161	/* Stop timer */
162	getrusage(0, &after);
163	timersub(&after.ru_utime, &before.ru_utime, &total);
164	seconds = total.tv_sec + (float) total.tv_usec / 1000000;
165
166	printf(" done\n");
167	printf("Digest = %s", p);
168	printf("\nTime = %f seconds\n", seconds);
169	printf
170	    ("Speed = %f bytes/second\n",
171	    (float) TEST_BLOCK_LEN * (float) TEST_BLOCK_COUNT / seconds);
172}
173/*
174 * Digests a reference suite of strings and prints the results.
175 */
176
177#define MD5TESTCOUNT 8
178
179char *MDTestInput[] = {
180	"",
181	"a",
182	"abc",
183	"message digest",
184	"abcdefghijklmnopqrstuvwxyz",
185	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
186	"12345678901234567890123456789012345678901234567890123456789012345678901234567890",
187	"MD5 has not yet (2001-09-03) been broken, but sufficient attacks have been made that its security is in some doubt"
188};
189
190char *MDTestOutput[MD5TESTCOUNT] = {
191	"d41d8cd98f00b204e9800998ecf8427e",
192	"0cc175b9c0f1b6a831c399e269772661",
193	"900150983cd24fb0d6963f7d28e17f72",
194	"f96b697d7cb7938d525a2f31aaf161d0",
195	"c3fcd3d76192e4007dfb496cca67e13b",
196	"d174ab98d277d9f5a5611c2c9f419d9f",
197	"57edf4a22be3c955ac49da2e2107b67a",
198	"b50663f41d44d92171cb9976bc118538"
199};
200
201static void
202MDTestSuite(void)
203{
204	int i;
205	char buffer[33];
206
207	printf("MD5 test suite:\n");
208	for (i = 0; i < MD5TESTCOUNT; i++) {
209		MD5Data(MDTestInput[i], strlen(MDTestInput[i]), buffer);
210		printf("MD5 (\"%s\") = %s", MDTestInput[i], buffer);
211		if (strcmp(buffer, MDTestOutput[i]) == 0)
212			printf(" - verified correct\n");
213		else
214			printf(" - INCORRECT RESULT!\n");
215	}
216}
217
218/*
219 * Digests the standard input and prints the result.
220 */
221static void
222MDFilter(int tee)
223{
224	MD5_CTX context;
225	unsigned int len;
226	unsigned char buffer[BUFSIZ];
227	char buf[33];
228
229	MD5Init(&context);
230	while ((len = fread(buffer, 1, BUFSIZ, stdin))) {
231		if (tee && len != fwrite(buffer, 1, len, stdout))
232			err(1, "stdout");
233		MD5Update(&context, buffer, len);
234	}
235	printf("%s\n", MD5End(&context,buf));
236}
237
238static void
239usage(void)
240{
241
242	fprintf(stderr, "usage: md5 [-pqrtx] [-s string] [files ...]\n");
243	exit(1);
244}
245