[docs] Enable documentation for utils.
[mono.git] / mono / utils / mono-sha1.c
1 /**
2 \file
3 SHA-1 in C
4 By Steve Reid <sreid@sea-to-sky.net>
5 100% Public Domain
6
7 -----------------
8 Modified 7/98 
9 By James H. Brown <jbrown@burgoyne.com>
10 Still 100% Public Domain
11
12 Corrected a problem which generated improper hash values on 16 bit machines
13 Routine mono_sha1_update changed from
14         void mono_sha1_update(MonoSHA1Context* context, unsigned char* data, unsigned int
15 len)
16 to
17         void mono_sha1_update(MonoSHA1Context* context, unsigned char* data, unsigned
18 long len)
19
20 The 'len' parameter was declared an int which works fine on 32 bit machines.
21 However, on 16 bit machines an int is too small for the shifts being done
22 against
23 it.  This caused the hash function to generate incorrect values if len was
24 greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of mono_sha1_update().
25
26 Since the file IO in main() reads 16K at a time, any file 8K or larger would
27 be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
28 "a"s).
29
30 I also changed the declaration of variables i & j in mono_sha1_update to 
31 unsigned long from unsigned int for the same reason.
32
33 These changes should make no difference to any 32 bit implementations since
34 an
35 int and a long are the same size in those environments.
36
37 --
38 I also corrected a few compiler warnings generated by Borland C.
39 1. Added #include <process.h> for exit() prototype
40 2. Removed unused variable 'j' in mono_sha1_final
41 3. Changed exit(0) to return(0) at end of main.
42
43 ALL changes I made can be located by searching for comments containing 'JHB'
44 -----------------
45 Modified 8/98
46 By Steve Reid <sreid@sea-to-sky.net>
47 Still 100% public domain
48
49 1- Removed #include <process.h> and used return() instead of exit()
50 2- Fixed overwriting of finalcount in mono_sha1_final() (discovered by Chris Hall)
51 3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
52
53 -----------------
54 Modified 4/01
55 By Saul Kravitz <Saul.Kravitz@celera.com>
56 Still 100% PD
57 Modified to run on Compaq Alpha hardware.  
58
59
60 */
61
62 /*
63 Test Vectors (from FIPS PUB 180-1)
64 "abc"
65   A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
66 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
67   84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
68 A million repetitions of "a"
69   34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
70 */
71
72 #define SHA1HANDSOFF
73
74 #include <stdio.h>
75 #include <string.h>
76 #include <glib.h>
77 #include "mono-digest.h"
78
79 #if HAVE_COMMONCRYPTO_COMMONDIGEST_H
80
81 void
82 mono_sha1_init (MonoSHA1Context* context)
83 {
84         CC_SHA1_Init (context);
85 }
86
87 void
88 mono_sha1_update (MonoSHA1Context* context, const guchar* data, guint32 len)
89 {
90         CC_SHA1_Update (context, data, len);
91 }
92
93 void
94 mono_sha1_final (MonoSHA1Context* context, unsigned char digest[20])
95 {
96         CC_SHA1_Final (digest, context);
97 }
98
99 #else
100
101 /* #include <process.h> */      /* prototype for exit() - JHB */
102 /* Using return() instead of exit() - SWR */
103
104 static void SHA1Transform(guint32 state[5], const guchar buffer[64]);
105
106 #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
107
108 /* blk0() and blk() perform the initial expand. */
109 /* I got the idea of expanding during the round function from SSLeay */
110 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
111 #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
112     |(rol(block->l[i],8)&0x00FF00FF))
113 #else
114 #define blk0(i) block->l[i]
115 #endif
116 #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
117     ^block->l[(i+2)&15]^block->l[i&15],1))
118
119 /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
120 #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
121 #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
122 #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
123 #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
124 #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
125
126
127 #ifdef VERBOSE  /* SAK */
128 static void SHAPrintContext(MonoSHA1Context *context, char *msg){
129   printf("%s (%d,%d) %x %x %x %x %x\n",
130          msg,
131          context->count[0], context->count[1], 
132          context->state[0],
133          context->state[1],
134          context->state[2],
135          context->state[3],
136          context->state[4]);
137 }
138 #endif
139
140 /* Hash a single 512-bit block. This is the core of the algorithm. */
141
142 static void SHA1Transform(guint32 state[5], const guchar buffer[64])
143 {
144 guint32 a, b, c, d, e;
145 typedef union {
146     unsigned char c[64];
147     guint32 l[16];
148 } CHAR64LONG16;
149 CHAR64LONG16* block;
150 #ifdef SHA1HANDSOFF
151     unsigned char workspace[64];
152     block = (CHAR64LONG16*)workspace;
153     memcpy(block, buffer, 64);
154 #else
155     block = (CHAR64LONG16*)buffer;
156 #endif
157     /* Copy context->state[] to working vars */
158     a = state[0];
159     b = state[1];
160     c = state[2];
161     d = state[3];
162     e = state[4];
163     /* 4 rounds of 20 operations each. Loop unrolled. */
164     R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
165     R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
166     R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
167     R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
168     R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
169     R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
170     R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
171     R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
172     R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
173     R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
174     R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
175     R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
176     R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
177     R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
178     R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
179     R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
180     R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
181     R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
182     R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
183     R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
184     /* Add the working vars back into context.state[] */
185     state[0] += a;
186     state[1] += b;
187     state[2] += c;
188     state[3] += d;
189     state[4] += e;
190     /* Wipe variables */
191     a = b = c = d = e = 0;
192 }
193
194
195 /* mono_sha1_init - Initialize new context */
196
197 void mono_sha1_init(MonoSHA1Context* context)
198 {
199     /* SHA1 initialization constants */
200     context->state[0] = 0x67452301;
201     context->state[1] = 0xEFCDAB89;
202     context->state[2] = 0x98BADCFE;
203     context->state[3] = 0x10325476;
204     context->state[4] = 0xC3D2E1F0;
205     context->count[0] = context->count[1] = 0;
206 }
207
208
209 /* Run your data through this. */
210
211 void mono_sha1_update(MonoSHA1Context* context, const guchar* data, guint32 len)        /*
212 JHB */
213 {
214 guint32 i, j;   /* JHB */
215
216 #ifdef VERBOSE
217     SHAPrintContext(context, "before");
218 #endif
219     j = (context->count[0] >> 3) & 63;
220     if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
221     context->count[1] += (len >> 29);
222     if ((j + len) > 63) {
223         memcpy(&context->buffer[j], data, (i = 64-j));
224         SHA1Transform(context->state, context->buffer);
225         for ( ; i + 63 < len; i += 64) {
226             SHA1Transform(context->state, &data[i]);
227         }
228         j = 0;
229     }
230     else i = 0;
231     memcpy(&context->buffer[j], &data[i], len - i);
232 #ifdef VERBOSE
233     SHAPrintContext(context, "after ");
234 #endif
235 }
236
237
238 /* Add padding and return the message digest. */
239
240 void mono_sha1_final( MonoSHA1Context* context, unsigned char digest[20])
241 {
242 guint32 i;      /* JHB */
243 unsigned char finalcount[8];
244
245     for (i = 0; i < 8; i++) {
246         finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
247          >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
248     }
249     mono_sha1_update(context, (unsigned char *)"\200", 1);
250     while ((context->count[0] & 504) != 448) {
251         mono_sha1_update(context, (unsigned char *)"\0", 1);
252     }
253     mono_sha1_update(context, finalcount, 8);  /* Should cause a SHA1Transform()
254 */
255     for (i = 0; i < 20; i++) {
256         digest[i] = (unsigned char)
257          ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
258     }
259     /* Wipe variables */
260     i = 0;      /* JHB */
261     memset(context->buffer, 0, 64);
262     memset(context->state, 0, 20);
263     memset(context->count, 0, 8);
264     memset(finalcount, 0, 8);   /* SWR */
265 #ifdef SHA1HANDSOFF  /* make SHA1Transform overwrite it's own static vars */
266     SHA1Transform(context->state, context->buffer);
267 #endif
268 }
269
270 #endif
271  
272 void
273 mono_sha1_get_digest (const guchar *buffer, gint buffer_size, guchar digest [20])
274 {       
275         MonoSHA1Context ctx;
276
277         mono_sha1_init (&ctx);
278         mono_sha1_update (&ctx, buffer, buffer_size);
279         mono_sha1_final (&ctx, digest);
280         
281 }
282
283 /**
284  * mono_sha1_get_digest_from_file: get the sha1 hash of a file
285  * @filename: file name
286  * @digest: 20 bytes buffer receiving the hash code.
287  * 
288  * Get the sha1 hash of a file. The result is put in 
289  * the 20 bytes buffer @digest .
290  * 
291  * If an IO error happens the value in @digest is not updated.
292  **/
293 void
294 mono_sha1_get_digest_from_file (const gchar *filename, guchar digest [20])
295 {       
296         MonoSHA1Context ctx;
297         guchar tmp_buf[1024];
298         gint nb_bytes_read;
299         FILE *fp;
300
301         mono_sha1_init (&ctx);
302         fp = fopen(filename, "r");
303         if (!fp) {
304                 return;
305         }
306         
307         while ((nb_bytes_read = fread (tmp_buf, sizeof (guchar), 1024, fp)) > 0)
308                 mono_sha1_update (&ctx, tmp_buf, nb_bytes_read);
309         
310         if (ferror(fp)) {
311                 fclose(fp);
312                 return;
313         } else {
314                 fclose(fp);
315         }
316
317         mono_sha1_final (&ctx, digest);
318 }
319
320 /*
321  * mono_digest_get_public_token:
322  *
323  * Get the public token from public key data.
324  * @token must point to at least 8 bytes of storage.
325  */
326 void 
327 mono_digest_get_public_token (guchar* token, const guchar *pubkey, guint32 len)
328 {
329         guchar digest [20];
330         int i;
331
332         g_return_if_fail (token != NULL);
333         mono_sha1_get_digest (pubkey, len, digest);
334         for (i = 0; i < 8; ++i)
335                 token [i] = digest [19 - i];
336 }
337