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