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