summaryrefslogtreecommitdiff
path: root/lib/securerandom.rb
diff options
context:
space:
mode:
authorshyouhei <shyouhei@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2017-01-20 08:00:00 +0000
committershyouhei <shyouhei@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2017-01-20 08:00:00 +0000
commitabae70d6ed63054d7d01bd6cd80c1b5b98b93ba3 (patch)
tree3de590fcf8f22217987675fbc5eea399597c8fca /lib/securerandom.rb
parente95eb9584ac85b728734d77e07fa27f23c91f38d (diff)
SecureRandom should try /dev/urandom first [Bug #9569]
* random.c (InitVM_Random): rename Random.raw_seed to Random.urandom. A quick search seems there are no practical use of this method than securerandom.rb so I think it's OK to rename but if there are users of it, this hunk is subject to revert. * test/ruby/test_rand.rb (TestRand#test_urandom): test for it. * lib/securerandom.rb (SecureRandom.gen_random): Prefer OS- provided CSPRNG if available. Otherwise falls back to OpenSSL. Current preference is: 1. CSPRNG routine that the OS has; one of - getrandom(2), - arc4random(3), or - CryptGenRandom() 2. /dev/urandom device 3. OpenSSL's RAND_bytes(3) If none of above random number generators are available, you cannot use this module. An exception is raised that case. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@57384 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/securerandom.rb')
-rw-r--r--lib/securerandom.rb57
1 files changed, 44 insertions, 13 deletions
diff --git a/lib/securerandom.rb b/lib/securerandom.rb
index 9201c9337a..3f8845ea38 100644
--- a/lib/securerandom.rb
+++ b/lib/securerandom.rb
@@ -1,9 +1,5 @@
# -*- coding: us-ascii -*-
# frozen_string_literal: true
-begin
- require 'openssl'
-rescue LoadError
-end
# == Secure random number generator interface.
#
@@ -48,8 +44,47 @@ end
#
module SecureRandom
- if defined?(OpenSSL::Random) && /mswin|mingw/ !~ RUBY_PLATFORM
- def self.gen_random(n)
+ @rng_chooser = Mutex.new # :nodoc:
+
+ class << self
+ def bytes(n)
+ return gen_random(n)
+ end
+
+ def gen_random(n)
+ ret = Random.urandom(n)
+ if ret.nil?
+ begin
+ require 'openssl'
+ rescue NoMethodError
+ raise NotImplementedError, "No random device"
+ else
+ @rng_chooser.synchronize do
+ class << self
+ remove_method :gen_random
+ alias gen_random gen_random_openssl
+ end
+ end
+ return gen_random(n)
+ end
+ elsif ret.length != n
+ raise NotImplementedError, \
+ "Unexpected partial read from random device: " \
+ "only #{ret.length} for #{n} bytes"
+ else
+ @rng_chooser.synchronize do
+ class << self
+ remove_method :gen_random
+ alias gen_random gen_random_urandom
+ end
+ end
+ return gen_random(n)
+ end
+ end
+
+ private
+
+ def gen_random_openssl(n)
@pid = 0 unless defined?(@pid)
pid = $$
unless @pid == pid
@@ -63,9 +98,9 @@ module SecureRandom
end
return OpenSSL::Random.random_bytes(n)
end
- else
- def self.gen_random(n)
- ret = Random.raw_seed(n)
+
+ def gen_random_urandom(n)
+ ret = Random.urandom(n)
unless ret
raise NotImplementedError, "No random device"
end
@@ -75,10 +110,6 @@ module SecureRandom
ret
end
end
-
- class << self
- alias bytes gen_random
- end
end
module Random::Formatter