File Coverage

lib/Crypt/Perl/RSA/PrivateKey.pm
Criterion Covered Total %
statement 86 87 98.8
branch 1 2 50.0
condition 2 3 66.6
subroutine 17 17 100.0
pod 0 6 0.0
total 106 115 92.1


line stmt bran cond sub pod time code
1             package Crypt::Perl::RSA::PrivateKey;
2              
3             =encoding utf-8
4              
5             =head1 NAME
6              
7             Crypt::Perl::RSA::PrivateKey - object representation of an RSA private key
8              
9             =head1 SYNOPSIS
10              
11             #You’ll probably instantiate this class using Parser.pm
12             #or Generate.pm.
13              
14             #cf. JSON Web Algorithms (RFC 7518, page 5)
15             #These return an octet string.
16             $sig = $prkey->sign_RS256($message);
17             $sig = $prkey->sign_RS384($message);
18             $sig = $prkey->sign_RS512($message);
19              
20             #These return 1 or 0 to indicate verification or non-verification.
21             $prkey->verify_RS256($message, $sig);
22             $prkey->verify_RS384($message, $sig);
23             $prkey->verify_RS512($message, $sig);
24              
25             #----------------------------------------------------------------------
26              
27             my $enc = $prkey->encrypt_raw($payload);
28             my $orig = $prkey->decrypt_raw($enc);
29              
30             #----------------------------------------------------------------------
31              
32             my $der = $prkey->to_der();
33             my $pem = $prkey->to_pem();
34              
35             #For use in creating PKCS #10 CSRs and X.509 certificates
36             my $pub_der = $prkey->to_subject_public_der();
37              
38             my $pbkey = $prkey->get_public_key();
39              
40             #----------------------------------------------------------------------
41              
42             $prkey->version(); #scalar, integer
43              
44             $prkey->size(); #modulus length, in bits
45             $prkey->modulus_byte_length();
46              
47             #----------------------------------------------------------------------
48             # The following all return instances of Crypt::Perl::BigInt,
49             # a subclass of Math::BigInt.
50             # The pairs (e.g., modulus() and N()) are aliases.
51             #----------------------------------------------------------------------
52              
53             $prkey->modulus();
54             $prkey->N();
55              
56             $prkey->publicExponent();
57             $prkey->E();
58              
59             $prkey->privateExponent();
60             $prkey->D();
61              
62             $prkey->prime1();
63             $prkey->P();
64              
65             $prkey->prime2();
66             $prkey->Q();
67              
68             $prkey->exponent1();
69             $prkey->DP();
70              
71             $prkey->exponent2();
72             $prkey->DQ();
73              
74             $prkey->coefficient();
75             $prkey->QINV();
76              
77             =cut
78              
79 6     6   552 use strict;
  6         16  
  6         224  
80 6     6   50 use warnings;
  6         16  
  6         320  
81              
82 6         78 use parent qw(
83             Crypt::Perl::RSA::KeyBase
84 6     6   64 );
  6         44  
85              
86 6     6   285 use Module::Load ();
  6         15  
  6         107  
87              
88 6     6   1338 use Crypt::Perl::RNG ();
  6         16  
  6         103  
89 6     6   36 use Crypt::Perl::X ();
  6         12  
  6         120  
90              
91 6     6   27 use constant _PEM_HEADER => 'RSA PRIVATE KEY';
  6         15  
  6         372  
92 6     6   35 use constant _ASN1_MACRO => 'RSAPrivateKey';
  6         23  
  6         969  
93              
94             BEGIN {
95 6     6   74 __PACKAGE__->mk_ro_accessors(
96             qw(
97             version
98             publicExponent
99             privateExponent
100             prime1
101             prime2
102             exponent1
103             exponent2
104             coefficient
105             )
106             );
107              
108 6         9617 *E = \&publicExponent;
109 6         16 *D = \&privateExponent;
110              
111 6         29 *P = \&prime1;
112 6         21 *Q = \&prime2;
113              
114 6         13 *DP = \&exponent1;
115 6         12 *DQ = \&exponent2;
116              
117 6         30 *QINV = \&coefficient;
118              
119 6         6009 *to_subject_public_der = __PACKAGE__->can('_to_subject_public_der');
120             }
121              
122             sub sign_RS256 {
123 262     262 0 795 my ($self, $msg) = @_;
124              
125 262         1216 return $self->_sign($msg, 'Digest::SHA', 'sha256', 'PKCS1_v1_5');
126             }
127              
128             sub sign_RS384 {
129 2     2 0 81 my ($self, $msg) = @_;
130              
131 2         14 return $self->_sign($msg, 'Digest::SHA', 'sha384', 'PKCS1_v1_5');
132             }
133              
134             sub sign_RS512 {
135 4     4 0 804 my ($self, $msg) = @_;
136              
137 4         32 return $self->_sign($msg, 'Digest::SHA', 'sha512', 'PKCS1_v1_5');
138             }
139              
140             sub get_public_key {
141 4     4 0 156 my ($self) = @_;
142              
143 4         1247 require Crypt::Perl::RSA::PublicKey;
144              
145             return Crypt::Perl::RSA::PublicKey->new( {
146             modulus => $self->{'modulus'},
147 4         57 publicExponent => $self->{'publicExponent'},
148             } );
149             }
150              
151             sub get_struct_for_private_jwk {
152 1     1 0 859 my ($self) = @_;
153              
154 1         7 require MIME::Base64;
155              
156 1         5 my $jwk = $self->get_struct_for_public_jwk();
157              
158 1         25 my %augment = qw(
159             d D
160             p P
161             q Q
162             dp DP
163             dq DQ
164             qi QINV
165             );
166              
167 1         5 for my $k (keys %augment) {
168 6         72 my $accessor = $augment{$k};
169 6         192 $jwk->{$k} = MIME::Base64::encode_base64url( $self->$accessor()->as_bytes() );
170             }
171              
172 1         26 return $jwk;
173             }
174              
175             #----------------------------------------------------------------------
176             #This function, in tandem with encrypt_raw(), represents the fundamental
177             #mathematical truth on which RSA rests.
178             #
179              
180             sub decrypt_raw {
181 1     1 0 1131 my ($self, $x) = @_;
182              
183 1         6 $x = Crypt::Perl::BigInt->from_bytes($x);
184              
185             #jsrsasign avoids this when it has P and Q, which we have.
186             #presumably that’s because privateExponent (D) is quite large,
187             #so using it as an exponent is expensive.
188             #return $self->bmodpow($self->{'privateExponent'}, $self->{'modulus'})->as_bytes();
189              
190 1         293 my $p = $self->P();
191 1         28 my $q = $self->Q();
192              
193 1         14 my $p1 = $p->copy()->bdec();
194 1         127 my $q1 = $q->copy()->bdec();
195              
196 1         66 my $xp = $x->copy()->bmod($p)->bmodpow( $self->D()->copy()->bmod($p1), $p );
197 1         576 my $xq = $x->copy()->bmod($q)->bmodpow( $self->D()->copy()->bmod($q1), $q );
198              
199             #$xp->binc($p) while $xp->blt($xq);
200              
201             #return ($xq + ((($xp - $xq) * $self->QINV()) % $p) * $q)->as_bytes();
202              
203 1         514 my $diff = $xp->bsub($xq)->babs()->bmod($p)->bsub($p)->babs();
204              
205 1         369 $diff->bmul($self->QINV())->bmod($p)->bmuladd($q, $xq)->as_bytes();
206             }
207              
208             #----------------------------------------------------------------------
209              
210             sub _sign {
211 268     268   1069 my ($self, $msg, $hash_module, $hasher, $scheme) = @_;
212              
213 268         1034 Module::Load::load($hash_module);
214              
215 268         27125 my $dgst = $hash_module->can($hasher)->($msg);
216              
217 268         690 my $sig;
218              
219 268 50       872 if ($scheme eq 'PKCS1_v1_5') {
220 268         3409 require Crypt::Perl::RSA::PKCS1_v1_5;
221              
222 268         1140 my $sig_length = $self->modulus_byte_length();
223              
224             #The encoded length equals the length, in bytes,
225             #of the key’s modulus.
226 268         1685 my $eb = Crypt::Perl::RSA::PKCS1_v1_5::encode(
227             $dgst,
228             $hasher,
229             $sig_length,
230             );
231              
232             #printf "PERL: %v02x\n", $eb;
233             #print "mod byte length: " . Crypt::Perl::RSA::modulus_byte_length($key_obj) . $/;
234              
235 268         1513 my $x = Crypt::Perl::BigInt->from_hex( unpack 'H*', $eb );
236              
237 268         75535 $sig = $self->_transform($x)->as_bytes();
238              
239 268         2283 substr( $sig, 0, 0 ) = "\0" x ($sig_length - length $sig);
240             }
241             else {
242 0         0 die Crypt::Perl::X::create('Generic', "Unknown RSA signature scheme: “$scheme”");
243             }
244              
245 268         1658 return $sig;
246             }
247              
248             #RSA’s signing operation.
249             #This function is based on _modPow() in forge’s js/rsa.js.
250             #
251             #Returns a BigInt.
252             sub _transform {
253 268     268   1122 my ($self, $x) = @_;
254              
255 268         920 my $key_bytes_length = $self->modulus_byte_length();
256              
257             #cryptographic blinding
258 268         690 my $r;
259 268   66     973 do {
260 11844         7670088 $r = Crypt::Perl::BigInt->from_hex(
261             Crypt::Perl::RNG::bytes_hex( $key_bytes_length ),
262             );
263             } while ($r->bge($self->N())) || ($r->bgcd($self->N())->bne(1));
264              
265 268         306018 $x->bmul( $r->copy()->bmodpow($self->E(), $self->N()) )->bmod($self->N());
266              
267             #calculate xp and xq
268 268         95312 my $xp = $x->copy()->bmod($self->P())->bmodpow($self->DP(), $self->P());
269 268         112629 my $xq = $x->copy()->bmod($self->Q())->bmodpow($self->DQ(), $self->Q());
270              
271             #xp must be larger than xq to avoid signed bit usage
272 268         109057 $xp->badd($self->P()) while $xp->blt($xq);
273              
274 268         20649 my $y = $xp->bsub($xq)->bmul($self->QINV())->bmod($self->P());
275              
276             #$y *= $self->Q();
277             #$y += $xq;
278 268         80470 $y->bmuladd( $self->Q(), $xq );
279              
280             #remove effect of random for cryptographic blinding
281 268         32073 $y->bmul( $r->bmodinv($self->N()) );
282 268         99878 $y->bmod($self->N());
283              
284 268         24996 return $y;
285             }
286              
287             1;