Issue #9569 has been updated by George Koehler.


I made myself a benchmark:
https://gist.github.com/kernigh/d169895a700c6511d08511c005a28d88

RAND_bytes() from OpenSSL seems to be faster than /dev/urandom on my computer. I'm running OpenBSD, and OpenSSL is really LibreSSL. At first it seems that /dev/urandom is just as fast as RAND_bytes():

~~~
$ ruby -v
ruby 2.4.0dev (2016-05-05 trunk 54913) [x86_64-openbsd5.9]
$ ruby benchrand.rb                                                            
                           user     system      total        real
Random::DEFAULT        0.410000   0.190000   0.600000 (  0.597455)
/dev/urandom           0.000000   1.340000   1.340000 (  1.395878)
arc4random_buf         1.210000   0.160000   1.370000 (  1.373183)
RAND_bytes             1.160000   0.260000   1.420000 (  1.508660)
gnutls_rnd NONCE       0.810000   0.180000   0.990000 (  0.988610)
gnutls_rnd RANDOM      1.940000   0.210000   2.150000 (  2.147149)
gnutls_rnd KEY         2.010000   0.140000   2.150000 (  2.147888)
PK11_GenerateRandom    4.030000   0.230000   4.260000 (  4.266837)
~~~

But if I run two instances in parallel on my dual-core machine:

~~~
$ for i in 1 2; do xterm -hold -e ruby benchrand.rb& done
~~~

then /dev/urandom is suddenly much slower:

~~~
                           user     system      total        real
Random::DEFAULT        0.450000   0.250000   0.700000 (  0.706498)
/dev/urandom           0.000000   2.470000   2.470000 (  2.549602)
arc4random_buf         1.260000   0.530000   1.790000 (  1.838425)
RAND_bytes             1.210000   0.470000   1.680000 (  1.795881)
gnutls_rnd NONCE       0.840000   0.450000   1.290000 (  1.335806)
gnutls_rnd RANDOM      1.960000   0.430000   2.390000 (  2.440970)
gnutls_rnd KEY         1.960000   0.440000   2.400000 (  2.434435)
PK11_GenerateRandom    4.070000   0.410000   4.480000 (  4.535968)
~~~

Seems that /dev/urandom on OpenBSD doesn't scale to multiple cores. So switching from RAND_bytes() to /dev/urandom would be bad idea. The other generators scale better: they are arc4random_buf() from libc, RAND_bytes() from LibreSSL, gnutls_rnd() from GnuTLS, and PK11_GenerateRandom() from NSS. In OpenBSD, RAND_bytes() seems to call arc4random_buf(), so their times are similar. I know that RAND_bytes() is different between OpenSSL and LibreSSL, and arc4random_buf() is different in other BSDs.

My benchmark is for 256 MB of random data. Real programs might not need so much randomness. PK11_GenerateRandom() imposes a limit of 65536 bytes per call, but this seems enough for NSS users like Firefox. So my benchmark might not be realistic.

Ruby has been avoiding /dev/urandom if possible. Ruby trunk works as Delan Azabani wrote in #9569-29, with one addition: as of a few days ago, fill_random_bytes_syscall() now tries arc4random_buf() first. So SecureRandom tries in order RAND_bytes(), arc4random_buf(), CryptGenRandom(), getrandom() before /dev/urandom.

----------------------------------------
Bug #9569: SecureRandom should try /dev/urandom first
https://bugs.ruby-lang.org/issues/9569#change-58534

* Author: Corey Csuhta
* Status: Rejected
* Priority: Normal
* Assignee: ruby-core
* ruby -v: 
* Backport: 
----------------------------------------
Right now, `SecureRandom.random_bytes` tries to detect an OpenSSL to use before it tries to detect `/dev/urandom`. I think it should be the other way around. In both cases, you just need random bytes to unpack, so SecureRandom could skip the middleman (and [second point of failure](http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/)) and just talk to `/dev/urandom` directly if it's available.

Is this a case of just re-ordering the two code chunks so that `/dev/urandom` is tried first?

Relevant lines: https://github.com/ruby/ruby/blob/trunk/lib/securerandom.rb#L59-L90



-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>