Argon2 won the Password Hashing Competition in 2015 and is the current OWASP-recommended password hashing algorithm — but it has three variants (Argon2i, Argon2d, Argon2id) with different security properties, and using the wrong variant for the wrong context provides weaker-than-expected protection
The previous articles on this site covered bcrypt basics, bcrypt history vs Argon2, credential breaches, bcrypt's 72-byte limit, and work factor selection and upgrade strategy. This article addresses Argon2 in depth — its three variants, its memory-hardness property, and the practical decision of when to switch from bcrypt to Argon2.
Why memory-hardness matters: GPU password cracking
GPU-based password cracking has dramatically changed the threat landscape for password hashing. A modern GPU has thousands of parallel execution cores that can try millions of hash computations per second for fast hash functions.
bcrypt's resistance: bcrypt's work factor creates sequential computation that takes a fixed time per hash — but this time is the same whether computed on a CPU or GPU. A GPU can parallelize bcrypt across thousands of cores, each running at the same speed as a CPU. The parallelism advantage is somewhat mitigated by bcrypt's memory requirements (the bcrypt algorithm uses an 4KB memory table), but not eliminated.
The memory-hardness concept: a memory-hard function requires both time and a significant amount of memory per computation. GPUs have many cores but limited memory per core — if a hash function requires, say, 64 MB of memory per computation, a GPU with 8 GB of total memory can only run approximately 128 computations in parallel (8,000 MB ÷ 64 MB). Memory-hard functions convert the GPU's parallelism advantage into a memory bottleneck.
Argon2's memory parameter: unlike bcrypt (where work factor controls only time), Argon2 takes three parameters:
memory_cost: how many KiB of memory are required per hash computationtime_cost: how many iterationsparallelism: how many parallel threads can be used
Setting memory_cost=65536 (64 MB) makes GPU cracking approximately as expensive as CPU cracking by eliminating the memory parallelism advantage.
The three Argon2 variants
Argon2d: maximum resistance to GPU cracking attacks through data-dependent memory access patterns. Not suitable for password hashing where an attacker has access to the system while it's running — data-dependent patterns are vulnerable to side-channel attacks (timing attacks that leak information about the password based on memory access patterns).
Argon2i: data-independent memory access patterns — resistant to side-channel attacks, but slightly more vulnerable to time-memory trade-off (TMTO) attacks than Argon2d. Suitable for: scenarios where side-channel resistance is more important than TMTO resistance (key derivation, disk encryption key derivation).
Argon2id: a hybrid that uses Argon2i in the first half of the memory filling and Argon2d in the second half. Provides both side-channel resistance (from the first pass) and TMTO resistance (from the second pass). The recommended default for password hashing.
OWASP recommendation (2024):
Argon2id with:
memory_cost = 19 MiB (19456 KiB) minimum
time_cost = 2 iterations minimum
parallelism = 1
This produces hashes that take approximately 200-500ms on typical hardware, with 19 MB of memory required — making GPU parallel cracking significantly harder than bcrypt.
Bcrypt vs Argon2id: migration decision framework
When to stick with bcrypt:
- You have an existing bcrypt implementation working correctly at an appropriate work factor
- Your threat model doesn't include GPU-based offline cracking (your data isn't high-value enough to warrant targeted GPU cracking attempts)
- Your team isn't prepared to implement Argon2 correctly (correct parameter selection, proper library usage)
- Legacy platform constraints (Argon2 is not available in the version of PHP/Python/Node you're running)
When to migrate to Argon2id:
- Building a new authentication system from scratch
- Your application handles high-value credentials (financial, healthcare, government)
- You have reason to believe your password database could be targeted with dedicated cracking hardware
- Your platform supports Argon2 well (Python's
argon2-cffi, Node.js'sargon2package, PHP'spassword_hashwithPASSWORD_ARGON2ID)
The migration approach (same as bcrypt work factor upgrade): opportunistic re-hashing on successful login. When a user authenticates, verify against the current hash, and if it's a bcrypt hash, re-hash with Argon2id and update the stored hash.
Scrypt: the third memory-hard alternative
scrypt (designed by Colin Percival, 2009) is another memory-hard password hashing function that predates Argon2. It uses the same general principle — requiring large amounts of memory — with parameters:
N: CPU/memory cost parameter (must be a power of 2)r: block size parameterp: parallelization parameter
scrypt vs Argon2id: Argon2 won the PHC because it was considered more thoroughly analyzed, simpler to parameterize correctly, and more flexible. scrypt has known issues with the interaction between N, r, and p parameters that can lead to suboptimal security if not carefully chosen. For new implementations, Argon2id is generally preferred over scrypt.
The pepper pattern: adding a server secret to password hashing
Bcrypt and Argon2 are designed to resist offline cracking if the hash database is stolen. An additional technique — the "pepper" — adds a server-side secret to the password before hashing:
stored_hash = argon2id(password + pepper, salt)
The pepper is a secret value stored in application configuration (not the database). If an attacker steals the password database but not the server configuration, even if they brute-force the hashes they need the pepper to verify guesses.
Important distinction from salt: the salt is unique per user and stored with the hash; the pepper is shared across all hashes and stored separately from the database. Losing the pepper (without the database) renders the hashes unverifiable; losing the database (without the pepper) means the attacker can't crack hashes without the pepper.
HMAC-based pepper: a more robust implementation uses HMAC with the pepper as the key: stored_hash = argon2id(HMAC(password, pepper), salt) — providing cryptographic binding between the password and pepper rather than simple concatenation.
How to use the Bcrypt Generator on sadiqbd.com
- For testing bcrypt parameters: generate hashes at different work factors to empirically measure the time cost on representative hardware before setting production work factors
- For learning Argon2 migration: the tool illustrates the hash format and work factor concept; when migrating to Argon2id, libraries in your target language handle the actual implementation
- For verifying stored hashes: paste a bcrypt hash and a candidate password to verify whether they match — useful for debugging authentication flows or verifying that a hash was generated correctly
Frequently Asked Questions
Should I use Argon2id even if bcrypt is "good enough" for my use case? "Good enough" depends on your threat model. For applications where password databases are low-value targets (a personal blog, a hobby project, a non-sensitive application), bcrypt at work factor 12+ is excellent protection and the implementation risk of switching algorithms is not justified. For applications handling sensitive credentials, Argon2id provides meaningfully better protection against dedicated GPU cracking attempts and represents current best practice. The practical answer: if you're building new and your platform supports it easily, default to Argon2id; if you have existing bcrypt, only migrate if there's a specific reason to believe GPU cracking is in your threat model.
Is the Bcrypt Generator free? Yes — completely free, no sign-up required.
Try the Bcrypt Generator free at sadiqbd.com — generate and verify bcrypt hashes with configurable work factors.