| blib/lib/Crypt/DSA/KeyChain.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % |
| statement | 118 | 125 | 94.4 |
| branch | 30 | 50 | 60.0 |
| condition | 5 | 12 | 41.6 |
| subroutine | 15 | 15 | 100.0 |
| pod | 3 | 3 | 100.0 |
| total | 171 | 205 | 83.4 |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | package Crypt::DSA::KeyChain; | ||||||
| 2 | |||||||
| 3 | 5 | 5 | 33 | use strict; | |||
| 5 | 11 | ||||||
| 5 | 268 | ||||||
| 4 | 5 | 5 | 8886 | use Math::BigInt 1.78 try => 'GMP, Pari'; | |||
| 5 | 308433 | ||||||
| 5 | 33 | ||||||
| 5 | 5 | 5 | 180292 | use Digest::SHA qw( sha1 ); | |||
| 5 | 11 | ||||||
| 5 | 389 | ||||||
| 6 | 5 | 5 | 42 | use Carp qw( croak ); | |||
| 5 | 9 | ||||||
| 5 | 281 | ||||||
| 7 | 5 | 5 | 3217 | use IPC::Open3; | |||
| 5 | 29240 | ||||||
| 5 | 382 | ||||||
| 8 | 5 | 5 | 41 | use File::Spec; | |||
| 5 | 12 | ||||||
| 5 | 156 | ||||||
| 9 | 5 | 5 | 2889 | use File::Which (); | |||
| 5 | 8145 | ||||||
| 5 | 186 | ||||||
| 10 | 5 | 5 | 36 | use Symbol qw( gensym ); | |||
| 5 | 16 | ||||||
| 5 | 401 | ||||||
| 11 | |||||||
| 12 | our $VERSION = '1.19'; #VERSION | ||||||
| 13 | |||||||
| 14 | 5 | 5 | 36 | use vars qw{$VERSION}; | |||
| 5 | 19 | ||||||
| 5 | 276 | ||||||
| 15 | |||||||
| 16 | 5 | 5 | 2833 | use Crypt::DSA::Key; | |||
| 5 | 19 | ||||||
| 5 | 323 | ||||||
| 17 | 5 | 5 | 39 | use Crypt::DSA::Util qw( bin2mp bitsize mod_exp makerandom isprime ); | |||
| 5 | 10 | ||||||
| 5 | 8397 | ||||||
| 18 | |||||||
| 19 | sub new { | ||||||
| 20 | 2 | 2 | 1 | 6 | my $class = shift; | ||
| 21 | 2 | 22 | bless { @_ }, $class; | ||||
| 22 | } | ||||||
| 23 | |||||||
| 24 | sub generate_params { | ||||||
| 25 | 1 | 1 | 1 | 2 | my $keygen = shift; | ||
| 26 | 1 | 6 | my %param = @_; | ||||
| 27 | 1 | 10 | my $bits = Math::BigInt->new($param{Size}); | ||||
| 28 | 1 | 50 | 245 | croak "Number of bits (Size) is too small" unless $bits; | |||
| 29 | 1 | 50 | 33 | 145 | delete $param{Seed} if $param{Seed} && length $param{Seed} != 20; | ||
| 30 | 1 | 4 | my $v = $param{Verbosity}; | ||||
| 31 | |||||||
| 32 | # try to use fast implementations found on the system, if available. | ||||||
| 33 | 1 | 50 | 33 | 14 | unless ($param{Seed} || wantarray || $param{PurePerl}) { | ||
| 33 | |||||||
| 34 | |||||||
| 35 | # OpenSSL support | ||||||
| 36 | 1 | 50 | 8 | my $bin = $^O eq 'MSWin32' ? 'openssl.exe' : 'openssl'; | |||
| 37 | 1 | 13 | my $openssl = File::Which::which($bin); | ||||
| 38 | 1 | 50 | 297 | if ( $openssl ) { | |||
| 39 | 1 | 50 | 5 | print STDERR "Using openssl\n" if $v; | |||
| 40 | 1 | 6 | my $bits_n = int($bits); | ||||
| 41 | 1 | 203 | open( NULL, ">", File::Spec->devnull ); | ||||
| 42 | 1 | 10 | my $pid = open3( gensym, \*OPENSSL, ">&NULL", "$openssl dsaparam -text -noout $bits_n" ); | ||||
| 43 | 1 | 9707 | my @res; | ||||
| 44 | 1 | 44375 | while( |
||||
| 45 | 21 | 595 | push @res, $_; | ||||
| 46 | } | ||||||
| 47 | 1 | 32 | waitpid( $pid, 0 ); | ||||
| 48 | 1 | 25 | close OPENSSL; | ||||
| 49 | 1 | 13 | close NULL; | ||||
| 50 | |||||||
| 51 | 1 | 6 | my %parts; | ||||
| 52 | my $cur_part; | ||||||
| 53 | 1 | 7 | foreach (@res) { | ||||
| 54 | 21 | 50 | 54 | if (/^\s+(\w):\s*$/) { | |||
| 55 | 0 | 0 | $cur_part = lc($1); | ||||
| 56 | 0 | 0 | next; | ||||
| 57 | } | ||||||
| 58 | 21 | 100 | 115 | if (/^\s*((?:[0-9a-f]{2,2}:?)+)\s*$/) { | |||
| 59 | 14 | 50 | 26 | $parts{$cur_part} .= $1 if defined $cur_part; | |||
| 60 | } | ||||||
| 61 | } | ||||||
| 62 | |||||||
| 63 | 1 | 10 | $parts{$_} =~ s/://g for keys %parts; | ||||
| 64 | |||||||
| 65 | 1 | 50 | 88 | if (scalar keys %parts == 3) { | |||
| 66 | 0 | 0 | my $key = Crypt::DSA::Key->new; | ||||
| 67 | 0 | 0 | $key->p(Math::BigInt->new("0x" . $parts{p})); | ||||
| 68 | 0 | 0 | $key->q(Math::BigInt->new("0x" . $parts{q})); | ||||
| 69 | 0 | 0 | $key->g(Math::BigInt->new("0x" . $parts{g})); | ||||
| 70 | 0 | 0 | return $key; | ||||
| 71 | } | ||||||
| 72 | } | ||||||
| 73 | |||||||
| 74 | } | ||||||
| 75 | |||||||
| 76 | # Pure Perl version: | ||||||
| 77 | |||||||
| 78 | 1 | 7 | my($counter, $q, $p, $seed, $seedp1) = (0); | ||||
| 79 | |||||||
| 80 | ## Generate q. | ||||||
| 81 | SCOPE: { | ||||||
| 82 | 1 | 50 | 3 | print STDERR "." if $v; | |||
| 35 | 6648 | ||||||
| 83 | $seed = $param{Seed} ? delete $param{Seed} : | ||||||
| 84 | 35 | 50 | 1443 | join '', map chr rand 256, 1..20; | |||
| 85 | 35 | 290 | $seedp1 = _seed_plus_one($seed); | ||||
| 86 | 35 | 499 | my $md = sha1($seed) ^ sha1($seedp1); | ||||
| 87 | 35 | 170 | vec($md, 0, 8) |= 0x80; | ||||
| 88 | 35 | 158 | vec($md, 19, 8) |= 0x01; | ||||
| 89 | 35 | 180 | $q = bin2mp($md); | ||||
| 90 | 35 | 100 | 30106 | redo unless isprime($q); | |||
| 91 | } | ||||||
| 92 | |||||||
| 93 | 1 | 50 | 5 | print STDERR "*\n" if $v; | |||
| 94 | 1 | 41 | my $n = int(("$bits"-1) / 160); | ||||
| 95 | 1 | 103 | my $b = ($bits-1)-Math::BigInt->new($n)*160; | ||||
| 96 | 1 | 993 | my $p_test = Math::BigInt->new(1); $p_test <<= ($bits-1); | ||||
| 1 | 116 | ||||||
| 97 | |||||||
| 98 | ## Generate p. | ||||||
| 99 | SCOPE: { | ||||||
| 100 | 1 | 50 | 2157 | print STDERR "." if $v; | |||
| 51 | 272 | ||||||
| 101 | 51 | 187 | my $W = Math::BigInt->new(0); | ||||
| 102 | 51 | 4656 | for my $k (0..$n) { | ||||
| 103 | 204 | 188418 | $seedp1 = _seed_plus_one($seedp1); | ||||
| 104 | 204 | 1859 | my $r0 = bin2mp(sha1($seedp1)); | ||||
| 105 | 204 | 100 | 196697 | $r0 %= Math::BigInt->new(2) ** $b | |||
| 106 | if $k == $n; | ||||||
| 107 | 204 | 47759 | $W += $r0 << (Math::BigInt->new(160) * $k); | ||||
| 108 | } | ||||||
| 109 | 51 | 93055 | my $X = $W + $p_test; | ||||
| 110 | 51 | 9919 | $p = $X - ($X % (2 * $q) - 1); | ||||
| 111 | 51 | 100 | 66 | 85532 | last if $p >= $p_test && isprime($p); | ||
| 112 | 50 | 50 | 11392 | redo unless ++$counter >= 4096; | |||
| 113 | } | ||||||
| 114 | |||||||
| 115 | 1 | 50 | 31 | print STDERR "*" if $v; | |||
| 116 | 1 | 8 | my $e = ($p - 1) / $q; | ||||
| 117 | 1 | 1531 | my $h = Math::BigInt->new(2); | ||||
| 118 | 1 | 123 | my $g; | ||||
| 119 | SCOPE: { | ||||||
| 120 | 1 | 2 | $g = mod_exp($h, $e, $p); | ||||
| 1 | 26 | ||||||
| 121 | 1 | 50 | 1374581 | $h++, redo if $g == 1; | |||
| 122 | } | ||||||
| 123 | 1 | 50 | 348 | print STDERR "\n" if $v; | |||
| 124 | |||||||
| 125 | 1 | 244 | my $key = Crypt::DSA::Key->new; | ||||
| 126 | 1 | 29 | $key->p($p); | ||||
| 127 | 1 | 36 | $key->q($q); | ||||
| 128 | 1 | 18 | $key->g($g); | ||||
| 129 | |||||||
| 130 | 1 | 50 | 101 | return wantarray ? ($key, $counter, "$h", $seed) : $key; | |||
| 131 | } | ||||||
| 132 | |||||||
| 133 | sub generate_keys { | ||||||
| 134 | 1 | 1 | 1 | 3 | my $keygen = shift; | ||
| 135 | 1 | 2 | my $key = shift; | ||||
| 136 | 1 | 3 | my($priv_key, $pub_key); | ||||
| 137 | SCOPE: { | ||||||
| 138 | 1 | 10 | my $i = bitsize($key->q); | ||||
| 1 | 5 | ||||||
| 139 | 1 | 924 | $priv_key = makerandom(Size => $i); | ||||
| 140 | 1 | 50 | 1053 | $priv_key -= $key->q if $priv_key >= $key->q; | |||
| 141 | 1 | 50 | 249 | redo if $priv_key == 0; | |||
| 142 | } | ||||||
| 143 | 1 | 248 | $pub_key = mod_exp($key->g, $priv_key, $key->p); | ||||
| 144 | 1 | 799063 | $key->priv_key($priv_key); | ||||
| 145 | 1 | 6 | $key->pub_key($pub_key); | ||||
| 146 | } | ||||||
| 147 | |||||||
| 148 | sub _seed_plus_one { | ||||||
| 149 | 239 | 239 | 762 | my($s, $i) = ($_[0]); | |||
| 150 | 239 | 1032 | for ($i=19; $i>=0; $i--) { | ||||
| 151 | 240 | 1257 | vec($s, $i, 8)++; | ||||
| 152 | 240 | 100 | 1091 | last unless vec($s, $i, 8) == 0; | |||
| 153 | } | ||||||
| 154 | 239 | 589 | $s; | ||||
| 155 | } | ||||||
| 156 | |||||||
| 157 | 1; | ||||||
| 158 | |||||||
| 159 | =pod | ||||||
| 160 | |||||||
| 161 | =head1 NAME | ||||||
| 162 | |||||||
| 163 | Crypt::DSA::KeyChain - DSA key generation system | ||||||
| 164 | |||||||
| 165 | =head1 SYNOPSIS | ||||||
| 166 | |||||||
| 167 | use Crypt::DSA::KeyChain; | ||||||
| 168 | my $keychain = Crypt::DSA::KeyChain->new; | ||||||
| 169 | |||||||
| 170 | my $key = $keychain->generate_params( | ||||||
| 171 | Size => 512, | ||||||
| 172 | Seed => $seed, | ||||||
| 173 | Verbosity => 1, | ||||||
| 174 | ); | ||||||
| 175 | |||||||
| 176 | $keychain->generate_keys($key); | ||||||
| 177 | |||||||
| 178 | =head1 DESCRIPTION | ||||||
| 179 | |||||||
| 180 | I |
||||||
| 181 | generation than the interface in I |
||||||
| 182 | method). It allows you to separately generate the I , I |
||||||
| 183 | and I |
||||||
| 184 | a mandatory bit size for I (I |
||||||
| 185 | |||||||
| 186 | You can then call I |
||||||
| 187 | private portions of the key. | ||||||
| 188 | |||||||
| 189 | =head1 USAGE | ||||||
| 190 | |||||||
| 191 | =head2 $keychain = Crypt::DSA::KeyChain->new | ||||||
| 192 | |||||||
| 193 | Constructs a new I |
||||||
| 194 | this isn't particularly useful in itself, other than being the | ||||||
| 195 | object you need in order to call the other methods. | ||||||
| 196 | |||||||
| 197 | Returns the new object. | ||||||
| 198 | |||||||
| 199 | =head2 $key = $keychain->generate_params(%arg) | ||||||
| 200 | |||||||
| 201 | Generates a set of DSA parameters: the I , I |
||||||
| 202 | values of the key. This involves finding primes, and as such | ||||||
| 203 | it can be a relatively long process. | ||||||
| 204 | |||||||
| 205 | When invoked in scalar context, returns a new | ||||||
| 206 | I |
||||||
| 207 | |||||||
| 208 | In list context, returns the new I |
||||||
| 209 | along with: the value of the internal counter when a suitable | ||||||
| 210 | prime I was found; the value of I |
||||||
| 211 | and the value of the seed (a 20-byte string) when Iwas |
||||||
| 212 | found. These values aren't particularly useful in normal | ||||||
| 213 | circumstances, but they could be useful. | ||||||
| 214 | |||||||
| 215 | I<%arg> can contain: | ||||||
| 216 | |||||||
| 217 | =over 4 | ||||||
| 218 | |||||||
| 219 | =item * Size | ||||||
| 220 | |||||||
| 221 | The size in bits of the I value to generate. The I |
||||||
| 222 | I |
||||||
| 223 | |||||||
| 224 | This argument is mandatory. | ||||||
| 225 | |||||||
| 226 | =item * Seed | ||||||
| 227 | |||||||
| 228 | A seed with which Igeneration will begin. If this seed does |
||||||
| 229 | not lead to a suitable prime, it will be discarded, and a new | ||||||
| 230 | random seed chosen in its place, until a suitable prime can be | ||||||
| 231 | found. | ||||||
| 232 | |||||||
| 233 | This is entirely optional, and if not provided a random seed will | ||||||
| 234 | be generated automatically. | ||||||
| 235 | |||||||
| 236 | =item * Verbosity | ||||||
| 237 | |||||||
| 238 | Should be either 0 or 1. A value of 1 will give you a progress | ||||||
| 239 | meter during I and I |
||||||
| 240 | the process can be relatively long. | ||||||
| 241 | |||||||
| 242 | The default is 0. | ||||||
| 243 | |||||||
| 244 | =back | ||||||
| 245 | |||||||
| 246 | =head2 $keychain->generate_keys($key) | ||||||
| 247 | |||||||
| 248 | Generates the public and private portions of the key I<$key>, | ||||||
| 249 | a I |
||||||
| 250 | |||||||
| 251 | =head1 AUTHOR & COPYRIGHT | ||||||
| 252 | |||||||
| 253 | Please see the L |
||||||
| 254 | and license information. | ||||||
| 255 | |||||||
| 256 | =cut |