File Coverage

lib/Crypt/Perl/RSA/Parse.pm
Criterion Covered Total %
statement 100 101 99.0
branch 6 6 100.0
condition n/a
subroutine 31 32 96.8
pod 0 5 0.0
total 137 144 95.1


line stmt bran cond sub pod time code
1             package Crypt::Perl::RSA::Parse;
2              
3             =encoding utf-8
4              
5             =head1 NAME
6              
7             Crypt::Perl::RSA::Parse - RSA key parsing
8              
9             =head1 SYNOPSIS
10              
11             use Crypt::Perl::RSA::Parse ();
12              
13             #These accept either DER or PEM, native format or PKCS8.
14             #
15             my $prkey = Crypt::Perl::RSA::Parse::private($buffer);
16             my $pbkey = Crypt::Perl::RSA::Parse::public($buffer);
17              
18             =head1 DISCUSSION
19              
20             See L and L
21             for descriptions of the interfaces that this module returns.
22              
23             =cut
24              
25 6     6   127025 use strict;
  6         25  
  6         191  
26 6     6   137 use warnings;
  6         20  
  6         217  
27              
28 6     6   34 use Try::Tiny;
  6         20  
  6         490  
29              
30 6     6   926 use Crypt::Format ();
  6         977  
  6         130  
31              
32 6     6   1089 use Crypt::Perl::ASN1 ();
  6         23  
  6         128  
33 6     6   2355 use Crypt::Perl::RSA::Template ();
  6         20  
  6         130  
34 6     6   39 use Crypt::Perl::X ();
  6         16  
  6         7534  
35              
36             sub _asn1 {
37 323     323   1986 return Crypt::Perl::ASN1->new()->prepare(
38             Crypt::Perl::RSA::Template::get_template('INTEGER'),
39             );
40             }
41              
42             sub private {
43 284     284 0 151458 my ( $pem_or_der) = @_;
44              
45 284         1398 _ensure_der($pem_or_der);
46              
47 284         592 my $key_obj;
48              
49             try {
50 284     284   24569 my $parsed = _decode_rsa($pem_or_der);
51 273         19427948 $key_obj = _new_private($parsed);
52             }
53             catch {
54 11     11   187 my $rsa_err = $_;
55              
56             try {
57 11         878 $key_obj = private_pkcs8($pem_or_der);
58             }
59             catch {
60 9         275 die Crypt::Perl::X::create('Generic', "Failed to parse as either RSA ($rsa_err) or PKCS8 ($_)");
61 11         113 };
62 284         3837 };
63              
64 275         7155 return $key_obj;
65             }
66              
67             #Like private(), but only does PKCS8.
68             sub private_pkcs8 {
69 13     13 0 55 my ($pem_or_der) = @_;
70              
71 13         49 _ensure_der($pem_or_der);
72              
73 13         57 my $pkcs8 = _decode_pkcs8($pem_or_der);
74              
75 4         1341 my $parsed = _decode_rsa_within_pkcs8_or_die($pkcs8);
76              
77 4         51 return _new_private($parsed);
78             }
79              
80             #Checks for RSA format first, then falls back to PKCS8.
81             sub public {
82 11     11 0 1554 my ($pem_or_der) = @_;
83              
84 11         44 _ensure_der($pem_or_der);
85              
86 11         50 my $key_obj;
87              
88             try {
89 11     11   685 my $parsed = _decode_rsa_public($pem_or_der);
90 2         39563 $key_obj = _new_public($parsed);
91             }
92             catch {
93 9     9   115 my $rsa_err = $_;
94              
95             try {
96 9         654 $key_obj = public_SPKI($pem_or_der);
97             }
98             catch {
99 8         127 die Crypt::Perl::X::create('Generic', "Failed to parse as either RSA ($rsa_err) or SubjectPublicKeyInfo ($_)");
100 9         70 };
101 11         152 };
102              
103 3         67 return $key_obj;
104             }
105              
106             #Like public(), but only does SubjectPublicKeyInfo.
107             sub public_SPKI {
108 9     9 0 30 my ($pem_or_der) = @_;
109              
110 9         43 _ensure_der($pem_or_der);
111              
112 9         38 my $spki = _decode_spki($pem_or_der);
113              
114 2         513 my $parsed = _decode_rsa_public_within_spki_or_die($spki);
115              
116 1         5 return _new_public($parsed);
117             }
118              
119             my %JTK_TO_NEW = qw(
120             n modulus
121             e publicExponent
122             d privateExponent
123             p prime1
124             q prime2
125             dp exponent1
126             dq exponent2
127             qi coefficient
128             );
129              
130             sub jwk {
131 4     4 0 5871 my ($hr) = @_;
132              
133 4         11 my %constr_args;
134              
135 4         379 require Crypt::Perl::JWK;
136              
137 4         22 for my $k (keys %$hr) {
138 24 100       31513 next if !$JTK_TO_NEW{$k};
139 20         51 $constr_args{ $JTK_TO_NEW{$k} } = Crypt::Perl::JWK::jwk_num_to_bigint($hr->{$k});
140             }
141              
142 4 100       4908 if ($hr->{'d'}) {
143 2         5 $constr_args{'version'} = 0;
144 2         1094 require Crypt::Perl::RSA::PrivateKey;
145 2         25 return Crypt::Perl::RSA::PrivateKey->new( \%constr_args );
146             }
147              
148 2         1182 require Crypt::Perl::RSA::PublicKey;
149 2         18 return Crypt::Perl::RSA::PublicKey->new( \%constr_args );
150             }
151              
152             #----------------------------------------------------------------------
153              
154             sub _decode_macro {
155 323     323   1111 my ( $der_r, $macro ) = ( \$_[0], $_[1] );
156              
157 323         1190 my $parser = _asn1()->find($macro);
158              
159 323         6693 return $parser->decode($$der_r);
160             }
161              
162             #Checks for RSA format first, then falls back to PKCS8.
163             sub _decode_rsa {
164 288     288   818 my ($der_r) = (\$_[0]);
165              
166 288         1064 return _decode_macro( $$der_r, 'RSAPrivateKey' );
167             }
168              
169             sub _decode_rsa_public {
170 13     13   34 my ($der_r) = (\$_[0]);
171              
172 13         47 return _decode_macro( $$der_r, 'RSAPublicKey' );
173             }
174              
175             sub _decode_rsa_within_pkcs8_or_die {
176 4     4   12 my ($pkcs8_hr) = @_;
177              
178 4         8 my $dec;
179             try {
180 4     4   337 $dec = _decode_rsa( $pkcs8_hr->{'privateKey'} );
181             }
182             catch {
183 0     0   0 die Crypt::Perl::X::create('Generic', "Failed to parse RSA within PKCS8: $_");
184 4         58 };
185              
186 4         347097 return $dec;
187             }
188              
189             sub _decode_rsa_public_within_spki_or_die {
190 2     2   4 my ($spki_hr) = @_;
191              
192 2         3 my $dec;
193             try {
194 2     2   152 $dec = _decode_rsa_public( $spki_hr->{'subjectPublicKey'}[0] );
195             }
196             catch {
197 1     1   13 die Crypt::Perl::X::create('Generic', "Failed to parse RSA within SubjectPublicKeyInfo: $_");
198 2         25 };
199              
200 1         13434 return $dec;
201             }
202              
203             sub _decode_pkcs8 {
204 13     13   35 my ($der_r) = (\$_[0]);
205              
206 13         44 return _decode_macro( $$der_r, 'PrivateKeyInfo' );
207             }
208              
209             sub _decode_spki {
210 9     9   21 my ($der_r) = (\$_[0]);
211              
212 9         30 return _decode_macro( $$der_r, 'SubjectPublicKeyInfo' );
213             }
214              
215             sub _new_public {
216 3     3   11 my ($parsed_hr) = @_;
217              
218 3         20 require Crypt::Perl::RSA::PublicKey;
219 3         18 return Crypt::Perl::RSA::PublicKey->new($parsed_hr);
220             }
221              
222             sub _new_private {
223 277     277   1024 my ($parsed_hr) = @_;
224              
225 277         3936 require Crypt::Perl::RSA::PrivateKey;
226 277         2137 return Crypt::Perl::RSA::PrivateKey->new($parsed_hr);
227             }
228              
229             #Modifies in-place.
230             sub _pem_to_der {
231 295     295   1836 $_[0] = Crypt::Format::pem2der(@_);
232              
233 295         20778 return;
234             }
235              
236             sub _ensure_der {
237 317     317   955 my ($pem_or_der_r) = \$_[0];
238              
239 317 100       1982 if ( $$pem_or_der_r =~ m<\A-> ) {
240 295         1087 _pem_to_der($$pem_or_der_r);
241             }
242              
243 317         599 return;
244             }
245              
246             1;