1/*	$OpenBSD: md5.c,v 1.98 2023/10/31 19:37:17 millert Exp $	*/
2
3/*
4 * Copyright (c) 2001,2003,2005-2007,2010,2013,2014
5 *	Todd C. Miller <millert@openbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 *
19 * Sponsored in part by the Defense Advanced Research Projects
20 * Agency (DARPA) and Air Force Research Laboratory, Air Force
21 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
22 */
23
24#include <sys/types.h>
25#include <sys/time.h>
26#include <sys/queue.h>
27#include <sys/resource.h>
28#include <netinet/in.h>
29#include <ctype.h>
30#include <err.h>
31#include <fcntl.h>
32#include <resolv.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <limits.h>
37#include <time.h>
38#include <unistd.h>
39#include <errno.h>
40
41#include <md5.h>
42#include <rmd160.h>
43#include <sha1.h>
44#include <sha2.h>
45#include <crc.h>
46
47#define STYLE_MD5	0
48#define STYLE_CKSUM	1
49#define STYLE_TERSE	2
50
51#define MAX_DIGEST_LEN	128
52
53#define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
54#define MAXIMUM(a, b)	(((a) > (b)) ? (a) : (b))
55
56union ANY_CTX {
57#if !defined(SHA2_ONLY)
58	CKSUM_CTX cksum;
59	MD5_CTX md5;
60	RMD160_CTX rmd160;
61	SHA1_CTX sha1;
62#endif /* !defined(SHA2_ONLY) */
63	SHA2_CTX sha2;
64};
65
66struct hash_function {
67	const char *name;
68	size_t digestlen;
69	int style;
70	int base64;
71	void *ctx;	/* XXX - only used by digest_file() */
72	void (*init)(void *);
73	void (*update)(void *, const unsigned char *, size_t);
74	void (*final)(unsigned char *, void *);
75	char * (*end)(void *, char *);
76	TAILQ_ENTRY(hash_function) tailq;
77} functions[] = {
78#if !defined(SHA2_ONLY)
79	{
80		"CKSUM",
81		CKSUM_DIGEST_LENGTH,
82		STYLE_CKSUM,
83		-1,
84		NULL,
85		(void (*)(void *))CKSUM_Init,
86		(void (*)(void *, const unsigned char *, size_t))CKSUM_Update,
87		(void (*)(unsigned char *, void *))CKSUM_Final,
88		(char *(*)(void *, char *))CKSUM_End
89	},
90	{
91		"MD5",
92		MD5_DIGEST_LENGTH,
93		STYLE_MD5,
94		0,
95		NULL,
96		(void (*)(void *))MD5Init,
97		(void (*)(void *, const unsigned char *, size_t))MD5Update,
98		(void (*)(unsigned char *, void *))MD5Final,
99		(char *(*)(void *, char *))MD5End
100	},
101	{
102		"RMD160",
103		RMD160_DIGEST_LENGTH,
104		STYLE_MD5,
105		0,
106		NULL,
107		(void (*)(void *))RMD160Init,
108		(void (*)(void *, const unsigned char *, size_t))RMD160Update,
109		(void (*)(unsigned char *, void *))RMD160Final,
110		(char *(*)(void *, char *))RMD160End
111	},
112	{
113		"SHA1",
114		SHA1_DIGEST_LENGTH,
115		STYLE_MD5,
116		0,
117		NULL,
118		(void (*)(void *))SHA1Init,
119		(void (*)(void *, const unsigned char *, size_t))SHA1Update,
120		(void (*)(unsigned char *, void *))SHA1Final,
121		(char *(*)(void *, char *))SHA1End
122	},
123	{
124		"SHA224",
125		SHA224_DIGEST_LENGTH,
126		STYLE_MD5,
127		0,
128		NULL,
129		(void (*)(void *))SHA224Init,
130		(void (*)(void *, const unsigned char *, size_t))SHA224Update,
131		(void (*)(unsigned char *, void *))SHA224Final,
132		(char *(*)(void *, char *))SHA224End
133	},
134#endif /* !defined(SHA2_ONLY) */
135	{
136		"SHA256",
137		SHA256_DIGEST_LENGTH,
138		STYLE_MD5,
139		0,
140		NULL,
141		(void (*)(void *))SHA256Init,
142		(void (*)(void *, const unsigned char *, size_t))SHA256Update,
143		(void (*)(unsigned char *, void *))SHA256Final,
144		(char *(*)(void *, char *))SHA256End
145	},
146#if !defined(SHA2_ONLY)
147	{
148		"SHA384",
149		SHA384_DIGEST_LENGTH,
150		STYLE_MD5,
151		0,
152		NULL,
153		(void (*)(void *))SHA384Init,
154		(void (*)(void *, const unsigned char *, size_t))SHA384Update,
155		(void (*)(unsigned char *, void *))SHA384Final,
156		(char *(*)(void *, char *))SHA384End
157	},
158	{
159		"SHA512/256",
160		SHA512_256_DIGEST_LENGTH,
161		STYLE_MD5,
162		0,
163		NULL,
164		(void (*)(void *))SHA512_256Init,
165		(void (*)(void *, const unsigned char *, size_t))SHA512_256Update,
166		(void (*)(unsigned char *, void *))SHA512_256Final,
167		(char *(*)(void *, char *))SHA512_256End
168	},
169#endif /* !defined(SHA2_ONLY) */
170	{
171		"SHA512",
172		SHA512_DIGEST_LENGTH,
173		STYLE_MD5,
174		0,
175		NULL,
176		(void (*)(void *))SHA512Init,
177		(void (*)(void *, const unsigned char *, size_t))SHA512Update,
178		(void (*)(unsigned char *, void *))SHA512Final,
179		(char *(*)(void *, char *))SHA512End
180	},
181	{
182		NULL,
183	}
184};
185
186TAILQ_HEAD(hash_list, hash_function);
187
188void digest_end(const struct hash_function *, void *, char *, size_t, int);
189int  digest_file(const char *, struct hash_list *, int);
190void digest_print(const struct hash_function *, const char *, const char *);
191#if !defined(SHA2_ONLY)
192int  digest_filelist(const char *, struct hash_function *, int, char **);
193void digest_printstr(const struct hash_function *, const char *, const char *);
194void digest_string(char *, struct hash_list *);
195void digest_test(struct hash_list *);
196void digest_time(struct hash_list *, int);
197#endif /* !defined(SHA2_ONLY) */
198void hash_insert(struct hash_list *, struct hash_function *, int);
199void usage(void) __attribute__((__noreturn__));
200
201extern char *__progname;
202int qflag = 0;
203FILE *ofile = NULL;
204
205int
206main(int argc, char **argv)
207{
208	struct hash_function *hf, *hftmp;
209	struct hash_list hl;
210	size_t len;
211	char *cp, *input_string, *selective_checklist;
212	const char *optstr;
213	int fl, error, base64;
214	int bflag, cflag, pflag, rflag, tflag, xflag;
215
216	if (pledge("stdio rpath wpath cpath", NULL) == -1)
217		err(1, "pledge");
218
219	TAILQ_INIT(&hl);
220	input_string = NULL;
221	selective_checklist = NULL;
222	error = bflag = cflag = pflag = qflag = rflag = tflag = xflag = 0;
223
224#if !defined(SHA2_ONLY)
225	if (strcmp(__progname, "cksum") == 0)
226		optstr = "a:bC:ch:pqrs:tx";
227	else
228#endif /* !defined(SHA2_ONLY) */
229		optstr = "bC:ch:pqrs:tx";
230
231	/* Check for -b option early since it changes behavior. */
232	while ((fl = getopt(argc, argv, optstr)) != -1) {
233		switch (fl) {
234		case 'b':
235			bflag = 1;
236			break;
237		case '?':
238			usage();
239		}
240	}
241	optind = 1;
242	optreset = 1;
243	while ((fl = getopt(argc, argv, optstr)) != -1) {
244		switch (fl) {
245		case 'a':
246			while ((cp = strsep(&optarg, " \t,")) != NULL) {
247				if (*cp == '\0')
248					continue;
249				base64 = -1;
250				for (hf = functions; hf->name != NULL; hf++) {
251					len = strlen(hf->name);
252					if (strncasecmp(cp, hf->name, len) != 0)
253						continue;
254					if (cp[len] == '\0') {
255						if (hf->base64 != -1)
256							base64 = bflag;
257						break;	/* exact match */
258					}
259					if (cp[len + 1] == '\0' &&
260					    (cp[len] == 'b' || cp[len] == 'x')) {
261						base64 =
262						    cp[len] == 'b' ?  1 : 0;
263						break;	/* match w/ suffix */
264					}
265				}
266				if (hf->name == NULL) {
267					warnx("unknown algorithm \"%s\"", cp);
268					usage();
269				}
270				if (hf->base64 == -1 && base64 != -1) {
271					warnx("%s doesn't support %s",
272					    hf->name,
273					    base64 ? "base64" : "hex");
274					usage();
275				}
276				/* Check for dupes. */
277				TAILQ_FOREACH(hftmp, &hl, tailq) {
278					if (hftmp->base64 == base64 &&
279					    strcmp(hf->name, hftmp->name) == 0)
280						break;
281				}
282				if (hftmp == NULL)
283					hash_insert(&hl, hf, base64);
284			}
285			break;
286		case 'b':
287			/* has already been parsed */
288			break;
289		case 'h':
290			ofile = fopen(optarg, "w");
291			if (ofile == NULL)
292				err(1, "%s", optarg);
293			break;
294#if !defined(SHA2_ONLY)
295		case 'C':
296			selective_checklist = optarg;
297			break;
298		case 'c':
299			cflag = 1;
300			break;
301#endif /* !defined(SHA2_ONLY) */
302		case 'p':
303			pflag = 1;
304			break;
305		case 'q':
306			qflag = 1;
307			break;
308		case 'r':
309			rflag = 1;
310			break;
311		case 's':
312			input_string = optarg;
313			break;
314		case 't':
315			tflag++;
316			break;
317		case 'x':
318			xflag = 1;
319			break;
320		default:
321			usage();
322		}
323	}
324	argc -= optind;
325	argv += optind;
326
327	if (ofile == NULL)
328		ofile = stdout;
329
330	if (pledge("stdio rpath", NULL) == -1)
331		err(1, "pledge");
332
333	/* Most arguments are mutually exclusive */
334	fl = pflag + (tflag ? 1 : 0) + xflag + cflag + (input_string != NULL);
335	if (fl > 1 || (fl && argc && cflag == 0) || (rflag && qflag) ||
336	    (selective_checklist != NULL && argc == 0))
337		usage();
338	if (selective_checklist || cflag) {
339		if (TAILQ_FIRST(&hl) != TAILQ_LAST(&hl, hash_list))
340			errx(1, "only a single algorithm may be specified "
341			    "in -C or -c mode");
342	}
343
344	/* No algorithm specified, check the name we were called as. */
345	if (TAILQ_EMPTY(&hl)) {
346		for (hf = functions; hf->name != NULL; hf++) {
347			if (strcasecmp(hf->name, __progname) == 0)
348				break;
349		}
350		if (hf->name == NULL)
351			hf = &functions[0];	/* default to cksum */
352		hash_insert(&hl, hf, (hf->base64 == -1 ? 0 : bflag));
353	}
354
355	if ((rflag || qflag) && !cflag) {
356		const int new_style = rflag ? STYLE_CKSUM : STYLE_TERSE;
357		TAILQ_FOREACH(hf, &hl, tailq) {
358			hf->style = new_style;
359		}
360	}
361
362#if !defined(SHA2_ONLY)
363	if (tflag)
364		digest_time(&hl, tflag);
365	else if (xflag)
366		digest_test(&hl);
367	else if (input_string)
368		digest_string(input_string, &hl);
369	else if (selective_checklist) {
370		int i;
371
372		error = digest_filelist(selective_checklist, TAILQ_FIRST(&hl),
373		    argc, argv);
374		for (i = 0; i < argc; i++) {
375			if (argv[i] != NULL) {
376				warnx("%s does not exist in %s", argv[i],
377				    selective_checklist);
378				error++;
379			}
380		}
381	} else if (cflag) {
382		if (argc == 0)
383			error = digest_filelist("-", TAILQ_FIRST(&hl), 0, NULL);
384		else
385			while (argc--)
386				error += digest_filelist(*argv++,
387				    TAILQ_FIRST(&hl), 0, NULL);
388	} else
389#endif /* !defined(SHA2_ONLY) */
390	if (pflag || argc == 0)
391		error = digest_file("-", &hl, pflag);
392	else
393		while (argc--)
394			error += digest_file(*argv++, &hl, 0);
395
396	return(error ? EXIT_FAILURE : EXIT_SUCCESS);
397}
398
399void
400hash_insert(struct hash_list *hl, struct hash_function *hf, int base64)
401{
402	struct hash_function *hftmp;
403
404	hftmp = malloc(sizeof(*hftmp));
405	if (hftmp == NULL)
406		err(1, NULL);
407	*hftmp = *hf;
408	hftmp->base64 = base64;
409	TAILQ_INSERT_TAIL(hl, hftmp, tailq);
410}
411
412void
413digest_end(const struct hash_function *hf, void *ctx, char *buf, size_t bsize,
414    int base64)
415{
416	u_char *digest;
417
418	if (base64 == 1) {
419		if ((digest = malloc(hf->digestlen)) == NULL)
420			err(1, NULL);
421		hf->final(digest, ctx);
422		if (b64_ntop(digest, hf->digestlen, buf, bsize) == -1)
423			errx(1, "error encoding base64");
424		free(digest);
425	} else {
426		hf->end(ctx, buf);
427	}
428}
429
430#if !defined(SHA2_ONLY)
431void
432digest_string(char *string, struct hash_list *hl)
433{
434	struct hash_function *hf;
435	char digest[MAX_DIGEST_LEN + 1];
436	union ANY_CTX context;
437
438	TAILQ_FOREACH(hf, hl, tailq) {
439		hf->init(&context);
440		hf->update(&context, string, strlen(string));
441		digest_end(hf, &context, digest, sizeof(digest),
442		    hf->base64);
443		digest_printstr(hf, string, digest);
444	}
445}
446#endif /* !defined(SHA2_ONLY) */
447
448void
449digest_print(const struct hash_function *hf, const char *what,
450    const char *digest)
451{
452	switch (hf->style) {
453	case STYLE_MD5:
454		(void)fprintf(ofile, "%s (%s) = %s\n", hf->name, what, digest);
455		break;
456	case STYLE_CKSUM:
457		(void)fprintf(ofile, "%s %s\n", digest, what);
458		break;
459	case STYLE_TERSE:
460		(void)fprintf(ofile, "%s\n", digest);
461		break;
462	}
463}
464
465#if !defined(SHA2_ONLY)
466void
467digest_printstr(const struct hash_function *hf, const char *what,
468    const char *digest)
469{
470	switch (hf->style) {
471	case STYLE_MD5:
472		(void)fprintf(ofile, "%s (\"%s\") = %s\n", hf->name, what, digest);
473		break;
474	case STYLE_CKSUM:
475		(void)fprintf(ofile, "%s %s\n", digest, what);
476		break;
477	case STYLE_TERSE:
478		(void)fprintf(ofile, "%s\n", digest);
479		break;
480	}
481}
482#endif /* !defined(SHA2_ONLY) */
483
484int
485digest_file(const char *file, struct hash_list *hl, int echo)
486{
487	struct hash_function *hf;
488	FILE *fp;
489	size_t nread;
490	u_char data[32 * 1024];
491	char digest[MAX_DIGEST_LEN + 1];
492
493	if (strcmp(file, "-") == 0)
494		fp = stdin;
495	else if ((fp = fopen(file, "r")) == NULL) {
496		warn("cannot open %s", file);
497		return(1);
498	}
499
500	TAILQ_FOREACH(hf, hl, tailq) {
501		if ((hf->ctx = malloc(sizeof(union ANY_CTX))) == NULL)
502			err(1, NULL);
503		hf->init(hf->ctx);
504	}
505	while ((nread = fread(data, 1UL, sizeof(data), fp)) != 0) {
506		if (echo) {
507			(void)fwrite(data, nread, 1UL, stdout);
508			(void)fflush(stdout);
509			if (ferror(stdout))
510				err(1, "stdout: write error");
511		}
512		TAILQ_FOREACH(hf, hl, tailq)
513			hf->update(hf->ctx, data, nread);
514	}
515	if (ferror(fp)) {
516		warn("%s: read error", file);
517		if (fp != stdin)
518			fclose(fp);
519		TAILQ_FOREACH(hf, hl, tailq) {
520			free(hf->ctx);
521			hf->ctx = NULL;
522		}
523		return(1);
524	}
525	if (fp != stdin)
526		fclose(fp);
527	TAILQ_FOREACH(hf, hl, tailq) {
528		digest_end(hf, hf->ctx, digest, sizeof(digest), hf->base64);
529		free(hf->ctx);
530		hf->ctx = NULL;
531		if (fp == stdin)
532			fprintf(ofile, "%s\n", digest);
533		else
534			digest_print(hf, file, digest);
535	}
536	return(0);
537}
538
539#if !defined(SHA2_ONLY)
540/*
541 * Parse through the input file looking for valid lines.
542 * If one is found, use this checksum and file as a reference and
543 * generate a new checksum against the file on the filesystem.
544 * Print out the result of each comparison.
545 */
546int
547digest_filelist(const char *file, struct hash_function *defhash, int selcount,
548    char **sel)
549{
550	int found, base64, error, cmp, i;
551	size_t algorithm_max, algorithm_min;
552	const char *algorithm;
553	char *filename, *checksum, *line, *p, *tmpline;
554	char digest[MAX_DIGEST_LEN + 1];
555	ssize_t linelen;
556	FILE *listfp, *fp;
557	size_t len, linesize, nread;
558	int *sel_found = NULL;
559	u_char data[32 * 1024];
560	union ANY_CTX context;
561	struct hash_function *hf;
562
563	if (strcmp(file, "-") == 0) {
564		listfp = stdin;
565	} else if ((listfp = fopen(file, "r")) == NULL) {
566		warn("cannot open %s", file);
567		return(1);
568	}
569
570	if (sel != NULL) {
571		sel_found = calloc((size_t)selcount, sizeof(*sel_found));
572		if (sel_found == NULL)
573			err(1, NULL);
574	}
575
576	algorithm_max = algorithm_min = strlen(functions[0].name);
577	for (hf = &functions[1]; hf->name != NULL; hf++) {
578		len = strlen(hf->name);
579		algorithm_max = MAXIMUM(algorithm_max, len);
580		algorithm_min = MINIMUM(algorithm_min, len);
581	}
582
583	error = found = 0;
584	line = NULL;
585	linesize = 0;
586	while ((linelen = getline(&line, &linesize, listfp)) != -1) {
587		tmpline = line;
588		base64 = 0;
589		if (line[linelen - 1] == '\n')
590			line[linelen - 1] = '\0';
591		while (isspace((unsigned char)*tmpline))
592			tmpline++;
593
594		/*
595		 * Crack the line into an algorithm, filename, and checksum.
596		 * Lines are of the form:
597		 *  ALGORITHM (FILENAME) = CHECKSUM
598		 *
599		 * Fallback on GNU form:
600		 *  CHECKSUM  FILENAME
601		 */
602		p = strchr(tmpline, ' ');
603		if (p != NULL && *(p + 1) == '(') {
604			/* BSD form */
605			*p = '\0';
606			algorithm = tmpline;
607			len = strlen(algorithm);
608			if (len > algorithm_max || len < algorithm_min)
609				continue;
610
611			filename = p + 2;
612			p = strrchr(filename, ')');
613			if (p == NULL || strncmp(p + 1, " = ", (size_t)3) != 0)
614				continue;
615			*p = '\0';
616
617			checksum = p + 4;
618			p = strpbrk(checksum, " \t\r");
619			if (p != NULL)
620				*p = '\0';
621
622			/*
623			 * Check that the algorithm is one we recognize.
624			 */
625			for (hf = functions; hf->name != NULL; hf++) {
626				if (strcasecmp(algorithm, hf->name) == 0)
627					break;
628			}
629			if (hf->name == NULL || *checksum == '\0')
630				continue;
631		} else {
632			/* could be GNU form */
633			if ((hf = defhash) == NULL)
634				continue;
635			algorithm = hf->name;
636			checksum = tmpline;
637			if ((p = strchr(checksum, ' ')) == NULL)
638				continue;
639			if (hf->style == STYLE_CKSUM) {
640				if ((p = strchr(p + 1, ' ')) == NULL)
641					continue;
642			}
643			*p++ = '\0';
644			while (isspace((unsigned char)*p))
645				p++;
646			if (*p == '\0')
647				continue;
648			filename = p;
649			p = strpbrk(filename, "\t\r");
650			if (p != NULL)
651				*p = '\0';
652		}
653
654		if (hf->style == STYLE_MD5) {
655			/*
656			 * Check the length to see if this could be
657			 * a valid digest.  If hex, it will be 2x the
658			 * size of the binary data.  For base64, we have
659			 * to check both with and without the '=' padding.
660			 */
661			len = strlen(checksum);
662			if (len != hf->digestlen * 2) {
663				size_t len2;
664
665				if (checksum[len - 1] == '=') {
666					/* use padding */
667					len2 = 4 * ((hf->digestlen + 2) / 3);
668				} else {
669					/* no padding */
670					len2 = (4 * hf->digestlen + 2) / 3;
671				}
672				if (len != len2)
673					continue;
674				base64 = 1;
675			}
676		}
677		found = 1;
678
679		/*
680		 * If only a selection of files is wanted, proceed only
681		 * if the filename matches one of those in the selection.
682		 */
683		if (sel != NULL) {
684			for (i = 0; i < selcount; i++) {
685				if (strcmp(sel[i], filename) == 0) {
686					sel_found[i] = 1;
687					break;
688				}
689			}
690			if (i == selcount)
691				continue;
692		}
693
694		if ((fp = fopen(filename, "r")) == NULL) {
695			warn("cannot open %s", filename);
696			(void)printf("(%s) %s: %s\n", algorithm, filename,
697			    (errno == ENOENT ? "MISSING" : "FAILED"));
698			error = 1;
699			continue;
700		}
701
702		hf->init(&context);
703		while ((nread = fread(data, 1UL, sizeof(data), fp)) > 0)
704			hf->update(&context, data, nread);
705		if (ferror(fp)) {
706			warn("%s: read error", file);
707			error = 1;
708			fclose(fp);
709			continue;
710		}
711		fclose(fp);
712		digest_end(hf, &context, digest, sizeof(digest), base64);
713
714		if (base64)
715			cmp = strncmp(checksum, digest, len);
716		else
717			cmp = strcasecmp(checksum, digest);
718		if (cmp == 0) {
719			if (qflag == 0)
720				(void)printf("(%s) %s: OK\n", algorithm,
721				    filename);
722		} else {
723			(void)printf("(%s) %s: FAILED\n", algorithm, filename);
724			error = 1;
725		}
726	}
727	free(line);
728	if (ferror(listfp)) {
729		warn("%s: getline", file);
730		error = 1;
731	}
732	if (listfp != stdin)
733		fclose(listfp);
734	if (!found)
735		warnx("%s: no properly formatted checksum lines found", file);
736	if (sel_found != NULL) {
737		/*
738		 * Mark found files by setting them to NULL so that we can
739		 * detect files that are missing from the checklist later.
740		 */
741		for (i = 0; i < selcount; i++) {
742			if (sel_found[i])
743				sel[i] = NULL;
744		}
745		free(sel_found);
746	}
747	return(error || !found);
748}
749
750#define TEST_BLOCK_LEN 10000
751#define TEST_BLOCK_COUNT 10000
752
753void
754digest_time(struct hash_list *hl, int times)
755{
756	struct hash_function *hf;
757	struct rusage start, stop;
758	struct timeval res;
759	union ANY_CTX context;
760	u_int i;
761	u_char data[TEST_BLOCK_LEN];
762	char digest[MAX_DIGEST_LEN + 1];
763	double elapsed;
764	int count = TEST_BLOCK_COUNT;
765	while (--times > 0 && count < INT_MAX / 10)
766		count *= 10;
767
768	TAILQ_FOREACH(hf, hl, tailq) {
769		(void)printf("%s time trial.  Processing %d %d-byte blocks...",
770		    hf->name, count, TEST_BLOCK_LEN);
771		fflush(stdout);
772
773		/* Initialize data based on block number. */
774		for (i = 0; i < TEST_BLOCK_LEN; i++)
775			data[i] = (u_char)(i & 0xff);
776
777		getrusage(RUSAGE_SELF, &start);
778		hf->init(&context);
779		for (i = 0; i < count; i++)
780			hf->update(&context, data, (size_t)TEST_BLOCK_LEN);
781		digest_end(hf, &context, digest, sizeof(digest), hf->base64);
782		getrusage(RUSAGE_SELF, &stop);
783		timersub(&stop.ru_utime, &start.ru_utime, &res);
784		elapsed = (double)res.tv_sec + (double)res.tv_usec / 1000000.0;
785
786		(void)printf("\nDigest = %s\n", digest);
787		(void)printf("Time   = %f seconds\n", elapsed);
788		(void)printf("Speed  = %f bytes/second\n",
789		    (double)TEST_BLOCK_LEN * count / elapsed);
790	}
791}
792
793void
794digest_test(struct hash_list *hl)
795{
796	struct hash_function *hf;
797	union ANY_CTX context;
798	int i;
799	char digest[MAX_DIGEST_LEN + 1];
800	unsigned char buf[1000];
801	unsigned const char *test_strings[] = {
802		"",
803		"a",
804		"abc",
805		"message digest",
806		"abcdefghijklmnopqrstuvwxyz",
807		"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
808		"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
809		    "0123456789",
810		"12345678901234567890123456789012345678901234567890123456789"
811		    "012345678901234567890",
812	};
813
814	TAILQ_FOREACH(hf, hl, tailq) {
815		(void)printf("%s test suite:\n", hf->name);
816
817		for (i = 0; i < 8; i++) {
818			hf->init(&context);
819			hf->update(&context, test_strings[i],
820			    strlen(test_strings[i]));
821			digest_end(hf, &context, digest, sizeof(digest),
822			    hf->base64);
823			digest_printstr(hf, test_strings[i], digest);
824		}
825
826		/* Now simulate a string of a million 'a' characters. */
827		memset(buf, 'a', sizeof(buf));
828		hf->init(&context);
829		for (i = 0; i < 1000; i++)
830			hf->update(&context, buf, sizeof(buf));
831		digest_end(hf, &context, digest, sizeof(digest), hf->base64);
832		digest_print(hf, "one million 'a' characters",
833		    digest);
834	}
835}
836#endif /* !defined(SHA2_ONLY) */
837
838void
839usage(void)
840{
841#if !defined(SHA2_ONLY)
842	if (strcmp(__progname, "cksum") == 0)
843		fprintf(stderr, "usage: %s [-bcpqrtx] [-a algorithms] [-C checklist] "
844		    "[-h hashfile]\n"
845		    "	[-s string] [file ...]\n",
846		    __progname);
847	else
848#endif /* !defined(SHA2_ONLY) */
849		fprintf(stderr, "usage:"
850		    "\t%s [-bcpqrtx] [-C checklist] [-h hashfile] [-s string] "
851		    "[file ...]\n",
852		    __progname);
853
854	exit(EXIT_FAILURE);
855}
856