howdy/auth/password_hasher
How password credentials are stored. auth.with_passwords uses
pbkdf2_sha256, which runs on OTP’s crypto module and needs no native
code of its own. A different algorithm, such as Argon2id from a native
library, plugs in with auth.with_password_hasher.
Types
The operations Howdy needs from a password hashing algorithm.
hash(password)returns a self-describing encoded hash with a fresh random salt.verify(encoded, password)reports whether the password matches, comparing in constant time.needs_rehash(encoded)isTruewhen a stored hash is weaker than this hasher would produce now; Howdy then rehashes it after a successful login.rehash(encoded, password)hashes again without lowering any cost the old hash already exceeds.
Howdy passes NFC-normalized passwords, except when checking a credential
stored before normalization was introduced. An Error means the hash could
not be produced or read: Howdy fails closed with an internal error and never
treats it as a wrong password. Hashing is expensive, so run it where it
cannot block a scheduler (a dirty NIF, or OTP’s crypto).
pub type PasswordHasher {
PasswordHasher(
hash: fn(String) -> Result(String, Nil),
verify: fn(String, String) -> Result(Bool, Nil),
needs_rehash: fn(String) -> Bool,
rehash: fn(String, String) -> Result(String, Nil),
)
}
Constructors
-
PasswordHasher( hash: fn(String) -> Result(String, Nil), verify: fn(String, String) -> Result(Bool, Nil), needs_rehash: fn(String) -> Bool, rehash: fn(String, String) -> Result(String, Nil), )
Values
pub fn pbkdf2_sha256() -> PasswordHasher
PBKDF2-HMAC-SHA256 with 600,000 iterations, a fresh 16 byte salt and a 32 byte hash, OWASP’s recommendation for this algorithm. Hashes are stored in PHC string format:
$pbkdf2-sha256$i=600000,l=32$<salt>$<hash>
with unpadded standard base64. It is not memory-hard: a leaked database costs an attacker less to guess on GPUs than an Argon2id one would, which the password length minimum and common-password check offset.