Module: SecureRandom

Defined in:
lib/securerandom.rb

Overview

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.

You can use this library in your application by requiring it:

require 'securerandom'

It supports following secure random number generators.

  • openssl

  • /dev/urandom

  • Win32

Examples

Hexadecimal string.

require 'securerandom'

p SecureRandom.hex(10) #=> "52750b30ffbc7de3b362"
p SecureRandom.hex(10) #=> "92b15d6c8dc4beb5f559"
p SecureRandom.hex(13) #=> "39b290146bea6ce975c37cfc23"

Base64 string.

p SecureRandom.base64(10) #=> "EcmTPZwWRAozdA=="
p SecureRandom.base64(10) #=> "KO1nIU+p9DKxGg=="
p SecureRandom.base64(12) #=> "7kJSM/MzBJI+75j8"

Binary string.

p SecureRandom.random_bytes(10) #=> "\016\t{\370g\310pbr\301"
p SecureRandom.random_bytes(10) #=> "\323U\030TO\234\357\020\a\337"

Defined Under Namespace

Modules: AdvApi32, Kernel32

Class Method Summary collapse

Class Method Details

.base64(n = nil) ⇒ Object

SecureRandom.base64 generates a random base64 string.

The argument n specifies the length, in bytes, of the random number to be generated. The length of the result string is about 4/3 of n.

If n is not specified or is nil, 16 is assumed. It may be larger in future.

The result may contain A-Z, a-z, 0-9, “+”, “/” and “=”.

p SecureRandom.base64 #=> "/2BuBuLf3+WfSKyQbRcc/A=="
p SecureRandom.base64 #=> "6BbW0pxO0YENxn38HMUbcQ=="

If secure random number generator is not available, NotImplementedError is raised.

See RFC 3548 for the definition of base64.



193
194
195
# File 'lib/securerandom.rb', line 193

def self.base64(n=nil)
  [random_bytes(n)].pack("m*").delete("\n")
end

.gen_random(n) ⇒ Object



114
115
116
117
118
119
120
121
122
123
124
# File 'lib/securerandom.rb', line 114

def self.gen_random(n)
  @pid = 0 unless defined?(@pid)
  pid = $$
  unless @pid == pid
    now = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
    ary = [now, @pid, pid]
    OpenSSL::Random.random_add(ary.join("").to_s, 0.0)
    @pid = pid
  end
  return OpenSSL::Random.random_bytes(n)
end

.hex(n = nil) ⇒ Object

SecureRandom.hex generates a random hexadecimal string.

The argument n specifies the length, in bytes, of the random number to be generated. The length of the resulting hexadecimal string is twice n.

If n is not specified or is nil, 16 is assumed. It may be larger in future.

The result may contain 0-9 and a-f.

p SecureRandom.hex #=> "eb693ec8252cd630102fd0d0fb7c3485"
p SecureRandom.hex #=> "91dc3bfb4de5b11d029d376634589b61"

If secure random number generator is not available, NotImplementedError is raised.



172
173
174
# File 'lib/securerandom.rb', line 172

def self.hex(n=nil)
  random_bytes(n).unpack("H*")[0]
end

.lastWin32ErrorMessageObject



130
131
132
133
# File 'lib/securerandom.rb', line 130

def self.lastWin32ErrorMessage # :nodoc:
  # for compatibility
  return Kernel32.last_error_message
end

.random_bytes(n = nil) ⇒ Object

SecureRandom.random_bytes generates a random binary string.

The argument n specifies the length of the result string.

If n is not specified or is nil, 16 is assumed. It may be larger in future.

The result may contain any byte: “x00” - “xff”.

p SecureRandom.random_bytes #=> "\xD8\\\xE0\xF4\r\xB2\xFC*WM\xFF\x83\x18\xF45\xB6"
p SecureRandom.random_bytes #=> "m\xDC\xFC/\a\x00Uf\xB2\xB2P\xBD\xFF6S\x97"

If secure random number generator is not available, NotImplementedError is raised.



108
109
110
111
# File 'lib/securerandom.rb', line 108

def self.random_bytes(n=nil)
  n = n ? n.to_int : 16
  gen_random(n)
end

.random_number(n = 0) ⇒ Object

SecureRandom.random_number generates a random number.

If a positive integer is given as n, SecureRandom.random_number returns an integer: 0 <= SecureRandom.random_number(n) < n.

p SecureRandom.random_number(100) #=> 15
p SecureRandom.random_number(100) #=> 88

If 0 is given or an argument is not given, SecureRandom.random_number returns a float: 0.0 <= SecureRandom.random_number() < 1.0.

p SecureRandom.random_number #=> 0.596506046187744
p SecureRandom.random_number #=> 0.350621695741409


247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/securerandom.rb', line 247

def self.random_number(n=0)
  if 0 < n
    if defined? OpenSSL::BN
      OpenSSL::BN.rand_range(n).to_i
    else
      hex = n.to_s(16)
      hex = '0' + hex if (hex.length & 1) == 1
      bin = [hex].pack("H*")
      mask = bin[0].ord
      mask |= mask >> 1
      mask |= mask >> 2
      mask |= mask >> 4
      begin
        rnd = SecureRandom.random_bytes(bin.length)
        rnd[0] = (rnd[0].ord & mask).chr
      end until rnd < bin
      rnd.unpack("H*")[0].hex
    end
  else
    # assumption: Float::MANT_DIG <= 64
    if defined? OpenSSL::BN
      i64 = OpenSSL::BN.rand(64, -1).to_i
    else
      i64 = SecureRandom.random_bytes(8).unpack("Q")[0]
    end
    Math.ldexp(i64 >> (64-Float::MANT_DIG), -Float::MANT_DIG)
  end
end

.urlsafe_base64(n = nil, padding = false) ⇒ Object

SecureRandom.urlsafe_base64 generates a random URL-safe base64 string.

The argument n specifies the length, in bytes, of the random number to be generated. The length of the result string is about 4/3 of n.

If n is not specified or is nil, 16 is assumed. It may be larger in future.

The boolean argument padding specifies the padding. If it is false or nil, padding is not generated. Otherwise padding is generated. By default, padding is not generated because “=” may be used as a URL delimiter.

The result may contain A-Z, a-z, 0-9, “-” and “_”. “=” is also used if padding is true.

p SecureRandom.urlsafe_base64 #=> "b4GOKm4pOYU_-BOXcrUGDg"
p SecureRandom.urlsafe_base64 #=> "UZLdOkzop70Ddx-IJR0ABg"

p SecureRandom.urlsafe_base64(nil, true) #=> "i0XQ-7gglIsHGV2_BNPrdQ=="
p SecureRandom.urlsafe_base64(nil, true) #=> "-M8rLhr7JEpJlqFGUMmOxg=="

If secure random number generator is not available, NotImplementedError is raised.

See RFC 3548 for the definition of URL-safe base64.



223
224
225
226
227
228
229
# File 'lib/securerandom.rb', line 223

def self.urlsafe_base64(n=nil, padding=false)
  s = [random_bytes(n)].pack("m*")
  s.delete!("\n")
  s.tr!("+/", "-_")
  s.delete!("=") unless padding
  s
end

.uuidObject

SecureRandom.uuid generates a v4 random UUID (Universally Unique IDentifier).

p SecureRandom.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594"
p SecureRandom.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab"
p SecureRandom.uuid #=> "62936e70-1815-439b-bf89-8492855a7e6b"

The version 4 UUID is purely random (except the version). It doesn't contain meaningful information such as MAC address, time, etc.

See RFC 4122 for details of UUID.



287
288
289
290
291
292
# File 'lib/securerandom.rb', line 287

def self.uuid
  ary = self.random_bytes(16).unpack("NnnnnN")
  ary[2] = (ary[2] & 0x0fff) | 0x4000
  ary[3] = (ary[3] & 0x3fff) | 0x8000
  "%08x-%04x-%04x-%04x-%04x%08x" % ary
end