Key Exchanges (Diffie-Hellman, MITM on unauthenticated DH)
Security & Cryptography · Nutthakorn Chalaemwongwan
Today
- Why a key exchange, not just "send a key"
- Diffie-Hellman: the math, and why it's hard to break passively
- The active-attacker gap: MITM on unauthenticated DH
- The fix: authenticate the DH public keys (HMAC), keep forward secrecy
- 🕵️ Signature game: The Silent Third Wheel
Recap — Week 4
- AES block-cipher modes: ECB leaks patterns, CBC needs a random IV, GCM gives you authentication too
- Week 4 assumed Alice and Bob already share a key
- Week 5's question: how do they get that shared key in the first place — over a network anyone can watch?
The problem: sharing a secret over a public channel
- Alice and Bob want a shared symmetric key for AES
- They've never met; the only channel is the open network
- Naive idea: just send the key. What goes wrong?
- Anyone watching the wire (an eavesdropper) now has it too
Diffie-Hellman — the idea
- Public, shared: a large prime
pand generatorg - Alice picks secret
a, sendsg^a mod p - Bob picks secret
b, sendsg^b mod p - Both compute the same shared secret:
g^(ab) mod p - An eavesdropper sees
g^a,g^b,p,g— but nota,b, org^(ab)
Why an eavesdropper can't recover it
- Recovering
afromg^a mod p= the discrete logarithm problem - No efficient algorithm known for a well-chosen, large prime
p - This week's lab uses RFC 3526 Group 14: a standard 2048-bit MODP prime
- Small or non-prime
p, or a badg, can make the DLP tractable — never invent your own parameters
But DH answers the wrong question
- DH gives Alice and Bob a secret no eavesdropper can compute
- It gives neither side a way to check who they just agreed it with
- "Whoever answers when I dial Bob's address" ≠ "Bob"
- That gap is everything an active attacker needs
Meet the lab: Alice, Bob, and Relay
| Service | Role | What it does |
|---|---|---|
bob.py | Server | Listens on :5000, DH handshake, decrypts one AES-GCM message |
alice.py | Client | Connects to PEER_HOST:PEER_PORT — relay, never bob directly |
relay.py | Attacker | Sits on the network path between them |
- The premise is the lesson: Alice's peer address is
relay, notbob
The attack: two handshakes, one puppet-master
- Relay runs a full DH handshake with Alice — posing as Bob
- Relay runs a second, completely independent DH handshake with Bob — posing as Alice
- Relay now holds two different shared secrets: one per side
- It decrypts Alice's AES-GCM message with the Alice-side key, logs it, re-encrypts the same plaintext under the Bob-side key, forwards it
Why neither side notices
- Alice's handshake completes successfully — she has a shared secret
- Bob's handshake completes successfully — he has a shared secret
- Neither is the same secret, and neither side ever checks that
- No MAC, no signature, nothing binds the public key to an identity
- CWE-322 — Key Exchange without Entity Authentication
Watch it happen (vulnerable mode)
week05-relay | RELAY: completed independent DH handshake with Alice (posing as Bob)
week05-alice | ALICE: sent encrypted message ('the launch code is 4471')
week05-relay | RELAY: completed independent DH handshake with Bob (posing as Alice)
week05-relay | RELAY INTERCEPTED: the launch code is 4471
week05-bob | BOB RECEIVED: the launch code is 4471
The fix: authenticate the public key, not the channel
- Each side computes HMAC-SHA256(AUTH_KEY, public_key_bytes) and sends the tag alongside its DH public value
- The peer verifies the tag before deriving any session key
relaydoesn't haveAUTH_KEY— it can still try the identical substitution, but it can only send a junk tag- Verification fails → both sides print
AUTH FAILED - ABORTINGand exit
What the fix does not give up
- DH still runs fresh, every session — the session key is still ephemeral
- That means forward secrecy: if
AUTH_KEYleaks next year, past captured traffic is still safe - The fix authenticates the exchange — it does not replace it
- Real systems (TLS, SSH) authenticate DH with signatures/certificates, not a shared MAC — same principle: authenticate the exchange, don't just run it
Watch it fail (fixed mode)
week05-relay | RELAY: (fixed mode) attempted DH substitution with Alice (posing as Bob)
week05-alice | AUTH FAILED - ABORTING
week05-relay | RELAY: (fixed mode) attempted DH substitution with Bob (posing as Alice)
week05-bob | AUTH FAILED - ABORTING
- Zero occurrences of
RELAY INTERCEPTED— that absence is the evidence
Not in today's demo — stays conceptual
- Weak/non-standard groups (small or non-prime
p) — Q7 - Missing public-key validation — this lab's code does not validate
peer_yagainst the group order — Q8 - Small-subgroup confinement attack — a malicious public key can collapse the "shared secret" to one of only a handful of possible values, letting an attacker leak bits of a private key — Q9 / EiPE Part 2b
🕵️ Signature game — The Silent Third Wheel
- Be the eavesdropper who becomes an active puppet-master
- Sit silently between Alice and Bob, run your own DH handshake with each
- Read their "secure" channel in the clear — they never notice
- Then watch one HMAC tag over the public key shut you out completely, no other code change
Lab today
📋 Worksheet 5 —
labs/week05-key-exchanges/worksheet.md· Part 1 (10 essay Q's) + Part 2 (AIR-Sec: lab evidence, EiPE, Prompt Problem, viva)
- Run, don't exploit-and-fix code — this is a read/run/observe lab
- Vulnerable:
docker compose -f docker-compose.vulnerable.yml up --build→ captureRELAY INTERCEPTED: the launch code is 4471 - Fixed:
docker compose -f docker-compose.fixed.yml up --build→ capture bothAUTH FAILED - ABORTINGlines + confirmgrep -c "RELAY INTERCEPTED"= 0 - + EiPE (small-subgroup, plain English) + Prompt Problem (critique an AI's MITM explanation) — see worksheet
Key takeaways
- Textbook-secure primitive, real-system failure: DH's math defeats a passive eavesdropper — it says nothing about who you just agreed a secret with
- An active attacker who controls the network path exploits exactly that silence — two independent handshakes, invisible to both sides (CWE-322, CWE-300)
- The fix authenticates the exchange without sacrificing what DH is for: forward secrecy
Questions?
Next week: Authenticated Encryption (AEAD)