Better vertical alignment
[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 extern const int hashbitlen;
35
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;
40
41
42 char invalidOption = 0;
43
44 enum
45 {
46   QUIET_OPTION = 11,
47   STATUS_OPTION,
48   TAG_OPTION,
49   HELP_OPTION,
50   VERSION_OPTION
51 };
52
53 static struct option const long_options[] =
54 {
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 },
64   { NULL, 0, NULL, 0 }
65 };
66
67 void printHexMsg(unsigned char *ptr, int size, char result[])
68 {
69         int i = 0;
70         int j = 0;
71         char High, Low;
72         for (i = 0 ; i < size ; i ++)
73         {
74                 High = ptr[i]>>4;
75                 Low = ptr[i]&0x0F;
76
77                 if (High >= 0xA && High <= 0xF)
78                         High += 0x37;
79                 else if(High >= 0x0 && High <= 0x9)
80                         High += 0x30;
81
82                 if (Low >= 0xA && Low <= 0xF)
83                         Low += 0x37;
84                 else if(Low >= 0x0 && Low <= 0x9)
85                         Low += 0x30;
86
87                 result[j++] = High;
88                 result[j++] = Low;
89         }
90         result[i] = 0;
91 }
92
93 int HashWithMode(char file_name[], char MsgDigest[], char mode)
94 {
95         /* Try to get file info */
96         struct stat st;
97         if (stat(file_name, &st) < 0) {
98                 printf("skein%dsum: %s: STAT FAILED: %s\n", hashbitlen, file_name, strerror(errno));
99                 return -1;
100         }
101
102         /* Get filesize */
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);
107         }
108
109         /* Open file */
110         FILE *fp_in = fopen(file_name, (mode == 't' ? "r" : "rb") );
111         if (!fp_in) {
112                 printf("skein%dsum: %s: OPEN FAILED: %s\n", hashbitlen, file_name, strerror(errno));
113                 return -1;
114         }
115
116         /* Allocate buffer */
117         size_t bufsize = fsize + 1;
118         if (bufsize < input_minbufsize)
119                 bufsize = input_minbufsize;
120
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);
125         }
126
127         if (!readbuf) {
128                 printf("skein%dsum: %s: MEM FAILED: %s\n", hashbitlen, file_name, strerror(errno));
129                 fclose(fp_in);
130                 return -1;
131         }
132
133         /* Read contents */
134         size_t readpos = 0, readed, total_readed = 0;
135
136         while (1) {
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)
142                         break;
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);
146                 char *newbuf = NULL;
147                 if (bufsize * 2 > bufsize)  /* ..check overflow */
148                         newbuf = realloc(readbuf, bufsize * 2);
149                 if (!newbuf) {
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);
152                         break;
153                 }
154                 readbuf = newbuf;
155                 readpos += readed;
156                 bufsize *= 2;
157         }
158
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));
162         }
163
164         fclose(fp_in);
165
166         unsigned char output[hashbitlen/4];
167         Hash(hashbitlen, (unsigned char*) readbuf, total_readed, output);
168
169         free(readbuf);
170
171         printHexMsg(output,hashbitlen/4,MsgDigest);
172
173         return 1;
174 }
175
176 int HashTextMode(char file_name[], char MsgDigest[])
177 {
178         return HashWithMode(file_name, MsgDigest, 't');
179 }
180
181 int HashBinaryMode(char file_name[], char MsgDigest[])
182 {
183         return HashWithMode(file_name, MsgDigest, 'b');
184 }
185
186 void HashStringMode(char Msg[],char MsgDigest[])
187 {
188         unsigned char output[hashbitlen/4];
189         Hash(hashbitlen,(unsigned char*) Msg,sizeof(Msg),output);
190         printHexMsg(output,hashbitlen/4,MsgDigest);
191 }
192
193 void HashStdin(char MsgDigest[], int tag)
194 {
195         char stri[100];
196         char longstring[1000];
197         longstring[0] = 0;
198         while(fgets(stri,100,stdin))
199                 strcat(longstring, stri);
200         HashStringMode(longstring, MsgDigest);
201         if (tag) {
202                 printf("skein%d_v%s (-) = %s\n", hashbitlen, skeinVersion, MsgDigest);
203         } else {
204                 printf("%s *-\n", MsgDigest);
205         }
206 }
207
208 int isProper(char MsgDigest[])
209 {
210         if ((strlen(MsgDigest) - hashbitlen/4) != 0)
211                 return 0;
212         int index = 0;
213         char c = 0;
214         for(index = 0; index < strlen(MsgDigest);index++)
215         {
216                 c = MsgDigest[index];
217                 if(!(( c >= '0' && c <= '9' ) || ( c >= 'A' && c <= 'F')))
218                         return 0;
219         }
220
221         return 1;
222 }
223
224
225 int decomposeHashLine(char hash[], char MsgDigest_tmp[], char file_tmp[])
226 {
227         char c = 0;
228         int i = 0 , j =0;
229         int isTagFile = 0;
230         char alg[20];
231         char tmp[1000];
232         while(((c = hash[i])!=' ')&&((c = hash[i])!='_'))
233         {
234                 tmp[i] = hash[i];
235                 i++;
236         }
237         tmp[i] = 0;
238
239         sprintf(alg,"skein%d",hashbitlen);
240         if(!strcmp(alg,tmp))
241         {
242                 isTagFile = 1;
243         }
244
245         if(isTagFile == 0)
246         {
247                 strcpy(MsgDigest_tmp,tmp);
248                 i++;
249                 while((c = hash[i])!= '\n')
250                 {
251                         file_tmp[j] = hash[i];
252                         i++;
253                         j++;
254                 }
255                 file_tmp[j] = 0;
256         }
257         else if((hash[i]=='_')&&(hash[i+1]=='v'))
258         {
259                 i = i + 2;
260                 j = 0;
261                 char version[5];
262                 while((c = hash[i])!=' ')
263                 {
264                         version[j] = hash[i];
265                         i++;
266                         j++;
267                 }
268                 version[i] = 0;
269                 float vers = 0, skeinVers = 0;
270                 sscanf(version,"%f",&vers);
271                 sscanf(skeinVersion,"%f",&skeinVers);
272
273                 j = 0;
274                 i = i + 2;
275                 while((c = hash[i])!=')')
276                 {
277                         file_tmp[j] = hash[i];
278                         i++;
279                         j++;
280                 }
281                 file_tmp[j] = 0;
282
283                 i = i + 4;
284                 j = 0;
285                 while((c = hash[i])!='\n')
286                 {
287                         MsgDigest_tmp[j] = hash[i];
288                         i++;
289                         j++;
290                 }
291                 MsgDigest_tmp[j] = 0;
292
293                 if(skeinVers < vers)
294                 {//version newer than mine
295                         return (-1);
296                 }
297                 else if (skeinVers > vers)
298                 {//going to use older version than mine
299                         return (0) ;
300                 }
301                 else
302                 { //versions match
303                         return (1);
304                 }
305         }
306         return 1;
307
308 }
309
310 void print_version(void)
311 {
312         printf("skein%dsum 1.0\n", hashbitlen);
313         printf("License GPLv3+: GNU GPL version 3 or later\n");
314         printf("<http://gnu.org/licenses/gpl.html>\n");
315         printf("This is free software: you are free to change and redistribute it.\n");
316         printf("There is NO WARRANTY, to the extent permitted by law.\n");
317         exit(1);
318 }
319
320 void print_usage(void)
321 {
322         printf("Usage: skein%dsum [OPTION]... [FILE]...\n",hashbitlen);
323         printf("Print or check skein (%d-bit) checksums.\n",hashbitlen);
324         printf("With no FILE, or when FILE is -, read standard input.\n");
325         printf("\n");
326         printf("-b, --binary         read in binary mode\n");
327         printf("-c, --check          read skein sums from the FILEs and check them\n");
328         printf("--tag            create a BSD-style checksum\n");
329         printf("-t, --text           read in text mode (default)\n");
330         printf("\n");
331         printf("The following three options are useful only when verifying checksums:\n");
332         printf("--quiet          don't print OK for each successfully verified file\n");
333         printf("--status         don't output anything, status code shows success\n");
334         printf("-w, --warn           warn about improperly formatted checksum lines\n");
335         printf("\n");
336         printf("--strict         with --check, exit non-zero for any invalid input\n");
337         printf("--help     display this help and exit\n");
338         printf("--version  output version information and exit\n");
339         printf("\n");
340         printf("The sums are computed as described in version 1.3 of the Skein\n");
341         printf("specification. When checking, the input should be a former output of\n");
342         printf("this program. The default mode is to print a line with checksum, a\n");
343         printf("character indicating input mode ('*' for binary, space for text), and\n");
344         printf("name for each FILE.\n");
345         exit(1);
346 }
347
348
349
350 int main(int argc, char** argv)
351 {
352         char MsgDigest[hashbitlen/2];
353         char *list_files[MaxNmberFiles];
354         int number_files = 0;
355         int binary = -1,
356                  check = -1,
357                  quiet = -1,
358                  warn = -1,
359                  status = -1,
360                  tag = -1,
361                  hashString = -1;
362
363         int errorFound = 0;
364         int opt = 0;
365 /*****************************************************************************************
366  ************************************* GETTING DATA ***********************************
367  *****************************************************************************************/
368         while ((opt = getopt_long (argc, argv, "bctw", long_options, NULL)) != -1)
369         {
370                 switch (opt) {
371                         case 0              : hashString = 1;  break;
372                         case 'b'            : binary     = 1;  break;
373                         case 't'            : binary     = 0;  break;
374                         case 'c'            : check      = 1;  break;
375                         case 'w'            : warn       = 1;  break;
376                         case QUIET_OPTION   : quiet      = 1;  break;
377                         case STATUS_OPTION  : status     = 1;  break;
378                         case TAG_OPTION     : tag        = 1;  break;
379                         
380                         case HELP_OPTION    : print_usage();   /* ..never returns */
381                         case VERSION_OPTION : print_version(); /* ..never returns */
382
383                         default:
384                                 printf("Try `skein%dsum --help' for more information.\n",hashbitlen);
385                                 exit(1);
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                         printf("Try 'skein%dsum --help' for more information.\n",hashbitlen);
428                         exit(0);
429                 }
430                 if(number_files > 0)
431                 {
432                         int index_files = 0;
433                         while(index_files < number_files)
434                         {
435                                 if(hashString == 1)
436                                 {
437                                         printf("skein%dsum: %s: No such file or directory\n",hashbitlen,list_files[index_files]);
438                                         index_files++;
439                                         continue;
440                                 }
441                                 if(HashBinaryMode(list_files[index_files],MsgDigest)!=-1)
442                                 {
443                                         if(tag == 1)
444                                         {
445                                                 printf("skein%d_v%s (%s) = %s\n",hashbitlen,skeinVersion,list_files[index_files],MsgDigest);
446                                         }
447                                         else
448                                         {
449                                                 printf("%s ",MsgDigest);
450                                                 printf("*%s\n",list_files[index_files]);
451                                         }
452                                 }
453                                 index_files++;
454                         }
455                 }
456                 else if(errorFound != 1)
457                 {
458                         HashStdin(MsgDigest, tag);
459                 }
460         }
461
462         else if (argc > 1 && binary == 0) // Text Mode
463         {
464                 if (check == 1 || quiet == 1 || warn == 1 || status == 1)
465                 {
466                         printf("skein%dsum: the --binary and --text options are meaningless when verifying checksums\n",hashbitlen);
467                         printf("Try 'skein%dsum --help' for more information.\n",hashbitlen);
468                         exit(0);
469                 }
470                 if(number_files > 0)
471                 {
472                         int index_files = 0;
473                         while(index_files < number_files)
474                         {
475                                 if(HashTextMode(list_files[index_files],MsgDigest)!=-1)
476                                 {
477                                         if(tag == 1)
478                                         {
479                                                 printf("skein%d_v%s (%s) = %s\n",hashbitlen,skeinVersion,list_files[index_files],MsgDigest);
480                                         }
481                                         else
482                                         {
483                                                 printf("%s ",MsgDigest);
484                                                 printf("%s\n",list_files[index_files]);
485                                         }
486                                 }
487                                 index_files++;
488                         }
489                 }
490                 else if(errorFound != 1)
491                 {
492                         HashStdin(MsgDigest, tag);
493                 }
494         }
495
496         else if (argc > 1 && binary == -1)
497         {
498                 if(check == -1)
499                 {// hashing stdin entries
500                         if(quiet == 1 || status == 1 || warn == 1)
501                         {
502                                 if(quiet == 1)
503                                         printf("skein%dsum: the --quiet option is meaningful only when verifying checksums\n",hashbitlen);
504                                 if(status ==1)
505                                         printf("skein%dsum: the --status option is meaningful only when verifying checksums\n",hashbitlen);
506                                 if(warn == 1)
507                                         printf("skein%dsum: the --warn option is meaningful only when verifying checksums\n",hashbitlen);
508
509                                 printf("Try 'skein%dsum --help' for more information.\n",hashbitlen);
510                                 exit(1);
511                         }
512                         if(number_files > 0)
513                         {// hashing files
514                                 int index_files = 0;
515                                 while(index_files < number_files)
516                                 {
517                                         if(HashTextMode(list_files[index_files],MsgDigest)!=-1)
518                                         {
519                                                 if(tag == 1)
520                                                 {
521                                                         printf("skein%d_v%s (%s) = %s\n",hashbitlen,skeinVersion,list_files[index_files],MsgDigest);
522                                                 }
523                                                 else
524                                                 {
525                                                         printf("%s ",MsgDigest);
526                                                         printf("%s\n",list_files[index_files]);
527                                                 }
528                                         }
529                                         index_files++;
530                                 }
531                         }
532                         else if(errorFound != 1)
533                         {
534                                 HashStdin(MsgDigest, tag);
535                         }
536                 }
537                 else if(check == 1)
538                 {
539                         if(tag == 1)
540                         {
541                                 printf("skein%dsum: the --tag option is meaningless when verifying checksums\n",hashbitlen);
542                                 printf("Try 'skein%dsum --help' for more information\n",hashbitlen);
543                                 exit(1);
544                         }
545                         int index_files = 0;
546                         while(index_files < number_files)
547                         {
548                                 FILE *fp;
549                                 char hash[500], file_name[100], file_tmp[100], MsgDigest_tmp[hashbitlen/2];
550                                 int NoMatch = 0, NotProper = 0, Computed = 0;
551                                 strcpy(file_name,list_files[index_files]);
552                                 //show everything
553                                 int line = 0;
554                                 fp = fopen(file_name,"r");
555                                 if(fp!=NULL)
556                                 {
557                                         while(fgets(hash,500,fp)!=NULL)
558                                         {
559                                                 line ++;
560                                                 Computed++;
561                                                 unsigned int hashVersion = decomposeHashLine(hash,MsgDigest_tmp,file_tmp);
562                                                 if(hashVersion == -1)
563                                                 {
564                                                         printf("skein%d: %s is using newer version of skein%d algorithm\n",hashbitlen,file_tmp,hashbitlen);
565                                                         printf("You should update your algorithm\n");
566                                                         continue;
567                                                 }
568                                                 else if( hashVersion == 0)
569                                                 {
570                                                         printf("skein%d: %s is using an older version of skein%d algorithm\n",hashbitlen,file_tmp,hashbitlen);
571                                                         printf("You should use the older algorithm\n");
572                                                         continue;
573                                                 }
574                                                 else
575                                                 {
576                                                         if(!isProper(MsgDigest_tmp))
577                                                         {
578                                                                 if(status != 1 && warn == 1)
579                                                                 {
580                                                                         printf("skein%dsum: %s: %d: improperly formatted skein%d checksum line\n",hashbitlen,file_tmp,line,hashbitlen);
581                                                                 }
582                                                                 NotProper ++;
583                                                                 NoMatch ++;
584                                                                 continue;
585                                                         }
586                                                         if(file_tmp[0] == '*')
587                                                         {
588                                                                 int index_file_tmp = 0;
589                                                                 for( ; file_tmp[index_file_tmp+1]!=0 ; index_file_tmp++)
590                                                                 {
591                                                                         file_tmp[index_file_tmp] = file_tmp[index_file_tmp + 1];
592                                                                 }
593                                                                 file_tmp[index_file_tmp] = 0;
594                                                                 HashBinaryMode(file_tmp,MsgDigest);
595                                                         }
596                                                         else
597                                                         {
598                                                                 HashTextMode(file_tmp,MsgDigest);
599                                                         }
600
601                                                         if(!strcmp(MsgDigest,MsgDigest_tmp))
602                                                         {
603                                                                 if(quiet != 1)
604                                                                 {
605                                                                         printf("%s: OK\n",file_tmp);
606                                                                 }
607                                                         }
608                                                         else
609                                                         {
610                                                                 printf("%s: FAILED\n",file_tmp);
611                                                                 NoMatch ++;
612                                                         }
613                                                 }
614                                         }
615                                         if(NoMatch)
616                                         {
617                                                 printf("skein%dsum: WARNING: %d of %d computed checksums did NOT match\n",
618                                                                 hashbitlen,NoMatch,Computed);
619                                         }
620                                         if(NotProper)
621                                         {
622                                                 printf("skein%dsum: WARNING: %d line is improperly formatted\n",hashbitlen,NotProper);
623                                         }
624                                 }
625                                 else
626                                 {
627                                         printf("skein%dsum: %s: No such file or directory\n",hashbitlen,file_name);
628                                 }
629                                 index_files++;
630
631                         }
632
633                         if(number_files  == 0)
634                         {
635                                 char longstring[1000];
636                                 char file_name[30];
637                                 char MsgDigest_tmp[hashbitlen/2];
638                                 int Computed = 0, NotProper = 0, NoMatch = 0 , line = 0;
639                                 while((fgets(longstring,1000,stdin))!=NULL)
640                                 {
641                                         Computed++;
642                                         line ++;
643                                         decomposeHashLine(longstring,MsgDigest_tmp,file_name);
644                                         if(!isProper(MsgDigest_tmp))
645                                         {
646                                                 if(status != 1 && warn == 1)
647                                                 {
648                                                         printf("skein%dsum: %s: %d: improperly formatted skein%d checksum line\n",hashbitlen,file_name,line,hashbitlen);
649                                                 }
650                                                 NotProper ++;
651                                                 NoMatch ++;
652                                                 continue;
653                                         }
654                                         if(file_name[0] == '*')
655                                         {
656                                                 int index_file_tmp = 0;
657                                                 for( ; file_name[index_file_tmp+1]!=0 ; index_file_tmp++)
658                                                 {
659                                                         file_name[index_file_tmp] = file_name[index_file_tmp + 1];
660                                                 }
661                                                 file_name[index_file_tmp] = 0;
662                                                 HashBinaryMode(file_name,MsgDigest);
663                                         }
664                                         else
665                                         {
666                                                 HashTextMode(file_name,MsgDigest);
667                                         }
668
669                                         if(!strcmp(MsgDigest,MsgDigest_tmp))
670                                         {
671                                                 if(quiet != 1)
672                                                 {
673                                                         printf("%s: OK\n",file_name);
674                                                 }
675                                         }
676                                         else
677                                         {
678                                                 printf("%s: FAILED\n",file_name);
679                                                 NoMatch ++;
680                                         }
681                                 }
682                                 if(NoMatch)
683                                 {
684                                         printf("skein%dsum: WARNING: %d of %d computed checksums did NOT match\n",
685                                                         hashbitlen,NoMatch,Computed);
686                                 }
687                                 if(NotProper)
688                                 {
689                                         printf("skein%dsum: WARNING: %d line is improperly formatted\n",hashbitlen,NotProper);
690                                 }
691
692                         }
693
694
695                 }
696         }
697
698         if (!errorFound && (argc == 1 || (hashString == 1 && !number_files)))
699                 HashStdin(MsgDigest, 0);
700
701         return 1;
702 }