File Coverage

blib/lib/Crypt/Random/Seed.pm
Criterion Covered Total %
statement 112 195 57.4
branch 37 106 34.9
condition 14 39 35.9
subroutine 24 31 77.4
pod 6 6 100.0
total 193 377 51.1


line stmt bran cond sub pod time code
1             package Crypt::Random::Seed;
2 8     8   8224 use strict;
  8         16  
  8         281  
3 8     8   44 use warnings;
  8         15  
  8         206  
4 8     8   55 use Fcntl;
  8         12  
  8         3380  
5 8     8   57 use Carp qw/carp croak/;
  8         16  
  8         803  
6              
7             # cert insists on using constant, but regular critic doesn't like it.
8             ## no critic (constant)
9              
10             BEGIN {
11 8     8   20 $Crypt::Random::Seed::AUTHORITY = 'cpan:DANAJ';
12 8         360 $Crypt::Random::Seed::VERSION = '0.03';
13             }
14              
15 8     8   66 use base qw( Exporter );
  8         24  
  8         2125  
16             our @EXPORT_OK = qw( );
17             our %EXPORT_TAGS = (all => [ @EXPORT_OK ]);
18             # Export nothing by default
19              
20 8     8   54 use constant UINT32_SIZE => 4;
  8         17  
  8         45254  
21              
22             # These are the pre-defined names. We don't let user methods use these.
23             my %defined_methods = map { $_ => 1 }
24             (qw(CryptGenRandom RtlGenRand EGD /dev/random /dev/urandom
25             TESHA2-strong TESHA2-weak));
26             # If given one of these names as whitelist/blacklist, we add these also.
27             my %name_aliases = (
28             'Win32' => [qw(RtlGenRand CryptGenRandom)],
29             'TESHA2' => [qw(TESHA2-strong TESHA2-weak)],
30             );
31              
32             sub new {
33 15     15 1 4634 my ($class, %params) = @_;
34 15         32 my $self = {};
35              
36             # Trying to handle strong vs. weak is fraught with complication, so just
37             # remove the idea entirely.
38 15 50       50 if (defined $params{Weak}) {
39             # In this release, just silently don't use it.
40 0         0 delete $params{Weak};
41             }
42              
43 15 50       42 if (defined $params{Source}) {
44 0 0       0 if (ref($params{Source}) eq 'CODE') {
    0          
45 0         0 $self->{Name} = 'User';
46 0         0 $self->{SourceSub} = $params{Source};
47             # We don't know if it is blocking or strong, assume neither
48 0         0 $self->{Blocking} = 0;
49 0         0 $self->{Strong} = 0;
50             } elsif (ref($params{Source}) eq 'ARRAY') {
51 0         0 ($self->{Name}, $self->{SourceSub}, $self->{Blocking}, $self->{Strong})
52 0         0 = @{$params{Source}};
53             # For sanity, don't let them redefine the standard names.
54 0 0       0 croak "Invalid name: $self->{Name}. Name reserved."
55             if defined $defined_methods{$self->{Name}};
56             } else {
57 0         0 croak "Invalid 'Source'. Should be code or array reference.";
58             }
59             } else {
60             # This is a sorted list -- the first one that returns true gets used.
61 15         76 my @methodlist = (
62             \&_try_win32,
63             \&_try_egd,
64             \&_try_dev_random,
65             \&_try_dev_urandom,
66             \&_try_tesha2,
67             );
68              
69 15         26 my %whitelist;
70 15         23 my $have_whitelist = 0;
71 15 100       45 if (defined $params{Only}) {
72 3 100       224 croak "Parameter 'Only' must be an array ref" unless ref($params{Only}) eq 'ARRAY';
73 2         3 $have_whitelist = 1;
74 2         5 $whitelist{$_} = 1 for @{$params{Only}};
  2         8  
75 2         12 while ( my($name, $list) = each %name_aliases) {
76 4 100       24 @whitelist{@$list} = (1) x scalar @$list if $whitelist{$name};
77             }
78             }
79 14         17 my %blacklist;
80 14 100       67 if (defined $params{Never}) {
81 2 100       210 croak "Parameter 'Never' must be an array ref" unless ref($params{Never}) eq 'ARRAY';
82 1         2 $blacklist{$_} = 1 for @{$params{Never}};
  1         4  
83 1         8 while ( my($name, $list) = each %name_aliases) {
84 2 50       11 @blacklist{@$list} = (1) x scalar @$list if $blacklist{$name};
85             }
86             }
87              
88 13         28 foreach my $m (@methodlist) {
89 47         124 my ($name, $rsub, $isblocking, $isstrong) = $m->();
90 47 100       118 next unless defined $name;
91 21 50 66     165 next if $isblocking && ($params{NonBlocking} || $params{Nonblocking} || $params{nonblocking});
      66        
92             #next if !$isstrong && !$params{Weak};
93 18 100       48 next if $blacklist{$name};
94 17 100 100     80 next if $have_whitelist && !$whitelist{$name};
95 12         28 $self->{Name} = $name;
96 12         21 $self->{SourceSub} = $rsub;
97 12         23 $self->{Blocking} = $isblocking;
98 12         18 $self->{Strong} = $isstrong;
99 12         39 last;
100             }
101             }
102             # Couldn't find anything appropriate
103 13 100       43 return unless defined $self->{SourceSub};
104              
105 12         28 bless $self, $class;
106 12         39 return $self;
107             }
108              
109             # Nothing special to do on destroy
110             #sub DESTROY {
111             # my $self = shift;
112             # delete $self->{$_} for keys $self;
113             # return;
114             #}
115              
116             sub name {
117 5     5 1 1104 my $self = shift;
118 5         34 return $self->{Name};
119             }
120             sub is_blocking {
121 0     0 1 0 my $self = shift;
122 0         0 return $self->{Blocking};
123             }
124             sub is_strong {
125 0     0 1 0 my $self = shift;
126 0         0 return $self->{Strong};
127             }
128             sub random_bytes {
129 2     2 1 7 my ($self, $nbytes) = @_;
130 2 50 33     12 return '' unless defined $nbytes && int($nbytes) > 0;
131 2         9 my $rsub = $self->{SourceSub};
132 2 50       8 return unless defined $rsub;
133 2         5 return $rsub->(int($nbytes));
134             }
135             sub random_values {
136 7     7 1 2651 my ($self, $nvalues) = @_;
137 7 100 100     39 return unless defined $nvalues && int($nvalues) > 0;
138 2         9 my $rsub = $self->{SourceSub};
139 2 50       7 return unless defined $rsub;
140 2         7 return unpack( 'L*', $rsub->(UINT32_SIZE * int($nvalues)) );
141             }
142              
143              
144             sub _try_tesha2 {
145 2 50   2   3 eval { require Crypt::Random::TESHA2; Crypt::Random::TESHA2->import(); 1; }
  2         1028  
  2         21265  
  2         88  
146             or return;
147 2         7 my $isstrong = Crypt::Random::TESHA2::is_strong();
148 2 50       16 my $name = join('-', 'TESHA2', ($isstrong) ? 'strong' : 'weak');
149 2         9 return ($name, \&Crypt::Random::TESHA2::random_bytes, 0, 1);
150             }
151              
152             sub _try_dev_urandom {
153 6 50   6   167 return unless -r "/dev/urandom";
154 6     2   29 return ('/dev/urandom', sub { __read_file('/dev/urandom', @_); }, 0, 0);
  2         7  
155             }
156              
157             sub _try_dev_random {
158 13 50   13   311 return unless -r "/dev/random";
159             # FreeBSD's /dev/random is 256-bit Yarrow non-blocking.
160             # Is it 'strong'? Debatable -- we'll say it is.
161 13 50       49 my $blocking = ($^O eq 'freebsd') ? 0 : 1;
162 13     2   79 return ('/dev/random', sub { __read_file('/dev/random', @_); }, $blocking, 1);
  2         6  
163             }
164              
165             sub __read_file {
166 4     4   6 my ($file, $nbytes) = @_;
167 4 50 33     19 return unless defined $nbytes && $nbytes > 0;
168 4         173 sysopen(my $fh, $file, O_RDONLY);
169 4         13 binmode $fh;
170 4         7 my($s, $buffer, $nread) = ('', '', 0);
171 4         12 while ($nread < $nbytes) {
172 4         57 my $thisread = sysread $fh, $buffer, $nbytes-$nread;
173             # Count EOF as an error.
174 4 50 33     19 croak "Error reading $file: $!\n" unless defined $thisread && $thisread > 0;
175 4         8 $s .= $buffer;
176 4         11 $nread += length($buffer);
177             #die unless $nread == length($s); # assert
178             }
179 4 50       13 croak "Internal file read error: wanted $nbytes, read $nread"
180             unless $nbytes == length($s); # assert
181 4         51 return $s;
182             }
183              
184             # Most of this is taken without notice from Crypt::URandom 0.28 and
185             # Crypt::Random::Source::Strong::Win32 0.07.
186             # Kudos to David Dick and Max Kanat-Alexander for doing all the work.
187             #
188             # See some documentation here:
189             # http://msdn.microsoft.com/en-us/library/aa379942.aspx
190             # where they note that the output of these is really a well seeded CSPRNG:
191             # either FIPS 186-2 (older) or AES-CTR (Vista SP1 and newer).
192              
193             sub _try_win32 {
194 13 50   13   68 return unless $^O eq 'MSWin32';
195             # Cygwin has /dev/random at least as far back as 2000.
196 0 0       0 eval { require Win32; require Win32::API; require Win32::API::Type; 1; }
  0         0  
  0         0  
  0         0  
  0         0  
197             or return;
198              
199 8     8   80 use constant CRYPT_SILENT => 0x40; # Never display a UI.
  8         16  
  8         528  
200 8     8   38 use constant PROV_RSA_FULL => 1; # Which service provider.
  8         17  
  8         401  
201 8     8   47 use constant VERIFY_CONTEXT => 0xF0000000; # Don't need existing keypairs.
  8         95  
  8         348  
202 8     8   120 use constant W2K_MAJOR_VERSION => 5; # Windows 2000
  8         12  
  8         455  
203 8     8   113 use constant W2K_MINOR_VERSION => 0;
  8         13  
  8         10428  
204              
205 0         0 my ($major, $minor) = (Win32::GetOSVersion())[1, 2];
206 0 0       0 return if $major < W2K_MAJOR_VERSION;
207              
208 0 0 0     0 if ($major == W2K_MAJOR_VERSION && $minor == W2K_MINOR_VERSION) {
209             # We are Windows 2000. Use the older CryptGenRandom interface.
210 0         0 my $crypt_acquire_context_a =
211             Win32::API->new( 'advapi32', 'CryptAcquireContextA', 'PPPNN',
212             'I' );
213 0 0       0 return unless defined $crypt_acquire_context_a;
214 0         0 my $context = chr(0) x Win32::API::Type->sizeof('PULONG');
215 0         0 my $result = $crypt_acquire_context_a->Call(
216             $context, 0, 0, PROV_RSA_FULL, CRYPT_SILENT | VERIFY_CONTEXT );
217 0 0       0 return unless $result;
218 0         0 my $pack_type = Win32::API::Type::packing('PULONG');
219 0         0 $context = unpack $pack_type, $context;
220 0         0 my $crypt_gen_random =
221             Win32::API->new( 'advapi32', 'CryptGenRandom', 'NNP', 'I' );
222 0 0       0 return unless defined $crypt_gen_random;
223             return ('CryptGenRandom',
224             sub {
225 0     0   0 my $nbytes = shift;
226 0         0 my $buffer = chr(0) x $nbytes;
227 0         0 my $result = $crypt_gen_random->Call($context, $nbytes, $buffer);
228 0 0       0 croak "CryptGenRandom failed: $^E" unless $result;
229 0         0 return $buffer;
230             },
231 0         0 0, 1); # Assume non-blocking and strong
232             } else {
233 0         0 my $rtlgenrand = Win32::API->new( 'advapi32', <<'_RTLGENRANDOM_PROTO_');
234             INT SystemFunction036(
235             PVOID RandomBuffer,
236             ULONG RandomBufferLength
237             )
238             _RTLGENRANDOM_PROTO_
239 0 0       0 return unless defined $rtlgenrand;
240             return ('RtlGenRand',
241             sub {
242 0     0   0 my $nbytes = shift;
243 0         0 my $buffer = chr(0) x $nbytes;
244 0         0 my $result = $rtlgenrand->Call($buffer, $nbytes);
245 0 0       0 croak "RtlGenRand failed: $^E" unless $result;
246 0         0 return $buffer;
247             },
248 0         0 0, 1); # Assume non-blocking and strong
249             }
250 0         0 return;
251             }
252              
253             sub _try_egd {
254             # For locations, we'll look in the files OpenSSL's RAND_egd looks, as well
255             # as /etc/entropy which egd 0.9 recommends. This also works with PRNGD.
256             # PRNGD uses a seed+CSPRNG so is non-blocking, but we can't tell them apart.
257 13     13   27 foreach my $device (qw( /var/run/egd-pool /dev/egd-pool /etc/egd-pool /etc/entropy )) {
258 52 50 33     1147 next unless -r $device && -S $device;
259 0 0       0 eval { require IO::Socket; 1; } or return;
  0         0  
  0         0  
260             # We're looking for a socket that returns the entropy available when given
261             # that command. Set timeout to 1 to prevent hanging -- if it is a socket
262             # but won't return the available entropy in under a second, move on.
263 0         0 my $socket = IO::Socket::UNIX->new(Peer => $device, Timeout => 1);
264 0 0       0 next unless $socket;
265 0 0       0 $socket->syswrite( pack("C", 0x00), 1) or next;
266 0 0       0 die if $socket->error;
267 0         0 my($entropy_string, $nread);
268             # Sadly this doesn't honor the timeout. We'll have to do an eval / alarm.
269             # We only timeout here if this is a live socket to a sleeping process.
270 0         0 eval {
271 0     0   0 local $SIG{ALRM} = sub { die "alarm\n" };
  0         0  
272 0         0 alarm 1;
273 0         0 $nread = $socket->sysread($entropy_string, 4);
274 0         0 alarm 0;
275             };
276 0 0       0 if ($@) {
277 0 0       0 die unless $@ eq "alarm\n";
278 0         0 next;
279             }
280 0 0 0     0 next unless defined $nread && $nread == 4;
281 0         0 my $entropy_avail = unpack("N", $entropy_string);
282 0     0   0 return ('EGD', sub { __read_egd($device, @_); }, 1, 1);
  0         0  
283             }
284 13         30 return;
285             }
286              
287             sub __read_egd {
288 0     0     my ($device, $nbytes) = @_;
289 0 0         return unless defined $device;
290 0 0 0       return unless defined $nbytes && int($nbytes) > 0;
291 0 0 0       croak "$device doesn't exist!" unless -r $device && -S $device;
292 0           my $socket = IO::Socket::UNIX->new(Peer => $device);
293 0 0         croak "Can't talk to EGD on $device. $!" unless $socket;
294 0           my($s, $buffer, $toread) = ('', '', $nbytes);
295 0           while ($toread > 0) {
296 0 0         my $this_request = ($toread > 255) ? 255 : $toread;
297             # Use the blocking interface.
298 0           $socket->syswrite( pack("CC", 0x02, $this_request), 2);
299 0           my $this_grant = $socket->sysread($buffer, $this_request);
300 0 0 0       croak "Error reading EDG data from $device: $!\n"
301             unless defined $this_grant && $this_grant == $this_request;
302 0           $s .= $buffer;
303 0           $toread -= length($buffer);
304             }
305 0 0         croak "Internal EGD read error: wanted $nbytes, read ", length($s), ""
306             unless $nbytes == length($s); # assert
307 0           return $s;
308             }
309              
310             1;
311              
312             __END__