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