96 lines
2.7 KiB
Elixir
96 lines
2.7 KiB
Elixir
|
defmodule Argon2 do
|
||
|
@moduledoc """
|
||
|
Argon2 password hashing for Elixir using Rust NIFs.
|
||
|
|
||
|
This module provides a secure way to hash passwords using the Argon2i algorithm
|
||
|
with configuration presets following security best practices.
|
||
|
|
||
|
## Security Presets
|
||
|
|
||
|
* `:owasp` (default) - OWASP recommended settings (m=19456, t=2, p=1)
|
||
|
* `:strong` - Higher security settings (m=65540, t=3, p=4)
|
||
|
* `:test_unsafe` - Fast settings for testing only (m=1024, t=1, p=1)
|
||
|
|
||
|
## Examples
|
||
|
|
||
|
# Hash with default OWASP settings
|
||
|
iex> hash = Argon2.hash_password("secure_password123")
|
||
|
iex> String.starts_with?(hash, "$argon2i$v=19$m=19456,t=2,p=1$")
|
||
|
true
|
||
|
|
||
|
# Hash with strong settings
|
||
|
iex> hash = Argon2.hash_password("secure_password123", "strong")
|
||
|
iex> String.starts_with?(hash, "$argon2i$v=19$m=65540,t=3,p=4$")
|
||
|
true
|
||
|
|
||
|
# Verify password
|
||
|
iex> hash = Argon2.hash_password("secure_password123")
|
||
|
iex> Argon2.verify_password("secure_password123", hash)
|
||
|
true
|
||
|
iex> Argon2.verify_password("wrong_password", hash)
|
||
|
false
|
||
|
|
||
|
## Security Notes
|
||
|
|
||
|
* Passwords must be at least 8 characters long
|
||
|
* Each hash uses a unique random salt
|
||
|
* The `:test_unsafe` preset should never be used in production
|
||
|
"""
|
||
|
|
||
|
@type password :: String.t()
|
||
|
@type hash :: String.t()
|
||
|
@type config :: String.t()
|
||
|
|
||
|
@doc """
|
||
|
Hashes a password using Argon2i.
|
||
|
|
||
|
## Options
|
||
|
|
||
|
* `config` - One of `"owasp"` (default), `"strong"`, or `"test_unsafe"`
|
||
|
|
||
|
## Examples
|
||
|
|
||
|
iex> hash = Argon2.hash_password("secure_password123")
|
||
|
iex> is_binary(hash)
|
||
|
true
|
||
|
|
||
|
## Security Notes
|
||
|
|
||
|
* Passwords must be at least 8 characters
|
||
|
* A unique random salt is used for each hash
|
||
|
* The default OWASP preset is recommended for most use cases
|
||
|
|
||
|
Raises `ArgumentError` if the password is less than 8 characters long.
|
||
|
"""
|
||
|
@spec hash_password(password :: password, config :: config | nil) :: hash
|
||
|
def hash_password(password, config \\ nil) do
|
||
|
case Argon2.Native.hash_password(password, config) do
|
||
|
{:error, message} -> raise ArgumentError, message
|
||
|
result -> result
|
||
|
end
|
||
|
end
|
||
|
|
||
|
@doc """
|
||
|
Verifies a password against a hash.
|
||
|
|
||
|
Takes constant time regardless of whether the password matches or not.
|
||
|
|
||
|
## Examples
|
||
|
|
||
|
iex> hash = Argon2.hash_password("secure_password123")
|
||
|
iex> Argon2.verify_password("secure_password123", hash)
|
||
|
true
|
||
|
|
||
|
Raises `ArgumentError` if:
|
||
|
* The password is less than 8 characters long
|
||
|
* The hash format is invalid
|
||
|
"""
|
||
|
@spec verify_password(password :: password, hash :: hash) :: boolean
|
||
|
def verify_password(password, hash) do
|
||
|
case Argon2.Native.verify_password(password, hash) do
|
||
|
{:error, message} -> raise ArgumentError, message
|
||
|
result -> result
|
||
|
end
|
||
|
end
|
||
|
end
|