Dragonblood: Analyzing the Dragonfly Handshake of WPA3 and EAP-pwd Mathy Vanhoef and Eyal Ronen ANRW. Montreal, Canada, 22 July 2019.
Background: Dragonfly in WPA3 = Password Authenticated Key Exchange (PAKE) Negotiate Provide mutual session key authentication Forward secrecy Protect against & prevent offline server compromise dictionary attacks 2
Dragonfly Convert password to Convert password to elliptic curve point P elliptic curve point P Commit phase Confirm phase 3
With MODP groups: hash-to-group for (counter = 1; counter < 256; counter++) value = hash(pw, counter, addr1, addr2) if value >= p: continue P = π€πππ£π (πβ1)/π if P > 1: return P 4
With MODP groups: hash-to-group for (counter = 1; counter < 256; counter++) value = hash(pw, counter, addr1, addr2) if value >= p: continue P = π€πππ£π (πβ1)/π if P > 1: return P In practice always true 5
With MODP groups: hash-to-group for (counter = 1; counter < 256; counter++) value = hash(pw, counter, addr1, addr2) if value >= p: continue Problem: value >= p P = π€πππ£π (πβ1)/π if P > 1: return P In practice always true 6
With MODP groups: hash-to-group for (counter = 1; counter < 256; counter++) value = hash(pw, counter, addr1, addr2) if value >= p: continue P = π€πππ£π (πβ1)/π if P > 1: return P 7
With MODP groups: hash-to-group for (counter = 1; counter < 256; counter++) value = hash(pw, counter, addr1, addr2) if value >= p: continue P = π€πππ£π (πβ1)/π if P > 1: return P 8
With MODP groups: hash-to-group for (counter = 1; counter < 256; counter++) value = hash(pw, counter, addr1, addr2) if value >= p: continue P = π€πππ£π (πβ1)/π if P > 1: return P No timing leak countermeasures despite warnings by IETF & CFRG! 9
With MODP groups: hash-to-group for (counter = 1; counter < 256; counter++) value = hash(pw, counter, addr1, addr2) if value >= p: continue WPA3: spoof client address P = π€πππ£π (πβ1)/π to obtain different executions if P > 1: return P No timing leak countermeasures despite warnings by IETF & CFRG! 10
Raspberry Pi 1 B+: differences are measurable 11
Raspberry Pi 1 B+: differences are measurable Hostap (WPA3): ~75 measurements / address iwd (EAP-pwd): ~30 measurements / address 12
Leaked information: #iterations needed Client address addrA Measured 13
Leaked information: #iterations needed Client address addrA Measured Password 1 Password 2 Password 3 14
Leaked information: #iterations needed Client address addrA Measured Password 1 Password 2 Password 3 15
Leaked information: #iterations needed Client address addrA addrB Measured Password 1 Password 2 Password 3 16
Leaked information: #iterations needed Client address addrA addrB Measured Password 1 Password 2 Password 3 17
Leaked information: #iterations needed Client address addrA addrB addrC Measured Password 1 Password 2 Password 3 18
Leaked information: #iterations needed Client address addrA addrB addrC Measured forms a signature of the password Password 1 Password 2 Need ~17 addresses to test ~ 10 7 passwords Password 3 19
What about elliptic curves? Hash-to-group with elliptic curves also affected? βΊ By default Dragonfly uses NIST curves βΊ Timing leaks for NIST curves are mitigated Dragonfly also supports Brainpool curves βΊ After our initial disclosure, the Wi-Fi Alliace private created guidelines that mention these are secure to use βΊ Bad news: Brainpool curves in Dragonfly are insecure 20
Hash-to-curve for (counter = 1; counter < k or not x; counter++) value = hash(pw, counter, addr1, addr2) if value >= p: continue y_sqr = value^3 + a * value + b if is_quadratic_residue(y_sqr) and not x: x = value pw = random() y = sqrt(x^3 + a * x + b) return (x, y) 21
Hash-to-curve for (counter = 1; counter < k or not x; counter++) value = hash(pw, counter, addr1, addr2) if value >= p: continue y_sqr = value^3 + a * value + b Problem: no solution for y if is_quadratic_residue(y_sqr) and not x: x = value pw = random() y = sqrt(x^3 + a * x + b) return (x, y) 22
Hash-to-curve for (counter = 1; counter < k or not x; counter++) value = hash(pw, counter, addr1, addr2) if value >= p: continue y_sqr = value^3 + a * value + b if is_quadratic_residue(y_sqr) and not x: x = value pw = random() y = sqrt(x^3 + a * x + b) return (x, y) 23
Hash-to-curve for (counter = 1; counter < k or not x; counter++) value = hash(pw, counter, addr1, addr2) if value >= p: continue y_sqr = value^3 + a * value + b if is_quadratic_residue(y_sqr) and not x: x = value pw = random() y = sqrt(x^3 + a * x + b) return (x, y) 24
Hash-to-curve for (counter = 1; counter < k or not x; counter++) value = hash(pw, counter, addr1, addr2) if value >= p: continue y_sqr = value^3 + a * value + b Problem: different passwords if is_quadratic_residue(y_sqr) and not x: have different execution time x = value pw = random() y = sqrt(x^3 + a * x + b) return (x, y) 25
Hash-to-curve for (counter = 1; counter < k or not x; counter++) value = hash(pw, counter, addr1, addr2) if value >= p: continue y_sqr = value^3 + a * value + b if is_quadratic_residue(y_sqr) and not x : x = value pw = random() ο Always execute at y = sqrt(x^3 + a * x + b) least k iterations return (x, y) 26
Hash-to-curve for (counter = 1; counter < k or not x; counter++) value = hash(pw, counter, addr1, addr2) if value >= p: continue y_sqr = value^3 + a * value + b if is_quadratic_residue(y_sqr) and not x : x = value In case quadratic test pw = random() is not constant time y = sqrt(x^3 + a * x + b) return (x, y) 27
Hash-to-curve for (counter = 1; counter < k or not x; counter++) value = hash(pw, counter, addr1, addr2) if value >= p: continue y_sqr = value^3 + a * value + b Problem: value >= p if is_quadratic_residue(y_sqr) and not x: x = value pw = random() y = sqrt(x^3 + a * x + b) return (x, y) 28
Hash-to-curve May be true for for (counter = 1; counter < k or not x; counter++) Brainpool curves! value = hash(pw, counter, addr1, addr2) if value >= p: continue y_sqr = value^3 + a * value + b if is_quadratic_residue(y_sqr) and not x: x = value pw = random() y = sqrt(x^3 + a * x + b) return (x, y) 29
Hash-to-curve May be true for for (counter = 1; counter < k or not x; counter++) Brainpool curves! value = hash(pw, counter, addr1, addr2) if value >= p: continue y_sqr = value^3 + a * value + b if is_quadratic_residue(y_sqr) and not x: x = value Quadratic test may be skipped pw = random() y = sqrt(x^3 + a * x + b) return (x, y) 30
Hash-to-curve May be true for for (counter = 1; counter < k or not x; counter++) Brainpool curves! value = hash(pw, counter, addr1, addr2) if value >= p: continue y_sqr = value^3 + a * value + b if is_quadratic_residue(y_sqr) and not x: x = value Quadratic test may be skipped pw = random() y = sqrt(x^3 + a * x + b) A random #(extra iterations) return (x, y) have a too big hash output 31
Influence of extra iterations Execution 1 32
Influence of extra iterations Execution 1 Execution 2 Execution 3 Execution 4 33
Influence of extra iterations Execution 1 Execution 2 Execution 3 Execution 4 34
Influence of extra iterations Execution 1 Execution 2 Execution 3 Execution 4 Variance ~ when password element was found 35
Influence of extra iterations Execution 1 Execution 2 Execution 3 Execution 4 Variance ~ when password element was found Average ~ when found and #iterations with big hash 36
Influence of extra iterations Execution 1 Execution 2 Execution 3 Execution 4 Variance ~ when password element was found Average ~ when found and #iterations with big hash ο Again forms a signature of the password 37
Raspberry Pi 1 B+ 38
Raspberry Pi 1 B+ Hostap (WPA3): ~300 measurements / address 39
Cache Attacks 40
Hash-to-curve: Quadratic Residue for (counter = 1; counter < k or not x; counter++) value = hash(pw, counter, addr1, addr2) if value >= p: continue y_sqr = value^3 + a * value + b if is_quadratic_residue(y_sqr) and not x: x = value pw = random() NIST curves: use Flush+Reload to y = sqrt(x^3 + a * x + b) detect if code is executed in 1 st iteration return (x, y) 41
Hash-to-curve: Quadratic Residue Use as clock to detect in which iteration we are for (counter = 1; counter < k or not x; counter++) value = hash(pw, counter, addr1, addr2) if value >= p: continue y_sqr = value^3 + a * value + b if is_quadratic_residue(y_sqr) and not x: x = value pw = random() NIST curves: use Flush+Reload to y = sqrt(x^3 + a * x + b) detect if code is executed in 1 st iteration return (x, y) 42
Recommend
More recommend