gostsum.c revision 296341
1/**********************************************************************
2 *                        gostsum.c                                   *
3 *             Copyright (c) 2005-2006 Cryptocom LTD                  *
4 *         This file is distributed under the same license as OpenSSL *
5 *                                                                    *
6 *        Almost drop-in replacement for md5sum and sha1sum           *
7 *          which computes GOST R 34.11-94 hashsum instead            *
8 *                                                                    *
9 **********************************************************************/
10#include <stdio.h>
11#include <stdlib.h>
12#include <unistd.h>
13#include <limits.h>
14#include <fcntl.h>
15#include <string.h>
16#include "gosthash.h"
17#define BUF_SIZE 262144
18int hash_file(gost_hash_ctx * ctx, char *filename, char *sum, int mode);
19int hash_stream(gost_hash_ctx * ctx, int fd, char *sum);
20int get_line(FILE *f, char *hash, char *filename);
21void help()
22{
23    fprintf(stderr, "gostsum [-bvt] [-c [file]]| [files]\n"
24            "\t-c check message digests (default is generate)\n"
25            "\t-v verbose, print file names when checking\n"
26            "\t-b read files in binary mode\n"
27            "\t-t use test GOST paramset (default is CryptoPro paramset)\n"
28            "The input for -c should be the list of message digests and file names\n"
29            "that is printed on stdout by this program when it generates digests.\n");
30    exit(3);
31}
32
33#ifndef O_BINARY
34# define O_BINARY 0
35#endif
36
37int main(int argc, char **argv)
38{
39    int c, i;
40    int verbose = 0;
41    int errors = 0;
42    int open_mode = O_RDONLY;
43    gost_subst_block *b = &GostR3411_94_CryptoProParamSet;
44    FILE *check_file = NULL;
45    gost_hash_ctx ctx;
46
47    while ((c = getopt(argc, argv, "bc::tv")) != -1) {
48        switch (c) {
49        case 'v':
50            verbose = 1;
51            break;
52        case 't':
53            b = &GostR3411_94_TestParamSet;
54            break;
55        case 'b':
56            open_mode |= O_BINARY;
57            break;
58        case 'c':
59            if (optarg) {
60                check_file = fopen(optarg, "r");
61                if (!check_file) {
62                    perror(optarg);
63                    exit(2);
64                }
65            } else {
66                check_file = stdin;
67            }
68            break;
69        default:
70            fprintf(stderr, "invalid option %c", optopt);
71            help();
72        }
73    }
74    init_gost_hash_ctx(&ctx, b);
75    if (check_file) {
76        char inhash[65], calcsum[65], filename[PATH_MAX];
77        int failcount = 0, count = 0;;
78        if (check_file == stdin && optind < argc) {
79            check_file = fopen(argv[optind], "r");
80            if (!check_file) {
81                perror(argv[optind]);
82                exit(2);
83            }
84        }
85        while (get_line(check_file, inhash, filename)) {
86            if (!hash_file(&ctx, filename, calcsum, open_mode)) {
87                exit(2);
88            }
89            count++;
90            if (!strncmp(calcsum, inhash, 65)) {
91                if (verbose) {
92                    fprintf(stderr, "%s\tOK\n", filename);
93                }
94            } else {
95                if (verbose) {
96                    fprintf(stderr, "%s\tFAILED\n", filename);
97                } else {
98                    fprintf(stderr,
99                            "%s: GOST hash sum check failed for '%s'\n",
100                            argv[0], filename);
101                }
102                failcount++;
103            }
104        }
105        if (verbose && failcount) {
106            fprintf(stderr,
107                    "%s: %d of %d file(f) failed GOST hash sum check\n",
108                    argv[0], failcount, count);
109        }
110        exit(failcount ? 1 : 0);
111    }
112    if (optind == argc) {
113        char sum[65];
114        if (!hash_stream(&ctx, fileno(stdin), sum)) {
115            perror("stdin");
116            exit(1);
117        }
118        printf("%s -\n", sum);
119        exit(0);
120    }
121    for (i = optind; i < argc; i++) {
122        char sum[65];
123        if (!hash_file(&ctx, argv[i], sum, open_mode)) {
124            errors++;
125        } else {
126            printf("%s %s\n", sum, argv[i]);
127        }
128    }
129    exit(errors ? 1 : 0);
130}
131
132int hash_file(gost_hash_ctx * ctx, char *filename, char *sum, int mode)
133{
134    int fd;
135    if ((fd = open(filename, mode)) < 0) {
136        perror(filename);
137        return 0;
138    }
139    if (!hash_stream(ctx, fd, sum)) {
140        perror(filename);
141        return 0;
142    }
143    close(fd);
144    return 1;
145}
146
147int hash_stream(gost_hash_ctx * ctx, int fd, char *sum)
148{
149    unsigned char buffer[BUF_SIZE];
150    ssize_t bytes;
151    int i;
152    start_hash(ctx);
153    while ((bytes = read(fd, buffer, BUF_SIZE)) > 0) {
154        hash_block(ctx, buffer, bytes);
155    }
156    if (bytes < 0) {
157        return 0;
158    }
159    finish_hash(ctx, buffer);
160    for (i = 0; i < 32; i++) {
161        sprintf(sum + 2 * i, "%02x", buffer[31 - i]);
162    }
163    return 1;
164}
165
166int get_line(FILE *f, char *hash, char *filename)
167{
168    int i;
169    if (fread(hash, 1, 64, f) < 64)
170        return 0;
171    hash[64] = 0;
172    for (i = 0; i < 64; i++) {
173        if (hash[i] < '0' || (hash[i] > '9' && hash[i] < 'A')
174            || (hash[i] > 'F' && hash[i] < 'a') || hash[i] > 'f') {
175            fprintf(stderr, "Not a hash value '%s'\n", hash);
176            return 0;
177        }
178    }
179    if (fgetc(f) != ' ') {
180        fprintf(stderr, "Malformed input line\n");
181        return 0;
182    }
183    i = strlen(fgets(filename, PATH_MAX, f));
184    while (filename[--i] == '\n' || filename[i] == '\r')
185        filename[i] = 0;
186    return 1;
187}
188