1 /* Copyright (C) 2014 2015 Jason Self <j@jxself.org>
3 This file is part of skeinsum.
5 skeinsum is free software: you can redistribute it and/or modify it
6 under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 skeinsum is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with skeinsum. If not, see <http://www.gnu.org/licenses/>.
32 #include "SHA3api_ref.h"
34 extern const int hashbitlen;
36 #define MaxNmberFiles 10
37 #define skeinVersion "1.3"
38 const size_t input_minbufsize = 32 * 1024;
39 const size_t input_maxbufsize = 32 * 1024 * 1024;
42 char invalidOption = 0;
53 static struct option const long_options[] =
55 { "binary", no_argument, NULL, 'b' },
56 { "check", no_argument, NULL, 'c' },
57 { "quiet", no_argument, NULL, QUIET_OPTION },
58 { "status", no_argument, NULL, STATUS_OPTION },
59 { "text", no_argument, NULL, 't' },
60 { "warn", no_argument, NULL, 'w' },
61 { "tag", no_argument, NULL, TAG_OPTION },
62 { "help", no_argument, NULL, HELP_OPTION },
63 { "version", no_argument, NULL, VERSION_OPTION },
67 void printHexMsg(unsigned char *ptr, int size, char result[])
72 for (i = 0 ; i < size ; i ++)
77 if (High >= 0xA && High <= 0xF)
79 else if(High >= 0x0 && High <= 0x9)
82 if (Low >= 0xA && Low <= 0xF)
84 else if(Low >= 0x0 && Low <= 0x9)
93 int HashWithMode(char file_name[], char MsgDigest[], char mode)
95 /* Try to get file info */
97 if (stat(file_name, &st) < 0) {
98 printf("skein%dsum: %s: STAT FAILED: %s\n", hashbitlen, file_name, strerror(errno));
103 size_t fsize = st.st_size;
104 if (fsize != st.st_size) {
105 printf("skein%dsum: %s: SIZE WARNING: filesize %llu is too big for reading into memory!\n",
106 hashbitlen, file_name, (long long unsigned)st.st_size);
110 FILE *fp_in = fopen(file_name, (mode == 't' ? "r" : "rb") );
112 printf("skein%dsum: %s: OPEN FAILED: %s\n", hashbitlen, file_name, strerror(errno));
116 /* Allocate buffer */
117 size_t bufsize = fsize + 1;
118 if (bufsize < input_minbufsize)
119 bufsize = input_minbufsize;
121 char *readbuf = malloc (bufsize);
122 if (!readbuf && bufsize > input_maxbufsize) { /* ..Try to get contents by smaller portions */
123 bufsize = input_maxbufsize;
124 readbuf = malloc (bufsize);
128 printf("skein%dsum: %s: MEM FAILED: %s\n", hashbitlen, file_name, strerror(errno));
134 size_t readpos = 0, readed, total_readed = 0;
137 size_t maxread = bufsize - readpos;
138 readed = fread(readbuf, 1, maxread, fp_in);
139 if (readed > 0 && readed <= maxread)
140 total_readed += readed;
141 if (readed != maxread)
143 if (getenv("SKEIN_DEBUG"))
144 printf("DEBUG: bufsize=%u (0x%x), readpos=%u (0x%x), maxread=%u (0x%x), total=%u (0x%x)\n",
145 bufsize, bufsize, readpos, readpos, maxread, maxread, total_readed, total_readed);
147 if (bufsize * 2 > bufsize) /* ..check overflow */
148 newbuf = realloc(readbuf, bufsize * 2);
150 printf("skein%dsum: %s: MEM WARNING: %u bytes only readed from %llu\n",
151 hashbitlen, file_name, total_readed, (long long unsigned)st.st_size);
159 if (total_readed < st.st_size && total_readed < bufsize) {
160 printf("skein%dsum: %s: READ WARNING: filesize=%llu, readed=%u, %s\n",
161 hashbitlen, file_name, (long long unsigned)st.st_size, total_readed, strerror(errno));
166 unsigned char output[hashbitlen/4];
167 Hash(hashbitlen, (unsigned char*) readbuf, total_readed, output);
171 printHexMsg(output,hashbitlen/4,MsgDigest);
176 int HashTextMode(char file_name[], char MsgDigest[])
178 return HashWithMode(file_name, MsgDigest, 't');
181 int HashBinaryMode(char file_name[], char MsgDigest[])
183 return HashWithMode(file_name, MsgDigest, 'b');
186 int HashStringMode(char Msg[],char MsgDigest[])
188 unsigned char output[hashbitlen/4];
189 Hash(hashbitlen,(unsigned char*) Msg,sizeof(Msg),output);
190 printHexMsg(output,hashbitlen/4,MsgDigest);
194 int isProper(char MsgDigest[])
196 if ((strlen(MsgDigest) - hashbitlen/4) != 0)
200 for(index = 0; index < strlen(MsgDigest);index++)
202 c = MsgDigest[index];
203 if(!(( c >= '0' && c <= '9' ) || ( c >= 'A' && c <= 'F')))
211 int decomposeHashLine(char hash[], char MsgDigest_tmp[], char file_tmp[])
218 while(((c = hash[i])!=' ')&&((c = hash[i])!='_'))
225 sprintf(alg,"skein%d",hashbitlen);
233 strcpy(MsgDigest_tmp,tmp);
235 while((c = hash[i])!= '\n')
237 file_tmp[j] = hash[i];
243 else if((hash[i]=='_')&&(hash[i+1]=='v'))
248 while((c = hash[i])!=' ')
250 version[j] = hash[i];
255 float vers = 0, skeinVers = 0;
256 sscanf(version,"%f",&vers);
257 sscanf(skeinVersion,"%f",&skeinVers);
261 while((c = hash[i])!=')')
263 file_tmp[j] = hash[i];
271 while((c = hash[i])!='\n')
273 MsgDigest_tmp[j] = hash[i];
277 MsgDigest_tmp[j] = 0;
280 {//version newer than mine
283 else if (skeinVers > vers)
284 {//going to use older version than mine
296 void print_version(void)
298 printf("skein%dsum 1.0\n", hashbitlen);
299 printf("License GPLv3+: GNU GPL version 3 or later\n");
300 printf("<http://gnu.org/licenses/gpl.html>\n");
301 printf("This is free software: you are free to change and redistribute it.\n");
302 printf("There is NO WARRANTY, to the extent permitted by law.\n");
306 void print_usage(void)
308 printf("Usage: skein%dsum [OPTION]... [FILE]...\n",hashbitlen);
309 printf("Print or check skein (%d-bit) checksums.\n",hashbitlen);
310 printf("With no FILE, or when FILE is -, read standard input.\n");
312 printf("-b, --binary read in binary mode\n");
313 printf("-c, --check read skein sums from the FILEs and check them\n");
314 printf("--tag create a BSD-style checksum\n");
315 printf("-t, --text read in text mode (default)\n");
317 printf("The following three options are useful only when verifying checksums:\n");
318 printf("--quiet don't print OK for each successfully verified file\n");
319 printf("--status don't output anything, status code shows success\n");
320 printf("-w, --warn warn about improperly formatted checksum lines\n");
322 printf("--strict with --check, exit non-zero for any invalid input\n");
323 printf("--help display this help and exit\n");
324 printf("--version output version information and exit\n");
326 printf("The sums are computed as described in version 1.3 of the Skein\n");
327 printf("specification. When checking, the input should be a former output of\n");
328 printf("this program. The default mode is to print a line with checksum, a\n");
329 printf("character indicating input mode ('*' for binary, space for text), and\n");
330 printf("name for each FILE.\n");
336 int main(int argc, char** argv)
338 char MsgDigest[hashbitlen/2];
339 char *list_files[MaxNmberFiles];
340 int number_files = 0;
351 /*****************************************************************************************
352 ************************************* GETTING DATA ***********************************
353 *****************************************************************************************/
354 while ((opt = getopt_long (argc, argv, "bctw", long_options, NULL)) != -1)
357 case 0 : hashString = 1; break;
358 case 'b' : binary = 1; break;
359 case 't' : binary = 0; break;
360 case 'c' : check = 1; break;
361 case 'w' : warn = 1; break;
362 case QUIET_OPTION : quiet = 1; break;
363 case STATUS_OPTION : status = 1; break;
364 case TAG_OPTION : tag = 1; break;
366 case HELP_OPTION : print_usage(); /* ..never returns */
367 case VERSION_OPTION : print_version(); /* ..never returns */
370 printf("Try `skein%dsum --help' for more information.\n",hashbitlen);
375 for (; optind < argc; ++optind)
378 if (stat(argv[optind], &s) < 0)
380 printf("skein%dsum: %s: no such file or directory\n", hashbitlen, argv[optind]);
383 else if (s.st_mode & (S_IFREG|S_IFBLK)) /* ..regular file or block device? */
385 if (number_files < MaxNmberFiles) {
386 list_files[number_files++] = argv[optind];
388 printf("skein%dsum: %s: ignore because filelist is too long\n",
389 hashbitlen, argv[optind]);
392 else if (s.st_mode & S_IFDIR)
394 printf("skein%dsum: %s: is a directory\n",hashbitlen,argv[optind]);
399 printf("skein%dsum: %s: wrong filetype 0x%Xu\n",
400 hashbitlen, argv[optind], s.st_mode);
405 /*****************************************************************************************
406 ************************************* PROCESSING DATA ***********************************
407 *****************************************************************************************/
408 if(argc > 1 && binary == 1)
410 if (check == 1 || quiet == 1 || warn == 1 || status == 1)
412 printf("skein%dsum: the --binary and --text options are meaningless when verifying checksums\n",hashbitlen);
413 printf("Try 'skein%dsum --help' for more information.\n",hashbitlen);
419 while(index_files < number_files)
423 printf("skein%dsum: %s: No such file or directory\n",hashbitlen,list_files[index_files]);
427 if(HashBinaryMode(list_files[index_files],MsgDigest)!=-1)
431 printf("skein%d_v%s (%s) = %s\n",hashbitlen,skeinVersion,list_files[index_files],MsgDigest);
435 printf("%s ",MsgDigest);
436 printf("*%s\n",list_files[index_files]);
442 else if(errorFound != 1)
443 { // read stdin for strings
445 char longstring[1000];
447 while((fgets(stri,100,stdin))!=NULL)
449 strcat(longstring,stri);
451 HashStringMode(longstring,MsgDigest);
454 printf("skein%d_v%s (-) = %s\n",hashbitlen,skeinVersion,MsgDigest);
458 printf("%s *-\n",MsgDigest);
463 else if (argc > 1 && binary == 0) // Text Mode
465 if (check == 1 || quiet == 1 || warn == 1 || status == 1)
467 printf("skein%dsum: the --binary and --text options are meaningless when verifying checksums\n",hashbitlen);
468 printf("Try 'skein%dsum --help' for more information.\n",hashbitlen);
474 while(index_files < number_files)
476 if(HashTextMode(list_files[index_files],MsgDigest)!=-1)
480 printf("skein%d_v%s (%s) = %s\n",hashbitlen,skeinVersion,list_files[index_files],MsgDigest);
484 printf("%s ",MsgDigest);
485 printf("%s\n",list_files[index_files]);
491 else if(errorFound != 1)
492 { // read stdin for strings
494 char longstring[1000];
496 while((fgets(stri,100,stdin))!=NULL)
498 strcat(longstring,stri);
500 HashStringMode(longstring,MsgDigest);
503 printf("skein%d_v%s (-) = %s\n",hashbitlen,skeinVersion,MsgDigest);
507 printf("%s -\n",MsgDigest);
512 else if (argc > 1 && binary == -1)
515 {// hashing stdin entries
516 if(quiet == 1 || status == 1 || warn == 1)
519 printf("skein%dsum: the --quiet option is meaningful only when verifying checksums\n",hashbitlen);
521 printf("skein%dsum: the --status option is meaningful only when verifying checksums\n",hashbitlen);
523 printf("skein%dsum: the --warn option is meaningful only when verifying checksums\n",hashbitlen);
525 printf("Try 'skein%dsum --help' for more information.\n",hashbitlen);
531 while(index_files < number_files)
533 if(HashTextMode(list_files[index_files],MsgDigest)!=-1)
537 printf("skein%d_v%s (%s) = %s\n",hashbitlen,skeinVersion,list_files[index_files],MsgDigest);
541 printf("%s ",MsgDigest);
542 printf("%s\n",list_files[index_files]);
548 else if(errorFound != 1)
549 { // hasing strings read from stdin
551 char longstring[1000]; /* TODO!!! Fix buffer overflow!!! */
553 while((fgets(stri,100,stdin))!=NULL)
555 strcat(longstring,stri);
557 HashStringMode(longstring,MsgDigest);
560 printf("skein%d_v%s (-) = %s\n",hashbitlen,skeinVersion,MsgDigest);
564 printf("%s -\n",MsgDigest);
572 printf("skein%dsum: the --tag option is meaningless when verifying checksums\n",hashbitlen);
573 printf("Try 'skein%dsum --help' for more information\n",hashbitlen);
577 while(index_files < number_files)
580 char hash[500], file_name[100], file_tmp[100], MsgDigest_tmp[hashbitlen/2];
581 int NoMatch = 0, NotProper = 0, Computed = 0;
582 strcpy(file_name,list_files[index_files]);
585 fp = fopen(file_name,"r");
588 while(fgets(hash,500,fp)!=NULL)
592 unsigned int hashVersion = decomposeHashLine(hash,MsgDigest_tmp,file_tmp);
593 if(hashVersion == -1)
595 printf("skein%d: %s is using newer version of skein%d algorithm\n",hashbitlen,file_tmp,hashbitlen);
596 printf("You should update your algorithm\n");
599 else if( hashVersion == 0)
601 printf("skein%d: %s is using an older version of skein%d algorithm\n",hashbitlen,file_tmp,hashbitlen);
602 printf("You should use the older algorithm\n");
607 if(!isProper(MsgDigest_tmp))
609 if(status != 1 && warn == 1)
611 printf("skein%dsum: %s: %d: improperly formatted skein%d checksum line\n",hashbitlen,file_tmp,line,hashbitlen);
617 if(file_tmp[0] == '*')
619 int index_file_tmp = 0;
620 for( ; file_tmp[index_file_tmp+1]!=0 ; index_file_tmp++)
622 file_tmp[index_file_tmp] = file_tmp[index_file_tmp + 1];
624 file_tmp[index_file_tmp] = 0;
625 HashBinaryMode(file_tmp,MsgDigest);
629 HashTextMode(file_tmp,MsgDigest);
632 if(!strcmp(MsgDigest,MsgDigest_tmp))
636 printf("%s: OK\n",file_tmp);
641 printf("%s: FAILED\n",file_tmp);
648 printf("skein%dsum: WARNING: %d of %d computed checksums did NOT match\n",
649 hashbitlen,NoMatch,Computed);
653 printf("skein%dsum: WARNING: %d line is improperly formatted\n",hashbitlen,NotProper);
658 printf("skein%dsum: %s: No such file or directory\n",hashbitlen,file_name);
664 if(number_files == 0)
666 char longstring[1000];
668 char MsgDigest_tmp[hashbitlen/2];
669 int Computed = 0, NotProper = 0, NoMatch = 0 , line = 0;
670 while((fgets(longstring,1000,stdin))!=NULL)
674 decomposeHashLine(longstring,MsgDigest_tmp,file_name);
675 if(!isProper(MsgDigest_tmp))
677 if(status != 1 && warn == 1)
679 printf("skein%dsum: %s: %d: improperly formatted skein%d checksum line\n",hashbitlen,file_name,line,hashbitlen);
685 if(file_name[0] == '*')
687 int index_file_tmp = 0;
688 for( ; file_name[index_file_tmp+1]!=0 ; index_file_tmp++)
690 file_name[index_file_tmp] = file_name[index_file_tmp + 1];
692 file_name[index_file_tmp] = 0;
693 HashBinaryMode(file_name,MsgDigest);
697 HashTextMode(file_name,MsgDigest);
700 if(!strcmp(MsgDigest,MsgDigest_tmp))
704 printf("%s: OK\n",file_name);
709 printf("%s: FAILED\n",file_name);
715 printf("skein%dsum: WARNING: %d of %d computed checksums did NOT match\n",
716 hashbitlen,NoMatch,Computed);
720 printf("skein%dsum: WARNING: %d line is improperly formatted\n",hashbitlen,NotProper);
729 if ((errorFound != 1) && (argc == 1 || (hashString == 1 && number_files == 0)))
732 char longstring[1000];
734 while((fgets(stri,100,stdin))!=NULL)
736 strcat(longstring,stri);
738 HashStringMode(longstring,MsgDigest);
739 printf("%s -\n",MsgDigest);