File Coverage

blib/lib/Bitcoin/Crypto/Key/Private.pm
Criterion Covered Total %
statement 73 74 98.6
branch 9 14 64.2
condition 1 3 33.3
subroutine 20 21 95.2
pod 3 3 100.0
total 106 115 92.1


line stmt bran cond sub pod time code
1             package Bitcoin::Crypto::Key::Private;
2             $Bitcoin::Crypto::Key::Private::VERSION = '1.008_01'; # TRIAL
3             $Bitcoin::Crypto::Key::Private::VERSION = '1.00801';
4 9     9   12180 use v5.10;
  9         44  
5 9     9   58 use strict;
  9         23  
  9         192  
6 9     9   58 use warnings;
  9         21  
  9         274  
7 9     9   1125 use Moo;
  9         14816  
  9         70  
8 9     9   7638 use Crypt::PK::ECC;
  9         32295  
  9         549  
9 9     9   1660 use Bitcoin::BIP39 qw(bip39_mnemonic_to_entropy entropy_to_bip39_mnemonic);
  9         4782  
  9         537  
10 9     9   66 use List::Util qw(first);
  9         23  
  9         636  
11              
12 9     9   4051 use Bitcoin::Crypto::Key::Public;
  9         33  
  9         377  
13 9     9   70 use Bitcoin::Crypto::Base58 qw(encode_base58check decode_base58check);
  9         25  
  9         577  
14 9     9   74 use Bitcoin::Crypto::Config;
  9         22  
  9         221  
15 9     9   55 use Bitcoin::Crypto::Network;
  9         22  
  9         293  
16 9     9   68 use Bitcoin::Crypto::Util qw(validate_wif);
  9         23  
  9         415  
17 9     9   87 use Bitcoin::Crypto::Helpers qw(ensure_length);
  9         19  
  9         395  
18 9     9   68 use Bitcoin::Crypto::Exception;
  9         43  
  9         293  
19              
20 9     9   66 use namespace::clean;
  9         20  
  9         116  
21              
22             with qw(Bitcoin::Crypto::Role::BasicKey);
23              
24 83     83   470 sub _is_private { 1 }
25              
26             sub to_wif
27             {
28 16     16 1 106 my ($self) = @_;
29 16         68 my $bytes = $self->to_bytes();
30              
31             # wif network - 1B
32 16         104 my $wifdata = $self->network->wif_byte;
33              
34             # key entropy - 32B
35 16         68 $wifdata .= ensure_length $bytes, Bitcoin::Crypto::Config::key_max_length;
36              
37             # additional byte for compressed key - 1B
38 16 100       109 $wifdata .= Bitcoin::Crypto::Config::wif_compressed_byte if $self->compressed;
39              
40 16         83 return encode_base58check($wifdata);
41             }
42              
43             sub from_wif
44             {
45 8     8 1 1360 my ($class, $wif, $network) = @_;
46              
47 8 50       43 Bitcoin::Crypto::Exception::KeyCreate->raise(
48             'base58 string is not valid WIF'
49             ) unless validate_wif($wif);
50              
51 8         30 my $decoded = decode_base58check($wif);
52 8         24 my $private = substr $decoded, 1;
53              
54 8         20 my $compressed = 0;
55 8 100       30 if (length($private) > Bitcoin::Crypto::Config::key_max_length) {
56 4         9 chop $private;
57 4         9 $compressed = 1;
58             }
59              
60 8         17 my $wif_network_byte = substr $decoded, 0, 1;
61             my @found_networks =
62 8     32   70 Bitcoin::Crypto::Network->find(sub { shift->wif_byte eq $wif_network_byte });
  32         130  
63 0     0   0 @found_networks = first { $_ eq $network }
64             @found_networks
65 8 50       46 if defined $network;
66              
67 8 50       26 Bitcoin::Crypto::Exception::KeyCreate->raise(
68             'found multiple networks possible for given WIF'
69             ) if @found_networks > 1;
70              
71 8 50 33     27 Bitcoin::Crypto::Exception::KeyCreate->raise(
72             "network name $network cannot be used for given WIF"
73             ) if @found_networks == 0 && defined $network;
74              
75 8 50       24 Bitcoin::Crypto::Exception::NetworkConfig->raise(
76             "couldn't find network for WIF byte $wif_network_byte"
77             ) if @found_networks == 0;
78              
79 8         43 my $instance = $class->from_bytes($private);
80 8         48 $instance->set_compressed($compressed);
81 8         40 $instance->set_network(@found_networks);
82 8         49 return $instance;
83             }
84              
85             sub get_public_key
86             {
87 37     37 1 18969 my ($self) = @_;
88              
89 37         151 my $public = Bitcoin::Crypto::Key::Public->new(
90             key_instance => $self->raw_key('public'),
91             compressed => $self->compressed,
92             network => $self->network,
93             purpose => $self->purpose,
94             );
95              
96 37         215 return $public;
97             }
98              
99             1;
100              
101             __END__
102             =head1 NAME
103              
104             Bitcoin::Crypto::Key::Private - Bitcoin private keys
105              
106             =head1 SYNOPSIS
107              
108             use Bitcoin::Crypto::Key::Private;
109              
110             # get Bitcoin::Crypto::Key::Public instance from private key
111              
112             my $pub = $priv->get_public_key();
113              
114             # create signature using private key (sha256 of string byte representation)
115              
116             my $sig = $priv->sign_message('Hello world');
117              
118             # signature is returned as byte string
119             # use unpack to get the representation you need
120              
121             my $sig_hex = unpack 'H*', $sig;
122              
123             # signature verification
124              
125             $priv->verify_message('Hello world', $sig);
126              
127             =head1 DESCRIPTION
128              
129             This class allows you to create a private key instance.
130              
131             You can use a private key to:
132              
133             =over 2
134              
135             =item * generate public keys
136              
137             =item * sign and verify messages
138              
139             =back
140              
141             Please note that any keys generated are by default compressed.
142              
143             see L<Bitcoin::Crypto::Network> if you want to work with other networks than Bitcoin Mainnet.
144              
145             =head1 METHODS
146              
147             =head2 new
148              
149             Constructor is reserved for internal and advanced use only. Use L</from_bytes>,
150             L</from_hex> and L</from_wif> instead.
151              
152             =head2 from_bytes
153              
154             $key_object = $class->from_bytes($data)
155              
156             Use this method to create a PrivateKey instance from a byte string.
157             Data C<$data> will be used as a private key entropy.
158              
159             Returns class instance.
160              
161             =head2 to_bytes
162              
163             $bytestring = $object->to_bytes()
164              
165             Does the opposite of from_bytes on a target object
166              
167             =head2 from_hex
168              
169             $key_object = $class->from_hex($hex)
170              
171             Use this method to create a PrivateKey instance from a hexadecimal number.
172             Number C<$hex> will be used as a private key entropy.
173              
174             Returns class instance.
175              
176             =head2 to_hex
177              
178             $hex_string = $object->to_hex()
179              
180             Does the opposite of from_hex on a target object
181              
182             =head2 from_wif
183              
184             $key_object = $class->from_wif($str, $network = undef)
185              
186             Creates a new private key from Wallet Import Format string.
187              
188             Takes an additional optional argument, which is network name. It may be useful if you use many networks and some have the same WIF byte.
189              
190             This method will change compression and network states of the created private key, as this data is included in WIF format.
191              
192             Returns class instance.
193              
194             =head2 to_wif
195              
196             $wif_string = $object->to_wif()
197              
198             Does the opposite of from_wif on a target object
199              
200             =head2 set_compressed
201              
202             $key_object = $object->set_compressed($val)
203              
204             Change key's compression state to C<$val> (1/0). This will change the WIF generated by
205             toWif() method and also enable creation of uncompressed public keys.
206             If C<$val> is omitted it is set to 1.
207              
208             Returns current key instance.
209              
210             =head2 set_network
211              
212             $key_object = $object->set_network($val)
213              
214             Change key's network state to C<$val>. It can be either network name present in Bitcoin::Crypto::Network package or an instance of this class.
215              
216             Returns current key instance.
217              
218             =head2 get_public_key
219              
220             $public_key_object = $object->get_public_key()
221              
222             Returns instance of L<Bitcoin::Crypto::Key::Public> generated from the private key.
223              
224             =head2 sign_message
225              
226             $signature = $object->sign_message($message, $algo = 'sha256')
227              
228             Signs a digest of C<$message> (using C<$algo> digest algorithm) with a private key.
229              
230             C<$algo> must be available in L<Digest> package.
231              
232             Returns a byte string containing signature.
233              
234             Character encoding note: C<$message> should be encoded in the proper encoding before passing it to this method. Passing Unicode string will cause the function to fail. You can encode like this (for UTF-8):
235              
236             use Encode qw(encode);
237             $message = encode('UTF-8', $message);
238              
239             Caution: libtomcrypt cryptographic package that is generating signatures does not currently offer a deterministic mechanism. For this reason the sign_message method will complain with a warning. You should install an optional L<Crypt::Perl> package, which supports deterministic signatures, which will disable the warning. Non-deterministic signatures can lead to leaking private keys if the random number generator's entropy is insufficient.
240              
241             =head2 verify_message
242              
243             $signature_valid = $object->verify_message($message, $signature, $algo = 'sha256')
244              
245             Verifies C<$signature> against digest of C<$message> (with C<$algo> digest algorithm) using private key.
246              
247             C<$algo> must be available in Digest package.
248              
249             Returns boolean.
250              
251             Character encoding note: C<$message> should be encoded in the proper encoding before passing it to this method. Passing Unicode string will cause the function to fail. You can encode like this (for UTF-8):
252              
253             use Encode qw(encode);
254             $message = encode('UTF-8', $message);
255              
256             =head1 EXCEPTIONS
257              
258             This module throws an instance of L<Bitcoin::Crypto::Exception> if it encounters an error. It can produce the following error types from the L<Bitcoin::Crypto::Exception> namespace:
259              
260             =over 2
261              
262             =item * Sign - couldn't sign the message correctly
263              
264             =item * Verify - couldn't verify the message correctly
265              
266             =item * KeyCreate - key couldn't be created correctly
267              
268             =item * NetworkConfig - incomplete or corrupted network configuration
269              
270             =back
271              
272             =head1 SEE ALSO
273              
274             =over 2
275              
276             =item L<Bitcoin::Crypto::Key::Public>
277              
278             =item L<Bitcoin::Crypto::Network>
279              
280             =back
281              
282             =cut
283