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