Expand description
Password hashing for the web admin’s operators.
PBKDF2-HMAC-SHA256 over ring::pbkdf2, which is already this crate’s crypto
backend everywhere else. Deliberately not Argon2id, which is the stronger
primitive: it would add four crates (argon2, password-hash, base64ct,
blake2) to a certificate authority’s dependency graph – all of them
audited on every cargo deny check, since deny.toml runs with
all-features = true – for a subsystem that is enabled = false by
default and whose password is the bootstrap credential in a design that
ends in a second factor. PBKDF2-HMAC-SHA256 at 600 000 iterations is
OWASP’s current recommendation for the non-Argon2 case.
The stored form is self-describing, so that trade can be revisited without
a migration: raising the iteration count, or swapping the algorithm
outright, needs a new branch in verify_password and nothing else –
needs_rehash then re-encodes each row on its owner’s next successful
login.
pbkdf2-sha256$600000$<salt-b64url>$<hash-b64url>The other half of this module is check_password_policy, which is the
single place every rule about an acceptable password lives. Three of them:
length, a list of words naming this deployment (PasswordContext), and a
corpus of common passwords compiled in from corpus/common-passwords.txt.
The last two are ASVS 5.0 V6.2.11 and V6.2.4/V6.2.12, and both rest on the
same observation – the length rule has already refused everything short,
so a corpus filtered at MIN_PASSWORD_LEN is 195 KB where the list it was
derived from is 8.5 MB. corpus/README.md has the provenance and the
budget.
This module holds no database access and no I/O: it is shared by the CLI
(admin user create/passwd) and the web login path, which is why it lives
under admin:: beside the other logic both front ends use rather than
inside webadmin::.
Structs§
- Password
Context - The words that name this deployment, barred from an operator’s password.
Enums§
- Password
Error - A stored hash that could not be read back.
Constants§
- MAX_
PASSWORD_ LEN - Longest password accepted. A DoS control, not a security one: without it a login request could hand 600 000 iterations a multi-megabyte input.
- MIN_
PASSWORD_ LEN - Shortest password accepted. Length is the only rule – composition rules (“one digit, one symbol”) measurably push people towards weaker, more guessable passwords, and this is an operator-facing surface with a handful of accounts, not a consumer signup.
- RECOVERY_
ITERATIONS - The cost
hash_generated_secretuses. Named so the reasoning above has something to point at.
Functions§
- check_
password_ policy - Rejects a password before it is ever hashed.
- dummy_
hash - A well-formed hash of nothing, verified against when the username is unknown so a miss costs the same KDF time as a wrong password.
- hash_
generated_ secret - Hashes a high-entropy generated secret, at a cost matched to the fact that it is one.
- hash_
password - Hashes
passwordunder the current parameters, returning the encoded form to store. - hash_
password_ off_ runtime hash_passwordon tokio’s blocking pool, for the login path’s rehash.Noneonly when the runtime is shutting down; the rehash then waits for the next login.- needs_
rehash - Whether
storedwas written under parameters this build has since moved past – a different algorithm, or a lower iteration count. - verify_
password - Verifies
passwordagainst a stored hash, in constant time (ring::pbkdf2::verifycompares that way). - verify_
password_ off_ runtime verify_passwordon tokio’s blocking pool.