class Pwqgen::FakeRandom

Pwqgen::FakeRandom is a keyed, not really random, number generator that uses an HMAC. it intentionally mimics the random_bytes method in SecureRandom/Sysrandom. The high number of iterations is an attempt to make brute forcing the key more expensive given the string and some pwqgen output. The idea of using a HMAC based keyed generator was inspired by the pwdhash algorithm.

Example:

require 'pwqgen'
n = Pwqgen::FakeRandom.new('bob', 'terribly secret key').method(:random_bytes)
# returns a string with 256 random bytes
puts n.call(256)
# use the FakeRandom object to generate a not really random passphrase
puts Pwqgen.pwqgen(n_words: 5, random_generator: n)
# OR
n1 = Pwqgen::FakeRandom.new('bob', 'even more terribly secret key')
puts Pwqgen.pwqgen(n_words: 4, random_generator: proc { |x| n1.random_bytes(x) })

Constants

HMAC_ITERATIONS

number of iterations for the HMAC.

Public Class Methods

new(string, key) click to toggle source

Initialize a new FakeRandom object

Arguments:

string: (String)
key: (String)
# File lib/pwqgen/fakerandom.rb, line 31
def initialize(string, key)
  @key = key.encode(Encoding::ASCII_8BIT)
  @dstr = string.encode(Encoding::ASCII_8BIT)
  @digest = ::OpenSSL::Digest.new('sha512').freeze
  @results = []
end

Public Instance Methods

random_bytes(n = 16) click to toggle source

Generate n random bytes. This mimics the interface of Sysrandom.random_bytes and SecureRandom.random_bytes Returns a string of length n Arguments:

n: (Integer)
# File lib/pwqgen/fakerandom.rb, line 43
def random_bytes(n = 16)
  generate_bytes while @results.length < n
  # now @results.length >= n
  bytes = @results[0..(n - 1)]
  @results = @results[n..-1]
  bytes.pack('C*')
end

Private Instance Methods

generate_bytes() click to toggle source

Generate (more) bytes of quasi-random data.

# File lib/pwqgen/fakerandom.rb, line 54
def generate_bytes
  HMAC_ITERATIONS.times { @dstr = ::OpenSSL::HMAC.digest(@digest, @key, @dstr) }
  @results += @dstr.unpack('C*')

  # Generate new @key and @dstr for next call (if it ever happens)
  # This is inspired by HMAC-DRBG
  @key = ::OpenSSL::HMAC.digest(@digest, @key, @dstr + "\000")
  @dstr = ::OpenSSL::HMAC.digest(@digest, @key, @dstr)
end