WPA3 overcomes the problems of the four-way handshake in WPA2. This overcomes offline dictionary attacks and provides Forward Secrecy (FS). 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 the commit phase, Bob and Alice select two random numbers. Alice generates (\(r_A\),\(m_A\)) and Bob generates (\(r_B\),\(m_B\)). Bob computes (\(s_B = r_B + m_B\)) and then generates \(E_B = −m_B.G\). He sends these to Alice as part of the commit phase. Alice computes (\(s_a = r_a + m_a\)) and then generates \(E_A = −m_A.G\). She sends these to Bob for the commit phase. Alice generates a key of \(K = r_A · (s_B · G + E_B)\) and Bob generates the same key of \(K = r_B · (s_A · G + E_A) \).
WPA3: SAE (Simultaneous Authentication of Equals) handshake - DragonFly |
Method
WPA3 overcomes the problems of the four-way handshake in WPA2. This overcomes offline dictionary attacks and provides Forward Secrecy (FS). 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 the commit phase, Bob and Alice select two random numbers. Alice generates:
\(r_A\),\(m_A\)
Bob generates:
\(r_B\),\(m_B\)
Bob computes:
\(s_B = r_B + m_B\)
and then generates:
\(E_B = −m_B.G\)
and where \(G\) is the generator point on a curve. He sends these to Alice as part of the commit phase. Alice computes:
\(s_A = r_A + m_A\)
and then generates:
\(E_A = −m_A.G\)
She sends these to Bob for the commit phase. Alice generates a key of:
\(K = r_A · (s_B · G + E_B)\)
and Bob generates the same key of:
\(K = r_B · (s_A · G + E_A) \)
These are the same as:
\(K = r_A · (s_B · G + E_B) = r_A · (r_B+m_B)·G − m_B·G = r_A · (r_B·G + m_B · G − m_B.G) = r_A·r_B·G\)
\(K = r_B · (s_A · G + E_A) = r_B · (r_A+m_A)·G − m_A·G = r_B · (r_A·G + m_A · G − m_A.G) = r_B·r_A·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. The method is illustrated with [1]:
Figure 1: Dragonfly Handshake of WPA3 [1]
Coding
The outline code is:
package main import ( "crypto/rand" "bytes" "fmt" "crypto/sha256" "crypto/hmac" "io" "os" "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() { ctype:="0" curve := curves.K256() argCount := len(os.Args[1:]) if (argCount>0) {ctype = string(os.Args[1])} if (ctype=="1") { curve = curves.P256() } if (ctype=="2") { curve = curves.ED25519() } if (ctype=="3") { curve = curves.PALLAS() } if (ctype=="4") { curve = curves.BLS12381G1() } if (ctype=="5") { curve = curves.BLS12381G2() } if (ctype=="6") { curve = curves.BLS12377G1() } if (ctype=="7") { curve = curves.BLS12377G2() } G := curve.Point.Generator() fmt.Printf("Curve= %s\n", G.CurveName()) fmt.Printf("Base Point (G)= %x\n", G.ToAffineUncompressed()) lenb := len(G.ToAffineUncompressed()) Gx := G.ToAffineUncompressed()[1 : lenb/2+1] Gy := G.ToAffineUncompressed()[lenb/2+1 : lenb] fmt.Printf("--- Basic point operation ---\n") fmt.Printf("G(x,y)=%x, %x\n", Gx, Gy) 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()) tr1:=append(sA.Bytes(),Ea.ToAffineCompressed()[:]...) tr1=append(tr1, sB.Bytes()...) tr1=append(tr1, Ea.ToAffineCompressed()[:]...) fmt.Printf("\ntr (Alice)= %x\n", tr1) hash.Write(tr1) fmt.Printf("\nHMAC-Sha256 (cA): %x\n", hash.Sum(nil)) hash = hmac.New(sha256.New, K2.ToAffineCompressed()) tr2:=append(sB.Bytes(),Eb.ToAffineCompressed()[:]...) tr2=append(tr2, sA.Bytes()...) tr2=append(tr2, Eb.ToAffineCompressed()[:]...) fmt.Printf("\ntr (Bob)= %x\n", tr2) hash.Write(tr2) fmt.Printf("\nHMAC-Sha256 (cB): %x", hash.Sum(nil)) }
We generate the four random values by first generating the curve and the generator point, and then defining four random scalar values:
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)
Next, we can compute \(s_B = r_B + m_B\) and \(s_A = r_A + m_A\):
sA := ra.Add(ma) sB := rb.Add(mb)
and then \(E_B = −m_B.G\) and \(E_A = −m_A.G\) :
Ea :=G.Mul(ma.Neg()) Eb :=G.Mul(mb.Neg())
Finally, we compute \(K = r_A · (s_B · G + E_B)\) and \(K = r_B · (s_A · G + E_A)\) with:
K1 := G.Mul(sB).Add(Eb).Mul(ra) K2 := G.Mul(sA).Add(Ea).Mul(rb)
A sample run:
Curve= secp256k1 Base Point (G)= 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 --- Basic point operation --- G(x,y)=79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798, 483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 sA (Alice)= 53b9d2d84af8e46c76110f495876b9ad63864848540472bf2537644fcf4c4df3 Ea (Alice)= 036ab2c77f387d50e640eb9fd0b58cfcc006697a6ca07209d7699127c70fb9dcb5 sB (Bob)= cb99b631bfeaa9019653b849da84660360b46ee7d8581ddbd08ca072d504bb4c Eb (Bob)= 0306f702398ea9fc2b98d2f7ad42025bff2055eb01db482cc98ea72abb915e8e87 Shared Secret K1 (Alice): 035d03e7813fbd7b21b6260208623eb3323c49dc00c4a727861a0ad43dd5862635 Shared Secret K2 (Bob): 035d03e7813fbd7b21b6260208623eb3323c49dc00c4a727861a0ad43dd5862635 Keys are the same Hash(K1)= 907dba1a855aa4ac01cec1ed592aca0b718fba14397f6449c748286aad74e144 tr (Alice)= 53b9d2d84af8e46c76110f495876b9ad63864848540472bf2537644fcf4c4df3036ab2c77f387d50e640eb9fd0b58cfcc006697a6ca07209d7699127c70fb9dcb5cb99b631bfeaa9019653b849da84660360b46ee7d8581ddbd08ca072d504bb4c036ab2c77f387d50e640eb9fd0b58cfcc006697a6ca07209d7699127c70fb9dcb5 HMAC-Sha256 (cA): 1a1ab5537cbb93a349e4ce115c5db0c4724ee004e6f7466c525a7c153eaf5d76 tr (Bob)= cb99b631bfeaa9019653b849da84660360b46ee7d8581ddbd08ca072d504bb4c0306f702398ea9fc2b98d2f7ad42025bff2055eb01db482cc98ea72abb915e8e8753b9d2d84af8e46c76110f495876b9ad63864848540472bf2537644fcf4c4df30306f702398ea9fc2b98d2f7ad42025bff2055eb01db482cc98ea72abb915e8e87 HMAC-Sha256 (cB): 1732f2760bce0ba1e43d0b87dc221319c26ff00e145d2aa85d143dc0089568ff
Notice that Ea (Alice) is an compressed point (035f24e933053618b ... 95b6f5395b532ef23bbb) and where the "03" identifies that the y-axis value is odd, and Eb 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, where as an uncompressed point will identify both the x and y axis point.
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.