From e328ca9478dcdc0342a9078b6a72a6c21308f6de Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Tue, 23 May 2017 15:54:34 +0200 Subject: [PATCH] Add Random::System Allows to generate random numbers using a secure source provided by the system. It actually uses the same source as SecureRandom. --- spec/std/random/system_spec.cr | 10 ++++++++++ src/lib_c/amd64-unknown-openbsd/c/stdlib.cr | 1 + src/random/system.cr | 17 +++++++++++++++++ src/sys/linux/random.cr | 15 +++++++++++++++ src/sys/random.cr | 11 +++++++++-- src/sys/unix/arc4random.cr | 4 ++++ src/sys/unix/random.cr | 10 ++++++++++ 7 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 spec/std/random/system_spec.cr create mode 100644 src/random/system.cr diff --git a/spec/std/random/system_spec.cr b/spec/std/random/system_spec.cr new file mode 100644 index 000000000000..db8c707eb5e1 --- /dev/null +++ b/spec/std/random/system_spec.cr @@ -0,0 +1,10 @@ +require "spec" +require "random/system" + +describe "Random::System" do + rng = Random::System.new + + it "returns random numbers from the secure system source" do + typeof(rng.next_u).should eq(UInt32) + end +end diff --git a/src/lib_c/amd64-unknown-openbsd/c/stdlib.cr b/src/lib_c/amd64-unknown-openbsd/c/stdlib.cr index 5a1ddf83e627..079cee258a8b 100644 --- a/src/lib_c/amd64-unknown-openbsd/c/stdlib.cr +++ b/src/lib_c/amd64-unknown-openbsd/c/stdlib.cr @@ -7,6 +7,7 @@ lib LibC rem : Int end + fun arc4random : UInt32 fun arc4random_buf(x0 : Void*, x1 : SizeT) : Void fun atof(x0 : Char*) : Double fun div(x0 : Int, x1 : Int) : DivT diff --git a/src/random/system.cr b/src/random/system.cr new file mode 100644 index 000000000000..05622d016e7f --- /dev/null +++ b/src/random/system.cr @@ -0,0 +1,17 @@ +require "sys/random" + +# Generates random numbers from a secure source of the system. +# +# For example `arc4random` is used on OpenBSD, whereas on Linux it uses +# `getrandom` (if the kernel supports it) and fallbacks on reading from +# `/dev/urandom` on UNIX systems. +class Random::System + include Random + + def initialize + end + + def next_u + Sys::Random.random_number + end +end diff --git a/src/sys/linux/random.cr b/src/sys/linux/random.cr index 4c515f1f488a..f159346fd159 100644 --- a/src/sys/linux/random.cr +++ b/src/sys/linux/random.cr @@ -30,6 +30,21 @@ module Sys end end + # Returns a random UInt32. + def self.random_number : UInt32 + init unless @@initialized + + if @@getrandom_available + buf = uninitialized UInt8[sizeof(UInt32)] + getrandom(buf.to_slice) + buf.to_unsafe.as(UInt32*).value + elsif urandom = @@urandom + urandom.read_bytes(UInt32) + else + raise "Failed to access secure source to generate random bytes!" + end + end + # Reads n random bytes using the Linux `getrandom(2)` syscall. private def self.getrandom(buf) # getrandom(2) may only read up to 256 bytes at once without being diff --git a/src/sys/random.cr b/src/sys/random.cr index 4a44c18c0ff1..c51bf3d8b243 100644 --- a/src/sys/random.cr +++ b/src/sys/random.cr @@ -1,7 +1,14 @@ +# :nodoc: module Sys + # :nodoc: module Random - # Fills *buffer* with random bytes from a secure source. - # def self.random_bytes(buffer : Bytes) : Nil + # Fills *buf* with random bytes from a secure source. + # def self.random_bytes(buf : Bytes) : Nil + + # Returns a random UInt32 from a secure source. + # + # This method is optional. + # def self.random_number : UInt32 end end diff --git a/src/sys/unix/arc4random.cr b/src/sys/unix/arc4random.cr index 5cf6d7e8eb1b..16047cd80bbc 100644 --- a/src/sys/unix/arc4random.cr +++ b/src/sys/unix/arc4random.cr @@ -8,5 +8,9 @@ module Sys def self.random_bytes(buf : Bytes) : Nil LibC.arc4random_buf(buf.to_unsafe.as(Void*), buf.size) end + + def self.random_number : UInt32 + LibC.arc4random + end end end diff --git a/src/sys/unix/random.cr b/src/sys/unix/random.cr index 8a2be07a1b19..a03354f45372 100644 --- a/src/sys/unix/random.cr +++ b/src/sys/unix/random.cr @@ -17,5 +17,15 @@ module Sys raise "Failed to access secure source to generate random bytes!" end end + + def self.random_number : UInt32 + init unless @@initialized + + if urandom = @@urandom + urandom.read_bytes(UInt32) + else + raise "Failed to access secure source to generate random bytes!" + end + end end end