1// Copyright 2025 The BoringSSL Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package spake2plus
16
17import (
18	"bytes"
19	"encoding/hex"
20	"math/big"
21	"testing"
22)
23
24func hexToBytes(h string) []byte {
25	b, err := hex.DecodeString(h)
26	if err != nil {
27		panic(err)
28	}
29	return b
30}
31
32func TestSPAKE2PlusBasicRoundTrip(t *testing.T) {
33	pw := []byte("password")
34	context := []byte("SPAKE2+-P256-SHA256-HKDF-SHA256-HMAC-SHA256 Test Vectors")
35	idProver := []byte("client")
36	idVerifier := []byte("server")
37
38	pwVerifierW0, pwVerifierW1, registrationRecord, err := Register(
39		pw, idProver, idVerifier,
40	)
41	if err != nil {
42		t.Fatalf("Registration failed: %v", err)
43	}
44
45	prover, err := NewProver(
46		context, idProver, idVerifier,
47		pwVerifierW0, pwVerifierW1,
48	)
49	if err != nil {
50		t.Fatalf("Prover context creation failed: %v", err)
51	}
52	verifier, err := NewVerifier(
53		context, idProver, idVerifier,
54		pwVerifierW0, registrationRecord,
55	)
56	if err != nil {
57		t.Fatalf("Verifier context creation failed: %v", err)
58	}
59
60	proverShare, err := prover.GenerateProverShare()
61	if err != nil {
62		t.Fatalf("Prover share generation failed: %v", err)
63	}
64
65	verifierShare, verifierConfirm, verifierSecret, err := verifier.ProcessProverShare(proverShare)
66	if err != nil {
67		t.Fatalf("Verifier failed to process prover's share: %v", err)
68	}
69
70	proverConfirm, proverSecret, err := prover.ComputeProverConfirmation(verifierShare, verifierConfirm)
71	if err != nil {
72		t.Fatalf("Prover failed to compute confirmation: %v", err)
73	}
74
75	if err := verifier.VerifyProverConfirmation(proverConfirm); err != nil {
76		t.Fatalf("Verifier failed to verify prover confirmation: %v", err)
77	}
78
79	if !bytes.Equal(proverSecret, verifierSecret) {
80		t.Fatal("Shared secrets do not match")
81	}
82}
83
84func TestSPAKE2PlusTestVectors(t *testing.T) {
85	// Test Vectors from RFC 9383 Appendix C
86	context := []byte("SPAKE2+-P256-SHA256-HKDF-SHA256-HMAC-SHA256 Test Vectors")
87	idProver := []byte("client")
88	idVerifier := []byte("server")
89
90	w0_str := "bb8e1bbcf3c48f62c08db243652ae55d3e5586053fca77102994f23ad95491b3"
91	w1_str := "7e945f34d78785b8a3ef44d0df5a1a97d6b3b460409a345ca7830387a74b1dba"
92	L_str := "04eb7c9db3d9a9eb1f8adab81b5794c1f13ae3e225efbe91ea487425854c7fc00f00bfedcbd09b2400142d40a14f2064ef31dfaa903b91d1faea7093d835966efd"
93	x_str := "d1232c8e8693d02368976c174e2088851b8365d0d79a9eee709c6a05a2fad539"
94	y_str := "717a72348a182085109c8d3917d6c43d59b224dc6a7fc4f0483232fa6516d8b3"
95	share_p_str := "04ef3bd051bf78a2234ec0df197f7828060fe9856503579bb1733009042c15c0c1de127727f418b5966afadfdd95a6e4591d171056b333dab97a79c7193e341727"
96	share_v_str := "04c0f65da0d11927bdf5d560c69e1d7d939a05b0e88291887d679fcadea75810fb5cc1ca7494db39e82ff2f50665255d76173e09986ab46742c798a9a68437b048"
97	confirm_p_str := "926cc713504b9b4d76c9162ded04b5493e89109f6d89462cd33adc46fda27527"
98	confirm_v_str := "9747bcc4f8fe9f63defee53ac9b07876d907d55047e6ff2def2e7529089d3e68"
99	secret_str := "0c5f8ccd1413423a54f6c1fb26ff01534a87f893779c6e68666d772bfd91f3e7"
100
101	w0 := hexToBytes(w0_str)
102	w1 := hexToBytes(w1_str)
103	L := hexToBytes(L_str)
104	x := hexToBytes(x_str)
105	y := hexToBytes(y_str)
106
107	prover, err := newContext(
108		RoleProver, context, idProver, idVerifier,
109		w0, w1, nil, bytesToBigInt(x), nil,
110	)
111	if err != nil {
112		t.Fatalf("failed to create prover: %v", err)
113	}
114	verifier, err := newContext(
115		RoleVerifier, context, idProver, idVerifier,
116		w0, nil, L, nil, bytesToBigInt(y),
117	)
118	if err != nil {
119		t.Fatalf("failed to create verifier: %v", err)
120	}
121
122	proverShare, err := prover.GenerateProverShare()
123	if err != nil {
124		t.Fatalf("failed to generate prover share: %v", err)
125	}
126	expectedShareP := hexToBytes(share_p_str)
127	if !bytes.Equal(proverShare, expectedShareP) {
128		t.Fatalf("prover share mismatch:\n got  %x\nwant %x", proverShare, expectedShareP)
129	}
130
131	vShare, vConfirm, vSecret, err := verifier.ProcessProverShare(proverShare)
132	if err != nil {
133		t.Fatalf("verifier failed to process prover share: %v", err)
134	}
135	expectedShareV := hexToBytes(share_v_str)
136	if !bytes.Equal(vShare, expectedShareV) {
137		t.Fatalf("verifier share mismatch:\n got  %x\nwant %x", vShare, expectedShareV)
138	}
139	expectedConfirmV := hexToBytes(confirm_v_str)
140	if !bytes.Equal(vConfirm, expectedConfirmV) {
141		t.Fatalf("verifier confirm mismatch:\n got  %x\nwant %x", vConfirm, expectedConfirmV)
142	}
143
144	pConfirm, pSecret, err := prover.ComputeProverConfirmation(vShare, vConfirm)
145	if err != nil {
146		t.Fatalf("prover failed to compute confirmation: %v", err)
147	}
148	expectedConfirmP := hexToBytes(confirm_p_str)
149	if !bytes.Equal(pConfirm, expectedConfirmP) {
150		t.Fatalf("prover confirm mismatch:\n got  %x\nwant %x", pConfirm, expectedConfirmP)
151	}
152
153	if err := verifier.VerifyProverConfirmation(pConfirm); err != nil {
154		t.Fatalf("verifier failed to verify prover confirmation: %v", err)
155	}
156
157	if !bytes.Equal(pSecret, vSecret) {
158		t.Fatal("shared secrets do not match")
159	}
160	expectedSecret := hexToBytes(secret_str)
161	if !bytes.Equal(expectedSecret, vSecret) {
162		t.Fatalf("shared secret mismatch:\n got  %x\nwant %x", vSecret, expectedSecret)
163	}
164}
165
166func TestSPAKE2PlusMultipleRuns(t *testing.T) {
167	pw := []byte("password")
168	context := []byte("Repeated test")
169	idProver := []byte("client")
170	idVerifier := []byte("server")
171
172	for i := 0; i < 5; i++ {
173		pwVerifierW0, pwVerifierW1, registrationRecord, err := Register(
174			pw, idProver, idVerifier)
175		if err != nil {
176			t.Fatalf("registration failed: %v", err)
177		}
178		prover, err := NewProver(context, idProver, idVerifier, pwVerifierW0, pwVerifierW1)
179		if err != nil {
180			t.Fatalf("prover context creation failed: %v", err)
181		}
182		verifier, err := NewVerifier(context, idProver, idVerifier, pwVerifierW0, registrationRecord)
183		if err != nil {
184			t.Fatalf("verifier context creation failed: %v", err)
185		}
186
187		proverShare, err := prover.GenerateProverShare()
188		if err != nil {
189			t.Fatalf("prover share gen failed: %v", err)
190		}
191
192		vShare, vConfirm, vSecret, err := verifier.ProcessProverShare(proverShare)
193		if err != nil {
194			t.Fatalf("verifier process share failed: %v", err)
195		}
196
197		pConfirm, pSecret, err := prover.ComputeProverConfirmation(vShare, vConfirm)
198		if err != nil {
199			t.Fatalf("prover compute confirm failed: %v", err)
200		}
201
202		if err := verifier.VerifyProverConfirmation(pConfirm); err != nil {
203			t.Fatalf("verifier confirm failed: %v", err)
204		}
205
206		if !bytes.Equal(pSecret, vSecret) {
207			t.Fatalf("shared secrets differ")
208		}
209	}
210}
211
212func TestSPAKE2PlusWrongPassword(t *testing.T) {
213	correctPw := []byte("password")
214	wrongPw := []byte("wrongpassword")
215	context := []byte("Wrong password test")
216	idProver := []byte("client")
217	idVerifier := []byte("server")
218
219	// Register with the correct password
220	correctW0, _, registrationRecord, err := Register(
221		correctPw, idProver, idVerifier)
222	if err != nil {
223		t.Fatalf("registration failed: %v", err)
224	}
225
226	// Register with the wrong password
227	wrongW0, wrongW1, _, err := Register(
228		wrongPw, idProver, idVerifier)
229	if err != nil {
230		t.Fatalf("registration failed: %v", err)
231	}
232
233	// Create prover with wrong password verifiers
234	prover, err := NewProver(context, idProver, idVerifier, wrongW0, wrongW1)
235	if err != nil {
236		t.Fatalf("prover context creation failed: %v", err)
237	}
238
239	// Create verifier with correct password verifiers
240	verifier, err := NewVerifier(context, idProver, idVerifier, correctW0, registrationRecord)
241	if err != nil {
242		t.Fatalf("verifier context creation failed: %v", err)
243	}
244
245	proverShare, err := prover.GenerateProverShare()
246	if err != nil {
247		t.Fatalf("prover share gen failed: %v", err)
248	}
249
250	vShare, vConfirm, _, err := verifier.ProcessProverShare(proverShare)
251	if err != nil {
252		t.Fatalf("verifier process share failed: %v", err)
253	}
254
255	_, _, err = prover.ComputeProverConfirmation(vShare, vConfirm)
256	if err == nil {
257		t.Fatalf("expected error computing confirmation, got nil")
258	}
259}
260
261func bytesToBigInt(b []byte) *big.Int {
262	if len(b) == 0 {
263		return nil
264	}
265	return new(big.Int).SetBytes(b)
266}
267