File Coverage

lib/Crypt/Perl/RSA/Parse.pm
Criterion Covered Total %
statement 101 101 100.0
branch 6 6 100.0
condition n/a
subroutine 32 32 100.0
pod 0 5 0.0
total 139 144 96.5


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/SPKI.
14             my $prkey = Crypt::Perl::RSA::Parse::private($buffer);
15             my $pbkey = Crypt::Perl::RSA::Parse::public($buffer);
16              
17             # Note that this accepts a structure, not raw JSON.
18             my $key = Crypt::Perl::RSA::Parse::jwk($jwk_hr);
19              
20             =head1 DISCUSSION
21              
22             See L and L
23             for descriptions of the interfaces that this module returns.
24              
25             =cut
26              
27 7     7   10977 use strict;
  7         39  
  7         450  
28 7     7   78 use warnings;
  7         16  
  7         342  
29              
30 7     7   45 use Try::Tiny;
  7         19  
  7         792  
31              
32 7     7   1429 use Crypt::Format ();
  7         1975  
  7         242  
33              
34 7     7   1258 use Crypt::Perl::ASN1 ();
  7         26  
  7         193  
35 7     7   3738 use Crypt::Perl::RSA::Template ();
  7         28  
  7         189  
36 7     7   62 use Crypt::Perl::X ();
  7         18  
  7         10243  
37              
38             sub _asn1 {
39 337     337   1874 return Crypt::Perl::ASN1->new()->prepare(
40             Crypt::Perl::RSA::Template::get_template('INTEGER'),
41             );
42             }
43              
44             sub private {
45 287     287 0 171615 my ( $pem_or_der) = @_;
46              
47 287         1450 _ensure_der($pem_or_der);
48              
49 287         576 my $key_obj;
50              
51             try {
52 287     287   27396 my $parsed = _decode_rsa($pem_or_der);
53 273         23874220 $key_obj = _new_private($parsed);
54             }
55             catch {
56 14     14   294 my $rsa_err = $_;
57              
58             try {
59 14         1351 $key_obj = private_pkcs8($pem_or_der);
60             }
61             catch {
62 12         378 die Crypt::Perl::X::create('Generic', "Failed to parse as either RSA ($rsa_err) or PKCS8 ($_)");
63 14         166 };
64 287         3510 };
65              
66 275         8262 return $key_obj;
67             }
68              
69             #Like private(), but only does PKCS8.
70             sub private_pkcs8 {
71 16     16 0 77 my ($pem_or_der) = @_;
72              
73 16         78 _ensure_der($pem_or_der);
74              
75 16         113 my $pkcs8 = _decode_pkcs8($pem_or_der);
76              
77 6         2346 my $parsed = _decode_rsa_within_pkcs8_or_die($pkcs8);
78              
79 4         28 return _new_private($parsed);
80             }
81              
82             #Checks for RSA format first, then falls back to PKCS8.
83             sub public {
84 14     14 0 1847 my ($pem_or_der) = @_;
85              
86 14         67 _ensure_der($pem_or_der);
87              
88 14         51 my $key_obj;
89              
90             try {
91 14     14   1100 my $parsed = _decode_rsa_public($pem_or_der);
92 2         45055 $key_obj = _new_public($parsed);
93             }
94             catch {
95 12     12   230 my $rsa_err = $_;
96              
97             try {
98 12         1106 $key_obj = public_SPKI($pem_or_der);
99             }
100             catch {
101 11         242 die Crypt::Perl::X::create('Generic', "Failed to parse as either RSA ($rsa_err) or SubjectPublicKeyInfo ($_)");
102 12         180 };
103 14         202 };
104              
105 3         84 return $key_obj;
106             }
107              
108             #Like public(), but only does SubjectPublicKeyInfo.
109             sub public_SPKI {
110 12     12 0 54 my ($pem_or_der) = @_;
111              
112 12         45 _ensure_der($pem_or_der);
113              
114 12         79 my $spki = _decode_spki($pem_or_der);
115              
116 2         626 my $parsed = _decode_rsa_public_within_spki_or_die($spki);
117              
118 1         5 return _new_public($parsed);
119             }
120              
121             my %JTK_TO_NEW = qw(
122             n modulus
123             e publicExponent
124             d privateExponent
125             p prime1
126             q prime2
127             dp exponent1
128             dq exponent2
129             qi coefficient
130             );
131              
132             sub jwk {
133 4     4 0 8451 my ($hr) = @_;
134              
135 4         8 my %constr_args;
136              
137 4         477 require Crypt::Perl::JWK;
138              
139 4         37 for my $k (keys %$hr) {
140 24 100       4503 next if !$JTK_TO_NEW{$k};
141 20         66 $constr_args{ $JTK_TO_NEW{$k} } = Crypt::Perl::JWK::jwk_num_to_bigint($hr->{$k});
142             }
143              
144 4 100       946 if ($hr->{'d'}) {
145 2         6 $constr_args{'version'} = 0;
146 2         979 require Crypt::Perl::RSA::PrivateKey;
147 2         18 return Crypt::Perl::RSA::PrivateKey->new( \%constr_args );
148             }
149              
150 2         1094 require Crypt::Perl::RSA::PublicKey;
151 2         25 return Crypt::Perl::RSA::PublicKey->new( \%constr_args );
152             }
153              
154             #----------------------------------------------------------------------
155              
156             sub _decode_macro {
157 337     337   1069 my ( $der_r, $macro ) = ( \$_[0], $_[1] );
158              
159 337         1070 my $parser = _asn1()->find($macro);
160              
161 337         8405 return $parser->decode($$der_r);
162             }
163              
164             #Checks for RSA format first, then falls back to PKCS8.
165             sub _decode_rsa {
166 293     293   791 my ($der_r) = (\$_[0]);
167              
168 293         1089 return _decode_macro( $$der_r, 'RSAPrivateKey' );
169             }
170              
171             sub _decode_rsa_public {
172 16     16   50 my ($der_r) = (\$_[0]);
173              
174 16         75 return _decode_macro( $$der_r, 'RSAPublicKey' );
175             }
176              
177             sub _decode_rsa_within_pkcs8_or_die {
178 6     6   18 my ($pkcs8_hr) = @_;
179              
180 6         14 my $dec;
181             try {
182 6     6   649 $dec = _decode_rsa( $pkcs8_hr->{'privateKey'} );
183             }
184             catch {
185 2     2   68 die Crypt::Perl::X::create('Generic', "Failed to parse RSA within PKCS8: $_");
186 6         89 };
187              
188 4         406413 return $dec;
189             }
190              
191             sub _decode_rsa_public_within_spki_or_die {
192 2     2   6 my ($spki_hr) = @_;
193              
194 2         5 my $dec;
195             try {
196 2     2   157 $dec = _decode_rsa_public( $spki_hr->{'subjectPublicKey'}[0] );
197             }
198             catch {
199 1     1   20 die Crypt::Perl::X::create('Generic', "Failed to parse RSA within SubjectPublicKeyInfo: $_");
200 2         20 };
201              
202 1         15668 return $dec;
203             }
204              
205             sub _decode_pkcs8 {
206 16     16   65 my ($der_r) = (\$_[0]);
207              
208 16         61 return _decode_macro( $$der_r, 'PrivateKeyInfo' );
209             }
210              
211             sub _decode_spki {
212 12     12   34 my ($der_r) = (\$_[0]);
213              
214 12         50 return _decode_macro( $$der_r, 'SubjectPublicKeyInfo' );
215             }
216              
217             sub _new_public {
218 3     3   11 my ($parsed_hr) = @_;
219              
220 3         24 require Crypt::Perl::RSA::PublicKey;
221 3         29 return Crypt::Perl::RSA::PublicKey->new($parsed_hr);
222             }
223              
224             sub _new_private {
225 277     277   882 my ($parsed_hr) = @_;
226              
227 277         4340 require Crypt::Perl::RSA::PrivateKey;
228 277         2241 return Crypt::Perl::RSA::PrivateKey->new($parsed_hr);
229             }
230              
231             #Modifies in-place.
232             sub _pem_to_der {
233 301     301   1587 $_[0] = Crypt::Format::pem2der(@_);
234              
235 301         30002 return;
236             }
237              
238             sub _ensure_der {
239 329     329   856 my ($pem_or_der_r) = \$_[0];
240              
241 329 100       2189 if ( $$pem_or_der_r =~ m<\A-> ) {
242 301         973 _pem_to_der($$pem_or_der_r);
243             }
244              
245 329         751 return;
246             }
247              
248             1;