Fix bug (wrong value computed) when reading file from stdin, implement
[oweals/busybox.git] / coreutils / sha1sum.c
1 /*
2  *  Based on shasum from http://www.netsw.org/crypto/hash/
3  *  
4  *  shasum fixed with reference to coreutils and the nist fip180-1 document
5  *  which is incorrect, in section 5
6  *  - ft(B,C,D) = (B AND C) OR ((NOT B) AND D) ( 0 <= t <= 19)
7  *  + ft(B,C,D) = (D XOR (B AND (C XOR D))) ( 0 <= t <= 19)
8  *
9  *  Copyright (C) 1999 Scott G. Miller
10  *  Copyright (C) 2003 Glenn L. McGrath
11  * 
12  *  This program is free software; you can redistribute it and/or modify
13  *  it under the terms of the GNU General Public License as published by
14  *  the Free Software Foundation; either version 2 of the License, or
15  *  (at your option) any later version.
16  *
17  *  This program is distributed in the hope that it will be useful,
18  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  *  GNU General Public License for more details.
21  *
22  *  You should have received a copy of the GNU General Public License
23  *  along with this program; if not, write to the Free Software
24  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25  */
26
27 #include <stdio.h>
28 #include <getopt.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <endian.h>
33 #include "busybox.h"
34
35 #if __BYTE_ORDER == __BIG_ENDIAN
36 # define SWAP(n) (n)
37 #else
38 # define SWAP(n) \
39     (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
40 #endif
41
42 #define f1(X,Y,Z) (Z ^ (X & (Y ^ Z)))
43 #define f2(X,Y,Z) (X ^ Y ^ Z)
44 #define f3(X,Y,Z) ((X & Y) | (Z & (X | Y)))
45
46 #define rol1(x) (x<<1) | ((x>>31) & 1)
47 #define rol5(x) ((x<<5) | ((x>>27) & 0x1f))
48 #define rol30(x) (x<<30) | ((x>>2) & 0x3fffffff)
49
50 static void sha_hash(unsigned int *data, int *hash)
51 {
52         RESERVE_CONFIG_BUFFER(word, 80 * sizeof(unsigned int));
53         int *W = (unsigned int *) &word;
54         int a = hash[0];
55         int b = hash[1];
56         int c = hash[2];
57         int d = hash[3];
58         int e = hash[4];
59         int t;
60         int TEMP;
61
62         for (t = 0; t < 16; t++) {
63                 W[t] = SWAP(data[t]);
64         }
65
66         /** Data expansion from 16 to 80 blocks **/
67         for (t = 16; t < 80; t++) {
68                 int x = W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16];
69                 W[t] = rol1(x);
70         }
71
72         /** Main loops **/
73         for (t = 0; t < 20; t++) {
74                 TEMP = rol5(a) + f1(b, c, d) + e + W[t] + 0x5a827999;
75                 e = d;
76                 d = c;
77                 c = rol30(b);
78                 b = a;
79                 a = TEMP;
80         }
81         for (; t < 40; t++) {
82                 TEMP = rol5(a) + f2(b, c, d) + e + W[t] + 0x6ed9eba1;
83                 e = d;
84                 d = c;
85                 c = rol30(b);
86                 b = a;
87                 a = TEMP;
88         }
89         for (; t < 60; t++) {
90                 TEMP = rol5(a) + f3(b, c, d) + e + W[t] + 0x8f1bbcdc;
91                 e = d;
92                 d = c;
93                 c = rol30(b);
94                 b = a;
95                 a = TEMP;
96         }
97         for (; t < 80; t++) {
98                 TEMP = rol5(a) + f2(b, c, d) + e + W[t] + 0xca62c1d6;
99                 e = d;
100                 d = c;
101                 c = rol30(b);
102                 b = a;
103                 a = TEMP;
104         }
105
106         RELEASE_CONFIG_BUFFER(word);
107
108         hash[0] += a;
109         hash[1] += b;
110         hash[2] += c;
111         hash[3] += d;
112         hash[4] += e;
113 }
114
115 static void sha1sum_stream(FILE *fd, unsigned int *hashval)
116 {
117         RESERVE_CONFIG_BUFFER(buffer, 64);
118         int length = 0;
119
120         hashval[0] = 0x67452301;
121         hashval[1] = 0xefcdab89;
122         hashval[2] = 0x98badcfe;
123         hashval[3] = 0x10325476;
124         hashval[4] = 0xc3d2e1f0;
125
126         while (!feof(fd) && !ferror(fd)) {
127                 int c = fread(&buffer, 1, 64, fd);
128                 length += c;
129                 if (feof(fd) || ferror(fd)) {
130                         int i;
131                         for (i = c; i < 61; i++) {
132                                 if (i == c) {
133                                         buffer[i] = 0x80;
134                                 }
135                                 else if (i == 60) {
136                                         /* This ends up being swaped twice */
137                                         ((unsigned int *) &buffer)[15] = SWAP(length * 8);
138                                 } else {
139                                         buffer[i] = 0;
140                                 }
141                         }
142                 }
143                 sha_hash((unsigned int *) &buffer, hashval);
144         }
145
146         RELEASE_CONFIG_BUFFER(buffer);
147
148         return;
149 }
150
151 static void print_hash(unsigned int *hash_value, unsigned char hash_length, unsigned char *filename)
152 {
153         unsigned char x;
154
155         for (x = 0; x < hash_length; x++) {
156                 printf("%08x", hash_value[x]);
157         }
158         putchar(' ');
159         putchar(' ');
160         puts(filename);
161 }
162
163 #define FLAG_SILENT     1
164 #define FLAG_CHECK      2
165 #define FLAG_WARN       3
166
167 /* This should become a common function used by sha1sum and md5sum,
168  * it needs extra functionality first
169  */
170 extern int authenticate(int argc, char **argv, void (*hash_ptr)(FILE *stream, unsigned int *hashval), const unsigned char hash_length)
171 {
172         unsigned int hash_value[hash_length];
173         unsigned char flags = 0;
174         int opt;
175
176         while ((opt = getopt(argc, argv, "sc:w")) != -1) {
177                 switch (opt) {
178                 case 's':       /* Dont output anything, status code shows success */
179                         flags |= FLAG_SILENT;
180                         break;
181 #if 0
182                 case 'c':       /* Check a list of checksums against stored values  */
183                         break;
184                 case 'w':       /* Warn of bad formatting when checking files */
185                         break;
186 #endif
187                 default:
188                         bb_show_usage();
189                 }
190         }
191
192         if (argc == optind) {
193                 argv[argc] = "-";
194         }
195
196         while (optind < argc) {
197                 FILE *stream;
198                 unsigned char *file_ptr = argv[optind];
199
200                 if ((file_ptr[0] == '-') && (file_ptr[1] == '\0')) {
201                         stream = stdin;
202                 } else {
203                         stream = bb_wfopen(file_ptr, "r");
204                         if (stream == NULL) {
205                                 return(EXIT_FAILURE);
206                         }
207                 }
208                 hash_ptr(stream, hash_value);
209                 if (!flags & FLAG_SILENT) {
210                         print_hash(hash_value, hash_length, file_ptr);
211                 }
212
213                 if (fclose(stream) == EOF) {
214                         bb_perror_msg_and_die("Couldnt close file %s", file_ptr);
215                 }
216
217                 optind++;
218         }
219
220         return(EXIT_SUCCESS);
221 }
222
223 extern int sha1sum_main(int argc, char **argv)
224 {
225         return (authenticate(argc, argv, sha1sum_stream, 5));
226 }