… | |
… | |
23 | package Digest::Hashcash; |
23 | package Digest::Hashcash; |
24 | |
24 | |
25 | use Time::Local; |
25 | use Time::Local; |
26 | use Time::HiRes; |
26 | use Time::HiRes; |
27 | |
27 | |
28 | require XSLoader; |
|
|
29 | |
|
|
30 | no warnings; |
28 | no warnings; |
31 | |
29 | |
32 | $VERSION = 0.02; |
30 | BEGIN { |
|
|
31 | our $VERSION = 1.1; |
33 | |
32 | |
|
|
33 | require XSLoader; |
34 | XSLoader::load Digest::Hashcash, $VERSION; |
34 | XSLoader::load Digest::Hashcash, $VERSION; |
|
|
35 | } |
35 | |
36 | |
36 | =item $secs = estimate_time $size |
37 | =item $secs = estimate_time $size |
37 | |
38 | |
38 | Estimate the average time necessary to calculate a token of the given |
39 | Estimate the average time necessary to calculate a token of the given |
39 | size. |
40 | size. |
… | |
… | |
48 | Estimating the time to be used can go wrong by as much as 50% (but is |
49 | Estimating the time to be used can go wrong by as much as 50% (but is |
49 | usually quite accurate), and the estimation itself can take as much as a |
50 | usually quite accurate), and the estimation itself can take as much as a |
50 | second on slower (<pentium) machines, but faster machines (1Ghz P3 for |
51 | second on slower (<pentium) machines, but faster machines (1Ghz P3 for |
51 | example) usually handle it within a hundredth of a second or so. |
52 | example) usually handle it within a hundredth of a second or so. |
52 | |
53 | |
53 | The estimation will be done only once, so you can call this fucntion as |
54 | The estimation will be done only once, so you can call this function as |
54 | often as you like without incuring the overhead everytime. |
55 | often as you like without incuring the overhead everytime. |
55 | |
56 | |
56 | =cut |
57 | =cut |
57 | |
58 | |
58 | my $rounds; |
59 | my $rounds; |
59 | |
60 | |
60 | sub _rounds { |
61 | sub _rounds() { |
61 | $rounds ||= &_estimate_rounds(); |
62 | $rounds ||= _estimate_rounds |
62 | } |
63 | } |
63 | |
64 | |
64 | sub estimate_time { |
65 | sub estimate_time($) { |
65 | my ($size) = @_; |
66 | my ($size) = @_; |
|
|
67 | |
66 | 2**$size / &_rounds; |
68 | 2**$size / _rounds |
67 | } |
69 | } |
68 | |
70 | |
69 | sub estimate_size { |
71 | sub estimate_size($$) { |
70 | my ($time, $min) = @_; |
72 | my ($time, $min) = @_; |
|
|
73 | |
71 | $time = (log $time * $rounds) / log 2; |
74 | $time = (log $time * _rounds) / log 2; |
72 | $time < $min ? $min : int $time; |
75 | $time < $min ? $min : int $time |
73 | } |
76 | } |
74 | |
77 | |
75 | =item $cipher = new [param => value...] |
78 | =item $cipher = new Digest::Hashcash [param => value...] |
76 | |
79 | |
77 | =over 4 |
80 | =over 4 |
78 | |
81 | |
79 | =item size => 18 |
82 | =item size => 18 |
80 | |
83 | |
… | |
… | |
101 | The timestamp to use. A value of 0 (the default) means to use the current |
104 | The timestamp to use. A value of 0 (the default) means to use the current |
102 | time. |
105 | time. |
103 | |
106 | |
104 | =back |
107 | =back |
105 | |
108 | |
106 | =item $token = $cipher->hash($data [, param => value...]) |
109 | =item $token = $cipher->hash ($data [, param => value...]) |
107 | |
110 | |
108 | Creates and returns a new token. This can take some time. |
111 | Creates and returns a new token. This can take some time. |
109 | |
112 | |
110 | Any additional parameters are interpreted the same way as arguments to |
113 | Any additional parameters are interpreted the same way as arguments to |
111 | C<new>. |
114 | C<new>. |
112 | |
115 | |
113 | =item $prefix = $cipher->verify($token [, param => value...])) |
116 | =item $prefix = $cipher->verify ($token [, param => value...])) |
114 | |
117 | |
115 | Checks the given token and returns true if the token has the minimum |
118 | Checks the given token and returns true if the token has the minimum |
116 | number of prefix bits, or false otherwise. The value returned is actually |
119 | number of prefix bits, or false otherwise. The value returned is actually |
117 | the number of collisions, so to find the number of collisions bits specify |
120 | the number of collisions, so to find the number of collisions bits specify |
118 | C<< collisions => 0 >>. |
121 | C<< collisions => 0 >>. |
119 | |
122 | |
120 | Any additional parameters are interpreted the same way as arguments to |
123 | Any additional parameters are interpreted the same way as arguments to |
121 | C<new>. |
124 | C<new>. |
122 | |
125 | |
123 | =item $resource = $cipher->resource($token) |
126 | =item $resource = $cipher->resource ($token) |
124 | |
127 | |
125 | Returns the resource part, or C<undef>. |
128 | Returns the resource part, or C<undef>. |
126 | |
129 | |
127 | =item $tstamp = $ciper->timestamp($token) |
130 | =item $tstamp = $ciper->timestamp ($token) |
128 | |
131 | |
129 | Returns the timestamp part (in the same format as perl's C<time>), or |
132 | Returns the timestamp part (in the same format as perl's C<time>), or |
130 | C<undef>. |
133 | C<undef>. |
131 | |
134 | |
132 | =back |
135 | =back |
… | |
… | |
141 | |
144 | |
142 | sub hash { |
145 | sub hash { |
143 | my $self = shift; |
146 | my $self = shift; |
144 | my %arg = (%$self, resource => @_); |
147 | my %arg = (%$self, resource => @_); |
145 | |
148 | |
146 | &_gentoken(@arg{qw(size timestamp resource uid extrarand)}); |
149 | &_gentoken (@arg{qw(size timestamp resource uid extrarand)}) |
147 | } |
150 | } |
148 | |
151 | |
149 | sub verify { |
152 | sub verify { |
150 | my ($self, $token) = (shift, shift); |
153 | my ($self, $token) = (shift, shift); |
151 | my %arg = (%$self, @_); |
154 | my %arg = (%$self, @_); |
152 | |
155 | |
153 | my $prefix = &_prefixlen($token); |
156 | my $prefix = _prefixlen $token; |
154 | |
157 | |
155 | $prefix < $arg{size} |
158 | $prefix < $arg{size} |
156 | ? undef |
159 | ? undef |
157 | : $prefix; |
160 | : $prefix |
158 | } |
161 | } |
159 | |
162 | |
160 | sub resource { |
163 | sub resource { |
161 | my ($self, $token) = @_; |
164 | my ($self, $token) = @_; |
162 | |
165 | |
163 | $token =~ /^\d+:\d*:(.*):/ |
166 | $token =~ /^\d+:\d*:(.*):/ |
164 | or return undef; |
167 | or return undef; |
165 | |
168 | |
166 | return $1; |
169 | $1 |
167 | } |
170 | } |
168 | |
171 | |
169 | sub timestamp { |
172 | sub timestamp { |
170 | my ($self, $token) = @_; |
173 | my ($self, $token) = @_; |
171 | |
174 | |
… | |
… | |
179 | $d = /\G(\d\d)/gc ? $1 : 1; |
182 | $d = /\G(\d\d)/gc ? $1 : 1; |
180 | $H = /\G(\d\d)/gc ? $1 : 0; |
183 | $H = /\G(\d\d)/gc ? $1 : 0; |
181 | $M = /\G(\d\d)/gc ? $1 : 0; |
184 | $M = /\G(\d\d)/gc ? $1 : 0; |
182 | $S = /\G(\d\d)/gc ? $1 : 0; |
185 | $S = /\G(\d\d)/gc ? $1 : 0; |
183 | |
186 | |
184 | return timegm $S, $M, $H, $d, $m - 1, $y; |
187 | timegm $S, $M, $H, $d, $m - 1, $y |
185 | } |
188 | } |
186 | |
189 | |
187 | =head1 SEE ALSO |
190 | =head1 SEE ALSO |
188 | |
191 | |
189 | L<http://www.hashcash.org>. |
192 | L<http://www.hashcash.org>. |
|
|
193 | |
|
|
194 | =head1 SUPPORT FOR THE PERL MULTICORE SPECIFICATION |
|
|
195 | |
|
|
196 | This module supports the perl multicore specification |
|
|
197 | (<http://perlmulticore.schmorp.de/>) for token generation of any length |
|
|
198 | and size. |
190 | |
199 | |
191 | =head1 BUGS |
200 | =head1 BUGS |
192 | |
201 | |
193 | * There is a y2k+100 problem, as I always assume the same as Time::Local. |
202 | * There is a y2k+100 problem, as I always assume the same as Time::Local. |
194 | This is a problem with the hashcash specification, which specifies |
203 | This is a problem with the hashcash specification, which specifies |
195 | years as 2 digits :( |
204 | years as 2 digits :( |
196 | |
205 | |
197 | =head1 AUTHOR |
206 | =head1 AUTHOR |
198 | |
207 | |
199 | Marc Lehmann <pcg@goof.com> |
208 | Marc Lehmann <schmorp@schmorp.de> |
200 | http://home.schmorp.de |
209 | http://home.schmorp.de |
201 | |
210 | |
202 | =cut |
211 | =cut |
203 | |
212 | |
204 | 1; |
213 | 1; |