Secure random number generator interface.

This library is an interface for secure random number generator which is suitable for generating session key in HTTP cookies, etc.

It supports following secure random number generators.

  • openssl
  • /dev/urandom
  • Win32

Note: This module is based on the SecureRandom library from Ruby 1.9, revision 18786, August 23 2008. It‘s 100% interface-compatible with Ruby 1.9‘s SecureRandom library.

Example

 # random hexadecimal string.
 p SecureRandom.hex(10) #=> "52750b30ffbc7de3b362"
 p SecureRandom.hex(10) #=> "92b15d6c8dc4beb5f559"
 p SecureRandom.hex(11) #=> "6aca1b5c58e4863e6b81b8"
 p SecureRandom.hex(12) #=> "94b2fff3e7fd9b9c391a2306"
 p SecureRandom.hex(13) #=> "39b290146bea6ce975c37cfc23"
 ...

 # random base64 string.
 p SecureRandom.base64(10) #=> "EcmTPZwWRAozdA=="
 p SecureRandom.base64(10) #=> "9b0nsevdwNuM/w=="
 p SecureRandom.base64(10) #=> "KO1nIU+p9DKxGg=="
 p SecureRandom.base64(11) #=> "l7XEiFja+8EKEtY="
 p SecureRandom.base64(12) #=> "7kJSM/MzBJI+75j8"
 p SecureRandom.base64(13) #=> "vKLJ0tXBHqQOuIcSIg=="
 ...

 # random binary string.
 p SecureRandom.random_bytes(10) #=> "\016\t{\370g\310pbr\301"
 p SecureRandom.random_bytes(10) #=> "\323U\030TO\234\357\020\a\337"
 ...
Methods
Public Class methods
base64(n=nil)

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 activesupport/lib/active_support/secure_random.rb, line 151
151:       def self.base64(n=nil)
152:         [random_bytes(n)].pack("m*").delete("\n")
153:       end
hex(n=nil)

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 activesupport/lib/active_support/secure_random.rb, line 137
137:       def self.hex(n=nil)
138:         random_bytes(n).unpack("H*")[0]
139:       end
random_bytes(n=nil)

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

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 activesupport/lib/active_support/secure_random.rb, line 164
164:       def self.random_number(n=0)
165:         if 0 < n
166:           hex = n.to_s(16)
167:           hex = '0' + hex if (hex.length & 1) == 1
168:           bin = [hex].pack("H*")
169:           mask = bin[0]
170:           mask |= mask >> 1
171:           mask |= mask >> 2
172:           mask |= mask >> 4
173:           begin
174:             rnd = SecureRandom.random_bytes(bin.length)
175:             rnd[0] = rnd[0] & mask
176:           end until rnd < bin
177:           rnd.unpack("H*")[0].hex
178:         else
179:           # assumption: Float::MANT_DIG <= 64
180:           i64 = SecureRandom.random_bytes(8).unpack("Q")[0]
181:           Math.ldexp(i64 >> (64-Float::MANT_DIG), -Float::MANT_DIG)
182:         end
183:       end