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
|
8
|
|
|
8
|
|
58
|
use strict; |
|
8
|
|
|
|
|
24
|
|
|
8
|
|
|
|
|
253
|
|
77
|
8
|
|
|
8
|
|
56
|
use warnings; |
|
8
|
|
|
|
|
32
|
|
|
8
|
|
|
|
|
243
|
|
78
|
|
|
|
|
|
|
|
79
|
8
|
|
|
8
|
|
47
|
use parent qw( Crypt::Perl::ECDSA::KeyBase ); |
|
8
|
|
|
|
|
24
|
|
|
8
|
|
|
|
|
97
|
|
80
|
|
|
|
|
|
|
|
81
|
8
|
|
|
8
|
|
452
|
use Try::Tiny; |
|
8
|
|
|
|
|
22
|
|
|
8
|
|
|
|
|
430
|
|
82
|
|
|
|
|
|
|
|
83
|
8
|
|
|
8
|
|
55
|
use Bytes::Random::Secure::Tiny (); |
|
8
|
|
|
|
|
21
|
|
|
8
|
|
|
|
|
116
|
|
84
|
|
|
|
|
|
|
|
85
|
8
|
|
|
8
|
|
40
|
use Crypt::Perl::ASN1 (); |
|
8
|
|
|
|
|
17
|
|
|
8
|
|
|
|
|
132
|
|
86
|
8
|
|
|
8
|
|
36
|
use Crypt::Perl::BigInt (); |
|
8
|
|
|
|
|
27
|
|
|
8
|
|
|
|
|
229
|
|
87
|
8
|
|
|
8
|
|
42
|
use Crypt::Perl::Math (); |
|
8
|
|
|
|
|
20
|
|
|
8
|
|
|
|
|
159
|
|
88
|
8
|
|
|
8
|
|
2299
|
use Crypt::Perl::ToDER (); |
|
8
|
|
|
|
|
54
|
|
|
8
|
|
|
|
|
162
|
|
89
|
8
|
|
|
8
|
|
54
|
use Crypt::Perl::X (); |
|
8
|
|
|
|
|
72
|
|
|
8
|
|
|
|
|
281
|
|
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
#This is not the standard ASN.1 template as found in RFC 5915, |
92
|
|
|
|
|
|
|
#but it seems to generate equivalent results. |
93
|
|
|
|
|
|
|
# |
94
|
8
|
|
|
|
|
742
|
use constant ASN1_PRIVATE => Crypt::Perl::ECDSA::KeyBase->ASN1_Params() . q< |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
ECPrivateKey ::= SEQUENCE { |
97
|
|
|
|
|
|
|
version INTEGER, |
98
|
|
|
|
|
|
|
privateKey OCTET STRING, |
99
|
|
|
|
|
|
|
parameters [0] EXPLICIT EcpkParameters OPTIONAL, |
100
|
|
|
|
|
|
|
publicKey [1] EXPLICIT BIT STRING |
101
|
|
|
|
|
|
|
} |
102
|
8
|
|
|
8
|
|
43
|
>; |
|
8
|
|
|
|
|
18
|
|
103
|
|
|
|
|
|
|
|
104
|
8
|
|
|
8
|
|
63
|
use constant _PEM_HEADER => 'EC PRIVATE KEY'; |
|
8
|
|
|
|
|
16
|
|
|
8
|
|
|
|
|
397
|
|
105
|
|
|
|
|
|
|
|
106
|
8
|
|
|
8
|
|
48
|
use constant NUMBER_CLASS => 'Crypt::Perl::BigInt'; |
|
8
|
|
|
|
|
16
|
|
|
8
|
|
|
|
|
11011
|
|
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
#$curve_parts is also a hash ref, defined as whatever the ASN.1 |
109
|
|
|
|
|
|
|
#parse of the main key’s “parameters” returned, whether that be |
110
|
|
|
|
|
|
|
#explicit key parameters or a named curve. |
111
|
|
|
|
|
|
|
# |
112
|
|
|
|
|
|
|
sub new { |
113
|
546
|
|
|
546
|
0
|
2399
|
my ($class, $key_parts, $curve_parts) = @_; |
114
|
|
|
|
|
|
|
|
115
|
546
|
50
|
|
|
|
3141
|
if (!length $key_parts->{'version'}) { |
116
|
0
|
|
|
|
|
0
|
die Crypt::Perl::X::create('Generic', 'Need a “version”! (Try 1)'); |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
my $self = { |
120
|
546
|
|
|
|
|
2309
|
version => $key_parts->{'version'}, |
121
|
|
|
|
|
|
|
}; |
122
|
|
|
|
|
|
|
|
123
|
546
|
|
|
|
|
1452
|
bless $self, $class; |
124
|
|
|
|
|
|
|
|
125
|
546
|
|
|
|
|
5302
|
$self->_set_public( $key_parts->{'public'} ); |
126
|
|
|
|
|
|
|
|
127
|
546
|
|
|
|
|
2907
|
for my $k ( qw( private ) ) { |
128
|
546
|
50
|
|
546
|
|
6177
|
if ( try { $key_parts->{$k}->isa(NUMBER_CLASS()) } ) { |
|
546
|
|
|
|
|
18144
|
|
129
|
546
|
|
|
|
|
8753
|
$self->{$k} = $key_parts->{$k}; |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
else { |
132
|
0
|
|
|
|
|
0
|
die Crypt::Perl::X::create('Generic', sprintf "“$k” must be “%s”, not “$key_parts->{$k}”!", NUMBER_CLASS()); |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
546
|
|
|
|
|
3819
|
return $self->_add_params( $curve_parts ); |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
sub sign { |
140
|
274
|
|
|
274
|
0
|
117072
|
return $_[0]->_sign_and_serialize($_[1]); |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
sub _sign_and_serialize { |
144
|
487
|
|
|
487
|
|
3337
|
my ($self, $whatsit, $hashfn) = @_; |
145
|
|
|
|
|
|
|
|
146
|
487
|
|
|
|
|
4168
|
my ($r, $s) = $self->_sign($whatsit, $hashfn); |
147
|
445
|
|
|
|
|
5083
|
return $self->_serialize_sig( $r, $s ); |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
sub _hash_sign_and_serialize { |
151
|
213
|
|
|
213
|
|
2885
|
my ($self, $whatsit, $hashfn) = @_; |
152
|
|
|
|
|
|
|
|
153
|
213
|
|
|
|
|
3348
|
require Digest::SHA; |
154
|
213
|
|
|
|
|
9198
|
$whatsit = Digest::SHA->can($hashfn)->($whatsit); |
155
|
|
|
|
|
|
|
|
156
|
213
|
|
|
|
|
3073
|
return $self->_sign_and_serialize($whatsit, $hashfn); |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
sub sign_sha1 { |
160
|
199
|
|
|
199
|
0
|
7853850
|
my ($self, $whatsit) = @_; |
161
|
|
|
|
|
|
|
|
162
|
199
|
|
|
|
|
1120
|
return $_[0]->_hash_sign_and_serialize($whatsit, 'sha1'); |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
sub sign_sha224 { |
166
|
3
|
|
|
3
|
0
|
1121
|
my ($self, $whatsit) = @_; |
167
|
|
|
|
|
|
|
|
168
|
3
|
|
|
|
|
34
|
return $_[0]->_hash_sign_and_serialize($whatsit, 'sha224'); |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
sub sign_sha256 { |
172
|
6
|
|
|
6
|
0
|
1146
|
my ($self, $whatsit) = @_; |
173
|
|
|
|
|
|
|
|
174
|
6
|
|
|
|
|
60
|
return $_[0]->_hash_sign_and_serialize($whatsit, 'sha256'); |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
sub sign_sha384 { |
178
|
2
|
|
|
2
|
0
|
1152
|
my ($self, $whatsit) = @_; |
179
|
|
|
|
|
|
|
|
180
|
2
|
|
|
|
|
50
|
return $_[0]->_hash_sign_and_serialize($whatsit, 'sha384'); |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
sub sign_sha512 { |
184
|
3
|
|
|
3
|
0
|
1081
|
my ($self, $whatsit) = @_; |
185
|
|
|
|
|
|
|
|
186
|
3
|
|
|
|
|
63
|
return $_[0]->_hash_sign_and_serialize($whatsit, 'sha512'); |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
#cf. RFC 7518, page 8 |
190
|
|
|
|
|
|
|
sub sign_jwa { |
191
|
6
|
|
|
6
|
0
|
4000
|
my ($self, $whatsit) = @_; |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
# As of version 0.34 this method creates deterministic signatures. |
194
|
|
|
|
|
|
|
|
195
|
6
|
|
|
|
|
34
|
my $dgst_name = $self->_get_jwk_digest_name(); |
196
|
|
|
|
|
|
|
|
197
|
6
|
|
|
|
|
35
|
require Digest::SHA; |
198
|
6
|
|
|
|
|
200
|
$whatsit = Digest::SHA->can($dgst_name)->($whatsit); |
199
|
|
|
|
|
|
|
|
200
|
6
|
|
|
|
|
36
|
my ($r, $s) = map { $_->as_bytes() } $self->_sign($whatsit, $dgst_name); |
|
12
|
|
|
|
|
60
|
|
201
|
|
|
|
|
|
|
|
202
|
6
|
|
|
|
|
72
|
my $octet_length = Crypt::Perl::Math::ceil($self->max_sign_bits() / 8); |
203
|
|
|
|
|
|
|
|
204
|
6
|
|
|
|
|
43
|
substr( $_, 0, 0 ) = "\0" x ($octet_length - length) for ($r, $s); |
205
|
|
|
|
|
|
|
|
206
|
6
|
|
|
|
|
46
|
return $r . $s; |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
sub get_public_key { |
210
|
13
|
|
|
13
|
0
|
202
|
my ($self) = @_; |
211
|
|
|
|
|
|
|
|
212
|
13
|
|
|
|
|
1333
|
require Crypt::Perl::ECDSA::PublicKey; |
213
|
|
|
|
|
|
|
|
214
|
13
|
|
|
|
|
186
|
my $curve_hr = $self->_explicit_curve_parameters( seed => 1 ); |
215
|
13
|
|
|
|
|
83
|
my $ccurve_hr = $curve_hr->{'ecParameters'}{'curve'}; |
216
|
13
|
|
|
|
|
52
|
$ccurve_hr->{'seed'} = [ $ccurve_hr->{'seed'} ]; |
217
|
|
|
|
|
|
|
|
218
|
13
|
|
|
|
|
110
|
return Crypt::Perl::ECDSA::PublicKey->new( |
219
|
|
|
|
|
|
|
$self->_decompress_public_point(), |
220
|
|
|
|
|
|
|
$curve_hr, |
221
|
|
|
|
|
|
|
); |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
sub get_struct_for_private_jwk { |
225
|
1
|
|
|
1
|
0
|
839
|
my ($self) = @_; |
226
|
|
|
|
|
|
|
|
227
|
1
|
|
|
|
|
8
|
my $hr = $self->get_struct_for_public_jwk(); |
228
|
|
|
|
|
|
|
|
229
|
1
|
|
|
|
|
32
|
require MIME::Base64; |
230
|
|
|
|
|
|
|
|
231
|
1
|
|
|
|
|
7
|
$hr->{'d'} = MIME::Base64::encode_base64url( $self->{'private'}->as_bytes() ); |
232
|
|
|
|
|
|
|
|
233
|
1
|
|
|
|
|
18
|
return $hr; |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
#---------------------------------------------------------------------- |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
#$whatsit is probably a message digest, e.g., from SHA256 |
239
|
|
|
|
|
|
|
sub _sign { |
240
|
493
|
|
|
493
|
|
2277
|
my ($self, $whatsit, $det_hashfuncname) = @_; |
241
|
|
|
|
|
|
|
|
242
|
493
|
|
|
|
|
3409
|
my $dgst = Crypt::Perl::BigInt->from_bytes( $whatsit ); |
243
|
|
|
|
|
|
|
|
244
|
493
|
|
|
|
|
117050
|
my $priv_num = $self->{'private'}; #Math::BigInt->from_hex( $priv_hex ); |
245
|
|
|
|
|
|
|
|
246
|
493
|
|
|
|
|
3383
|
my $n = $self->_curve()->{'n'}; #$curve_data->{'n'}; |
247
|
|
|
|
|
|
|
|
248
|
493
|
|
|
|
|
3020
|
my $key_len = $self->max_sign_bits(); |
249
|
493
|
|
|
|
|
24705
|
my $dgst_len = $dgst->bit_length(); |
250
|
493
|
100
|
|
|
|
15223
|
if ( $dgst_len > $key_len ) { |
251
|
42
|
|
|
|
|
453
|
die Crypt::Perl::X::create('TooLongToSign', $key_len, $dgst_len ); |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
#isa ECPoint |
255
|
451
|
|
|
|
|
4869
|
my $G = $self->_G(); |
256
|
451
|
|
|
|
|
1839
|
my ($k, $r); |
257
|
|
|
|
|
|
|
|
258
|
451
|
|
|
|
|
1244
|
do { |
259
|
451
|
100
|
|
|
|
3561
|
if ($det_hashfuncname) { |
260
|
219
|
|
|
|
|
4397
|
require Crypt::Perl::ECDSA::Deterministic; |
261
|
219
|
|
|
|
|
4364
|
$k = Crypt::Perl::ECDSA::Deterministic::generate_k( |
262
|
|
|
|
|
|
|
$n, |
263
|
|
|
|
|
|
|
$priv_num, |
264
|
|
|
|
|
|
|
$whatsit, |
265
|
|
|
|
|
|
|
$det_hashfuncname, |
266
|
|
|
|
|
|
|
); |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
else { |
269
|
232
|
|
|
|
|
3725
|
$k = Crypt::Perl::Math::randint($n); |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
|
272
|
451
|
|
|
|
|
4419
|
my $Q = $G->multiply($k); #$Q isa ECPoint |
273
|
|
|
|
|
|
|
|
274
|
451
|
|
|
|
|
4350
|
$r = $Q->get_x()->to_bigint()->copy()->bmod($n); |
275
|
|
|
|
|
|
|
} while !$r->is_positive(); |
276
|
|
|
|
|
|
|
|
277
|
451
|
|
|
|
|
65628
|
my $s = $k->bmodinv($n); |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
#$s *= ( $dgst + ( $priv_num * $r ) ); |
280
|
451
|
|
|
|
|
89685
|
$s->bmul( $priv_num->copy()->bmuladd( $r, $dgst ) ); |
281
|
|
|
|
|
|
|
|
282
|
451
|
|
|
|
|
70316
|
$s->bmod($n); |
283
|
|
|
|
|
|
|
|
284
|
451
|
|
|
|
|
44002
|
return ($r, $s); |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
sub _get_asn1_parts { |
288
|
418
|
|
|
418
|
|
2495
|
my ($self, $curve_parts, @params) = @_; |
289
|
|
|
|
|
|
|
|
290
|
418
|
|
|
|
|
2652
|
my $private_str = $self->{'private'}->as_bytes(); |
291
|
|
|
|
|
|
|
|
292
|
418
|
|
|
|
|
7767
|
return $self->__to_der( |
293
|
|
|
|
|
|
|
'ECPrivateKey', |
294
|
|
|
|
|
|
|
ASN1_PRIVATE(), |
295
|
|
|
|
|
|
|
{ |
296
|
|
|
|
|
|
|
version => 1, |
297
|
|
|
|
|
|
|
privateKey => $private_str, |
298
|
|
|
|
|
|
|
parameters => $curve_parts, |
299
|
|
|
|
|
|
|
}, |
300
|
|
|
|
|
|
|
@params, |
301
|
|
|
|
|
|
|
); |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
sub _serialize_sig { |
305
|
445
|
|
|
445
|
|
2116
|
my ($self, $r, $s) = @_; |
306
|
|
|
|
|
|
|
|
307
|
445
|
|
|
|
|
4406
|
my $asn1 = Crypt::Perl::ASN1->new()->prepare( $self->ASN1_SIGNATURE() ); |
308
|
445
|
|
|
|
|
5052
|
return $asn1->encode( r => $r, s => $s ); |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
1; |