L33 · Password Hashing: Salt, Slow Hashes & Key Derivation

CS 161, Lesson 33, in 51 slides. It explains why storing H(w) is not enough, in section 14.7, then covers the offline guessing attack and the amortized precomputation attack, and the two defenses given in section 14.8: a per-user salt, and slow iterated hashing. It closes with what this implies for key derivation, in section 14.9. It is anchored to textbook sections 14.7 to 14.9.

Subject: Computer Security · 82 slides · applied lesson

Open the interactive version of this deck · Homework for this lesson

What this lesson covers

The lesson, slide by slide

1. Hashing Passwords

Title

CS 161 · Lesson 33 of 45

why storing H(w) isn't enough · offline & precomputation attacks · salt · slow iterated hashing · and key derivation

2. By the end of this lesson you can…

Objectives

  1. Explain why a server stores H(w) instead of a cleartext password, and why a fast hash alone is not enough.
  2. Describe the offline guessing attack and estimate its cost — billions of fast hashes per second on a stolen database.
  3. Describe the amortized / precomputation attack (rainbow tables) and why unsalted hashes let one table crack everyone.
  4. Apply the two fixes: a per-user salt to defeat precomputation, and a slow iterated hash to defeat fast offline guessing.
  5. Extend the idea to key derivation — why k = SHA-256(password) is weak and you need a slow KDF (PBKDF2 / scrypt / Argon2).

3. What survived from L32 · Password Risks & Mitigations?

Warm-up

Discussion prompt

Before we open L33 · Password Hashing: Salt, Slow Hashes & Key Derivation: without looking back, what was the main idea of L32 · Password Risks & Mitigations, and what could you do by the end of it that you could not do before?

Hint: One sentence for the idea, one for the skill. If the second one is blank, that is the part to revisit.

Answer:

CS 161, Lesson 32, in 54 slides. It covers the five password risk vectors in section 14.1, eavesdropping and SSL/TLS in section 14.2, client-side malware and two-factor authentication in section 14.3, and the statistics of online guessing in section 14.4. It then covers the mitigations - rate limiting, CAPTCHAs, and password requirements - in section 14.5, and server compromise and why you must never store cleartext, in section 14.6. It is anchored to textbook sections 14.1 to 14.6.

4. Three questions this lesson answers

Concept

Last lesson (L32) warned that a server compromise leaks whatever the server stores. So how should a server store passwords so that a stolen database doesn't hand the attacker everyone's password?

Why not store H(w)?
§14.7 a fast hash falls to offline + precomputation guessing
What are the fixes?
§14.8 a per-user salt, plus a slow iterated hash
What about keys?
§14.9 the same idea drives password-based key derivation

5. Which is which: Three questions this lesson answers

Matching

Match the pairs

From Three questions this lesson answers — match each one to what it actually does. The descriptions have been shuffled.

  • c1. Why not store H(w)?
  • c2. What are the fixes?
  • c3. What about keys?
  • b1. §14.7 a fast hash falls to offline + precomputation guessing
  • b2. §14.8 a per-user salt, plus a slow iterated hash
  • b3. §14.9 the same idea drives password-based key derivation

Why: Why not store H(w)?, What are the fixes?, What about keys? are easy to tell apart while they are sitting next to their descriptions and much harder afterwards, which is what this checks.

6. Storing H(w) — the First Idea

Section

Part 1 · §14.7 hash, don't store cleartext

7. §14.7 Don't store cleartext passwords

Concept

Scenario first: a web service must check the password a user types at login. The naive design stores each user's password w in cleartext — and L32 showed why that's a disaster: one database breach leaks every password directly.

The first improvement: never store w. Instead store its hash H(w) (e.g. SHA-256). The server keeps the hashes; it never keeps the passwords themselves.

8. Break it if you can: §14.7 Don't store cleartext passwords

Counterexample

Discussion prompt

The first improvement: never store w. Instead store its hash H(w) (e.g. SHA-256). The server keeps the hashes; it never keeps the passwords themselves.

That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.

Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.

9. §14.7 Store the hash, verify on login

Concept

At registration the server computes and stores H(w). At login it hashes the entered password w′ and compares to the stored value — equal hashes mean the right password.

\[ \text{store } H(w); \qquad \text{on login, accept} \iff H(w') = H(w) \]

Hashes are one-way, so a stolen database of H(w) values doesn't directly reveal any password. This sounds like it solves the problem — but it doesn't.

10. By analogy: §14.7 Store the hash, verify on login

Analogy

Discussion prompt

Explain §14.7 Store the hash, verify on login by analogy to something with no Computer Security in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.

Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.

Answer:

At registration the server computes and stores H(w). At login it hashes the entered password w′ and compares to the stored value — equal hashes mean the right password.

11. §14.7 One-way ≠ unguessable

Intuition

A one-way hash can't be run backward — but the attacker doesn't need to. Real passwords aren't random: a huge fraction are drawn from a small, predictable pool ('password123', a pet's name, a dictionary word).

So the attacker doesn't invert H. She guesses forward: hash a likely password, see if it matches a stored value. The weakness isn't the hash — it's that passwords are guessable and the hash is fast.

Ask yourself: what makes guessing forward cheap? (A fast hash + a small space of likely passwords. The next two attacks exploit exactly that.)

12. Teach it back: §14.7 One-way ≠ unguessable

Explain it

Discussion prompt

Explain §14.7 One-way ≠ unguessable to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.

Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.

Answer:

Ask yourself: what makes guessing forward cheap? (A fast hash + a small space of likely passwords. The next two attacks exploit exactly that.)

13. §14.7 Online vs offline guessing

Concept

There are two settings for a guessing attack. Online: the attacker submits guesses to the live login form — slow, and easily throttled by rate limits and lockouts. The server is the bottleneck.

Offline: the attacker has stolen the hash database and tests guesses on her OWN hardware. No server, no throttle. This lesson is about defending against the offline case — the one that survives a breach.

14. Something is wrong here: 'a fast hash like SHA-256 makes stolen passwords safe'

Anomaly

Predict first

A student writes this, and it looks reasonable:

A student: 'SHA-256 is one-way, so once I store H(w) a stolen database is useless to the attacker.'

It is wrong. Say what breaks — and say it before you turn the page.

Correct: One-way means the attacker can't INVERT H — but she can GUESS forward.

A student: what does storing H(w) actually protect against, and what does it not?

Why: One-way means the attacker can't INVERT H — but she can GUESS forward. A fast hash lets her test billions of candidate passwords per second against the stolen hashes. Speed is the enemy here.

15. Trap: 'a fast hash like SHA-256 makes stolen passwords safe'

Trap

The trap

A student: 'SHA-256 is one-way, so once I store H(w) a stolen database is useless to the attacker.'

Conclude that storing a fast cryptographic hash is sufficient

Why: Wrong. One-way means the attacker can't INVERT H — but she can GUESS forward. A fast hash lets her test billions of candidate passwords per second against the stolen hashes. Speed is the enemy here.

The fix

A student: what does storing H(w) actually protect against, and what does it not?

Recognize H(w) blocks DIRECT reading, not guessing

Why: §14.7: storing H(w) stops the attacker from reading passwords straight off disk, but a fast hash invites offline and precomputation guessing attacks. We need salt AND a slow hash on top.

16. Attack 1 — Offline Guessing

Section

Part 2 · §14.7 testing guesses locally

17. §14.7 Mallory guesses locally, not at the server

Concept

Mallory has stolen the database of stored hashes. She no longer needs to talk to the website at all. For each candidate guess g, she computes H(g) on her own machine and checks whether it matches any stored hash.

\[ \text{for each guess } g:\quad \text{compute } H(g),\ \text{check } H(g) \stackrel{?}{=} H(w) \]

Offline guessing attack — An attack run entirely on the attacker's own hardware against a stolen hash database. There is no rate limit, no lockout, and no server interaction — the only cost is the time to hash each guess.

18. §14.7 Why passwords are guessable at all

Intuition

If every password were a uniformly random 256-bit string, guessing would be hopeless. But humans pick MEMORABLE passwords, so the real distribution is wildly skewed toward a tiny set of likely choices.

Studies and leaked databases show a large fraction of users pick from the same few million strings. That's why testing only 2²⁰ ≈ a million guesses per user already finds about half of all passwords.

Ask yourself: why does the attacker bother with common passwords first? (Because the password distribution is concentrated — a small ordered guess list covers most users for minimal work.)

19. What rests on this: §14.7 Why passwords are guessable at all

Socratic

Discussion prompt

Studies and leaked databases show a large fraction of users pick from the same few million strings. That's why testing only 2²⁰ ≈ a million guesses per user already finds about half of all passwords.

Suppose that were not true. What is the first thing in L33 · Password Hashing: Salt, Slow Hashes & Key Derivation that would stop working?

Hint: Follow it one step downstream. The answer is whatever was quietly relying on it.

Answer:

Ask yourself: why does the attacker bother with common passwords first? (Because the password distribution is concentrated — a small ordered guess list covers most users for minimal work.)

20. §14.7 Fast hashes hash a BILLION guesses a second

Concept

SHA-256 was designed to be fast. On modern hardware an attacker can compute on the order of one billion (10⁹) hashes per second — and far more with GPUs or ASICs.

\[ \text{rate} \approx 10^9 \ \text{hashes/second} = 1\ \text{billion/sec} \]

That speed is a feature for verifying file integrity — but a liability for passwords, where you WANT the attacker's per-guess cost to be high.

21. What has to happen first: §14.7 The cost of cracking 100 million users

Ranking

Put in order

Put the moves of §14.7 The cost of cracking 100 million users into the order they have to happen.

  1. Set up the textbook scenario: a site with 100 million users, testing 2²⁰ guesses per user
  2. Count total hashes: 100 million users × 2²⁰ guesses each
  3. Verify the order of magnitude: 10¹⁴ hashes ÷ 10⁹ hashes/sec ≈ 10⁵ seconds ≈ ~1 day

Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. The textbook's numbers: testing about 2²⁰ ≈ one million common guesses per user is enough to find roughly half of all passwords, since so many people pick from a small pool.

22. §14.7 The cost of cracking 100 million users

Worked example

Set up the textbook scenario: a site with 100 million users, testing 2²⁰ guesses per user

Why: The textbook's numbers: testing about 2²⁰ ≈ one million common guesses per user is enough to find roughly half of all passwords, since so many people pick from a small pool.

\[ \text{users} = 10^8, \qquad \text{guesses/user} = 2^{20} \approx 10^6 \]

Count total hashes: 100 million users × 2²⁰ guesses each

Why: Without salt this is the upper bound on work; the total is the product of users and per-user guesses.

quantityvalue≈
users10⁸100 million
guesses per user2²⁰≈ 10⁶
total hashes10⁸ × 2²⁰≈ 10¹⁴ (100 trillion)
hash rate10⁹ / sec1 billion / sec
time10¹⁴ / 10⁹≈ 10⁵ sec ≈ ~1 day

\[ \frac{10^8 \times 2^{20}}{10^9\ \text{hash/s}} \approx \frac{10^{14}}{10^9} = 10^{5}\ \text{s} \approx 1\ \text{day} \]

Verify the order of magnitude: 10¹⁴ hashes ÷ 10⁹ hashes/sec ≈ 10⁵ seconds ≈ ~1 day

Why: §14.7: 10⁵ seconds is about 27.8 hours — roughly a single day finds about half of 100 million users' passwords. A fast hash makes mass cracking cheap.

23. Fill in: value for §14.7 The cost of cracking 100 million users

Comparison

Comparison matrix

From §14.7 The cost of cracking 100 million users: refill the value column from what you know. The rest of the table is as it appeared.

quantityvalue≈
users10⁸100 million
guesses per user2²⁰≈ 10⁶
total hashes10⁸ × 2²⁰≈ 10¹⁴ (100 trillion)
hash rate10⁹ / sec1 billion / sec
time10¹⁴ / 10⁹≈ 10⁵ sec ≈ ~1 day

24. Something is wrong here: 'offline guessing has to talk to the server'

Anomaly

Predict first

A student writes this, and it looks reasonable:

A student: 'My login form locks out after 5 wrong tries, so guessing is rate-limited and slow.'

It is wrong. Say what breaks — and say it before you turn the page.

Correct: Lockouts only limit ONLINE guessing at the live login form.

A student: where does the offline attacker actually do the work?

Why: Lockouts only limit ONLINE guessing at the live login form. Once Mallory has the stolen hash database, she guesses OFFLINE on her own machine — no server, no rate limit, no lockout. That's the whole point of an offline attack.

25. Trap: 'offline guessing has to talk to the server'

Trap

The trap

A student: 'My login form locks out after 5 wrong tries, so guessing is rate-limited and slow.'

Assume lockouts and rate limits stop the guessing attack

Why: Wrong. Lockouts only limit ONLINE guessing at the live login form. Once Mallory has the stolen hash database, she guesses OFFLINE on her own machine — no server, no rate limit, no lockout. That's the whole point of an offline attack.

The fix

A student: where does the offline attacker actually do the work?

Realize the work happens entirely on the attacker's hardware

Why: §14.7: with the stolen hashes in hand, H(g) is computed locally and compared locally. The server never sees a single guess, so its rate limits and lockouts are irrelevant.

26. Attack 2 — Amortized Precomputation

Section

Part 3 · §14.7 one table cracks everyone

27. §14.7 Identical passwords give identical hashes

Concept

Here's a deeper problem: H(w) depends only on w, not on WHO chose it. So if Alice and Bob both pick 'password123', their stored hashes are byte-for-byte identical.

\[ w_A = w_B \ \Rightarrow\ H(w_A) = H(w_B) \]

That means Mallory's hashing work isn't tied to any one user — a hash she computes once can be matched against the WHOLE database at once.

28. §14.7 Precompute once, look up many

Concept

Mallory precomputes a table of (H(g), g) pairs for the 2²⁰ most common passwords, sorts it by hash, then binary-searches each user's stored hash in it.

\[ \text{build sorted } \{(H(g),\, g)\}; \quad \text{lookup cost} = \lg(2^{20}) = 20 \text{ comparisons} \]

Amortized (precomputation) attack — Do the expensive hashing ONCE, store the results, and reuse them against every user. The cost of hashing is amortized across the whole user base, so cracking the second, third, … millionth user is nearly free.

29. Take the definitions apart: Offline guessing attack vs Amortized (precomputatio…

Definition probe

Sort into buckets

Every line below is part of the definition of Offline guessing attack or of Amortized (precomputation) attack — one or the other, never both. Put each where it belongs.

Offline guessing attack
An attack run entirely on the attacker's own hardware against a stolen hash database.; There is no rate limit, no lockout, and no server interaction; the only cost is the time to hash each guess.
Amortized (precomputation) attack
Do the expensive hashing ONCE, store the results, and reuse them against every user.; The cost of hashing is amortized across the whole user base, so cracking the second, third, … millionth user is nearly free.
b1
An attack run entirely on the attacker's own hardware against a stolen hash database. There is no rate limit, no lockout, and no server interaction — the only cost is the time to hash each guess.
b2
Do the expensive hashing ONCE, store the results, and reuse them against every user. The cost of hashing is amortized across the whole user base, so cracking the second, third, … millionth user is nearly free.

30. §14.7 Rainbow tables: the time/memory trade-off

Concept

Storing every (H(g), g) pair for a huge guess list takes a lot of disk. Rainbow tables compress this with a chaining trick: store only the endpoints of long hash chains and regenerate the middle on demand.

The effect is the same threat: a one-time precomputation that then cracks many unsalted users cheaply. Crucially, a salt (Part 4) breaks rainbow tables exactly as it breaks plain precomputation — there's no shared work left to compress.

31. Where does each piece belong: L33 · Password Hashing: Salt, Slow Hashes &…

Sorting

Sort into buckets

These are the pieces of L33 · Password Hashing: Salt, Slow Hashes & Key Derivation, out of order. Put each one back under the part of the lesson it belongs to.

Storing H(w) — the First Idea
§14.7 Don't store cleartext passwords; §14.7 Store the hash, verify on login; §14.7 One-way ≠ unguessable
Attack 1 — Offline Guessing
§14.7 Mallory guesses locally, not at the server; §14.7 Why passwords are guessable at all; §14.7 Fast hashes hash a BILLION guesses a second
Attack 2 — Amortized Precomputation
§14.7 Identical passwords give identical hashes; §14.7 Precompute once, look up many; §14.7 Rainbow tables: the time/memory trade-off
s1
Storing H(w) — the First Idea is where L33 · Password Hashing: Salt, Slow Hashes & Key Derivation puts §14.7 Don't store cleartext passwords, §14.7 Store the hash, verify on login, §14.7 One-way ≠ unguessable. Knowing which part of the lesson a problem belongs to is most of knowing which method to reach for.
s2
Attack 1 — Offline Guessing is where L33 · Password Hashing: Salt, Slow Hashes & Key Derivation puts §14.7 Mallory guesses locally, not at the server, §14.7 Why passwords are guessable at all, §14.7 Fast hashes hash a BILLION guesses a second. Knowing which part of the lesson a problem belongs to is most of knowing which method to reach for.
s3
Attack 2 — Amortized Precomputation is where L33 · Password Hashing: Salt, Slow Hashes & Key Derivation puts §14.7 Identical passwords give identical hashes, §14.7 Precompute once, look up many, §14.7 Rainbow tables: the time/memory trade-off. Knowing which part of the lesson a problem belongs to is most of knowing which method to reach for.

32. §14.7 Why this is so much worse

Intuition

Per-user offline guessing redid the same hashes for every user. Precomputation hashes each common password once — about a millisecond of work for 2²⁰ guesses plus a quick sort — and then every user is just a fast lookup.

One precomputed table cracks all users who share a common password. This is the idea behind rainbow tables, which use a clever time/memory trade-off to shrink the stored table.

Ask yourself: what single fact makes this possible? (That H(w) doesn't depend on the user — identical passwords collide, so one table serves everyone.)

33. Plan first: §14.7 Precompute-and-lookup, counted

Step zero

Discussion prompt

§14.7 Precompute-and-lookup, counted — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.

Hint: It starts with: Precompute: hash the 2²⁰ common passwords once and sort by hash value

Answer:

  1. Precompute: hash the 2²⁰ common passwords once and sort by hash value
  2. Crack each user with a binary search: ~20 comparisons against the sorted table
  3. Verify: the amortized total is dramatically smaller than per-user guessing

34. §14.7 Precompute-and-lookup, counted

Worked example

Precompute: hash the 2²⁰ common passwords once and sort by hash value

Why: Hashing 2²⁰ ≈ 10⁶ guesses at 10⁹/sec takes about a millisecond; sorting 10⁶ entries is fast. This cost is paid ONCE for the entire attack.

phasecost (once)cost per user
per-user guessing—2²⁰ hashes each
precompute table2²⁰ hashes + sort—
lookup per user—lg(2²⁰) = 20 lookups

Crack each user with a binary search: ~20 comparisons against the sorted table

Why: lg(2²⁰) = 20, so finding a user's password (if common) costs about 20 lookups — no hashing at all per user.

\[ \text{total} \approx \underbrace{2^{20}}_{\text{hash once}} + \underbrace{10^8 \times 20}_{\text{lookups}} \ \ll\ \underbrace{10^8 \times 2^{20}}_{\text{per-user guessing}} \]

Verify: the amortized total is dramatically smaller than per-user guessing

Why: §14.7: per-user guessing pays 2²⁰ hashes for EACH of 10⁸ users; precomputation pays 2²⁰ hashes ONCE then cheap lookups. The hashing work is shared across all users — a huge win for the attacker.

35. What each one costs: §14.7 Precompute-and-lookup, counted

Trade off

Comparison matrix

From §14.7 Precompute-and-lookup, counted: every row here is a choice with a cost. Fill the cost (once) column, then say which row you would actually pick and what you give up for it.

phasecost (once)cost per user
per-user guessing—2²⁰ hashes each
precompute table2²⁰ hashes + sort—
lookup per user—lg(2²⁰) = 20 lookups

36. Why is this step legal: Assume the attack cost scales linearly with the…

Explain it to yourself

Discussion prompt

In Trap: 'every user must be attacked separately' this move is made:

Assume the attack cost scales linearly with the number of users

Why is that legal? Name the rule or definition it rests on before you read on.

Hint: If you can only say "because that is what you do", the rule is the thing to go and find.

Answer:

Wrong. Without salt, the hash of a common password is the same for everyone, so ONE precomputed table covers the whole database. Adding more users barely adds cost — only a cheap lookup apiece.

37. Trap: 'every user must be attacked separately'

Trap

The trap

A student: 'Cracking a million users takes a million times as long as cracking one.'

Assume the attack cost scales linearly with the number of users

Why: Wrong. Without salt, the hash of a common password is the same for everyone, so ONE precomputed table covers the whole database. Adding more users barely adds cost — only a cheap lookup apiece.

The fix

A student: how does the attack cost actually scale with users?

Recognize precomputation amortizes the hashing across all users

Why: §14.7: hash each common password once, reuse against everyone. The dominant cost (hashing) is paid once, not per user — exactly what salt will break in Part 4.

38. Fix 1 — Salt (Defeats Precomputation)

Section

Part 4 · §14.8 a unique per-user value

39. §14.8 Add a random per-user salt

Concept

When a user creates an account, the server picks a fresh random salt s for that user. The salt is NOT secret. The server stores the pair ⟨s, H(w, s)⟩ — the salt in the clear, alongside the salted hash.

\[ \text{at registration: pick random } s; \quad \text{store } \langle s,\ H(w, s) \rangle \]

Salt — A random, per-user, NON-secret value mixed into the hash input. Its only job is to be unique per user so that identical passwords produce different stored hashes. It is stored in cleartext right next to the hash.

40. §14.8 The salt must be RANDOM

Concept

The salt has to be drawn from a good random source (callback to the L25 randomness unit). A long, unpredictable salt guarantees each user's salt is unique — that uniqueness is what blocks shared precomputation.

A predictable or REPEATED salt fails: if many users share salt s, the attacker can again build one table for s and reuse it across all of them — reopening the very attack salt was meant to close.

41. §14.8 Identical passwords, different hashes

Concept

Now if Alice and Bob both choose the same password w, they still get DIFFERENT stored hashes, because each gets a different salt:

\[ H(w,\ s_A) \ne H(w,\ s_B) \quad\text{even though}\quad w_A = w_B \]

On login the server reads the user's stored salt s, recomputes H(w′, s), and compares. The salt being public doesn't matter for this — it just has to be there.

42. §14.8 Why salt kills precomputation

Intuition

Precomputation worked because one hash of 'password123' matched every user who chose it. With salt, the attacker would have to build a SEPARATE table for each user's salt — there's no shared work left to amortize.

Rainbow tables and precomputed dictionaries are defeated outright: a table built for salt s_A is useless against salt s_B. The attacker is forced back to attacking each user individually.

Ask yourself: does salt slow down a SINGLE-user guess? (No — salt removes the cross-user shortcut. It does nothing about the speed of one guess; that's what the slow hash in Part 5 fixes.)

43. What has to happen first: §14.8 Same password, two salts, two hashes

Ranking

Put in order

Put the moves of §14.8 Same password, two salts, two hashes into the order they have to happen.

  1. Alice and Bob both pick the password w = 'sunshine'; the server assigns each a random salt
  2. Note the attacker cannot tell Alice and Bob share a password, and cannot reuse any precomputed table
  3. Verify the defense: the amortized / rainbow-table attack is defeated, even though the salt is public

Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. Salts are drawn independently at random per user, so s_A and s_B differ with overwhelming probability.

44. §14.8 Same password, two salts, two hashes

Worked example

Alice and Bob both pick the password w = 'sunshine'; the server assigns each a random salt

Why: Salts are drawn independently at random per user, so s_A and s_B differ with overwhelming probability.

userpasswordsaltstored hash
Alicesunshines_AH(sunshine, s_A)
Bobsunshines_BH(sunshine, s_B)
—(same w)(s_A ≠ s_B)(hashes differ!)

Note the attacker cannot tell Alice and Bob share a password, and cannot reuse any precomputed table

Why: Because the salts differ, the stored hashes differ, so a table built for one salt never matches the other.

\[ H(\text{sunshine},\, s_A) \ne H(\text{sunshine},\, s_B) \]

Verify the defense: the amortized / rainbow-table attack is defeated, even though the salt is public

Why: §14.8: salt forces per-user work; the attacker can no longer share hashing across users. The salt's publicness is fine — uniqueness, not secrecy, is what does the job.

45. Fill in: stored hash for §14.8 Same password, two salts, two hashes

Comparison

Comparison matrix

From §14.8 Same password, two salts, two hashes: refill the stored hash column from what you know. The rest of the table is as it appeared.

userpasswordsaltstored hash
Alicesunshines_AH(sunshine, s_A)
Bobsunshines_BH(sunshine, s_B)
—(same w)(s_A ≠ s_B)(hashes differ!)

46. §14.8 Real case: the LinkedIn breach

Concept

In 2012 LinkedIn's password database was stolen. It used unsalted SHA-256 — exactly the broken design from Part 1.

Because there was no salt, precomputation applied at full force: a researcher recovered about 90% of the passwords in roughly 6 days. A salt would have blocked the precomputation entirely.

47. Something is wrong here: 'the salt must be kept secret'

Anomaly

Predict first

A student writes this, and it looks reasonable:

A student: 'The salt is part of the defense, so it must be a secret — store it encrypted or in a separate vault.'

It is wrong. Say what breaks — and say it before you turn the page.

Correct: The salt is NOT secret — it's stored in cleartext right next to the hash, and the attacker who steals the database gets it too.

A student: what property of the salt actually matters?

Why: The salt is NOT secret — it's stored in cleartext right next to the hash, and the attacker who steals the database gets it too. Its only job is to be UNIQUE per user, not hidden.

48. Trap: 'the salt must be kept secret'

Trap

The trap

A student: 'The salt is part of the defense, so it must be a secret — store it encrypted or in a separate vault.'

Treat the salt as a secret that must be hidden from the attacker

Why: Wrong. The salt is NOT secret — it's stored in cleartext right next to the hash, and the attacker who steals the database gets it too. Its only job is to be UNIQUE per user, not hidden.

The fix

A student: what property of the salt actually matters?

Make the salt random and unique per user — public is fine

Why: §14.8: salt defeats precomputation by making identical passwords hash differently. Even knowing every salt, the attacker can't share hashing across users — that's all salt needs to do.

49. Fix 2 — Slow Hashing (Defeats Fast Guessing)

Section

Part 5 · §14.8 iterate to slow it down

50. §14.8 Salt alone isn't enough

Concept

Salt killed precomputation, but it did nothing about raw speed. Per-user offline guessing still works: 2²⁰ guesses for each of 100 million salted users is still about 10¹⁴ fast hashes — still roughly a day.

The remaining problem is that each hash is too CHEAP. We need to make computing the hash deliberately slow.

51. §14.8 Make the hash slow by iterating

Concept

Build a slow function F by applying the hash to itself n times in a row. Each call is fast, but n of them in sequence is n times slower — and n is a tunable knob.

\[ F(x) = \underbrace{H(H(H(\cdots H(x)\cdots)))}_{n\ \text{iterations}} \]

\[ \text{store } \langle s,\ F(w, s) \rangle \quad\text{instead of}\quad \langle s,\ H(w, s) \rangle \]

52. §14.8 n is a knob you can turn up over time

Concept

Because n is just an iteration count, you can RAISE it as hardware gets faster — keeping the honest cost near 1 ms while the attacker's per-guess cost climbs with it. The defense ages gracefully.

This is why production schemes expose a tunable 'work factor' or 'cost' parameter. You re-hash at a higher cost the next time a user logs in, with no change to the user's password.

53. §14.8 Tune n so one hash takes ~1 ms

Concept

Pick n so that F takes about 1 millisecond to compute. For an honest server seeing at most ~10 logins/second, that's roughly 1% CPU overhead — completely affordable.

But it multiplies the attacker's cost by the same factor. A fast SHA-256 hash is ~1 nanosecond; a 1 ms slow hash is about a million times slower, turning the attacker's 1-day job into thousands of machine-years.

54. §14.8 The asymmetry favors the defender

Intuition

The defender computes F once per login — a sprinkle of extra milliseconds nobody notices. The attacker computes F once per guess, across trillions of guesses — so the same slowdown that costs the defender 1% costs the attacker everything.

Slow hashing is why a breach buys you TIME rather than instant compromise: while the attacker grinds through machine-years, users can be warned and forced to reset.

Ask yourself: why doesn't a slow hash hurt the honest server much? (It runs F rarely — once per login — whereas the attacker runs it once per guess, trillions of times.)

55. What rests on this: §14.8 The asymmetry favors the defender

Socratic

Discussion prompt

Slow hashing is why a breach buys you TIME rather than instant compromise: while the attacker grinds through machine-years, users can be warned and forced to reset.

Suppose that were not true. What is the first thing in L33 · Password Hashing: Salt, Slow Hashes & Key Derivation that would stop working?

Hint: Follow it one step downstream. The answer is whatever was quietly relying on it.

Answer:

Ask yourself: why doesn't a slow hash hurt the honest server much? (It runs F rarely — once per login — whereas the attacker runs it once per guess, trillions of times.)

56. What has to happen first: §14.8 Attacker time: fast hash vs slow hash

Ranking

Put in order

Put the moves of §14.8 Attacker time: fast hash vs slow hash into the order they have to happen.

  1. Start from Part 2: with fast SHA-256, cracking 100M users (2²⁰ guesses each) was ~10¹⁴ hashes ≈ ~1 day
  2. Switch to a 1 ms slow hash: every one of those hashes is now about a million times slower
  3. Verify: a 1 ms slow hash turns the attacker's ~1-day job into ~3000 machine-years, at ~1% server overhead

Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. This is the baseline from the offline-guessing worked example: 10¹⁴ hashes at 10⁹/sec ≈ 10⁵ sec ≈ ~1 day.

57. §14.8 Attacker time: fast hash vs slow hash

Worked example

Start from Part 2: with fast SHA-256, cracking 100M users (2²⁰ guesses each) was ~10¹⁴ hashes ≈ ~1 day

Why: This is the baseline from the offline-guessing worked example: 10¹⁴ hashes at 10⁹/sec ≈ 10⁵ sec ≈ ~1 day.

Switch to a 1 ms slow hash: every one of those hashes is now about a million times slower

Why: A 1 ms slow hash vs a ~1 ns fast hash is roughly a 10⁶× slowdown, applied to every guess the attacker makes.

hashper-hash costattacker time for 10¹⁴ hashes
fast SHA-256~1 ns≈ 10⁵ sec ≈ ~1 day
slow hash (1 ms)~1 ms≈ 10¹¹ sec ≈ ~3000 machine-years
honest server~1 ms / login≈ 1% CPU at 10 logins/sec

\[ 10^{14}\ \text{hashes} \times 10^{-3}\ \text{s/hash} = 10^{11}\ \text{s} \approx 3000\ \text{machine-years} \]

Verify: a 1 ms slow hash turns the attacker's ~1-day job into ~3000 machine-years, at ~1% server overhead

Why: §14.8: 10¹¹ seconds is about 3000 years on one machine. The same slowdown costs the honest server only ~1% CPU — the asymmetry is the entire point.

58. Fill in: attacker time for 10¹⁴ hashes for §14.8 Attacker time: fast hash vs slow hash

Comparison

Comparison matrix

From §14.8 Attacker time: fast hash vs slow hash: refill the attacker time for 10¹⁴ hashes column from what you know. The rest of the table is as it appeared.

hashper-hash costattacker time for 10¹⁴ hashes
fast SHA-256~1 ns≈ 10⁵ sec ≈ ~1 day
slow hash (1 ms)~1 ms≈ 10¹¹ sec ≈ ~3000 machine-years
honest server~1 ms / login≈ 1% CPU at 10 logins/sec

59. §14.8 Real slow-hash schemes

Concept

Don't invent your own. Production password hashing uses standardized iterated schemes, all built on the slow-hash idea above:

60. §14.8 ⊕ Beyond iteration: memory-hardness & passkeys

Concept

⊕ Supplemental — beyond §14.7–14.9. Attackers parallelize cheap iterated hashes on GPUs/ASICs. The modern answer is memory-hardness: force each guess to use lots of MEMORY, not just time, which custom hardware can't cheaply replicate.

61. Something is wrong here: 'a longer hash output (SHA-512) fixes offline guessing'

Anomaly

Predict first

A student writes this, and it looks reasonable:

A student: 'SHA-256 is too weak — switch to SHA-512 for a longer output and offline guessing goes away.'

It is wrong. Say what breaks — and say it before you turn the page.

Correct: A longer output doesn't make the hash SLOWER to compute — SHA-512 is still a fast hash, ~10⁹/sec.

A student: what actually raises the attacker's per-guess cost?

Why: A longer output doesn't make the hash SLOWER to compute — SHA-512 is still a fast hash, ~10⁹/sec. The attacker guesses just as fast. Output length resists nothing here; you need a deliberately SLOW (iterated, ideally memory-hard) hash.

62. Trap: 'a longer hash output (SHA-512) fixes offline guessing'

Trap

The trap

A student: 'SHA-256 is too weak — switch to SHA-512 for a longer output and offline guessing goes away.'

Swap SHA-256 for SHA-512 to stop fast offline guessing

Why: Wrong. A longer output doesn't make the hash SLOWER to compute — SHA-512 is still a fast hash, ~10⁹/sec. The attacker guesses just as fast. Output length resists nothing here; you need a deliberately SLOW (iterated, ideally memory-hard) hash.

The fix

A student: what actually raises the attacker's per-guess cost?

Use a slow, iterated hash (scrypt / bcrypt / PBKDF2), not a longer fast one

Why: §14.8: offline guessing is beaten by making each hash take ~1 ms, not by lengthening the digest. Tune n (or a memory cost) so one guess is expensive; output size is irrelevant to guessing speed.

63. Key Derivation — the Same Lesson

Section

Part 6 · §14.9 from passwords to keys

64. §14.9 Deriving an encryption key from a password

Concept

Scenario: you want to encrypt a file under a password the user types. You need an encryption key k. The tempting shortcut is to hash the password directly with a fast hash:

\[ k = \mathrm{SHA\text{-}256}(\text{password}) \quad\text{— tempting, but weak} \]

This is the SAME mistake as storing a fast unsalted hash — now in the key-derivation setting.

65. Teach it back: §14.9 Deriving an encryption key from a password

Explain it

Discussion prompt

Explain §14.9 Deriving an encryption key from a password to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.

Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.

Answer:

Scenario: you want to encrypt a file under a password the user types. You need an encryption key k. The tempting shortcut is to hash the password directly with a fast hash:

66. §14.9 Why k = SHA-256(password) is weak

Concept

An attacker with the ciphertext tries the 2²⁰ common passwords: for each, she computes k = SHA-256(guess) (fast), trial-decrypts, and checks for valid plaintext. She wins in milliseconds.

The fix is identical to password storage: use a slow key-derivation function with a salt — PBKDF2, scrypt, or Argon2 — so each guessed key costs ~1 ms, not ~1 ns.

67. By analogy: §14.9 Why k = SHA-256(password) is weak

Analogy

Discussion prompt

Explain §14.9 Why k = SHA-256(password) is weak by analogy to something with no Computer Security in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.

Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.

Answer:

An attacker with the ciphertext tries the 2²⁰ common passwords: for each, she computes k = SHA-256(guess) (fast), trial-decrypts, and checks for valid plaintext. She wins in milliseconds.

68. §14.9 One idea, two settings

Intuition

Whether you're STORING a password or DERIVING a key from one, the attacker's move is the same: guess the password, run the function forward, check. So the defense is the same: make the function slow and salt it.

That's why the very tools used to hash passwords — PBKDF2, scrypt, Argon2 — are literally called key-derivation functions. They're the same primitive doing the same job.

Ask yourself: what does the salt do for KEY derivation? (Same as before — defeats precomputation, so one table can't crack many encrypted files that share a password.)

69. Break it if you can: §14.9 One idea, two settings

Counterexample

Discussion prompt

Ask yourself: what does the salt do for KEY derivation? (Same as before — defeats precomputation, so one table can't crack many encrypted files that share a password.)

That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.

Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.

70. Something is wrong here: 'k = SHA-256(password) is a fine encryption key'

Anomaly

Predict first

A student writes this, and it looks reasonable:

A student: 'SHA-256 outputs 256 bits, which is a full-size key, so k = SHA-256(password) is a strong key.'

It is wrong. Say what breaks — and say it before you turn the page.

Correct: The key is only as unguessable as the password, and a fast hash lets the attacker try 2²⁰ common passwords in milliseconds — compute SHA-256(guess), trial-decrypt, win.

A student: how should you turn a password into an encryption key?

Why: The key is only as unguessable as the password, and a fast hash lets the attacker try 2²⁰ common passwords in milliseconds — compute SHA-256(guess), trial-decrypt, win. A full-length output doesn't help when the input is guessable and the hash is fast.

71. Trap: 'k = SHA-256(password) is a fine encryption key'

Trap

The trap

A student: 'SHA-256 outputs 256 bits, which is a full-size key, so k = SHA-256(password) is a strong key.'

Use a single fast hash of the password as the encryption key

Why: Wrong. The key is only as unguessable as the password, and a fast hash lets the attacker try 2²⁰ common passwords in milliseconds — compute SHA-256(guess), trial-decrypt, win. A full-length output doesn't help when the input is guessable and the hash is fast.

The fix

A student: how should you turn a password into an encryption key?

Derive k with a salted, slow KDF (PBKDF2 / scrypt / Argon2)

Why: §14.9: a slow, salted key-derivation function makes each guessed key cost ~1 ms, turning a millisecond attack into machine-years — the same slow-hash + salt defense as password storage.

72. Which of these survive contact with L33 · Password Hashing: Salt, Slow Hashes &…?

Two truths and a lie

Sort into buckets

Some of these hold up and some are the exact mistakes this lesson is built to prevent. Sort them.

Holds up
The first improvement: never store w. Instead store its hash H(w) (e.g. SHA-256). The server keeps the hashes; it never keeps the passwords themselves.; At registration the server computes and stores H(w). At login it hashes the entered password w′ and compares to the stored value — equal hashes mean the right password.; Ask yourself: what makes guessing forward cheap? (A fast hash + a small space of likely passwords. The next two attacks exploit exactly that.)
Breaks
A student: 'SHA-256 is one-way, so once I store H(w) a stolen database is useless to the attacker.'; A student: 'My login form locks out after 5 wrong tries, so guessing is rate-limited and slow.'
sound
These are stated as this lesson states them — each one survives the edge cases L33 · Password Hashing: Salt, Slow Hashes & Key Derivation puts it through.
flawed
Each of these is lifted from a trap in this deck: reasonable-sounding, and wrong in a way that only shows up once you rely on it.

73. Without one step: The password-storage playbook

Constraint

Discussion prompt

Run The password-storage playbook with this step confiscated:

Slow the hash — iterate F(x) = H(H(…H(x)…)) n times so one hash ≈ 1 ms; store ⟨s, F(w, s)⟩. Honest server ≈ 1% overhead; attacker's day becomes ~3000 machine-years (§14.8).

Is it still possible? If it is, say what takes its place and what it costs you. If it is not, say exactly what that step was providing that nothing else does.

Hint: A step you can drop for free was never load-bearing. If you cannot drop it, name the thing that goes wrong the moment it is gone.

Answer:

  1. Never store cleartext — store a hash of the password (§14.7); a stolen database then can't be read directly.
  2. Assume offline guessing — a stolen hash DB is attacked locally with no rate limit; a fast hash tests ~10⁹ guesses/sec (~1 day for 100M users at 2²⁰ guesses…
  3. Salt per user — store ⟨s, H(w, s)⟩ with a random, PUBLIC, unique salt; identical passwords now hash differently, defeating precomputation and rainbow…
  4. Slow the hash — iterate F(x) = H(H(…H(x)…)) n times so one hash ≈ 1 ms; store ⟨s, F(w, s)⟩. Honest server ≈ 1% overhead; attacker's day becomes ~3000…
  5. Use a vetted scheme — scrypt, bcrypt, or PBKDF2; ⊕ prefer memory-hard Argon2id, or skip passwords with passkeys/FIDO2.
  6. Same rule for keys — never k = SHA-256(password); derive keys with a salted slow KDF (PBKDF2 / scrypt / Argon2) (§14.9).

74. The password-storage playbook

Pattern

  1. Never store cleartext — store a hash of the password (§14.7); a stolen database then can't be read directly.
  2. Assume offline guessing — a stolen hash DB is attacked locally with no rate limit; a fast hash tests ~10⁹ guesses/sec (~1 day for 100M users at 2²⁰ guesses each).
  3. Salt per user — store ⟨s, H(w, s)⟩ with a random, PUBLIC, unique salt; identical passwords now hash differently, defeating precomputation and rainbow tables (§14.8).
  4. Slow the hash — iterate F(x) = H(H(…H(x)…)) n times so one hash ≈ 1 ms; store ⟨s, F(w, s)⟩. Honest server ≈ 1% overhead; attacker's day becomes ~3000 machine-years (§14.8).
  5. Use a vetted scheme — scrypt, bcrypt, or PBKDF2; ⊕ prefer memory-hard Argon2id, or skip passwords with passkeys/FIDO2.
  6. Same rule for keys — never k = SHA-256(password); derive keys with a salted slow KDF (PBKDF2 / scrypt / Argon2) (§14.9).

75. Where does it stop working: The password-storage playbook

Edge cases

Discussion prompt

The password-storage playbook works on the cases you have just seen. Push it to the edge: what is the most degenerate input it still handles — empty, zero, one item, everything equal — and what is the first case where it stops being true? Name the case, not just "it breaks".

Hint: Try the smallest legal input, then the largest, then the one where two things collide. Methods are specified at their edges; the middle takes care of itself.

Answer:

  1. Never store cleartext — store a hash of the password (§14.7); a stolen database then can't be read directly.
  2. Assume offline guessing — a stolen hash DB is attacked locally with no rate limit; a fast hash tests ~10⁹ guesses/sec (~1 day for 100M users at 2²⁰ guesses…
  3. Salt per user — store ⟨s, H(w, s)⟩ with a random, PUBLIC, unique salt; identical passwords now hash differently, defeating precomputation and rainbow…
  4. Slow the hash — iterate F(x) = H(H(…H(x)…)) n times so one hash ≈ 1 ms; store ⟨s, F(w, s)⟩. Honest server ≈ 1% overhead; attacker's day becomes ~3000…
  5. Use a vetted scheme — scrypt, bcrypt, or PBKDF2; ⊕ prefer memory-hard Argon2id, or skip passwords with passkeys/FIDO2.
  6. Same rule for keys — never k = SHA-256(password); derive keys with a salted slow KDF (PBKDF2 / scrypt / Argon2) (§14.9).

76. Rule out three: Checkpoint — salt, slowness, and what they each…

Elimination

Eliminate the wrong options

Which statement about this scheme is TRUE?

3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.

  • A. The salt must be kept secret; since Mallory stole it, the salt provides no protection at all.
  • B. The salt defeats the precomputation/rainbow-table attack, but a fast hash still allows per-user offline guessing — so a slow hash is also needed.
  • C. Switching from SHA-256 to SHA-512 would stop the offline guessing attack.
  • D. Because the passwords are hashed, the stolen database is safe regardless of hash speed.

Survives elimination: B

Why: §14.7–14.8: the salt's job is to be unique per user (it is stored in cleartext and the attacker is expected to have it). Uniqueness makes identical passwords hash differently, so no single precomputed table — and no rainbow table — can be reused across users; the amortized attack is defeated. But salt does nothing about SPEED: each user can still be attacked individually with fast offline guessing (~10⁹ guesses/sec). To stop that you must also make the hash SLOW — iterate it so one guess costs ~1 ms, turning the attacker's ~1-day job into thousands of machine-years. Salt and slow hashing fix two different problems; you need both.

77. Checkpoint — salt, slowness, and what they each buy

Check

A site stores ⟨s, H(w, s)⟩ using a single fast SHA-256 and a random per-user salt. Mallory steals the whole database, salts included. Think it through before choosing.

Check your understanding

Which statement about this scheme is TRUE?

  • A. The salt must be kept secret; since Mallory stole it, the salt provides no protection at all.
  • B. The salt defeats the precomputation/rainbow-table attack, but a fast hash still allows per-user offline guessing — so a slow hash is also needed. (correct)
  • C. Switching from SHA-256 to SHA-512 would stop the offline guessing attack.
  • D. Because the passwords are hashed, the stolen database is safe regardless of hash speed.

Answer: B

Why: §14.7–14.8: the salt's job is to be unique per user (it is stored in cleartext and the attacker is expected to have it). Uniqueness makes identical passwords hash differently, so no single precomputed table — and no rainbow table — can be reused across users; the amortized attack is defeated. But salt does nothing about SPEED: each user can still be attacked individually with fast offline guessing (~10⁹ guesses/sec). To stop that you must also make the hash SLOW — iterate it so one guess costs ~1 ms, turning the attacker's ~1-day job into thousands of machine-years. Salt and slow hashing fix two different problems; you need both.

Why A tempts people
The salt is NOT supposed to be secret — it is stored in cleartext precisely so the server can recompute the hash at login, and the attacker is assumed to have it. Its protection comes from being UNIQUE per user (defeating shared precomputation), not from being hidden. Mallory having it does not weaken its purpose.
Why C tempts people
A longer output does not slow the hash down. SHA-512 is still a fast hash (~10⁹/sec), so per-user offline guessing proceeds just as fast. Defeating offline guessing requires a deliberately SLOW, iterated (ideally memory-hard) hash — output length is irrelevant.
Why D tempts people
Hashing only blocks reading passwords directly off disk. With a FAST hash the attacker guesses forward at ~10⁹/sec; a stolen database of fast hashes is cracked in about a day for 100M users. Hash speed matters enormously — that is why a slow hash is required.

78. Misconceptions to retire

Concept

79. Synthesis — three ideas stacked into defense-in-depth

Concept

80. Primary sources & where to read more

Concept

81. Connect it up: L33 · Password Hashing: Salt, Slow Hashes & Key Derivation

Connect it up

Draw it

One page, no notation unless you need it: draw how these connect — Storing H(w) — the First Idea · Attack 1 — Offline Guessing · Attack 2 — Amortized Precomputation · Fix 1 — Salt (Defeats Precomputation) · Fix 2 — Slow Hashing (Defeats Fast Guessing) · Key Derivation — the Same Lesson. Put an arrow wherever one of them is what makes another possible, and label the arrow with why.

82. Recap — Lesson 33

Recap

You can now explain why storing a fast hash H(w) isn't enough, estimate the cost of offline and amortized guessing, apply a per-user salt to defeat precomputation and a slow iterated hash to defeat fast guessing, and carry the same recipe over to password-based key derivation.

Idea§The one-line version
Store H(w)14.7Never store cleartext; hashes are one-way
Offline guessing14.7Stolen DB attacked locally; fast hash ≈ 10⁹/sec ≈ ~1 day for 100M
Amortized attack14.7Unsalted: one precomputed/rainbow table cracks everyone
Salt14.8Random, public, per-user → identical pwds hash differently
Slow hash14.8Iterate n× to ~1 ms; attacker's day → ~3000 machine-years
Real schemes14.8scrypt / bcrypt / PBKDF2; ⊕ memory-hard Argon2id
Key derivation14.9Not SHA-256(pwd); use a salted slow KDF

Sources

  1. CS 161 Computer Security Textbook §14.7–14.9 — Wagner, Weaver, Kao, Shakir, Law & Ngai, UC Berkeley — hashing passwords and why a fast hash isn't enough plus the offline and amortized guessing attacks (§14.7), the two fixes of per-user salt and slow iterated hashing (§14.8), and the implications for key derivation (§14.9)
  2. A Future-Adaptable Password Scheme (bcrypt) — N. Provos & D. Mazières, Proceedings of the USENIX Annual Technical Conference, FREENIX Track (1999) — introduces bcrypt, a salted, adaptive-cost (tunably slow) password hash
  3. Stronger Key Derivation via Sequential Memory-Hard Functions (scrypt) — C. Percival, Proceedings of BSDCan (2009) — introduces scrypt, a memory-hard key-derivation function that resists hardware-parallel attacks
  4. Argon2: the memory-hard function for password hashing and other applications ⊕ — A. Biryukov, D. Dinu & D. Khovratovich, IEEE European Symposium on Security and Privacy (2016) — supplemental, beyond the textbook: the Password Hashing Competition winner and its memory-hard Argon2id variant
  5. RFC 8018 — PKCS #5: Password-Based Cryptography Specification Version 2.1 (PBKDF2) — K. Moriarty, B. Kaliski & A. Rusch, IETF RFC 8018 (2017) — standardizes PBKDF2, a salted, iteration-count-tunable password-based key-derivation function

Want this taught 1-on-1? Alexander tutors Computer Security — $55/session, free consultation.

Book on Wyzant · Text (657) 465-8108