File Coverage

lib/Crypt/Perl/ECDSA/Parse.pm
Criterion Covered Total %
statement 78 79 98.7
branch 3 4 75.0
condition n/a
subroutine 17 17 100.0
pod 0 3 0.0
total 98 103 95.1


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 6     6   3582 use strict;
  6         19  
  6         189  
26 6     6   33 use warnings;
  6         12  
  6         188  
27              
28 6     6   47 use Try::Tiny;
  6         14  
  6         402  
29              
30 6     6   1355 use Crypt::Perl::ASN1 ();
  6         13  
  6         149  
31 6     6   2142 use Crypt::Perl::PKCS8 ();
  6         16  
  6         135  
32 6     6   1254 use Crypt::Perl::ToDER ();
  6         20  
  6         110  
33 6     6   1361 use Crypt::Perl::ECDSA::ECParameters ();
  6         16  
  6         145  
34 6     6   45 use Crypt::Perl::X ();
  6         15  
  6         6114  
35              
36             sub private {
37 496     496 0 7879691 my ($pem_or_der) = @_;
38              
39 496         8870 require Crypt::Perl::ECDSA::PrivateKey;
40              
41 496         6839 Crypt::Perl::ToDER::ensure_der($pem_or_der);
42              
43 496         3662 my $asn1 = _private_asn1();
44 496         3536 my $asn1_ec = $asn1->find('ECPrivateKey');
45              
46 496         12074 my $struct;
47             try {
48 496     496   39087 $struct = $asn1_ec->decode($pem_or_der);
49             }
50             catch {
51 4     4   70 my $ec_err = $_;
52              
53 4         20 my $asn1_pkcs8 = $asn1->find('PrivateKeyInfo');
54              
55             try {
56 4         317 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 2 50       720 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 2         17 my $asn1_params = $asn1->find('EcpkParameters');
64 2         55 my $params = $asn1_params->decode($pk8_struct->{'privateKeyAlgorithm'}{'parameters'});
65              
66 2         209 $struct = $asn1_ec->decode($pk8_struct->{'privateKey'});
67 2         574 $struct->{'parameters'} = $params;
68             }
69             catch {
70 2         46 die Crypt::Perl::X::create('Generic', "Failed to decode private key as either ECDSA native ($ec_err) or PKCS8 ($_)");
71 4         110 };
72 496         9119 };
73              
74             my $key_parts = {
75             version => $struct->{'version'},
76             private => Crypt::Perl::BigInt->from_bytes($struct->{'privateKey'}),
77 494         3147862 public => Crypt::Perl::BigInt->from_bytes($struct->{'publicKey'}[0]),
78             };
79              
80 494         126233 return Crypt::Perl::ECDSA::PrivateKey->new($key_parts, $struct->{'parameters'});
81             }
82              
83             sub public {
84 27     27 0 107895 my ($pem_or_der) = @_;
85              
86 27         878 require Crypt::Perl::ECDSA::PublicKey;
87              
88 27         207 Crypt::Perl::ToDER::ensure_der($pem_or_der);
89              
90 27         115 my $asn1 = _public_asn1();
91 27         158 my $asn1_ec = $asn1->find('ECPublicKey');
92              
93 27         559 my $struct;
94             try {
95 27     27   2208 $struct = $asn1_ec->decode($pem_or_der);
96             }
97             catch {
98 2     2   89 die Crypt::Perl::X::create('Generic', "Failed to decode input as ECDSA public key ($_)");
99 27         378 };
100              
101             return Crypt::Perl::ECDSA::PublicKey->new(
102             $struct->{'publicKey'}[0],
103 25         98592 $struct->{'keydata'}{'parameters'},
104             );
105             }
106              
107             sub jwk {
108 4     4 0 8653 my ($hr) = @_;
109              
110 4         842 require Crypt::Perl::ECDSA::NIST;
111 4         904 require Crypt::Perl::ECDSA::EC::DB;
112 4         822 require Crypt::Perl::Math;
113 4         944 require MIME::Base64;
114              
115 4         1326 my $curve_name = Crypt::Perl::ECDSA::NIST::get_curve_name_for_nist($hr->{'crv'});
116 4         24 my $curve_hr = Crypt::Perl::ECDSA::EC::DB::get_curve_data_by_name($curve_name);
117              
118 4         24 my $keylen = $curve_hr->{'p'}->bit_length();
119 4         217 my $pub_half_byte_length = Crypt::Perl::Math::ceil( $keylen / 8 );
120              
121 4         20 my $x = MIME::Base64::decode_base64url($hr->{'x'});
122 4         58 my $y = MIME::Base64::decode_base64url($hr->{'y'});
123              
124             #Make sure both halves are the proper length.
125 4         57 substr($_, 0, 0) = ("\0" x ($pub_half_byte_length - length)) for ($x, $y);
126              
127 4         29 my $public = Crypt::Perl::BigInt->from_bytes("\x{04}$x$y");
128              
129 4 100       1324 if ($hr->{'d'}) {
130 2         1078 require Crypt::Perl::ECDSA::PrivateKey;
131 2         886 require Crypt::Perl::JWK;
132              
133             my %args = (
134             version => 1,
135             public => $public,
136 2         16 private => Crypt::Perl::JWK::jwk_num_to_bigint($hr->{'d'}),
137             );
138              
139 2         582 return Crypt::Perl::ECDSA::PrivateKey->new_by_curve_name(\%args, $curve_name);
140             }
141              
142 2         1305 require Crypt::Perl::ECDSA::PublicKey;
143 2         27 return Crypt::Perl::ECDSA::PublicKey->new_by_curve_name( $public, $curve_name);
144             }
145              
146             #----------------------------------------------------------------------
147              
148             sub _private_asn1 {
149 496     496   13236 my $template = join("\n", Crypt::Perl::ECDSA::PrivateKey->ASN1_PRIVATE(), Crypt::Perl::PKCS8::ASN1());
150              
151 496         8163 return Crypt::Perl::ASN1->new()->prepare($template);
152             }
153              
154             sub _public_asn1 {
155 27     27   358 my $template = join("\n", Crypt::Perl::ECDSA::PublicKey->ASN1_PUBLIC(), Crypt::Perl::PKCS8::ASN1());
156              
157 27         227 return Crypt::Perl::ASN1->new()->prepare($template);
158             }
159              
160             1;