File Coverage

lib/Crypt/Perl/RSA/Generate.pm
Criterion Covered Total %
statement 51 53 96.2
branch 9 12 75.0
condition 3 6 50.0
subroutine 9 9 100.0
pod 0 1 0.0
total 72 81 88.8


line stmt bran cond sub pod time code
1             package Crypt::Perl::RSA::Generate;
2              
3             =encoding utf-8
4              
5             =head1 NAME
6              
7             Crypt::Perl::RSA::Generate - RSA key generation
8              
9             =head1 SYNOPSIS
10              
11             use Crypt::Perl::RSA::Generate ();
12              
13             #$prkey is a Crypt::Perl::RSA::PrivateKey instance.
14             my $prkey = Crypt::Perl::RSA::Generate::create(2048);
15              
16             =head1 DISCUSSION
17              
18             Unfortunately, this is quite slow in Perl—too slow, in fact, if you
19             don’t have either L or L.
20             The logic here will still run under pure Perl, but it’ll take too long
21             to be practical.
22              
23             The current L backend is slated to be replaced
24             with L; once that happens, pure-Perl operation should
25             be much more feasible.
26              
27             =head1 ALTERNATIVES
28              
29             =over 4
30              
31             =item L - probably the fastest way to generate RSA
32             keys in perl. (It relies on XS, so this project can’t use it.)
33              
34             =item Use the C binary L directly,
35             e.g., C. Most *NIX systems can do this.
36              
37             =back
38              
39             NOTE: As of December 2016, L is NOT suitable for key
40             generation because it can only generate keys with up to a 512-bit modulus.
41              
42             =cut
43              
44 1     1   41804 use strict;
  1         11  
  1         46  
45 1     1   25 use warnings;
  1         3  
  1         49  
46              
47 1     1   1183 use Math::ProvablePrime ();
  1         20741  
  1         39  
48              
49 1     1   12 use Crypt::Perl::BigInt ();
  1         4  
  1         15  
50 1     1   566 use Crypt::Perl::RSA::PrivateKey ();
  1         3  
  1         21  
51 1     1   8 use Crypt::Perl::X ();
  1         2  
  1         25  
52              
53 1     1   5 use constant PUBLIC_EXPONENTS => ( 65537, 3 );
  1         2  
  1         477  
54              
55             sub create {
56 50     50 0 9274060 my ($mod_bits, $exp) = @_;
57              
58 50 50       681 die Crypt::Perl::X::create('Generic', "Need modulus length!") if !$mod_bits;
59              
60 50   33     367 $exp ||= (PUBLIC_EXPONENTS())[0];
61              
62 50 50       266 if (!grep { $exp eq $_ } PUBLIC_EXPONENTS()) {
  100         902  
63 0         0 my @allowed = PUBLIC_EXPONENTS();
64 0         0 die Crypt::Perl::X::create('Generic', "Invalid public exponent ($exp); should be one of: [@allowed]");
65             }
66              
67 50         436 my $qs = $mod_bits >> 1;
68 50 50       1950 (ref $exp) or $exp = Crypt::Perl::BigInt->new($exp);
69              
70 50         11527 while (1) {
71 50         549 my ($p, $q, $p1, $q1);
72              
73             #Create a random number, ($mod_bits - $qs) bits long.
74             {
75 63         2540 $p = _get_random_prime($mod_bits - $qs);
76 63         10975 $p1 = $p->copy()->bdec();
77              
78             #($p - 1) needs not to be a multiple of $exp
79 63 100       5459 redo if $p1->copy()->bmod($exp)->is_zero();
80             }
81              
82             {
83 50         223 $q = _get_random_prime($qs);
  50         7069  
  81         5150  
84 81         12688 $q1 = $q->copy()->bdec();
85              
86             #Same restriction as on $p applies to $q.
87             #Let’s also make sure these are two different numbers!
88 81 100 66     7376 redo if $q1->copy()->bmod($exp)->is_zero() || $q->beq($p);
89             }
90              
91             #$p should be > $q
92 50 100       10907 if ($p->blt($q)) {
93 19         682 my $t = $p;
94 19         55 $p = $q;
95 19         43 $q = $t;
96              
97 19         47 $t = $p1;
98 19         66 $p1 = $q1;
99 19         58 $q1 = $t;
100             }
101              
102 50         1409 my $phi = $p1->copy()->bmul($q1);
103              
104 50         3915 my $d = $exp->copy()->bmodinv($phi);
105              
106 50         16397 my $obj = Crypt::Perl::RSA::PrivateKey->new(
107             {
108             version => 0,
109             modulus => $p->copy()->bmul($q),
110             publicExponent => $exp,
111             privateExponent => $d,
112             prime1 => $p,
113             prime2 => $q,
114             exponent1 => $d->copy()->bmod($p1),
115             exponent2 => $d->copy()->bmod($q1),
116             coefficient => $q->copy()->bmodinv($p),
117             },
118             );
119              
120 50         804 return $obj;
121             }
122             }
123              
124             sub _get_random_prime {
125 144     144   1794 my @got = Math::ProvablePrime::find(@_);
126 144         100826191 return Crypt::Perl::BigInt->new(@got);
127             }
128              
129             1;