gostsum.c revision 280304
120253Sjoerg/**********************************************************************
220253Sjoerg *                        gostsum.c                                   *
320253Sjoerg *             Copyright (c) 2005-2006 Cryptocom LTD                  *
420253Sjoerg *         This file is distributed under the same license as OpenSSL *
520253Sjoerg *                                                                    *
620253Sjoerg *        Almost drop-in replacement for md5sum and sha1sum           *
720253Sjoerg *          which computes GOST R 34.11-94 hashsum instead            *
820253Sjoerg *                                                                    *
920253Sjoerg **********************************************************************/
1020253Sjoerg#include <stdio.h>
1120253Sjoerg#include <stdlib.h>
1220253Sjoerg#include <unistd.h>
1320253Sjoerg#include <limits.h>
1420253Sjoerg#include <fcntl.h>
1520253Sjoerg#include <string.h>
1620253Sjoerg#include "gosthash.h"
1720253Sjoerg#define BUF_SIZE 262144
1820253Sjoergint hash_file(gost_hash_ctx * ctx, char *filename, char *sum, int mode);
1920253Sjoergint hash_stream(gost_hash_ctx * ctx, int fd, char *sum);
2020253Sjoergint get_line(FILE *f, char *hash, char *filename);
2120253Sjoergvoid help()
2220253Sjoerg{
2320253Sjoerg    fprintf(stderr, "gostsum [-bvt] [-c [file]]| [files]\n"
2420253Sjoerg            "\t-c check message digests (default is generate)\n"
2520253Sjoerg            "\t-v verbose, print file names when checking\n"
2620253Sjoerg            "\t-b read files in binary mode\n"
2720253Sjoerg            "\t-t use test GOST paramset (default is CryptoPro paramset)\n"
2820253Sjoerg            "The input for -c should be the list of message digests and file names\n"
2920253Sjoerg            "that is printed on stdout by this program when it generates digests.\n");
3020253Sjoerg    exit(3);
3120253Sjoerg}
3220253Sjoerg
3320253Sjoerg#ifndef O_BINARY
3420253Sjoerg# define O_BINARY 0
3520253Sjoerg#endif
3620253Sjoerg
3720253Sjoergint main(int argc, char **argv)
3820253Sjoerg{
3920253Sjoerg    int c, i;
4020253Sjoerg    int verbose = 0;
4120253Sjoerg    int errors = 0;
4220253Sjoerg    int open_mode = O_RDONLY;
4320253Sjoerg    gost_subst_block *b = &GostR3411_94_CryptoProParamSet;
4420253Sjoerg    FILE *check_file = NULL;
4520253Sjoerg    gost_hash_ctx ctx;
4620253Sjoerg
4720253Sjoerg    while ((c = getopt(argc, argv, "bc::tv")) != -1) {
4820253Sjoerg        switch (c) {
4920253Sjoerg        case 'v':
5020253Sjoerg            verbose = 1;
5120253Sjoerg            break;
5220253Sjoerg        case 't':
5320253Sjoerg            b = &GostR3411_94_TestParamSet;
5420253Sjoerg            break;
5520253Sjoerg        case 'b':
5620253Sjoerg            open_mode |= O_BINARY;
5720253Sjoerg            break;
5820253Sjoerg        case 'c':
5920253Sjoerg            if (optarg) {
6020253Sjoerg                check_file = fopen(optarg, "r");
6120253Sjoerg                if (!check_file) {
6220253Sjoerg                    perror(optarg);
6320253Sjoerg                    exit(2);
6420253Sjoerg                }
6520253Sjoerg            } else {
6620253Sjoerg                check_file = stdin;
6720253Sjoerg            }
6820253Sjoerg            break;
6920253Sjoerg        default:
7020253Sjoerg            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