12 if defined?(::SecureRandom)
13 # Use Ruby 1.9's SecureRandom library whenever possible.
14 SecureRandom
= ::SecureRandom # :nodoc:
16 # = Secure random number generator interface.
18 # This library is an interface for secure random number generator which is
19 # suitable for generating session key in HTTP cookies, etc.
21 # It supports following secure random number generators.
27 # *Note*: This module is based on the SecureRandom library from Ruby 1.9,
28 # revision 18786, August 23 2008. It's 100% interface-compatible with Ruby 1.9's
29 # SecureRandom library.
33 # # random hexadecimal string.
34 # p SecureRandom.hex(10) #=> "52750b30ffbc7de3b362"
35 # p SecureRandom.hex(10) #=> "92b15d6c8dc4beb5f559"
36 # p SecureRandom.hex(11) #=> "6aca1b5c58e4863e6b81b8"
37 # p SecureRandom.hex(12) #=> "94b2fff3e7fd9b9c391a2306"
38 # p SecureRandom.hex(13) #=> "39b290146bea6ce975c37cfc23"
41 # # random base64 string.
42 # p SecureRandom.base64(10) #=> "EcmTPZwWRAozdA=="
43 # p SecureRandom.base64(10) #=> "9b0nsevdwNuM/w=="
44 # p SecureRandom.base64(10) #=> "KO1nIU+p9DKxGg=="
45 # p SecureRandom.base64(11) #=> "l7XEiFja+8EKEtY="
46 # p SecureRandom.base64(12) #=> "7kJSM/MzBJI+75j8"
47 # p SecureRandom.base64(13) #=> "vKLJ0tXBHqQOuIcSIg=="
50 # # random binary string.
51 # p SecureRandom.random_bytes(10) #=> "\016\t{\370g\310pbr\301"
52 # p SecureRandom.random_bytes(10) #=> "\323U\030TO\234\357\020\a\337"
55 # SecureRandom.random_bytes generates a random binary string.
57 # The argument n specifies the length of the result string.
59 # If n is not specified, 16 is assumed.
60 # It may be larger in future.
62 # If secure random number generator is not available,
63 # NotImplementedError is raised.
64 def self.random_bytes(n
=nil)
67 if defined? OpenSSL
::Random
68 return OpenSSL
::Random.random_bytes(n
)
71 if !defined?(@has_urandom) || @has_urandom
73 flags
|= File
::NONBLOCK if defined? File
::NONBLOCK
74 flags
|= File
::NOCTTY if defined? File
::NOCTTY
75 flags
|= File
::NOFOLLOW if defined? File
::NOFOLLOW
77 File
.open("/dev/urandom", flags
) {|f
|
78 unless f
.stat
.chardev
?
82 ret
= f
.readpartial(n
)
84 raise NotImplementedError
, "Unexpected partial read from random device"
93 if !defined?(@has_win32)
97 crypt_acquire_context
= Win32API
.new("advapi32", "CryptAcquireContext", 'PPPII', 'L')
98 @crypt_gen_random = Win32API
.new("advapi32", "CryptGenRandom", 'LIP', 'L')
102 crypt_verifycontext
= 0xF0000000
104 if crypt_acquire_context
.call(hProvStr
, nil, nil, prov_rsa_full
, crypt_verifycontext
) == 0
105 raise SystemCallError
, "CryptAcquireContext failed: #{lastWin32ErrorMessage}"
107 @hProv, = hProvStr
.unpack('L')
116 if @crypt_gen_random.call(@hProv, bytes
.size
, bytes
) == 0
117 raise SystemCallError
, "CryptGenRandom failed: #{lastWin32ErrorMessage}"
122 raise NotImplementedError
, "No random device"
125 # SecureRandom.hex generates a random hex string.
127 # The argument n specifies the length of the random length.
128 # The length of the result string is twice of n.
130 # If n is not specified, 16 is assumed.
131 # It may be larger in future.
133 # If secure random number generator is not available,
134 # NotImplementedError is raised.
136 random_bytes(n
).unpack("H*")[0]
139 # SecureRandom.base64 generates a random base64 string.
141 # The argument n specifies the length of the random length.
142 # The length of the result string is about 4/3 of n.
144 # If n is not specified, 16 is assumed.
145 # It may be larger in future.
147 # If secure random number generator is not available,
148 # NotImplementedError is raised.
149 def self.base64(n
=nil)
150 [random_bytes(n
)].pack("m*").delete("\n")
153 # SecureRandom.random_number generates a random number.
155 # If an positive integer is given as n,
156 # SecureRandom.random_number returns an integer:
157 # 0 <= SecureRandom.random_number(n) < n.
159 # If 0 is given or an argument is not given,
160 # SecureRandom.random_number returns an float:
161 # 0.0 <= SecureRandom.random_number() < 1.0.
162 def self.random_number(n
=0)
165 hex
= '0' + hex
if (hex
.length
& 1) == 1
166 bin
= [hex
].pack("H*")
172 rnd
= SecureRandom
.random_bytes(bin
.length
)
173 rnd
[0] = rnd
[0] & mask
175 rnd
.unpack("H*")[0].hex
177 # assumption: Float::MANT_DIG <= 64
178 i64
= SecureRandom
.random_bytes(8).unpack("Q")[0]
179 Math
.ldexp(i64
>> (64-Float
::MANT_DIG), -Float
::MANT_DIG)
183 # Following code is based on David Garamond's GUID library for Ruby.
184 def self.lastWin32ErrorMessage
# :nodoc:
185 get_last_error
= Win32API
.new("kernel32", "GetLastError", '', 'L')
186 format_message
= Win32API
.new("kernel32", "FormatMessageA", 'LPLLPLPPPPPPPP', 'L')
187 format_message_ignore_inserts
= 0x00000200
188 format_message_from_system
= 0x00001000
190 code
= get_last_error
.call
192 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)
193 msg
[0, len
].tr("\r", '').chomp