diff options
Diffstat (limited to 'Bugzilla/RNG.pm')
-rw-r--r-- | Bugzilla/RNG.pm | 245 |
1 files changed, 124 insertions, 121 deletions
diff --git a/Bugzilla/RNG.pm b/Bugzilla/RNG.pm index cc2dfc58c..b3b89a0ca 100644 --- a/Bugzilla/RNG.pm +++ b/Bugzilla/RNG.pm @@ -28,7 +28,7 @@ our @EXPORT_OK = qw(rand srand irand); use constant DIVIDE_BY => 2**32; # How many bytes of seed to read. -use constant SEED_SIZE => 16; # 128 bits. +use constant SEED_SIZE => 16; # 128 bits. ################# # Windows Stuff # @@ -43,6 +43,7 @@ use constant PROV_RSA_FULL => 1; # Flags for CryptGenRandom: # Don't ever display a UI to the user, just fail if one would be needed. use constant CRYPT_SILENT => 64; + # Don't require existing public/private keypairs. use constant CRYPT_VERIFYCONTEXT => 0xF0000000; @@ -60,40 +61,42 @@ END ################# sub rand (;$) { - my ($limit) = @_; - my $int = irand(); - return _to_float($int, $limit); + my ($limit) = @_; + my $int = irand(); + return _to_float($int, $limit); } sub irand (;$) { - my ($limit) = @_; - Bugzilla::RNG::srand() if !defined $RNG; - my $int = $RNG->irand(); - if (defined $limit) { - # We can't just use the mod operator because it will bias - # our output. Search for "modulo bias" on the Internet for - # details. This is slower than mod(), but does not have a bias, - # as demonstrated by Math::Random::Secure's uniform.t test. - return int(_to_float($int, $limit)); - } - return $int; + my ($limit) = @_; + Bugzilla::RNG::srand() if !defined $RNG; + my $int = $RNG->irand(); + if (defined $limit) { + + # We can't just use the mod operator because it will bias + # our output. Search for "modulo bias" on the Internet for + # details. This is slower than mod(), but does not have a bias, + # as demonstrated by Math::Random::Secure's uniform.t test. + return int(_to_float($int, $limit)); + } + return $int; } sub srand (;$) { - my ($value) = @_; - # Remove any RNG that might already have been made. - $RNG = undef; - my %args; - if (defined $value) { - $args{seed} = $value; - } - $RNG = _create_rng(\%args); + my ($value) = @_; + + # Remove any RNG that might already have been made. + $RNG = undef; + my %args; + if (defined $value) { + $args{seed} = $value; + } + $RNG = _create_rng(\%args); } sub _to_float { - my ($integer, $limit) = @_; - $limit ||= 1; - return ($integer / DIVIDE_BY) * $limit; + my ($integer, $limit) = @_; + $limit ||= 1; + return ($integer / DIVIDE_BY) * $limit; } ########################## @@ -101,123 +104,123 @@ sub _to_float { ########################## sub _create_rng { - my ($params) = @_; + my ($params) = @_; - if (!defined $params->{seed}) { - $params->{seed} = _get_seed(); - } + if (!defined $params->{seed}) { + $params->{seed} = _get_seed(); + } - _check_seed($params->{seed}); + _check_seed($params->{seed}); - my @seed_ints = unpack('L*', $params->{seed}); + my @seed_ints = unpack('L*', $params->{seed}); - my $rng = Math::Random::ISAAC->new(@seed_ints); + my $rng = Math::Random::ISAAC->new(@seed_ints); - # It's faster to skip the frontend interface of Math::Random::ISAAC - # and just use the backend directly. However, in case the internal - # code of Math::Random::ISAAC changes at some point, we do make sure - # that the {backend} element actually exists first. - return $rng->{backend} ? $rng->{backend} : $rng; + # It's faster to skip the frontend interface of Math::Random::ISAAC + # and just use the backend directly. However, in case the internal + # code of Math::Random::ISAAC changes at some point, we do make sure + # that the {backend} element actually exists first. + return $rng->{backend} ? $rng->{backend} : $rng; } sub _check_seed { - my ($seed) = @_; - if (length($seed) < 8) { - warn "Your seed is less than 8 bytes (64 bits). It could be" - . " easy to crack"; - } - # If it looks like we were seeded with a 32-bit integer, warn the - # user that they are making a dangerous, easily-crackable mistake. - elsif (length($seed) <= 10 and $seed =~ /^\d+$/) { - warn "RNG seeded with a 32-bit integer, this is easy to crack"; - } + my ($seed) = @_; + if (length($seed) < 8) { + warn "Your seed is less than 8 bytes (64 bits). It could be" . " easy to crack"; + } + + # If it looks like we were seeded with a 32-bit integer, warn the + # user that they are making a dangerous, easily-crackable mistake. + elsif (length($seed) <= 10 and $seed =~ /^\d+$/) { + warn "RNG seeded with a 32-bit integer, this is easy to crack"; + } } sub _get_seed { - return _windows_seed() if ON_WINDOWS; + return _windows_seed() if ON_WINDOWS; - if (-r '/dev/urandom') { - return _read_seed_from('/dev/urandom'); - } + if (-r '/dev/urandom') { + return _read_seed_from('/dev/urandom'); + } - return _read_seed_from('/dev/random'); + return _read_seed_from('/dev/random'); } sub _read_seed_from { - my ($from) = @_; - - my $fh = IO::File->new($from, "r") or die "$from: $!"; - my $buffer; - $fh->read($buffer, SEED_SIZE); - if (length($buffer) < SEED_SIZE) { - die "Could not read enough seed bytes from $from, got only " - . length($buffer); - } - $fh->close; - return $buffer; + my ($from) = @_; + + my $fh = IO::File->new($from, "r") or die "$from: $!"; + my $buffer; + $fh->read($buffer, SEED_SIZE); + if (length($buffer) < SEED_SIZE) { + die "Could not read enough seed bytes from $from, got only " . length($buffer); + } + $fh->close; + return $buffer; } sub _windows_seed { - my ($major, $minor) = (Win32::GetOSVersion())[1,2]; - if ($major < 5) { - die "Bugzilla does not support versions of Windows before" - . " Windows 2000"; - } - # This means Windows 2000. - if ($major == 5 and $minor == 0) { - return _win2k_seed(); - } - - my $rtlgenrand = Win32::API->new('advapi32', RTLGENRANDOM_PROTO); - if (!defined $rtlgenrand) { - die "Could not import RtlGenRand: $^E"; - } - my $buffer = chr(0) x SEED_SIZE; - my $result = $rtlgenrand->Call($buffer, SEED_SIZE); - if (!$result) { - die "RtlGenRand failed: $^E"; - } - return $buffer; + my ($major, $minor) = (Win32::GetOSVersion())[1, 2]; + if ($major < 5) { + die "Bugzilla does not support versions of Windows before" . " Windows 2000"; + } + + # This means Windows 2000. + if ($major == 5 and $minor == 0) { + return _win2k_seed(); + } + + my $rtlgenrand = Win32::API->new('advapi32', RTLGENRANDOM_PROTO); + if (!defined $rtlgenrand) { + die "Could not import RtlGenRand: $^E"; + } + my $buffer = chr(0) x SEED_SIZE; + my $result = $rtlgenrand->Call($buffer, SEED_SIZE); + if (!$result) { + die "RtlGenRand failed: $^E"; + } + return $buffer; } sub _win2k_seed { - my $crypt_acquire = Win32::API->new( - "advapi32", 'CryptAcquireContext', 'PPPNN', 'I'); - if (!defined $crypt_acquire) { - die "Could not import CryptAcquireContext: $^E"; - } - - my $crypt_release = Win32::API->new( - "advapi32", 'CryptReleaseContext', 'NN', 'I'); - if (!defined $crypt_release) { - die "Could not import CryptReleaseContext: $^E"; - } - - my $crypt_gen_random = Win32::API->new( - "advapi32", 'CryptGenRandom', 'NNP', 'I'); - if (!defined $crypt_gen_random) { - die "Could not import CryptGenRandom: $^E"; - } - - my $context = chr(0) x Win32::API::Type->sizeof('PULONG'); - my $acquire_result = $crypt_acquire->Call( - $context, 0, 0, PROV_RSA_FULL, CRYPT_SILENT | CRYPT_VERIFYCONTEXT); - if (!defined $acquire_result) { - die "CryptAcquireContext failed: $^E"; - } - - my $pack_type = Win32::API::Type::packing('PULONG'); - $context = unpack($pack_type, $context); - - my $buffer = chr(0) x SEED_SIZE; - my $rand_result = $crypt_gen_random->Call($context, SEED_SIZE, $buffer); - my $rand_error = $^E; - # We don't check this if it fails, we don't care. - $crypt_release->Call($context, 0); - if (!defined $rand_result) { - die "CryptGenRandom failed: $rand_error"; - } - return $buffer; + my $crypt_acquire + = Win32::API->new("advapi32", 'CryptAcquireContext', 'PPPNN', 'I'); + if (!defined $crypt_acquire) { + die "Could not import CryptAcquireContext: $^E"; + } + + my $crypt_release + = Win32::API->new("advapi32", 'CryptReleaseContext', 'NN', 'I'); + if (!defined $crypt_release) { + die "Could not import CryptReleaseContext: $^E"; + } + + my $crypt_gen_random + = Win32::API->new("advapi32", 'CryptGenRandom', 'NNP', 'I'); + if (!defined $crypt_gen_random) { + die "Could not import CryptGenRandom: $^E"; + } + + my $context = chr(0) x Win32::API::Type->sizeof('PULONG'); + my $acquire_result = $crypt_acquire->Call($context, 0, 0, PROV_RSA_FULL, + CRYPT_SILENT | CRYPT_VERIFYCONTEXT); + if (!defined $acquire_result) { + die "CryptAcquireContext failed: $^E"; + } + + my $pack_type = Win32::API::Type::packing('PULONG'); + $context = unpack($pack_type, $context); + + my $buffer = chr(0) x SEED_SIZE; + my $rand_result = $crypt_gen_random->Call($context, SEED_SIZE, $buffer); + my $rand_error = $^E; + + # We don't check this if it fails, we don't care. + $crypt_release->Call($context, 0); + if (!defined $rand_result) { + die "CryptGenRandom failed: $rand_error"; + } + return $buffer; } 1; |