line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Crypt::Perl::ECDSA::PrivateKey; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=encoding utf-8 |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=head1 NAME |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
Crypt::Perl::ECDSA::PrivateKey - object representation of ECDSA private key |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
=head1 SYNOPSIS |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
#Use Generate.pm or Parse.pm rather |
12
|
|
|
|
|
|
|
#than instantiating this class directly. |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
#This works even if the object came from a key file that doesn’t |
15
|
|
|
|
|
|
|
#contain the curve name. |
16
|
|
|
|
|
|
|
$prkey->get_curve_name(); |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
if ($payload > ($prkey->max_sign_bits() / 8)) { |
19
|
|
|
|
|
|
|
die "Payload too long!"; |
20
|
|
|
|
|
|
|
} |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
#$payload is probably a hash (e.g., SHA-256) of your original message. |
23
|
|
|
|
|
|
|
my $sig = $prkey->sign($payload); |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
#For JSON Web Algorithms (JWT et al.), cf. RFC 7518 page 8 |
26
|
|
|
|
|
|
|
#This will also apply the appropriate SHA algorithm before signing. |
27
|
|
|
|
|
|
|
my $sig_jwa = $prkey->sign_jwa($payload); |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
$prkey->verify($payload, $sig) or die "Invalid signature!"; |
30
|
|
|
|
|
|
|
$prkey->verify_jwa($payload, $sig_jwa) or die "Invalid signature!"; |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
#Corresponding “der” methods exist as well. |
33
|
|
|
|
|
|
|
my $cn_pem = $prkey->to_pem_with_curve_name(); |
34
|
|
|
|
|
|
|
my $expc_pem = $prkey->to_pem_with_explicit_curve(); |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
#---------------------------------------------------------------------- |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
my $pbkey = $prkey->get_public_key(); |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
#---------------------------------------------------------------------- |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
#Includes “kty”, “crv”, “x”, “y”, and (for private) “d”. |
43
|
|
|
|
|
|
|
#Add in whatever else your application needs afterward. |
44
|
|
|
|
|
|
|
# |
45
|
|
|
|
|
|
|
#These will die() if you try to run it with a curve that |
46
|
|
|
|
|
|
|
#doesn’t have a known JWK “crv” value. |
47
|
|
|
|
|
|
|
# |
48
|
|
|
|
|
|
|
my $prv_jwk = $prkey->get_struct_for_private_jwk(); |
49
|
|
|
|
|
|
|
my $pub_jwk = $prkey->get_struct_for_public_jwk(); |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
#Useful for JWTs |
52
|
|
|
|
|
|
|
my $jwt_alg = $pbkey->get_jwa_alg(); |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
=head1 DISCUSSION |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
The SYNOPSIS above should be illustration enough of how to use this class. |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=head1 SECURITY |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
The security advantages of elliptic-curve cryptography (ECC) are a matter of |
61
|
|
|
|
|
|
|
some controversy. While the math itself is apparently bulletproof, there are |
62
|
|
|
|
|
|
|
varying opinions about the integrity of the various curves that are recommended |
63
|
|
|
|
|
|
|
for ECC. Some believe that some curves contain “backdoors” that would allow |
64
|
|
|
|
|
|
|
L to sniff a transmission. |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
That said, RSA will eventually no longer be viable: as the keys get bigger, the |
67
|
|
|
|
|
|
|
security advantage of increasing their size diminishes. |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=head1 TODO |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
This minimal set of functionality can be augmented as feature requests come in. |
72
|
|
|
|
|
|
|
Patches are welcome—particularly with tests! |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
=cut |
75
|
|
|
|
|
|
|
|
76
|
7
|
|
|
7
|
|
77
|
use strict; |
|
7
|
|
|
|
|
15
|
|
|
7
|
|
|
|
|
180
|
|
77
|
7
|
|
|
7
|
|
31
|
use warnings; |
|
7
|
|
|
|
|
12
|
|
|
7
|
|
|
|
|
196
|
|
78
|
|
|
|
|
|
|
|
79
|
7
|
|
|
7
|
|
34
|
use parent qw( Crypt::Perl::ECDSA::KeyBase ); |
|
7
|
|
|
|
|
16
|
|
|
7
|
|
|
|
|
47
|
|
80
|
|
|
|
|
|
|
|
81
|
7
|
|
|
7
|
|
301
|
use Try::Tiny; |
|
7
|
|
|
|
|
11
|
|
|
7
|
|
|
|
|
291
|
|
82
|
|
|
|
|
|
|
|
83
|
7
|
|
|
7
|
|
35
|
use Bytes::Random::Secure::Tiny (); |
|
7
|
|
|
|
|
13
|
|
|
7
|
|
|
|
|
82
|
|
84
|
|
|
|
|
|
|
|
85
|
7
|
|
|
7
|
|
28
|
use Crypt::Perl::ASN1 (); |
|
7
|
|
|
|
|
14
|
|
|
7
|
|
|
|
|
96
|
|
86
|
7
|
|
|
7
|
|
25
|
use Crypt::Perl::BigInt (); |
|
7
|
|
|
|
|
14
|
|
|
7
|
|
|
|
|
118
|
|
87
|
7
|
|
|
7
|
|
1518
|
use Crypt::Perl::PKCS8 (); |
|
7
|
|
|
|
|
14
|
|
|
7
|
|
|
|
|
107
|
|
88
|
7
|
|
|
7
|
|
33
|
use Crypt::Perl::Math (); |
|
7
|
|
|
|
|
11
|
|
|
7
|
|
|
|
|
89
|
|
89
|
7
|
|
|
7
|
|
1331
|
use Crypt::Perl::ToDER (); |
|
7
|
|
|
|
|
14
|
|
|
7
|
|
|
|
|
115
|
|
90
|
7
|
|
|
7
|
|
34
|
use Crypt::Perl::X (); |
|
7
|
|
|
|
|
9
|
|
|
7
|
|
|
|
|
227
|
|
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
#This is not the standard ASN.1 template as found in RFC 5915, |
93
|
|
|
|
|
|
|
#but it seems to generate equivalent results. |
94
|
|
|
|
|
|
|
# |
95
|
7
|
|
|
|
|
491
|
use constant ASN1_PRIVATE => Crypt::Perl::ECDSA::KeyBase->ASN1_Params() . q< |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
ECPrivateKey ::= SEQUENCE { |
98
|
|
|
|
|
|
|
version INTEGER, |
99
|
|
|
|
|
|
|
privateKey OCTET STRING, |
100
|
|
|
|
|
|
|
parameters [0] EXPLICIT EcpkParameters OPTIONAL, |
101
|
|
|
|
|
|
|
publicKey [1] EXPLICIT BIT STRING |
102
|
|
|
|
|
|
|
} |
103
|
7
|
|
|
7
|
|
32
|
>; |
|
7
|
|
|
|
|
73
|
|
104
|
|
|
|
|
|
|
|
105
|
7
|
|
|
7
|
|
40
|
use constant _PEM_HEADER => 'EC PRIVATE KEY'; |
|
7
|
|
|
|
|
11
|
|
|
7
|
|
|
|
|
282
|
|
106
|
|
|
|
|
|
|
|
107
|
7
|
|
|
7
|
|
36
|
use constant NUMBER_CLASS => 'Crypt::Perl::BigInt'; |
|
7
|
|
|
|
|
37
|
|
|
7
|
|
|
|
|
5310
|
|
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
#$curve_parts is also a hash ref, defined as whatever the ASN.1 |
110
|
|
|
|
|
|
|
#parse of the main key’s “parameters” returned, whether that be |
111
|
|
|
|
|
|
|
#explicit key parameters or a named curve. |
112
|
|
|
|
|
|
|
# |
113
|
|
|
|
|
|
|
sub new { |
114
|
544
|
|
|
544
|
0
|
1991
|
my ($class, $key_parts, $curve_parts) = @_; |
115
|
|
|
|
|
|
|
|
116
|
544
|
50
|
|
|
|
2580
|
if (!length $key_parts->{'version'}) { |
117
|
0
|
|
|
|
|
0
|
die Crypt::Perl::X::create('Generic', 'Need a “version”! (Try 1)'); |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
my $self = { |
121
|
544
|
|
|
|
|
1981
|
version => $key_parts->{'version'}, |
122
|
|
|
|
|
|
|
}; |
123
|
|
|
|
|
|
|
|
124
|
544
|
|
|
|
|
1492
|
bless $self, $class; |
125
|
|
|
|
|
|
|
|
126
|
544
|
|
|
|
|
4298
|
$self->_set_public( $key_parts->{'public'} ); |
127
|
|
|
|
|
|
|
|
128
|
544
|
|
|
|
|
2073
|
for my $k ( qw( private ) ) { |
129
|
544
|
50
|
|
544
|
|
5594
|
if ( try { $key_parts->{$k}->isa(NUMBER_CLASS()) } ) { |
|
544
|
|
|
|
|
15680
|
|
130
|
544
|
|
|
|
|
7669
|
$self->{$k} = $key_parts->{$k}; |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
else { |
133
|
0
|
|
|
|
|
0
|
die Crypt::Perl::X::create('Generic', sprintf "“$k” must be “%s”, not “$key_parts->{$k}”!", NUMBER_CLASS()); |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
544
|
|
|
|
|
3399
|
return $self->_add_params( $curve_parts ); |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
sub sign { |
141
|
284
|
|
|
284
|
0
|
353795
|
my ($self, $whatsit) = @_; |
142
|
|
|
|
|
|
|
|
143
|
284
|
|
|
|
|
2368
|
my ($r, $s) = $self->_sign($whatsit); |
144
|
242
|
|
|
|
|
2808
|
return $self->_serialize_sig( $r, $s ); |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
#cf. RFC 7518, page 8 |
148
|
|
|
|
|
|
|
sub sign_jwa { |
149
|
3
|
|
|
3
|
0
|
1944
|
my ($self, $whatsit) = @_; |
150
|
|
|
|
|
|
|
|
151
|
3
|
|
|
|
|
18
|
my $dgst_cr = $self->_get_jwk_digest_cr(); |
152
|
|
|
|
|
|
|
|
153
|
3
|
|
|
|
|
73
|
my ($r, $s) = map { $_->as_bytes() } $self->_sign($dgst_cr->($whatsit)); |
|
6
|
|
|
|
|
26
|
|
154
|
|
|
|
|
|
|
|
155
|
3
|
|
|
|
|
37
|
my $octet_length = Crypt::Perl::Math::ceil($self->max_sign_bits() / 8); |
156
|
|
|
|
|
|
|
|
157
|
3
|
|
|
|
|
20
|
substr( $_, 0, 0 ) = "\0" x ($octet_length - length) for ($r, $s); |
158
|
|
|
|
|
|
|
|
159
|
3
|
|
|
|
|
21
|
return $r . $s; |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
sub get_public_key { |
163
|
13
|
|
|
13
|
0
|
136
|
my ($self) = @_; |
164
|
|
|
|
|
|
|
|
165
|
13
|
|
|
|
|
1186
|
require Crypt::Perl::ECDSA::PublicKey; |
166
|
|
|
|
|
|
|
|
167
|
13
|
|
|
|
|
138
|
my $curve_hr = $self->_explicit_curve_parameters( seed => 1 ); |
168
|
13
|
|
|
|
|
49
|
my $ccurve_hr = $curve_hr->{'ecParameters'}{'curve'}; |
169
|
13
|
|
|
|
|
41
|
$ccurve_hr->{'seed'} = [ $ccurve_hr->{'seed'} ]; |
170
|
|
|
|
|
|
|
|
171
|
13
|
|
|
|
|
120
|
return Crypt::Perl::ECDSA::PublicKey->new( |
172
|
|
|
|
|
|
|
$self->_decompress_public_point(), |
173
|
|
|
|
|
|
|
$curve_hr, |
174
|
|
|
|
|
|
|
); |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
sub get_struct_for_private_jwk { |
178
|
1
|
|
|
1
|
0
|
676
|
my ($self) = @_; |
179
|
|
|
|
|
|
|
|
180
|
1
|
|
|
|
|
4
|
my $hr = $self->get_struct_for_public_jwk(); |
181
|
|
|
|
|
|
|
|
182
|
1
|
|
|
|
|
23
|
require MIME::Base64; |
183
|
|
|
|
|
|
|
|
184
|
1
|
|
|
|
|
5
|
$hr->{'d'} = MIME::Base64::encode_base64url( $self->{'private'}->as_bytes() ); |
185
|
|
|
|
|
|
|
|
186
|
1
|
|
|
|
|
14
|
return $hr; |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
#---------------------------------------------------------------------- |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
#$whatsit is probably a message digest, e.g., from SHA256 |
192
|
|
|
|
|
|
|
sub _sign { |
193
|
287
|
|
|
287
|
|
1761
|
my ($self, $whatsit) = @_; |
194
|
|
|
|
|
|
|
|
195
|
287
|
|
|
|
|
2911
|
my $dgst = Crypt::Perl::BigInt->from_bytes( $whatsit ); |
196
|
|
|
|
|
|
|
|
197
|
287
|
|
|
|
|
144153
|
my $priv_num = $self->{'private'}; #Math::BigInt->from_hex( $priv_hex ); |
198
|
|
|
|
|
|
|
|
199
|
287
|
|
|
|
|
2492
|
my $n = $self->_curve()->{'n'}; #$curve_data->{'n'}; |
200
|
|
|
|
|
|
|
|
201
|
287
|
|
|
|
|
3118
|
my $key_len = $self->max_sign_bits(); |
202
|
287
|
|
|
|
|
249914
|
my $dgst_len = $dgst->bit_length(); |
203
|
287
|
100
|
|
|
|
138053
|
if ( $dgst_len > $key_len ) { |
204
|
42
|
|
|
|
|
514
|
die Crypt::Perl::X::create('TooLongToSign', $key_len, $dgst_len ); |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
#isa ECPoint |
208
|
245
|
|
|
|
|
2456
|
my $G = $self->_G(); |
209
|
|
|
|
|
|
|
#printf "G.x: %s\n", $G->{'x'}->to_bigint()->as_hex(); |
210
|
|
|
|
|
|
|
#printf "G.y: %s\n", $G->{'y'}->to_bigint()->as_hex(); |
211
|
|
|
|
|
|
|
#printf "G.z: %s\n", $G->{'z'}->as_hex(); |
212
|
|
|
|
|
|
|
|
213
|
245
|
|
|
|
|
1307
|
my ($k, $r); |
214
|
|
|
|
|
|
|
|
215
|
245
|
|
|
|
|
1351
|
do { |
216
|
245
|
|
|
|
|
3848
|
$k = Crypt::Perl::Math::randint($n); |
217
|
|
|
|
|
|
|
#print "once\n"; |
218
|
|
|
|
|
|
|
#printf "big random: %s\n", $k->as_hex(); |
219
|
|
|
|
|
|
|
#$k = Crypt::Perl::BigInt->new("98452900523450592996995215574085435893040452563985855319633891614520662229711"); |
220
|
|
|
|
|
|
|
#printf "k: %s\n", $k->bstr(); |
221
|
245
|
|
|
|
|
2910
|
my $Q = $G->multiply($k); #$Q isa ECPoint |
222
|
|
|
|
|
|
|
#printf "Q.x: %s\n", $Q->{'x'}->to_bigint()->as_hex(); |
223
|
|
|
|
|
|
|
#printf "Q.y: %s\n", $Q->{'y'}->to_bigint()->as_hex(); |
224
|
|
|
|
|
|
|
#printf "Q.z: %s\n", $Q->{'z'}->as_hex(); |
225
|
245
|
|
|
|
|
1175
|
$r = $Q->get_x()->to_bigint()->copy()->bmod($n); |
226
|
|
|
|
|
|
|
} while !$r->is_positive(); |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
#printf "k: %s\n", $k->as_hex(); |
229
|
|
|
|
|
|
|
#printf "n: %s\n", $n->as_hex(); |
230
|
|
|
|
|
|
|
#printf "e: %s\n", $dgst->as_hex(); |
231
|
|
|
|
|
|
|
#printf "d: %s\n", $priv_num->as_hex(); |
232
|
|
|
|
|
|
|
#printf "r: %s\n", $r->as_hex(); |
233
|
|
|
|
|
|
|
|
234
|
245
|
|
|
|
|
35770
|
my $s = $k->bmodinv($n); |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
#$s *= ( $dgst + ( $priv_num * $r ) ); |
237
|
245
|
|
|
|
|
5467096
|
$s->bmul( $priv_num->copy()->bmuladd( $r, $dgst ) ); |
238
|
|
|
|
|
|
|
|
239
|
245
|
|
|
|
|
218740
|
$s->bmod($n); |
240
|
|
|
|
|
|
|
|
241
|
245
|
|
|
|
|
227068
|
return ($r, $s); |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
sub _get_asn1_parts { |
245
|
250
|
|
|
250
|
|
1067
|
my ($self, $curve_parts, @params) = @_; |
246
|
|
|
|
|
|
|
|
247
|
250
|
|
|
|
|
1162
|
my $private_str = $self->{'private'}->as_bytes(); |
248
|
|
|
|
|
|
|
|
249
|
250
|
|
|
|
|
3063
|
return $self->__to_der( |
250
|
|
|
|
|
|
|
'ECPrivateKey', |
251
|
|
|
|
|
|
|
ASN1_PRIVATE(), |
252
|
|
|
|
|
|
|
{ |
253
|
|
|
|
|
|
|
version => 1, |
254
|
|
|
|
|
|
|
privateKey => $private_str, |
255
|
|
|
|
|
|
|
parameters => $curve_parts, |
256
|
|
|
|
|
|
|
}, |
257
|
|
|
|
|
|
|
@params, |
258
|
|
|
|
|
|
|
); |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
sub _serialize_sig { |
262
|
242
|
|
|
242
|
|
1213
|
my ($self, $r, $s) = @_; |
263
|
|
|
|
|
|
|
|
264
|
242
|
|
|
|
|
2518
|
my $asn1 = Crypt::Perl::ASN1->new()->prepare( $self->ASN1_SIGNATURE() ); |
265
|
242
|
|
|
|
|
1729
|
return $asn1->encode( r => $r, s => $s ); |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
1; |