/********************************************************************** * gostsum.c * * Copyright (c) 2005-2006 Cryptocom LTD * * This file is distributed under the same license as OpenSSL * * * * Almost drop-in replacement for md5sum and sha1sum * * which computes GOST R 34.11-94 hashsum instead * * * **********************************************************************/ #include #include #include #include #include #include #include "gosthash.h" #define BUF_SIZE 262144 int hash_file(gost_hash_ctx * ctx, char *filename, char *sum, int mode); int hash_stream(gost_hash_ctx * ctx, int fd, char *sum); int get_line(FILE *f, char *hash, char *filename); void help() { fprintf(stderr, "gostsum [-bvt] [-c [file]]| [files]\n" "\t-c check message digests (default is generate)\n" "\t-v verbose, print file names when checking\n" "\t-b read files in binary mode\n" "\t-t use test GOST paramset (default is CryptoPro paramset)\n" "The input for -c should be the list of message digests and file names\n" "that is printed on stdout by this program when it generates digests.\n"); exit(3); } #ifndef O_BINARY # define O_BINARY 0 #endif int main(int argc, char **argv) { int c, i; int verbose = 0; int errors = 0; int open_mode = O_RDONLY; gost_subst_block *b = &GostR3411_94_CryptoProParamSet; FILE *check_file = NULL; gost_hash_ctx ctx; while ((c = getopt(argc, argv, "bc::tv")) != -1) { switch (c) { case 'v': verbose = 1; break; case 't': b = &GostR3411_94_TestParamSet; break; case 'b': open_mode |= O_BINARY; break; case 'c': if (optarg) { check_file = fopen(optarg, "r"); if (!check_file) { perror(optarg); exit(2); } } else { check_file = stdin; } break; default: fprintf(stderr, "invalid option %c", optopt); help(); } } init_gost_hash_ctx(&ctx, b); if (check_file) { char inhash[65], calcsum[65], filename[PATH_MAX]; int failcount = 0, count = 0;; if (check_file == stdin && optind < argc) { check_file = fopen(argv[optind], "r"); if (!check_file) { perror(argv[optind]); exit(2); } } while (get_line(check_file, inhash, filename)) { if (!hash_file(&ctx, filename, calcsum, open_mode)) { exit(2); } count++; if (!strncmp(calcsum, inhash, 65)) { if (verbose) { fprintf(stderr, "%s\tOK\n", filename); } } else { if (verbose) { fprintf(stderr, "%s\tFAILED\n", filename); } else { fprintf(stderr, "%s: GOST hash sum check failed for '%s'\n", argv[0], filename); } failcount++; } } if (verbose && failcount) { fprintf(stderr, "%s: %d of %d file(f) failed GOST hash sum check\n", argv[0], failcount, count); } exit(failcount ? 1 : 0); } if (optind == argc) { char sum[65]; if (!hash_stream(&ctx, fileno(stdin), sum)) { perror("stdin"); exit(1); } printf("%s -\n", sum); exit(0); } for (i = optind; i < argc; i++) { char sum[65]; if (!hash_file(&ctx, argv[i], sum, open_mode)) { errors++; } else { printf("%s %s\n", sum, argv[i]); } } exit(errors ? 1 : 0); } int hash_file(gost_hash_ctx * ctx, char *filename, char *sum, int mode) { int fd; if ((fd = open(filename, mode)) < 0) { perror(filename); return 0; } if (!hash_stream(ctx, fd, sum)) { perror(filename); return 0; } close(fd); return 1; } int hash_stream(gost_hash_ctx * ctx, int fd, char *sum) { unsigned char buffer[BUF_SIZE]; ssize_t bytes; int i; start_hash(ctx); while ((bytes = read(fd, buffer, BUF_SIZE)) > 0) { hash_block(ctx, buffer, bytes); } if (bytes < 0) { return 0; } finish_hash(ctx, buffer); for (i = 0; i < 32; i++) { sprintf(sum + 2 * i, "%02x", buffer[31 - i]); } return 1; } int get_line(FILE *f, char *hash, char *filename) { int i; if (fread(hash, 1, 64, f) < 64) return 0; hash[64] = 0; for (i = 0; i < 64; i++) { if (hash[i] < '0' || (hash[i] > '9' && hash[i] < 'A') || (hash[i] > 'F' && hash[i] < 'a') || hash[i] > 'f') { fprintf(stderr, "Not a hash value '%s'\n", hash); return 0; } } if (fgetc(f) != ' ') { fprintf(stderr, "Malformed input line\n"); return 0; } i = strlen(fgets(filename, PATH_MAX, f)); while (filename[--i] == '\n' || filename[i] == '\r') filename[i] = 0; return 1; }