File Coverage

blib/lib/Crypt/DSA/KeyChain.pm
Criterion Covered Total %
statement 34 126 26.9
branch 0 48 0.0
condition 0 12 0.0
subroutine 12 16 75.0
pod 3 3 100.0
total 49 205 23.9


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

, I,

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

(I and I are 160 bits each).

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

, I, and I

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

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

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

value to generate. The I and

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

and I generation--this can be useful, since

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