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 |
||||||
182 | generation than the interface in I |
||||||
183 | method). It allows you to separately generate the I , I |
||||||
184 | and I |
||||||
185 | a mandatory bit size for I (I |
||||||
186 | |||||||
187 | You can then call I |
||||||
188 | private portions of the key. | ||||||
189 | |||||||
190 | =head1 USAGE | ||||||
191 | |||||||
192 | =head2 $keychain = Crypt::DSA::KeyChain->new | ||||||
193 | |||||||
194 | Constructs a new I |
||||||
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 |
||||||
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 |
||||||
208 | |||||||
209 | In list context, returns the new I |
||||||
210 | along with: the value of the internal counter when a suitable | ||||||
211 | prime I was found; the value of I |
||||||
212 | and the value of the seed (a 20-byte string) when Iwas |
||||||
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 |
||||||
223 | I |
||||||
224 | |||||||
225 | This argument is mandatory. | ||||||
226 | |||||||
227 | =item * Seed | ||||||
228 | |||||||
229 | A seed with which Igeneration 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 |
||||||
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 |
||||||
251 | |||||||
252 | =head1 AUTHOR & COPYRIGHT | ||||||
253 | |||||||
254 | Please see the L |
||||||
255 | and license information. | ||||||
256 | |||||||
257 | =cut |