Initial commit.
[hondza-y36pr2.git] / sha256sum / sha256sum.c
blob71466987146e553f514fd0169842e08e96f7a549
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
8 #define _GNU_SOURCE
10 #include <stdio.h>
11 #include <string.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <errno.h>
16 #include <getopt.h> /* solaris wants it */
18 #include <tomcrypt.h>
20 #include "crc32.h"
23 #ifdef WIN32
24 #define FOPEN_MODE "rb"
25 #define BUFSIZE_CONST
26 #else
27 #define FOPEN_MODE "r"
28 #include <sys/stat.h>
29 #endif /* WIN32 */
31 #ifdef NEED_GETLINE
32 #include "getline.h"
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
44 #define MD5 1
45 #define SHA1 2
46 #define SHA256 4
47 #define CRC32 8
48 #define HASH_ALL (MD5|SHA1|SHA256|CRC32)
51 #define RELEASE "r2"
54 static const unsigned char hexchar[] = "0123456789abcdef";
57 /* long time ago there was a number of options :) */
58 struct t_options {
59 int hashes;
60 int check_selected;
61 } opt;
63 static int verbose=0;
64 static int comments=1;
66 struct hash_out {
67 char *filename;
68 int hashes;
69 crc32_t crc32;
70 hash_state md5;
71 hash_state sha1;
72 hash_state sha256;
73 off_t written;
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++;
90 *out = '\0';
91 } /* bin2hex() */
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') ?
100 ( (*in) - 'a' + 10 )
101 : ( ((*in) >= 'A' && (*in) <= 'F')
102 ? ((*in) - 'A' + 10)
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);
111 } /* hex2bin() */
115 static int is_hex_string(char *in, size_t len)
117 for(; len; len--, in++)
118 if(!isxdigit(*in)) return 0;
119 return 1;
120 } /* is_hex_string() */
125 static size_t getbufsize(FILE *f)
127 #ifdef BUFSIZE_CONST
128 return DEFAULT_BL;
129 #else
130 struct stat st;
131 int err;
132 err = fstat(fileno(f), &st);
133 if(err == -1 || !st.st_blksize) return DEFAULT_BL;
134 return st.st_blksize;
135 #endif
136 } /* getbufsize() */
141 static int mark_filename_end(char *in)
143 int i;
144 for(i=0; *in != '\0' && *in != '\n' && *in != '\r'; in++, i++)
146 *in = '\0';
147 return i;
148 } /* mark_filename_end() */
153 static int mark_hash_end(char *in, char *c)
155 int i;
156 for(i=0; isxdigit(*in); in++, i++)
158 *c = *in;
159 *in = '\0';
160 return i;
161 } /* mark_hash_end() */
166 static int make_checksum(struct hash_out *out)
168 size_t err, blks;
169 unsigned char *data;
170 FILE *f;
172 if(!(out->filename)) f = stdin;
173 else
175 f = fopen(out->filename, FOPEN_MODE);
176 if(!f) return MAKE_CHECKSUM_ERR_OPEN;
179 blks = getbufsize(f);
180 data = malloc(blks);
181 if(!data)
183 fclose(f);
184 return MAKE_CHECKSUM_ERR_MEM;
187 /* init hashes */
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));
193 while(1)
195 err = fread(data, 1, blks, f);
196 /* process */
197 if(err > 0)
199 out->written += err;
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;
206 else
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);
212 fclose(f);
213 memset(data, 0, blks); free(data);
214 return MAKE_CHECKSUM_ERR_READ;
216 } /* while 1 */
218 fclose(f);
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;
236 hex_form[64] = '0';
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 : "-"); \
242 } while(0)
244 if(opt.hashes & CRC32)
246 bin_form = in->crc32_o;
247 print_hash(sizeof(in->crc32_o), "CRC32");
249 if(opt.hashes & MD5)
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");
264 } /* print_hash() */
269 /* depends on "char *filename" */
270 #define handle_make_checksum_err(x) do { \
271 if((x) != MAKE_CHECKSUM_ERR_OK) \
273 switch((x)) \
275 case MAKE_CHECKSUM_ERR_OPEN: \
276 fprintf(stderr, "fopen(\"%s\") failed: %s\n", filename, \
277 strerror(errno)); \
278 return 2; \
279 case MAKE_CHECKSUM_ERR_MEM: \
280 fprintf(stderr, "malloc() failed, file: '%s'\n", \
281 filename); \
282 return 2; \
283 case MAKE_CHECKSUM_ERR_READ: \
284 fprintf(stderr, "fread(\"%s\") failed: %s\n", filename, \
285 strerror(errno)); \
286 return 2; \
287 } /* switch err */ \
289 } while(0)
293 int do_hash(char *filename)
295 int err;
296 struct hash_out out;
297 unsigned char hex_form[64];
299 hex_form[64] = '\0';
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);
308 print_hashes(&out);
310 return 0;
311 } /* do_hash() */
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;
321 int err, retval=0;
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");
347 return retval;
348 } /* check_file() */
353 int do_hash_check(char *in_filename)
355 FILE *f;
356 char *temp_line, *filename;
357 char sep;
358 int retval=0, current_hash=0;
359 ssize_t err2;
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;
367 else
369 f = fopen(in_filename, FOPEN_MODE);
370 if(!f)
372 fprintf(stderr, "fopen(\"%s\") failed: %s\n", in_filename,
373 strerror(errno));
374 return 2;
378 temp_line = malloc(DEFAULT_LINE_LEN);
379 if(!temp_line)
381 fprintf(stderr, "malloc() failed, file: '%s'\n", in_filename ? in_filename : "(stdin)");
382 if(f != stdin) fclose(f);
383 return 2;
387 for(lineno=1; ;lineno++)
389 hex_len=0;
390 filename=NULL;
392 err2 = getline(&temp_line, &line_len, f);
393 if(err2 < 1)
395 /* we may have a pending check to do */
396 if(temp_out.hashes)
398 /* make checksum */
399 retval |= check_file(&temp_out, &to_compare);
400 /* clean */
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); }
408 fclose(f);
409 return retval;
412 /* skip comment */
413 if(*temp_line == '#' || *temp_line == ';') continue;
415 if(!(filename = strstr(temp_line, " "))) filename = strstr(temp_line, " *");
417 if(!filename)
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)");
424 continue;
427 else
429 *filename='\0';
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)");
434 continue;
438 switch(hex_len)
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;
444 default:
446 if(verbose) fprintf(stderr, "malformed (hex_len) line %d in file: '%s'\n", lineno, in_filename ? in_filename : "(stdin)");
447 continue;
451 /* skip on '-C' and non-selected hash */
452 if(opt.check_selected && !(current_hash & opt.hashes)) continue;
454 if(filename)
456 filename += 2;
457 filename_len = mark_filename_end(filename);
458 /* XXX: perhaps allow it as stdin? */
459 if(!filename_len)
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; \
475 } while(0)
479 #define allocate_filename() do { \
480 if(filename) \
482 temp_out.filename = to_compare.filename = strdup(filename); \
483 if(!temp_out.filename) \
485 perror("strdup() failed"); \
486 if(f != stdin) fclose(f); \
487 return 2; \
490 } while(0)
493 /* first */
494 if(!temp_out.hashes)
496 allocate_filename();
497 temp_out.hashes = to_compare.hashes = current_hash;
498 modify_to_compare();
499 } /* !temp_out.hashes */
500 else /* not first */
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;
508 modify_to_compare();
510 else /* different files => make checksums & re-init temp_out */
512 /* make checksum */
513 retval |= check_file(&temp_out, &to_compare);
515 /* clean */
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));
520 /* re-init */
521 allocate_filename();
522 temp_out.hashes = to_compare.hashes = current_hash;
523 modify_to_compare();
525 } /* not first */
527 } /* for lineno */
529 return retval;
530 } /* do_hash_check() */
536 static void help()
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);
540 fputs("Options:\n"
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"
544 "\t-1\t\tSHA1\n"
545 "\t-2\t\tSHA256\n"
546 "\t-3\t\tCRC32\n"
547 "\t-5\t\tMD5\n"
548 "\t-a\t\tall hashes\n"
549 "\t-m <number>\t\tselected hashes\n"
550 "\t-h\t\thelp\n"
551 "\t-v\t\tverbose\n"
552 "\t-q\t\tcreate comments\n"
553 "\t-V\t\tversion\n"
554 "\n", stderr);
555 } /* help() */
560 static void version()
562 fprintf(stderr, "s2-%s\n\n", RELEASE);
563 } /* version() */
568 /* samotny programek */
569 int main(int argc, char ** argv)
571 int check = 0, retval = 0, c, temp;
573 opt.hashes = SHA256;
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;
581 else opt.hashes = 0;
583 while( (c = getopt(argc, argv, "ahvVcCq01235m:")) != -1 )
585 switch(c)
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;
606 if(optind < argc)
608 if(check)
610 while(optind < argc)
611 retval |= (do_hash_check(argv[optind++]));
612 } /* if check */
613 else
615 while(optind < argc)
616 retval |= (do_hash(argv[optind++]));
617 } /* not check */
619 return retval;
621 else
623 if(check) { retval = do_hash_check(NULL); }
624 else
625 { retval = (do_hash(NULL)); }
627 return retval;
628 } /* no file => stdin */
632 * 0 .. OK, 1 .. bad checksum, 2 .. other error (fopen(), ...)
633 * bitwise ORed
635 return retval;
636 } /* main() */