Skip to content

Commit

Permalink
Add Random::System
Browse files Browse the repository at this point in the history
Allows to generate random numbers using a secure source provided by
the system. It actually uses the same source as SecureRandom.
  • Loading branch information
ysbaddaden committed May 23, 2017
1 parent 53dc365 commit e1330f1
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 0 deletions.
10 changes: 10 additions & 0 deletions spec/std/random/system_spec.cr
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions src/lib_c/amd64-unknown-openbsd/c/stdlib.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 17 additions & 0 deletions src/random/system.cr
Original file line number Diff line number Diff line change
@@ -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
15 changes: 15 additions & 0 deletions src/sys/linux/random.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions src/sys/random.cr
Original file line number Diff line number Diff line change
@@ -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

# Returns a random UInt32 from a secure source.
#
# This method is optional.
# def self.random_number : UInt32
end
end

Expand Down
4 changes: 4 additions & 0 deletions src/sys/unix/arc4random.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit e1330f1

Please sign in to comment.