lib/authlogic/crypto_providers/scrypt.rb
# frozen_string_literal: true
require "scrypt"
module Authlogic
module CryptoProviders
# SCrypt is the default provider for Authlogic. It is the only
# choice in the adaptive hash family that accounts for hardware
# based attacks by compensating with memory bound as well as cpu
# bound computational constraints. It offers the same guarantees
# as BCrypt in the way of one-way, unique and slow.
#
# Decided SCrypt is for you? Just install the scrypt gem:
#
# gem install scrypt
#
# Tell acts_as_authentic to use it:
#
# acts_as_authentic do |c|
# c.crypto_provider = Authlogic::CryptoProviders::SCrypt
# end
class SCrypt
class << self
DEFAULTS = {
key_len: 32,
salt_size: 8,
max_time: 0.2,
max_mem: 1024 * 1024,
max_memfrac: 0.5
}.freeze
attr_writer :key_len, :salt_size, :max_time, :max_mem, :max_memfrac
# Key length - length in bytes of generated key, from 16 to 512.
def key_len
@key_len ||= DEFAULTS[:key_len]
end
# Salt size - size in bytes of random salt, from 8 to 32
def salt_size
@salt_size ||= DEFAULTS[:salt_size]
end
# Max time - maximum time spent in computation
def max_time
@max_time ||= DEFAULTS[:max_time]
end
# Max memory - maximum memory usage. The minimum is always 1MB
def max_mem
@max_mem ||= DEFAULTS[:max_mem]
end
# Max memory fraction - maximum memory out of all available. Always
# greater than zero and <= 0.5.
def max_memfrac
@max_memfrac ||= DEFAULTS[:max_memfrac]
end
# Creates an SCrypt hash for the password passed.
def encrypt(*tokens)
::SCrypt::Password.create(
join_tokens(tokens),
key_len: key_len,
salt_size: salt_size,
max_mem: max_mem,
max_memfrac: max_memfrac,
max_time: max_time
)
end
# Does the hash match the tokens? Uses the same tokens that were used to encrypt.
def matches?(hash, *tokens)
hash = new_from_hash(hash)
return false if hash.blank?
hash == join_tokens(tokens)
end
private
def join_tokens(tokens)
tokens.flatten.join
end
def new_from_hash(hash)
::SCrypt::Password.new(hash)
rescue ::SCrypt::Errors::InvalidHash
nil
end
end
end
end
end