File Coverage

lib/Crypt/Perl/ECDSA/Parse.pm
Criterion Covered Total %
statement 77 89 86.5
branch 3 6 50.0
condition n/a
subroutine 16 17 94.1
pod 0 3 0.0
total 96 115 83.4


line stmt bran cond sub pod time code
1             package Crypt::Perl::ECDSA::Parse;
2              
3             =encoding utf-8
4              
5             =head1 NAME
6              
7             Crypt::Perl::ECDSA::Parse - ECDSA key parsing
8              
9             =head1 SYNOPSIS
10              
11             use Crypt::Perl::ECDSA::Parse ();
12              
13             #These accept either DER or PEM, native format or PKCS8.
14             #
15             my $prkey = Crypt::Perl::ECDSA::Parse::private($buffer);
16             my $pbkey = Crypt::Perl::ECDSA::Parse::public($buffer);
17              
18             =head1 DISCUSSION
19              
20             See L and L
21             for descriptions of the interfaces of these two classes.
22              
23             =cut
24              
25 5     5   150427 use strict;
  5         31  
  5         132  
26 5     5   26 use warnings;
  5         17  
  5         136  
27              
28 5     5   25 use Try::Tiny;
  5         9  
  5         286  
29              
30 5     5   1060 use Crypt::Perl::ASN1 ();
  5         11  
  5         97  
31 5     5   1154 use Crypt::Perl::PKCS8 ();
  5         12  
  5         71  
32 5     5   1068 use Crypt::Perl::ToDER ();
  5         33  
  5         80  
33 5     5   1100 use Crypt::Perl::ECDSA::ECParameters ();
  5         13  
  5         99  
34 5     5   25 use Crypt::Perl::X ();
  5         20  
  5         4928  
35              
36             sub private {
37 493     493 0 2907504 my ($pem_or_der) = @_;
38              
39 493         6295 require Crypt::Perl::ECDSA::PrivateKey;
40              
41 493         4416 Crypt::Perl::ToDER::ensure_der($pem_or_der);
42              
43 493         2649 my $asn1 = _private_asn1();
44 493         3162 my $asn1_ec = $asn1->find('ECPrivateKey');
45              
46 493         9366 my $struct;
47             try {
48 493     493   31147 $struct = $asn1_ec->decode($pem_or_der);
49             }
50             catch {
51 2     2   28 my $ec_err = $_;
52              
53 2         10 my $asn1_pkcs8 = $asn1->find('PrivateKeyInfo');
54              
55             try {
56 2         104 my $pk8_struct = $asn1_pkcs8->decode($pem_or_der);
57              
58             #It still might succeed, even if this is wrong, so don’t die().
59 1 50       269 if ( $pk8_struct->{'privateKeyAlgorithm'}{'algorithm'} ne Crypt::Perl::ECDSA::ECParameters::OID_ecPublicKey() ) {
60 0         0 warn "Unknown private key algorithm OID: “$pk8_struct->{'privateKeyAlgorithm'}{'algorithm'}”";
61             }
62              
63 1         4 my $asn1_params = $asn1->find('EcpkParameters');
64 1         14 my $params = $asn1_params->decode($pk8_struct->{'privateKeyAlgorithm'}{'parameters'});
65              
66 1         85 $struct = $asn1_ec->decode($pk8_struct->{'privateKey'});
67 1         227 $struct->{'parameters'} = $params;
68             }
69             catch {
70 1         29 die Crypt::Perl::X::create('Generic', "Failed to decode private key as either ECDSA native ($ec_err) or PKCS8 ($_)");
71 2         47 };
72 493         8017 };
73              
74             my $key_parts = {
75             version => $struct->{'version'},
76             private => Crypt::Perl::BigInt->from_bytes($struct->{'privateKey'}),
77 492         2604321 public => Crypt::Perl::BigInt->from_bytes($struct->{'publicKey'}[0]),
78             };
79              
80 492         793151 return Crypt::Perl::ECDSA::PrivateKey->new($key_parts, $struct->{'parameters'});
81             }
82              
83             sub public {
84 13     13 0 54386 my ($pem_or_der) = @_;
85              
86 13         132 require Crypt::Perl::ECDSA::PublicKey;
87              
88 13         69 Crypt::Perl::ToDER::ensure_der($pem_or_der);
89              
90 13         72 my $asn1 = _public_asn1();
91 13         169 my $asn1_ec = $asn1->find('ECPublicKey');
92              
93 13         226 my $struct;
94             try {
95 13     13   1213 $struct = $asn1_ec->decode($pem_or_der);
96             }
97             catch {
98 0     0   0 my $ec_err = $_;
99              
100 0         0 my $asn1_pkcs8 = $asn1->find('SubjectPublicKeyInfo');
101              
102             try {
103 0         0 my $spk_struct = $asn1_pkcs8->decode($pem_or_der);
104              
105             #It still might succeed, even if this is wrong, so don’t die().
106 0 0       0 if ( $spk_struct->{'algorithm'}{'algorithm'} ne Crypt::Perl::ECDSA::ECParameters::OID_ecPublicKey() ) {
107 0         0 warn "Unknown private key algorithm OID: “$spk_struct->{'algorithm'}{'algorithm'}”";
108             }
109              
110 0         0 my $asn1_params = $asn1->find('EcpkParameters');
111 0         0 my $params = $asn1_params->decode($spk_struct->{'algorithm'}{'parameters'});
112              
113 0         0 $struct = { publicKey => $spk_struct->{'subjectPublicKey'} };
114 0         0 $struct->{'keydata'}{'parameters'} = $params;
115             }
116             catch {
117 0         0 die Crypt::Perl::X::create('Generic', "Failed to decode public key as either ECDSA native ($ec_err) or SubjectPublicKeyInfo ($_)");
118 0         0 };
119 13         192 };
120              
121             return Crypt::Perl::ECDSA::PublicKey->new(
122             $struct->{'publicKey'}[0],
123 13         64449 $struct->{'keydata'}{'parameters'},
124             );
125             }
126              
127             sub jwk {
128 4     4 0 6137 my ($hr) = @_;
129              
130 4         739 require Crypt::Perl::ECDSA::NIST;
131 4         777 require Crypt::Perl::ECDSA::EC::DB;
132 4         690 require Crypt::Perl::Math;
133 4         838 require MIME::Base64;
134              
135 4         1048 my $curve_name = Crypt::Perl::ECDSA::NIST::get_curve_name_for_nist($hr->{'crv'});
136 4         16 my $curve_hr = Crypt::Perl::ECDSA::EC::DB::get_curve_data_by_name($curve_name);
137              
138 4         33 my $keylen = $curve_hr->{'p'}->bit_length();
139 4         6004 my $pub_half_byte_length = Crypt::Perl::Math::ceil( $keylen / 8 );
140              
141 4         16 my $x = MIME::Base64::decode_base64url($hr->{'x'});
142 4         40 my $y = MIME::Base64::decode_base64url($hr->{'y'});
143              
144             #Make sure both halves are the proper length.
145 4         40 substr($_, 0, 0) = ("\0" x ($pub_half_byte_length - length)) for ($x, $y);
146              
147 4         22 my $public = Crypt::Perl::BigInt->from_bytes("\x{04}$x$y");
148              
149 4 100       14865 if ($hr->{'d'}) {
150 2         901 require Crypt::Perl::ECDSA::PrivateKey;
151 2         721 require Crypt::Perl::JWK;
152              
153             my %args = (
154             version => 1,
155             public => $public,
156 2         25 private => Crypt::Perl::JWK::jwk_num_to_bigint($hr->{'d'}),
157             );
158              
159 2         3068 return Crypt::Perl::ECDSA::PrivateKey->new_by_curve_name(\%args, $curve_name);
160             }
161              
162 2         1098 require Crypt::Perl::ECDSA::PublicKey;
163 2         21 return Crypt::Perl::ECDSA::PublicKey->new_by_curve_name( $public, $curve_name);
164             }
165              
166             #----------------------------------------------------------------------
167              
168             sub _private_asn1 {
169 493     493   11698 my $template = join("\n", Crypt::Perl::ECDSA::PrivateKey->ASN1_PRIVATE(), Crypt::Perl::PKCS8::ASN1());
170              
171 493         6490 return Crypt::Perl::ASN1->new()->prepare($template);
172             }
173              
174             sub _public_asn1 {
175 13     13   221 my $template = join("\n", Crypt::Perl::ECDSA::PublicKey->ASN1_PUBLIC(), Crypt::Perl::PKCS8::ASN1());
176              
177 13         84 return Crypt::Perl::ASN1->new()->prepare($template);
178             }
179              
180             1;