Password Hashing

RecipeCratesCategories
argon2argon2cat-cryptography
scryptscryptcat-cryptography
bcryptbcryptcat-cryptography

A key derivation function⮳ (KDF) is a cryptographic algorithm that derives one or more secret keys from a secret value, such as a master key, a password, or a passphrase, using a pseudorandom function (typically a cryptographic hash function or block cipher).

The original use for a KDF is key derivation, the generation of multiple keys from secret passwords or passphrases.

Despite their original use for key derivation, KDFs are possibly better known for their use in password hashing (password verification by hash comparison), as used by the passwd file or shadow password file. Password hash functions should be relatively expensive to calculate in case of brute-force attacks, and the key stretching of KDFs happen to provide this characteristic. The non-secret parameters are called "salt" in this context.

In that role, key derivation functions take a password, a salt, (and sometimes a cost factor) as inputs, then generate a password hash - deliberately slowly. Their purpose is to make each password guessing trial by an attacker who has obtained a password hash file expensive and therefore the cost of a guessing attack high or prohibitive.

Hash a password, then verify a password against the hash

argon2 argon2-crates.io argon2-github argon2-lib.rs cat-authentication cat-cryptography cat-no-std

argon2 is a pure-Rust implementation of the 'Argon2'⮳ key derivation function, which is commonly used for secure password hashing.

use argon2::Argon2;
use argon2::password_hash::Error;
use argon2::password_hash::PasswordHash;
use argon2::password_hash::PasswordHasher;
use argon2::password_hash::PasswordVerifier;
use argon2::password_hash::SaltString;
use argon2::password_hash::rand_core::OsRng;

fn main() -> Result<(), Error> {
    let password_to_hash = b"super_secret_password"; // Bad password; don't actually use!

    let phc_string = password_hashing(password_to_hash)?;
    println!("Hashed password: {}", phc_string);

    // In a real application, save the PHC string to a secure database or file.
    // The PHC string contains the (one-way) hashed password and the salt, not
    // the password. Should an attacker gain access to the database or file,
    // they won't be able to retrieve the password.

    // When a user logs in, the password they provide is checked against the
    // saved PHC string: The correct password passes the verification
    // function
    let password_to_check = password_to_hash;
    let is_valid = password_verification(password_to_check, &phc_string)?;
    println!("The password is valid: {is_valid}");

    // An incorrect password fails the check
    assert!(!password_verification(b"random_guess", &phc_string)?);

    // Key derivation: derive a child key from a password and salt
    key_derivation()?;

    Ok(())
}

// Hash a password to a “PHC string” suitable for the purposes of password-based
// authentication.
fn password_hashing(password_to_save: &[u8]) -> Result<String, Error> {
    // Generate a random salt
    let salt = SaltString::generate(&mut OsRng);

    // Configure Argon2 with default params (Argon2id v19)
    let argon2 = Argon2::default();

    // Hash the password to a PHC string ($argon2id$v=19$...)
    let phc_string = argon2.hash_password(password_to_save, &salt)?.to_string();

    Ok(phc_string)
}

// Verify a given password against the provided PHC string.
// Returns true if the password is correct.
fn password_verification(
    password_to_check: &[u8],
    phc_string: &str,
) -> Result<bool, Error> {
    // Parsed representation of a PHC string
    let parsed_hash = PasswordHash::new(phc_string)?;

    // Verify the password
    Ok(Argon2::default()
        .verify_password(password_to_check, &parsed_hash)
        .is_ok())
}

// Transform a password into cryptographic keys that can be used for e.g.
// encryption.
fn key_derivation() -> Result<(), Error> {
    let password = b"passwd"; // Bad password; don't use!
    let salt = b"example salt, etc, etc"; // Salt should be unique per password

    let mut output_key_material = [0u8; 128]; // Can be any desired size

    // Hash a password and associated parameters into the provided output
    // buffer.
    Argon2::default().hash_password_into(
        password,
        salt,
        &mut output_key_material,
    )?;

    // `output_key_material` can now be used as a cryptographic key

    Ok(())
}

scrypt

scrypt scrypt-crates.io scrypt-github scrypt-lib.rs cat-authentication cat-cryptography cat-no-std

The 'scrypt' key derivation function is designed to be far more secure against hardware brute-force attacks than alternative functions such as 'PBKDF2' or 'bcrypt'.

use std::error::Error;

use scrypt::Scrypt;
use scrypt::password_hash::PasswordHash;
use scrypt::password_hash::PasswordHasher;
use scrypt::password_hash::PasswordVerifier;
use scrypt::password_hash::SaltString;
use scrypt::password_hash::rand_core::OsRng;

fn main() -> Result<(), Box<dyn Error>> {
    // 1) When setting the password, hash it and store the hash

    // Password to be hashed
    let password = b"super_secret_password";

    // Generate a random salt
    let salt = SaltString::generate(&mut OsRng);

    // Hash the password to a PHC string ($scrypt$...)
    let password_hash: PasswordHash<'_> =
        Scrypt.hash_password(password, &salt)?;

    // Print the hashed password
    let password_hash = password_hash.to_string();
    println!("Hashed password: {}", password_hash);

    // 2) Later, in order to verify a password, the hash is retrieved from the
    // database, and the password is checked against it.

    let parsed_hash = PasswordHash::new(&password_hash)?;
    let is_valid = Scrypt.verify_password(password, &parsed_hash).is_ok();

    // Print the verification result
    println!("Password is valid: {}", is_valid);

    Ok(())
}

bcrypt

bcrypt bcrypt-crates.io bcrypt-github bcrypt-lib.rs

'bcrypt'⮳ is a password-hashing function. Besides incorporating a salt to protect against rainbow table attacks, 'bcrypt'⮳ is an adaptive function: over time, the iteration count can be increased to make it slower, so it remains resistant to brute-force search attacks even with increasing computation power. 'bcrypt'⮳ is not a key derivation function (KDF). For example, bcrypt cannot be used to derive a 512-bit key from a password.

use std::error::Error;

use bcrypt::DEFAULT_COST;
use bcrypt::hash;
use bcrypt::verify;

fn main() -> Result<(), Box<dyn Error>> {
    // 1) When setting the password, hash it and store the hash:

    // Password to be hashed
    let password = "super_secret_password";

    // Generate a password hash using the default cost.
    // The salt is generated randomly using the OS randomness
    let hashed_password: String = hash(password, DEFAULT_COST)?;

    println!("Hashed password: {}", hashed_password);

    // 2) Later, in order to verify a password, the hash is retrieved from the
    // database, and the password is checked against it:

    let is_valid = verify(password, &hashed_password)?;
    println!("Password is valid: {}", is_valid);

    Ok(())
}

For more algorithms, see Rust Crypto Password Hashes.