1 /******************************************************************************
2 * {sha256,sha1,md5,crc32}sum, depends on argv[0] (default is sha256sum).
3 * Uses libtomcrypt (public domain) and dietlibc's getline() ... GLPv2.
4 * Due to GPLv2 virus it is GPLv2.
5 *****************************************************************************/
7 #define _FILE_OFFSET_BITS 64
16 #include <getopt.h> /* solaris wants it */
24 #define FOPEN_MODE "rb"
27 #define FOPEN_MODE "r"
33 #endif /* NEED_GETLINE */
36 #define DEFAULT_BL 4096
37 #define DEFAULT_LINE_LEN 4096
39 #define MAKE_CHECKSUM_ERR_OK 0
40 #define MAKE_CHECKSUM_ERR_OPEN 1
41 #define MAKE_CHECKSUM_ERR_READ 3
42 #define MAKE_CHECKSUM_ERR_MEM 4
48 #define HASH_ALL (MD5|SHA1|SHA256|CRC32)
54 static const unsigned char hexchar
[] = "0123456789abcdef";
57 /* long time ago there was a number of options :) */
64 static int comments
=1;
74 unsigned char md5_o
[16];
75 unsigned char sha1_o
[20];
76 unsigned char sha256_o
[32];
77 unsigned char crc32_o
[4];
83 static void bin2hex(unsigned char *out
, unsigned char *in
, size_t size
)
85 for(; size
; size
--, in
++)
87 *out
= hexchar
[((*in
)>>4)]; out
++;
88 *out
= hexchar
[((*in
)&15)]; out
++;
95 static void hex2bin(unsigned char *out
, unsigned char *in
, size_t size
)
97 for(; size
; size
-=2, in
+=2, out
++)
99 *out
= ((((*in
) >= 'a' && (*in
) <= 'f') ?
101 : ( ((*in
) >= 'A' && (*in
) <= 'F')
103 : ((*in
) - '0'))) << 4)
105 ((((*(in
+1)) >= 'a' && (*(in
+1)) <= 'f') ?
106 ( (*(in
+1)) - 'a' + 10 )
107 : ( ((*(in
+1)) >= 'A' && (*(in
+1)) <= 'F')
108 ? ((*(in
+1)) - 'A' + 10)
109 : ((*(in
+1)) - '0'))) & 15);
115 static int is_hex_string(char *in
, size_t len
)
117 for(; len
; len
--, in
++)
118 if(!isxdigit(*in
)) return 0;
120 } /* is_hex_string() */
125 static size_t getbufsize(FILE *f
)
132 err
= fstat(fileno(f
), &st
);
133 if(err
== -1 || !st
.st_blksize
) return DEFAULT_BL
;
134 return st
.st_blksize
;
141 static int mark_filename_end(char *in
)
144 for(i
=0; *in
!= '\0' && *in
!= '\n' && *in
!= '\r'; in
++, i
++)
148 } /* mark_filename_end() */
153 static int mark_hash_end(char *in
, char *c
)
156 for(i
=0; isxdigit(*in
); in
++, i
++)
161 } /* mark_hash_end() */
166 static int make_checksum(struct hash_out
*out
)
172 if(!(out
->filename
)) f
= stdin
;
175 f
= fopen(out
->filename
, FOPEN_MODE
);
176 if(!f
) return MAKE_CHECKSUM_ERR_OPEN
;
179 blks
= getbufsize(f
);
184 return MAKE_CHECKSUM_ERR_MEM
;
188 if(out
->hashes
& CRC32
) crc32_init(&(out
->crc32
));
189 if(out
->hashes
& MD5
) md5_init(&(out
->md5
));
190 if(out
->hashes
& SHA1
) sha1_init(&(out
->sha1
));
191 if(out
->hashes
& SHA256
) sha256_init(&(out
->sha256
));
195 err
= fread(data
, 1, blks
, f
);
200 if(out
->hashes
& CRC32
) crc32_update(&(out
->crc32
), data
, err
);
201 if(out
->hashes
& MD5
) md5_process(&(out
->md5
), data
, err
);
202 if(out
->hashes
& SHA1
) sha1_process(&(out
->sha1
), data
, err
);
203 if(out
->hashes
& SHA256
) sha256_process(&(out
->sha256
), data
, err
);
205 else if(feof(f
)) break;
208 if(out
->hashes
& CRC32
) crc32_final(&(out
->crc32
), out
->crc32_o
);
209 if(out
->hashes
& MD5
) md5_done(&(out
->md5
), out
->md5_o
);
210 if(out
->hashes
& SHA1
) sha1_done(&(out
->sha1
), out
->sha1_o
);
211 if(out
->hashes
& SHA256
) sha256_done(&(out
->sha256
), out
->sha256_o
);
213 memset(data
, 0, blks
); free(data
);
214 return MAKE_CHECKSUM_ERR_READ
;
219 memset(data
, 0, blks
); free(data
);
220 if(out
->hashes
& CRC32
) crc32_final(&(out
->crc32
), out
->crc32_o
);
221 if(out
->hashes
& MD5
) md5_done(&(out
->md5
), out
->md5_o
);
222 if(out
->hashes
& SHA1
) sha1_done(&(out
->sha1
), out
->sha1_o
);
223 if(out
->hashes
& SHA256
) sha256_done(&(out
->sha256
), out
->sha256_o
);
225 return MAKE_CHECKSUM_ERR_OK
;
226 } /* make_checksum() */
231 void print_hashes(struct hash_out
*in
)
233 unsigned char hex_form
[65];
234 unsigned char *bin_form
;
238 #define print_hash(a,b) do { \
239 bin2hex(hex_form, bin_form, (a)); \
240 if(comments) printf("# R %s %llu %*s %s\n", (b), in->written, 2*(a), hex_form, in->filename ? in->filename : "-"); \
241 printf("%*s %s\n", 2*(a), hex_form, in->filename ? in->filename : "-"); \
244 if(opt
.hashes
& CRC32
)
246 bin_form
= in
->crc32_o
;
247 print_hash(sizeof(in
->crc32_o
), "CRC32");
251 bin_form
= in
->md5_o
;
252 print_hash(sizeof(in
->md5_o
), "MD5");
254 if(opt
.hashes
& SHA1
)
256 bin_form
= in
->sha1_o
;
257 print_hash(sizeof(in
->sha1_o
), "SHA1");
259 if(opt
.hashes
& SHA256
)
261 bin_form
= in
->sha256_o
;
262 print_hash(sizeof(in
->sha256_o
), "SHA256");
269 /* depends on "char *filename" */
270 #define handle_make_checksum_err(x) do { \
271 if((x) != MAKE_CHECKSUM_ERR_OK) \
275 case MAKE_CHECKSUM_ERR_OPEN: \
276 fprintf(stderr, "fopen(\"%s\") failed: %s\n", filename, \
279 case MAKE_CHECKSUM_ERR_MEM: \
280 fprintf(stderr, "malloc() failed, file: '%s'\n", \
283 case MAKE_CHECKSUM_ERR_READ: \
284 fprintf(stderr, "fread(\"%s\") failed: %s\n", filename, \
293 int do_hash(char *filename
)
297 unsigned char hex_form
[64];
301 memset(&out
, 0, sizeof(out
));
302 out
.hashes
= opt
.hashes
;
303 out
.filename
= filename
;
305 err
= make_checksum(&out
);
306 handle_make_checksum_err(err
);
314 #define check_failed(x, y) do { printf("%s: FAILED (%s)\n", ((x) ? (x) : "(stdin)"), (y)); } while(0)
315 #define check_ok(x, y) do { if(verbose) printf("%s: OK (%s)\n", ((x) ? (x) : "(stdin)"), (y)); } while(0)
318 int check_file(struct hash_out
*what
, struct hash_out
*against
)
320 char *filename
= what
->filename
;
323 err
= make_checksum(what
);
324 handle_make_checksum_err(err
);
326 if(what
->hashes
& CRC32
)
328 if((memcmp(what
->crc32_o
, against
->crc32_o
, sizeof(what
->crc32_o
))) != 0) { check_failed(what
->filename
, "CRC32"); retval
= 1; }
329 else check_ok(what
->filename
, "CRC32");
331 if(what
->hashes
& MD5
)
333 if((memcmp(what
->md5_o
, against
->md5_o
, sizeof(what
->md5_o
))) != 0) { check_failed(what
->filename
, "MD5"); retval
= 1; }
334 else check_ok(what
->filename
, "MD5");
336 if(what
->hashes
& SHA1
)
338 if((memcmp(what
->sha1_o
, against
->sha1_o
, sizeof(what
->sha1_o
))) != 0) { check_failed(what
->filename
, "SHA1"); retval
= 1; }
339 else check_ok(what
->filename
, "SHA1");
341 if(what
->hashes
& SHA256
)
343 if((memcmp(what
->sha256_o
, against
->sha256_o
, sizeof(what
->sha256_o
))) != 0) { check_failed(what
->filename
, "SHA256"); retval
= 1; }
344 else check_ok(what
->filename
, "SHA256");
353 int do_hash_check(char *in_filename
)
356 char *temp_line
, *filename
;
358 int retval
=0, current_hash
=0;
360 size_t line_len
= DEFAULT_LINE_LEN
, lineno
, hex_len
, filename_len
;
361 struct hash_out temp_out
, to_compare
;
363 memset(&temp_out
, 0, sizeof(temp_out
));
364 memset(&to_compare
, 0, sizeof(to_compare
));
366 if(!in_filename
) f
= stdin
;
369 f
= fopen(in_filename
, FOPEN_MODE
);
372 fprintf(stderr
, "fopen(\"%s\") failed: %s\n", in_filename
,
378 temp_line
= malloc(DEFAULT_LINE_LEN
);
381 fprintf(stderr
, "malloc() failed, file: '%s'\n", in_filename
? in_filename
: "(stdin)");
382 if(f
!= stdin
) fclose(f
);
387 for(lineno
=1; ;lineno
++)
392 err2
= getline(&temp_line
, &line_len
, f
);
395 /* we may have a pending check to do */
399 retval
|= check_file(&temp_out
, &to_compare
);
401 if(temp_out
.filename
) free(temp_out
.filename
);
402 memset(&temp_out
, 0, sizeof(temp_out
));
403 memset(&to_compare
, 0, sizeof(to_compare
));
406 if(!feof(f
)) retval
|= 2;
407 if(temp_line
) { memset(temp_line
, 0, line_len
); free(temp_line
); }
413 if(*temp_line
== '#' || *temp_line
== ';') continue;
415 if(!(filename
= strstr(temp_line
, " "))) filename
= strstr(temp_line
, " *");
419 hex_len
= mark_hash_end(temp_line
, &sep
);
420 /* skip malformed line */
421 if(!(sep
== '\n' || sep
== '\r' || sep
== '\0'))
423 if(verbose
) fprintf(stderr
, "malformed (sep) line %d in file: '%s'\n", lineno
, in_filename
? in_filename
: "(stdin)");
430 hex_len
= filename
- temp_line
;
431 if(!is_hex_string(temp_line
, hex_len
))
433 if(verbose
) fprintf(stderr
, "malformed (!is_hex_string) line %d in file: '%s'\n", lineno
, in_filename
? in_filename
: "(stdin)");
440 case 8: current_hash
= CRC32
; break;
441 case 32: current_hash
= MD5
; break;
442 case 40: current_hash
= SHA1
; break;
443 case 64: current_hash
= SHA256
; break;
446 if(verbose
) fprintf(stderr
, "malformed (hex_len) line %d in file: '%s'\n", lineno
, in_filename
? in_filename
: "(stdin)");
451 /* skip on '-C' and non-selected hash */
452 if(opt
.check_selected
&& !(current_hash
& opt
.hashes
)) continue;
457 filename_len
= mark_filename_end(filename
);
458 /* XXX: perhaps allow it as stdin? */
461 if(verbose
) fprintf(stderr
, "malformed (nothing_after_sep) line %d in file: '%s'\n", lineno
, in_filename
? in_filename
: "(stdin)");
463 else if(filename_len
== 1 && *filename
== '-') filename
= NULL
;
467 #define modify_to_compare() do { \
468 switch(current_hash) \
470 case CRC32: hex2bin(to_compare.crc32_o, (unsigned char *)temp_line, hex_len); break; \
471 case MD5: hex2bin(to_compare.md5_o, (unsigned char *)temp_line, hex_len); break; \
472 case SHA1: hex2bin(to_compare.sha1_o, (unsigned char *)temp_line, hex_len); break; \
473 case SHA256: hex2bin(to_compare.sha256_o, (unsigned char *)temp_line, hex_len); break; \
479 #define allocate_filename() do { \
482 temp_out.filename = to_compare.filename = strdup(filename); \
483 if(!temp_out.filename) \
485 perror("strdup() failed"); \
486 if(f != stdin) fclose(f); \
497 temp_out
.hashes
= to_compare
.hashes
= current_hash
;
499 } /* !temp_out.hashes */
502 /* now I need to compare filenames */
503 if( (!filename
&& !temp_out
.filename
) || (filename
&& temp_out
.filename
&& (strcmp(filename
, temp_out
.filename
) == 0)) )
505 /* same files, just bitwise OR the hash */
506 temp_out
.hashes
|= current_hash
;
507 to_compare
.hashes
= temp_out
.hashes
;
510 else /* different files => make checksums & re-init temp_out */
513 retval
|= check_file(&temp_out
, &to_compare
);
516 if(temp_out
.filename
) free(temp_out
.filename
);
517 memset(&temp_out
, 0, sizeof(temp_out
));
518 memset(&to_compare
, 0, sizeof(to_compare
));
522 temp_out
.hashes
= to_compare
.hashes
= current_hash
;
530 } /* do_hash_check() */
538 fputs("{sha256,sha1,md5,crc32}sum: generates or verifies checksums (message digests)\n", stderr
);
539 fputs("Usage: {sha256,sha1,md5,crc32}sum [-01235CachqvV] [file...] \n\n", stderr
);
541 "\t-c\t\tcheck mode\n"
542 "\t-C\t\tcheck mode (check against selected hashes)\n"
543 "\t-0\t\tZero previous hashes\n"
548 "\t-a\t\tall hashes\n"
549 "\t-m <number>\t\tselected hashes\n"
552 "\t-q\t\tcreate comments\n"
560 static void version()
562 fprintf(stderr
, "s2-%s\n\n", RELEASE
);
568 /* samotny programek */
569 int main(int argc
, char ** argv
)
571 int check
= 0, retval
= 0, c
, temp
;
574 opt
.check_selected
= 0;
576 if( (strstr(argv
[0], "s1")) || (strstr(argv
[0], "sha1sum")) ) opt
.hashes
= SHA1
;
577 else if( (strstr(argv
[0], "s2")) || (strstr(argv
[0], "sha256sum")) ) opt
.hashes
= SHA256
;
578 else if( (strstr(argv
[0], "md5")) ) opt
.hashes
= MD5
;
579 else if( (strstr(argv
[0], "crc32")) ) opt
.hashes
= CRC32
;
580 else if( (strstr(argv
[0], "md")) ) opt
.hashes
= HASH_ALL
;
583 while( (c
= getopt(argc
, argv
, "ahvVcCq01235m:")) != -1 )
587 case 'c': check
= 1; break;
588 case 'q': comments
= 0; break;
589 case 'C': check
= opt
.check_selected
= 1; break;
590 case 'v': verbose
=1; break;
591 case 'V': version(); exit(0);
592 case 'h': help(); exit(0);
593 case 'a': opt
.hashes
= HASH_ALL
; break;
594 case '0': opt
.hashes
= 0; break;
595 case '2': opt
.hashes
|= SHA256
; break;
596 case '1': opt
.hashes
|= SHA1
; break;
597 case '3': opt
.hashes
|= CRC32
; break;
598 case '5': opt
.hashes
|= MD5
; break;
599 case 'm': temp
= strtoul(optarg
, NULL
, 10); if((temp
& HASH_ALL
)) opt
.hashes
= temp
; break;
600 default: help(opt
.hashes
); exit(1);
601 } /* switch argument */
602 } /* while getopt() */
604 if(!(opt
.hashes
& HASH_ALL
)) opt
.hashes
= SHA256
;
611 retval
|= (do_hash_check(argv
[optind
++]));
616 retval
|= (do_hash(argv
[optind
++]));
623 if(check
) { retval
= do_hash_check(NULL
); }
625 { retval
= (do_hash(NULL
)); }
628 } /* no file => stdin */
632 * 0 .. OK, 1 .. bad checksum, 2 .. other error (fopen(), ...)