class RbNaCl::PasswordHash::Argon2

Since version 1.0.9, Sodium provides a password hashing scheme called Argon2. Argon2 summarizes the state of the art in the design of memory- hard functions. It aims at the highest memory filling rate and effective use of multiple computing units, while still providing defense against tradeoff attacks. It prevents ASICs from having a significant advantage over software implementations.

Constants

ALG_DEFAULT
ARGON2_MAX_OUTLEN
ARGON2_MIN_OUTLEN
ARGON_ERROR_CODES
MEMLIMIT_MAX
MEMLIMIT_MIN
OPSLIMIT_MAX
OPSLIMIT_MIN

Public Class Methods

digest_size_value(digest_size) click to toggle source

Clamps digest size between 16..4294967295

@raise [LengthError] if the value is out of range

@return [Integer] digest_size a valid value for digest size

# File lib/rbnacl/password_hash/argon2.rb, line 197
def self.digest_size_value(digest_size)
  digest_size = digest_size.to_i
  raise LengthError, "digest size too short" if digest_size < ARGON2_MIN_OUTLEN
  raise LengthError, "digest size too long"  if digest_size > ARGON2_MAX_OUTLEN
  digest_size
end
digest_str_verify(password, digest_string) click to toggle source

Compares a password with a digest string

@param [String] password to be hashed @param [String] digest_string to compare to

@return [boolean] true if password matches digest_string

# File lib/rbnacl/password_hash/argon2.rb, line 147
def self.digest_str_verify(password, digest_string)
  raise ArgumentError, "password must be a String" unless password.is_a?(String)
  raise ArgumentError, "digest_string must be a String" unless digest_string.is_a?(String)
  pwhash_str_verify(
    digest_string,
    password, password.bytesize
  )
end
memlimit_value(memlimit) click to toggle source

Clamps memlimit between 8192 bytes and 4 TB (eg. 2**32)

@param [Integer] memlimit, in bytes

@raise [ArgumentError] if the value is out of range

@return [Integer] memlimit a valid value for memlimit

# File lib/rbnacl/password_hash/argon2.rb, line 181
def self.memlimit_value(memlimit)
  case memlimit
  when :interactive then MEMLIMIT_INTERACTIVE
  when :moderate then MEMLIMIT_MODERATE
  when :sensitive then MEMLIMIT_SENSITIVE
  when MEMLIMIT_MIN..MEMLIMIT_MAX then memlimit.to_i
  else
    raise ArgumentError, "memlimit must be within the range 2**(13..32)"
  end
end
new(opslimit, memlimit, digest_size = nil) click to toggle source

Create a new Argon2 password hash object

opslimit and memlimit may be an integer, or one of the following symbols:

:interactive

Suitable for interactive online operations. This requires 32 Mb of dedicated RAM.

:moderate

A compromise between interactive and sensitive. This requires 128 Mb of dedicated RAM, and takes about 0.7 seconds on a 2.8 Ghz Core i7 CPU.

:sensitive

For highly sensitive and non-interactive operations. This requires 128 Mb of dedicated RAM, and takes about 0.7 seconds on a 2.8 Ghz Core i7 CPU

@param [Integer] opslimit the CPU cost (1..10) @param [Integer] memlimit the memory cost (e.g. 2**24) @param [Integer] digest_size the byte length of the resulting digest

@return [RbNaCl::PasswordHash::Argon2] An Argon2 password hasher object

# File lib/rbnacl/password_hash/argon2.rb, line 96
def initialize(opslimit, memlimit, digest_size = nil)
  @opslimit    = self.class.opslimit_value(opslimit)
  @memlimit    = self.class.memlimit_value(memlimit)
  @digest_size = self.class.digest_size_value(digest_size) if digest_size
end
opslimit_value(opslimit) click to toggle source

Clamps opslimit to an acceptable range (3..10)

@param [Integer] opslimit value to be checked

@raise [ArgumentError] if the value is out of range

@return [Integer] opslimit a valid value for opslimit

# File lib/rbnacl/password_hash/argon2.rb, line 163
def self.opslimit_value(opslimit)
  case opslimit
  when :interactive then OPSLIMIT_INTERACTIVE
  when :moderate then OPSLIMIT_MODERATE
  when :sensitive then OPSLIMIT_SENSITIVE
  when OPSLIMIT_MIN..OPSLIMIT_MAX then opslimit.to_i
  else
    raise ArgumentError, "opslimit must be within the range 3..10"
  end
end

Public Instance Methods

digest(password, salt) click to toggle source

Calculate an Argon2 digest for a given password and salt

@param [String] password to be hashed @param [String] salt to make the digest unique

@return [String] scrypt digest of the string as raw bytes

# File lib/rbnacl/password_hash/argon2.rb, line 108
def digest(password, salt)
  raise ArgumentError, "digest_size is required" unless @digest_size
  digest = Util.zeros(@digest_size)
  salt   = Util.check_string(salt, SALTBYTES, "salt")

  status = self.class.pwhash(
    digest, @digest_size,
    password, password.bytesize, salt,
    @opslimit, @memlimit, ALG_DEFAULT
  )
  raise CryptoError, ARGON_ERROR_CODES[status] if status.nonzero?
  digest
end
digest_str(password) click to toggle source

Calculate an Argon2 digest in the form of a crypt-style string. The resulting string encodes the parameters and salt.

@param [String] password to be hashed

@return [String] argon2 digest string

# File lib/rbnacl/password_hash/argon2.rb, line 128
def digest_str(password)
  raise ArgumentError, "password must be a String" unless password.is_a?(String)
  result = Util.zeros(STRBYTES)

  ok = self.class.pwhash_str(
    result,
    password, password.bytesize,
    @opslimit, @memlimit
  )
  raise CryptoError, "unknown error in Argon2#digest_str" unless ok
  result.delete("\x00")
end