Password hashing, verification, and hash upgrade operations.
This module wraps the configured Sigra.Hasher implementation
(default: Sigra.Hashers.Argon2) to provide a stable API for
password operations. Application code should always use this module
rather than calling hashing libraries directly.
Three-Way Verification
verify_with_upgrade/2,3 returns one of three results:
{:ok, :valid}-- password correct, hash is current{:ok, :valid, new_hash}-- password correct, hash needs upgrade{:error, :invalid}-- password incorrect
This enables transparent migration from bcrypt to Argon2id and automatic rehashing when Argon2 parameters are strengthened.
Enumeration Prevention
The no_user_verify/1 function runs a dummy hash operation when a
user is not found, preventing timing-based user enumeration attacks.
Summary
Functions
Returns true if the hash string has an Argon2 prefix ($argon2).
Returns true if the hash string has a bcrypt prefix ($2b$ or $2a$).
Hashes a plaintext password using the configured hasher.
Checks whether an Argon2id hash needs rehashing due to parameter changes.
Runs a dummy hash to prevent timing-based user enumeration.
Verifies a plaintext password against a hashed password.
Verifies a password and detects whether the hash needs upgrading.
Functions
Returns true if the hash string has an Argon2 prefix ($argon2).
Returns true if the hash string has a bcrypt prefix ($2b$ or $2a$).
Hashes a plaintext password using the configured hasher.
Returns the hashed password string (e.g., "$argon2id$...").
Options
:hasher- Module implementingSigra.Hasher. Default:Sigra.Hashers.Argon2
Examples
iex> hashed = Sigra.Crypto.hash_password("supersecret123")
iex> String.starts_with?(hashed, "$argon2id$")
true
Checks whether an Argon2id hash needs rehashing due to parameter changes.
Parses the hash string to extract m, t, and p parameters and compares
them against the current configuration. Returns true if any parameter
differs or if the hash cannot be parsed.
Non-Argon2 hashes always return true.
Options
:m_cost- Expected memory cost as power of 2. Default: from:argon2_elixirconfig or 16.:t_cost- Expected time cost (iterations). Default: from:argon2_elixirconfig or 3.:parallelism- Expected parallelism. Default: from:argon2_elixirconfig or 4.
Examples
iex> hashed = Sigra.Crypto.hash_password("test")
iex> Sigra.Crypto.needs_rehash?(hashed)
false
iex> Sigra.Crypto.needs_rehash?("$2b$12$...")
true
@spec no_user_verify(keyword()) :: false
Runs a dummy hash to prevent timing-based user enumeration.
When a login attempt references a non-existent user, call this function
to ensure the response time is similar to a real password verification.
Always returns false.
Options
:hasher- Module implementingSigra.Hasher. Default:Sigra.Hashers.Argon2
Examples
iex> Sigra.Crypto.no_user_verify()
false
Verifies a plaintext password against a hashed password.
Returns true if the password matches, false otherwise.
Uses constant-time comparison internally (provided by the hasher).
Options
:hasher- Module implementingSigra.Hasher. Default:Sigra.Hashers.Argon2
Examples
iex> hashed = Sigra.Crypto.hash_password("supersecret123")
iex> Sigra.Crypto.verify_password("supersecret123", hashed)
true
iex> Sigra.Crypto.verify_password("wrong", hashed)
false
@spec verify_with_upgrade(String.t(), String.t() | nil, keyword()) :: {:ok, :valid} | {:ok, :valid, String.t()} | {:error, :invalid}
Verifies a password and detects whether the hash needs upgrading.
Returns one of three results:
{:ok, :valid}-- password matches, hash is current{:ok, :valid, new_hash}-- password matches, caller should persistnew_hash{:error, :invalid}-- password does not match
Hash upgrade is triggered when:
- The hash is bcrypt (
$2b$or$2a$prefix) -- migrates to Argon2id - The hash is Argon2id with stale parameters -- rehashes with current params
When hashed_password is nil, runs timing protection and returns
{:error, :invalid}.
Options
:hasher- Module implementingSigra.Hasher. Default:Sigra.Hashers.Argon2:m_cost- Argon2 memory cost parameter for rehash detection:t_cost- Argon2 time cost parameter for rehash detection:parallelism- Argon2 parallelism parameter for rehash detection
Examples
iex> hashed = Sigra.Crypto.hash_password("secret")
iex> Sigra.Crypto.verify_with_upgrade("secret", hashed)
{:ok, :valid}
iex> Sigra.Crypto.verify_with_upgrade("wrong", hashed)
{:error, :invalid}