ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Digest-Hashcash/Hashcash.xs
Revision: 1.1
Committed: Sat Sep 6 22:12:00 2003 UTC (20 years, 8 months ago) by root
Branch: MAIN
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 #include "EXTERN.h"
2     #include "perl.h"
3     #include "XSUB.h"
4    
5     #include <time.h>
6     #include <stdlib.h>
7     #include <stdint.h>
8    
9     /* NIST Secure Hash Algorithm */
10     /* heavily modified by Uwe Hollerbach <uh@alumni.caltech edu> */
11     /* from Peter C. Gutmann's implementation as found in */
12     /* Applied Cryptography by Bruce Schneier */
13     /* Further modifications to include the "UNRAVEL" stuff, below */
14    
15     /* This code is in the public domain */
16    
17     /* pcg: I was tempted to just rip this code off, after all, if you don't
18     * demand anything I am inclined not to give anything. *Sigh* something
19     * kept me from doing it, so here's the truth: I took this code from the
20     * SHA1 perl module, since it looked reasonably well-crafted. I modified
21     * it here and there, though.
22     */
23    
24     /* don't expect _too_ much from compilers for now. */
25     #if __GNUC_MAJOR > 2
26     # define restrict __restrict__
27     #elif __STDC_VERSION__ < 199900
28     # define restrict
29     #endif
30    
31     /* Useful defines & typedefs */
32    
33     #if defined(U64TYPE) && (defined(USE_64_BIT_INT) || ((BYTEORDER != 0x1234) && (BYTEORDER != 0x4321)))
34     typedef U64TYPE ULONG;
35     # if BYTEORDER == 0x1234
36     # undef BYTEORDER
37     # define BYTEORDER 0x12345678
38     # elif BYTEORDER == 0x4321
39     # undef BYTEORDER
40     # define BYTEORDER 0x87654321
41     # endif
42     #else
43     typedef uint_fast32_t ULONG; /* 32-or-more-bit quantity */
44     #endif
45    
46     #define SHA_BLOCKSIZE 64
47     #define SHA_DIGESTSIZE 20
48    
49     typedef struct {
50     ULONG digest[5]; /* message digest */
51     ULONG count; /* 32-bit bit count */
52     U8 data[SHA_BLOCKSIZE]; /* SHA data buffer */
53     int local; /* unprocessed amount in data */
54     } SHA_INFO;
55    
56    
57     /* UNRAVEL should be fastest & biggest */
58     /* UNROLL_LOOPS should be just as big, but slightly slower */
59     /* both undefined should be smallest and slowest */
60    
61     #define SHA_VERSION 1
62     #define UNRAVEL
63     /* #define UNROLL_LOOPS */
64    
65     /* SHA f()-functions */
66     #define f1(x,y,z) ((x & y) | (~x & z))
67     #define f2(x,y,z) (x ^ y ^ z)
68     #define f3(x,y,z) ((x & y) | (x & z) | (y & z))
69     #define f4(x,y,z) (x ^ y ^ z)
70    
71     /* SHA constants */
72     #define CONST1 0x5a827999L
73     #define CONST2 0x6ed9eba1L
74     #define CONST3 0x8f1bbcdcL
75     #define CONST4 0xca62c1d6L
76    
77     /* truncate to 32 bits -- should be a null op on 32-bit machines */
78     #define T32(x) ((x) & 0xffffffffL)
79    
80     /* 32-bit rotate */
81     #define R32(x,n) T32(((x << n) | (x >> (32 - n))))
82    
83     /* the generic case, for when the overall rotation is not unraveled */
84     #define FG(n) \
85     T = T32(R32(A,5) + f##n(B,C,D) + E + *WP++ + CONST##n); \
86     E = D; D = C; C = R32(B,30); B = A; A = T
87    
88     /* specific cases, for when the overall rotation is unraveled */
89     #define FA(n) \
90     T = T32(R32(A,5) + f##n(B,C,D) + E + *WP++ + CONST##n); B = R32(B,30)
91    
92     #define FB(n) \
93     E = T32(R32(T,5) + f##n(A,B,C) + D + *WP++ + CONST##n); A = R32(A,30)
94    
95     #define FC(n) \
96     D = T32(R32(E,5) + f##n(T,A,B) + C + *WP++ + CONST##n); T = R32(T,30)
97    
98     #define FD(n) \
99     C = T32(R32(D,5) + f##n(E,T,A) + B + *WP++ + CONST##n); E = R32(E,30)
100    
101     #define FE(n) \
102     B = T32(R32(C,5) + f##n(D,E,T) + A + *WP++ + CONST##n); D = R32(D,30)
103    
104     #define FT(n) \
105     A = T32(R32(B,5) + f##n(C,D,E) + T + *WP++ + CONST##n); C = R32(C,30)
106    
107    
108     static void sha_transform(restrict SHA_INFO *sha_info)
109     {
110     int i;
111     U8 *dp;
112     ULONG T, A, B, C, D, E, W[80], *WP;
113    
114     dp = sha_info->data;
115    
116     /*
117     the following makes sure that at least one code block below is
118     traversed or an error is reported, without the necessity for nested
119     preprocessor if/else/endif blocks, which are a great pain in the
120     nether regions of the anatomy...
121     */
122     #undef SWAP_DONE
123    
124     #if BYTEORDER == 0x1234
125     #define SWAP_DONE
126     assert(sizeof(ULONG) == 4);
127     for (i = 0; i < 16; ++i) {
128     T = *((ULONG *) dp);
129     dp += 4;
130     W[i] = ((T << 24) & 0xff000000) | ((T << 8) & 0x00ff0000) |
131     ((T >> 8) & 0x0000ff00) | ((T >> 24) & 0x000000ff);
132     }
133     #endif
134    
135     #if BYTEORDER == 0x4321
136     #define SWAP_DONE
137     assert(sizeof(ULONG) == 4);
138     for (i = 0; i < 16; ++i) {
139     T = *((ULONG *) dp);
140     dp += 4;
141     W[i] = T32(T);
142     }
143     #endif
144    
145     #if BYTEORDER == 0x12345678
146     #define SWAP_DONE
147     assert(sizeof(ULONG) == 8);
148     for (i = 0; i < 16; i += 2) {
149     T = *((ULONG *) dp);
150     dp += 8;
151     W[i] = ((T << 24) & 0xff000000) | ((T << 8) & 0x00ff0000) |
152     ((T >> 8) & 0x0000ff00) | ((T >> 24) & 0x000000ff);
153     T >>= 32;
154     W[i+1] = ((T << 24) & 0xff000000) | ((T << 8) & 0x00ff0000) |
155     ((T >> 8) & 0x0000ff00) | ((T >> 24) & 0x000000ff);
156     }
157     #endif
158    
159     #if BYTEORDER == 0x87654321
160     #define SWAP_DONE
161     assert(sizeof(ULONG) == 8);
162     for (i = 0; i < 16; i += 2) {
163     T = *((ULONG *) dp);
164     dp += 8;
165     W[i] = T32(T >> 32);
166     W[i+1] = T32(T);
167     }
168     #endif
169    
170     #ifndef SWAP_DONE
171     #error Unknown byte order -- you need to add code here
172     #endif /* SWAP_DONE */
173    
174     for (i = 16; i < 80; ++i) {
175     W[i] = W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16];
176     #if (SHA_VERSION == 1)
177     W[i] = R32(W[i], 1);
178     #endif /* SHA_VERSION */
179     }
180     A = sha_info->digest[0];
181     B = sha_info->digest[1];
182     C = sha_info->digest[2];
183     D = sha_info->digest[3];
184     E = sha_info->digest[4];
185     WP = W;
186     #ifdef UNRAVEL
187     FA(1); FB(1); FC(1); FD(1); FE(1); FT(1); FA(1); FB(1); FC(1); FD(1);
188     FE(1); FT(1); FA(1); FB(1); FC(1); FD(1); FE(1); FT(1); FA(1); FB(1);
189     FC(2); FD(2); FE(2); FT(2); FA(2); FB(2); FC(2); FD(2); FE(2); FT(2);
190     FA(2); FB(2); FC(2); FD(2); FE(2); FT(2); FA(2); FB(2); FC(2); FD(2);
191     FE(3); FT(3); FA(3); FB(3); FC(3); FD(3); FE(3); FT(3); FA(3); FB(3);
192     FC(3); FD(3); FE(3); FT(3); FA(3); FB(3); FC(3); FD(3); FE(3); FT(3);
193     FA(4); FB(4); FC(4); FD(4); FE(4); FT(4); FA(4); FB(4); FC(4); FD(4);
194     FE(4); FT(4); FA(4); FB(4); FC(4); FD(4); FE(4); FT(4); FA(4); FB(4);
195     sha_info->digest[0] = T32(sha_info->digest[0] + E);
196     sha_info->digest[1] = T32(sha_info->digest[1] + T);
197     sha_info->digest[2] = T32(sha_info->digest[2] + A);
198     sha_info->digest[3] = T32(sha_info->digest[3] + B);
199     sha_info->digest[4] = T32(sha_info->digest[4] + C);
200     #else /* !UNRAVEL */
201     #ifdef UNROLL_LOOPS
202     FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1);
203     FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1);
204     FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2);
205     FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2);
206     FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3);
207     FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3);
208     FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4);
209     FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4);
210     #else /* !UNROLL_LOOPS */
211     for (i = 0; i < 20; ++i) { FG(1); }
212     for (i = 20; i < 40; ++i) { FG(2); }
213     for (i = 40; i < 60; ++i) { FG(3); }
214     for (i = 60; i < 80; ++i) { FG(4); }
215     #endif /* !UNROLL_LOOPS */
216     sha_info->digest[0] = T32(sha_info->digest[0] + A);
217     sha_info->digest[1] = T32(sha_info->digest[1] + B);
218     sha_info->digest[2] = T32(sha_info->digest[2] + C);
219     sha_info->digest[3] = T32(sha_info->digest[3] + D);
220     sha_info->digest[4] = T32(sha_info->digest[4] + E);
221     #endif /* !UNRAVEL */
222     }
223    
224     /* initialize the SHA digest */
225    
226     static void sha_init(restrict SHA_INFO *sha_info)
227     {
228     sha_info->digest[0] = 0x67452301L;
229     sha_info->digest[1] = 0xefcdab89L;
230     sha_info->digest[2] = 0x98badcfeL;
231     sha_info->digest[3] = 0x10325476L;
232     sha_info->digest[4] = 0xc3d2e1f0L;
233     sha_info->count = 0L;
234     sha_info->local = 0;
235     }
236    
237     /* update the SHA digest */
238    
239     static void sha_update(restrict SHA_INFO *sha_info, restrict U8 *buffer, int count)
240     {
241     int i;
242    
243     sha_info->count += count << 3;
244     if (sha_info->local) {
245     i = SHA_BLOCKSIZE - sha_info->local;
246     if (i > count) {
247     i = count;
248     }
249     memcpy(((U8 *) sha_info->data) + sha_info->local, buffer, i);
250     count -= i;
251     buffer += i;
252     sha_info->local += i;
253     if (sha_info->local == SHA_BLOCKSIZE) {
254     sha_transform(sha_info);
255     } else {
256     return;
257     }
258     }
259     while (count >= SHA_BLOCKSIZE) {
260     memcpy(sha_info->data, buffer, SHA_BLOCKSIZE);
261     buffer += SHA_BLOCKSIZE;
262     count -= SHA_BLOCKSIZE;
263     sha_transform(sha_info);
264     }
265     memcpy(sha_info->data, buffer, count);
266     sha_info->local = count;
267     }
268    
269    
270     #if 0
271     static void sha_transform_and_copy (unsigned char digest[20], restrict SHA_INFO *sha_info)
272     {
273     sha_transform (sha_info);
274    
275     digest[ 0] = (unsigned char) ((sha_info->digest[0] >> 24) & 0xff);
276     digest[ 1] = (unsigned char) ((sha_info->digest[0] >> 16) & 0xff);
277     digest[ 2] = (unsigned char) ((sha_info->digest[0] >> 8) & 0xff);
278     digest[ 3] = (unsigned char) ((sha_info->digest[0] ) & 0xff);
279     digest[ 4] = (unsigned char) ((sha_info->digest[1] >> 24) & 0xff);
280     digest[ 5] = (unsigned char) ((sha_info->digest[1] >> 16) & 0xff);
281     digest[ 6] = (unsigned char) ((sha_info->digest[1] >> 8) & 0xff);
282     digest[ 7] = (unsigned char) ((sha_info->digest[1] ) & 0xff);
283     digest[ 8] = (unsigned char) ((sha_info->digest[2] >> 24) & 0xff);
284     digest[ 9] = (unsigned char) ((sha_info->digest[2] >> 16) & 0xff);
285     digest[10] = (unsigned char) ((sha_info->digest[2] >> 8) & 0xff);
286     digest[11] = (unsigned char) ((sha_info->digest[2] ) & 0xff);
287     digest[12] = (unsigned char) ((sha_info->digest[3] >> 24) & 0xff);
288     digest[13] = (unsigned char) ((sha_info->digest[3] >> 16) & 0xff);
289     digest[14] = (unsigned char) ((sha_info->digest[3] >> 8) & 0xff);
290     digest[15] = (unsigned char) ((sha_info->digest[3] ) & 0xff);
291     digest[16] = (unsigned char) ((sha_info->digest[4] >> 24) & 0xff);
292     digest[17] = (unsigned char) ((sha_info->digest[4] >> 16) & 0xff);
293     digest[18] = (unsigned char) ((sha_info->digest[4] >> 8) & 0xff);
294     digest[19] = (unsigned char) ((sha_info->digest[4] ) & 0xff);
295     }
296     #endif
297    
298     /* finish computing the SHA digest */
299     static void sha_final(SHA_INFO *sha_info)
300     {
301     int count;
302     U32 bit_count;
303    
304     bit_count = sha_info->count;
305     count = (int) ((bit_count >> 3) & 0x3f);
306     ((U8 *) sha_info->data)[count++] = 0x80;
307    
308     if (count > SHA_BLOCKSIZE - 8) {
309     memset(((U8 *) sha_info->data) + count, 0, SHA_BLOCKSIZE - count);
310     sha_transform(sha_info);
311     memset((U8 *) sha_info->data, 0, SHA_BLOCKSIZE - 8);
312     } else {
313     memset(((U8 *) sha_info->data) + count, 0, SHA_BLOCKSIZE - 8 - count);
314     }
315    
316     sha_info->data[56] = 0;
317     sha_info->data[57] = 0;
318     sha_info->data[58] = 0;
319     sha_info->data[59] = 0;
320     sha_info->data[60] = (bit_count >> 24) & 0xff;
321     sha_info->data[61] = (bit_count >> 16) & 0xff;
322     sha_info->data[62] = (bit_count >> 8) & 0xff;
323     sha_info->data[63] = (bit_count >> 0) & 0xff;
324    
325     sha_transform (sha_info);
326     }
327    
328     #define TRIALCHAR "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&()*+,-./;<=>?@[]{}^_|"
329    
330     static char nextenc[256];
331    
332     static char rand_char ()
333     {
334     return TRIALCHAR[rand () % sizeof (TRIALCHAR)];
335     }
336    
337     static int zprefix (ULONG n)
338     {
339     static char zp[256] =
340     {
341     8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
342     3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
343     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
344     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
345     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
346     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
347     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
348     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
349     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
350     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
351     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
352     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
353     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
354     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
355     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
356     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
357     };
358    
359     return
360     n > 0xffffff ? zp[n >> 24]
361     : n > 0xffff ? 8 + zp[n >> 16]
362     : n > 0xff ? 16 + zp[n >> 8]
363     : 24 + zp[n];
364     }
365    
366     MODULE = Digest::Hashcash PACKAGE = Digest::Hashcash
367    
368     BOOT:
369     {
370     int i;
371    
372     for (i = 0; i < sizeof (TRIALCHAR); i++)
373     nextenc[TRIALCHAR[i]] = TRIALCHAR[(i + 1) % sizeof (TRIALCHAR)];
374     }
375    
376     PROTOTYPES: ENABLE
377    
378     int
379     _estimate_time (float seconds = 2, float minfactor = 1)
380     CODE:
381     RETVAL = minfactor;
382     OUTPUT:
383     RETVAL
384    
385     SV *
386     _gentoken (int collisions, IV timestamp, char *resource, char *trial = "", int extrarand = 0)
387     CODE:
388     SHA_INFO ctx1, ctx;
389     char *token, *seq, *s;
390     int toklen, i;
391     time_t tstamp = timestamp ? timestamp : time (0);
392     struct tm *tm = gmtime (&tstamp);
393    
394     New (0, token,
395     1 + 1 // version
396     + 12 + 1 // time field sans century
397     + strlen (resource) + 1 // ressource
398     + strlen (trial) + extrarand + 8 + 1 // trial
399     + 1,
400     char);
401    
402     if (!token)
403     croak ("out of memory");
404    
405     if (collisions > 32)
406     croak ("collisions must be <= 32 in this implementation\n");
407    
408     toklen = sprintf (token, "%d:%02d%02d%02d%02d%02d%02d:%s:%s",
409     0, tm->tm_year % 100, tm->tm_mon + 1, tm->tm_mday,
410     tm->tm_hour, tm->tm_min, tm->tm_sec,
411     resource, trial);
412    
413     i = toklen + extrarand;
414     while (toklen < i)
415     token[toklen++] = rand_char ();
416    
417     sha_init (&ctx1);
418     sha_update (&ctx1, token, toklen);
419    
420     seq = token + toklen;
421     i += 8;
422     while (toklen < i)
423     token[toklen++] = rand_char ();
424    
425     for (;;)
426     {
427     ctx = ctx1; // this "optimization" can help a lot for longer resource strings
428     sha_update (&ctx, seq, 8);
429     sha_final (&ctx);
430    
431     i = zprefix (ctx.digest[0]);
432    
433     if (i >= collisions)
434     break;
435    
436     s = seq;
437     do {
438     *s = nextenc [*s];
439     } while (*s++ == 'a');
440     }
441    
442     RETVAL = newSVpvn (token, toklen);
443     OUTPUT:
444     RETVAL
445    
446     int
447     _prefixlen (SV *tok)
448     CODE:
449     STRLEN toklen;
450     char *token = SvPV (tok, toklen);
451     SHA_INFO ctx;
452    
453     sha_init (&ctx);
454     sha_update (&ctx, token, toklen);
455     sha_final (&ctx);
456    
457     RETVAL = zprefix (ctx.digest[0]);
458     OUTPUT:
459     RETVAL
460    
461