Announcing the `password-hash` crate

The RustCrypto Org has released v0.1 of the password-hash crate, which provides traits and types for password hashing functions:

It provides an implementation of the PHC string format as well as support for upgrading password hashes in the legacy Modular Crypt Format (MCF).

The password-hash crate is used by the following RustCrypto password hashing crates:

The PasswordHash::verify_password function makes it possible to specify multiple algorithms to verify a password hash against, allowing you to store password hashes with multiple algorithms and verify a password against all of them:

use password_hash::PasswordHash;

use argon2::Argon2;
use pbkdf2::Pbkdf2;
use scrypt::Scrypt;

let hash_string = load_hash_string_for_user(...); // e.g. `$argon2id$...`
let input_password = read_password_from_user();

let password_hash = PasswordHash::new(&hash_string)
    .expect("invalid password hash");

// Trait objects for algorithms to support
let algs = &[Argon2::default(), Pbkdf2, Scrypt];

if password_hash.verify_password(algs, input_password).is_ok() {
    access_granted();
} else {
    access_denied();
}

Enjoy!

18 Likes

A part of this announcement that deserves some emphasis: The argon2 crate finally emerges with proper use and belonging to the right group of people after having been reserved for four years. That's a good example to consider next time a discussion of crate reservation policy comes up.

5 Likes

Do these implementations provide any constant-time guarantees? When I looked into password hashing last year, this was one of the sticking points for getting my implementation past the CISO.

2 Likes

Note that I (a core member of the RustCrypto org together with @bascule) have reserved argon2 in 2017, while the first version of argon2rs was registered in 2016. I went on a bit of "reservation" spree after I understood that unfortunately crate owners are not always open to cooperation or it could be practically impossible to reach them. I intentionally didn't want to create a competing implementations for crates with a good traction (plus the project had other priorities at the time), but I was open to sharing the reserved names. Yes, it was on a condition of joining the project, I understand that some may not like such string attached, but it has worked well in the case of rsa and hkdf.

Though, I do agree with you regarding the need for a crate reservation policy. I even drafted my own proposal for it.

@djc
Comparison of values and decoding of B64 is guaranteed to be done in const time (it's done via subtle and base64ct respectively). For algorithms themselves we do not make such guarantees (e.g. for PBKDF2 it depends on properties of a used hash function and they generally do not care about const time), but as far as I know, it's not required for security, since only stored hashes are considered to be secret and not input passwords.

2 Likes

There are two places where side-channel attacks on password hashing algorithms matter:

The password-hash crate mitigates the first via a constant-time comparison of the hash, but also by making it easy to generate a large, cryptographically random salt value. Taken together this provides a belt-and-suspenders defense against such attacks, but also note that they are somewhat impractical to begin with.

The second is really a matter of algorithm design. Argon2, for example, has two main variants: Argon2d which optimizes against attackers performing time-memory tradeoffs, and Argon2i which is hardened against side-channel attacks. The Argon2id variant is a hybrid mixture thereof which doesn't provide as good of a TMTO defense as Argon2d, but includes some of the side-channel protections of Argon2i.

In either case if side-channel resistance is a concern, we have options available.

Can you link any relevant articles about this attack scenario? If I understand it correctly, you mean that attacker will be able to reconstruct user password by observing computation timings, but I think that for password hashing it's far less important than for encryption, to the point of impracticality. If a legitimate user enters a correct password, then computation is done only once, so to accumulate enough data adversary has not only to be able to know which user logins at the moment, but also listen long enough for selected user logins which are few and far in between. And even before that, password hashing is an intentionally long computation, which uses plain text password only in the very initial computation steps, thus making recovery of a plaintext password from timings even harder.

But either way, ideally plaintext password should not leave user computer in the first place. In other words, people should use PAKE whenever possible.

I agree it's a somewhat impractical scenario. One of the main places it's discussed is in the Argon2 paper (see sections 2.2, 3, and 4.1, for example):

1 Like

One of the side channels they were worried about at my previous employer was leaking the existence of a named user. Obviously this goes beyond just the password hashing, but it's important to understand the performance characteristics of the password hash verification step as well.

1 Like

Yeah, user enumeration attacks are rather tricky and most systems are vulnerable in some way or another.

In that case the much larger timing sidechannel will generally be whether the password hashing function was run at all or not, as opposed to the much smaller timing variability of a short-circuiting comparison of the hash.

Systems which want to avoid this attack should always run the password hashing function, substituting a placeholder/random salt in the event a user isn't found, and then ignoring the resulting hash in the event of a nonexistent user.

2 Likes