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 skeinVersion "1.3"
39 const size_t input_minbufsize = 32 * 1024;
40 const size_t input_maxbufsize = 32 * 1024 * 1024;
43 char invalidOption = 0;
54 static struct option const long_options[] =
56 { "binary", no_argument, NULL, 'b' },
57 { "check", no_argument, NULL, 'c' },
58 { "quiet", no_argument, NULL, QUIET_OPTION },
59 { "status", no_argument, NULL, STATUS_OPTION },
60 { "text", no_argument, NULL, 't' },
61 { "warn", no_argument, NULL, 'w' },
62 { "tag", no_argument, NULL, TAG_OPTION },
63 { "help", no_argument, NULL, HELP_OPTION },
64 { "version", no_argument, NULL, VERSION_OPTION },
68 void printHexMsg(unsigned char *ptr, int size, char result[])
73 for (i = 0 ; i < size ; i ++)
78 if (High >= 0xA && High <= 0xF)
80 else if(High >= 0x0 && High <= 0x9)
83 if (Low >= 0xA && Low <= 0xF)
85 else if(Low >= 0x0 && Low <= 0x9)
94 int HashWithMode(const char file_name[], char MsgDigest[], char mode)
96 /* Try to get file info */
98 if (stat(file_name, &st) < 0) {
99 printf("skein%dsum: %s: STAT FAILED: %s\n", hashbitlen, file_name, strerror(errno));
104 size_t fsize = st.st_size;
105 if (fsize != st.st_size) {
106 printf("skein%dsum: %s: SIZE WARNING: filesize %llu is too big for reading into memory!\n",
107 hashbitlen, file_name, (long long unsigned)st.st_size);
111 FILE *fp_in = fopen(file_name, (mode == 't' ? "r" : "rb") );
113 printf("skein%dsum: %s: OPEN FAILED: %s\n", hashbitlen, file_name, strerror(errno));
117 /* Allocate buffer */
118 size_t bufsize = fsize + 1;
119 if (bufsize < input_minbufsize)
120 bufsize = input_minbufsize;
122 char *readbuf = malloc (bufsize);
123 if (!readbuf && bufsize > input_maxbufsize) { /* ..Try to get contents by smaller portions */
124 bufsize = input_maxbufsize;
125 readbuf = malloc (bufsize);
129 printf("skein%dsum: %s: MEM FAILED: %s\n", hashbitlen, file_name, strerror(errno));
135 size_t readpos = 0, readed, total_readed = 0;
138 size_t maxread = bufsize - readpos;
139 readed = fread(readbuf, 1, maxread, fp_in);
140 if (readed > 0 && readed <= maxread)
141 total_readed += readed;
142 if (readed != maxread)
144 if (getenv("SKEIN_DEBUG"))
145 printf("DEBUG: bufsize=%u (0x%x), readpos=%u (0x%x), maxread=%u (0x%x), total=%u (0x%x)\n",
146 bufsize, bufsize, readpos, readpos, maxread, maxread, total_readed, total_readed);
148 if (bufsize * 2 > bufsize) /* ..check overflow */
149 newbuf = realloc(readbuf, bufsize * 2);
151 printf("skein%dsum: %s: MEM WARNING: %u bytes only readed from %llu\n",
152 hashbitlen, file_name, total_readed, (long long unsigned)st.st_size);
160 if (total_readed < st.st_size && total_readed < bufsize) {
161 printf("skein%dsum: %s: READ WARNING: filesize=%llu, readed=%u, %s\n",
162 hashbitlen, file_name, (long long unsigned)st.st_size, total_readed, strerror(errno));
167 unsigned char output[hashbitlen/4];
168 Hash(hashbitlen, (unsigned char*) readbuf, total_readed, output);
172 printHexMsg(output,hashbitlen/4,MsgDigest);
177 int PrintHash(const char filename[], int tag, char mode)
179 char MsgDigest[hashbitlen/2];
180 if (HashWithMode(filename, MsgDigest, mode) < 0)
183 printf("skein%d_v%s (%s) = %s\n", hashbitlen, skeinVersion, filename, MsgDigest);
185 printf("%s %s%s\n", MsgDigest, (mode == 'b' ? "*" : ""), filename);
190 void HashStringMode(const char Msg[], char MsgDigest[])
192 unsigned char output[hashbitlen/4];
193 Hash(hashbitlen, (unsigned char*) Msg, strlen(Msg), output);
194 printHexMsg(output,hashbitlen/4,MsgDigest);
197 void HashStdin(int tag)
199 char MsgDigest[hashbitlen/2];
200 char stri[100]; /* TODO!!! REWRITE THIS HARDCORE!!! */
201 char longstring[1000];
203 while(fgets(stri,100,stdin))
204 strcat(longstring, stri);
205 HashStringMode(longstring, MsgDigest);
207 printf("skein%d_v%s (-) = %s\n", hashbitlen, skeinVersion, MsgDigest);
209 printf("%s *-\n", MsgDigest);
213 /* Return: -1 = Error, 0 = Mismatch, 1 = Match */
214 int HashMatch(const char StoredDigest[], const char *filename, int quiet)
217 if (filename[0] == '*') {
222 char MsgDigest[hashbitlen/2];
223 if (HashWithMode(filename, MsgDigest, mode) < 0)
226 if (strcmp(MsgDigest, StoredDigest)) {
227 printf("%s: FAILED\n", filename);
232 printf("%s: OK\n", filename);
236 /* Return: -1 = some errors/mismatches, 1 = all ok */
237 int VerifyHashesFromFile(FILE *fp, int status, int warn, int quiet)
239 char hash[PATH_MAX + hashbitlen/4 + 4];
240 char MsgDigest_tmp[hashbitlen/2];
241 int NoMatch = 0, NotProper = 0, Computed = 0;
244 while (fgets(hash, sizeof(hash)-1, fp))
246 char file_tmp[PATH_MAX];
249 int hashVersion = decomposeHashLine(hash,MsgDigest_tmp,file_tmp);
250 if (hashVersion == -1)
252 fprintf(stderr, "skein%d: %s is using newer version of skein%d algorithm\n",hashbitlen,file_tmp,hashbitlen);
253 fprintf(stderr, "You should update your algorithm\n");
256 else if (hashVersion == 0)
258 fprintf(stderr, "skein%d: %s is using an older version of skein%d algorithm\n",hashbitlen,file_tmp,hashbitlen);
259 fprintf(stderr, "You should use the older algorithm\n");
262 else if (!isProper(MsgDigest_tmp))
264 if(status != 1 && warn == 1)
265 fprintf(stderr, "skein%dsum: %s: %d: improperly formatted skein%d checksum line\n",hashbitlen,file_tmp,line,hashbitlen);
269 else if (HashMatch(MsgDigest_tmp, file_tmp, quiet) <= 0)
276 fprintf(stderr, "skein%dsum: WARNING: %d of %d computed checksums did NOT match\n",
277 hashbitlen,NoMatch,Computed);
281 fprintf(stderr, "skein%dsum: WARNING: %d line is improperly formatted\n",hashbitlen,NotProper);
283 return (NotProper || NoMatch) ? -1 : 1;
286 int isProper(const char MsgDigest[])
288 int len = strlen(MsgDigest);
289 if (len != (hashbitlen / 4))
292 for (index = 0; index < len; index++)
294 char c = MsgDigest[index];
295 if (c >= '0' && c <= '9') continue;
296 if (c >= 'A' && c <= 'F') continue;
304 int decomposeHashLine(char hash[], char MsgDigest_tmp[], char file_tmp[])
311 while(((c = hash[i])!=' ')&&((c = hash[i])!='_'))
318 sprintf(alg,"skein%d",hashbitlen);
326 strcpy(MsgDigest_tmp,tmp);
328 while((c = hash[i])!= '\n')
330 file_tmp[j] = hash[i];
336 else if((hash[i]=='_')&&(hash[i+1]=='v'))
341 while((c = hash[i])!=' ')
343 version[j] = hash[i];
348 float vers = 0, skeinVers = 0;
349 sscanf(version,"%f",&vers);
350 sscanf(skeinVersion,"%f",&skeinVers);
354 while((c = hash[i])!=')')
356 file_tmp[j] = hash[i];
364 while((c = hash[i])!='\n')
366 MsgDigest_tmp[j] = hash[i];
370 MsgDigest_tmp[j] = 0;
373 {//version newer than mine
376 else if (skeinVers > vers)
377 {//going to use older version than mine
389 void print_version(void)
391 printf("skein%dsum 1.0\n", hashbitlen);
392 printf("License GPLv3+: GNU GPL version 3 or later\n");
393 printf("<http://gnu.org/licenses/gpl.html>\n");
394 printf("This is free software: you are free to change and redistribute it.\n");
395 printf("There is NO WARRANTY, to the extent permitted by law.\n");
399 void print_usage(void)
401 printf("Usage: skein%dsum [OPTION]... [FILE]...\n",hashbitlen);
402 printf("Print or check skein (%d-bit) checksums.\n",hashbitlen);
403 printf("With no FILE, or when FILE is -, read standard input.\n");
405 printf("-b, --binary read in binary mode\n");
406 printf("-c, --check read skein sums from the FILEs and check them\n");
407 printf("--tag create a BSD-style checksum\n");
408 printf("-t, --text read in text mode (default)\n");
410 printf("The following three options are useful only when verifying checksums:\n");
411 printf("--quiet don't print OK for each successfully verified file\n");
412 printf("--status don't output anything, status code shows success\n");
413 printf("-w, --warn warn about improperly formatted checksum lines\n");
415 printf("--strict with --check, exit non-zero for any invalid input\n");
416 printf("--help display this help and exit\n");
417 printf("--version output version information and exit\n");
419 printf("The sums are computed as described in version 1.3 of the Skein\n");
420 printf("specification. When checking, the input should be a former output of\n");
421 printf("this program. The default mode is to print a line with checksum, a\n");
422 printf("character indicating input mode ('*' for binary, space for text), and\n");
423 printf("name for each FILE.\n");
427 int is_goodfile(const char filename[])
429 if (!strcmp(filename, "-"))
434 if (stat(filename, &s) < 0) {
435 fprintf(stderr, "skein%dsum: %s: no such file or directory\n", hashbitlen, filename);
439 if (s.st_mode & (S_IFREG|S_IFBLK)) /* ..regular file or block device? */
442 if (s.st_mode & S_IFDIR) {
443 fprintf(stderr, "skein%dsum: %s: is a directory\n", hashbitlen, filename);
447 fprintf(stderr, "skein%dsum: %s: wrong filetype 0x%Xu\n", hashbitlen, filename, s.st_mode);
452 int main(int argc, char** argv)
454 int number_files = 0;
455 int first_file = argc;
466 /*****************************************************************************************
467 ************************************* GETTING DATA ***********************************
468 *****************************************************************************************/
469 while ((opt = getopt_long (argc, argv, "bctw", long_options, NULL)) != -1)
472 case 0 : hashString = 1; break;
473 case 'b' : binary = 1; break;
474 case 't' : binary = 0; break;
475 case 'c' : check = 1; break;
476 case 'w' : warn = 1; break;
477 case QUIET_OPTION : quiet = 1; break;
478 case STATUS_OPTION : status = 1; break;
479 case TAG_OPTION : tag = 1; break;
481 case HELP_OPTION : print_usage(); /* ..never returns */
482 case VERSION_OPTION : print_version(); /* ..never returns */
484 default: TRYHELP_GOODBYE();
490 /*****************************************************************************************
491 ************************************* PROCESSING DATA ***********************************
492 *****************************************************************************************/
494 if (check < 0) /* READ FILES, GENERATE CHECKSUMS AND PRINT TO STDOUT.. */
496 if (quiet == 1 || warn == 1 || status == 1)
499 fprintf(stderr, "skein%dsum: the --quiet option is meaningful only when verifying checksums\n", hashbitlen);
501 fprintf(stderr, "skein%dsum: the --status option is meaningful only when verifying checksums\n", hashbitlen);
503 fprintf(stderr, "skein%dsum: the --warn option is meaningful only when verifying checksums\n", hashbitlen);
508 for (file_index = first_file; file_index < argc; file_index++)
510 const char *filename = argv[file_index];
511 if (!is_goodfile(filename)) {
517 if (binary > 0 && hashString == 1) {
518 printf("skein%dsum: %s: No such file or directory\n", hashbitlen, filename);
522 char mode = binary > 0 ? 'b' : 't';
523 if (PrintHash(filename, tag, mode) < 0)
527 if (!number_files && !errorFound)
530 return (errorFound ? 1 : 0);
533 if (check == 1) /* READ LISTFILES, GENERATE HASHES, COMPARE WITH STORED, PRINT RESULT */
535 if (tag == 1 || binary >= 0)
538 fprintf(stderr, "skein%dsum: the --tag option is meaningless when verifying checksums\n", hashbitlen);
540 fprintf(stderr, "skein%dsum: the --text and --binary options are meaningless when verifying checksums\n", hashbitlen);
545 for (file_index = first_file; file_index < argc; file_index++)
547 const char *filename = argv[file_index];
548 if (!is_goodfile(filename)) {
554 FILE *fp = fopen(filename, "r");
556 printf("skein%dsum: %s: error %d, %s\n", hashbitlen, filename, errno, strerror(errno));
559 if (VerifyHashesFromFile(fp, status, warn, quiet) < 0)
564 if (!number_files && !errorFound)
565 if (VerifyHashesFromFile(stdin, status, warn, quiet) < 0)
568 return (errorFound ? 1 : 0);