File Coverage

blib/lib/Crypt/DSA/KeyChain.pm
Criterion Covered Total %
statement 125 132 94.7
branch 32 54 59.2
condition 5 12 41.6
subroutine 17 17 100.0
pod 3 3 100.0
total 182 218 83.4


line stmt bran cond sub pod time code
1             package Crypt::DSA::KeyChain;
2              
3 5     5   29 use strict;
  5         9  
  5         171  
4 5     5   22 use warnings;
  5         9  
  5         350  
5 5     5   8611 use Math::BigInt 1.78 try => 'GMP, Pari';
  5         285788  
  5         41  
6 5     5   189127 use Digest::SHA qw( sha1 );
  5         11  
  5         358  
7 5     5   34 use Carp qw( croak );
  5         12  
  5         331  
8 5     5   3066 use IPC::Open3;
  5         28660  
  5         346  
9 5     5   42 use File::Spec;
  5         10  
  5         139  
10 5     5   2822 use File::Which ();
  5         8096  
  5         173  
11 5     5   39 use Symbol qw( gensym );
  5         14  
  5         332  
12 5     5   2776 use Crypt::SysRandom qw( random_bytes );
  5         19053  
  5         549  
13              
14             our $VERSION = '1.20'; #VERSION
15              
16 5     5   40 use vars qw{$VERSION};
  5         8  
  5         255  
17              
18 5     5   2865 use Crypt::DSA::Key;
  5         20  
  5         281  
19 5     5   35 use Crypt::DSA::Util qw( bin2mp bitsize mod_exp makerandom isprime );
  5         9  
  5         8520  
20              
21             sub new {
22 2     2 1 7 my $class = shift;
23 2         17 bless { @_ }, $class;
24             }
25              
26             sub generate_params {
27 1     1 1 2 my $keygen = shift;
28 1         6 my %param = @_;
29 1         10 my $bits = Math::BigInt->new($param{Size});
30 1 50       252 croak "Number of bits (Size) is too small" unless $bits;
31 1 50 33     127 delete $param{Seed} if $param{Seed} && length $param{Seed} != 20;
32 1         3 my $v = $param{Verbosity};
33              
34             # try to use fast implementations found on the system, if available.
35 1 50 33     12 unless ($param{Seed} || wantarray || $param{PurePerl}) {
      33        
36             # OpenSSL support
37 1 50       7 my $bin = $^O eq 'MSWin32' ? 'openssl.exe' : 'openssl';
38 1         8 my $openssl = File::Which::which($bin);
39 1 50       296 if ( $openssl ) {
40 1 50       5 print STDERR "Using openssl\n" if $v;
41 1         6 my $bits_n = int($bits);
42 1 50       178 open( NULL, ">", File::Spec->devnull ) or die "Unable to open devnull: $!";
43 1         10 my $pid = open3(gensym, \*OPENSSL, ">&NULL", $openssl, 'dsaparam', '-text', '-noout', $bits_n);
44 1         8652 my @res;
45 1         55414 while( ) {
46 21         515 push @res, $_;
47             }
48 1         54 close OPENSSL;
49 1         99 close NULL;
50 1         30 waitpid( $pid, 0 );
51 1 50       22 die "openssl dsaparam failed: " . ($? >> 8) if $?;
52              
53 1         12 my %parts;
54             my $cur_part;
55 1         7 foreach (@res) {
56 21 50       84 if (/^\s+(\w):\s*$/) {
57 0         0 $cur_part = lc($1);
58 0         0 next;
59             }
60 21 100       160 if (/^\s*((?:[0-9a-f]{2,2}:?)+)\s*$/) {
61 14 50       38 $parts{$cur_part} .= $1 if defined $cur_part;
62             }
63             }
64              
65 1         41 $parts{$_} =~ s/://g for keys %parts;
66              
67 1 50       138 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         6 my($counter, $q, $p, $seed, $seedp1) = (0);
81              
82             ## Generate q.
83             SCOPE: {
84 1 50       7 print STDERR "." if $v;
  9         1512  
85             $seed = $param{Seed} ? delete $param{Seed} :
86 9 50       159 Crypt::SysRandom::random_bytes(20);
87 9         46 $seedp1 = _seed_plus_one($seed);
88 9         115 my $md = sha1($seed) ^ sha1($seedp1);
89 9         35 vec($md, 0, 8) |= 0x80;
90 9         23 vec($md, 19, 8) |= 0x01;
91 9         52 $q = bin2mp($md);
92 9 100       8089 redo unless isprime($q);
93             }
94              
95 1 50       3 print STDERR "*\n" if $v;
96 1         3 my $n = int(("$bits"-1) / 160);
97 1         55 my $b = ($bits-1)-Math::BigInt->new($n)*160;
98 1         542 my $p_test = Math::BigInt->new(1); $p_test <<= ($bits-1);
  1         100  
99              
100             ## Generate p.
101             SCOPE: {
102 1 50       1364 print STDERR "." if $v;
  191         929  
103 191         763 my $W = Math::BigInt->new(0);
104 191         17047 for my $k (0..$n) {
105 764         708194 $seedp1 = _seed_plus_one($seedp1);
106 764         7032 my $r0 = bin2mp(sha1($seedp1));
107 764 100       731584 $r0 %= Math::BigInt->new(2) ** $b
108             if $k == $n;
109 764         171700 $W += $r0 << (Math::BigInt->new(160) * $k);
110             }
111 191         347562 my $X = $W + $p_test;
112 191         35694 $p = $X - ($X % (2 * $q) - 1);
113 191 100 66     309552 last if $p >= $p_test && isprime($p);
114 190 50       42343 redo unless ++$counter >= 4096;
115             }
116              
117 1 50       38 print STDERR "*" if $v;
118 1         14 my $e = ($p - 1) / $q;
119 1         1537 my $h = Math::BigInt->new(2);
120 1         131 my $g;
121             SCOPE: {
122 1         13 $g = mod_exp($h, $e, $p);
  1         39  
123 1 50       1257575 $h++, redo if $g == 1;
124             }
125 1 50       323 print STDERR "\n" if $v;
126              
127 1         246 my $key = Crypt::DSA::Key->new;
128 1         79 $key->p($p);
129 1         26 $key->q($q);
130 1         24 $key->g($g);
131              
132 1 50       151 return wantarray ? ($key, $counter, "$h", $seed) : $key;
133             }
134              
135             sub generate_keys {
136 1     1 1 33 my $keygen = shift;
137 1         4 my $key = shift;
138 1         33 my($priv_key, $pub_key);
139             SCOPE: {
140 1         4 my $i = bitsize($key->q);
  1         13  
141 1         1036 $priv_key = makerandom(Size => $i);
142 1 50       1767 $priv_key -= $key->q if $priv_key >= $key->q;
143 1 50       268 redo if $priv_key == 0;
144             }
145 1         258 $pub_key = mod_exp($key->g, $priv_key, $key->p);
146 1         711771 $key->priv_key($priv_key);
147 1         22 $key->pub_key($pub_key);
148             }
149              
150             sub _seed_plus_one {
151 773     773   2492 my($s, $i) = ($_[0]);
152 773         2983 for ($i=19; $i>=0; $i--) {
153 777         4071 vec($s, $i, 8)++;
154 777 100       2942 last unless vec($s, $i, 8) == 0;
155             }
156 773         1896 $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