defmodule Argon2 do @moduledoc """ Elixir wrapper for the Argon2 password hashing function. For a lower-level API, see `Argon2.Base`. ## Configuration See the documentation for `Argon2.Stats` for information about configuration. ## Argon2 Argon2 is the winner of the [Password Hashing Competition (PHC)](https://password-hashing.net). Argon2 is a memory-hard password hashing function which can be used to hash passwords for credential storage, key derivation, or other applications. Argon2 has the following three variants (Argon2id is the default): * Argon2d - suitable for applications with no threats from side-channel timing attacks (eg. cryptocurrencies) * Argon2i - suitable for password hashing and password-based key derivation * Argon2id - a hybrid of Argon2d and Argon2i Argon2i, Argon2d, and Argon2id are parametrized by: * A **time** cost, which defines the amount of computation realized and therefore the execution time, given in number of iterations * A **memory** cost, which defines the memory usage, given in kibibytes * A **parallelism** degree, which defines the number of parallel threads More information can be found in the documentation for the `Argon2.Stats` module and at the [Argon2 reference C implementation repository](https://github.com/P-H-C/phc-winner-argon2). ## Comparison with Bcrypt / Pbkdf2 Argon2 has better password cracking resistance than Bcrypt and Pbkdf2. Its main advantage is that, as it is a memory-hard function, it is designed to withstand parallel attacks that use GPUs or other dedicated hardware. """ use Comeonin alias Argon2.Base @doc """ Hashes a password with a randomly generated salt. ## Options In addition to the `:salt_len` option shown below, this function also takes options that are then passed on to the `hash_password` function in the `Argon2.Base` module. See the documentation for `Argon2.Base.hash_password/3` for further details. * `:salt_len` - the length of the random salt * the default is 16 (the minimum is 8) bytes ## Examples The following examples show how to hash a password with a randomly-generated salt and then verify a password: iex> hash = Argon2.hash_pwd_salt("password") ...> Argon2.verify_pass("password", hash) true iex> hash = Argon2.hash_pwd_salt("password") ...> Argon2.verify_pass("incorrect", hash) false """ @impl true def hash_pwd_salt(password, opts \\ []) do Base.hash_password(password, Base.gen_salt(Keyword.get(opts, :salt_len, 16)), opts) end @doc """ Verifies a password by hashing the password and comparing the hashed value with a stored hash. See the documentation for `hash_pwd_salt/2` for examples of using this function. """ @impl true def verify_pass(password, stored_hash) do hash = :binary.bin_to_list(stored_hash) case Base.verify_nif(hash, password, argon2_type(stored_hash)) do 0 -> true -35 -> false error -> Base.handle_error(error) end end defp argon2_type("$argon2id" <> _), do: 2 defp argon2_type("$argon2i" <> _), do: 1 defp argon2_type("$argon2d" <> _), do: 0 defp argon2_type(_) do raise ArgumentError, "Invalid Argon2 hash. " <> "Please check the 'stored_hash' input to verify_pass." end end