xref: /openbsd/bin/md5/md5.c (revision 6f4f14eb)
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 
56 union 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 
66 struct 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 
186 TAILQ_HEAD(hash_list, hash_function);
187 
188 void digest_end(const struct hash_function *, void *, char *, size_t, int);
189 int  digest_file(const char *, struct hash_list *, int);
190 void digest_print(const struct hash_function *, const char *, const char *);
191 #if !defined(SHA2_ONLY)
192 int  digest_filelist(const char *, struct hash_function *, int, char **);
193 void digest_printstr(const struct hash_function *, const char *, const char *);
194 void digest_string(char *, struct hash_list *);
195 void digest_test(struct hash_list *);
196 void digest_time(struct hash_list *, int);
197 #endif /* !defined(SHA2_ONLY) */
198 void hash_insert(struct hash_list *, struct hash_function *, int);
199 void usage(void) __attribute__((__noreturn__));
200 
201 extern char *__progname;
202 int qflag = 0;
203 FILE *ofile = NULL;
204 
205 int
main(int argc,char ** argv)206 main(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 
399 void
hash_insert(struct hash_list * hl,struct hash_function * hf,int base64)400 hash_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 
412 void
digest_end(const struct hash_function * hf,void * ctx,char * buf,size_t bsize,int base64)413 digest_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)
431 void
digest_string(char * string,struct hash_list * hl)432 digest_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 
448 void
digest_print(const struct hash_function * hf,const char * what,const char * digest)449 digest_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)
466 void
digest_printstr(const struct hash_function * hf,const char * what,const char * digest)467 digest_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 
484 int
digest_file(const char * file,struct hash_list * hl,int echo)485 digest_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  */
546 int
digest_filelist(const char * file,struct hash_function * defhash,int selcount,char ** sel)547 digest_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 
753 void
digest_time(struct hash_list * hl,int times)754 digest_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 
793 void
digest_test(struct hash_list * hl)794 digest_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 
838 void
usage(void)839 usage(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