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
18 int hash_file(gost_hash_ctx *ctx,char *filename,char *sum,int mode);
19 int hash_stream(gost_hash_ctx *ctx,int fd, char *sum);
20 int get_line(FILE *f,char *hash,char *filename);
21 void 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 
37 int 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 
149 int 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 
166 int 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 
188 int 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