Why memory matters
When an attacker steals password hashes or an encrypted vault, they guess offline, as fast as their hardware goes. A GPU has thousands of small cores, so a function that needs little memory, like PBKDF2, runs on many of them at once.
Argon2 needs a block of memory for each guess, and it cannot be skipped cheaply. Memory is what is scarce on a GPU. As plain arithmetic, not a benchmark: a card with 24 GiB fits about 96 guesses at once at 256 MiB each, and about 1,290 at 19 MiB. PBKDF2 sets no such limit.
There are three variants. Argon2d resists GPUs best, but its memory access depends on the password, which can leak through side channels. Argon2i avoids that, at some cost in GPU resistance. Argon2id starts as Argon2i for the first half of the first pass, then works as Argon2d. RFC 9106 recommends it as the default.
The parameters
| Name | Meaning | Effect |
|---|---|---|
m | Memory, in KiB | The main defense. Raise this first. |
t | Passes over the memory | More time for each guess. |
p | Lanes, for parallel threads | Lets a device with more cores fill the memory faster. It does not add memory. |
| Salt | Random value for each password | Same password, different hash. Not secret, stored in the clear. 16 bytes is enough for all uses, says the RFC. |
| Tag length | Size of the output | 16 bytes is enough for most uses. 32 is common when it becomes a key. |
What values to use
| Source | m | t | p | Time on our Mac |
|---|---|---|---|---|
| RFC 9106, first recommended | 2 GiB | 1 | 4 | 1.43 s |
| RFC 9106, second recommended, for less memory | 64 MiB | 3 | 4 | 0.11 s |
| OWASP, minimum in its summary | 19 MiB | 2 | 1 | 0.02 s |
OWASP lists several settings it treats as equal in defense, such as 46 MiB with t=1, and 12 MiB with t=3. They are all minimums for a server that has to log in many users. An app that derives one key on your own device can afford much more, because a user waits for it only once per unlock.
A good way to choose: pick the most memory every device you support can spare, keep t at 3 or below, and check the time on your slowest device. Remember that if a phone runs out of memory, the unlock fails.
Try it
The argon2 command line tool is brew install argon2 on macOS, or sudo apt install argon2. Memory is given in KiB with -k. The salt must be at least 8 characters. Do not use real passwords like this, since they can stay in shell history:
$ printf 'correct horse battery staple' | argon2 examplesalt12345 -id -t 3 -k 262144 -p 1 -l 32
Type: Argon2id
Iterations: 3
Memory: 262144 KiB
Parallelism: 1
Hash: e2c39f45f62dd7bddc5b6eff815f32fae69748a05f4c6f9875dd0e06bb9b2934
Encoded: $argon2id$v=19$m=262144,t=3,p=1$ZXhhbXBsZXNhbHQxMjM0NQ$4sOfRfYt173cW27/gV8y+uaXSKBfTG+Ydd0OBrubKTQ
0.356 seconds
Verification ok
The encoded string holds all that is needed to check a password later: the variant, the version (v=19 is 1.3), the parameters, the salt and the hash, the last two in Base64. So you can raise the parameters later, and old hashes still verify.
In Python, with argon2-cffi in a virtual environment:
from argon2 import PasswordHasher, Type
ph = PasswordHasher(time_cost=3, memory_cost=262144, parallelism=1,
hash_len=32, salt_len=16, type=Type.ID)
stored = ph.hash("correct horse battery staple")
ph.verify(stored, "correct horse battery staple") # True; a wrong password raises VerifyMismatchError
ph.check_needs_rehash(stored) # False until you raise the parameters
It creates a random salt for you. Do not write your own salt handling.
Argon2id or PBKDF2
PBKDF2 repeats a hash many times and needs almost no memory. OWASP asks for 600,000 iterations with HMAC-SHA256, which took 0.04 seconds on our Mac. That is a fine wait for you, but every guess is cheap to run in parallel. Use PBKDF2 when a standard such as FIPS requires it, and Argon2id otherwise.
A weak password still falls
Argon2id makes each guess slower. It does not make a bad password good. Attackers try common passwords first, so a password near the top of their list needs very few guesses.
How a vault combines a password, Argon2id and a separate key is covered in what end-to-end encryption means for a password vault.
Questions
Which Argon2 variant should I use?
Argon2id. RFC 9106 recommends it as the default, because it resists both GPU guessing and side channel attacks.
What does $argon2id$v=19$m=65536,t=3,p=4 mean?
Argon2id, version 1.3, 65536 KiB (64 MiB) of memory, 3 passes and 4 lanes. The salt and the hash follow in Base64.
Is Argon2id better than bcrypt?
For new systems, yes. OWASP lists Argon2id first and keeps bcrypt for legacy systems. bcrypt uses a small, fixed amount of memory, and most implementations read only the first 72 bytes of a password.
Sources
- RFC 9106: Argon2 Memory-Hard Function
- OWASP Password Storage Cheat Sheet
- RFC 8018: PKCS #5, including PBKDF2
- argon2-cffi documentation