package Hashcash; # use strict; use vars qw(@ISA @EXPORT @EXPORT_OK $VERSION); use Exporter; $VESRION = 1.00; @ISA = qw(Exporter); @EXPORT = qw(charge pay check); use Digest::SHA1 qw(sha1); use constant RANDOMDEV => "/dev/urandom"; use constant PREFIXLENGTH => 20; # pay brute-forces SHA1 to find an input that hashes # to something partially matching a given string. Idea by # Adam Back. # This function expects an arbitrary prefix and a bit-string of # significantly less than 160 bits. Both conditions (being a bit-string # and much less than 160 bits in length) should be checked by the caller. # sub pay { my ( $prefix, $bits) = @_; my ($h, $i) = ("", 0); while ($h !~ m/$bits$/){ $h = unpack "b*", sha1("$prefix" . ++$i); } return $i; } # charge takes a number n and returns # a pair of strings, a prefix (length defined in # PREFIXLENGTH) and a challenge of n bits. # sub charge { my ( $n) = shift; my ($prefix, $chall); my $pat = "H" . PREFIXLENGTH; my $rpat = "b" . $n; open RAND, RANDOMDEV or die "Couldn't open random device"; $chall = unpack $rpat, ; if (length $chall < $n) { die "Mist!"; } $prefix = unpack $pat , ; close RAND; return ($prefix, $chall); } # check takes three parameters, a (prefix,challenge) pair # and one response to the challenge. It checks if # prefix . response hashes to something matching challenge. # sub check { my ( $prefix, $chall, $claim) = @_; my $cl = length($chall); my $temp = unpack "b*", sha1($prefix . $claim); return ($temp =~ m/$chall$/); } 1;