Features
What hsh does, and where it stops
Each section says what the feature is, how to turn it on and what is not finished yet.
Hash and verify with one policy
A Policy says which algorithm new hashes use and with which parameters. Policy::owasp_minimum_2025() follows the OWASP Password Storage Cheat Sheet's 2025 minimums; PolicyBuilder starts from a preset or from scratch.
use ;
use PolicyBuilder;
let policy = owasp_minimum_2025;
let stored = hash?;
// $argon2id$v=19$m=19456,t=2,p=1$...
let bcrypt_policy = from_preset
.primary
.build?;
Hashes are stored as PHC strings ($argon2id$..., $scrypt$..., $pbkdf2-sha256$...), or in bcrypt's own $2b$ format. Salts come from the operating system's random-number generator.
Rehash on login
api::verify_and_upgrade verifies against whatever algorithm made the stored hash, then compares it with the current policy.
use ;
match verify_and_upgrade?
A new hash comes back when the stored one used another algorithm, lower cost parameters, a different PBKDF2 hash function or an older pepper key. A bcrypt hash checked under the default policy, for example, comes back as Argon2id. Old hashes keep verifying at their original cost until then, so there is no batch migration and no forced password reset.
Four algorithms
| Algorithm | Default in the OWASP 2025 preset | Notes |
|---|---|---|
| Argon2id | 19,456 KiB, 2 passes, 1 lane | The default, and memory-hard |
| bcrypt | cost 10 | Rejects input over 72 bytes unless pre-hashing is turned on |
| scrypt | N = 217, r = 8, p = 1 | |
| PBKDF2 | HMAC-SHA-256, 600,000 iterations | HMAC-SHA-512 is also supported; the only option in FIPS mode |
hsh calibrate measures how long each setting takes on your hardware and suggests the parameters that meet a time budget.
A pepper with key rotation
With the pepper feature, a server-side secret is mixed into every password with HMAC-SHA-256 before hashing, so a stolen database alone is not enough to crack passwords offline. Keys are numbered, and the number is stored with the hash:
use ;
use ;
let pepper = builder
.add
.current
.build
.unwrap;
let policy = owasp_minimum_2025.with_pepper;
let stored = hash?;
// hsh-pepper:1:$argon2id$...
Add a second key and make it current, and each hash made under key 1 is rehashed under key 2 at its next successful verify. A peppered hash checked without the pepper is Invalid, never accepted.
LocalPepper keeps the keys in your process. The AWS KMS, Google Cloud KMS, Azure Key Vault and HashiCorp Vault providers in hsh-kms are stubs in 0.0.10: they return an error.
FIPS 140-3 mode
Policy::fips_140_pbkdf2() hashes with PBKDF2 only, and only when it can use a FIPS-validated implementation. Build hsh with the fips feature and PBKDF2 runs inside the AWS-LC FIPS module through aws-lc-rs, by way of the hsh-backend-awslc crate. Without the feature, a FIPS policy refuses to hash and returns an error instead of falling back to the pure-Rust code; a FIPS policy asked to use Argon2id refuses too.
The fips build needs Go, CMake and a recent clang to compile AWS-LC. hsh itself is not FIPS-validated: the validated part is the AWS-LC module, so check its version and certificate with your compliance team.
Safety rails
- No silent truncation. bcrypt only reads 72 bytes; hsh rejects a longer password rather than hash part of it, unless you opt into pre-hashing.
- Constant-time comparisons with
subtlewherever a hash is compared. - Secrets wiped from memory with
zeroizewhen their buffers are dropped. - No unsafe code: every crate in the workspace is
#![forbid(unsafe_code)]. The default build is pure Rust; thefipsfeature links the AWS-LC C library throughaws-lc-rs.
Command-line tool
cargo install hsh-cli installs the hsh command. It reads passwords on standard input:
|
|
verify prints valid and exits with 0, or prints invalid and exits with
- The other commands are
rehash,inspect-backend, which shows the route a preset would take, andcompletionsfor shell completions.--jsonswitches the output to JSON. Avoid passing passwords in arguments, where shell history and the process list can see them.
General-purpose digests
hsh-digest offers SHA-256, SHA-384, SHA-512, SHA3-256, SHA3-384, SHA3-512 and BLAKE3 behind one interface, one-shot or streaming. It is for content addressing and checksums, not for passwords. KangarooTwelve and Ascon have feature names but no implementation yet.
use ;
let digest = hash.unwrap;
assert_eq!;
Signed releases
The release workflow signs what it attaches to a GitHub release. For 0.0.10 that is a Linux x86-64 build of the hsh command and a SHA256SUMS file, each with a keyless cosign bundle, and the binary carries SLSA build provenance. The security page shows how to check them.