# module SecureRandom

### Public Class Methods

base64(n=nil) click to toggle source

`SecureRandom.base64` generates a random base64 string.

The argument n specifies the length of the random length. The length of the result string is about 4/3 of n.

If n is not specified, 16 is assumed. It may be larger in future.

If secure random number generator is not available, NotImplementedError is raised.

        # File lib/compat/securerandom.rb
    145 def self.base64(n=nil)
    146   [random_bytes(n)].pack("m*").delete("\n")
    147 end

hex(n=nil) click to toggle source

`SecureRandom.hex` generates a random hex string.

The argument n specifies the length of the random length. The length of the result string is twice of n.

If n is not specified, 16 is assumed. It may be larger in future.

If secure random number generator is not available, NotImplementedError is raised.

        # File lib/compat/securerandom.rb
    131 def self.hex(n=nil)
    132   random_bytes(n).unpack("H*")[0]
    133 end

random_bytes(n=nil) click to toggle source

`SecureRandom.random_bytes` generates a random binary string.

The argument n specifies the length of the result string.

If n is not specified, 16 is assumed. It may be larger in future.

If secure random number generator is not available, NotImplementedError is raised.

        # File lib/compat/securerandom.rb
     52 def self.random_bytes(n=nil)
     53   n ||= 16
     54
     55   if defined? OpenSSL::Random
     56     return OpenSSL::Random.random_bytes(n)
     57   end
     58
     59   if !defined?(@has_urandom) || @has_urandom
     60     flags = File::RDONLY
     61     flags |= File::NONBLOCK if defined? File::NONBLOCK
     62     flags |= File::NOCTTY if defined? File::NOCTTY
     63     flags |= File::NOFOLLOW if defined? File::NOFOLLOW
     64     begin
     65       File.open("/dev/urandom", flags) {|f|
     66         unless f.stat.chardev?
     67           raise Errno::ENOENT
     68         end
     69         @has_urandom = true
     70         ret = f.readpartial(n)
     71         if ret.length != n
     72           raise NotImplementedError,
     73             "Unexpected partial read from random device"
     74         end
     75         return ret
     76       }
     77     rescue Errno::ENOENT
     78       @has_urandom = false
     79     end
     80   end
     81
     82   if !defined?(@has_win32)
     83     begin
     84       require 'Win32API'
     85
     86       crypt_acquire_context = Win32API.new(
     87         "advapi32", "CryptAcquireContext", 'PPPII', 'L'
     88       )
     89       @crypt_gen_random = Win32API.new(
     90         "advapi32", "CryptGenRandom", 'LIP', 'L'
     91       )
     92
     93       hProvStr = " " * 4
     94       prov_rsa_full = 1
     95       crypt_verifycontext = 0xF0000000
     96
     97       if crypt_acquire_context.call(
     98           hProvStr, nil, nil, prov_rsa_full, crypt_verifycontext) == 0
     99         raise SystemCallError,
    100           "CryptAcquireContext failed: #{lastWin32ErrorMessage}"
    101       end
    102       @hProv, = hProvStr.unpack('L')
    103
    104       @has_win32 = true
    105     rescue LoadError
    106       @has_win32 = false
    107     end
    108   end
    109   if @has_win32
    110     bytes = " " * n
    111     if @crypt_gen_random.call(@hProv, bytes.size, bytes) == 0
    112       raise SystemCallError,
    113         "CryptGenRandom failed: #{lastWin32ErrorMessage}"
    114     end
    115     return bytes
    116   end
    117
    118   raise NotImplementedError, "No random device"
    119 end

random_number(n=0) click to toggle source

`SecureRandom.random_number` generates a random number.

If an positive integer is given as n, `SecureRandom.random_number` returns an integer: 0 <= `SecureRandom.random_number(n)` < n.

If 0 is given or an argument is not given, `SecureRandom.random_number` returns an float: 0.0 <= `SecureRandom.random_number()` < 1.0.

        # File lib/compat/securerandom.rb
    158 def self.random_number(n=0)
    159   if 0 < n
    160     hex = n.to_s(16)
    161     hex = '0' + hex if (hex.length & 1) == 1
    162     bin = [hex].pack("H*")
    163     first = bin[0..0]
    164     mask = first.respond_to?(:ord) ? first.ord : first.sum(8)
    165     mask |= mask >> 1
    166     mask |= mask >> 2
    167     mask |= mask >> 4
    168     begin
    169       rnd = SecureRandom.random_bytes(bin.length)
    170       first = rnd[0..0]
    171       ordinal = first.respond_to?(:ord) ? first.ord : first.sum(8)
    172       rnd[0..0] = (ordinal & mask).chr
    173     end until rnd < bin
    174     rnd.unpack("H*")[0].hex
    175   else
    176     # assumption: Float::MANT_DIG <= 64
    177     i64 = SecureRandom.random_bytes(8).unpack("Q")[0]
    178     Math.ldexp(i64 >> (64-Float::MANT_DIG), -Float::MANT_DIG)
    179   end
    180 end