Key stretching
Salt made every stored hash unique, so an attacker can no longer crack a million accounts with one lookup. But they can still crack them one at a time — and a hash function is built to be fast, which means guessing is fast too. The answer is deliberately, expensively slow hashing.
Salt buys uniqueness, not time
A salt forces an attacker to attack each account separately. It does nothing to make each attempt harder. Hash functions like SHA-256 were designed to be quick — that is exactly what you want when fingerprinting a file, and exactly what you don’t want when someone is guessing passwords. A single modern graphics card can compute billions of SHA-256 hashes per second, and rented in bulk they run through every common password, every dictionary word and every predictable substitution long before you have read the breach notification email.
Make it slow on purpose
Key stretching flips that asymmetry. Instead of hashing the password once, you hash it, then hash the result, then hash that — hundreds of thousands of times over — and only store the final output. Logging in legitimately costs you one run of that loop: a fraction of a second, and you notice nothing. An attacker pays the same cost on every single guess. Multiply a quarter of a second by a dictionary of ten million candidates and a job that took an afternoon now takes decades.
That is the whole idea: you cannot stop someone guessing, but you can make each guess cost real time and real electricity, until the arithmetic no longer works in their favour.
PBKDF2
The best-known way to do this is PBKDF2 — the Password-Based Key Derivation Function. You hand it four things: the password, the salt, an iteration count, and how many bytes of output you want. It returns a key derived from your password, and it takes as long to do it as you told it to.
Inside, it runs a keyed hash over and over, chaining each round into the next and mixing every round’s output into the running result. Two details matter. Because each round depends on the one before, an attacker cannot skip ahead or jump straight to the answer — the work is genuinely sequential. And because you choose the iteration count, the same algorithm can be re-tuned as computers get faster: the original specification suggested a thousand rounds, while current guidance for the same function is in the hundreds of thousands.
The keyed hash at the centre of that loop is an HMAC — a hash combined with a secret, which is exactly what the next lesson is about.
Harder than slow: memory
PBKDF2 has a weakness. It is expensive in time but cheap in memory, and that is the kind of work specialised hardware is brilliant at — a GPU or an ASIC can run thousands of those loops side by side, buying back much of the advantage. So later designs made the work expensive in a resource that is genuinely hard to scale.
- bcrypt (1999) — built for password hashing from the start, with a cost factor you turn up as hardware improves.
- scrypt (2009) — deliberately memory-hard: it needs a large block of RAM as well as time, so running a thousand copies in parallel means buying a thousand times the memory.
- Argon2 (2015) — the winner of the Password Hashing Competition, with time, memory and parallelism all tunable independently. The current default recommendation for new systems.
The trend is one long argument with the attacker’s hardware: every time raw speed gets cheaper, the defenders move the cost onto something that doesn’t.
Where you meet it in Bitcoin
Your seed phrase uses this. The standard that turns twelve or twenty-four words into an actual wallet seed runs them through PBKDF2 — 2,048 rounds of HMAC-SHA512, with the word “mnemonic” plus your optional passphrase as the salt. That optional passphrase, sometimes called the twenty-fifth word, is why key stretching is there at all: the words themselves already carry proper randomness, but a human-chosen passphrase does not, and stretching is what protects the weak half.
It is also why 2,048 rounds is modest by password standards and still reasonable here — the security is mostly resting on the entropy in the words, not on the loop. Encrypted wallet files and hardware-wallet PINs lean on the same trick.
When a breach is announced, “the passwords were hashed” is not the whole story. Salted and stretched with a modern function, most of those passwords will never be recovered. Hashed once with a fast function and no salt, they are effectively public. Same word, wildly different outcome.
Stretching leans on a keyed hash run thousands of times over. That building block — a hash plus a shared secret, and the guarantee it buys you — is worth meeting properly. That’s next.
Key takeaways
- Salt makes each stored hash unique, but hash functions are fast — an attacker can still brute-force one account at a time at billions of guesses per second.
- Key stretching (PBKDF2, bcrypt, scrypt, Argon2) runs the hash many thousands of times so every guess costs real time; Bitcoin's seed phrases use PBKDF2 with 2,048 rounds of HMAC-SHA512.
Check yourself
Salt already makes every stored hash unique. Why stretch as well?
A salt forces an attacker to attack accounts one at a time; it doesn't slow down any single attempt. Stretching does.
What does PBKDF2's iteration count control?
The count is the tuning knob: raise it as hardware gets faster, and every attacker guess gets proportionally more expensive.
Why do scrypt and Argon2 deliberately use a lot of memory?
PBKDF2 is time-hard but memory-cheap, which suits GPUs and ASICs. Memory-hard designs move the cost onto a resource that doesn't scale cheaply.
Spotted an error or have feedback on this lesson? Suggest a correction ↗
Comments
Powered by Nostr — reply from any Nostr client, and zap the lesson over Lightning.