File Coverage

blib/lib/Bitcoin/Crypto/Helpers.pm
Criterion Covered Total %
statement 82 94 87.2
branch 8 14 57.1
condition 3 9 33.3
subroutine 20 21 95.2
pod 0 7 0.0
total 113 145 77.9


line stmt bran cond sub pod time code
1             package Bitcoin::Crypto::Helpers;
2             $Bitcoin::Crypto::Helpers::VERSION = '1.008_01'; # TRIAL
3             $Bitcoin::Crypto::Helpers::VERSION = '1.00801';
4 19     19   221357 use v5.10;
  19         105  
5 19     19   121 use strict;
  19         45  
  19         431  
6 19     19   106 use warnings;
  19         58  
  19         589  
7 19     19   130 use Exporter qw(import);
  19         68  
  19         805  
8 19     19   9048 use Crypt::Digest::RIPEMD160 qw(ripemd160);
  19         55008  
  19         1304  
9 19     19   6956 use Crypt::Digest::SHA256 qw(sha256);
  19         9439  
  19         1116  
10 19     19   145 use List::Util qw(max);
  19         39  
  19         2095  
11 19     19   9930 use Crypt::PK::ECC;
  19         173928  
  19         1018  
12              
13 19     19   7516 use Bitcoin::Crypto::Config;
  19         57  
  19         636  
14 19     19   6119 use Bitcoin::Crypto::Exception;
  19         128  
  19         1939  
15              
16             BEGIN {
17 19     19   28707 require Math::BigInt;
18              
19             # Version 1.6003 of optional GMP is required for the from_bytes / to_bytes implementations
20 19 50       698226 if (eval { require Math::BigInt::GMP; Math::BigInt::GMP->VERSION('1.6003'); 1 }) {
  19         3828  
  0         0  
  0         0  
21 0         0 Math::BigInt->import(try => 'GMP,LTM');
22             }
23             else {
24 19         156 Math::BigInt->import(try => 'LTM');
25             }
26             }
27              
28             our @EXPORT_OK = qw(
29             new_bigint
30             pad_hex
31             ensure_length
32             verify_bytestring
33             hash160
34             hash256
35             add_ec_points
36             );
37              
38             sub new_bigint
39             {
40 907     907 0 20523 my ($bytes) = @_;
41 907         3944 return Math::BigInt->from_hex(unpack 'H*', $bytes);
42             }
43              
44             sub pad_hex
45             {
46 43     43 0 5470 my ($hex) = @_;
47 43         143 $hex =~ s/^0x//;
48 43         431 return '0' x (length($hex) % 2) . $hex;
49             }
50              
51             sub ensure_length
52             {
53 1246     1246 0 3242 my ($packed, $bytelen) = @_;
54 1246         2172 my $missing = $bytelen - length $packed;
55              
56 1246 100       2629 Bitcoin::Crypto::Exception->raise(
57             "packed string exceeds maximum number of bytes allowed ($bytelen)"
58             ) if $missing < 0;
59              
60 1245         4806 return pack("x$missing") . $packed;
61             }
62              
63             sub verify_bytestring
64             {
65 448     448 0 1677 my ($string) = @_;
66              
67 448 100 100     2129 Bitcoin::Crypto::Exception->raise(
68             'invalid input value, expected string'
69             ) if !defined $string || ref $string;
70              
71 446         3881 my @characters = split //, $string;
72              
73             Bitcoin::Crypto::Exception->raise(
74             'string contains characters with numeric values over 255 and cannot be used as a byte string'
75 446 50       1050 ) if (grep { ord($_) > 255 } @characters) > 0;
  20677         33337  
76             }
77              
78             sub hash160
79             {
80 341     341 0 742 my ($data) = @_;
81              
82 341         1033 return ripemd160(sha256($data));
83             }
84              
85             sub hash256
86             {
87 227     227 0 1108 my ($data) = @_;
88              
89 227         686 return sha256(sha256($data));
90             }
91              
92             # Self-contained implementation on elliptic curve points addition.
93             # This is only a partial implementation, but should be good enough for key
94             # derivation needs. Code borrowed from the archived Math::EllipticCurve::Prime
95             # module. Returns undef for infinity points, expects to get a valid uncompressed
96             # point data on input
97             sub add_ec_points
98             {
99 15     15 0 43 my ($point1, $point2) = @_;
100              
101 15         30 my $curve_size = Bitcoin::Crypto::Config::key_max_length;
102 15         59 my $curve_data = Crypt::PK::ECC->new->generate_key(Bitcoin::Crypto::Config::curve_name)->curve2hash;
103 15         46039 my $p = new_bigint(pack 'H*', $curve_data->{prime});
104 15         3393 my $a = new_bigint(pack 'H*', $curve_data->{A});
105              
106             my $add_points = sub {
107 15     15   62 my ($x1, $x2, $y1, $lambda) = @_;
108              
109 15         53 my $x = $lambda->copy->bmodpow(2, $p);
110 15         3196 $x->bsub($x1);
111 15         1301 $x->bsub($x2);
112 15         1267 $x->bmod($p);
113              
114 15         1182 my $y = $x1->copy->bsub($x);
115 15         1533 $y->bmul($lambda);
116 15         764 $y->bsub($y1);
117 15         1196 $y->bmod($p);
118              
119 15         1233 return {x => $x, y => $y};
120 15         2249 };
121              
122             my $double = sub {
123 0     0   0 my ($x, $y) = @_;
124 0         0 my $lambda = $x->copy->bmodpow(2, $p);
125 0         0 $lambda->bmul(3);
126 0         0 $lambda->badd($a);
127 0         0 my $bottom = $y->copy->bmul(2)->bmodinv($p);
128 0         0 $lambda->bmul($bottom)->bmod($p);
129              
130 0         0 return $add_points->($x, $x, $y, $lambda);
131 15         65 };
132              
133 15         62 my $format = "(a$curve_size)*";
134 15         89 my ($px1, $py1) = map { new_bigint($_) } unpack $format, substr $point1, 1;
  30         3157  
135 15         3107 my ($px2, $py2) = map { new_bigint($_) } unpack $format, substr $point2, 1;
  30         2991  
136              
137             my $ret = sub {
138 15 50 0 15   60 if ($px1->bcmp($px2)) {
    0 0        
139 15         362 my $lambda = $py2->copy->bsub($py1);
140 15         1955 my $bottom = $px2->copy->bsub($px1)->bmodinv($p);
141 15         4687 $lambda->bmul($bottom)->bmod($p);
142              
143 15         2019 return $add_points->($px1, $px2, $py1, $lambda);
144             }
145             elsif ($py1->is_zero || $py2->is_zero || $py1->bcmp($py2)) {
146 0         0 return undef;
147             }
148             else {
149 0         0 return $double->($px1, $py1);
150             }
151             }
152 15         3159 ->();
153              
154 15         148 my $exp_x = $ret->{x}->to_bytes;
155 15         488 my $exp_y = $ret->{y}->to_bytes;
156              
157 15 50       390 return defined $ret
158             ? "\x04" .
159             ensure_length($exp_x, $curve_size) .
160             ensure_length($exp_y, $curve_size)
161             : undef;
162             }
163              
164             1;
165              
166             # Internal use only
167