File Coverage

blib/lib/Crypt/RSA/Key.pm
Criterion Covered Total %
statement 85 95 89.4
branch 25 36 69.4
condition 15 24 62.5
subroutine 12 12 100.0
pod 2 2 100.0
total 139 169 82.2


line stmt bran cond sub pod time code
1             package Crypt::RSA::Key;
2 12     12   125700 use strict;
  12         32  
  12         297  
3 12     12   56 use warnings;
  12         26  
  12         314  
4              
5             ## Crypt::RSA::Keys
6             ##
7             ## Copyright (c) 2001, Vipul Ved Prakash. All rights reserved.
8             ## This code is free software; you can redistribute it and/or modify
9             ## it under the same terms as Perl itself.
10              
11 12     12   59 use base 'Class::Loader';
  12         39  
  12         4316  
12 12     12   77064 use base 'Crypt::RSA::Errorhandler';
  12         29  
  12         2849  
13 12     12   8701 use Math::Prime::Util qw(random_nbit_prime miller_rabin_random is_frobenius_khashin_pseudoprime);
  12         95496  
  12         62  
14 12     12   4491 use Crypt::RSA::DataFormat qw(bitsize);
  12         43  
  12         616  
15 12     12   72 use Math::BigInt try => 'GMP, Pari';
  12         22  
  12         77  
16 12     12   12868 use Crypt::RSA::Key::Private;
  12         34  
  12         351  
17 12     12   4253 use Crypt::RSA::Key::Public;
  12         30  
  12         256  
18 12     12   71 use Carp;
  12         25  
  12         7775  
19              
20             $Crypt::RSA::Key::VERSION = '1.99';
21              
22             my %MODMAP = (
23             Native_PKF => { Module => "Crypt::RSA::Key::Public" },
24             Native_SKF => { Module => "Crypt::RSA::Key::Private" },
25             SSH_PKF => { Module => "Crypt::RSA::Key::Public::SSH" },
26             SSH_SKF => { Module => "Crypt::RSA::Key::Private::SSH" },
27             );
28              
29              
30             sub new {
31 11     11 1 2133 my $class = shift;
32 11         32 my $self = {};
33 11         29 bless $self, $class;
34 11         185 $self->_storemap ( %MODMAP );
35 11         267 return $self;
36             }
37              
38              
39             sub generate {
40              
41 15     15 1 162 my ($self, %params) = @_;
42              
43 15         38 my $key;
44 15 50 66     89 unless ($params{q} && $params{p} && $params{e}) {
      33        
45              
46 14 50       70 return $self->error ("Missing argument.") unless $params{Size};
47              
48             return $self->error ("Keysize too small.") if
49 14 50       71 $params{Size} < 48;
50              
51             return $self->error ("Odd keysize.") if
52 14 50       57 $params{Size} % 2;
53              
54 14         70 my $size = int($params{Size}/2);
55 14   100     94 my $verbosity = $params{Verbosity} || 0;
56              
57             # Switch from Maurer prime to nbit prime, then add some more primality
58             # testing. This is faster and gives us a wider set of possible primes.
59              
60             # We really ought to consider the distribution. See:
61             # https://crocs.fi.muni.cz/_media/public/papers/usenixsec16_1mrsakeys_trfimu_201603.pdf
62             # for comments on p/q selection.
63              
64 14         33 while (1) {
65 29         2342 my $p = random_nbit_prime($size);
66 29         475093182 my $q = random_nbit_prime($size);
67 29 100       680530527 $p = Math::BigInt->new("$p") unless ref($p) eq 'Math::BigInt';
68 29 100       663 $q = Math::BigInt->new("$q") unless ref($q) eq 'Math::BigInt';
69              
70             # For unbiased rejection sampling, generate both p/q if size too small.
71 29 100       482 next unless bitsize($p * $q) == $params{Size};
72              
73             # Verify primes aren't too close together.
74 14 100       84 if ($params{Size} >= 256) {
75 11         52 my $threshold = Math::BigInt->new(2)->bpow($params{Size}/2 - 100);
76 11         5877 my $diff = $p->copy->bsub($q)->babs;
77 11 50       2005 next if $diff <= $threshold;
78             }
79              
80             # We could check p-1 and q-1 smoothness.
81              
82             # p and q have passed the strong BPSW test, so it would be shocking
83             # if they were not prime. We'll add a few more tests because they're
84             # cheap and we want to be extra careful, but also don't want to spend
85             # the time doing a full primality proof.
86              
87 14 50       687 do { carp "$p passes BPSW but fails Frobenius test!"; next; }
  0         0  
  0         0  
88             unless is_frobenius_khashin_pseudoprime($p);
89 14 50       32104300 do { carp "$q passes BPSW but fails Frobenius test!"; next; }
  0         0  
  0         0  
90             unless is_frobenius_khashin_pseudoprime($q);
91              
92 14 50       32210025 do { carp "$p fails Miller-Rabin testing!"; next; }
  0         0  
  0         0  
93             unless miller_rabin_random($p,3);
94 14 50       18475282 do { carp "$q fails Miller-Rabin testing!"; next; }
  0         0  
  0         0  
95             unless miller_rabin_random($q,3);
96              
97 14         19153359 $key = { p => $p, q => $q, e => Math::BigInt->new(65537) };
98 14         929 last;
99             }
100             }
101              
102 15 100       89 if ($params{KF}) {
103 4         20 $params{PKF} = { Name => "$params{KF}_PKF" };
104 4         17 $params{SKF} = { Name => "$params{KF}_SKF" }
105             }
106              
107 15 100       82 my $pubload = $params{PKF} ? $params{PKF} : { Name => "Native_PKF" };
108 15 100       70 my $priload = $params{SKF} ? $params{SKF} : { Name => "Native_SKF" };
109              
110 15   50     209 my $pubkey = $self->_load (%$pubload) ||
111             return $self->error ("Couldn't load the public key module: $@");
112 15   50     272 my $prikey = $self->_load ((%$priload), Args => ['Cipher' => $params{Cipher}, 'Password' => $params{Password} ]) ||
113             return $self->error ("Couldn't load the private key module: $@");
114 15         315 $pubkey->Identity ($params{Identity});
115 15         103 $prikey->Identity ($params{Identity});
116              
117 15   66     82 $pubkey->e ($$key{e} || $params{e});
118 15   66     62 $prikey->e ($$key{e} || $params{e});
119 15   66     61 $prikey->p ($$key{p} || $params{p});
120 15   66     67 $prikey->q ($$key{q} || $params{q});
121              
122 15         76 $prikey->phi ( ($prikey->p - 1) * ($prikey->q - 1) );
123              
124 15         114 $prikey->d ( ($pubkey->e)->copy->bmodinv($prikey->phi) );
125 15         80 $prikey->n ( $prikey->p * $prikey->q );
126 15         75 $pubkey->n ( $prikey->n );
127              
128 15         77 $prikey->dp ($prikey->d % ($prikey->p - 1));
129 15         93 $prikey->dq ($prikey->d % ($prikey->q - 1));
130 15         93 $prikey->u ( ($prikey->p)->copy->bmodinv($prikey->q) );
131              
132 15 50       96 return $self->error ("d is too small. Regenerate.") if
133             bitsize($prikey->d) < 0.25 * bitsize($prikey->n);
134              
135 15         60 $$key{p} = 0; $$key{q} = 0; $$key{e} = 0;
  15         39  
  15         42  
136              
137 15 50       76 if ($params{Filename}) {
138 0         0 $pubkey->write (Filename => "$params{Filename}.public");
139 0         0 $prikey->write (Filename => "$params{Filename}.private");
140             }
141              
142 15         183 return ($pubkey, $prikey);
143              
144             }
145              
146             1;
147              
148             =head1 NAME
149              
150             Crypt::RSA::Key - RSA Key Pair Generator.
151              
152             =head1 SYNOPSIS
153              
154             my $keychain = new Crypt::RSA::Key;
155             my ($public, $private) = $keychain->generate (
156             Identity => 'Lord Macbeth ',
157             Size => 2048,
158             Password => 'A day so foul & fair',
159             Verbosity => 1,
160             ) or die $keychain->errstr();
161              
162             =head1 DESCRIPTION
163              
164             This module provides a method to generate an RSA key pair.
165              
166             =head1 METHODS
167              
168             =head2 new()
169              
170             Constructor.
171              
172             =head2 generate()
173              
174             generate() generates an RSA key of specified bitsize. It returns a list of
175             two elements, a Crypt::RSA::Key::Public object that holds the public part
176             of the key pair and a Crypt::RSA::Key::Private object that holds that
177             private part. On failure, it returns undef and sets $self->errstr to
178             appropriate error string. generate() takes a hash argument with the
179             following keys:
180              
181             =over 4
182              
183             =item B
184              
185             Bitsize of the key to be generated. This should be an even integer > 48.
186             Bitsize is a mandatory argument.
187              
188             =item B
189              
190             String with which the private key will be encrypted. If Password is not
191             provided the key will be stored unencrypted.
192              
193             =item B
194              
195             A string that identifies the owner of the key. This string usually takes
196             the form of a name and an email address. The identity is not bound to the
197             key with a signature. However, a future release or another module will
198             provide this facility.
199              
200             =item B
201              
202             The block cipher which is used for encrypting the private key. Defaults to
203             `Blowfish'. Cipher could be set to any value that works with Crypt::CBC(3)
204             and Tie::EncryptedHash(3).
205              
206             =item B
207              
208             When set to 1, generate() will draw a progress display on STDOUT.
209              
210             =item B
211              
212             The generated key pair will be written to disk, in $Filename.public and
213             $Filename.private files, if this argument is provided. Disk writes can be
214             deferred by skipping this argument and achieved later with the write()
215             method of Crypt::RSA::Key::Public(3) and Crypt::RSA::Key::Private(3).
216              
217             =item B
218              
219             A string that specifies the key format. As of this writing, two key
220             formats, `Native' and `SSH', are supported. KF defaults to `Native'.
221              
222             =item B
223              
224             Secret (Private) Key Format. Instead of specifying KF, the user could
225             choose to specify secret and public key formats separately. The value for
226             SKF can be a string ("Native" or "SSH") or a hash reference that specifies
227             a module name, its constructor and constructor arguments. The specified
228             module is loaded with Class::Loader(3) and must be interface compatible
229             with Crypt::RSA::Key::Private(3).
230              
231             =item B
232              
233             Public Key Format. This option is like SKF but for the public key.
234              
235             =back
236              
237             =head1 ERROR HANDLING
238              
239             See B in Crypt::RSA(3) manpage.
240              
241             =head1 BUGS
242              
243             There's an inefficiency in the way generate() ensures the key pair is
244             exactly Size bits long. This will be fixed in a future release.
245              
246             =head1 AUTHOR
247              
248             Vipul Ved Prakash, Email@vipul.netE
249              
250             =head1 SEE ALSO
251              
252             Crypt::RSA(3), Crypt::RSA::Key::Public(3), Crypt::RSA::Key::Private(3),
253             Tie::EncryptedHash(3), Class::Loader(3),
254             Math::Prime::Util(3)
255              
256             =cut
257              
258