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"
...
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.
[ show source ]
# 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
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.
[ show source ]
# 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
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.
[ show source ]
# 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
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.
[ show source ]
# 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