WPA2, step by step — and the two attacks that ended the PSK era
A real WPA2-PSK handshake, executed in your browser. The PMK is a genuine PBKDF2(HMAC-SHA1, passphrase, SSID, 4096, 256) — checked against the published IEEE 802.11i / WPA test vectors — the PTK is the 802.11i PRF-512 with its sorted address and nonce ordering, and all four EAPOL-Key messages carry real HMAC-SHA1 MICs computed here. Below the walkthrough, two panels run live: an offline dictionary attack that recovers the passphrase from message 2 with no contact with the access point, and a nonce-reuse (KRACK) demonstration in which a retransmitted message 3 makes the client reinstall the same key with a reset packet number, so AES-CTR encrypts two different plaintexts under the same keystream.
Live: the offline dictionary attack on message 2
This is the whole of the WPA2-PSK threat model. The attacker has captured one handshake — four frames, all of which travel in the clear — and now guesses passphrases entirely offline: for each candidate it computes PMK → PTK → MIC and compares the MIC with the one message 2 carried. Nothing is sent to the access point; the AP cannot rate-limit, lock out or even notice. The only thing standing between the attacker and the network is how much each guess costs and how many guesses are needed — i.e. the strength of the passphrase.
measuring…
Live: nonce reuse (KRACK) — the same keystream, twice
The 4-way handshake is supposed to give the client a fresh pairwise key and a fresh packet number. Vanhoef’s KRACK attack (2017) breaks the second half: a retransmitted message 3 is processed again, the client reinstalls the same TK and resets its packet number to the start. Two packets are then encrypted with the same key and the same nonce — so the same AES-CTR keystream — and C1 ⊕ C2 = P1 ⊕ P2, with the keystream cancelled. Below, two plaintexts are encrypted under this page’s real TK with the same counter block, and the first is recovered from the second.
measuring…
WEP post-mortem: how a 24-bit IV turned a weak cipher into a broken one
WEP is not a security mechanism and has not been one since 2001. It is
worth two minutes because every mistake in it reappears somewhere: a stream cipher keyed by
IV ‖ key, a public per-packet IV with too few bits, and a checksum mistaken for a MAC.
| design choice | what WEP does | consequence |
|---|---|---|
| keying | RC4 keyed with the 3-byte IV prepended to the shared key: K = IV ‖ key |
The first three key bytes are public, and RC4’s key-scheduling leaves those bytes legible in the state (the FMS/Roos correlation). The RC4 page measures that correlation live. |
| IV space | 24 bits, never checked for repeats, sent in the clear | By the birthday bound two packets share an IV after roughly 212 packets — measured below. Same IV ⇒ same RC4 key ⇒ same keystream. |
| reuse | two packets, same IV, different plaintexts | C1 ⊕ C2 = P1 ⊕ P2: the keystream cancels, exactly as in the KRACK panel above. No key was recovered and none was needed. |
| key recovery | IVs of the form (3, 255, x) — a known subset | The FMS attack turns the first keystream byte into a biased function of a key byte. After 105–106 packets the shared key falls out. The RC4 biases are structural, so nothing about the implementation saves it. |
| integrity | CRC-32 “checksum”, not a MAC | CRC is linear and RC4 is XOR, so an attacker who cannot read a message can still flip chosen bits and repair the checksum: injection and replay. |
WPA’s TKIP added a per-packet key mix and a real MIC, which closed the IV-collision and bit-flipping issues — but not the RC4 biases, and TKIP is itself deprecated. WPA2’s CCMP replaced RC4 with AES-CCM and TKIP with a 48-bit packet number, and fixed the micro-level design. It left one thing untouched: the PMK is still PBKDF2-SHA1 over the passphrase, so the offline dictionary attack above works exactly as well on WPA2 as on WPA. WPA3’s SAE (Dragonfly) is the point at which that stops being true — a passive attacker can no longer capture anything worth guessing against.
measuring…
Notation
| passphrase | The human-shared secret, 8–63 printable characters. Both sides must have it, and it is the only secret in WPA2-PSK. |
| SSID | The network name, used as the PBKDF2 salt. It is public — broadcast in every beacon — so it adds no secrecy, only domain separation between networks. |
| PMK | “Pairwise Master Key”, 256 bits. PMK = PBKDF2(HMAC-SHA1, passphrase, SSID, 4096, 256). Derived independently by both sides; never transmitted. |
| AA | The authenticator’s MAC address (the access point). Called AA in 802.11i. |
| SPA | The supplicant’s MAC address (the client). Called SPA in 802.11i. |
| ANonce / SNonce | 32-byte random values, one from each side, exchanged in messages 1 and 2. They are what makes each session’s keys different. |
| PTK | “Pairwise Transient Key”, 512 bits = KCK ‖ KEK ‖ TK ‖ (two 64-bit MIC keys). Derived from PMK, the two MAC addresses and the two nonces. Split so that each use gets its own key. |
| KCK / KEK / TK | The first, second and third 16 bytes of the PTK. KCK keys the EAPOL-Key MICs, KEK wraps the key data, TK is the CCMP session key for data frames. |
| EAPOL-Key | The 802.1X frame that carries the handshake. Its 802.11i body is: descriptor type, key info, key length, replay counter, key nonce, key IV, key RSC, key ID, key MIC, key data length, key data. |
| MIC | MIC = HMAC-SHA1(KCK, EAPOL frame) truncated to 16 bytes, where the frame’s own MIC field is zeroed first. It proves the sender knows the PTK, hence the PMK, hence the passphrase. |
| GTK | The group key, sent to the client in message 3, wrapped with the KEK. It protects broadcast and multicast traffic. |
| CCMP | The AES-CCM data-confidentiality protocol of WPA2: a 48-bit packet number, a nonce built from it, a CBC-MAC tag and an AES-CTR keystream. |
Glossary
Why the PMK uses the SSID as a salt — and why that is not a secret
The SSID is in the salt so that the same passphrase on two networks produces two different PMKs. It is not a secret ingredient: every beacon frame announces it. Anyone attacking a handshake already knows it, so it buys no work factor at all — it only stops two access points from sharing a key derived from the same passphrase.
Why both sides must sort the addresses and nonces
The PRF is fed Min(AA,SPA) ‖ Max(AA,SPA) ‖ Min(ANonce,SNonce) ‖ Max(ANonce,SNonce), compared
byte by byte as unsigned strings. The sorting exists so that the pair of addresses (and the pair of
nonces) determines the input regardless of which end is computing it and which end is the access point. Get it
wrong — use AA ‖ SPA in transmission order — and you derive a perfectly valid PTK that the other
side will never reproduce, because it is deriving a different one. Both ends accept each other’s frames only
if they hash the same eight-byte-prefixed string, so a mismatch shows up immediately as a failed MIC. The step
below shows the sorted ordering explicitly.
Why 4096 iterations cannot be raised
This is the difference between a password database and a password protocol. A server storing password hashes can raise its work factor whenever it likes and re-hash on next login. WPA2 cannot: the iteration count is fixed at 4096 by IEEE 802.11i and every client must agree, so the cost per guess is pinned by the standard and there is no deployment-time knob. That is why a WPA2 PSK network can never be hardened by better configuration — only by a better passphrase, or by moving to WPA3.
What a capture really contains
Four EAPOL-Key frames, all in the clear. Message 1 carries the ANonce and no MIC (nothing could authenticate it yet). Message 2 carries the SNonce and a MIC under the KCK. Message 3 carries the GTK and a MIC. Message 4 is a bare MIC acknowledgement. The PMK, PTK, KCK, KEK and TK are never on the wire — which is exactly why the attack has to guess the passphrase rather than read a key. The MIC on message 2 is the attacker’s oracle: it is a function of the passphrase alone (given the public SSID, MACs and nonces), so a candidate passphrase can be tested without any further interaction.
What this page does and does not model
The PMK, the PRF, the PTK split, the EAPOL-Key framing and every MIC are real, complete and computed here. The dictionary attack is the real algorithm, run over a small wordlist so it finishes instantly. For the nonce-reuse panel, CCMP’s CTR keystream and its counter-block construction are modelled faithfully; the CCM CBC-MAC tag, the additional-authenticated-data encoding and the packet-number header are not modelled, because the reuse bug lives entirely in the keystream. In message 3 the GTK is shown unwrapped; a real frame wraps those same bytes with the KEK using AES-Key-Wrap, and the MIC covers the frame either way.
Where the test vectors come from
The three passphrase → PMK vectors used and checked below are the WPA reference vectors, reproduced in the
passphrase_tests[] table of Jouni Malinen’s wpa_supplicant
(src/crypto/crypto_module_tests.c, the reference implementation of WPA/WPA2), and trace back to the
IEEE 802.11i Annex H worked examples. They were fetched from that published source, not typed from memory,
and are re-derived here on every page load.