File Coverage

blib/lib/Bitcoin/Crypto/Util.pm
Criterion Covered Total %
statement 52 52 100.0
branch 4 4 100.0
condition n/a
subroutine 16 16 100.0
pod 1 1 100.0
total 73 73 100.0


line stmt bran cond sub pod time code
1             package Bitcoin::Crypto::Util;
2             $Bitcoin::Crypto::Util::VERSION = '2.000_01'; # TRIAL
3             $Bitcoin::Crypto::Util::VERSION = '2.00001';
4 31     31   61906 use v5.10;
  31         124  
5 31     31   174 use strict;
  31         71  
  31         684  
6 31     31   229 use warnings;
  31         84  
  31         1044  
7 31     31   173 use Exporter qw(import);
  31         73  
  31         1063  
8 31     31   9319 use Crypt::PK::ECC;
  31         186866  
  31         1452  
9 31     31   17706 use Unicode::Normalize;
  31         68444  
  31         2142  
10 31     31   13520 use Crypt::KeyDerivation qw(pbkdf2);
  31         9801  
  31         1879  
11 31     31   15822 use Encode qw(encode);
  31         294723  
  31         2219  
12 31     31   12928 use Crypt::Digest::RIPEMD160 qw(ripemd160);
  31         19533  
  31         1894  
13 31     31   7116 use Crypt::Digest::SHA256 qw(sha256);
  31         10199  
  31         1652  
14 31     31   12639 use Bitcoin::BIP39 qw(gen_bip39_mnemonic entropy_to_bip39_mnemonic);
  31         39113  
  31         2058  
15 31     31   6735 use Type::Params -sigs;
  31         1391617  
  31         324  
16              
17 31     31   20881 use Bitcoin::Crypto::Constants;
  31         126  
  31         1014  
18 31     31   7276 use Bitcoin::Crypto::Types qw(Str ByteStr FormatStr InstanceOf Maybe PositiveInt Tuple);
  31         254  
  31         306  
19 31     31   140534 use Bitcoin::Crypto::Exception;
  31         80  
  31         35557  
20              
21             our @EXPORT_OK = qw(
22             validate_wif
23             validate_segwit
24             get_key_type
25             get_public_key_compressed
26             generate_mnemonic
27             mnemonic_from_entropy
28             mnemonic_to_seed
29             get_path_info
30             to_format
31             hash160
32             hash256
33             );
34              
35             our %EXPORT_TAGS = (all => [@EXPORT_OK]);
36              
37             signature_for validate_wif => (
38             positional => [Str],
39             );
40              
41             sub validate_wif
42             {
43             my ($wif) = @_;
44              
45             require Bitcoin::Crypto::Base58;
46             my $byte_wif = Bitcoin::Crypto::Base58::decode_base58check($wif);
47              
48             my $last_byte = substr $byte_wif, -1;
49             if (length $byte_wif == Bitcoin::Crypto::Constants::key_max_length + 2) {
50             return $last_byte eq Bitcoin::Crypto::Constants::wif_compressed_byte;
51             }
52             else {
53             return length $byte_wif == Bitcoin::Crypto::Constants::key_max_length + 1;
54             }
55             }
56              
57             signature_for validate_segwit => (
58             positional => [ByteStr],
59             );
60              
61             sub validate_segwit
62             {
63             my ($program) = @_;
64              
65             my $version = unpack 'C', $program;
66             Bitcoin::Crypto::Exception::SegwitProgram->raise(
67             'incorrect witness program version ' . ($version // '[null]')
68             ) unless defined $version && $version >= 0 && $version <= Bitcoin::Crypto::Constants::max_witness_version;
69              
70             $program = substr $program, 1;
71              
72             # common validator
73             Bitcoin::Crypto::Exception::SegwitProgram->raise(
74             'incorrect witness program length'
75             ) unless length $program >= 2 && length $program <= 40;
76              
77             if ($version == 0) {
78              
79             # SegWit validator
80             Bitcoin::Crypto::Exception::SegwitProgram->raise(
81             'incorrect witness program length (segwit)'
82             ) unless length $program == 20 || length $program == 32;
83             }
84             elsif ($version == 1) {
85              
86             # Taproot validator
87              
88             # taproot outputs are 32 bytes, but other lengths "remain unencumbered"
89             # do not throw this exception to make bip350 test suite pass (10-Bech32.t)
90              
91             # Bitcoin::Crypto::Exception::SegwitProgram->raise(
92             # 'incorrect witness program length (taproot)'
93             # ) unless length $program == 32;
94             }
95              
96             return $version;
97             }
98              
99             signature_for get_key_type => (
100             positional => [ByteStr],
101             );
102              
103             sub get_key_type
104             {
105             my ($entropy) = @_;
106              
107             return 0 if defined get_public_key_compressed($entropy);
108             return 1
109             if length $entropy <= Bitcoin::Crypto::Constants::key_max_length;
110             return undef;
111             }
112              
113             signature_for get_public_key_compressed => (
114             positional => [ByteStr],
115             );
116              
117             sub get_public_key_compressed
118             {
119             my ($entropy) = @_;
120              
121             my $curve_size = Bitcoin::Crypto::Constants::key_max_length;
122             my $octet = substr $entropy, 0, 1;
123              
124             my $has_unc_oc = $octet eq "\x04" || $octet eq "\x06" || $octet eq "\x07";
125             my $is_unc = $has_unc_oc && length $entropy == 2 * $curve_size + 1;
126              
127             my $has_com_oc = $octet eq "\x02" || $octet eq "\x03";
128             my $is_com = $has_com_oc && length $entropy == $curve_size + 1;
129              
130             return 1 if $is_com;
131             return 0 if $is_unc;
132             return undef;
133             }
134              
135             signature_for mnemonic_to_seed => (
136             positional => [Str, Maybe [Str], {optional => 1}],
137             );
138              
139             sub mnemonic_to_seed
140             {
141             my ($mnemonic, $password) = @_;
142              
143             $mnemonic = encode('UTF-8', NFKD($mnemonic));
144             $password = encode('UTF-8', NFKD('mnemonic' . ($password // '')));
145              
146             return pbkdf2($mnemonic, $password, 2048, 'SHA512', 64);
147             }
148              
149             signature_for generate_mnemonic => (
150             positional => [PositiveInt, {default => 128}, Str, {default => 'en'}],
151             );
152              
153             sub generate_mnemonic
154             {
155             my ($len, $lang) = @_;
156             my ($min_len, $len_div, $max_len) = (128, 32, 256);
157              
158             # bip39 specification values
159             Bitcoin::Crypto::Exception::MnemonicGenerate->raise(
160             "required entropy of between $min_len and $max_len bits, divisible by $len_div"
161             ) if $len < $min_len || $len > $max_len || $len % $len_div != 0;
162              
163             return Bitcoin::Crypto::Exception::MnemonicGenerate->trap_into(
164             sub {
165             my $ret = gen_bip39_mnemonic(bits => $len, language => $lang);
166             $ret->{mnemonic};
167             }
168             );
169             }
170              
171             signature_for mnemonic_from_entropy => (
172             positional => [ByteStr, Str, {default => 'en'}],
173             );
174              
175             sub mnemonic_from_entropy
176             {
177             my ($entropy, $lang) = @_;
178              
179             return Bitcoin::Crypto::Exception::MnemonicGenerate->trap_into(
180             sub {
181             entropy_to_bip39_mnemonic(
182             entropy => $entropy,
183             language => $lang
184             );
185             }
186             );
187             }
188              
189             signature_for get_path_info => (
190             positional => [Str | InstanceOf ['Bitcoin::Crypto::BIP44']],
191             );
192              
193             sub get_path_info
194             {
195             my ($path) = @_;
196             if ($path =~ m{\A ([mM]) ((?: / \d+ '?)*) \z}x) {
197             my ($head, $rest) = ($1, $2);
198             my @path;
199              
200             if (defined $rest && length $rest > 0) {
201              
202             # remove leading slash (after $head)
203             substr $rest, 0, 1, '';
204              
205             for my $part (split '/', $rest) {
206             my $is_hardened = $part =~ tr/'//d;
207              
208             return undef if $part >= Bitcoin::Crypto::Constants::max_child_keys;
209              
210             $part += Bitcoin::Crypto::Constants::max_child_keys if $is_hardened;
211             push @path, $part;
212             }
213             }
214              
215             return {
216             private => $head eq 'm',
217             path => \@path,
218             };
219             }
220              
221             return undef;
222             }
223              
224             # use signature, not signature_for, because of the prototype
225             sub to_format ($)
226             {
227 170     170 1 9669 state $sig = signature(positional => [Tuple [FormatStr, ByteStr]]);
228 170         194977 my ($format, $data) = @{($sig->(@_))[0]};
  170         626  
229              
230 170 100       42862 if ($format eq 'hex') {
    100          
231 90         352 $data = unpack 'H*', $data;
232             }
233             elsif ($format eq 'base58') {
234 79         500 require Bitcoin::Crypto::Base58;
235 79         287 $data = Bitcoin::Crypto::Base58::encode_base58check($data);
236             }
237              
238 170         18863 return $data;
239             }
240              
241             signature_for hash160 => (
242             positional => [ByteStr],
243             );
244              
245             sub hash160
246             {
247             my ($data) = @_;
248              
249             return ripemd160(sha256($data));
250             }
251              
252             signature_for hash256 => (
253             positional => [ByteStr],
254             );
255              
256             sub hash256
257             {
258             my ($data) = @_;
259              
260             return sha256(sha256($data));
261             }
262              
263             1;
264              
265             __END__
266             =head1 NAME
267              
268             Bitcoin::Crypto::Util - Utilities for working with Bitcoin
269              
270             =head1 SYNOPSIS
271              
272             use Bitcoin::Crypto::Util qw(
273             validate_wif
274             validate_segwit
275             get_key_type
276             get_public_key_compressed
277             generate_mnemonic
278             mnemonic_from_entropy
279             mnemonic_to_seed
280             get_path_info
281             to_format
282             hash160
283             hash256
284             );
285              
286             =head1 DESCRIPTION
287              
288             These are basic utilities for working with Bitcoin. They do not fit well as a
289             part of other, more specialized packages.
290              
291             =head1 FUNCTIONS
292              
293             =head2 validate_wif
294              
295             $bool = validate_wif($str);
296              
297             Ensures Base58 encoded string looks like encoded private key in WIF format.
298             Throws an exception if C<$str> is not valid base58.
299              
300             =head2 validate_segwit
301              
302             $segwit_version = validate_segwit($program)
303              
304             Performs a segwit program validation on C<$program>, which is expected to be a
305             byte string in which the first byte is a segwit version.
306              
307             The function returns the detected segwit program version. Note that it does not
308             perform any more checks than ensuring the byte string is in correct format.
309              
310             The current implementation is in line with validations for segwit versions C<0>
311             and C<1>. Future segwit version addresses will work just fine, but no special
312             validation will be performed until implemented.
313              
314             Raises an exception (C<Bitcoin::Crypto::Exception::SegwitProgram>) on error.
315             Returns the detected segwit program version.
316              
317             =head2 get_key_type
318              
319             $is_private = get_key_type($bytestr);
320              
321             Checks if the C<$bytestr> looks like a valid ASN X9.62 format (compressed /
322             uncompressed / hybrid public key or private key entropy up to curve size bits).
323              
324             Returns boolean which states whether the key is private. Returns
325             undef if C<$bytestr> does not look like a valid key entropy.
326              
327             =head2 get_public_key_compressed
328              
329             $is_compressed = get_public_key_compressed($bytestr);
330              
331             Checks if the C<$bytestr> looks like a valid ASN X9.62 format (compressed /
332             uncompressed / hybrid public key).
333              
334             Returns boolean which states whether the key is compressed. Returns
335             undef if C<$bytestr> does not look like a valid public key.
336              
337             =head2 generate_mnemonic
338              
339             $mnemonic = generate_mnemonic($len = 128, $lang = 'en')
340              
341             Generates a new mnemonic code using L<Bytes::Random::Secure>. Default entropy
342             is C<128> bits. This can be increased up to C<256> bits (increasing by C<32>
343             bits each step) with C<$len> argument.
344              
345             Other languages than english require installation of additional modules
346             language-specific for L<Bitcoin::BIP39>.
347              
348             Returns newly generated BIP39 mnemonic string. Dies when C<$len> is invalid
349             (less than C<128>, more than C<256> or not divisible by C<32>).
350              
351             In some environments a problem may be encountered that causes the secure random
352             bytes generator to block the program execution (See
353             L<Bytes::Random::Secure/"BLOCKING ENTROPY SOURCE">). In this case you can use
354             L</mnemonic_from_entropy> and pass in entropy generated by
355             L<Bytes::Random::Secure> in non-blocking mode (via the OO interface).
356              
357             =head2 mnemonic_from_entropy
358              
359             $mnemonic = mnemonic_from_entropy($bytes, $lang = 'en')
360              
361             Generates a new mnemonic code from custom entropy given in C<$bytes> (a
362             bytestring). This entropy should be of the same bit size as in
363             L</"generate_mnemonic">. Returns newly generated BIP39 mnemonic string.
364              
365             This can be useful to avoid relying on the underlying PRNG implementation used
366             by L<Bitcoin::BIP39>.
367              
368             Another use would be implementing one's own entropy source that can be truly
369             random, not just cryptographically-secure. A popular example would be capturing
370             user's mouse movements.
371              
372             Be aware that the method you use to generate a mnemonic will be a very
373             important factor in your key's security. If possible, use real sources of
374             randomness (not pseudo-random) or a cryptographically secure pseduo-random
375             number generator like the one used by L<Bytes::Random::Secure>.
376              
377             =head2 mnemonic_to_seed
378              
379             $seed = mnemonic_to_seed($mnemonic, $password);
380              
381             Transforms the given BIP39 C<$mnemonic> and C<$password> into a valid BIP32
382             C<$seed>, which can be fed into L<Bitcoin::Crypto::Key::ExtPrivate/from_seed>.
383              
384             C<$seed> is a C<512> bit bytestring (64 characters). C<$mnemonic> should be a
385             BIP39 mnemonic, but will not be checked against a dictionary.
386              
387             This function is only useful if you need a seed instead of mnemonic (for
388             example, you use a wallet implementation which does not implement BIP39). If
389             you only want to create a private key from mnemonic, you should consider using
390             L<Bitcoin::Crypto::Key::ExtPrivate/from_mnemonic> instead.
391              
392             B<Important note about unicode:> this function only accepts UTF8-decoded
393             strings (both C<$mnemonic> and C<$password>), but can't detect whether it got
394             it or not. This will only become a problem if you use non-ascii mnemonic and/or
395             password. If there's a possibility of non-ascii, always use utf8 and set
396             binmodes to get decoded (wide) characters to avoid problems recovering your
397             wallet.
398              
399             =head2 get_path_info
400              
401             $path_data = get_path_info($path);
402              
403             Tries to get derivation path data from C<$path> (like C<"m/1/3'">). Returns
404             undef if C<$path> is not a valid path, otherwise returns the structure:
405              
406             {
407             private => bool, # is path derivation private (lowercase m)
408             path => [
409             # derivation path with 2^31 added to every hardened child number
410             int, int, ..
411             ],
412             }
413              
414             =head2 to_format
415              
416             $encoded = to_format [$format => $bytes];
417              
418             Unpacks bytestring C<$bytes> into the given C<$format>. Use this to avoid
419             manual unpacking.
420              
421             Supported C<$format> values are:
422              
423             C<bytes>, does nothing
424              
425             C<hex>, encodes as a hexadecimal string (no C<0x> prefix)
426              
427             C<base58>, uses base58 and includes the checksum (base58check)
428              
429             =head2 hash160
430              
431             my $hash = hash160($data);
432              
433             This is hash160 used by Bitcoin (C<RIPEMD160> of C<SHA256>)
434              
435             =head2 hash256
436              
437             my $hash = hash256($data);
438              
439             This is hash256 used by Bitcoin (C<SHA256> of C<SHA256>)
440              
441             =head1 SEE ALSO
442              
443             L<https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki>
444              
445             L<https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki>
446