File Coverage

blib/lib/Bitcoin/Crypto/Key/Private.pm
Criterion Covered Total %
statement 79 80 98.7
branch 9 14 64.2
condition 1 3 33.3
subroutine 21 22 95.4
pod 3 3 100.0
total 113 122 92.6


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