AR

Argon2

security v4.0+

Argon2

Version: 4.0+

Overview

Argon2 is the password hashing algorithm used in the Prismatic Platform for secure credential storage. Winner of the Password Hashing Competition (PHC) in 2015, Argon2 is specifically designed to be resistant to GPU and ASIC-based cracking attacks by requiring significant memory in addition to CPU time -- making parallel cracking attempts prohibitively expensive. This memory-hardness property is what distinguishes Argon2 from older algorithms like bcrypt and scrypt, which are primarily CPU-bound and increasingly vulnerable to specialized hardware attacks.

The Prismatic Platform uses Argon2id (the hybrid variant) for all password hashing operations. This variant combines Argon2i's resistance to side-channel attacks with Argon2d's resistance to GPU cracking, providing the best overall security profile for credential storage. The platform's authentication system never stores plain-text passwords -- all credentials are hashed with Argon2id before storage in PostgreSQL, and verification occurs by rehashing the provided password and comparing the result.

Argon2's configurable parameters (memory, iterations, parallelism) allow the platform to tune the hashing cost to balance security and user experience, targeting a hash time of approximately 500ms on production hardware. This deliberate slowness is a feature, not a bug -- it ensures that even if the password database is compromised, attackers face an enormous computational cost to crack individual passwords. The platform implements automatic parameter upgrades so that as hardware improves, existing password hashes are transparently rehashed with stronger parameters during the next successful login.

Key Features

Argon2 provides a comprehensive set of security properties that make it the current gold standard for password hashing in production systems.

  • Memory-Hard: Requires configurable memory (64MB+ per hash by default) making GPU/ASIC parallelism expensive
  • Time-Hard: Configurable iteration count for CPU cost tuning, scaling with Moore's Law over time
  • Parallelism: Multi-threaded hashing with configurable lane count for optimal utilization of modern CPUs
  • Side-Channel Resistant: Argon2id variant resists timing attacks through data-independent memory access patterns
  • Salt Generation: Automatic cryptographic salt generation ensuring identical passwords produce different hashes
  • PHC Winner: Peer-reviewed, competition-winning algorithm vetted by the international cryptographic community
  • Parameter Encoding: Hash output self-describes its parameters, enabling transparent parameter upgrades
  • OWASP Recommended: Designated as the primary choice for password hashing in OWASP guidelines
PropertyArgon2idbcryptscryptPBKDF2
Memory-hardnessYes (configurable)NoYes (configurable)No
Side-channel resistanceYes (hybrid)PartialNoNo
GPU resistanceStrongModerateStrongWeak
ASIC resistanceStrongModerateModerateWeak
PHC winnerYesNo (predates PHC)NoNo
OWASP primary choiceYesAcceptableAcceptableAcceptable
Parameter self-encodingYesYesVariesVaries

Platform Integration

Argon2 secures all credential storage in the platform through a dedicated password module that encapsulates hashing, verification, and parameter management. The module is used by the authentication system in prismatic_web and any service that handles user credentials.

defmodule PrismaticWeb.Auth.Password do
  @moduledoc """
  Argon2id password hashing with automatic parameter upgrades.
  All credential storage in the platform flows through this module.
  """

  @doc "Hash a password with Argon2id using platform-standard parameters"
  @spec hash(String.t()) :: String.t()
  def hash(password) do
    Argon2.hash_pwd_salt(password,
      t_cost: 3,         # 3 iterations
      m_cost: 16,        # 64MB memory (2^16 KB)
      parallelism: 4     # 4 threads
    )
  end

  @doc "Verify a password against its stored hash"
  @spec verify(String.t(), String.t()) :: boolean()
  def verify(password, hash) do
    Argon2.verify_pass(password, hash)
  end

  @doc "Check if a hash needs rehashing due to parameter upgrade"
  @spec needs_rehash?(String.t()) :: boolean()
  def needs_rehash?(hash) do
    Argon2.needs_rehash?(hash, t_cost: 3, m_cost: 16)
  end

  @doc "Perform no-op hash to prevent timing attacks on invalid usernames"
  @spec no_user_verify() :: false
  def no_user_verify do
    Argon2.no_user_verify()
  end
end

The authentication pipeline transparently upgrades password hashes when parameters change:

defmodule PrismaticWeb.Auth.Session do
  @moduledoc "Session authentication with transparent Argon2 parameter upgrades."

  alias PrismaticWeb.Auth.Password
  alias PrismaticStorage.Repo
  alias PrismaticWeb.Schema.User

  @spec authenticate(String.t(), String.t()) :: {:ok, User.t()} | {:error, :invalid_credentials}
  def authenticate(email, password) do
    case Repo.get_by(User, email: email) do
      nil ->
        Password.no_user_verify()
        {:error, :invalid_credentials}

      user ->
        if Password.verify(password, user.hashed_password) do
          maybe_rehash(user, password)
          {:ok, user}
        else
          {:error, :invalid_credentials}
        end
    end
  end

  defp maybe_rehash(user, password) do
    if Password.needs_rehash?(user.hashed_password) do
      user
      |> Ecto.Changeset.change(hashed_password: Password.hash(password))
      |> Repo.update()
    end
  end
end

Architecture

Argon2 sits at the security foundation layer of the platform's authentication architecture. It is the last line of defense protecting user credentials in the event of a database compromise.

LayerComponentProtection
TransportSSL/TLSEncryption in transit
ApplicationRate LimitingBrute-force prevention
SessionJOSE/JWTStateless authentication tokens
CredentialArgon2idPassword hashing at rest
StoragePostgreSQLEncrypted storage backend

The platform's defense-in-depth strategy means Argon2 is never the only protection mechanism, but it is the critical one that protects credentials even when all other layers have been compromised. The authentication flow ensures that plaintext passwords exist in memory only for the duration of the hash comparison and are never logged, cached, or transmitted after initial receipt.

Performance Characteristics

Argon2's deliberate computational cost is tuned to balance user experience with security. The platform's parameters target specific performance characteristics on production hardware.

ParameterValueEffect
t_cost (iterations)3CPU time per hash
m_cost (memory)16 (64MB)Memory per hash operation
parallelism4Threads per hash
Hash time~500msDeliberate slowness for security
Salt length16 bytesAutomatic random generation
Output length32 bytesHash digest size

The 500ms target means that even a modest password database of 100,000 users would require approximately 14 hours of continuous single-threaded computation to attempt one password per account. With the memory requirement, an attacker cannot simply parallelize across thousands of GPU cores as each hash requires 64MB of dedicated memory.

Configuration

Argon2 is configured through the application environment with parameters that can be adjusted per deployment environment. Test environments use reduced parameters for faster test execution.

# config/config.exs - Production parameters
config :argon2_elixir,
  t_cost: 3,
  m_cost: 16,
  parallelism: 4,
  argon2_type: 2  # Argon2id

# config/test.exs - Reduced parameters for test speed
config :argon2_elixir,
  t_cost: 1,
  m_cost: 10,      # 1MB instead of 64MB
  parallelism: 1,
  argon2_type: 2

Best Practices

The platform enforces strict security practices for password handling that extend beyond the hashing algorithm itself.

  • Always use Argon2id -- never Argon2i or Argon2d in isolation; the hybrid variant provides the strongest security profile
  • Call no_user_verify/0 when a username is not found to prevent timing-based user enumeration attacks
  • Implement automatic rehashing -- check needs_rehash?/1 on every successful login to transparently upgrade parameters
  • Use reduced parameters in test environments -- full production parameters make test suites unacceptably slow
  • Never log passwords -- ensure no logging middleware captures request bodies containing credentials
  • Monitor hash times -- if production hash times drop significantly below target, parameters should be increased
  • Store full hash strings -- Argon2's output includes algorithm, version, parameters, salt, and hash; store the complete string
  • Review parameters annually -- as hardware improves, increase t_cost and m_cost to maintain the target hash time

Comparison

The platform chose Argon2id over other password hashing algorithms based on a comprehensive security analysis aligned with OWASP and NIST recommendations.

CriterionArgon2idbcryptscryptPBKDF2-SHA256
Year introduced2015199920092000
Memory usage per hash64MB (configurable)4KB (fixed)ConfigurableNegligible
GPU resistanceExcellentGoodGoodPoor
ASIC resistanceExcellentModerateGoodPoor
Side-channel resistanceExcellent (hybrid)GoodPoorGood
Parameter flexibility3 independent knobs1 (cost factor)3 knobs2 knobs
OWASP recommendationPrimary choiceAcceptableAcceptableAcceptable
Elixir library maturityMature (argon2_elixir)Mature (bcrypt_elixir)LimitedBuilt-in (Plug.Crypto)
  • PostgreSQL - Credential storage backend where hashed passwords are persisted
  • Phoenix Framework - Web framework providing the authentication endpoints
  • Elixir - Runtime environment through the argon2_elixir NIF binding
  • BEAM - Virtual machine that manages the NIF thread pool for Argon2 operations
  • Docker - Container environment where Argon2 NIF libraries must be compiled

Connect & Contribute

Created by Tomáš Korcak (korczis) | Open Source under GHL