Open-source project
RustCrypto/password-hashes avatar
RustCrypto/password-hashes

RustCrypto password hashes: seven KDFs behind one PHC format and trait

Password hashing functions / KDFs

915 stars116 forksRustLicense varies

At a glance

What is it?
RustCrypto/password-hashes is a collection of pure Rust password hashing algorithms, each published as its own crate, sharing a PHC string format and a PasswordVerifier trait so an application can verify a stored hash without knowing in advance which algorithm produced it. Argon2, scrypt, PBKDF2, bcrypt-pbkdf, SHA-crypt, yescrypt and Balloon are all included.
Who is it for?
RustCrypto password-hashes suits a Rust service that needs to verify password hashes with parameters it did not choose itself, because the PHC string carries the algorithm and its cost parameters and the PasswordVerifier trait lets you try several algorithms against one stored hash.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One repository, seven crates, and the choice left to you

RustCrypto password-hashes is a collection of password hashing algorithms, otherwise known as password-based key derivation functions, written in pure Rust.

The repository is a Cargo workspace with one directory per algorithm. The workspace members listed in Cargo.toml are argon2, balloon-hash, bcrypt-pbkdf, password-auth, pbkdf2, scrypt, sha-crypt and yescrypt, with benches and fuzz excluded from the workspace and built separately.

That structure is the design decision. Each algorithm is its own crate published on crates.io with its own documentation, so a project that only wants Argon2 does not pull in scrypt or yescrypt. The repository is where they are developed together and kept consistent, not a single library that re-exports everything.

Seven algorithms is a wide menu, and choosing among them is not something the crate does for you. The documentation is explicit about that and points to the OWASP Password Storage Cheat Sheet for assistance in selecting an appropriate algorithm for your use case.

The full list is Argon2, Balloon hashing, bcrypt-pbkdf, PBKDF2, scrypt, SHA-crypt and yescrypt. That covers the modern memory-hard options, the older standard algorithms that existing databases already contain, and the Unix crypt family. The presence of SHA-crypt and bcrypt-pbkdf is the tell: this is a set built for verification of existing hashes as much as for creating new ones.

All of them share the same minimum supported Rust version, 1.85, and the MSRV policy is stated explicitly. MSRV bumps are considered breaking changes and will be performed only with a minor version bump, which is a stronger guarantee than most libraries make.

The PHC string is what makes old hashes still verifiable

The mechanism that holds the collection together is the PHC string format, and it is the part of this design that matters most in production.

A stored password hash in this ecosystem is not just a hash. It is a string that encodes the algorithm identifier, the algorithm version, the cost parameters, the salt and the hash itself. The example in the documentation is a real one.

rust
let hash_string = "$argon2i$v=19$m=65536,t=1,p=1$c29tZXNhbHQAAAAAAAAAAA$+r0d29hqEB0yasKr55ZgICsQGSkl0v0kgwhd+U3wyRo";

Read it from the left. The $argon2i$ prefix names the algorithm and variant. v=19 is the version. m=65536,t=1,p=1 are the memory cost in kibibytes, the iteration count and the parallelism. Then the base64 salt, then the base64 hash.

Because all of that travels inside the stored value, a server can verify a password it has never seen the parameters of. That is the property that makes algorithm migration possible without a flag day: when you change from PBKDF2 to Argon2, new hashes are written in the new format, and verification tries each algorithm against the stored string and succeeds on whichever one produced it.

The verification code in the documentation does exactly that. It parses the string into a PasswordHash, builds a list of algorithm implementations as trait objects, and tries each one.

rust
let algs: &[&dyn PasswordVerifier<phc::PasswordHash>] = &[
    &Argon2::default(),
    &Pbkdf2::default(),
    &Scrypt::default()
];

for alg in algs {
    if alg.verify_password(input_password.as_ref(), &password_hash).is_ok() {
        return Ok(());
    }
}

Note the Default instances. Those are the crate defaults, not necessarily the parameters you want for hashing new passwords, and the fact that the verification path uses defaults is fine because the parameters that matter come from the stored string, not from the algorithm object.

The trait object list is what makes this polymorphic. The verifier is generic over the hash type, so a single call site can hold Argon2, PBKDF2 and scrypt side by side, and adding a fourth algorithm is a matter of extending the list.

Seven crates, one MSRV, and a lint profile that says what the project values

The workspace lints in Cargo.toml are worth reading, because they are a compact statement of what the maintainers care about.

The clippy configuration enables a long list of correctness and safety lints as warnings. checked_conversions warns whenever an integer conversion could truncate or wrap. cast_lossless, cast_possible_truncation, cast_precision_loss and cast_sign_loss warn on the whole family of numeric cast problems. undocumented_unsafe_blocks warns about unsafe blocks without a safety comment, and unnecessary_safety_comment warns about safety comments on blocks that do not need them. unwrap_used warns about calling unwrap on anything that can fail.

The Rust lints add missing_copy_implementations, missing_debug_implementations and missing_docs, plus trivial_casts, trivial_numeric_casts, unused_lifetimes and unused_qualifications.

missing_docs being on is a strong signal for a library that others depend on. undocumented_unsafe_blocks is the one to note for an audit: unsafe code in a cryptographic primitive is where a reviewer should look, and requiring a safety comment is a minimum bar rather than a strong one, but it is the right direction.

The dev profile also sets opt-level = 2. Cryptographic hashing in a debug build is painfully slow, so compiling dependencies with optimisation even in dev profile is a practical quality-of-life decision that avoids the classic confusion of a KDF benchmark that suddenly gets fast after a release build.

The repository also carries .clippy.toml for clippy configuration and .typos.toml for spell checking, plus a fuzz/ directory excluded from the workspace, which is where the correctness testing for a hashing library belongs. A KDF has a large input space and a wrong answer is a wrong answer, so fuzzing the parsers and the primitives is not optional work.

password-auth, SECURITY.md and the parts that are not algorithms

Two entries in the workspace go beyond the algorithm crates, and both are worth understanding.

password-auth is a workspace member alongside the seven algorithms, and it is not itself a hashing algorithm. A library that only offers hashing primitives leaves you to write the same verification plumbing in every service: read the stored string, try to verify, handle the case where the hash is malformed, rehash on successful login if the parameters are stale. password-auth appears to be the layer that does that once for you.

SECURITY.md at the repository root is the other one. For a library whose whole job is to protect passwords, a published disclosure process is not a formality, and its presence signals that the project expects vulnerability reports.

The licensing is a choice worth reading carefully. All crates are licensed under either the Apache License Version 2.0 or the MIT licence, at your option. That dual licensing is a deliberate convenience for adopters: MIT is shorter and some organisations prefer it, Apache-2.0 includes an explicit patent grant, and letting the consumer pick avoids a legal negotiation for a dependency.

The contribution terms are stated with unusual precision. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. So a contributor cannot impose a different licence on their own patch, and the project does not accept contributions under bespoke terms.

Note that the repository licence field reads unknown, while the documentation states the dual licensing. That is a metadata artefact rather than a disagreement, but it is another reason to read the LICENSE files in the crate directories rather than relying on what the repository page says.

Where this collection is the wrong tool

Three cases come to mind, and none of them are about the algorithms being wrong.

The first is a project that does not need the choice. If you are starting from nothing and will never migrate, you need one algorithm, and the collection makes you pick. Seven crates on crates.io means seven dependency decisions, seven version pins and seven things to keep current. The width that makes the collection valuable for verification is a burden for a greenfield service.

The second is a project that needs password hashing for something other than passwords. The name of the workspace is password-hashes, and the algorithms are password-based key derivation functions. They are tuned for the assumption that the input is a low entropy human-chosen secret and the attacker's advantage is offline guessing. Using them as a general purpose KDF for high entropy key data wastes the memory hardness, and using Argon2 where a plain hash function belongs is a mistake the type system will not catch.

The third is a project that needs the parameter choice made for it. This collection implements algorithms; it does not have opinions about your cost parameters. The documentation routes that decision to the OWASP cheat sheet, and you are expected to bring it. If you want a library that says this is the work factor you should use and adjusts it over time, this is not that, and the README does not describe any rehash-on-login or parameter-upgrade facility in these crates.

There is one more practical consideration that comes from the trait object pattern in the verification example. Supporting multiple algorithms means the server tries each one in turn against every login attempt, and each attempt costs the full cost of the slowest algorithm you support. If you keep PBKDF2 with a high iteration count in the verification list alongside Argon2, every login pays for both. The fix is to prune the list as migrations complete, which is a routine maintenance task this library does not do for you.

Editorial conclusion

RustCrypto password-hashes suits a Rust service that needs to verify password hashes with parameters it did not choose itself, because the PHC string carries the algorithm and its cost parameters and the PasswordVerifier trait lets you try several algorithms against one stored hash. It does not suit a team that wants to pick one algorithm and be done, since there are seven crates and the choice is yours to make, and it does not decide for you, as the documentation points you at the OWASP Password Storage Cheat Sheet for that. Before adopting it, read the OWASP guidance first, since the library implements algorithms and does not select one, and note that every crate in the table requires Rust 1.85 or newer.

Frequently asked questions

Which password hashing algorithms does RustCrypto password-hashes provide?

The collection covers Argon2, Balloon hashing, bcrypt-pbkdf, PBKDF2, scrypt, SHA-crypt and yescrypt, each as its own crate on crates.io with its own documentation, plus a password-auth crate in the workspace.

What Rust version do the RustCrypto password-hashes crates require?

Every algorithm in the table lists an MSRV of 1.85. The MSRV policy states that MSRV bumps are considered breaking changes and will be performed only with a minor version bump.

How does the PHC string format help with verifying old password hashes?

The stored string carries the algorithm identifier, version, cost parameters, salt and hash, so a server can verify a hash whose parameters it did not choose. The documentation shows parsing the string into a PasswordHash and then trying a list of algorithm implementations held as trait objects until one verifies.

Which algorithm should I choose with RustCrypto password-hashes?

The library does not choose for you. The README points to the OWASP Password Storage Cheat Sheet for assistance in selecting an appropriate algorithm for your use case.

What licence applies to the RustCrypto password-hashes crates?

All crates are licensed under either the Apache License Version 2.0 or the MIT licence, at your option. Contributions are dual licensed the same way unless the contributor explicitly states otherwise.

Official sources

  1. Issues
  2. README
  3. RustCrypto/password-hashes on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/rustcrypto-password-hashes.svg)](https://hysenlabs.com/projects/rustcrypto-password-hashes)

Was this useful?