For Improved Wifi Security, It’s Hello To DragonFly (WPA3)

Like it or not, WPA2 is not secure and can be cracked using an offline dictionary attack. The core strength of the handshake between the…

For Improved Wifi Security, It’s Hello To DragonFly (WPA3)

Like it or not, WPA2 is not secure and can be cracked using an offline dictionary attack. The core strength of the handshake between the client and the access point is the strength of the difficulty in cracking a PBKDF2 hash. If a user’s password is contained in a dictionary, and fairly common, it can be fairly easy to crack the hash that is passed in the pairing of the client to the access point. Here is an example of the vulnerability:

WPA2 uses a four-way handshake and is open to offline dictionary attacks and does not provide Forward Secrecy (FS) — as once the hash is cracked, all the previous session keys will be revealed. WPA3 add a zero-knowledge proof method which overcomes dictionary attacks, and provides FS.

SAE (Simultaneous Authentication of Equals)

For this, WPA3 uses a SAE (Simultaneous Authentication of Equals) handshake, and which is commonly known as DragonFly. This method has a commit and confirm phase, and uses elliptic curve methods. In Figure 1, Bob is the Access Point (AP) and Alice is the client.

Figure 1: Commit and confirm phase of WPA3 [1]

In the commit phase, Bob and Alice select two random numbers. Alice generates:

rA, mA

Bob generates:

rB, mB

Bob computes:

sB=rB+mB

and then generates:

EB=−mB.G

and where G is the generator point on a curve. He sends these to Alice as part of the commit phase. Alice computes:

sA=rA+mA

and then generates:

EA=−mA.G

She sends these to Bob for the commit phase. Alice generates a key of:

K=rA·(sB·G+EB)

and Bob generates the same key of:

K=rB·(sA·G+EA)

These are the same as:

K=rA·(sB·G+EB)=rA·(rB+mBGmB·G=rA·(rB·G+mB·GmB.G)=rA·rB·G

K=rB·(sA·G+EA)=rB·(rA+mAGmA·G=rB·(rA·G+mA·GmA.G)=rB·rA·G

After this, we can generate the key using a hash of the K value, and then we can take an HMAC hash of this to confirm the commitment of the key.

Coding

The outline code is [here]:

package main
import (
"crypto/rand"
"bytes"
"fmt"
"crypto/sha256"
"crypto/hmac"
"io"
"github.com/coinbase/kryptology/pkg/core/curves"
)
func getSalt(n int) []byte {
nonce := make([]byte, n)
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
panic(err.Error())
}
return (nonce)
}
func main() {
curve := curves.K256()
G := curve.Point.Generator()

ra := curve.Scalar.Random(rand.Reader)
ma := curve.Scalar.Random(rand.Reader)
rb := curve.Scalar.Random(rand.Reader)
mb := curve.Scalar.Random(rand.Reader)
sA := ra.Add(ma)
sB := rb.Add(mb)
Ea :=G.Mul(ma.Neg())
Eb :=G.Mul(mb.Neg())
fmt.Printf("\nsA (Alice)= %x\n", sA.Bytes())
fmt.Printf("Ea (Alice)= %x\n", Ea.ToAffineCompressed())
fmt.Printf("\nsB (Bob)= %x\n", sB.Bytes())
fmt.Printf("Eb (Bob)= %x\n", Eb.ToAffineCompressed())
K1 := G.Mul(sB).Add(Eb).Mul(ra)
K2 := G.Mul(sA).Add(Ea).Mul(rb)
fmt.Printf("\nShared Secret K1 (Alice):\t%x\n", K1.ToAffineCompressed())
fmt.Printf("\nShared Secret K2 (Bob):\t%x\n",K2.ToAffineCompressed())
if (bytes.Equal(K1.ToAffineCompressed(), K2.ToAffineCompressed())) {
fmt.Printf("\nKeys are the same\n")
}
h := sha256.Sum256(K1.ToAffineCompressed()[:32])
fmt.Printf("\nHash(K1)= %x\n",h)

hash := hmac.New(sha256.New, K1.ToAffineCompressed())
salt := getSalt(16)
hash.Write(salt)
fmt.Printf("\nHMAC-Sha256: %x", hash.Sum(nil))
}

A sample run [here]:


sA (Alice)= 93de18abb9028f404fdf60efd07fb0da769fab9a75be7a53f7f9c7f393bab36f
Ea (Alice)= 0379fadf963e274a7e7832d189f623e5f1abaa3f0d03d1ddb0149886c65b9fa556

sB (Bob)= c83b29ae025fa92773847b36186e238aa56aabe69d865bb2f4d4252461683b43
Eb (Bob)= 027b61fa01010f738f717e2aac940a893643a23e9389cf410b80faf9f569e87f5f

Shared Secret K1 (Alice): 024c6be0b0a7fff641dcfcfc38f0cfde51c0481f0a442f73f5016db4319b02f610

Shared Secret K2 (Bob): 024c6be0b0a7fff641dcfcfc38f0cfde51c0481f0a442f73f5016db4319b02f610

Keys are the same

Hash(K1)= 8c3f000682212cad172b3175883e3963f65811cee4984edfe8db6c0ec64f47c2

HMAC-Sha256: 4bfd4da0441ddd0e4cd63f646338e1aa21dac9ce8e9bd3400ae1323aae9a5c6e

Notice that Ea (Alice) is an compressed point (035f24e933053618b … 95b6f5395b532ef23bbb) and where the “03” identifies that the y-axis value is odd, and Eb (Bob) is also a compressed point (02658b634a2b121 … 6570d34076a1b4113fb7) and where the “02” identifies that the y-axis value is even. The compressed point thus only identifies the x-axis value.

Conclusions

And there you go. While there have been some initial attacks on the implementation of WPA3 [1], it should now be fairly stable in its security implementation. One of the great advantages of the WPA-3 handshake is that it keeps compatability with WPA2.

Here is my implementation of SAE using Golang and the Kryptology library:

https://asecuritysite.com/golang/go_dragon

References

[1] Vanhoef, M., & Ronen, E. (2020, May). Dragonblood: Analyzing the Dragonfly Handshake of WPA3 and EAP-pwd. In 2020 IEEE Symposium on Security and Privacy (SP) (pp. 517–533). IEEE.