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 typedef long long unsigned LLU;
38 extern const int hashbitlen;
40 #define skeinVersion "1.3"
41 const size_t input_minbufsize = 32 * 1024;
42 const size_t input_maxbufsize = 32 * 1024 * 1024;
45 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, 'h' },
64 { "version", no_argument, NULL, 'V' },
68 void hash2hexstr(unsigned char hash[], char str[])
71 for (i = 0; i < hashbitlen / 8; i++) {
72 sprintf(&str[i * 2], "%02X", hash[i]);
74 str[i * 2 + 1] = '\0';
77 int HashFile(const char file_name[], char MsgDigest[], char mode)
79 int is_stdin = (strcmp(file_name, "-") == 0);
81 /* Try to get file info */
83 st.st_size = 0; /* ..needed when reading from stdio */
84 if (!is_stdin && stat(file_name, &st) < 0) {
85 printf("skein%dsum: %s: STAT FAILED: %s\n", hashbitlen, file_name, strerror(errno));
90 size_t fsize = st.st_size;
91 if (fsize != st.st_size) {
92 printf("skein%dsum: %s: SIZE WARNING: filesize %llu is too big for reading into memory!\n",
93 hashbitlen, file_name, (long long unsigned)st.st_size);
97 FILE *fp_in = is_stdin ? stdin : fopen(file_name, (mode == 't' ? "r" : "rb") );
99 printf("skein%dsum: %s: OPEN FAILED: %s\n", hashbitlen, file_name, strerror(errno));
103 /* Allocate buffer */
104 size_t bufsize = fsize + 1;
105 if (bufsize < input_minbufsize)
106 bufsize = input_minbufsize;
108 char *readbuf = malloc (bufsize);
109 if (!readbuf && bufsize > input_maxbufsize) { /* ..Try to get contents by smaller portions */
110 bufsize = input_maxbufsize;
111 readbuf = malloc (bufsize);
115 printf("skein%dsum: %s: MEM FAILED: %s\n", hashbitlen, file_name, strerror(errno));
122 size_t readpos = 0, total_readed = 0;
125 size_t maxread = bufsize - readpos;
126 size_t readed = fread(readbuf + readpos, 1, maxread, fp_in);
127 if (readed > 0 && readed <= maxread)
128 total_readed += readed;
129 if (readed != maxread)
131 if (getenv("SKEIN_DEBUG"))
132 printf("DEBUG: bufsize=%llu (0x%llx), readpos=%llu (0x%llx), maxread=%llu (0x%llx), total=%llu (0x%llx)\n",
133 (LLU)bufsize, (LLU)bufsize, (LLU)readpos, (LLU)readpos,
134 (LLU)maxread, (LLU)maxread, (LLU)total_readed, (LLU)total_readed);
136 if (bufsize * 2 > bufsize) /* ..check overflow */
137 newbuf = realloc(readbuf, bufsize * 2);
139 if (total_readed < st.st_size) {
140 printf("skein%dsum: %s: MEM WARNING: %llu bytes only readed from %llu\n",
141 hashbitlen, file_name, (LLU)total_readed, (LLU)st.st_size);
143 printf("skein%dsum: %s: MEM WARNING: %llu bytes only readed.\n",
144 hashbitlen, file_name, (LLU)total_readed);
156 if (!is_stdin && total_readed < st.st_size && total_readed < bufsize) {
157 printf("skein%dsum: %s: READ WARNING: filesize=%llu, readed=%llu, error %d, %s\n",
158 hashbitlen, file_name, (LLU)st.st_size, (LLU)total_readed, errno, strerror(errno));
161 if (getenv("SKEIN_DUMP")) {
162 printf("DEBUG: file=%s, bufsize=%llu (0x%llx), total_readed=%llu, buf:\n",
163 file_name, (LLU)bufsize, (LLU)bufsize, (LLU)total_readed);
164 fwrite(readbuf, 1, total_readed, stdout);
167 unsigned char output[hashbitlen/4];
168 Hash(hashbitlen, (unsigned char*) readbuf, total_readed, output);
172 hash2hexstr(output, MsgDigest);
177 int PrintFileHash(const char filename[], int tag, char mode)
179 char MsgDigest[hashbitlen/2];
180 if (HashFile(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 /* Return: -1 = Error, 0 = Mismatch, 1 = Match */
191 int HashMatch(const char StoredDigest[], const char *filename, int quiet)
194 if (filename[0] == '*') {
199 char MsgDigest[hashbitlen/2];
200 if (HashFile(filename, MsgDigest, mode) < 0)
203 if (strcmp(MsgDigest, StoredDigest)) {
204 printf("%s: FAILED\n", filename);
209 printf("%s: OK\n", filename);
213 int isProper(const char MsgDigest[])
215 int len = strlen(MsgDigest);
216 if (len != (hashbitlen / 4))
219 for (index = 0; index < len; index++)
221 char c = MsgDigest[index];
222 if (c >= '0' && c <= '9') continue;
223 if (c >= 'A' && c <= 'F') continue;
231 int decomposeHashLine(char hash[], char MsgDigest_tmp[], char file_tmp[])
238 while(((c = hash[i])!=' ')&&((c = hash[i])!='_'))
245 sprintf(alg,"skein%d",hashbitlen);
253 strcpy(MsgDigest_tmp,tmp);
255 while((c = hash[i])!= '\n')
257 file_tmp[j] = hash[i];
263 else if((hash[i]=='_')&&(hash[i+1]=='v'))
268 while((c = hash[i])!=' ')
270 version[j] = hash[i];
275 float vers = 0, skeinVers = 0;
276 sscanf(version,"%f",&vers);
277 sscanf(skeinVersion,"%f",&skeinVers);
281 while((c = hash[i])!=')')
283 file_tmp[j] = hash[i];
291 while((c = hash[i])!='\n')
293 MsgDigest_tmp[j] = hash[i];
297 MsgDigest_tmp[j] = 0;
300 {//version newer than mine
303 else if (skeinVers > vers)
304 {//going to use older version than mine
316 /* Return: -1 = some errors/mismatches, 1 = all ok */
317 int VerifyHashesFromFile(FILE *fp, int status, int warn, int quiet)
319 char hash[PATH_MAX + hashbitlen/4 + 4];
320 char MsgDigest_tmp[hashbitlen/2];
321 int NoMatch = 0, NotProper = 0, Computed = 0;
324 while (fgets(hash, sizeof(hash)-1, fp))
326 char file_tmp[PATH_MAX];
329 int hashVersion = decomposeHashLine(hash,MsgDigest_tmp,file_tmp);
330 if (hashVersion == -1)
332 fprintf(stderr, "skein%d: %s is using newer version of skein%d algorithm\n",hashbitlen,file_tmp,hashbitlen);
333 fprintf(stderr, "You should update your algorithm\n");
336 else if (hashVersion == 0)
338 fprintf(stderr, "skein%d: %s is using an older version of skein%d algorithm\n",hashbitlen,file_tmp,hashbitlen);
339 fprintf(stderr, "You should use the older algorithm\n");
342 else if (!isProper(MsgDigest_tmp))
344 if(status != 1 && warn == 1)
345 fprintf(stderr, "skein%dsum: %s: %d: improperly formatted skein%d checksum line\n",hashbitlen,file_tmp,line,hashbitlen);
349 else if (HashMatch(MsgDigest_tmp, file_tmp, quiet) <= 0)
356 fprintf(stderr, "skein%dsum: WARNING: %d of %d computed checksums did NOT match\n",
357 hashbitlen,NoMatch,Computed);
361 fprintf(stderr, "skein%dsum: WARNING: %d line is improperly formatted\n",hashbitlen,NotProper);
363 return (NotProper || NoMatch) ? -1 : 1;
366 void print_version(void)
368 printf("skein%dsum 1.0\n", hashbitlen);
369 printf("License GPLv3+: GNU GPL version 3 or later\n");
370 printf("<http://gnu.org/licenses/gpl.html>\n");
371 printf("This is free software: you are free to change and redistribute it.\n");
372 printf("There is NO WARRANTY, to the extent permitted by law.\n");
376 void print_usage(void)
378 printf("Usage: skein%dsum [OPTION]... [FILE]...\n",hashbitlen);
379 printf("Print or check skein (%d-bit) checksums.\n",hashbitlen);
380 printf("With no FILE, or when FILE is -, read standard input.\n");
382 printf("-b, --binary read in binary mode\n");
383 printf("-c, --check read skein sums from the FILEs and check them\n");
384 printf("--tag create a BSD-style checksum\n");
385 printf("-t, --text read in text mode (default)\n");
387 printf("The following three options are useful only when verifying checksums:\n");
388 printf("--quiet don't print OK for each successfully verified file\n");
389 printf("--status don't output anything, status code shows success\n");
390 printf("-w, --warn warn about improperly formatted checksum lines\n");
392 printf("--strict with --check, exit non-zero for any invalid input\n");
393 printf("--help display this help and exit\n");
394 printf("--version output version information and exit\n");
396 printf("The sums are computed as described in version 1.3 of the Skein\n");
397 printf("specification. When checking, the input should be a former output of\n");
398 printf("this program. The default mode is to print a line with checksum, a\n");
399 printf("character indicating input mode ('*' for binary, space for text), and\n");
400 printf("name for each FILE.\n");
404 int is_goodfile(const char filename[])
406 if (!strcmp(filename, "-"))
411 if (stat(filename, &s) < 0) {
412 fprintf(stderr, "skein%dsum: %s: no such file or directory\n", hashbitlen, filename);
416 if (S_ISREG (s.st_mode)) return 1;
417 if (S_ISCHR (s.st_mode)) return 1;
418 if (S_ISBLK (s.st_mode)) return 1;
419 if (S_ISLNK (s.st_mode)) return 1;
420 if (S_ISFIFO(s.st_mode)) return 1;
422 if (S_ISDIR (s.st_mode)) {
423 fprintf(stderr, "skein%dsum: %s: is a directory\n", hashbitlen, filename);
427 fprintf(stderr, "skein%dsum: %s: WARNING: wrong filetype 0x%Xu\n", hashbitlen, filename, s.st_mode);
428 return 1; /* try it as good */
432 int main(int argc, char** argv)
434 int first_file = argc;
445 /*****************************************************************************************
446 ************************************* GETTING DATA ***********************************
447 *****************************************************************************************/
448 while ((opt = getopt_long (argc, argv, "hVbctw", long_options, NULL)) != -1)
451 case 0 : hashString = 1; break;
452 case 'b' : binary = 1; break;
453 case 't' : binary = 0; break;
454 case 'c' : check = 1; break;
455 case 'w' : warn = 1; break;
456 case QUIET_OPTION : quiet = 1; break;
457 case STATUS_OPTION : status = 1; break;
458 case TAG_OPTION : tag = 1; break;
460 case 'h' : print_usage(); /* ..never returns */
461 case 'V' : print_version(); /* ..never returns */
463 default: TRYHELP_GOODBYE();
469 /*****************************************************************************************
470 ************************************* PROCESSING DATA ***********************************
471 *****************************************************************************************/
473 if (check < 0) /* READ FILES, GENERATE CHECKSUMS AND PRINT TO STDOUT.. */
475 if (quiet == 1 || warn == 1 || status == 1)
478 fprintf(stderr, "skein%dsum: the --quiet option is meaningful only when verifying checksums\n", hashbitlen);
480 fprintf(stderr, "skein%dsum: the --status option is meaningful only when verifying checksums\n", hashbitlen);
482 fprintf(stderr, "skein%dsum: the --warn option is meaningful only when verifying checksums\n", hashbitlen);
486 char mode = binary > 0 ? 'b' : 't';
489 for (file_index = first_file; file_index < argc; file_index++)
491 const char *filename = argv[file_index];
492 if (!is_goodfile(filename)) {
497 if (binary > 0 && hashString == 1) {
498 printf("skein%dsum: %s: No such file or directory\n", hashbitlen, filename);
502 if (PrintFileHash(filename, tag, mode) < 0)
506 if (first_file >= argc) /* no files in cmdline? hash stdin.. */
507 if (PrintFileHash("-", tag, mode) < 0)
510 return (errorFound ? 1 : 0);
513 if (check == 1) /* READ LISTFILES, GENERATE HASHES, COMPARE WITH STORED, PRINT RESULT */
515 if (tag == 1 || binary >= 0)
518 fprintf(stderr, "skein%dsum: the --tag option is meaningless when verifying checksums\n", hashbitlen);
520 fprintf(stderr, "skein%dsum: the --text and --binary options are meaningless when verifying checksums\n", hashbitlen);
525 for (file_index = first_file; file_index < argc; file_index++)
527 const char *filename = argv[file_index];
528 if (!is_goodfile(filename)) {
533 FILE *fp = (strcmp(filename, "-") == 0) ? stdin : fopen(filename, "r");
536 printf("skein%dsum: %s: error %d, %s\n", hashbitlen, filename, errno, strerror(errno));
539 if (VerifyHashesFromFile(fp, status, warn, quiet) < 0)
545 if (first_file >= argc) /* no files in cmdline? verify stdin.. */
546 if (VerifyHashesFromFile(stdin, status, warn, quiet) < 0)
549 return (errorFound ? 1 : 0);