lib/argon2/hash_format.rb
# frozen_string_literal: true
module Argon2
##
# Get the values from an Argon2 compatible string.
#
class HashFormat
attr_reader :variant, :version, :t_cost, :m_cost, :p_cost, :salt, :checksum
# FIXME: Reduce complexity/AbcSize
# rubocop:disable Metrics/AbcSize
def initialize(digest)
digest = digest.to_s unless digest.is_a?(String)
raise Argon2::ArgonHashFail, 'Invalid Argon2 hash' unless self.class.valid_hash?(digest)
_, variant, version, config, salt, checksum = digest.split('$')
# Regex magic to extract the values for each setting
version = /v=(\d+)/.match(version)
t_cost = /t=(\d+),/.match(config)
m_cost = /m=(\d+),/.match(config)
p_cost = /p=(\d+)/.match(config)
# Make sure none of the values are missing
raise Argon2::ArgonHashFail, 'Invalid Argon2 version' if version.nil?
raise Argon2::ArgonHashFail, 'Invalid Argon2 time cost' if t_cost.nil?
raise Argon2::ArgonHashFail, 'Invalid Argon2 memory cost' if m_cost.nil?
raise Argon2::ArgonHashFail, 'Invalid Argon2 parallelism cost' if p_cost.nil?
@variant = variant.to_str
@version = version[1].to_i
@t_cost = t_cost[1].to_i
@m_cost = m_cost[1].to_i
@p_cost = p_cost[1].to_i
@salt = salt.to_str
@checksum = checksum.to_str
end
# rubocop:enable Metrics/AbcSize
##
# Checks whether a given digest is a valid Argon2 hash.
#
# Supports 1 and argon2id formats.
#
def self.valid_hash?(digest)
/^\$argon2(id?|d).{,113}/ =~ digest
end
end
end