File Coverage

blib/lib/Math/Random/Secure/RNG.pm
Criterion Covered Total %
statement 41 42 97.6
branch 11 14 78.5
condition 2 3 66.6
subroutine 13 13 100.0
pod 0 1 0.0
total 67 73 91.7


line stmt bran cond sub pod time code
1             package Math::Random::Secure::RNG;
2             BEGIN {
3 5     5   557 $Math::Random::Secure::RNG::VERSION = '0.06';
4             }
5 5     5   2101 use Moo;
  5         45563  
  5         21  
6 5     5   7133 use Math::Random::ISAAC;
  5         4608  
  5         118  
7 5     5   2057 use Crypt::Random::Source::Factory;
  5         381370  
  5         267  
8 5 50   5   31 use constant ON_WINDOWS => $^O =~ /Win32/i ? 1 : 0;
  5         6  
  5         334  
9 5     5   2455 use if ON_WINDOWS, 'Crypt::Random::Source::Strong::Win32';
  5         37  
  5         22  
10              
11             has seeder => (
12             is => 'ro',
13             lazy => 1,
14             builder => '_build_seeder',
15             );
16             # Default to a 512-bit key, which should be impossible to break. I wrote
17             # to the author of ISAAC and he said it's fine to not use a full 256
18             # integers to seed ISAAC.
19             has seed_size => (
20             is => 'ro',
21             # isa => 'Int',
22             default => 64,
23             );
24              
25             has seed => (
26             is => 'rw',
27             isa => \&_check_seed,
28             lazy => 1,
29             builder => '_build_seed',
30             clearer => 'clear_seed',
31             predicate => 'has_seed',
32             );
33              
34             has rng => (
35             is => 'ro',
36             lazy => 1,
37             builder => '_build_rng',
38             handles => ['irand', 'rand'],
39             clearer => 'clear_rng',
40             );
41              
42             has _for_pid => (
43             is => 'rw',
44             default => sub { $$ },
45             );
46              
47 1     1   78 sub _clear_for_pid { shift->_for_pid($$) }
48              
49             before qw(irand rand) => '_maybe_clear_seed';
50              
51             sub _maybe_clear_seed {
52 80605     80605   244957 my $self = shift;
53              
54 80605 100       1247812 if ($self->_for_pid != $$) {
55 1         85 $self->clear_seed;
56 1         44 $self->_clear_for_pid;
57             }
58             }
59              
60              
61             sub BUILD {
62 9     9 0 82 my ($self) = @_;
63              
64 9 100       137 if ($self->seed_size < 8) {
65 2         34 warn "Setting seed_size to less than 8 is not recommended";
66             }
67             }
68              
69             sub _check_seed {
70 22     22   6646 my ($seed) = @_;
71 22 100 66     162 if (length($seed) < 8) {
    100          
72 4         44 warn "Your seed is less than 8 bytes (64 bits). It could be"
73             . " easy to crack";
74             }
75             # If it looks like we were seeded with a 32-bit integer, warn the
76             # user that they are making a dangerous, easily-crackable mistake.
77             # We do this during BUILD so that it happens during srand() in
78             # Math::Secure::RNG.
79             elsif (length($seed) <= 10 and $seed =~ /^\d+$/) {
80 4         28 warn "RNG seeded with a 32-bit integer, this is easy to crack";
81             }
82              
83             # _check_seed is used as a type constraint, so needs to return 1.
84 22         559 return 1;
85             }
86              
87             sub _build_seeder {
88 3     3   900 my $factory = Crypt::Random::Source::Factory->new();
89             # On Windows, we want to always pick Crypt::Random::Source::Strong::Win32,
90             # which this will do.
91 3         6723 if (ON_WINDOWS) {
92             return $factory->get_strong;
93             }
94              
95 3         15 my $source = $factory->get;
96             # Never allow rand() to be used as a source, it cannot possibly be
97             # cryptographically strong with 2^15 or 2^32 bits for its seed.
98 3 50       53946 if ($source->isa('Crypt::Random::Source::Weak::rand')) {
99 0         0 $source = $factory->get_strong;
100             }
101 3         48 return $source;
102             }
103              
104             sub _build_seed {
105 6     6   1349 my ($self) = @_;
106 6         88 return $self->seeder->get($self->seed_size);
107             }
108              
109             sub _build_rng {
110 12     12   1311 my ($self) = @_;
111 12         738 my @seed_ints = unpack('L*', $self->seed);
112 12         190 my $rng = Math::Random::ISAAC->new(@seed_ints);
113             # One part of having a cryptographically-secure RNG is not being
114             # able to see the seed in the internal state of the RNG.
115 12         366 $self->clear_seed;
116             # It's faster to skip the frontend interface of Math::Random::ISAAC
117             # and just use the backend directly. However, in case the internal
118             # code of Math::Random::ISAAC changes at some point, we do make sure
119             # that the {backend} element actually exists first.
120 12 50       1026 return $rng->{backend} ? $rng->{backend} : $rng;
121             }
122              
123             1;
124              
125             __END__