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 #define TRYHELP_GOODBYE() do { printf("Try 'skein%dsum --help' for more information.\n", hashbitlen); exit(1); } while(0)
36 extern const int hashbitlen;
38 #define MaxNmberFiles 10
39 #define skeinVersion "1.3"
40 const size_t input_minbufsize = 32 * 1024;
41 const size_t input_maxbufsize = 32 * 1024 * 1024;
44 char invalidOption = 0;
55 static struct option const long_options[] =
57 { "binary", no_argument, NULL, 'b' },
58 { "check", no_argument, NULL, 'c' },
59 { "quiet", no_argument, NULL, QUIET_OPTION },
60 { "status", no_argument, NULL, STATUS_OPTION },
61 { "text", no_argument, NULL, 't' },
62 { "warn", no_argument, NULL, 'w' },
63 { "tag", no_argument, NULL, TAG_OPTION },
64 { "help", no_argument, NULL, HELP_OPTION },
65 { "version", no_argument, NULL, VERSION_OPTION },
69 void printHexMsg(unsigned char *ptr, int size, char result[])
74 for (i = 0 ; i < size ; i ++)
79 if (High >= 0xA && High <= 0xF)
81 else if(High >= 0x0 && High <= 0x9)
84 if (Low >= 0xA && Low <= 0xF)
86 else if(Low >= 0x0 && Low <= 0x9)
95 int HashWithMode(char file_name[], char MsgDigest[], char mode)
97 /* Try to get file info */
99 if (stat(file_name, &st) < 0) {
100 printf("skein%dsum: %s: STAT FAILED: %s\n", hashbitlen, file_name, strerror(errno));
105 size_t fsize = st.st_size;
106 if (fsize != st.st_size) {
107 printf("skein%dsum: %s: SIZE WARNING: filesize %llu is too big for reading into memory!\n",
108 hashbitlen, file_name, (long long unsigned)st.st_size);
112 FILE *fp_in = fopen(file_name, (mode == 't' ? "r" : "rb") );
114 printf("skein%dsum: %s: OPEN FAILED: %s\n", hashbitlen, file_name, strerror(errno));
118 /* Allocate buffer */
119 size_t bufsize = fsize + 1;
120 if (bufsize < input_minbufsize)
121 bufsize = input_minbufsize;
123 char *readbuf = malloc (bufsize);
124 if (!readbuf && bufsize > input_maxbufsize) { /* ..Try to get contents by smaller portions */
125 bufsize = input_maxbufsize;
126 readbuf = malloc (bufsize);
130 printf("skein%dsum: %s: MEM FAILED: %s\n", hashbitlen, file_name, strerror(errno));
136 size_t readpos = 0, readed, total_readed = 0;
139 size_t maxread = bufsize - readpos;
140 readed = fread(readbuf, 1, maxread, fp_in);
141 if (readed > 0 && readed <= maxread)
142 total_readed += readed;
143 if (readed != maxread)
145 if (getenv("SKEIN_DEBUG"))
146 printf("DEBUG: bufsize=%u (0x%x), readpos=%u (0x%x), maxread=%u (0x%x), total=%u (0x%x)\n",
147 bufsize, bufsize, readpos, readpos, maxread, maxread, total_readed, total_readed);
149 if (bufsize * 2 > bufsize) /* ..check overflow */
150 newbuf = realloc(readbuf, bufsize * 2);
152 printf("skein%dsum: %s: MEM WARNING: %u bytes only readed from %llu\n",
153 hashbitlen, file_name, total_readed, (long long unsigned)st.st_size);
161 if (total_readed < st.st_size && total_readed < bufsize) {
162 printf("skein%dsum: %s: READ WARNING: filesize=%llu, readed=%u, %s\n",
163 hashbitlen, file_name, (long long unsigned)st.st_size, total_readed, strerror(errno));
168 unsigned char output[hashbitlen/4];
169 Hash(hashbitlen, (unsigned char*) readbuf, total_readed, output);
173 printHexMsg(output,hashbitlen/4,MsgDigest);
178 int HashTextMode(char file_name[], char MsgDigest[])
180 return HashWithMode(file_name, MsgDigest, 't');
183 int HashBinaryMode(char file_name[], char MsgDigest[])
185 return HashWithMode(file_name, MsgDigest, 'b');
188 void HashStringMode(char Msg[],char MsgDigest[])
190 unsigned char output[hashbitlen/4];
191 Hash(hashbitlen,(unsigned char*) Msg,sizeof(Msg),output);
192 printHexMsg(output,hashbitlen/4,MsgDigest);
195 void HashStdin(char MsgDigest[], int tag)
198 char longstring[1000];
200 while(fgets(stri,100,stdin))
201 strcat(longstring, stri);
202 HashStringMode(longstring, MsgDigest);
204 printf("skein%d_v%s (-) = %s\n", hashbitlen, skeinVersion, MsgDigest);
206 printf("%s *-\n", MsgDigest);
210 int isProper(char MsgDigest[])
212 if ((strlen(MsgDigest) - hashbitlen/4) != 0)
216 for(index = 0; index < strlen(MsgDigest);index++)
218 c = MsgDigest[index];
219 if(!(( c >= '0' && c <= '9' ) || ( c >= 'A' && c <= 'F')))
227 int decomposeHashLine(char hash[], char MsgDigest_tmp[], char file_tmp[])
234 while(((c = hash[i])!=' ')&&((c = hash[i])!='_'))
241 sprintf(alg,"skein%d",hashbitlen);
249 strcpy(MsgDigest_tmp,tmp);
251 while((c = hash[i])!= '\n')
253 file_tmp[j] = hash[i];
259 else if((hash[i]=='_')&&(hash[i+1]=='v'))
264 while((c = hash[i])!=' ')
266 version[j] = hash[i];
271 float vers = 0, skeinVers = 0;
272 sscanf(version,"%f",&vers);
273 sscanf(skeinVersion,"%f",&skeinVers);
277 while((c = hash[i])!=')')
279 file_tmp[j] = hash[i];
287 while((c = hash[i])!='\n')
289 MsgDigest_tmp[j] = hash[i];
293 MsgDigest_tmp[j] = 0;
296 {//version newer than mine
299 else if (skeinVers > vers)
300 {//going to use older version than mine
312 void print_version(void)
314 printf("skein%dsum 1.0\n", hashbitlen);
315 printf("License GPLv3+: GNU GPL version 3 or later\n");
316 printf("<http://gnu.org/licenses/gpl.html>\n");
317 printf("This is free software: you are free to change and redistribute it.\n");
318 printf("There is NO WARRANTY, to the extent permitted by law.\n");
322 void print_usage(void)
324 printf("Usage: skein%dsum [OPTION]... [FILE]...\n",hashbitlen);
325 printf("Print or check skein (%d-bit) checksums.\n",hashbitlen);
326 printf("With no FILE, or when FILE is -, read standard input.\n");
328 printf("-b, --binary read in binary mode\n");
329 printf("-c, --check read skein sums from the FILEs and check them\n");
330 printf("--tag create a BSD-style checksum\n");
331 printf("-t, --text read in text mode (default)\n");
333 printf("The following three options are useful only when verifying checksums:\n");
334 printf("--quiet don't print OK for each successfully verified file\n");
335 printf("--status don't output anything, status code shows success\n");
336 printf("-w, --warn warn about improperly formatted checksum lines\n");
338 printf("--strict with --check, exit non-zero for any invalid input\n");
339 printf("--help display this help and exit\n");
340 printf("--version output version information and exit\n");
342 printf("The sums are computed as described in version 1.3 of the Skein\n");
343 printf("specification. When checking, the input should be a former output of\n");
344 printf("this program. The default mode is to print a line with checksum, a\n");
345 printf("character indicating input mode ('*' for binary, space for text), and\n");
346 printf("name for each FILE.\n");
352 int main(int argc, char** argv)
354 char MsgDigest[hashbitlen/2];
355 char *list_files[MaxNmberFiles];
356 int number_files = 0;
367 /*****************************************************************************************
368 ************************************* GETTING DATA ***********************************
369 *****************************************************************************************/
370 while ((opt = getopt_long (argc, argv, "bctw", long_options, NULL)) != -1)
373 case 0 : hashString = 1; break;
374 case 'b' : binary = 1; break;
375 case 't' : binary = 0; break;
376 case 'c' : check = 1; break;
377 case 'w' : warn = 1; break;
378 case QUIET_OPTION : quiet = 1; break;
379 case STATUS_OPTION : status = 1; break;
380 case TAG_OPTION : tag = 1; break;
382 case HELP_OPTION : print_usage(); /* ..never returns */
383 case VERSION_OPTION : print_version(); /* ..never returns */
385 default: TRYHELP_GOODBYE();
389 for (; optind < argc; ++optind)
392 if (stat(argv[optind], &s) < 0)
394 printf("skein%dsum: %s: no such file or directory\n", hashbitlen, argv[optind]);
397 else if (s.st_mode & (S_IFREG|S_IFBLK)) /* ..regular file or block device? */
399 if (number_files < MaxNmberFiles) {
400 list_files[number_files++] = argv[optind];
402 printf("skein%dsum: %s: ignore because filelist is too long\n",
403 hashbitlen, argv[optind]);
406 else if (s.st_mode & S_IFDIR)
408 printf("skein%dsum: %s: is a directory\n",hashbitlen,argv[optind]);
413 printf("skein%dsum: %s: wrong filetype 0x%Xu\n",
414 hashbitlen, argv[optind], s.st_mode);
419 /*****************************************************************************************
420 ************************************* PROCESSING DATA ***********************************
421 *****************************************************************************************/
422 if(argc > 1 && binary == 1)
424 if (check == 1 || quiet == 1 || warn == 1 || status == 1)
426 printf("skein%dsum: the --binary and --text options are meaningless when verifying checksums\n",hashbitlen);
432 while(index_files < number_files)
436 printf("skein%dsum: %s: No such file or directory\n",hashbitlen,list_files[index_files]);
440 if(HashBinaryMode(list_files[index_files],MsgDigest)!=-1)
444 printf("skein%d_v%s (%s) = %s\n",hashbitlen,skeinVersion,list_files[index_files],MsgDigest);
448 printf("%s ",MsgDigest);
449 printf("*%s\n",list_files[index_files]);
455 else if(errorFound != 1)
457 HashStdin(MsgDigest, tag);
461 else if (argc > 1 && binary == 0) // Text Mode
463 if (check == 1 || quiet == 1 || warn == 1 || status == 1)
465 printf("skein%dsum: the --binary and --text options are meaningless when verifying checksums\n",hashbitlen);
471 while(index_files < number_files)
473 if(HashTextMode(list_files[index_files],MsgDigest)!=-1)
477 printf("skein%d_v%s (%s) = %s\n",hashbitlen,skeinVersion,list_files[index_files],MsgDigest);
481 printf("%s ",MsgDigest);
482 printf("%s\n",list_files[index_files]);
488 else if(errorFound != 1)
490 HashStdin(MsgDigest, tag);
494 else if (argc > 1 && binary == -1)
497 {// hashing stdin entries
498 if(quiet == 1 || status == 1 || warn == 1)
501 printf("skein%dsum: the --quiet option is meaningful only when verifying checksums\n",hashbitlen);
503 printf("skein%dsum: the --status option is meaningful only when verifying checksums\n",hashbitlen);
505 printf("skein%dsum: the --warn option is meaningful only when verifying checksums\n",hashbitlen);
511 while(index_files < number_files)
513 if(HashTextMode(list_files[index_files],MsgDigest)!=-1)
517 printf("skein%d_v%s (%s) = %s\n",hashbitlen,skeinVersion,list_files[index_files],MsgDigest);
521 printf("%s ",MsgDigest);
522 printf("%s\n",list_files[index_files]);
528 else if(errorFound != 1)
530 HashStdin(MsgDigest, tag);
537 printf("skein%dsum: the --tag option is meaningless when verifying checksums\n",hashbitlen);
541 while(index_files < number_files)
544 char hash[500], file_name[100], file_tmp[100], MsgDigest_tmp[hashbitlen/2];
545 int NoMatch = 0, NotProper = 0, Computed = 0;
546 strcpy(file_name,list_files[index_files]);
549 fp = fopen(file_name,"r");
552 while(fgets(hash,500,fp)!=NULL)
556 unsigned int hashVersion = decomposeHashLine(hash,MsgDigest_tmp,file_tmp);
557 if(hashVersion == -1)
559 printf("skein%d: %s is using newer version of skein%d algorithm\n",hashbitlen,file_tmp,hashbitlen);
560 printf("You should update your algorithm\n");
563 else if( hashVersion == 0)
565 printf("skein%d: %s is using an older version of skein%d algorithm\n",hashbitlen,file_tmp,hashbitlen);
566 printf("You should use the older algorithm\n");
571 if(!isProper(MsgDigest_tmp))
573 if(status != 1 && warn == 1)
575 printf("skein%dsum: %s: %d: improperly formatted skein%d checksum line\n",hashbitlen,file_tmp,line,hashbitlen);
581 if(file_tmp[0] == '*')
583 int index_file_tmp = 0;
584 for( ; file_tmp[index_file_tmp+1]!=0 ; index_file_tmp++)
586 file_tmp[index_file_tmp] = file_tmp[index_file_tmp + 1];
588 file_tmp[index_file_tmp] = 0;
589 HashBinaryMode(file_tmp,MsgDigest);
593 HashTextMode(file_tmp,MsgDigest);
596 if(!strcmp(MsgDigest,MsgDigest_tmp))
600 printf("%s: OK\n",file_tmp);
605 printf("%s: FAILED\n",file_tmp);
612 printf("skein%dsum: WARNING: %d of %d computed checksums did NOT match\n",
613 hashbitlen,NoMatch,Computed);
617 printf("skein%dsum: WARNING: %d line is improperly formatted\n",hashbitlen,NotProper);
622 printf("skein%dsum: %s: No such file or directory\n",hashbitlen,file_name);
628 if(number_files == 0)
630 char longstring[1000];
632 char MsgDigest_tmp[hashbitlen/2];
633 int Computed = 0, NotProper = 0, NoMatch = 0 , line = 0;
634 while((fgets(longstring,1000,stdin))!=NULL)
638 decomposeHashLine(longstring,MsgDigest_tmp,file_name);
639 if(!isProper(MsgDigest_tmp))
641 if(status != 1 && warn == 1)
643 printf("skein%dsum: %s: %d: improperly formatted skein%d checksum line\n",hashbitlen,file_name,line,hashbitlen);
649 if(file_name[0] == '*')
651 int index_file_tmp = 0;
652 for( ; file_name[index_file_tmp+1]!=0 ; index_file_tmp++)
654 file_name[index_file_tmp] = file_name[index_file_tmp + 1];
656 file_name[index_file_tmp] = 0;
657 HashBinaryMode(file_name,MsgDigest);
661 HashTextMode(file_name,MsgDigest);
664 if(!strcmp(MsgDigest,MsgDigest_tmp))
668 printf("%s: OK\n",file_name);
673 printf("%s: FAILED\n",file_name);
679 printf("skein%dsum: WARNING: %d of %d computed checksums did NOT match\n",
680 hashbitlen,NoMatch,Computed);
684 printf("skein%dsum: WARNING: %d line is improperly formatted\n",hashbitlen,NotProper);
693 if (!errorFound && (argc == 1 || (hashString == 1 && !number_files)))
694 HashStdin(MsgDigest, 0);