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
|
|
37
|
use strict; |
|
7
|
|
|
|
|
10
|
|
|
7
|
|
|
|
|
162
|
|
77
|
7
|
|
|
7
|
|
27
|
use warnings; |
|
7
|
|
|
|
|
12
|
|
|
7
|
|
|
|
|
173
|
|
78
|
|
|
|
|
|
|
|
79
|
7
|
|
|
7
|
|
29
|
use parent qw( Crypt::Perl::ECDSA::KeyBase ); |
|
7
|
|
|
|
|
13
|
|
|
7
|
|
|
|
|
41
|
|
80
|
|
|
|
|
|
|
|
81
|
7
|
|
|
7
|
|
292
|
use Try::Tiny; |
|
7
|
|
|
|
|
13
|
|
|
7
|
|
|
|
|
299
|
|
82
|
|
|
|
|
|
|
|
83
|
7
|
|
|
7
|
|
33
|
use Bytes::Random::Secure::Tiny (); |
|
7
|
|
|
|
|
13
|
|
|
7
|
|
|
|
|
92
|
|
84
|
|
|
|
|
|
|
|
85
|
7
|
|
|
7
|
|
29
|
use Crypt::Perl::ASN1 (); |
|
7
|
|
|
|
|
9
|
|
|
7
|
|
|
|
|
74
|
|
86
|
7
|
|
|
7
|
|
28
|
use Crypt::Perl::BigInt (); |
|
7
|
|
|
|
|
10
|
|
|
7
|
|
|
|
|
109
|
|
87
|
7
|
|
|
7
|
|
29
|
use Crypt::Perl::Math (); |
|
7
|
|
|
|
|
10
|
|
|
7
|
|
|
|
|
94
|
|
88
|
7
|
|
|
7
|
|
1428
|
use Crypt::Perl::ToDER (); |
|
7
|
|
|
|
|
11
|
|
|
7
|
|
|
|
|
86
|
|
89
|
7
|
|
|
7
|
|
30
|
use Crypt::Perl::X (); |
|
7
|
|
|
|
|
8
|
|
|
7
|
|
|
|
|
200
|
|
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
|
7
|
|
|
|
|
426
|
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
|
7
|
|
|
7
|
|
31
|
>; |
|
7
|
|
|
|
|
54
|
|
103
|
|
|
|
|
|
|
|
104
|
7
|
|
|
7
|
|
38
|
use constant _PEM_HEADER => 'EC PRIVATE KEY'; |
|
7
|
|
|
|
|
12
|
|
|
7
|
|
|
|
|
306
|
|
105
|
|
|
|
|
|
|
|
106
|
7
|
|
|
7
|
|
31
|
use constant NUMBER_CLASS => 'Crypt::Perl::BigInt'; |
|
7
|
|
|
|
|
38
|
|
|
7
|
|
|
|
|
4753
|
|
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
|
544
|
|
|
544
|
0
|
2077
|
my ($class, $key_parts, $curve_parts) = @_; |
114
|
|
|
|
|
|
|
|
115
|
544
|
50
|
|
|
|
2881
|
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
|
544
|
|
|
|
|
2137
|
version => $key_parts->{'version'}, |
121
|
|
|
|
|
|
|
}; |
122
|
|
|
|
|
|
|
|
123
|
544
|
|
|
|
|
1367
|
bless $self, $class; |
124
|
|
|
|
|
|
|
|
125
|
544
|
|
|
|
|
3953
|
$self->_set_public( $key_parts->{'public'} ); |
126
|
|
|
|
|
|
|
|
127
|
544
|
|
|
|
|
2220
|
for my $k ( qw( private ) ) { |
128
|
544
|
50
|
|
544
|
|
6317
|
if ( try { $key_parts->{$k}->isa(NUMBER_CLASS()) } ) { |
|
544
|
|
|
|
|
14580
|
|
129
|
544
|
|
|
|
|
6891
|
$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
|
544
|
|
|
|
|
3264
|
return $self->_add_params( $curve_parts ); |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
sub sign { |
140
|
284
|
|
|
284
|
0
|
308556
|
my ($self, $whatsit) = @_; |
141
|
|
|
|
|
|
|
|
142
|
284
|
|
|
|
|
2527
|
my ($r, $s) = $self->_sign($whatsit); |
143
|
242
|
|
|
|
|
3031
|
return $self->_serialize_sig( $r, $s ); |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
#cf. RFC 7518, page 8 |
147
|
|
|
|
|
|
|
sub sign_jwa { |
148
|
3
|
|
|
3
|
0
|
2439
|
my ($self, $whatsit) = @_; |
149
|
|
|
|
|
|
|
|
150
|
3
|
|
|
|
|
29
|
my $dgst_cr = $self->_get_jwk_digest_cr(); |
151
|
|
|
|
|
|
|
|
152
|
3
|
|
|
|
|
67
|
my ($r, $s) = map { $_->as_bytes() } $self->_sign($dgst_cr->($whatsit)); |
|
6
|
|
|
|
|
33
|
|
153
|
|
|
|
|
|
|
|
154
|
3
|
|
|
|
|
28
|
my $octet_length = Crypt::Perl::Math::ceil($self->max_sign_bits() / 8); |
155
|
|
|
|
|
|
|
|
156
|
3
|
|
|
|
|
20
|
substr( $_, 0, 0 ) = "\0" x ($octet_length - length) for ($r, $s); |
157
|
|
|
|
|
|
|
|
158
|
3
|
|
|
|
|
17
|
return $r . $s; |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
sub get_public_key { |
162
|
13
|
|
|
13
|
0
|
107
|
my ($self) = @_; |
163
|
|
|
|
|
|
|
|
164
|
13
|
|
|
|
|
1080
|
require Crypt::Perl::ECDSA::PublicKey; |
165
|
|
|
|
|
|
|
|
166
|
13
|
|
|
|
|
126
|
my $curve_hr = $self->_explicit_curve_parameters( seed => 1 ); |
167
|
13
|
|
|
|
|
44
|
my $ccurve_hr = $curve_hr->{'ecParameters'}{'curve'}; |
168
|
13
|
|
|
|
|
40
|
$ccurve_hr->{'seed'} = [ $ccurve_hr->{'seed'} ]; |
169
|
|
|
|
|
|
|
|
170
|
13
|
|
|
|
|
88
|
return Crypt::Perl::ECDSA::PublicKey->new( |
171
|
|
|
|
|
|
|
$self->_decompress_public_point(), |
172
|
|
|
|
|
|
|
$curve_hr, |
173
|
|
|
|
|
|
|
); |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
sub get_struct_for_private_jwk { |
177
|
1
|
|
|
1
|
0
|
1153
|
my ($self) = @_; |
178
|
|
|
|
|
|
|
|
179
|
1
|
|
|
|
|
6
|
my $hr = $self->get_struct_for_public_jwk(); |
180
|
|
|
|
|
|
|
|
181
|
1
|
|
|
|
|
40
|
require MIME::Base64; |
182
|
|
|
|
|
|
|
|
183
|
1
|
|
|
|
|
17
|
$hr->{'d'} = MIME::Base64::encode_base64url( $self->{'private'}->as_bytes() ); |
184
|
|
|
|
|
|
|
|
185
|
1
|
|
|
|
|
20
|
return $hr; |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
#---------------------------------------------------------------------- |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
#$whatsit is probably a message digest, e.g., from SHA256 |
191
|
|
|
|
|
|
|
sub _sign { |
192
|
287
|
|
|
287
|
|
1439
|
my ($self, $whatsit) = @_; |
193
|
|
|
|
|
|
|
|
194
|
287
|
|
|
|
|
2610
|
my $dgst = Crypt::Perl::BigInt->from_bytes( $whatsit ); |
195
|
|
|
|
|
|
|
|
196
|
287
|
|
|
|
|
134912
|
my $priv_num = $self->{'private'}; #Math::BigInt->from_hex( $priv_hex ); |
197
|
|
|
|
|
|
|
|
198
|
287
|
|
|
|
|
1593
|
my $n = $self->_curve()->{'n'}; #$curve_data->{'n'}; |
199
|
|
|
|
|
|
|
|
200
|
287
|
|
|
|
|
3018
|
my $key_len = $self->max_sign_bits(); |
201
|
287
|
|
|
|
|
232696
|
my $dgst_len = $dgst->bit_length(); |
202
|
287
|
100
|
|
|
|
134316
|
if ( $dgst_len > $key_len ) { |
203
|
42
|
|
|
|
|
466
|
die Crypt::Perl::X::create('TooLongToSign', $key_len, $dgst_len ); |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
#isa ECPoint |
207
|
245
|
|
|
|
|
2247
|
my $G = $self->_G(); |
208
|
|
|
|
|
|
|
#printf "G.x: %s\n", $G->{'x'}->to_bigint()->as_hex(); |
209
|
|
|
|
|
|
|
#printf "G.y: %s\n", $G->{'y'}->to_bigint()->as_hex(); |
210
|
|
|
|
|
|
|
#printf "G.z: %s\n", $G->{'z'}->as_hex(); |
211
|
|
|
|
|
|
|
|
212
|
245
|
|
|
|
|
679
|
my ($k, $r); |
213
|
|
|
|
|
|
|
|
214
|
245
|
|
|
|
|
462
|
do { |
215
|
245
|
|
|
|
|
2857
|
$k = Crypt::Perl::Math::randint($n); |
216
|
|
|
|
|
|
|
#print "once\n"; |
217
|
|
|
|
|
|
|
#printf "big random: %s\n", $k->as_hex(); |
218
|
|
|
|
|
|
|
#$k = Crypt::Perl::BigInt->new("98452900523450592996995215574085435893040452563985855319633891614520662229711"); |
219
|
|
|
|
|
|
|
#printf "k: %s\n", $k->bstr(); |
220
|
245
|
|
|
|
|
2301
|
my $Q = $G->multiply($k); #$Q isa ECPoint |
221
|
|
|
|
|
|
|
#printf "Q.x: %s\n", $Q->{'x'}->to_bigint()->as_hex(); |
222
|
|
|
|
|
|
|
#printf "Q.y: %s\n", $Q->{'y'}->to_bigint()->as_hex(); |
223
|
|
|
|
|
|
|
#printf "Q.z: %s\n", $Q->{'z'}->as_hex(); |
224
|
245
|
|
|
|
|
1263
|
$r = $Q->get_x()->to_bigint()->copy()->bmod($n); |
225
|
|
|
|
|
|
|
} while !$r->is_positive(); |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
#printf "k: %s\n", $k->as_hex(); |
228
|
|
|
|
|
|
|
#printf "n: %s\n", $n->as_hex(); |
229
|
|
|
|
|
|
|
#printf "e: %s\n", $dgst->as_hex(); |
230
|
|
|
|
|
|
|
#printf "d: %s\n", $priv_num->as_hex(); |
231
|
|
|
|
|
|
|
#printf "r: %s\n", $r->as_hex(); |
232
|
|
|
|
|
|
|
|
233
|
245
|
|
|
|
|
33673
|
my $s = $k->bmodinv($n); |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
#$s *= ( $dgst + ( $priv_num * $r ) ); |
236
|
245
|
|
|
|
|
5221729
|
$s->bmul( $priv_num->copy()->bmuladd( $r, $dgst ) ); |
237
|
|
|
|
|
|
|
|
238
|
245
|
|
|
|
|
214390
|
$s->bmod($n); |
239
|
|
|
|
|
|
|
|
240
|
245
|
|
|
|
|
215678
|
return ($r, $s); |
241
|
|
|
|
|
|
|
} |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
sub _get_asn1_parts { |
244
|
250
|
|
|
250
|
|
822
|
my ($self, $curve_parts, @params) = @_; |
245
|
|
|
|
|
|
|
|
246
|
250
|
|
|
|
|
1013
|
my $private_str = $self->{'private'}->as_bytes(); |
247
|
|
|
|
|
|
|
|
248
|
250
|
|
|
|
|
3714
|
return $self->__to_der( |
249
|
|
|
|
|
|
|
'ECPrivateKey', |
250
|
|
|
|
|
|
|
ASN1_PRIVATE(), |
251
|
|
|
|
|
|
|
{ |
252
|
|
|
|
|
|
|
version => 1, |
253
|
|
|
|
|
|
|
privateKey => $private_str, |
254
|
|
|
|
|
|
|
parameters => $curve_parts, |
255
|
|
|
|
|
|
|
}, |
256
|
|
|
|
|
|
|
@params, |
257
|
|
|
|
|
|
|
); |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
sub _serialize_sig { |
261
|
242
|
|
|
242
|
|
990
|
my ($self, $r, $s) = @_; |
262
|
|
|
|
|
|
|
|
263
|
242
|
|
|
|
|
2410
|
my $asn1 = Crypt::Perl::ASN1->new()->prepare( $self->ASN1_SIGNATURE() ); |
264
|
242
|
|
|
|
|
1615
|
return $asn1->encode( r => $r, s => $s ); |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
1; |