7 if defined?(::SecureRandom)
8 # Use Ruby's SecureRandom library if available.
9 SecureRandom
= ::SecureRandom # :nodoc:
11 # = Secure random number generator interface.
13 # This library is an interface for secure random number generator which is
14 # suitable for generating session key in HTTP cookies, etc.
16 # It supports following secure random number generators.
22 # *Note*: This module is based on the SecureRandom library from Ruby 1.9,
23 # revision 18786, August 23 2008. It's 100% interface-compatible with Ruby 1.9's
24 # SecureRandom library.
28 # # random hexadecimal string.
29 # p SecureRandom.hex(10) #=> "52750b30ffbc7de3b362"
30 # p SecureRandom.hex(10) #=> "92b15d6c8dc4beb5f559"
31 # p SecureRandom.hex(11) #=> "6aca1b5c58e4863e6b81b8"
32 # p SecureRandom.hex(12) #=> "94b2fff3e7fd9b9c391a2306"
33 # p SecureRandom.hex(13) #=> "39b290146bea6ce975c37cfc23"
36 # # random base64 string.
37 # p SecureRandom.base64(10) #=> "EcmTPZwWRAozdA=="
38 # p SecureRandom.base64(10) #=> "9b0nsevdwNuM/w=="
39 # p SecureRandom.base64(10) #=> "KO1nIU+p9DKxGg=="
40 # p SecureRandom.base64(11) #=> "l7XEiFja+8EKEtY="
41 # p SecureRandom.base64(12) #=> "7kJSM/MzBJI+75j8"
42 # p SecureRandom.base64(13) #=> "vKLJ0tXBHqQOuIcSIg=="
45 # # random binary string.
46 # p SecureRandom.random_bytes(10) #=> "\016\t{\370g\310pbr\301"
47 # p SecureRandom.random_bytes(10) #=> "\323U\030TO\234\357\020\a\337"
50 # SecureRandom.random_bytes generates a random binary string.
52 # The argument n specifies the length of the result string.
54 # If n is not specified, 16 is assumed.
55 # It may be larger in future.
57 # If secure random number generator is not available,
58 # NotImplementedError is raised.
59 def self.random_bytes(n
=nil)
62 unless defined? OpenSSL
69 if defined? OpenSSL
::Random
70 return OpenSSL
::Random.random_bytes(n
)
73 if !defined?(@has_urandom) || @has_urandom
75 flags
|= File
::NONBLOCK if defined? File
::NONBLOCK
76 flags
|= File
::NOCTTY if defined? File
::NOCTTY
77 flags
|= File
::NOFOLLOW if defined? File
::NOFOLLOW
79 File
.open("/dev/urandom", flags
) {|f
|
80 unless f
.stat
.chardev
?
84 ret
= f
.readpartial(n
)
86 raise NotImplementedError
, "Unexpected partial read from random device"
95 if !defined?(@has_win32)
99 crypt_acquire_context
= Win32API
.new("advapi32", "CryptAcquireContext", 'PPPII', 'L')
100 @crypt_gen_random = Win32API
.new("advapi32", "CryptGenRandom", 'LIP', 'L')
104 crypt_verifycontext
= 0xF0000000
106 if crypt_acquire_context
.call(hProvStr
, nil, nil, prov_rsa_full
, crypt_verifycontext
) == 0
107 raise SystemCallError
, "CryptAcquireContext failed: #{lastWin32ErrorMessage}"
109 @hProv, = hProvStr
.unpack('L')
118 if @crypt_gen_random.call(@hProv, bytes
.size
, bytes
) == 0
119 raise SystemCallError
, "CryptGenRandom failed: #{lastWin32ErrorMessage}"
124 raise NotImplementedError
, "No random device"
127 # SecureRandom.hex generates a random hex string.
129 # The argument n specifies the length of the random length.
130 # The length of the result string is twice of n.
132 # If n is not specified, 16 is assumed.
133 # It may be larger in future.
135 # If secure random number generator is not available,
136 # NotImplementedError is raised.
138 random_bytes(n
).unpack("H*")[0]
141 # SecureRandom.base64 generates a random base64 string.
143 # The argument n specifies the length of the random length.
144 # The length of the result string is about 4/3 of n.
146 # If n is not specified, 16 is assumed.
147 # It may be larger in future.
149 # If secure random number generator is not available,
150 # NotImplementedError is raised.
151 def self.base64(n
=nil)
152 [random_bytes(n
)].pack("m*").delete("\n")
155 # SecureRandom.random_number generates a random number.
157 # If an positive integer is given as n,
158 # SecureRandom.random_number returns an integer:
159 # 0 <= SecureRandom.random_number(n) < n.
161 # If 0 is given or an argument is not given,
162 # SecureRandom.random_number returns an float:
163 # 0.0 <= SecureRandom.random_number() < 1.0.
164 def self.random_number(n
=0)
167 hex
= '0' + hex
if (hex
.length
& 1) == 1
168 bin
= [hex
].pack("H*")
174 rnd
= SecureRandom
.random_bytes(bin
.length
)
175 rnd
[0] = rnd
[0] & mask
177 rnd
.unpack("H*")[0].hex
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)
185 # Following code is based on David Garamond's GUID library for Ruby.
186 def self.lastWin32ErrorMessage
# :nodoc:
187 get_last_error
= Win32API
.new("kernel32", "GetLastError", '', 'L')
188 format_message
= Win32API
.new("kernel32", "FormatMessageA", 'LPLLPLPPPPPPPP', 'L')
189 format_message_ignore_inserts
= 0x00000200
190 format_message_from_system
= 0x00001000
192 code
= get_last_error
.call
194 len
= format_message
.call(format_message_ignore_inserts
+ format_message_from_system
, 0, code
, 0, msg
, 1024, nil, nil, nil, nil, nil, nil, nil, nil)
195 msg
[0, len
].tr("\r", '').chomp