File Coverage

blib/lib/Crypt/DH.pm
Criterion Covered Total %
statement 11 57 19.3
branch 0 30 0.0
condition 0 8 0.0
subroutine 4 12 33.3
pod 2 3 66.7
total 17 110 15.5


line stmt bran cond sub pod time code
1             # $Id: DH.pm 1860 2005-06-11 06:15:44Z btrott $
2              
3             package Crypt::DH;
4 1     1   16 use strict;
  1         14  
  1         14  
5              
6 1     1   36 use Math::BigInt lib => "GMP,Pari";
  1         10  
  1         33  
7             our $VERSION = '0.06';
8              
9             sub new {
10 0     0 1       my $class = shift;
11 0               my $dh = bless {}, $class;
12              
13 0               my %param = @_;
14 0               for my $w (qw( p g priv_key )) {
15 0 0                 next unless exists $param{$w};
16 0                   $dh->$w(delete $param{$w});
17                 }
18 0 0             die "Unknown parameters to constructor: " . join(", ", keys %param) if %param;
19              
20 0               $dh;
21             }
22              
23             BEGIN {
24 1     1   21     no strict 'refs';
  1         9  
  1         16  
25 1     1   43     for my $meth (qw( p g pub_key priv_key )) {
26                     *$meth = sub {
27 0     0                 my $key = shift;
28 0 0                     if (@_) {
29 0                           $key->{$meth} = _any2bigint(shift);
30                         }
31 0   0                   my $ret = $key->{$meth} || "";
32 0                       $ret;
33 4         70         };
34                 }
35             }
36              
37             sub _any2bigint {
38 0     0         my($value) = @_;
39 0 0 0           if (ref $value eq 'Math::BigInt') {
    0          
    0          
    0          
40 0                   return $value;
41                 }
42                 elsif (ref $value eq 'Math::Pari') {
43 0                   return Math::BigInt->new(Math::Pari::pari2pv($value));
44                 }
45                 elsif (defined $value && !(ref $value)) {
46 0                   return Math::BigInt->new($value);
47                 }
48                 elsif (defined $value) {
49 0                   die "Unknown parameter type: $value\n";
50                 }
51             }
52              
53             sub generate_keys {
54 0     0 1       my $dh = shift;
55              
56 0 0             unless (defined $dh->{priv_key}) {
57 0                   my $i = _bitsize($dh->{p}) - 1;
58 0 0                 $dh->{priv_key} =
59                         $Crypt::Random::VERSION ?
60                         Crypt::Random::makerandom_itv(Strength => 0, Uniform => 1,
61                                                       Lower => 1, Upper => $dh->{p} - 1) :
62                         _makerandom_itv($i, 1, $dh->{p} - 1);
63                 }
64              
65 0               $dh->{pub_key} = $dh->{g}->copy->bmodpow($dh->{priv_key}, $dh->{p});
66             }
67              
68             sub compute_key {
69 0     0 0       my $dh = shift;
70 0               my $pub_key = _any2bigint(shift);
71 0               $pub_key->copy->bmodpow($dh->{priv_key}, $dh->{p});
72             }
73             *compute_secret = \&compute_key;
74              
75             sub _bitsize {
76 0     0         return length($_[0]->as_bin) - 2;
77             }
78              
79             sub _makerandom_itv {
80 0     0         my ($size, $min_inc, $max_exc) = @_;
81              
82 0               while (1) {
83 0                   my $r = _makerandom($size);
84 0 0 0               return $r if $r >= $min_inc && $r < $max_exc;
85                 }
86             }
87              
88             sub _makerandom {
89 0     0         my $size = shift;
90              
91 0 0             my $bytes = int($size / 8) + ($size % 8 ? 1 : 0);
92              
93 0               my $rand;
94 0 0             if (-e "/dev/urandom") {
95 0                   my $fh;
96 0 0                 open($fh, '/dev/urandom')
97                         or die "Couldn't open /dev/urandom";
98 0                   my $got = sysread $fh, $rand, $bytes;
99 0 0                 die "Didn't read all bytes from urandom" unless $got == $bytes;
100 0                   close $fh;
101                 } else {
102 0                   for (1..$bytes) {
103 0                       $rand .= chr(int(rand(256)));
104                     }
105                 }
106              
107 0               my $bits = unpack("b*", $rand);
108 0 0             die unless length($bits) >= $size;
109              
110 0               Math::BigInt->new('0b' . substr($bits, 0, $size));
111             }
112              
113             1;
114             __END__
115            
116             =head1 NAME
117            
118             Crypt::DH - Diffie-Hellman key exchange system
119            
120             =head1 SYNOPSIS
121            
122             use Crypt::DH;
123             my $dh = Crypt::DH->new;
124             $dh->g($g);
125             $dh->p($p);
126            
127             ## Generate public and private keys.
128             $dh->generate_keys;
129            
130             $my_pub_key = $dh->pub_key;
131            
132             ## Send $my_pub_key to "other" party, and receive "other"
133             ## public key in return.
134            
135             ## Now compute shared secret from "other" public key.
136             my $shared_secret = $dh->compute_secret( $other_pub_key );
137            
138             =head1 DESCRIPTION
139            
140             I<Crypt::DH> is a Perl implementation of the Diffie-Hellman key
141             exchange system. Diffie-Hellman is an algorithm by which two
142             parties can agree on a shared secret key, known only to them.
143             The secret is negotiated over an insecure network without the
144             two parties ever passing the actual shared secret, or their
145             private keys, between them.
146            
147             =head1 THE ALGORITHM
148            
149             The algorithm generally works as follows: Party A and Party B
150             choose a property I<p> and a property I<g>; these properties are
151             shared by both parties. Each party then computes a random private
152             key integer I<priv_key>, where the length of I<priv_key> is at
153             most (number of bits in I<p>) - 1. Each party then computes a
154             public key based on I<g>, I<priv_key>, and I<p>; the exact value
155             is
156            
157             g ^ priv_key mod p
158            
159             The parties exchange these public keys.
160            
161             The shared secret key is generated based on the exchanged public
162             key, the private key, and I<p>. If the public key of Party B is
163             denoted I<pub_key_B>, then the shared secret is equal to
164            
165             pub_key_B ^ priv_key mod p
166            
167             The mathematical principles involved insure that both parties will
168             generate the same shared secret key.
169            
170             More information can be found in PKCS #3 (Diffie-Hellman Key
171             Agreement Standard):
172            
173             http://www.rsasecurity.com/rsalabs/pkcs/pkcs-3/
174            
175             =head1 USAGE
176            
177             I<Crypt::DH> implements the core routines needed to use
178             Diffie-Hellman key exchange. To actually use the algorithm,
179             you'll need to start with values for I<p> and I<g>; I<p> is a
180             large prime, and I<g> is a base which must be larger than 0
181             and less than I<p>.
182            
183             I<Crypt::DH> uses I<Math::BigInt> internally for big-integer
184             calculations. All accessor methods (I<p>, I<g>, I<priv_key>, and
185             I<pub_key>) thus return I<Math::BigInt> objects, as does the
186             I<compute_secret> method. The accessors, however, allow setting with a
187             scalar decimal string, hex string (^0x), Math::BigInt object, or
188             Math::Pari object (for backwards compatibility).
189            
190             =head2 $dh = Crypt::DH->new([ %param ]).
191            
192             Constructs a new I<Crypt::DH> object and returns the object.
193             I<%param> may include none, some, or all of the keys I<p>, I<g>, and
194             I<priv_key>.
195            
196             =head2 $dh->p([ $p ])
197            
198             Given an argument I<$p>, sets the I<p> parameter (large prime) for
199             this I<Crypt::DH> object.
200            
201             Returns the current value of I<p>. (as a Math::BigInt object)
202            
203             =head2 $dh->g([ $g ])
204            
205             Given an argument I<$g>, sets the I<g> parameter (base) for
206             this I<Crypt::DH> object.
207            
208             Returns the current value of I<g>.
209            
210             =head2 $dh->generate_keys
211            
212             Generates the public and private key portions of the I<Crypt::DH>
213             object, assuming that you've already filled I<p> and I<g> with
214             appropriate values.
215            
216             If you've provided a priv_key, it's used, otherwise a random priv_key
217             is created using either Crypt::Random (if already loaded), or
218             /dev/urandom, or Perl's rand, in that order.
219            
220             =head2 $dh->compute_secret( $public_key )
221            
222             Given the public key I<$public_key> of Party B (the party with which
223             you're performing key negotiation and exchange), computes the shared
224             secret key, based on that public key, your own private key, and your
225             own large prime value (I<p>).
226            
227             The historical method name "compute_key" is aliased to this for
228             compatibility.
229            
230             =head2 $dh->priv_key([ $priv_key ])
231            
232             Returns the private key. Given an argument I<$priv_key>, sets the
233             I<priv_key> parameter for this I<Crypt::DH> object.
234            
235             =head2 $dh->pub_key
236            
237             Returns the public key.
238            
239             =head1 AUTHOR & COPYRIGHT
240            
241             Benjamin Trott, ben@rhumba.pair.com
242            
243             Brad Fitzpatrick, brad@danga.com
244            
245             Except where otherwise noted, Crypt::DH is Copyright 2001
246             Benjamin Trott. All rights reserved. Crypt::DH is free
247             software; you may redistribute it and/or modify it under
248             the same terms as Perl itself.
249            
250             =cut
251