CI Hex.pm HexDocs

Digestif is a password-hashing library with a deliberately small application API and strict resource bounds around stored hashes.

hash = Digestif.hash("correct horse battery staple")

Digestif.verify?("correct horse battery staple", hash)
#=> true

Digestif.needs_rehash?(hash)
#=> false

The default is Argon2id with 32 MiB of memory, two iterations, and one lane:

def deps do
  [
    {:digestif, "~> 0.4"}
  ]
end

CAUTION

Digestif is a work in progress and has not reached a stable 1.0 release. The API, configuration, and hash encodings may change without a migration path before then. Do not use it in production until a version 1.0 release.

Configuration

Select one primary hasher in application configuration:

config :digestif,
  hasher: {Digestif.Argon2id, []}

Set work factors centrally so hashing and verification always share one validated resource budget:

config :digestif,
  hasher: {Digestif.Argon2id, m_cost: 16}

Existing hashes can migrate without a flag that accepts arbitrary algorithms. Declare the exact legacy hashers that may verify:

config :digestif,
  hasher: {Digestif.Argon2id, []},
  legacy_hashers: [
    {Digestif.PBKDF2, iterations: 600_000, max_iterations: 1_000_000},
    {Digestif.Bcrypt, log_rounds: 12, max_cost: 16}
  ]

After a successful verify?/2, needs_rehash?/1 returns true for a legacy hash. Create and persist a new hash with the primary hasher.

Algorithms

Argon2id is included by default. argon2_elixir compiles a native extension, so deployments need a C toolchain when precompiled artifacts are unavailable. PBKDF2 uses OTP :crypto and requires no additional dependency. Bcrypt support is optional; add its backend only when the application needs to migrate existing bcrypt hashes:

{:bcrypt_elixir, "~> 3.0"}

Configure {Digestif.PBKDF2, options} when PBKDF2 is specifically required. Bcrypt is primarily for migration because it distinguishes only the first 72 password bytes.

Every bundled adapter:

  • validates options before use;
  • bounds encoded-hash length and attacker-controlled verification cost;
  • performs configured dummy work for malformed or over-budget hashes;
  • emits self-describing hashes; and
  • reports when its work factor should be upgraded.

Verification budgets default to the cost used for new hashes. Imported hashes with higher legitimate costs require an explicit larger budget. See each adapter's module documentation for the allowed ranges.

Libraries and multiple policies

The top-level facade uses application configuration for the common single policy case. Authentication libraries and multi-tenant applications should pass explicit {module, options} tuples through Digestif.Hasher:

primary = {Digestif.Argon2id, []}
legacy = [{Digestif.PBKDF2, []}]

case Digestif.Hasher.verify_with_hashers(password, stored_hash, primary, legacy) do
  {:ok, selected} ->
    Digestif.Hasher.needs_rehash?(stored_hash, selected, primary)

  :error ->
    false
end

License

MIT