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		{
49		switch (c)
50			{
51			case 'v': verbose=1; break;
52			case 't': b= &GostR3411_94_TestParamSet; break;
53			case 'b': open_mode |= O_BINARY; break;
54			case 'c':
55				if (optarg)
56					{
57					check_file = fopen(optarg,"r");
58					if (!check_file)
59						{
60						perror(optarg);
61						exit(2);
62						}
63					}
64				else
65					{
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		{
77		char inhash[65],calcsum[65],filename[PATH_MAX];
78		int failcount=0,count=0;;
79		if (check_file==stdin && optind<argc)
80			{
81			check_file=fopen(argv[optind],"r");
82			if (!check_file)
83				{
84				perror(argv[optind]);
85				exit(2);
86				}
87			}
88		while (get_line(check_file,inhash,filename))
89			{
90			if (!hash_file(&ctx,filename,calcsum,open_mode))
91				{
92				exit (2);
93				}
94			count++;
95			if (!strncmp(calcsum,inhash,65))
96				{
97				if (verbose)
98					{
99					fprintf(stderr,"%s\tOK\n",filename);
100					}
101				}
102			else
103				{
104				if (verbose)
105					{
106					fprintf(stderr,"%s\tFAILED\n",filename);
107					}
108				else
109					{
110					fprintf(stderr,"%s: GOST hash sum check failed for '%s'\n",
111						argv[0],filename);
112					}
113				failcount++;
114				}
115			}
116		if (verbose && failcount)
117			{
118			fprintf(stderr,"%s: %d of %d file(f) failed GOST hash sum check\n",
119				argv[0],failcount,count);
120			}
121		exit (failcount?1:0);
122		}
123	if (optind==argc)
124		{
125		char sum[65];
126		if (!hash_stream(&ctx,fileno(stdin),sum))
127			{
128			perror("stdin");
129			exit(1);
130			}
131		printf("%s -\n",sum);
132		exit(0);
133		}
134	for (i=optind;i<argc;i++)
135		{
136		char sum[65];
137		if (!hash_file(&ctx,argv[i],sum,open_mode))
138			{
139			errors++;
140			}
141		else
142			{
143			printf("%s %s\n",sum,argv[i]);
144			}
145		}
146	exit(errors?1:0);
147	}
148
149int hash_file(gost_hash_ctx *ctx,char *filename,char *sum,int mode)
150	{
151	int fd;
152	if ((fd=open(filename,mode))<0)
153		{
154		perror(filename);
155		return 0;
156		}
157	if (!hash_stream(ctx,fd,sum))
158		{
159		perror(filename);
160		return 0;
161		}
162	close(fd);
163	return 1;
164	}
165
166int hash_stream(gost_hash_ctx *ctx,int fd, char *sum)
167	{
168	unsigned char buffer[BUF_SIZE];
169	ssize_t bytes;
170	int i;
171	start_hash(ctx);
172	while ((bytes=read(fd,buffer,BUF_SIZE))>0)
173		{
174		hash_block(ctx,buffer,bytes);
175		}
176	if (bytes<0)
177		{
178		return 0;
179		}
180	finish_hash(ctx,buffer);
181	for (i=0;i<32;i++)
182		{
183		sprintf(sum+2*i,"%02x",buffer[31-i]);
184		}
185	return 1;
186	}
187
188int get_line(FILE *f,char *hash,char *filename)
189	{
190	int i;
191	if (fread(hash,1,64,f)<64) return 0;
192	hash[64]=0;
193	for (i=0;i<64;i++)
194		{
195		if (hash[i]<'0' || (hash[i]>'9' && hash[i]<'A') || (hash[i]>'F'
196				&& hash[i]<'a')||hash[i]>'f')
197			{
198			fprintf(stderr,"Not a hash value '%s'\n",hash);
199			return 0;
200			}
201		}
202	if (fgetc(f)!=' ')
203		{
204		fprintf(stderr,"Malformed input line\n");
205		return 0;
206		}
207	i=strlen(fgets(filename,PATH_MAX,f));
208	while (filename[--i]=='\n'||filename[i]=='\r') filename[i]=0;
209	return 1;
210	}
211