File Coverage

blib/lib/Crypt/DSA/KeyChain.pm
Criterion Covered Total %
statement 118 125 94.4
branch 30 50 60.0
condition 5 12 41.6
subroutine 15 15 100.0
pod 3 3 100.0
total 171 205 83.4


line stmt bran cond sub pod time code
1             package Crypt::DSA::KeyChain;
2              
3 5     5   33 use strict;
  5         11  
  5         268  
4 5     5   8886 use Math::BigInt 1.78 try => 'GMP, Pari';
  5         308433  
  5         33  
5 5     5   180292 use Digest::SHA qw( sha1 );
  5         11  
  5         389  
6 5     5   42 use Carp qw( croak );
  5         9  
  5         281  
7 5     5   3217 use IPC::Open3;
  5         29240  
  5         382  
8 5     5   41 use File::Spec;
  5         12  
  5         156  
9 5     5   2889 use File::Which ();
  5         8145  
  5         186  
10 5     5   36 use Symbol qw( gensym );
  5         16  
  5         401  
11              
12             our $VERSION = '1.19'; #VERSION
13              
14 5     5   36 use vars qw{$VERSION};
  5         19  
  5         276  
15              
16 5     5   2833 use Crypt::DSA::Key;
  5         19  
  5         323  
17 5     5   39 use Crypt::DSA::Util qw( bin2mp bitsize mod_exp makerandom isprime );
  5         10  
  5         8397  
18              
19             sub new {
20 2     2 1 6 my $class = shift;
21 2         22 bless { @_ }, $class;
22             }
23              
24             sub generate_params {
25 1     1 1 2 my $keygen = shift;
26 1         6 my %param = @_;
27 1         10 my $bits = Math::BigInt->new($param{Size});
28 1 50       245 croak "Number of bits (Size) is too small" unless $bits;
29 1 50 33     145 delete $param{Seed} if $param{Seed} && length $param{Seed} != 20;
30 1         4 my $v = $param{Verbosity};
31              
32             # try to use fast implementations found on the system, if available.
33 1 50 33     14 unless ($param{Seed} || wantarray || $param{PurePerl}) {
      33        
34              
35             # OpenSSL support
36 1 50       8 my $bin = $^O eq 'MSWin32' ? 'openssl.exe' : 'openssl';
37 1         13 my $openssl = File::Which::which($bin);
38 1 50       297 if ( $openssl ) {
39 1 50       5 print STDERR "Using openssl\n" if $v;
40 1         6 my $bits_n = int($bits);
41 1         203 open( NULL, ">", File::Spec->devnull );
42 1         10 my $pid = open3( gensym, \*OPENSSL, ">&NULL", "$openssl dsaparam -text -noout $bits_n" );
43 1         9707 my @res;
44 1         44375 while( ) {
45 21         595 push @res, $_;
46             }
47 1         32 waitpid( $pid, 0 );
48 1         25 close OPENSSL;
49 1         13 close NULL;
50              
51 1         6 my %parts;
52             my $cur_part;
53 1         7 foreach (@res) {
54 21 50       54 if (/^\s+(\w):\s*$/) {
55 0         0 $cur_part = lc($1);
56 0         0 next;
57             }
58 21 100       115 if (/^\s*((?:[0-9a-f]{2,2}:?)+)\s*$/) {
59 14 50       26 $parts{$cur_part} .= $1 if defined $cur_part;
60             }
61             }
62              
63 1         10 $parts{$_} =~ s/://g for keys %parts;
64              
65 1 50       88 if (scalar keys %parts == 3) {
66 0         0 my $key = Crypt::DSA::Key->new;
67 0         0 $key->p(Math::BigInt->new("0x" . $parts{p}));
68 0         0 $key->q(Math::BigInt->new("0x" . $parts{q}));
69 0         0 $key->g(Math::BigInt->new("0x" . $parts{g}));
70 0         0 return $key;
71             }
72             }
73              
74             }
75              
76             # Pure Perl version:
77              
78 1         7 my($counter, $q, $p, $seed, $seedp1) = (0);
79              
80             ## Generate q.
81             SCOPE: {
82 1 50       3 print STDERR "." if $v;
  35         6648  
83             $seed = $param{Seed} ? delete $param{Seed} :
84 35 50       1443 join '', map chr rand 256, 1..20;
85 35         290 $seedp1 = _seed_plus_one($seed);
86 35         499 my $md = sha1($seed) ^ sha1($seedp1);
87 35         170 vec($md, 0, 8) |= 0x80;
88 35         158 vec($md, 19, 8) |= 0x01;
89 35         180 $q = bin2mp($md);
90 35 100       30106 redo unless isprime($q);
91             }
92              
93 1 50       5 print STDERR "*\n" if $v;
94 1         41 my $n = int(("$bits"-1) / 160);
95 1         103 my $b = ($bits-1)-Math::BigInt->new($n)*160;
96 1         993 my $p_test = Math::BigInt->new(1); $p_test <<= ($bits-1);
  1         116  
97              
98             ## Generate p.
99             SCOPE: {
100 1 50       2157 print STDERR "." if $v;
  51         272  
101 51         187 my $W = Math::BigInt->new(0);
102 51         4656 for my $k (0..$n) {
103 204         188418 $seedp1 = _seed_plus_one($seedp1);
104 204         1859 my $r0 = bin2mp(sha1($seedp1));
105 204 100       196697 $r0 %= Math::BigInt->new(2) ** $b
106             if $k == $n;
107 204         47759 $W += $r0 << (Math::BigInt->new(160) * $k);
108             }
109 51         93055 my $X = $W + $p_test;
110 51         9919 $p = $X - ($X % (2 * $q) - 1);
111 51 100 66     85532 last if $p >= $p_test && isprime($p);
112 50 50       11392 redo unless ++$counter >= 4096;
113             }
114              
115 1 50       31 print STDERR "*" if $v;
116 1         8 my $e = ($p - 1) / $q;
117 1         1531 my $h = Math::BigInt->new(2);
118 1         123 my $g;
119             SCOPE: {
120 1         2 $g = mod_exp($h, $e, $p);
  1         26  
121 1 50       1374581 $h++, redo if $g == 1;
122             }
123 1 50       348 print STDERR "\n" if $v;
124              
125 1         244 my $key = Crypt::DSA::Key->new;
126 1         29 $key->p($p);
127 1         36 $key->q($q);
128 1         18 $key->g($g);
129              
130 1 50       101 return wantarray ? ($key, $counter, "$h", $seed) : $key;
131             }
132              
133             sub generate_keys {
134 1     1 1 3 my $keygen = shift;
135 1         2 my $key = shift;
136 1         3 my($priv_key, $pub_key);
137             SCOPE: {
138 1         10 my $i = bitsize($key->q);
  1         5  
139 1         924 $priv_key = makerandom(Size => $i);
140 1 50       1053 $priv_key -= $key->q if $priv_key >= $key->q;
141 1 50       249 redo if $priv_key == 0;
142             }
143 1         248 $pub_key = mod_exp($key->g, $priv_key, $key->p);
144 1         799063 $key->priv_key($priv_key);
145 1         6 $key->pub_key($pub_key);
146             }
147              
148             sub _seed_plus_one {
149 239     239   762 my($s, $i) = ($_[0]);
150 239         1032 for ($i=19; $i>=0; $i--) {
151 240         1257 vec($s, $i, 8)++;
152 240 100       1091 last unless vec($s, $i, 8) == 0;
153             }
154 239         589 $s;
155             }
156              
157             1;
158              
159             =pod
160              
161             =head1 NAME
162              
163             Crypt::DSA::KeyChain - DSA key generation system
164              
165             =head1 SYNOPSIS
166              
167             use Crypt::DSA::KeyChain;
168             my $keychain = Crypt::DSA::KeyChain->new;
169              
170             my $key = $keychain->generate_params(
171             Size => 512,
172             Seed => $seed,
173             Verbosity => 1,
174             );
175              
176             $keychain->generate_keys($key);
177              
178             =head1 DESCRIPTION
179              
180             I is a lower-level interface to key
181             generation than the interface in I (the I
182             method). It allows you to separately generate the I

, I,

183             and I key parameters, given an optional starting seed, and
184             a mandatory bit size for I

(I and I are 160 bits each).

185              
186             You can then call I to generate the public and
187             private portions of the key.
188              
189             =head1 USAGE
190              
191             =head2 $keychain = Crypt::DSA::KeyChain->new
192              
193             Constructs a new I object. At the moment
194             this isn't particularly useful in itself, other than being the
195             object you need in order to call the other methods.
196              
197             Returns the new object.
198              
199             =head2 $key = $keychain->generate_params(%arg)
200              
201             Generates a set of DSA parameters: the I

, I, and I

202             values of the key. This involves finding primes, and as such
203             it can be a relatively long process.
204              
205             When invoked in scalar context, returns a new
206             I object.
207              
208             In list context, returns the new I object,
209             along with: the value of the internal counter when a suitable
210             prime I

was found; the value of I when I was derived;

211             and the value of the seed (a 20-byte string) when I was
212             found. These values aren't particularly useful in normal
213             circumstances, but they could be useful.
214              
215             I<%arg> can contain:
216              
217             =over 4
218              
219             =item * Size
220              
221             The size in bits of the I

value to generate. The I and

222             I values are always 160 bits each.
223              
224             This argument is mandatory.
225              
226             =item * Seed
227              
228             A seed with which I generation will begin. If this seed does
229             not lead to a suitable prime, it will be discarded, and a new
230             random seed chosen in its place, until a suitable prime can be
231             found.
232              
233             This is entirely optional, and if not provided a random seed will
234             be generated automatically.
235              
236             =item * Verbosity
237              
238             Should be either 0 or 1. A value of 1 will give you a progress
239             meter during I

and I generation--this can be useful, since

240             the process can be relatively long.
241              
242             The default is 0.
243              
244             =back
245              
246             =head2 $keychain->generate_keys($key)
247              
248             Generates the public and private portions of the key I<$key>,
249             a I object.
250              
251             =head1 AUTHOR & COPYRIGHT
252              
253             Please see the L manpage for author, copyright,
254             and license information.
255              
256             =cut