File Coverage

blib/lib/Crypt/DSA/KeyChain.pm
Criterion Covered Total %
statement 123 130 94.6
branch 31 52 59.6
condition 5 12 41.6
subroutine 17 17 100.0
pod 3 3 100.0
total 179 214 83.6


line stmt bran cond sub pod time code
1             package Crypt::DSA::KeyChain;
2              
3 7     7   37 use strict;
  7         9  
  7         238  
4 7     7   24 use warnings;
  7         11  
  7         364  
5 7     7   10309 use Math::BigInt 1.78 try => 'GMP, Pari';
  7         285510  
  7         39  
6 7     7   177929 use Digest::SHA qw( sha1 );
  7         11  
  7         310  
7 7     7   30 use Carp qw( croak );
  7         8  
  7         217  
8 7     7   3487 use IPC::Open3;
  7         28958  
  7         366  
9 7     7   44 use File::Spec;
  7         8  
  7         164  
10 7     7   3093 use File::Which ();
  7         7833  
  7         167  
11 7     7   35 use Symbol qw( gensym );
  7         10  
  7         323  
12 7     7   2760 use Crypt::SysRandom qw( random_bytes );
  7         21279  
  7         481  
13              
14             our $VERSION = '1.24'; #VERSION
15              
16 7     7   41 use vars qw{$VERSION};
  7         10  
  7         260  
17              
18 7     7   2940 use Crypt::DSA::Key;
  7         21  
  7         272  
19 7     7   33 use Crypt::DSA::Util qw( bin2mp bitsize mod_exp makerandom randombelow isprime );
  7         7  
  7         7544  
20              
21             sub new {
22 2     2 1 4 my $class = shift;
23 2         9 bless { @_ }, $class;
24             }
25              
26             sub generate_params {
27 1     1 1 3 my $keygen = shift;
28 1         3 my %param = @_;
29 1         8 my $bits = Math::BigInt->new($param{Size});
30 1 50       144 croak "Number of bits (Size) is too small" unless $bits;
31 1 50 33     83 delete $param{Seed} if $param{Seed} && length $param{Seed} != 20;
32 1         2 my $v = $param{Verbosity};
33              
34             # try to use fast implementations found on the system, if available.
35 1 50 33     8 unless ($param{Seed} || wantarray || $param{PurePerl}) {
      33        
36             # OpenSSL support
37 1 50       4 my $bin = $^O eq 'MSWin32' ? 'openssl.exe' : 'openssl';
38 1         5 my $openssl = File::Which::which($bin);
39 1 50       167 if ( $openssl ) {
40 1 50       2 print STDERR "Using openssl\n" if $v;
41 1         4 my $bits_n = int($bits);
42 1 50       111 open( NULL, ">", File::Spec->devnull ) or die "Unable to open devnull: $!";
43 1         6 my $pid = open3(gensym, \*OPENSSL, ">&NULL", $openssl, 'dsaparam', '-text', '-noout', $bits_n);
44 1         6409 my @res;
45 1         24611 while( ) {
46 21         249 push @res, $_;
47             }
48 1         22 close OPENSSL;
49 1         9 close NULL;
50 1         21 waitpid( $pid, 0 );
51 1 50       17 die "openssl dsaparam failed: " . ($? >> 8) if $?;
52              
53 1         4 my %parts;
54             my $cur_part;
55 1         7 foreach (@res) {
56 21 50       50 if (/^\s+(\w):\s*$/) {
57 0         0 $cur_part = lc($1);
58 0         0 next;
59             }
60 21 100       130 if (/^\s*((?:[0-9a-f]{2,2}:?)+)\s*$/) {
61 14 50       24 $parts{$cur_part} .= $1 if defined $cur_part;
62             }
63             }
64              
65 1         9 $parts{$_} =~ s/://g for keys %parts;
66              
67 1 50       79 if (scalar keys %parts == 3) {
68 0         0 my $key = Crypt::DSA::Key->new;
69 0         0 $key->p(Math::BigInt->new("0x" . $parts{p}));
70 0         0 $key->q(Math::BigInt->new("0x" . $parts{q}));
71 0         0 $key->g(Math::BigInt->new("0x" . $parts{g}));
72 0         0 return $key;
73             }
74             }
75              
76             }
77              
78             # Pure Perl version:
79              
80 1         5 my($counter, $q, $p, $seed, $seedp1) = (0);
81              
82             ## Generate q.
83             SCOPE: {
84 1 50       3 print STDERR "." if $v;
  38         4756  
85             $seed = $param{Seed} ? delete $param{Seed} :
86 38 50       635 Crypt::SysRandom::random_bytes(20);
87 38         176 $seedp1 = _seed_plus_one($seed);
88 38         456 my $md = sha1($seed) ^ sha1($seedp1);
89 38         116 vec($md, 0, 8) |= 0x80;
90 38         87 vec($md, 19, 8) |= 0x01;
91 38         134 $q = bin2mp($md);
92 38 100       22742 redo unless isprime($q);
93             }
94              
95 1 50       3 print STDERR "*\n" if $v;
96 1         16 my $n = int(("$bits"-1) / 160);
97 1         60 my $b = ($bits-1)-Math::BigInt->new($n)*160;
98 1         484 my $p_test = Math::BigInt->new(1); $p_test <<= ($bits-1);
  1         49  
99              
100             ## Generate p.
101             SCOPE: {
102 1 50       1094 print STDERR "." if $v;
  236         852  
103 236         737 my $W = Math::BigInt->new(0);
104 236         13031 for my $k (0..$n) {
105 944         534275 $seedp1 = _seed_plus_one($seedp1);
106 944         6584 my $r0 = bin2mp(sha1($seedp1));
107 944 100       565489 $r0 %= Math::BigInt->new(2) ** $b
108             if $k == $n;
109 944         137976 $W += $r0 << (Math::BigInt->new(160) * $k);
110             }
111 236         260535 my $X = $W + $p_test;
112 236         27009 $p = $X - ($X % (2 * $q) - 1);
113 236 100 66     233653 last if $p >= $p_test && isprime($p);
114 235 50       32261 redo unless ++$counter >= 4096;
115             }
116              
117 1 50       28 print STDERR "*" if $v;
118 1         10 my $e = ($p - 1) / $q;
119 1         768 my $h = Math::BigInt->new(2);
120 1         62 my $g;
121             SCOPE: {
122 1         7 $g = mod_exp($h, $e, $p);
  1         32  
123 1 50       871356 $h++, redo if $g == 1;
124             }
125 1 50       229 print STDERR "\n" if $v;
126              
127 1         193 my $key = Crypt::DSA::Key->new;
128 1         31 $key->p($p);
129 1         20 $key->q($q);
130 1         4 $key->g($g);
131              
132 1 50       109 return wantarray ? ($key, $counter, "$h", $seed) : $key;
133             }
134              
135             sub generate_keys {
136 1     1 1 3 my $keygen = shift;
137 1         7 my $key = shift;
138 1         7 my($priv_key, $pub_key);
139             SCOPE: {
140             # Private key must be uniform in [1, q-1]; randombelow() does not
141             # force the high bit, so (unlike makerandom) it is not biased.
142 1         1 $priv_key = randombelow($key->q);
  1         13  
143 1 50       214 redo if $priv_key == 0;
144             }
145 1         137 $pub_key = mod_exp($key->g, $priv_key, $key->p);
146 1         395999 $key->priv_key($priv_key);
147 1         4 $key->pub_key($pub_key);
148             }
149              
150             sub _seed_plus_one {
151 982     982   2028 my($s, $i) = ($_[0]);
152 982         2451 for ($i=19; $i>=0; $i--) {
153 986         3320 vec($s, $i, 8)++;
154 986 100       2844 last unless vec($s, $i, 8) == 0;
155             }
156 982         1527 $s;
157             }
158              
159             1;
160              
161             =pod
162              
163             =head1 NAME
164              
165             Crypt::DSA::KeyChain - DSA key generation system
166              
167             =head1 SYNOPSIS
168              
169             use Crypt::DSA::KeyChain;
170             my $keychain = Crypt::DSA::KeyChain->new;
171              
172             my $key = $keychain->generate_params(
173             Size => 512,
174             Seed => $seed,
175             Verbosity => 1,
176             );
177              
178             $keychain->generate_keys($key);
179              
180             =head1 DESCRIPTION
181              
182             I is a lower-level interface to key
183             generation than the interface in I (the I
184             method). It allows you to separately generate the I

, I,

185             and I key parameters, given an optional starting seed, and
186             a mandatory bit size for I

(I and I are 160 bits each).

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

, I, and I

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

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

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

value to generate. The I and

224             I values are always 160 bits each.
225              
226             This argument is mandatory.
227              
228             =item * Seed
229              
230             A seed with which I generation will begin. If this seed does
231             not lead to a suitable prime, it will be discarded, and a new
232             random seed chosen in its place, until a suitable prime can be
233             found.
234              
235             This is entirely optional, and if not provided a random seed will
236             be generated automatically.
237              
238             B: This module now uses Crypt::SysRandom to generate a seed.
239             If you are not using one of the sources of randomness recommended at
240             L
241             you B provide your own Seed value.
242              
243             In particular, Perl's B function must not be used for any
244             session, token, hash, authentication, cryptographic value. Basically do
245             not use rand unless you absolutely know how it will be used.
246              
247             =item * Verbosity
248              
249             Should be either 0 or 1. A value of 1 will give you a progress
250             meter during I

and I generation--this can be useful, since

251             the process can be relatively long.
252              
253             The default is 0.
254              
255             =back
256              
257             =head2 $keychain->generate_keys($key)
258              
259             Generates the public and private portions of the key I<$key>,
260             a I object.
261              
262             =head1 AUTHOR & COPYRIGHT
263              
264             Please see the L manpage for author, copyright,
265             and license information.
266              
267             =cut