| 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 |