Merge pull request #5714 from alexischr/update_bockbuild
[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 /**
82  * mono_sha1_init:
83  */
84 void
85 mono_sha1_init (MonoSHA1Context* context)
86 {
87         CC_SHA1_Init (context);
88 }
89
90 /**
91  * mono_sha1_update:
92  */
93 void
94 mono_sha1_update (MonoSHA1Context* context, const guchar* data, guint32 len)
95 {
96         CC_SHA1_Update (context, data, len);
97 }
98
99 /**
100  * mono_sha1_final:
101  */
102 void
103 mono_sha1_final (MonoSHA1Context* context, unsigned char digest[20])
104 {
105         CC_SHA1_Final (digest, context);
106 }
107
108 #else
109
110 /* #include <process.h> */      /* prototype for exit() - JHB */
111 /* Using return() instead of exit() - SWR */
112
113 static void SHA1Transform(guint32 state[5], const guchar buffer[64]);
114
115 #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
116
117 /* blk0() and blk() perform the initial expand. */
118 /* I got the idea of expanding during the round function from SSLeay */
119 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
120 #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
121     |(rol(block->l[i],8)&0x00FF00FF))
122 #else
123 #define blk0(i) block->l[i]
124 #endif
125 #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
126     ^block->l[(i+2)&15]^block->l[i&15],1))
127
128 /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
129 #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
130 #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
131 #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
132 #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
133 #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
134
135
136 #ifdef VERBOSE  /* SAK */
137 static void SHAPrintContext(MonoSHA1Context *context, char *msg){
138   printf("%s (%d,%d) %x %x %x %x %x\n",
139          msg,
140          context->count[0], context->count[1], 
141          context->state[0],
142          context->state[1],
143          context->state[2],
144          context->state[3],
145          context->state[4]);
146 }
147 #endif
148
149 /* Hash a single 512-bit block. This is the core of the algorithm. */
150
151 static void SHA1Transform(guint32 state[5], const guchar buffer[64])
152 {
153 guint32 a, b, c, d, e;
154 typedef union {
155     unsigned char c[64];
156     guint32 l[16];
157 } CHAR64LONG16;
158 CHAR64LONG16* block;
159 #ifdef SHA1HANDSOFF
160     unsigned char workspace[64];
161     block = (CHAR64LONG16*)workspace;
162     memcpy(block, buffer, 64);
163 #else
164     block = (CHAR64LONG16*)buffer;
165 #endif
166     /* Copy context->state[] to working vars */
167     a = state[0];
168     b = state[1];
169     c = state[2];
170     d = state[3];
171     e = state[4];
172     /* 4 rounds of 20 operations each. Loop unrolled. */
173     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);
174     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);
175     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);
176     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);
177     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);
178     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);
179     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);
180     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);
181     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);
182     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);
183     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);
184     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);
185     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);
186     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);
187     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);
188     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);
189     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);
190     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);
191     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);
192     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);
193     /* Add the working vars back into context.state[] */
194     state[0] += a;
195     state[1] += b;
196     state[2] += c;
197     state[3] += d;
198     state[4] += e;
199     /* Wipe variables */
200     a = b = c = d = e = 0;
201 }
202
203
204 /* mono_sha1_init - Initialize new context */
205
206 void mono_sha1_init(MonoSHA1Context* context)
207 {
208     /* SHA1 initialization constants */
209     context->state[0] = 0x67452301;
210     context->state[1] = 0xEFCDAB89;
211     context->state[2] = 0x98BADCFE;
212     context->state[3] = 0x10325476;
213     context->state[4] = 0xC3D2E1F0;
214     context->count[0] = context->count[1] = 0;
215 }
216
217
218 /* Run your data through this. */
219
220 void mono_sha1_update(MonoSHA1Context* context, const guchar* data, guint32 len)        /*
221 JHB */
222 {
223 guint32 i, j;   /* JHB */
224
225 #ifdef VERBOSE
226     SHAPrintContext(context, "before");
227 #endif
228     j = (context->count[0] >> 3) & 63;
229     if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
230     context->count[1] += (len >> 29);
231     if ((j + len) > 63) {
232         memcpy(&context->buffer[j], data, (i = 64-j));
233         SHA1Transform(context->state, context->buffer);
234         for ( ; i + 63 < len; i += 64) {
235             SHA1Transform(context->state, &data[i]);
236         }
237         j = 0;
238     }
239     else i = 0;
240     memcpy(&context->buffer[j], &data[i], len - i);
241 #ifdef VERBOSE
242     SHAPrintContext(context, "after ");
243 #endif
244 }
245
246
247 /* Add padding and return the message digest. */
248
249 void mono_sha1_final( MonoSHA1Context* context, unsigned char digest[20])
250 {
251 guint32 i;      /* JHB */
252 unsigned char finalcount[8];
253
254     for (i = 0; i < 8; i++) {
255         finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
256          >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
257     }
258     mono_sha1_update(context, (unsigned char *)"\200", 1);
259     while ((context->count[0] & 504) != 448) {
260         mono_sha1_update(context, (unsigned char *)"\0", 1);
261     }
262     mono_sha1_update(context, finalcount, 8);  /* Should cause a SHA1Transform()
263 */
264     for (i = 0; i < 20; i++) {
265         digest[i] = (unsigned char)
266          ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
267     }
268     /* Wipe variables */
269     i = 0;      /* JHB */
270     memset(context->buffer, 0, 64);
271     memset(context->state, 0, 20);
272     memset(context->count, 0, 8);
273     memset(finalcount, 0, 8);   /* SWR */
274 #ifdef SHA1HANDSOFF  /* make SHA1Transform overwrite it's own static vars */
275     SHA1Transform(context->state, context->buffer);
276 #endif
277 }
278
279 #endif
280
281 /**
282  * mono_sha1_get_digest:
283  */
284 void
285 mono_sha1_get_digest (const guchar *buffer, gint buffer_size, guchar digest [20])
286 {       
287         MonoSHA1Context ctx;
288
289         mono_sha1_init (&ctx);
290         mono_sha1_update (&ctx, buffer, buffer_size);
291         mono_sha1_final (&ctx, digest);
292         
293 }
294
295 /**
296  * mono_sha1_get_digest_from_file:
297  * \param filename file name
298  * \param digest 20-byte buffer receiving the hash code.
299  * 
300  * Get the SHA-1 hash of a file. The result is put in 
301  * the 20-byte buffer \p digest.
302  * 
303  * If an IO error happens the value in \p digest is not updated.
304  */
305 void
306 mono_sha1_get_digest_from_file (const gchar *filename, guchar digest [20])
307 {       
308         MonoSHA1Context ctx;
309         guchar tmp_buf[1024];
310         gint nb_bytes_read;
311         FILE *fp;
312
313         mono_sha1_init (&ctx);
314         fp = fopen(filename, "r");
315         if (!fp) {
316                 return;
317         }
318         
319         while ((nb_bytes_read = fread (tmp_buf, sizeof (guchar), 1024, fp)) > 0)
320                 mono_sha1_update (&ctx, tmp_buf, nb_bytes_read);
321         
322         if (ferror(fp)) {
323                 fclose(fp);
324                 return;
325         } else {
326                 fclose(fp);
327         }
328
329         mono_sha1_final (&ctx, digest);
330 }
331
332 /**
333  * mono_digest_get_public_token:
334  *
335  * Get the public token from public key data.
336  * \p token must point to at least 8 bytes of storage.
337  */
338 void 
339 mono_digest_get_public_token (guchar* token, const guchar *pubkey, guint32 len)
340 {
341         guchar digest [20];
342         int i;
343
344         g_return_if_fail (token != NULL);
345         mono_sha1_get_digest (pubkey, len, digest);
346         for (i = 0; i < 8; ++i)
347                 token [i] = digest [19 - i];
348 }
349