summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/securerandom.rb57
-rw-r--r--random.c15
-rw-r--r--test/ruby/test_rand.rb4
3 files changed, 57 insertions, 19 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
diff --git a/random.c b/random.c
index ddbbeedf92..6555cc54ad 100644
--- a/random.c
+++ b/random.c
@@ -603,11 +603,18 @@ random_seed(void)
}
/*
- * call-seq: Random.raw_seed(size) -> string
+ * call-seq: Random.urandom(size) -> string
*
- * Returns a raw seed string, using platform providing features.
+ * Returns a string, using platform providing features.
+ * Returned value expected to be a cryptographically secure
+ * pseudo-random number in binary form.
*
- * Random.raw_seed(8) #=> "\x78\x41\xBA\xAF\x7D\xEA\xD8\xEA"
+ * In 2017, Linux manpage random(7) writes that "no cryptographic
+ * primitive available today can hope to promise more than 256 bits of
+ * security". So it might be questionable to pass size > 32 to this
+ * method.
+ *
+ * Random.urandom(8) #=> "\x78\x41\xBA\xAF\x7D\xEA\xD8\xEA"
*/
static VALUE
random_raw_seed(VALUE self, VALUE size)
@@ -1616,7 +1623,7 @@ InitVM_Random(void)
rb_define_singleton_method(rb_cRandom, "srand", rb_f_srand, -1);
rb_define_singleton_method(rb_cRandom, "rand", random_s_rand, -1);
rb_define_singleton_method(rb_cRandom, "new_seed", random_seed, 0);
- rb_define_singleton_method(rb_cRandom, "raw_seed", random_raw_seed, 1);
+ rb_define_singleton_method(rb_cRandom, "urandom", random_raw_seed, 1);
rb_define_private_method(CLASS_OF(rb_cRandom), "state", random_s_state, 0);
rb_define_private_method(CLASS_OF(rb_cRandom), "left", random_s_left, 0);
diff --git a/test/ruby/test_rand.rb b/test/ruby/test_rand.rb
index 46d10f8386..79befa8f04 100644
--- a/test/ruby/test_rand.rb
+++ b/test/ruby/test_rand.rb
@@ -550,9 +550,9 @@ END
End
end
- def test_raw_seed
+ def test_urandom
[0, 1, 100].each do |size|
- v = Random.raw_seed(size)
+ v = Random.urandom(size)
assert_kind_of(String, v)
assert_equal(size, v.bytesize)
end