class OpenToken::PasswordKeyGenerator

Constants

SHA1_DIGEST

Public Class Methods

generate(password, cipher) click to toggle source
# File lib/opentoken/password_key_generator.rb, line 6
def generate(password, cipher)
  salt = 0.chr * 8
  generate_impl(password, cipher, salt, 1000)
end

Private Class Methods

generate_block(password, salt, count, index) click to toggle source
# File lib/opentoken/password_key_generator.rb, line 12
def generate_block(password, salt, count, index)
  mac = salt
  mac += [index].pack("N")

  result = OpenSSL::HMAC.digest(SHA1_DIGEST, password, mac)
  cur = result

  i_count = 1
  while i_count < count
    i_count +=1

    cur = OpenSSL::HMAC.digest(SHA1_DIGEST, password, cur)

    20.times do |i|
      if RUBY_VERSION < "1.9"
        result[i] = result[i] ^ cur[i]
      else
        result[i] = (result[i].chr.ord ^ cur[i].chr.ord).chr
      end
    end
  end

  return result
end
generate_impl(password, cipher, salt, iterations) click to toggle source
# File lib/opentoken/password_key_generator.rb, line 37
def generate_impl(password, cipher, salt, iterations)
  return unless cipher.algorithm

  key_size = cipher.key_length / 8
  numblocks = key_size / 20
  numblocks += 1 if (key_size % 20) > 0

  # Generate the appropriate number of blocks and write their output to
  # the key bytes; note that it's important to start from 1 (vs. 0) as the
  # initial block number affects the hash. It's not clear that this fact
  # is stated explicitly anywhere, but without this approach, the generated
  # keys will not match up with test cases defined in RFC 3962.
  key_buffer_index = 0
  key = ""

  numblocks.times do |i|
    i+=1 # Previously zero based, needs to be 1 based
    block = generate_block(password, salt, iterations, i)
    len = [20, (key_size - key_buffer_index)].min
    key += block[0, len]
    key_buffer_index += len
  end

  return key
end