Migration

Moving password storage to hsh

Existing hashes keep working, and move to the current policy as users sign in.

The general approach

Point hsh at the hashes you already store. api::verify_and_upgrade reads PHC strings and bcrypt's $2b$ format, verifies each with the algorithm and parameters it was made with, and returns a new hash under your policy when the old one falls short. Save that hash and the migration happens one sign-in at a time.

From the bcrypt crate

-use bcrypt::{hash, verify, DEFAULT_COST};
-let stored = hash("hunter2", DEFAULT_COST)?;
-let ok = verify("hunter2", &stored)?;
+use hsh::{api, Policy};
+let policy = Policy::owasp_minimum_2025();
+let outcome = api::verify_and_upgrade(&policy, "hunter2", &stored)?;
+let ok = outcome.is_valid();

With the default policy, each bcrypt hash that verifies comes back as Argon2id. To keep bcrypt for new hashes, set .primary(PrimaryAlgorithm::Bcrypt) on a PolicyBuilder. Passwords over 72 bytes are rejected rather than truncated.

From rust-argon2

-let cfg = argon2::Config::owasp5();
-let stored = argon2::hash_encoded(b"hunter2", salt, &cfg)?;
-let ok = argon2::verify_encoded(&stored, b"hunter2")?;
+let policy = hsh::Policy::owasp_minimum_2025();
+let stored = hsh::api::hash(&policy, "hunter2")?;
+let ok = hsh::api::verify_and_upgrade(&policy, "hunter2", &stored)?.is_valid();

hsh makes the salt for you.

From argonautica

argonautica was archived in 2019. Replace its Hasher and Verifier with api::hash and api::verify_and_upgrade; its with_secret_key corresponds to hsh's pepper, set with Policy::with_pepper and the pepper feature. The repository's migration guide has the checklist.

From an older hsh

Version 0.0.9 replaced the stringly-typed API with Policy, api::hash and api::verify_and_upgrade. The compat-v0_0_x feature brings back the old shape while you move call sites; it is deprecated.

The repository has guides for bcrypt, rust-argon2, password-auth and djangohashers.