Skip to content

Password Hashing

Tony Arcieri edited this page May 19, 2019 · 17 revisions

Password hashing functions are specifically designed for password storage and/or deriving encryption keys. Unlike normal hash functions, which are designed to be as fast as possible (so e.g. sha256sum myfile completes as quickly as possible), password hashing functions are specifically designed to be slow to resist brute force attacks against people's passwords by those who recover the hashes.

All password hashing functions in RbNaCl are also proper Key Derivation Functions and can be used to deterministically derive cryptographic key material from a password and an (ideally random) "salt" value.

scrypt

The scrypt password hashing function is useful both for deriving encryption keys from a user's password (as a Key Derivation Function, or KDF) and also or for storing digests of passwords in such a way that they cannot easily be reversed back into a user's original password. In addition, scrypt has a property called "sequential memory hardness", which adds a memory cost to the algorithm to make it even harder to brute force.

KDF Code Example

# Get the password somehow
password = ...

# Generate a random salt (libsodium enforces 32-byte salts)
salt = RbNaCl::Random.random_bytes(RbNaCl::PasswordHash::SCrypt::SALTBYTES)

# scrypt CPU and memory cost parameters
opslimit = 2**20
memlimit = 2**24

# Size of digest to compute in bytes (default 64)
digest_size = 64

output_key_material = RbNaCl::PasswordHash.scrypt(
  password, 
  salt,
  opslimit,
  memlimit,
  digest_size
)

Argon2

The Argon2 password hashing algorithm is the winner of the 2013-2015 Password Hashing Competition and is supported by libsodium versions 1.0.9+.

KDF Code Example

# Get the password somehow
password = ...

# Generate a random salt (libsodium enforces 16-byte salts)
salt = RbNaCl::Random.random_bytes(RbNaCl::PasswordHash::Argon2::SALTBYTES)

# Argon2 CPU and memory cost parameters
opslimit = 5
memlimit = 7_256_678

# Size of digest to compute in bytes (default 64)
digest_size = 64

output_code_example = RbNaCl::PasswordHash.argon2(
  password, 
  salt,
  opslimit,
  memlimit,
  digest_size
)

Password Hashing Example

digest = RbNaCl::PasswordHash.argon2_str(password)
=> "$argon2i$v=19$m=32768,t=4,p=1$aRJ9T7ZVYTVIpkJCbCy9bw$hWiOb6QwZ591nV8tXAfqSLuPD8yqI+ZN/B7ajmU33Zc"
...
RbNaCl::PasswordHash.argon2_valid?(password, digest)
=> true

RDoc