File Coverage

blib/lib/Math/Prime/Util/Entropy.pm
Criterion Covered Total %
statement 55 103 53.4
branch 9 42 21.4
condition 1 6 16.6
subroutine 15 22 68.1
pod 1 1 100.0
total 81 174 46.5


line stmt bran cond sub pod time code
1             package Math::Prime::Util::Entropy;
2 1     1   7 use strict;
  1         3  
  1         39  
3 1     1   5 use warnings;
  1         1  
  1         66  
4 1     1   4 use Carp qw/carp croak confess/;
  1         2  
  1         95  
5              
6             BEGIN {
7 1     1   38 $Math::Prime::Util::Entropy::AUTHORITY = 'cpan:DANAJ';
8 1         61 $Math::Prime::Util::Entropy::VERSION = '0.74';
9             }
10              
11             sub _read_file {
12 1     1   5 my($file, $nbytes) = @_;
13 1     1   8 use Fcntl;
  1         1  
  1         582  
14 1         3 my($s, $buffer, $nread) = ('', '', 0);
15 1 50       13 return unless -r $file;
16 1         46 sysopen(my $fh, $file, O_RDONLY);
17 1         5 binmode $fh;
18 1         5 while ($nread < $nbytes) {
19 1         9 my $thisread = sysread $fh, $buffer, $nbytes-$nread;
20 1 50 33     49 last unless defined $thisread && $thisread > 0;
21 1         4 $s .= $buffer;
22 1         3 $nread += length($buffer);
23             }
24 1 50       3 return unless $nbytes == length($s);
25 1         16 return $s;
26             }
27              
28             sub _try_urandom {
29 1 50   1   49 if (-r "/dev/urandom") {
30 1     1   10 return ('urandom', sub { _read_file("/dev/urandom",shift); }, 0, 1);
  1         25  
31             }
32 0 0       0 if (-r "/dev/random") {
33 0     0   0 return ('random', sub { _read_file("/dev/random",shift); }, 1, 1);
  0         0  
34             }
35 0         0 return;
36             }
37              
38             sub _try_win32 {
39 1 50   1   6 return unless $^O eq 'MSWin32';
40 0 0       0 eval { require Win32; require Win32::API; require Win32::API::Type; 1; }
  0         0  
  0         0  
  0         0  
  0         0  
41             or return;
42 1     1   8 use constant CRYPT_SILENT => 0x40; # Never display a UI.
  1         1  
  1         77  
43 1     1   5 use constant PROV_RSA_FULL => 1; # Which service provider.
  1         2  
  1         101  
44 1     1   6 use constant VERIFY_CONTEXT => 0xF0000000; # Don't need existing keepairs
  1         2  
  1         52  
45 1     1   4 use constant W2K_MAJOR_VERSION => 5; # Windows 2000
  1         2  
  1         39  
46 1     1   4 use constant W2K_MINOR_VERSION => 0;
  1         1  
  1         727  
47 0         0 my ($major, $minor) = (Win32::GetOSVersion())[1, 2];
48 0 0       0 return if $major < W2K_MAJOR_VERSION;
49              
50 0 0 0     0 if ($major == W2K_MAJOR_VERSION && $minor == W2K_MINOR_VERSION) {
51             # We are Windows 2000. Use the older CryptGenRandom interface.
52 0         0 my $crypt_acquire_context_a =
53             Win32::API->new('advapi32','CryptAcquireContextA','PPPNN','I');
54 0 0       0 return unless defined $crypt_acquire_context_a;
55 0         0 my $context = chr(0) x Win32::API::Type->sizeof('PULONG');
56 0         0 my $result = $crypt_acquire_context_a->Call(
57             $context, 0, 0, PROV_RSA_FULL, CRYPT_SILENT | VERIFY_CONTEXT );
58 0 0       0 return unless $result;
59 0         0 my $pack_type = Win32::API::Type::packing('PULONG');
60 0         0 $context = unpack $pack_type, $context;
61 0         0 my $crypt_gen_random =
62             Win32::API->new( 'advapi32', 'CryptGenRandom', 'NNP', 'I' );
63 0 0       0 return unless defined $crypt_gen_random;
64             return ('CryptGenRandom',
65             sub {
66 0     0   0 my $nbytes = shift;
67 0         0 my $buffer = chr(0) x $nbytes;
68 0         0 my $result = $crypt_gen_random->Call($context, $nbytes, $buffer);
69 0 0       0 croak "CryptGenRandom failed: $^E" unless $result;
70 0         0 return $buffer;
71 0         0 }, 0, 1); # Assume non-blocking and strong
72             } else {
73 0         0 my $rtlgenrand = Win32::API->new( 'advapi32', <<'_RTLGENRANDOM_PROTO_');
74             INT SystemFunction036(
75             PVOID RandomBuffer,
76             ULONG RandomBufferLength
77             )
78             _RTLGENRANDOM_PROTO_
79 0 0       0 return unless defined $rtlgenrand;
80             return ('RtlGenRand',
81             sub {
82 0     0   0 my $nbytes = shift;
83 0         0 my $buffer = chr(0) x $nbytes;
84 0         0 my $result = $rtlgenrand->Call($buffer, $nbytes);
85 0 0       0 croak "RtlGenRand failed: $^E" unless $result;
86 0         0 return $buffer;
87 0         0 }, 0, 1); # Assume non-blocking and strong
88             }
89 0         0 return;
90             }
91              
92             sub _try_crypt_prng {
93 0 0   0   0 return unless eval { require Crypt::PRNG; 1; };
  0         0  
  0         0  
94 0     0   0 return ('Crypt::PRNG', sub { Crypt::PRNG::random_bytes(shift) }, 0, 1);
  0         0  
95             }
96              
97             sub _try_crypt_random_seed {
98 0 0   0   0 return unless eval { require Crypt::Random::Seed; 1; };
  0         0  
  0         0  
99 0 0   0   0 return ('Crypt::Random::Seed', sub { my $source = Crypt::Random::Seed->new(NonBlocking=>1); return unless $source; $source->random_bytes(shift) }, 0, 1);
  0         0  
  0         0  
  0         0  
100             }
101              
102             my $_method;
103              
104             sub entropy_bytes {
105 1     1 1 2 my $nbytes = shift;
106 1         5 my @methodlist = ( \&_try_win32, # All we have for Windows
107             \&_try_urandom, # Best if available
108             \&_try_crypt_random_seed, # More sources, fallbacks
109             \&_try_crypt_prng, # Good CSPRNG, worse seeding
110             );
111              
112 1 50       4 if (!defined $_method) {
113 1         3 foreach my $m (@methodlist) {
114 2         10 my ($name, $rsub, $isblocking, $isstrong) = $m->();
115 2 100       7 if (defined $name) {
116 1         2 $_method = $rsub;
117 1         3 last;
118             }
119             }
120             }
121 1 50       3 return unless defined $_method;
122 1         3 $_method->($nbytes);
123             }
124              
125             1;
126              
127             __END__