ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Digest-Hashcash/Hashcash.xs
Revision: 1.4
Committed: Mon Oct 20 04:17:05 2003 UTC (20 years, 7 months ago) by root
Branch: MAIN
Changes since 1.3: +9 -7 lines
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 root 1.2 #if __GNUC__ > 2
26 root 1.1 # define restrict __restrict__
27 root 1.2 # define inline __inline__
28     # ifdef __i386
29     # define GCCX86ASM 1
30     # endif
31 root 1.1 #elif __STDC_VERSION__ < 199900
32     # define restrict
33 root 1.2 # define inline
34 root 1.1 #endif
35    
36     /* Useful defines & typedefs */
37    
38     #if defined(U64TYPE) && (defined(USE_64_BIT_INT) || ((BYTEORDER != 0x1234) && (BYTEORDER != 0x4321)))
39     typedef U64TYPE ULONG;
40 root 1.4 # if BYTEORDER == 0x1234
41     # undef BYTEORDER
42     # define BYTEORDER 0x12345678
43     # elif BYTEORDER == 0x4321
44     # undef BYTEORDER
45     # define BYTEORDER 0x87654321
46     # endif
47 root 1.1 #else
48     typedef uint_fast32_t ULONG; /* 32-or-more-bit quantity */
49     #endif
50    
51 root 1.2 #if GCCX86ASM
52     # define zprefix(n) ({ int _r; __asm__ ("bsrl %1, %0" : "=r" (_r) : "r" (n)); 31 - _r ; })
53 root 1.4 #elif __GNUC__ > 2 && __GNUC_MINOR__ > 3
54     # define zprefix(n) (__extension__ ({ uint32_t n__ = (n); n ? __builtin_clz (n) : 32; }))
55 root 1.2 #else
56     static int zprefix (ULONG n)
57     {
58     static char zp[256] =
59     {
60     8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
61     3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
62     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
63     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
64     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
65     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
66     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
67     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
68     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
69     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
70     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
71     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
72     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
73     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
74     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
75     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
76     };
77    
78     return
79     n > 0xffffff ? zp[n >> 24]
80     : n > 0xffff ? 8 + zp[n >> 16]
81     : n > 0xff ? 16 + zp[n >> 8]
82     : 24 + zp[n];
83     }
84     #endif
85    
86 root 1.1 #define SHA_BLOCKSIZE 64
87     #define SHA_DIGESTSIZE 20
88    
89     typedef struct {
90     ULONG digest[5]; /* message digest */
91     ULONG count; /* 32-bit bit count */
92 root 1.2 int local; /* unprocessed amount in data */
93 root 1.1 U8 data[SHA_BLOCKSIZE]; /* SHA data buffer */
94     } SHA_INFO;
95    
96    
97     /* SHA f()-functions */
98     #define f1(x,y,z) ((x & y) | (~x & z))
99     #define f2(x,y,z) (x ^ y ^ z)
100     #define f3(x,y,z) ((x & y) | (x & z) | (y & z))
101     #define f4(x,y,z) (x ^ y ^ z)
102    
103     /* SHA constants */
104     #define CONST1 0x5a827999L
105     #define CONST2 0x6ed9eba1L
106     #define CONST3 0x8f1bbcdcL
107     #define CONST4 0xca62c1d6L
108    
109     /* truncate to 32 bits -- should be a null op on 32-bit machines */
110     #define T32(x) ((x) & 0xffffffffL)
111    
112     /* 32-bit rotate */
113     #define R32(x,n) T32(((x << n) | (x >> (32 - n))))
114    
115     /* specific cases, for when the overall rotation is unraveled */
116     #define FA(n) \
117     T = T32(R32(A,5) + f##n(B,C,D) + E + *WP++ + CONST##n); B = R32(B,30)
118    
119     #define FB(n) \
120     E = T32(R32(T,5) + f##n(A,B,C) + D + *WP++ + CONST##n); A = R32(A,30)
121    
122     #define FC(n) \
123     D = T32(R32(E,5) + f##n(T,A,B) + C + *WP++ + CONST##n); T = R32(T,30)
124    
125     #define FD(n) \
126     C = T32(R32(D,5) + f##n(E,T,A) + B + *WP++ + CONST##n); E = R32(E,30)
127    
128     #define FE(n) \
129     B = T32(R32(C,5) + f##n(D,E,T) + A + *WP++ + CONST##n); D = R32(D,30)
130    
131     #define FT(n) \
132     A = T32(R32(B,5) + f##n(C,D,E) + T + *WP++ + CONST##n); C = R32(C,30)
133    
134 root 1.2 static void sha_transform(SHA_INFO *restrict sha_info)
135 root 1.1 {
136     int i;
137     U8 *dp;
138 root 1.2 ULONG T, A, B, C, D, E, W[80], *restrict WP;
139 root 1.1
140     dp = sha_info->data;
141    
142     #if BYTEORDER == 0x1234
143     assert(sizeof(ULONG) == 4);
144 root 1.2 # ifdef HAS_NTOHL
145     for (i = 0; i < 16; ++i) {
146     T = *((ULONG *) dp);
147     dp += 4;
148     W[i] = ntohl (T);
149     }
150     # else
151 root 1.1 for (i = 0; i < 16; ++i) {
152     T = *((ULONG *) dp);
153     dp += 4;
154     W[i] = ((T << 24) & 0xff000000) | ((T << 8) & 0x00ff0000) |
155     ((T >> 8) & 0x0000ff00) | ((T >> 24) & 0x000000ff);
156     }
157 root 1.2 # endif
158     #elif BYTEORDER == 0x4321
159 root 1.1 assert(sizeof(ULONG) == 4);
160     for (i = 0; i < 16; ++i) {
161     T = *((ULONG *) dp);
162     dp += 4;
163     W[i] = T32(T);
164     }
165 root 1.2 #elif BYTEORDER == 0x12345678
166 root 1.1 assert(sizeof(ULONG) == 8);
167     for (i = 0; i < 16; i += 2) {
168     T = *((ULONG *) dp);
169     dp += 8;
170     W[i] = ((T << 24) & 0xff000000) | ((T << 8) & 0x00ff0000) |
171     ((T >> 8) & 0x0000ff00) | ((T >> 24) & 0x000000ff);
172     T >>= 32;
173     W[i+1] = ((T << 24) & 0xff000000) | ((T << 8) & 0x00ff0000) |
174     ((T >> 8) & 0x0000ff00) | ((T >> 24) & 0x000000ff);
175     }
176 root 1.2 #elif BYTEORDER == 0x87654321
177 root 1.1 assert(sizeof(ULONG) == 8);
178     for (i = 0; i < 16; i += 2) {
179     T = *((ULONG *) dp);
180     dp += 8;
181     W[i] = T32(T >> 32);
182     W[i+1] = T32(T);
183     }
184 root 1.2 #else
185     #error Unknown byte order -- you need to add code here
186 root 1.1 #endif
187    
188 root 1.2 for (i = 16; i < 80; ++i)
189     {
190     T = W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16];
191     W[i] = R32(T,1);
192     }
193 root 1.1
194     A = sha_info->digest[0];
195     B = sha_info->digest[1];
196     C = sha_info->digest[2];
197     D = sha_info->digest[3];
198     E = sha_info->digest[4];
199 root 1.2
200 root 1.1 WP = W;
201     FA(1); FB(1); FC(1); FD(1); FE(1); FT(1); FA(1); FB(1); FC(1); FD(1);
202     FE(1); FT(1); FA(1); FB(1); FC(1); FD(1); FE(1); FT(1); FA(1); FB(1);
203     FC(2); FD(2); FE(2); FT(2); FA(2); FB(2); FC(2); FD(2); FE(2); FT(2);
204     FA(2); FB(2); FC(2); FD(2); FE(2); FT(2); FA(2); FB(2); FC(2); FD(2);
205     FE(3); FT(3); FA(3); FB(3); FC(3); FD(3); FE(3); FT(3); FA(3); FB(3);
206     FC(3); FD(3); FE(3); FT(3); FA(3); FB(3); FC(3); FD(3); FE(3); FT(3);
207     FA(4); FB(4); FC(4); FD(4); FE(4); FT(4); FA(4); FB(4); FC(4); FD(4);
208     FE(4); FT(4); FA(4); FB(4); FC(4); FD(4); FE(4); FT(4); FA(4); FB(4);
209 root 1.2
210 root 1.1 sha_info->digest[0] = T32(sha_info->digest[0] + E);
211     sha_info->digest[1] = T32(sha_info->digest[1] + T);
212     sha_info->digest[2] = T32(sha_info->digest[2] + A);
213     sha_info->digest[3] = T32(sha_info->digest[3] + B);
214     sha_info->digest[4] = T32(sha_info->digest[4] + C);
215     }
216    
217     /* initialize the SHA digest */
218    
219 root 1.2 static void sha_init(SHA_INFO *restrict sha_info)
220 root 1.1 {
221     sha_info->digest[0] = 0x67452301L;
222     sha_info->digest[1] = 0xefcdab89L;
223     sha_info->digest[2] = 0x98badcfeL;
224     sha_info->digest[3] = 0x10325476L;
225     sha_info->digest[4] = 0xc3d2e1f0L;
226     sha_info->count = 0L;
227     sha_info->local = 0;
228     }
229    
230     /* update the SHA digest */
231    
232 root 1.2 static void sha_update(SHA_INFO *restrict sha_info, U8 *restrict buffer, int count)
233 root 1.1 {
234     int i;
235    
236 root 1.2 sha_info->count += count;
237 root 1.1 if (sha_info->local) {
238     i = SHA_BLOCKSIZE - sha_info->local;
239     if (i > count) {
240     i = count;
241     }
242     memcpy(((U8 *) sha_info->data) + sha_info->local, buffer, i);
243     count -= i;
244     buffer += i;
245     sha_info->local += i;
246     if (sha_info->local == SHA_BLOCKSIZE) {
247     sha_transform(sha_info);
248     } else {
249     return;
250     }
251     }
252     while (count >= SHA_BLOCKSIZE) {
253     memcpy(sha_info->data, buffer, SHA_BLOCKSIZE);
254     buffer += SHA_BLOCKSIZE;
255     count -= SHA_BLOCKSIZE;
256     sha_transform(sha_info);
257     }
258     memcpy(sha_info->data, buffer, count);
259     sha_info->local = count;
260     }
261    
262     /* finish computing the SHA digest */
263 root 1.2 static int sha_final(SHA_INFO *sha_info)
264 root 1.1 {
265 root 1.2 int count = sha_info->count;
266     int local = sha_info->local;
267 root 1.1
268 root 1.2 sha_info->data[local] = 0x80;
269 root 1.1
270 root 1.2 if (sha_info->local >= SHA_BLOCKSIZE - 8) {
271     memset(sha_info->data + local + 1, 0, SHA_BLOCKSIZE - 1 - local);
272     sha_transform(sha_info);
273     memset(sha_info->data, 0, SHA_BLOCKSIZE - 2);
274     } else {
275     memset(sha_info->data + local + 1, 0, SHA_BLOCKSIZE - 3 - local);
276     }
277    
278     sha_info->data[62] = count >> 5;
279     sha_info->data[63] = count << 3;
280    
281     sha_transform (sha_info);
282    
283     return sha_info->digest[0]
284     ? zprefix (sha_info->digest[0])
285     : zprefix (sha_info->digest[1]) + 32;
286 root 1.1 }
287    
288     #define TRIALCHAR "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&()*+,-./;<=>?@[]{}^_|"
289    
290     static char nextenc[256];
291    
292     static char rand_char ()
293     {
294     return TRIALCHAR[rand () % sizeof (TRIALCHAR)];
295     }
296    
297 root 1.2 typedef double (*NVTime)(void);
298    
299     static double simple_nvtime (void)
300     {
301     return time (0);
302     }
303    
304     static NVTime get_nvtime (void)
305 root 1.1 {
306 root 1.2 SV **svp = hv_fetch (PL_modglobal, "Time::NVtime", 12, 0);
307    
308     if (svp && SvIOK(*svp))
309     return INT2PTR(NVTime, SvIV(*svp));
310     else
311     return simple_nvtime;
312 root 1.1
313     }
314    
315     MODULE = Digest::Hashcash PACKAGE = Digest::Hashcash
316    
317     BOOT:
318     {
319     int i;
320    
321     for (i = 0; i < sizeof (TRIALCHAR); i++)
322     nextenc[TRIALCHAR[i]] = TRIALCHAR[(i + 1) % sizeof (TRIALCHAR)];
323     }
324    
325     PROTOTYPES: ENABLE
326    
327 root 1.2 # could be improved quite a bit in accuracy
328     NV
329     _estimate_rounds ()
330 root 1.1 CODE:
331 root 1.3 {
332 root 1.2 char data[40];
333     NVTime nvtime = get_nvtime ();
334     NV t1, t2, t;
335     int count = 0;
336     SHA_INFO ctx;
337    
338     t = nvtime ();
339     do {
340     t1 = nvtime ();
341     } while (t == t1);
342    
343     t = t2 = nvtime ();
344     do {
345     volatile int i;
346     sha_init (&ctx);
347     sha_update (&ctx, data, sizeof (data));
348     i = sha_final (&ctx);
349    
350     if (!(++count & 1023))
351     t2 = nvtime ();
352    
353     } while (t == t2);
354    
355     RETVAL = (NV)count / (t2 - t1);
356 root 1.3 }
357 root 1.1 OUTPUT:
358     RETVAL
359    
360     SV *
361 root 1.2 _gentoken (int size, IV timestamp, char *resource, char *trial = "", int extrarand = 0)
362 root 1.1 CODE:
363 root 1.3 {
364 root 1.1 SHA_INFO ctx1, ctx;
365     char *token, *seq, *s;
366     int toklen, i;
367     time_t tstamp = timestamp ? timestamp : time (0);
368     struct tm *tm = gmtime (&tstamp);
369    
370     New (0, token,
371     1 + 1 // version
372     + 12 + 1 // time field sans century
373     + strlen (resource) + 1 // ressource
374     + strlen (trial) + extrarand + 8 + 1 // trial
375     + 1,
376     char);
377    
378     if (!token)
379     croak ("out of memory");
380    
381 root 1.2 if (size > 64)
382     croak ("size must be <= 64 in this implementation\n");
383 root 1.1
384     toklen = sprintf (token, "%d:%02d%02d%02d%02d%02d%02d:%s:%s",
385     0, tm->tm_year % 100, tm->tm_mon + 1, tm->tm_mday,
386     tm->tm_hour, tm->tm_min, tm->tm_sec,
387     resource, trial);
388    
389 root 1.2 if (toklen > 8000)
390     croak ("token length must be <= 8000 in this implementation\n");
391    
392 root 1.1 i = toklen + extrarand;
393     while (toklen < i)
394     token[toklen++] = rand_char ();
395    
396     sha_init (&ctx1);
397     sha_update (&ctx1, token, toklen);
398    
399     seq = token + toklen;
400     i += 8;
401     while (toklen < i)
402     token[toklen++] = rand_char ();
403    
404     for (;;)
405     {
406     ctx = ctx1; // this "optimization" can help a lot for longer resource strings
407     sha_update (&ctx, seq, 8);
408 root 1.2 i = sha_final (&ctx);
409 root 1.1
410 root 1.2 if (i >= size)
411 root 1.1 break;
412    
413     s = seq;
414     do {
415     *s = nextenc [*s];
416     } while (*s++ == 'a');
417     }
418    
419     RETVAL = newSVpvn (token, toklen);
420 root 1.3 }
421 root 1.1 OUTPUT:
422     RETVAL
423    
424     int
425     _prefixlen (SV *tok)
426     CODE:
427 root 1.3 {
428 root 1.1 STRLEN toklen;
429     char *token = SvPV (tok, toklen);
430     SHA_INFO ctx;
431    
432     sha_init (&ctx);
433     sha_update (&ctx, token, toklen);
434 root 1.2 RETVAL = sha_final (&ctx);
435 root 1.3 }
436 root 1.1 OUTPUT:
437     RETVAL
438    
439