line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Bitcoin::Crypto::Util; |
2
|
|
|
|
|
|
|
$Bitcoin::Crypto::Util::VERSION = '1.008_01'; # TRIAL |
3
|
|
|
|
|
|
|
$Bitcoin::Crypto::Util::VERSION = '1.00801'; |
4
|
11
|
|
|
11
|
|
1526
|
use v5.10; |
|
11
|
|
|
|
|
41
|
|
5
|
11
|
|
|
11
|
|
63
|
use strict; |
|
11
|
|
|
|
|
29
|
|
|
11
|
|
|
|
|
256
|
|
6
|
11
|
|
|
11
|
|
59
|
use warnings; |
|
11
|
|
|
|
|
22
|
|
|
11
|
|
|
|
|
378
|
|
7
|
11
|
|
|
11
|
|
81
|
use Exporter qw(import); |
|
11
|
|
|
|
|
32
|
|
|
11
|
|
|
|
|
478
|
|
8
|
11
|
|
|
11
|
|
84
|
use List::Util qw(first); |
|
11
|
|
|
|
|
22
|
|
|
11
|
|
|
|
|
849
|
|
9
|
11
|
|
|
11
|
|
75
|
use Crypt::PK::ECC; |
|
11
|
|
|
|
|
27
|
|
|
11
|
|
|
|
|
516
|
|
10
|
11
|
|
|
11
|
|
8801
|
use Unicode::Normalize; |
|
11
|
|
|
|
|
30731
|
|
|
11
|
|
|
|
|
872
|
|
11
|
11
|
|
|
11
|
|
5157
|
use Crypt::KeyDerivation qw(pbkdf2); |
|
11
|
|
|
|
|
3846
|
|
|
11
|
|
|
|
|
706
|
|
12
|
11
|
|
|
11
|
|
5744
|
use Encode qw(encode); |
|
11
|
|
|
|
|
108000
|
|
|
11
|
|
|
|
|
781
|
|
13
|
|
|
|
|
|
|
|
14
|
11
|
|
|
11
|
|
94
|
use Bitcoin::Crypto::Config; |
|
11
|
|
|
|
|
31
|
|
|
11
|
|
|
|
|
295
|
|
15
|
11
|
|
|
11
|
|
1004
|
use Bitcoin::Crypto::Base58 qw(decode_base58check); |
|
11
|
|
|
|
|
31
|
|
|
11
|
|
|
|
|
7174
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
our @EXPORT_OK = qw( |
18
|
|
|
|
|
|
|
validate_wif |
19
|
|
|
|
|
|
|
get_key_type |
20
|
|
|
|
|
|
|
mnemonic_to_seed |
21
|
|
|
|
|
|
|
get_path_info |
22
|
|
|
|
|
|
|
); |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
our %EXPORT_TAGS = (all => [@EXPORT_OK]); |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
sub validate_wif |
27
|
|
|
|
|
|
|
{ |
28
|
11
|
|
|
11
|
1
|
1615
|
my ($wif) = @_; |
29
|
11
|
|
|
|
|
48
|
my $byte_wif = decode_base58check($wif); |
30
|
10
|
|
|
|
|
35
|
my $last_byte = substr $byte_wif, -1; |
31
|
10
|
100
|
|
|
|
39
|
if (length $byte_wif == Bitcoin::Crypto::Config::key_max_length + 2) { |
32
|
4
|
|
|
|
|
21
|
return $last_byte eq Bitcoin::Crypto::Config::wif_compressed_byte; |
33
|
|
|
|
|
|
|
} |
34
|
|
|
|
|
|
|
else { |
35
|
6
|
|
|
|
|
36
|
return length $byte_wif == Bitcoin::Crypto::Config::key_max_length + 1; |
36
|
|
|
|
|
|
|
} |
37
|
|
|
|
|
|
|
} |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
sub get_key_type |
40
|
|
|
|
|
|
|
{ |
41
|
502
|
|
|
502
|
1
|
961
|
my ($entropy) = @_; |
42
|
|
|
|
|
|
|
|
43
|
502
|
|
|
|
|
843
|
my $curve_size = Bitcoin::Crypto::Config::key_max_length; |
44
|
502
|
|
|
|
|
1004
|
my $octet = substr $entropy, 0, 1; |
45
|
|
|
|
|
|
|
|
46
|
502
|
|
100
|
|
|
2501
|
my $has_unc_oc = $octet eq "\x04" || $octet eq "\x06" || $octet eq "\x07"; |
47
|
502
|
|
100
|
|
|
1666
|
my $is_unc = $has_unc_oc && length $entropy == 2 * $curve_size + 1; |
48
|
|
|
|
|
|
|
|
49
|
502
|
|
100
|
|
|
1617
|
my $has_com_oc = $octet eq "\x02" || $octet eq "\x03"; |
50
|
502
|
|
66
|
|
|
1100
|
my $is_com = $has_com_oc && length $entropy == $curve_size + 1; |
51
|
|
|
|
|
|
|
|
52
|
502
|
100
|
100
|
|
|
1806
|
return 0 |
53
|
|
|
|
|
|
|
if $is_com || $is_unc; |
54
|
355
|
100
|
|
|
|
1164
|
return 1 |
55
|
|
|
|
|
|
|
if length $entropy <= $curve_size; |
56
|
1
|
|
|
|
|
5
|
return; |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
sub mnemonic_to_seed |
60
|
|
|
|
|
|
|
{ |
61
|
31
|
|
|
31
|
1
|
190
|
my ($mnemonic, $password) = @_; |
62
|
|
|
|
|
|
|
|
63
|
31
|
|
|
|
|
555
|
$mnemonic = encode('UTF-8', NFKD($mnemonic)); |
64
|
31
|
|
100
|
|
|
3405
|
$password = encode('UTF-8', NFKD('mnemonic' . ($password // ''))); |
65
|
|
|
|
|
|
|
|
66
|
31
|
|
|
|
|
314404
|
return pbkdf2($mnemonic, $password, 2048, 'SHA512', 64); |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
sub get_path_info |
70
|
|
|
|
|
|
|
{ |
71
|
97
|
|
|
97
|
1
|
4184
|
my ($path) = @_; |
72
|
97
|
100
|
|
|
|
878
|
if ($path =~ m#^([mM])((?:/\d+'?)*)$#) { |
73
|
93
|
|
|
|
|
203
|
my %info; |
74
|
93
|
|
|
|
|
422
|
$info{private} = $1 eq 'm'; |
75
|
93
|
100
|
66
|
|
|
650
|
if (defined $2 && length $2 > 0) { |
76
|
|
|
|
|
|
|
$info{path} = |
77
|
83
|
|
|
|
|
464
|
[map { s#(\d+)'#$1 + Bitcoin::Crypto::Config::max_child_keys#e; $_ } split /\//, substr $2, 1]; |
|
293
|
|
|
|
|
831
|
|
|
106
|
|
|
|
|
381
|
|
|
293
|
|
|
|
|
819
|
|
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
else { |
80
|
10
|
|
|
|
|
27
|
$info{path} = []; |
81
|
|
|
|
|
|
|
} |
82
|
292
|
|
|
292
|
|
657
|
return undef if first { $_ >= Bitcoin::Crypto::Config::max_child_keys * 2 } |
83
|
93
|
100
|
|
|
|
555
|
@{$info{path}}; |
|
93
|
|
|
|
|
488
|
|
84
|
91
|
|
|
|
|
554
|
return \%info; |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
else { |
87
|
4
|
|
|
|
|
22
|
return undef; |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
} |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
1; |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
__END__ |
94
|
|
|
|
|
|
|
=head1 NAME |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
Bitcoin::Crypto::Util - Basic utilities for working with bitcoin |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
=head1 SYNOPSIS |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
use Bitcoin::Crypto::Util qw( |
101
|
|
|
|
|
|
|
validate_wif |
102
|
|
|
|
|
|
|
get_key_type |
103
|
|
|
|
|
|
|
get_path_info |
104
|
|
|
|
|
|
|
); |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
=head1 DESCRIPTION |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
These are basic utilities for working with bitcoin, used by other packages. |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
=head1 FUNCTIONS |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
=head2 validate_wif |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
$bool = validate_wif($str); |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
Ensures Base58 encoded string looks like encoded private key in WIF format. |
117
|
|
|
|
|
|
|
Throws an exception if C<$str> is not valid base58. |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
=head2 get_key_type |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
$is_private = get_key_type($bytestr); |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
Checks if the C<$bytestr> looks like a valid ASN X9.62 format (compressed / uncompressed / hybrid public key or private key entropy up to curve size bits). |
124
|
|
|
|
|
|
|
Returns boolean which can be used to determine if the key is private. |
125
|
|
|
|
|
|
|
Returns undef if C<$bytestr> does not look like a valid key entropy. |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
=head2 mnemonic_to_seed |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
$seed = mnemonic_to_seed($mnemonic, $password); |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
Transforms the given BIP39 C<$mnemonic> and C<$password> into a valid BIP32 C<$seed>, which can be fed into L<Bitcoin::Crypto::Key::ExtPrivate/from_seed>. |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
C<$seed> is a 512 bit bytestring (64 characters). C<$mnemonic> should be a BIP39 mnemonic, but will not be checked against a dictionary. |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
This function is only useful if you need a seed instead of mnemonic (for example, you use a wallet implementation which does not implement BIP39). If you only want to create a private key from mnemonic, you should consider using L<Bitcoin::Crypto::Key::ExtPrivate/from_mnemonic> instead. |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
B<Important note about unicode:> this function only accepts UTF8-decoded strings (both C<$mnemonic> and C<$password>), but can't detect whether it got it or not. This will only become a problem if you use non-ascii mnemonic and/or password. If there's a possibility of non-ascii, always use utf8 and set binmodes to get decoded (wide) characters to avoid problems recovering your wallet. |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=head2 get_path_info |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
$path_data = get_path_info($path); |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
Tries to get derivation path data from C<$path>. |
144
|
|
|
|
|
|
|
Returns undef if C<$path> is not a valid path. |
145
|
|
|
|
|
|
|
Otherwise returns the structure: |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
{ |
148
|
|
|
|
|
|
|
private => bool, # is path derivation private (lowercase m) |
149
|
|
|
|
|
|
|
path => [ |
150
|
|
|
|
|
|
|
# derivation path with 2^31 added to every hardened child number |
151
|
|
|
|
|
|
|
int, int, .. |
152
|
|
|
|
|
|
|
], |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
Example: |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
my $path = "m/1/3'"; |
158
|
|
|
|
|
|
|
my $path_data = get_path_info($path); |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
=head1 SEE ALSO |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=over 2 |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
=item L<Bitcoin::Crypto::Key::ExtPrivate> |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=back |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=cut |
169
|
|
|
|
|
|
|
|