Choosing a key derivation function is usually presented as a strength contest, with Argon2 winning. The comparison is more interesting than that, and the reason Argon2 is better is not the reason most articles give.
This one is written from the position of a product that still runs PBKDF2, and it covers why, what that costs, and what would have to change.
Summary
Both functions make each password guess expensive. PBKDF2 charges an attacker processor time only, which is the dimension GPUs and ASICs are best at making cheap. Argon2 also charges memory, which specialised hardware finds genuinely expensive. Argon2id is the better choice where you can use it; PBKDF2 remains defensible where platform constraints rule Argon2 out, provided the iteration count is current.
What a key derivation function is for
A password is not a key. It is short, drawn from a predictable distribution, and frequently reused. A key derivation function turns one into the other and does two distinct jobs while it is at it.
The first is mechanical: produce a fixed-length, uniformly distributed key from an arbitrary input. The second is adversarial, and it is the one that matters: make computing that output deliberately expensive, so an attacker who obtains your stored data cannot test candidate passwords at speed.
That second job is why "use SHA-256" is wrong. SHA-256 is designed to be fast, which is a virtue when hashing a file and a catastrophe when hashing a password — fast means an attacker tests billions of candidates per second.
The real difference: what you are charging for
PBKDF2 has a single cost dial. Raising the iteration count means more HMAC operations per guess, so each guess takes longer. The work is pure arithmetic with a tiny working set.
That is precisely the workload specialised hardware is built for. A GPU runs thousands of independent arithmetic streams concurrently; an ASIC does the same thing with the operation baked into silicon. The defender's cost of raising iterations is linear and paid on every legitimate login. The attacker's cost is amortised across parallel lanes that are close to free to add.
Argon2, specified in RFC 9106, charges for memory as well. A guess requires filling and repeatedly accessing a large block of RAM — commonly tens of megabytes. Memory is the resource parallel hardware cannot conjure cheaply: a thousand concurrent guesses need a thousand times the RAM, and that is a physical cost that does not fall the way transistor counts do.
That asymmetry, not a difference in cryptographic strength, is the argument for Argon2.
The three Argon2 variants
| Variant | Access pattern | Use it for |
|---|---|---|
| Argon2d | Data-dependent | Maximum GPU resistance where side channels are not a threat |
| Argon2i | Data-independent | Side-channel resistance, at some cost in GPU resistance |
| Argon2id | Hybrid — one pass independent, the rest dependent | Password hashing; this is the default answer |
Data-dependent access means the memory addresses touched depend on the password itself, which resists parallel hardware well but can leak through cache-timing side channels on a shared machine. Argon2id takes the first pass data-independently and the remainder data-dependently, getting most of both properties. RFC 9106 recommends it as the default, and unless you have a specific reason to deviate, it is the one to pick.
Parameters, and the honest version of "recommended"
Any number published today is a snapshot. RFC 9106 gives two starting configurations for Argon2id — 2 GiB of memory with one iteration, or 64 MiB with three — and both assume server-side hashing with hardware you control.
For PBKDF2-HMAC-SHA256, the [OWASP Password Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/PasswordStorageCheat_Sheet.html) currently gives 600,000 iterations. It is worth reading that number carefully rather than treating it as a pass mark: OWASP's guidance is explicitly that PBKDF2 should be used when Argon2id and scrypt are not available, and the iteration count is the compensating control, not an equivalent.
The right way to set either is not to copy a number. Measure on the slowest device you must support, decide how long a legitimate user may reasonably wait, and set the parameter to consume that budget. Then re-measure annually, because the budget buys less every year.
Why this site still runs PBKDF2
Inkrypt derives keys with PBKDF2-HMAC-SHA256 at 310,000 iterations, in the browser, using the Web Crypto API. Two facts about that are worth stating plainly.
The first is that this is a compromise. Argon2id would be the better choice on the merits, for exactly the memory-hardness reason above.
The second is the constraint. The Web Crypto API exposes PBKDF2 natively; it does not expose Argon2. Using Argon2 in a browser means shipping a WebAssembly implementation, which means the key derivation for every note depends on a third-party binary delivered over the network. That is a meaningful change to the trust model of a tool whose entire claim is that the browser does the work with primitives the browser provides. It also costs a download before the first note can be saved.
Our iteration count also sits below OWASP's current 600,000, which is a genuine gap rather than a rounding difference — it reflects the browser-side budget on older mobile hardware, where derivation is already a perceptible pause. Both of these are the sort of thing our editorial policy requires us to publish rather than omit, and both are on the list to revisit.
What would change the decision: a native Argon2 binding in the Web Crypto specification, or a WebAssembly implementation with a review history strong enough that depending on it is not a downgrade.
When PBKDF2 is still the right answer
Not every use of it is a compromise.
FIPS-validated environments. PBKDF2 is specified in NIST SP 800-132 and available in validated cryptographic modules. Argon2 generally is not. If validation is a requirement, the decision is made for you.
Constrained devices. Argon2's memory appetite is the point, and on an embedded device with a few hundred kilobytes of RAM it is simply not available.
Platform primitives only. As above: where the platform ships PBKDF2 and adding Argon2 means adding a dependency you cannot properly review, the dependency may be the larger risk.
Existing deployments. A correctly parameterised PBKDF2 deployment is not an emergency. Raise the iteration count, plan the migration, and do it on rehash-at-next-login rather than in a panic.
What neither function does
This is the part most comparisons leave out, and it matters more than the choice between them.
Neither adds entropy. A KDF multiplies the cost of each guess. If the password is in the top ten thousand, an attacker does not need many guesses, and no iteration count rescues it. Password strength and derivation cost are independent variables, and only one of them is under the user's control.
Neither helps if the key is stored. All of this protects a password that is not kept. A system that derives a key and then caches it somewhere recoverable has moved the problem, not solved it.
Neither defends against interception before derivation. A keylogger, a malicious browser extension or a compromised script reads the password as it is typed. That is the code delivery problem, and it sits upstream of every choice discussed here.
A short decision path
- Server-side, no validation constraint → Argon2id, RFC 9106 parameters, tuned to your hardware.
- Server-side, FIPS required → PBKDF2-HMAC-SHA256, OWASP's current iteration count, re-checked annually.
- Browser-side, platform primitives only → PBKDF2 via Web Crypto, highest count your slowest supported device tolerates, and be honest in your documentation about what that costs.
- Constrained embedded device → PBKDF2, tuned to the hardware.
The worst outcome is not choosing the second-best function. It is choosing an excellent one and then leaving the parameters at whatever a tutorial suggested in 2015.
For the wider question of how to evaluate the tools that make these claims about themselves, how to judge an encrypted note service sets out what to ask and what the answers mean. Our security architecture page lists the parameters this site runs, read from the same constant the code reads.
FAQ: PBKDF2 and Argon2
Q1. Is Argon2 stronger than PBKDF2?
Not in the sense of harder mathematics — both are built on well-studied primitives. Argon2 is better because it charges an attacker memory as well as time, and memory is the cost that specialised cracking hardware cannot collapse cheaply. A correctly parameterised PBKDF2 deployment is not broken; it is defending on one axis instead of three.
Q2. Which Argon2 variant should I use?
Argon2id, unless you have a specific documented reason to choose otherwise. RFC 9106 recommends it as the default for password hashing because it takes the first pass data-independently, which limits cache-timing side channels, and the remaining passes data-dependently, which preserves most of the hardware resistance.
Q3. How many PBKDF2 iterations do I need?
The OWASP Password Storage Cheat Sheet currently gives 600,000 for PBKDF2-HMAC-SHA256, but treat any published figure as a snapshot. Measure on the slowest device you must support, decide what delay a legitimate user will tolerate, and spend that budget. Then re-check annually, because the same number buys less each year.
Q4. Can I use Argon2 in a browser?
Not natively. The Web Crypto API exposes PBKDF2 but not Argon2, so browser-side Argon2 means shipping a WebAssembly implementation and accepting a third-party binary into the path that derives your keys. Whether that trade is worth making depends on how well you can review the binary.
Q5. Is bcrypt or scrypt a reasonable middle ground?
Yes. scrypt is memory-hard and is listed alongside Argon2id in OWASP's guidance. bcrypt has a small fixed memory requirement and a long deployment history, and it caps input length at 72 bytes, which matters if you pre-hash. Both are better than unsalted fast hashing and neither is a reason to delay moving to Argon2id where you can.
Q6. Do I need to re-hash existing passwords if I change parameters?
You cannot re-derive without the password, so the standard approach is to upgrade opportunistically: when a user next authenticates successfully, derive again with the new parameters and replace the stored record. Store the parameters alongside each record so old and new can coexist during the migration.
Where to go next
- How the Web Crypto API works in the browser — the four calls this derivation sits inside, and the mistakes that survive review.
- What is a salt, and why does every record need its own? — the input that makes derivation cost scale with the number of records rather than staying flat.
- How to judge an encrypted note service — what to ask a vendor about their parameters, and what the answers tell you.
- Inkrypt's security architecture — the parameters this site actually runs, read from the same constant the code reads.
Primary sources for the claims above: RFC 9106 for Argon2, RFC 8018 for PBKDF2, NIST SP 800-132 for password-based key derivation, and the [OWASP Password Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/PasswordStorageCheat_Sheet.html) for current parameter guidance.