File Coverage

blib/lib/Crypt/DSA/KeyChain.pm
Criterion Covered Total %
statement 123 130 94.6
branch 30 52 57.6
condition 5 12 41.6
subroutine 17 17 100.0
pod 3 3 100.0
total 178 214 83.1


line stmt bran cond sub pod time code
1             package Crypt::DSA::KeyChain;
2              
3 7     7   34 use strict;
  7         12  
  7         191  
4 7     7   21 use warnings;
  7         11  
  7         349  
5 7     7   9735 use Math::BigInt 1.78 try => 'GMP, Pari';
  7         254012  
  7         47  
6 7     7   160507 use Digest::SHA qw( sha1 );
  7         11  
  7         306  
7 7     7   25 use Carp qw( croak );
  7         9  
  7         242  
8 7     7   3106 use IPC::Open3;
  7         27433  
  7         359  
9 7     7   42 use File::Spec;
  7         13  
  7         133  
10 7     7   2703 use File::Which ();
  7         7621  
  7         161  
11 7     7   36 use Symbol qw( gensym );
  7         7  
  7         316  
12 7     7   2770 use Crypt::SysRandom qw( random_bytes );
  7         19040  
  7         495  
13              
14             our $VERSION = '1.23'; #VERSION
15              
16 7     7   64 use vars qw{$VERSION};
  7         9  
  7         238  
17              
18 7     7   2820 use Crypt::DSA::Key;
  7         20  
  7         228  
19 7     7   32 use Crypt::DSA::Util qw( bin2mp bitsize mod_exp makerandom randombelow isprime );
  7         10  
  7         7116  
20              
21             sub new {
22 2     2 1 4 my $class = shift;
23 2         10 bless { @_ }, $class;
24             }
25              
26             sub generate_params {
27 1     1 1 1 my $keygen = shift;
28 1         4 my %param = @_;
29 1         8 my $bits = Math::BigInt->new($param{Size});
30 1 50       139 croak "Number of bits (Size) is too small" unless $bits;
31 1 50 33     70 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     9 unless ($param{Seed} || wantarray || $param{PurePerl}) {
      33        
36             # OpenSSL support
37 1 50       4 my $bin = $^O eq 'MSWin32' ? 'openssl.exe' : 'openssl';
38 1         6 my $openssl = File::Which::which($bin);
39 1 50       164 if ( $openssl ) {
40 1 50       2 print STDERR "Using openssl\n" if $v;
41 1         4 my $bits_n = int($bits);
42 1 50       139 open( NULL, ">", File::Spec->devnull ) or die "Unable to open devnull: $!";
43 1         32 my $pid = open3(gensym, \*OPENSSL, ">&NULL", $openssl, 'dsaparam', '-text', '-noout', $bits_n);
44 1         6155 my @res;
45 1         29603 while( ) {
46 21         392 push @res, $_;
47             }
48 1         27 close OPENSSL;
49 1         7 close NULL;
50 1         25 waitpid( $pid, 0 );
51 1 50       31 die "openssl dsaparam failed: " . ($? >> 8) if $?;
52              
53 1         12 my %parts;
54             my $cur_part;
55 1         8 foreach (@res) {
56 21 50       53 if (/^\s+(\w):\s*$/) {
57 0         0 $cur_part = lc($1);
58 0         0 next;
59             }
60 21 100       113 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       94 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       3 print STDERR "." if $v;
  142         17705  
85             $seed = $param{Seed} ? delete $param{Seed} :
86 142 50       2190 Crypt::SysRandom::random_bytes(20);
87 142         783 $seedp1 = _seed_plus_one($seed);
88 142         1668 my $md = sha1($seed) ^ sha1($seedp1);
89 142         413 vec($md, 0, 8) |= 0x80;
90 142         327 vec($md, 19, 8) |= 0x01;
91 142         535 $q = bin2mp($md);
92 142 100       84601 redo unless isprime($q);
93             }
94              
95 1 50       4 print STDERR "*\n" if $v;
96 1         3 my $n = int(("$bits"-1) / 160);
97 1         88 my $b = ($bits-1)-Math::BigInt->new($n)*160;
98 1         475 my $p_test = Math::BigInt->new(1); $p_test <<= ($bits-1);
  1         49  
99              
100             ## Generate p.
101             SCOPE: {
102 1 50       1195 print STDERR "." if $v;
  7         19  
103 7         22 my $W = Math::BigInt->new(0);
104 7         403 for my $k (0..$n) {
105 28         15468 $seedp1 = _seed_plus_one($seedp1);
106 28         185 my $r0 = bin2mp(sha1($seedp1));
107 28 100       16243 $r0 %= Math::BigInt->new(2) ** $b
108             if $k == $n;
109 28         4058 $W += $r0 << (Math::BigInt->new(160) * $k);
110             }
111 7         7548 my $X = $W + $p_test;
112 7         799 $p = $X - ($X % (2 * $q) - 1);
113 7 100 66     6847 last if $p >= $p_test && isprime($p);
114 6 50       803 redo unless ++$counter >= 4096;
115             }
116              
117 1 50       11 print STDERR "*" if $v;
118 1         4 my $e = ($p - 1) / $q;
119 1         761 my $h = Math::BigInt->new(2);
120 1         62 my $g;
121             SCOPE: {
122 1         2 $g = mod_exp($h, $e, $p);
  1         16  
123 1 50       843899 $h++, redo if $g == 1;
124             }
125 1 50       221 print STDERR "\n" if $v;
126              
127 1         63 my $key = Crypt::DSA::Key->new;
128 1         11 $key->p($p);
129 1         13 $key->q($q);
130 1         12 $key->g($g);
131              
132 1 50       52 return wantarray ? ($key, $counter, "$h", $seed) : $key;
133             }
134              
135             sub generate_keys {
136 1     1 1 2 my $keygen = shift;
137 1         4 my $key = shift;
138 1         2 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         3  
143 1 50       196 redo if $priv_key == 0;
144             }
145 1         160 $pub_key = mod_exp($key->g, $priv_key, $key->p);
146 1         400052 $key->priv_key($priv_key);
147 1         5 $key->pub_key($pub_key);
148             }
149              
150             sub _seed_plus_one {
151 170     170   415 my($s, $i) = ($_[0]);
152 170         703 for ($i=19; $i>=0; $i--) {
153 170         788 vec($s, $i, 8)++;
154 170 50       636 last unless vec($s, $i, 8) == 0;
155             }
156 170         312 $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