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