SecurerRandomer
This gem is deprecated. Please use sysrandom instead.
Ruby's SecureRandom prefers OpenSSL over other mechanisms (such as
/dev/urandom
and getrandom(2)
). This has recently garnered some
criticism.
RbNaCl provides Ruby bindings to libsodium—a portable crypto
library and fork of NaCl by Daniel J. Bernstein that includes hooks to
alternative, OpenSSL-free pseudo-random number generators (PRNGs) such as
getrandom(2)
on modern Linux kernels and RtlGenRandom()
on Windows.
This gem monkeypatches RbNaCl into SecureRandom and aims to be "bug-for-bug"
compatible with the "stock" implementation of SecureRandom across Ruby
versions. It also provides a bonus "do what I mean" random number method that
can be used instead of Kernel.rand
and SecureRandom.random_number
.
History
This gem started out as a very simple monkeypatch to
SecureRandom.random_bytes
and grew as I dug deeper. In newer rubies, for
example, you need to patch .gen_random
instead of .random_bytes
, and it has
a different calling signature.
Some rubies use OpenSSL for SecureRandom.random_number
as well, while others
appear to rely on Kernel.rand
. Addressing this proved to be tricky due to
inconsistencies of these two methods between Ruby implementations and versions.
For example:
-
Kernel.rand(nil)
andSecureRandom.random_number(nil)
both return a floatn
such that0.0 <= n < 1.0
in Ruby 2.3; butSecureRandom.random_number(nil)
throws an ArgumentError in Ruby 2.2 - Kernel
.rand
with an inverted range (e.g.0..-10
) returnsnil
in Ruby 2.2+, but SecureRandom.random_number
throws an ArgumentError in Ruby 2.2 and returns a floatn
such that0.0 <= n < 1.0
in Ruby 2.3
Branching logic, edge cases, and tests started to accumulate so I decided it was probably a good idea to gemify this!
Why a monkeypatch?
The concept of monkeypatching in Ruby is a sensitive subject. It has the potential to break things in unexpected ways and make Ruby code more difficult to troubleshoot, and it's these kinds of practices that give Ruby (and Rubyists) a bad name. It was actually way more labor-intensive to write this as a monkeypatch rather than landing it as a completely separate module. So why?
Simply put, I do not anticipate this gem being a long-term solution! At some
point in the hopefully-near future I would like to see the Ruby core team
acquiesce to community pressure and modify securerandom.rb
and random.c
to
not utilize OpenSSL and prefer getrandom(2)
over urandom
. And so my goal
was not to write some super-maintainable, standalone piece of software, but
rather a temporary fix that can be easily dropped into an existing project and
easily pulled out at a later date. A monkeypatch is the best way to achieve
this.
Features
-
SecureRandom
.gen_random
(or.random_bytes
)Monkeypatches SecureRandom such that its various formatter methods (
.uuid
,.hex
,.base64
,.urlsafe_base64
, and.random_bytes
) use RbNaCl for random byte generation instead of OpenSSL. -
SecureRandom
.random_number
Monkeypatches SecureRandom such that it uses SecurerRandomer
.kernel_rand
instead of OpenSSL to generate random numbers from numeric types and ranges. It is bug-for-bug compatible with "stock" SecureRandom, meaning it "chokes" on the same inputs and throws the same exception types.If you prefer to use Kernel
.rand
instead of SecurerRandomer.kernel_rand
, add this statement to your bootup process after requiring SecurerRandomer:SecurerRandomer::KERNEL_RAND = Kernel.method(:rand)
-
SecurerRandomer
.kernel_rand
A bug-for-bug reimplementation of Kernel
.rand
—meaning it "chokes" on the same inputs and throws the same exception types—that uses RbNaCl as its source of entropy. -
SecurerRandomer
.rand
An idealistic, "do what I mean" random number method that accepts a variety of inputs and returns what you might expect. Whereas
Kernel.rand(-5.6)
returns an integern
such that0 <= n < 5
andSecureRandom.random_number(-5.6)
returns a floatn
such that0.0 <= n < 1.0
,SecurerRandomer.rand(-5.6)
returns a floatn
such that0 >= n > -5.6
. WhereasKernel.rand(10..0)
returnsnil
andSecureRandom.random_number(10..0)
returns a floatn
such that0.0 <= n < 1.0
(in Ruby 2.3),SecurerRandomer.rand(10..0)
returns an integern
such that10 >= n >= 0
.
Installation
Please review the installation instructions for RbNaCl. You will need to install either libsodium or rbnacl-libsodium before installing this gem.
Add this line to your application's Gemfile:
gem 'securer_randomer', '~> 0.1.0'
And then execute:
$ bundle install
Or install it yourself as:
$ gem install securer_randomer
Compatibility
SecurerRandomer has been tested under MRI/YARV versions 1.9.3, 2.0, 2.1, 2.2, and 2.3, and JRuby 1.7 and 9.0 (both under Oracle Java 8).
DISCLAIMER
Use at your own risk!
I am neither a cryptologist nor a cryptographer. Although I'm fairly confident
in the test suite, serious bugs affecting compatibility, randomness, and
performance may be present. If you're cautious, I would recommend setting
SecurerRandomer::KERNEL_RAND = Kernel.method(:rand)
as described above.
Development
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and tags, and push the .gem
file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/mwpastore/securer_randomer.
License
The gem is available as open source under the terms of the MIT License.