0ea815bbda0f965110f3c8cfee10e4d38779a67b
[skeinsum.git] / skein_cli.c
1 /* Copyright (C) 2014 2015 Jason Self <j@jxself.org>
2
3 This file is part of skeinsum.
4
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.
9
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.
14
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/>.
17 */
18
19 #include "config.h"
20
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <dirent.h>
26 #include <getopt.h>
27 #include <malloc.h>
28 #include <math.h>
29 #include <glob.h>
30 #include <sys/stat.h>
31 #include <errno.h>
32 #include "SHA3api_ref.h"
33
34 #define TRYHELP_GOODBYE() do { printf("Try 'skein%dsum --help' for more information.\n", hashbitlen); exit(1); } while(0)
35
36 extern const int hashbitlen;
37
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;
42
43
44 char invalidOption = 0;
45
46 enum
47 {
48   QUIET_OPTION = 11,
49   STATUS_OPTION,
50   TAG_OPTION,
51   HELP_OPTION,
52   VERSION_OPTION
53 };
54
55 static struct option const long_options[] =
56 {
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 },
66   { NULL, 0, NULL, 0 }
67 };
68
69 void printHexMsg(unsigned char *ptr, int size, char result[])
70 {
71         int i = 0;
72         int j = 0;
73         char High, Low;
74         for (i = 0 ; i < size ; i ++)
75         {
76                 High = ptr[i]>>4;
77                 Low = ptr[i]&0x0F;
78
79                 if (High >= 0xA && High <= 0xF)
80                         High += 0x37;
81                 else if(High >= 0x0 && High <= 0x9)
82                         High += 0x30;
83
84                 if (Low >= 0xA && Low <= 0xF)
85                         Low += 0x37;
86                 else if(Low >= 0x0 && Low <= 0x9)
87                         Low += 0x30;
88
89                 result[j++] = High;
90                 result[j++] = Low;
91         }
92         result[i] = 0;
93 }
94
95 int HashWithMode(char file_name[], char MsgDigest[], char mode)
96 {
97         /* Try to get file info */
98         struct stat st;
99         if (stat(file_name, &st) < 0) {
100                 printf("skein%dsum: %s: STAT FAILED: %s\n", hashbitlen, file_name, strerror(errno));
101                 return -1;
102         }
103
104         /* Get filesize */
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);
109         }
110
111         /* Open file */
112         FILE *fp_in = fopen(file_name, (mode == 't' ? "r" : "rb") );
113         if (!fp_in) {
114                 printf("skein%dsum: %s: OPEN FAILED: %s\n", hashbitlen, file_name, strerror(errno));
115                 return -1;
116         }
117
118         /* Allocate buffer */
119         size_t bufsize = fsize + 1;
120         if (bufsize < input_minbufsize)
121                 bufsize = input_minbufsize;
122
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);
127         }
128
129         if (!readbuf) {
130                 printf("skein%dsum: %s: MEM FAILED: %s\n", hashbitlen, file_name, strerror(errno));
131                 fclose(fp_in);
132                 return -1;
133         }
134
135         /* Read contents */
136         size_t readpos = 0, readed, total_readed = 0;
137
138         while (1) {
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)
144                         break;
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);
148                 char *newbuf = NULL;
149                 if (bufsize * 2 > bufsize)  /* ..check overflow */
150                         newbuf = realloc(readbuf, bufsize * 2);
151                 if (!newbuf) {
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);
154                         break;
155                 }
156                 readbuf = newbuf;
157                 readpos += readed;
158                 bufsize *= 2;
159         }
160
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));
164         }
165
166         fclose(fp_in);
167
168         unsigned char output[hashbitlen/4];
169         Hash(hashbitlen, (unsigned char*) readbuf, total_readed, output);
170
171         free(readbuf);
172
173         printHexMsg(output,hashbitlen/4,MsgDigest);
174
175         return 1;
176 }
177
178 int HashTextMode(char file_name[], char MsgDigest[])
179 {
180         return HashWithMode(file_name, MsgDigest, 't');
181 }
182
183 int HashBinaryMode(char file_name[], char MsgDigest[])
184 {
185         return HashWithMode(file_name, MsgDigest, 'b');
186 }
187
188 void HashStringMode(char Msg[],char MsgDigest[])
189 {
190         unsigned char output[hashbitlen/4];
191         Hash(hashbitlen,(unsigned char*) Msg,sizeof(Msg),output);
192         printHexMsg(output,hashbitlen/4,MsgDigest);
193 }
194
195 void HashStdin(char MsgDigest[], int tag)
196 {
197         char stri[100];
198         char longstring[1000];
199         longstring[0] = 0;
200         while(fgets(stri,100,stdin))
201                 strcat(longstring, stri);
202         HashStringMode(longstring, MsgDigest);
203         if (tag) {
204                 printf("skein%d_v%s (-) = %s\n", hashbitlen, skeinVersion, MsgDigest);
205         } else {
206                 printf("%s *-\n", MsgDigest);
207         }
208 }
209
210 int isProper(char MsgDigest[])
211 {
212         if ((strlen(MsgDigest) - hashbitlen/4) != 0)
213                 return 0;
214         int index = 0;
215         char c = 0;
216         for(index = 0; index < strlen(MsgDigest);index++)
217         {
218                 c = MsgDigest[index];
219                 if(!(( c >= '0' && c <= '9' ) || ( c >= 'A' && c <= 'F')))
220                         return 0;
221         }
222
223         return 1;
224 }
225
226
227 int decomposeHashLine(char hash[], char MsgDigest_tmp[], char file_tmp[])
228 {
229         char c = 0;
230         int i = 0 , j =0;
231         int isTagFile = 0;
232         char alg[20];
233         char tmp[1000];
234         while(((c = hash[i])!=' ')&&((c = hash[i])!='_'))
235         {
236                 tmp[i] = hash[i];
237                 i++;
238         }
239         tmp[i] = 0;
240
241         sprintf(alg,"skein%d",hashbitlen);
242         if(!strcmp(alg,tmp))
243         {
244                 isTagFile = 1;
245         }
246
247         if(isTagFile == 0)
248         {
249                 strcpy(MsgDigest_tmp,tmp);
250                 i++;
251                 while((c = hash[i])!= '\n')
252                 {
253                         file_tmp[j] = hash[i];
254                         i++;
255                         j++;
256                 }
257                 file_tmp[j] = 0;
258         }
259         else if((hash[i]=='_')&&(hash[i+1]=='v'))
260         {
261                 i = i + 2;
262                 j = 0;
263                 char version[5];
264                 while((c = hash[i])!=' ')
265                 {
266                         version[j] = hash[i];
267                         i++;
268                         j++;
269                 }
270                 version[i] = 0;
271                 float vers = 0, skeinVers = 0;
272                 sscanf(version,"%f",&vers);
273                 sscanf(skeinVersion,"%f",&skeinVers);
274
275                 j = 0;
276                 i = i + 2;
277                 while((c = hash[i])!=')')
278                 {
279                         file_tmp[j] = hash[i];
280                         i++;
281                         j++;
282                 }
283                 file_tmp[j] = 0;
284
285                 i = i + 4;
286                 j = 0;
287                 while((c = hash[i])!='\n')
288                 {
289                         MsgDigest_tmp[j] = hash[i];
290                         i++;
291                         j++;
292                 }
293                 MsgDigest_tmp[j] = 0;
294
295                 if(skeinVers < vers)
296                 {//version newer than mine
297                         return (-1);
298                 }
299                 else if (skeinVers > vers)
300                 {//going to use older version than mine
301                         return (0) ;
302                 }
303                 else
304                 { //versions match
305                         return (1);
306                 }
307         }
308         return 1;
309
310 }
311
312 void print_version(void)
313 {
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");
319         exit(1);
320 }
321
322 void print_usage(void)
323 {
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");
327         printf("\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");
332         printf("\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");
337         printf("\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");
341         printf("\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");
347         exit(1);
348 }
349
350
351
352 int main(int argc, char** argv)
353 {
354         char MsgDigest[hashbitlen/2];
355         char *list_files[MaxNmberFiles];
356         int number_files = 0;
357         int binary = -1,
358                  check = -1,
359                  quiet = -1,
360                  warn = -1,
361                  status = -1,
362                  tag = -1,
363                  hashString = -1;
364
365         int errorFound = 0;
366         int opt = 0;
367 /*****************************************************************************************
368  ************************************* GETTING DATA ***********************************
369  *****************************************************************************************/
370         while ((opt = getopt_long (argc, argv, "bctw", long_options, NULL)) != -1)
371         {
372                 switch (opt) {
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;
381                         
382                         case HELP_OPTION    : print_usage();   /* ..never returns */
383                         case VERSION_OPTION : print_version(); /* ..never returns */
384
385                         default: TRYHELP_GOODBYE();
386                 }
387         }
388
389         for (; optind < argc; ++optind)
390         {
391                 struct stat s;
392                 if (stat(argv[optind], &s) < 0)
393                 {
394                         printf("skein%dsum: %s: no such file or directory\n", hashbitlen, argv[optind]);
395                         errorFound = 1;
396                 }
397                 else if (s.st_mode & (S_IFREG|S_IFBLK))  /* ..regular file or block device? */
398                 {
399                         if (number_files < MaxNmberFiles) {
400                                 list_files[number_files++] = argv[optind];
401                         } else {
402                                 printf("skein%dsum: %s: ignore because filelist is too long\n",
403                                         hashbitlen, argv[optind]);
404                         }
405                 }
406                 else if (s.st_mode & S_IFDIR)
407                 {
408                         printf("skein%dsum: %s: is a directory\n",hashbitlen,argv[optind]);
409                         errorFound = 1;
410                 }
411                 else
412                 {
413                         printf("skein%dsum: %s: wrong filetype 0x%Xu\n",
414                                 hashbitlen, argv[optind], s.st_mode);
415                                 errorFound = 1;
416                 }
417         }
418
419 /*****************************************************************************************
420  ************************************* PROCESSING DATA ***********************************
421  *****************************************************************************************/
422         if(argc > 1 && binary == 1)
423         {
424                 if (check == 1 || quiet == 1 || warn == 1 || status == 1)
425                 {
426                         printf("skein%dsum: the --binary and --text options are meaningless when verifying checksums\n",hashbitlen);
427                         TRYHELP_GOODBYE();
428                 }
429                 if(number_files > 0)
430                 {
431                         int index_files = 0;
432                         while(index_files < number_files)
433                         {
434                                 if(hashString == 1)
435                                 {
436                                         printf("skein%dsum: %s: No such file or directory\n",hashbitlen,list_files[index_files]);
437                                         index_files++;
438                                         continue;
439                                 }
440                                 if(HashBinaryMode(list_files[index_files],MsgDigest)!=-1)
441                                 {
442                                         if(tag == 1)
443                                         {
444                                                 printf("skein%d_v%s (%s) = %s\n",hashbitlen,skeinVersion,list_files[index_files],MsgDigest);
445                                         }
446                                         else
447                                         {
448                                                 printf("%s ",MsgDigest);
449                                                 printf("*%s\n",list_files[index_files]);
450                                         }
451                                 }
452                                 index_files++;
453                         }
454                 }
455                 else if(errorFound != 1)
456                 {
457                         HashStdin(MsgDigest, tag);
458                 }
459         }
460
461         else if (argc > 1 && binary == 0) // Text Mode
462         {
463                 if (check == 1 || quiet == 1 || warn == 1 || status == 1)
464                 {
465                         printf("skein%dsum: the --binary and --text options are meaningless when verifying checksums\n",hashbitlen);
466                         TRYHELP_GOODBYE();
467                 }
468                 if(number_files > 0)
469                 {
470                         int index_files = 0;
471                         while(index_files < number_files)
472                         {
473                                 if(HashTextMode(list_files[index_files],MsgDigest)!=-1)
474                                 {
475                                         if(tag == 1)
476                                         {
477                                                 printf("skein%d_v%s (%s) = %s\n",hashbitlen,skeinVersion,list_files[index_files],MsgDigest);
478                                         }
479                                         else
480                                         {
481                                                 printf("%s ",MsgDigest);
482                                                 printf("%s\n",list_files[index_files]);
483                                         }
484                                 }
485                                 index_files++;
486                         }
487                 }
488                 else if(errorFound != 1)
489                 {
490                         HashStdin(MsgDigest, tag);
491                 }
492         }
493
494         else if (argc > 1 && binary == -1)
495         {
496                 if(check == -1)
497                 {// hashing stdin entries
498                         if(quiet == 1 || status == 1 || warn == 1)
499                         {
500                                 if(quiet == 1)
501                                         printf("skein%dsum: the --quiet option is meaningful only when verifying checksums\n",hashbitlen);
502                                 if(status ==1)
503                                         printf("skein%dsum: the --status option is meaningful only when verifying checksums\n",hashbitlen);
504                                 if(warn == 1)
505                                         printf("skein%dsum: the --warn option is meaningful only when verifying checksums\n",hashbitlen);
506                                 TRYHELP_GOODBYE();
507                         }
508                         if(number_files > 0)
509                         {// hashing files
510                                 int index_files = 0;
511                                 while(index_files < number_files)
512                                 {
513                                         if(HashTextMode(list_files[index_files],MsgDigest)!=-1)
514                                         {
515                                                 if(tag == 1)
516                                                 {
517                                                         printf("skein%d_v%s (%s) = %s\n",hashbitlen,skeinVersion,list_files[index_files],MsgDigest);
518                                                 }
519                                                 else
520                                                 {
521                                                         printf("%s ",MsgDigest);
522                                                         printf("%s\n",list_files[index_files]);
523                                                 }
524                                         }
525                                         index_files++;
526                                 }
527                         }
528                         else if(errorFound != 1)
529                         {
530                                 HashStdin(MsgDigest, tag);
531                         }
532                 }
533                 else if(check == 1)
534                 {
535                         if(tag == 1)
536                         {
537                                 printf("skein%dsum: the --tag option is meaningless when verifying checksums\n",hashbitlen);
538                                 TRYHELP_GOODBYE();
539                         }
540                         int index_files = 0;
541                         while(index_files < number_files)
542                         {
543                                 FILE *fp;
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]);
547                                 //show everything
548                                 int line = 0;
549                                 fp = fopen(file_name,"r");
550                                 if(fp!=NULL)
551                                 {
552                                         while(fgets(hash,500,fp)!=NULL)
553                                         {
554                                                 line ++;
555                                                 Computed++;
556                                                 unsigned int hashVersion = decomposeHashLine(hash,MsgDigest_tmp,file_tmp);
557                                                 if(hashVersion == -1)
558                                                 {
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");
561                                                         continue;
562                                                 }
563                                                 else if( hashVersion == 0)
564                                                 {
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");
567                                                         continue;
568                                                 }
569                                                 else
570                                                 {
571                                                         if(!isProper(MsgDigest_tmp))
572                                                         {
573                                                                 if(status != 1 && warn == 1)
574                                                                 {
575                                                                         printf("skein%dsum: %s: %d: improperly formatted skein%d checksum line\n",hashbitlen,file_tmp,line,hashbitlen);
576                                                                 }
577                                                                 NotProper ++;
578                                                                 NoMatch ++;
579                                                                 continue;
580                                                         }
581                                                         if(file_tmp[0] == '*')
582                                                         {
583                                                                 int index_file_tmp = 0;
584                                                                 for( ; file_tmp[index_file_tmp+1]!=0 ; index_file_tmp++)
585                                                                 {
586                                                                         file_tmp[index_file_tmp] = file_tmp[index_file_tmp + 1];
587                                                                 }
588                                                                 file_tmp[index_file_tmp] = 0;
589                                                                 HashBinaryMode(file_tmp,MsgDigest);
590                                                         }
591                                                         else
592                                                         {
593                                                                 HashTextMode(file_tmp,MsgDigest);
594                                                         }
595
596                                                         if(!strcmp(MsgDigest,MsgDigest_tmp))
597                                                         {
598                                                                 if(quiet != 1)
599                                                                 {
600                                                                         printf("%s: OK\n",file_tmp);
601                                                                 }
602                                                         }
603                                                         else
604                                                         {
605                                                                 printf("%s: FAILED\n",file_tmp);
606                                                                 NoMatch ++;
607                                                         }
608                                                 }
609                                         }
610                                         if(NoMatch)
611                                         {
612                                                 printf("skein%dsum: WARNING: %d of %d computed checksums did NOT match\n",
613                                                                 hashbitlen,NoMatch,Computed);
614                                         }
615                                         if(NotProper)
616                                         {
617                                                 printf("skein%dsum: WARNING: %d line is improperly formatted\n",hashbitlen,NotProper);
618                                         }
619                                 }
620                                 else
621                                 {
622                                         printf("skein%dsum: %s: No such file or directory\n",hashbitlen,file_name);
623                                 }
624                                 index_files++;
625
626                         }
627
628                         if(number_files  == 0)
629                         {
630                                 char longstring[1000];
631                                 char file_name[30];
632                                 char MsgDigest_tmp[hashbitlen/2];
633                                 int Computed = 0, NotProper = 0, NoMatch = 0 , line = 0;
634                                 while((fgets(longstring,1000,stdin))!=NULL)
635                                 {
636                                         Computed++;
637                                         line ++;
638                                         decomposeHashLine(longstring,MsgDigest_tmp,file_name);
639                                         if(!isProper(MsgDigest_tmp))
640                                         {
641                                                 if(status != 1 && warn == 1)
642                                                 {
643                                                         printf("skein%dsum: %s: %d: improperly formatted skein%d checksum line\n",hashbitlen,file_name,line,hashbitlen);
644                                                 }
645                                                 NotProper ++;
646                                                 NoMatch ++;
647                                                 continue;
648                                         }
649                                         if(file_name[0] == '*')
650                                         {
651                                                 int index_file_tmp = 0;
652                                                 for( ; file_name[index_file_tmp+1]!=0 ; index_file_tmp++)
653                                                 {
654                                                         file_name[index_file_tmp] = file_name[index_file_tmp + 1];
655                                                 }
656                                                 file_name[index_file_tmp] = 0;
657                                                 HashBinaryMode(file_name,MsgDigest);
658                                         }
659                                         else
660                                         {
661                                                 HashTextMode(file_name,MsgDigest);
662                                         }
663
664                                         if(!strcmp(MsgDigest,MsgDigest_tmp))
665                                         {
666                                                 if(quiet != 1)
667                                                 {
668                                                         printf("%s: OK\n",file_name);
669                                                 }
670                                         }
671                                         else
672                                         {
673                                                 printf("%s: FAILED\n",file_name);
674                                                 NoMatch ++;
675                                         }
676                                 }
677                                 if(NoMatch)
678                                 {
679                                         printf("skein%dsum: WARNING: %d of %d computed checksums did NOT match\n",
680                                                         hashbitlen,NoMatch,Computed);
681                                 }
682                                 if(NotProper)
683                                 {
684                                         printf("skein%dsum: WARNING: %d line is improperly formatted\n",hashbitlen,NotProper);
685                                 }
686
687                         }
688
689
690                 }
691         }
692
693         if (!errorFound && (argc == 1 || (hashString == 1 && !number_files)))
694                 HashStdin(MsgDigest, 0);
695
696         return 1;
697 }