186 lines
5.2 KiB
Perl
186 lines
5.2 KiB
Perl
package Math::Prime::Util::Entropy;
|
|
use strict;
|
|
use warnings;
|
|
use Carp qw/carp croak confess/;
|
|
|
|
BEGIN {
|
|
$Math::Prime::Util::Entropy::AUTHORITY = 'cpan:DANAJ';
|
|
$Math::Prime::Util::Entropy::VERSION = '0.73';
|
|
}
|
|
|
|
sub _read_file {
|
|
my($file, $nbytes) = @_;
|
|
use Fcntl;
|
|
my($s, $buffer, $nread) = ('', '', 0);
|
|
return unless -r $file;
|
|
sysopen(my $fh, $file, O_RDONLY);
|
|
binmode $fh;
|
|
while ($nread < $nbytes) {
|
|
my $thisread = sysread $fh, $buffer, $nbytes-$nread;
|
|
last unless defined $thisread && $thisread > 0;
|
|
$s .= $buffer;
|
|
$nread += length($buffer);
|
|
}
|
|
return unless $nbytes == length($s);
|
|
return $s;
|
|
}
|
|
|
|
sub _try_urandom {
|
|
if (-r "/dev/urandom") {
|
|
return ('urandom', sub { _read_file("/dev/urandom",shift); }, 0, 1);
|
|
}
|
|
if (-r "/dev/random") {
|
|
return ('random', sub { _read_file("/dev/random",shift); }, 1, 1);
|
|
}
|
|
return;
|
|
}
|
|
|
|
sub _try_win32 {
|
|
return unless $^O eq 'MSWin32';
|
|
eval { require Win32; require Win32::API; require Win32::API::Type; 1; }
|
|
or return;
|
|
use constant CRYPT_SILENT => 0x40; # Never display a UI.
|
|
use constant PROV_RSA_FULL => 1; # Which service provider.
|
|
use constant VERIFY_CONTEXT => 0xF0000000; # Don't need existing keepairs
|
|
use constant W2K_MAJOR_VERSION => 5; # Windows 2000
|
|
use constant W2K_MINOR_VERSION => 0;
|
|
my ($major, $minor) = (Win32::GetOSVersion())[1, 2];
|
|
return if $major < W2K_MAJOR_VERSION;
|
|
|
|
if ($major == W2K_MAJOR_VERSION && $minor == W2K_MINOR_VERSION) {
|
|
# We are Windows 2000. Use the older CryptGenRandom interface.
|
|
my $crypt_acquire_context_a =
|
|
Win32::API->new('advapi32','CryptAcquireContextA','PPPNN','I');
|
|
return unless defined $crypt_acquire_context_a;
|
|
my $context = chr(0) x Win32::API::Type->sizeof('PULONG');
|
|
my $result = $crypt_acquire_context_a->Call(
|
|
$context, 0, 0, PROV_RSA_FULL, CRYPT_SILENT | VERIFY_CONTEXT );
|
|
return unless $result;
|
|
my $pack_type = Win32::API::Type::packing('PULONG');
|
|
$context = unpack $pack_type, $context;
|
|
my $crypt_gen_random =
|
|
Win32::API->new( 'advapi32', 'CryptGenRandom', 'NNP', 'I' );
|
|
return unless defined $crypt_gen_random;
|
|
return ('CryptGenRandom',
|
|
sub {
|
|
my $nbytes = shift;
|
|
my $buffer = chr(0) x $nbytes;
|
|
my $result = $crypt_gen_random->Call($context, $nbytes, $buffer);
|
|
croak "CryptGenRandom failed: $^E" unless $result;
|
|
return $buffer;
|
|
}, 0, 1); # Assume non-blocking and strong
|
|
} else {
|
|
my $rtlgenrand = Win32::API->new( 'advapi32', <<'_RTLGENRANDOM_PROTO_');
|
|
INT SystemFunction036(
|
|
PVOID RandomBuffer,
|
|
ULONG RandomBufferLength
|
|
)
|
|
_RTLGENRANDOM_PROTO_
|
|
return unless defined $rtlgenrand;
|
|
return ('RtlGenRand',
|
|
sub {
|
|
my $nbytes = shift;
|
|
my $buffer = chr(0) x $nbytes;
|
|
my $result = $rtlgenrand->Call($buffer, $nbytes);
|
|
croak "RtlGenRand failed: $^E" unless $result;
|
|
return $buffer;
|
|
}, 0, 1); # Assume non-blocking and strong
|
|
}
|
|
return;
|
|
}
|
|
|
|
sub _try_crypt_prng {
|
|
return unless eval { require Crypt::PRNG; 1; };
|
|
return ('Crypt::PRNG', sub { Crypt::PRNG::random_bytes(shift) }, 0, 1);
|
|
}
|
|
|
|
sub _try_crypt_random_seed {
|
|
return unless eval { require Crypt::Random::Seed; 1; };
|
|
return ('Crypt::Random::Seed', sub { my $source = Crypt::Random::Seed->new(NonBlocking=>1); return unless $source; $source->random_bytes(shift) }, 0, 1);
|
|
}
|
|
|
|
my $_method;
|
|
|
|
sub entropy_bytes {
|
|
my $nbytes = shift;
|
|
my @methodlist = ( \&_try_win32, # All we have for Windows
|
|
\&_try_urandom, # Best if available
|
|
\&_try_crypt_random_seed, # More sources, fallbacks
|
|
\&_try_crypt_prng, # Good CSPRNG, worse seeding
|
|
);
|
|
|
|
if (!defined $_method) {
|
|
foreach my $m (@methodlist) {
|
|
my ($name, $rsub, $isblocking, $isstrong) = $m->();
|
|
if (defined $name) {
|
|
$_method = $rsub;
|
|
last;
|
|
}
|
|
}
|
|
}
|
|
return unless defined $_method;
|
|
$_method->($nbytes);
|
|
}
|
|
|
|
1;
|
|
|
|
__END__
|
|
|
|
|
|
# ABSTRACT: Get a good random seed
|
|
|
|
=pod
|
|
|
|
=encoding utf8
|
|
|
|
=head1 NAME
|
|
|
|
Math::Prime::Util::Entropy - Get a good random seed
|
|
|
|
|
|
=head1 VERSION
|
|
|
|
Version 0.73
|
|
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
Provides a single method to get a good seed if possible. This is a streamlined
|
|
version of L<Crypt::Random::Seed>, with ideas from L<Bytes::Random::Secure::Tiny>.
|
|
|
|
=head2 entropy_bytes
|
|
|
|
Takes a number of bytes C<n> and returns either undef (no good seed available) or
|
|
a binary string with good entropy.
|
|
|
|
We try in order:
|
|
|
|
- the Win32 Crypto API
|
|
- /dev/urandom
|
|
- /dev/random
|
|
- L<Crypt::Random::Seed>
|
|
- L<Crypt::PRNG>
|
|
|
|
=head1 SEE ALSO
|
|
|
|
L<Math::Prime::Util>
|
|
L<Crypt::Random::Seed>
|
|
L<Bytes::Random::Secure>
|
|
L<Bytes::Random::Secure::Tiny>
|
|
L<Crypt::PRNG>
|
|
|
|
=head1 AUTHORS
|
|
|
|
Dana Jacobsen E<lt>dana@acm.orgE<gt>
|
|
|
|
|
|
=head1 COPYRIGHT
|
|
|
|
Copyright 2017 by Dana Jacobsen E<lt>dana@acm.orgE<gt>
|
|
|
|
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
|
|
|
|
=cut
|