Argon2id explained: what m, t and p mean and what to choose

Argon2id is a password hashing function that forces every guess to use a set amount of memory, which makes large-scale guessing on GPUs expensive. You set memory (m), passes (t) and lanes (p). RFC 9106 recommends m=2 GiB, t=1, p=4, or m=64 MiB, t=3, p=4 with less memory. OWASP's minimum is m=19 MiB, t=2, p=1. A weak password still falls.

Updated . argon2 command line tool and argon2-cffi 25.1.0 in a Python venv, on one Apple M6 Mac.

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

NameMeaningEffect
mMemory, in KiBThe main defense. Raise this first.
tPasses over the memoryMore time for each guess.
pLanes, for parallel threadsLets a device with more cores fill the memory faster. It does not add memory.
SaltRandom value for each passwordSame password, different hash. Not secret, stored in the clear. 16 bytes is enough for all uses, says the RFC.
Tag lengthSize of the output16 bytes is enough for most uses. 32 is common when it becomes a key.

What values to use

SourcemtpTime on our Mac
RFC 9106, first recommended2 GiB141.43 s
RFC 9106, second recommended, for less memory64 MiB340.11 s
OWASP, minimum in its summary19 MiB210.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

All guides