class Forgiva

Password generation from 4 inputs

Attributes

account[RW]
complexity[RW]
hostname[RW]
length[RW]
master_password[RW]
renewal_date[RW]
use_scrypt[RW]

Public Class Methods

encrypt(alg, data, extension) click to toggle source
# File lib/forgiva.rb, line 187
def self.encrypt(alg, data, extension)

  return self.encrypt_ex(alg,
                          data,
                          self.pbkdf2_hmac_sha(extension,'forgiva', Constants::FORGIVA_PG_SIMPLE),
                          sha512ize(extension))
end
encrypt_ex(alg, data, key,iv) click to toggle source
# File lib/forgiva.rb, line 157
def self.encrypt_ex(alg, data, key,iv)

  cipher = OpenSSL::Cipher::Cipher.new(alg)
  cipher.encrypt

  key_ = shorten_if_long(pad_with_zeroes(key,cipher.key_len),cipher.key_len)
  iv_ = shorten_if_long(pad_with_zeroes(iv,cipher.iv_len),cipher.iv_len)

  cipher.key = key_
  cipher.iv = iv_

  ## Padding if length is not multiple of block size of cipher
  data = pad_with_zeroes(data,cipher.block_size)

  puts "self.encrypt: #{alg} - #{data.unpack('H*')} Key: #{cipher.key_len} #{key_.unpack('H*')} IV: #{cipher.iv_len} #{iv_.unpack('H*')}" if Constants::DEBUG_OUTPUT


  ret = cipher.update(data)

  #+ cipher.final

  puts "self.encrypt: (ret) #{ret.unpack('H*')}" if Constants::DEBUG_OUTPUT

  return ret


  rescue OpenSSL::Cipher::CipherError => e
    puts "Error: #{e.message} data_len #{data.length} key_len #{key.length} iv_len #{iv.length}"
end
hash(alg, val) click to toggle source
# File lib/forgiva.rb, line 209
def self.hash(alg, val)
  dig = OpenSSL::Digest.new(alg)
  nval =val
  puts "alg: #{alg}" if (val == nil)


  ret = ""
  puts "HASH IN: #{nval.unpack('H*')}:#{nval.length} alg: #{alg} dl: #{dig.digest_length}" if Constants::DEBUG_OUTPUT

  if (dig.digest_length < val.length) then

    st = 0
    en = dig.digest_length
    while (true) do

      inblock = val[st..en-1]
      puts "INBLOCK: #{inblock.unpack('H*')}:#{inblock.length}" if Constants::DEBUG_OUTPUT
      outblock = OpenSSL::Digest.digest(alg,inblock)
      puts "OUTBLOCK: #{outblock.unpack('H*')}:#{outblock.length}" if Constants::DEBUG_OUTPUT
      ret = ret + outblock
      puts "NRET: #{ret.unpack('H*')}:#{ret.length}" if Constants::DEBUG_OUTPUT

      st = en
      break if (st >= val.length)

      en = st + dig.digest_length
      en = val.length if (en > val.length)
    end

    ret = ret[0...val.length]

  else
    ret = dig.digest(nval)
  end


  puts "HASH OUT: #{ret.unpack('H*')}:#{ret.length}" if Constants::DEBUG_OUTPUT
  return ret
end
hash_to_password(val,complexity,length) click to toggle source
# File lib/forgiva.rb, line 268
def self.hash_to_password(val,complexity,length)
  ret = ''

  # to be sure it is long enough
  hashed = sha512ize(val)


  pchars = (complexity == 2 ? Constants::PASSWORD_CHARS_INTERMEDIATE :
                (complexity == 3 ? Constants::PASSWORD_CHARS_ADVANCED : Constants::PASSWORD_CHARS))


  hashed.each_byte do |c|
    ret += pchars[c % pchars.length]
    break if ret.length >= length
  end

  ret = ret[0..length-1]

  puts "HASH_TO_PASSWORD (IN): #{val.unpack('H*')}"   if Constants::DEBUG_OUTPUT
  puts "HASH_TO_PASSWORD (HASHED): #{hashed.unpack('H*')}"   if Constants::DEBUG_OUTPUT
  puts "HASH_TO_PASSWORD (OUT): #{ret.unpack('H*')}"   if Constants::DEBUG_OUTPUT

  return ret

end
hash_twice(val) click to toggle source
# File lib/forgiva.rb, line 264
def self.hash_twice(val)
  iterative_hash(iterative_hash(val))
end
iterative_encrypt(val1, val2) click to toggle source
# File lib/forgiva.rb, line 195
def self.iterative_encrypt(val1, val2)
  ret = val1

  val1.each_byte do |c|
    alg = Constants::ENC_ALGS[c % Constants::ENC_ALGS.length]
    ret = encrypt(alg, ret, val2)
  end


  puts "#{ret.unpack('H*')}" if Constants::DEBUG_OUTPUT

  ret
end
iterative_hash(val) click to toggle source
# File lib/forgiva.rb, line 249
def self.iterative_hash(val)
  ret = val

  val.each_byte do |c|
    alg = Constants::HASH_ALGS[c % Constants::HASH_ALGS.length]
    ret = hash(alg, ret)
  end

  ret
end
new(hostname, account, renewal_date, master_password, complexity, length, use_scrypt) click to toggle source
# File lib/forgiva.rb, line 11
def initialize(hostname, account, renewal_date, master_password, complexity, length, use_scrypt)
  @hostname = hostname
  @account = account
  @renewal_date = renewal_date
  @master_password = master_password
  @complexity = complexity
  @length = length
  @use_scrypt = use_scrypt
end
pad_with_zeroes(data,block_size) click to toggle source
# File lib/forgiva.rb, line 133
def self.pad_with_zeroes(data,block_size)

  if (data.length % block_size != 0) then

    tot = block_size + data.length

    toadd = (tot - (tot % block_size) - data.length)

    for i in (1..toadd) do
      data = data + "\x00"
    end

  end

  data
end
pbkdf2_hmac_sha(key,salt,type) click to toggle source

Class methods #

# File lib/forgiva.rb, line 116
def self.pbkdf2_hmac_sha(key,salt,type)

  rkey = nil

  if (type == Constants::FORGIVA_PG_SIMPLE) then
      rkey = OpenSSL::PKCS5.pbkdf2_hmac_sha1(key, salt, 10_000, 32)
  elsif (type == Constants::FORGIVA_PG_INTERMEDIATE) then
      digest = OpenSSL::Digest::SHA256.new
      rkey = OpenSSL::PKCS5.pbkdf2_hmac(key,salt,10_000 * 1000, 32,digest);
  elsif (type == Constants::FORGIVA_PG_ADVANCED) then
    digest = OpenSSL::Digest::SHA512.new
    rkey = OpenSSL::PKCS5.pbkdf2_hmac(key,salt,10_000 * 10000, 32,digest);
  end

  return rkey
end
sha512ize(val) click to toggle source
# File lib/forgiva.rb, line 260
def self.sha512ize(val)
  hash('sha512', val)
end
shorten_if_long(data,block_size) click to toggle source
# File lib/forgiva.rb, line 150
def self.shorten_if_long(data,block_size)

  data = data[0..block_size] if data.length > block_size

  data
end

Public Instance Methods

encrypted_inputs() click to toggle source
# File lib/forgiva.rb, line 81
def encrypted_inputs


  puts "hashed_hostname: #{hashed_hostname.unpack('H*')}" if Constants::DEBUG_OUTPUT
  puts "hashed_account:  #{hashed_account.unpack('H*')}" if Constants::DEBUG_OUTPUT

  # Encrypt iteratively hostname and account and master_password
  encrypt01 = Forgiva.iterative_encrypt(Forgiva.iterative_encrypt(hashed_hostname, hashed_account),
                                        hashed_master_password)

  puts "encrypt01:  #{encrypt01.unpack('H*')}" if Constants::DEBUG_OUTPUT

  puts "hashed_renewal_date:  #{hashed_renewal_date.unpack('H*')}" if Constants::DEBUG_OUTPUT
  puts "hashed_master_password:  #{hashed_master_password.unpack('H*')}" if Constants::DEBUG_OUTPUT

  # Encrypt iteratively renewal date and master key
  encrypt02 = Forgiva.iterative_encrypt(hashed_renewal_date, hashed_master_password)

  puts "encrypt02:  #{encrypt02.unpack('H*')}" if Constants::DEBUG_OUTPUT

  # Encrypt iteratively prior generated values
  ret = Forgiva.iterative_encrypt(
    encrypt01,
    encrypt02)

  puts "forgiva_encrypted_inputs:  #{ret.unpack('H*')}" if Constants::DEBUG_OUTPUT

  return ret
end
generate() click to toggle source
# File lib/forgiva.rb, line 27
def generate
  passwords  = {}

  # Getting input data as encrypted as salt
  salt = encrypted_inputs

  puts "SALT: #{salt.unpack('H*')}" if Constants::DEBUG_OUTPUT

  # Getting master password as already hashed SHA512
  key = master_password

  puts "CLEAR KEY: #{key.unpack('H*')}"  if Constants::DEBUG_OUTPUT

  # If we have complexity options, then we overrun key
  # with the pbkdf2 hmac sha256 or sha512 algorithms
  if (@complexity == 2 || @complexity == 3) then
    key = Forgiva.pbkdf2_hmac_sha(key,salt,@complexity)
  end

  puts "ENC KEY: #{key.unpack('H*')}"  if Constants::DEBUG_OUTPUT

  if (@use_scrypt) then
    key = SCrypt::Engine.scrypt(key,salt,131072,8,1,32)
  end


  Constants::ANIMALS.each do |a|
    # For every other animal we re-run pbkdf2 hmac with sha1 over key
    key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(key, salt, 10_000, 32)

    puts "GEN_KEY: #{key.unpack('H*')}"  if Constants::DEBUG_OUTPUT

    passwords[a] = Forgiva.hash_to_password(key,@complexity, @length)
  end

  passwords
end
hashed_account() click to toggle source
# File lib/forgiva.rb, line 69
def hashed_account
  Forgiva.hash_twice(account)
end
hashed_hostname() click to toggle source
# File lib/forgiva.rb, line 65
def hashed_hostname
  Forgiva.hash_twice(hostname)
end
hashed_master_password() click to toggle source
# File lib/forgiva.rb, line 77
def hashed_master_password
  Forgiva.hash_twice(master_password)
end
hashed_renewal_date() click to toggle source
# File lib/forgiva.rb, line 73
def hashed_renewal_date
  Forgiva.hash_twice(renewal_date)
end
passwords() click to toggle source
# File lib/forgiva.rb, line 21
def passwords
  @passwords ||= generate
end