Is Ruby's seed for OpenSSL::Random sufficient?
cryptography, prng, ruby, security
Solution
It depends on the configuration of Ruby which RNG is used:
Secure random number generator interface.
This library is an interface for secure random number generator which is suitable for generating session key in HTTP cookies, etc.
It supports following secure random number generators.
openssl
`/dev/urandom`
Win32
All three of the above are generally considered secure. However, it depends on the implementation of the `SecureRandom` class if it actually is secure. The only way of knowing this is an extensive research into the implementations.
Looking at the code in the question it is clear that Ruby directly uses bytes generated by OpenSSL, after additionally seeding the PID:
Whenever seed data is added, it is inserted into the 'state' as follows.
The input is chopped up into units of 20 bytes (or less for the last block). Each of these blocks is run through the hash function as follows: The data passed to the hash function is the current 'md', the same number of bytes from the 'state' (the location determined by in incremented looping index) as the current 'block', the new key data 'block', and 'count' (which is incremented after each use). The result of this is kept in 'md' and also xored into the 'state' at the same locations that were used as input into the hash function. I believe this system addresses points 1 (hash function; currently SHA-1), 3 (the 'state'), 4 (via the 'md'), 5 (by the use of a hash function and xor).
Problem
I know very little about Ruby, so please forgive me if the answer to this is obvious. I noticed at http://www.ruby-doc.org/stdlib-1.9.3/libdoc/securerandom/rdoc/SecureRandom.html that Ruby uses the pid and the current time to seed OpenSSL::Random when a call to random_bytes is made. Unless something else happens under the covers, isn't this pretty much the seed that Netscape used in their initial SSL implementation in the mid 90s? http://en.wikipedia.org/wiki/Random_number_generator_attack#Prominent_examples_of_random_number_generator_security_issues Surely Ruby hasn't revived an 18 year old bug. What am I missing here? Edit: Here's the source for random_bytes. Notice the first check to see if ruby was compiled with OpenSSL, in which case it seeds it with the pid and current time. ``` def self.random_bytes(n=nil) n = n ? n.to_int : 16 if defined? OpenSSL::Random @pid = 0 if !defined?(@pid) pid = $$ if @pid != pid now = Time.now ary = [now.to_i, now.nsec, @pid, pid] OpenSSL::Random.seed(ary.to_s) @pid = pid end return OpenSSL::Random.random_bytes(n) end if !defined?(@has_urandom) || @has_urandom flags = File::RDONLY flags |= File::NONBLOCK if defined? File::NONBLOCK flags |= File::NOCTTY if defined? File::NOCTTY begin File.open("/dev/urandom", flags) {|f| unless f.stat.chardev? raise Errno::ENOENT end @has_urandom = true ret = f.readpartial(n) if ret.length != n raise NotImplementedError, "Unexpected partial read from random device: only #{ret.length} for #{n} bytes" end return ret } rescue Errno::ENOENT @has_urandom = false end end if !defined?(@has_win32) begin require 'Win32API' crypt_acquire_context = Win32API.new("advapi32", "CryptAcquireContext", 'PPPII', 'L') @crypt_gen_random = Win32API.new("advapi32", "CryptGenRandom", 'LIP', 'L') hProvStr = " " * 4 prov_rsa_full = 1 crypt_verifycontext = 0xF0000000 if crypt_acquire_context.call(hProvStr, nil, nil, prov_rsa_full, crypt_verifycontext) == 0 raise SystemCallError, "CryptAcquireContext failed: #{lastWin32ErrorMessage}" end @hProv, = hProvStr.unpack('L') @has_win32 = true rescue LoadError @has_win32 = false end end if @has_win32 bytes = " ".force_encoding("ASCII-8BIT") * n if @crypt_gen_random.call(@hProv, bytes.size, bytes) == 0 raise SystemCallError, "CryptGenRandom failed: #{lastWin32ErrorMessage}" end return bytes end raise NotImplementedError, "No random device" end ```