sxs_validation_test.go raw

   1  //go:build !js
   2  
   3  package p256k1
   4  
   5  import (
   6  	"bytes"
   7  	"crypto/sha256"
   8  	"encoding/hex"
   9  	"fmt"
  10  	"testing"
  11  )
  12  
  13  // Side-by-Side (SxS) Validation Tests
  14  // These tests compare Go implementation results directly against libsecp256k1
  15  
  16  // TestSxSGeneratorPoint verifies the generator point G matches
  17  func TestSxSGeneratorPoint(t *testing.T) {
  18  	// Known generator point coordinates from secp256k1 spec
  19  	expectedGx := "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
  20  	expectedGy := "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"
  21  
  22  	var gx, gy [32]byte
  23  	gxFe := Generator.x
  24  	gyFe := Generator.y
  25  	gxFe.normalize()
  26  	gyFe.normalize()
  27  	gxFe.getB32(gx[:])
  28  	gyFe.getB32(gy[:])
  29  
  30  	if hex.EncodeToString(gx[:]) != expectedGx {
  31  		t.Errorf("Generator.x mismatch:\n  got:  %s\n  want: %s",
  32  			hex.EncodeToString(gx[:]), expectedGx)
  33  	}
  34  	if hex.EncodeToString(gy[:]) != expectedGy {
  35  		t.Errorf("Generator.y mismatch:\n  got:  %s\n  want: %s",
  36  			hex.EncodeToString(gy[:]), expectedGy)
  37  	}
  38  }
  39  
  40  // TestSxSScalarMultGenerator tests k*G for various scalars
  41  func TestSxSScalarMultGenerator(t *testing.T) {
  42  	lib, err := GetLibSecp256k1()
  43  	if err != nil {
  44  		t.Skipf("libsecp256k1 not available: %v", err)
  45  	}
  46  
  47  	testCases := []string{
  48  		"0000000000000000000000000000000000000000000000000000000000000001", // 1
  49  		"0000000000000000000000000000000000000000000000000000000000000002", // 2
  50  		"0000000000000000000000000000000000000000000000000000000000000003", // 3
  51  		"0000000000000000000000000000000000000000000000000000000000000007", // 7
  52  		"000000000000000000000000000000000000000000000000000000000000000f", // 15
  53  		"00000000000000000000000000000000000000000000000000000000000000ff", // 255
  54  		"000000000000000000000000000000000000000000000000000000000000ffff", // 65535
  55  		"e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35", // random
  56  		"deadbeefcafebabe1234567890abcdef0123456789abcdef0123456789abcdef", // random
  57  		"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", // n-1
  58  		"7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", // n/2
  59  		"5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72", // lambda
  60  	}
  61  
  62  	for _, scalarHex := range testCases {
  63  		t.Run(scalarHex[:8], func(t *testing.T) {
  64  			seckey, _ := hex.DecodeString(scalarHex)
  65  
  66  			// Get libsecp256k1 result (full compressed pubkey)
  67  			libPubkey, libParity, err := lib.CreatePubkeyCompressed(seckey)
  68  			if err != nil {
  69  				t.Skipf("libsecp256k1 rejected key: %v", err)
  70  			}
  71  
  72  			// Parse scalar
  73  			var s Scalar
  74  			s.setB32(seckey)
  75  
  76  			// Compute using each Go implementation
  77  			implementations := []struct {
  78  				name string
  79  				fn   func(*GroupElementJacobian, *Scalar)
  80  			}{
  81  				{"EcmultConst", func(r *GroupElementJacobian, s *Scalar) { EcmultConst(r, &Generator, s) }},
  82  				{"ecmultWindowedVar", func(r *GroupElementJacobian, s *Scalar) { ecmultWindowedVar(r, &Generator, s) }},
  83  				{"ecmultGenGLV", func(r *GroupElementJacobian, s *Scalar) { ecmultGenGLV(r, s) }},
  84  				{"ecmultStraussWNAFGLV", func(r *GroupElementJacobian, s *Scalar) { ecmultStraussWNAFGLV(r, &Generator, s) }},
  85  			}
  86  
  87  			for _, impl := range implementations {
  88  				t.Run(impl.name, func(t *testing.T) {
  89  					var result GroupElementJacobian
  90  					impl.fn(&result, &s)
  91  
  92  					var aff GroupElementAffine
  93  					aff.setGEJ(&result)
  94  					aff.x.normalize()
  95  					aff.y.normalize()
  96  
  97  					// Serialize to compressed format
  98  					var goCompressed [33]byte
  99  					var xBytes, yBytes [32]byte
 100  					aff.x.getB32(xBytes[:])
 101  					aff.y.getB32(yBytes[:])
 102  
 103  					if yBytes[31]&1 == 0 {
 104  						goCompressed[0] = 0x02
 105  					} else {
 106  						goCompressed[0] = 0x03
 107  					}
 108  					copy(goCompressed[1:], xBytes[:])
 109  
 110  					// Compare
 111  					if !bytes.Equal(goCompressed[:], libPubkey) {
 112  						t.Errorf("Mismatch:\n  Go:  %s\n  Lib: %s",
 113  							hex.EncodeToString(goCompressed[:]),
 114  							hex.EncodeToString(libPubkey))
 115  					}
 116  
 117  					// Also verify parity
 118  					goParity := 0
 119  					if yBytes[31]&1 == 1 {
 120  						goParity = 1
 121  					}
 122  					if goParity != libParity {
 123  						t.Errorf("Parity mismatch: Go=%d, Lib=%d", goParity, libParity)
 124  					}
 125  				})
 126  			}
 127  		})
 128  	}
 129  }
 130  
 131  // TestSxSFullPubkey tests full X,Y coordinates match
 132  func TestSxSFullPubkey(t *testing.T) {
 133  	lib, err := GetLibSecp256k1()
 134  	if err != nil {
 135  		t.Skipf("libsecp256k1 not available: %v", err)
 136  	}
 137  
 138  	testCases := []string{
 139  		"0000000000000000000000000000000000000000000000000000000000000001",
 140  		"0000000000000000000000000000000000000000000000000000000000000002",
 141  		"e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35",
 142  	}
 143  
 144  	for _, scalarHex := range testCases {
 145  		t.Run(scalarHex[:8], func(t *testing.T) {
 146  			seckey, _ := hex.DecodeString(scalarHex)
 147  
 148  			// Get libsecp256k1 uncompressed pubkey (65 bytes: 0x04 || X || Y)
 149  			libPubkey, err := lib.CreatePubkeyUncompressed(seckey)
 150  			if err != nil {
 151  				t.Fatalf("libsecp256k1 failed: %v", err)
 152  			}
 153  
 154  			if len(libPubkey) != 65 || libPubkey[0] != 0x04 {
 155  				t.Fatalf("Invalid uncompressed pubkey format")
 156  			}
 157  
 158  			libX := libPubkey[1:33]
 159  			libY := libPubkey[33:65]
 160  
 161  			// Compute using Go
 162  			var s Scalar
 163  			s.setB32(seckey)
 164  
 165  			var result GroupElementJacobian
 166  			EcmultConst(&result, &Generator, &s)
 167  
 168  			var aff GroupElementAffine
 169  			aff.setGEJ(&result)
 170  			aff.x.normalize()
 171  			aff.y.normalize()
 172  
 173  			var goX, goY [32]byte
 174  			aff.x.getB32(goX[:])
 175  			aff.y.getB32(goY[:])
 176  
 177  			t.Logf("Scalar: %s", scalarHex)
 178  			t.Logf("Lib X:  %s", hex.EncodeToString(libX))
 179  			t.Logf("Go  X:  %s", hex.EncodeToString(goX[:]))
 180  			t.Logf("Lib Y:  %s", hex.EncodeToString(libY))
 181  			t.Logf("Go  Y:  %s", hex.EncodeToString(goY[:]))
 182  
 183  			if !bytes.Equal(goX[:], libX) {
 184  				t.Errorf("X coordinate mismatch")
 185  			}
 186  			if !bytes.Equal(goY[:], libY) {
 187  				t.Errorf("Y coordinate mismatch")
 188  			}
 189  		})
 190  	}
 191  }
 192  
 193  // TestSxSArbitraryPointMult tests k*P for arbitrary points P
 194  func TestSxSArbitraryPointMult(t *testing.T) {
 195  	lib, err := GetLibSecp256k1()
 196  	if err != nil {
 197  		t.Skipf("libsecp256k1 not available: %v", err)
 198  	}
 199  
 200  	// Generate a base point P = baseScalar * G
 201  	baseScalars := []string{
 202  		"0000000000000000000000000000000000000000000000000000000000000007",
 203  		"e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35",
 204  	}
 205  
 206  	multScalars := []string{
 207  		"0000000000000000000000000000000000000000000000000000000000000002",
 208  		"0000000000000000000000000000000000000000000000000000000000000003",
 209  		"deadbeefcafebabe1234567890abcdef0123456789abcdef0123456789abcdef",
 210  	}
 211  
 212  	for _, baseHex := range baseScalars {
 213  		for _, multHex := range multScalars {
 214  			name := fmt.Sprintf("base_%s_mult_%s", baseHex[:4], multHex[:4])
 215  			t.Run(name, func(t *testing.T) {
 216  				baseBytes, _ := hex.DecodeString(baseHex)
 217  				multBytes, _ := hex.DecodeString(multHex)
 218  
 219  				// Compute P = baseScalar * G using Go
 220  				var baseScalar Scalar
 221  				baseScalar.setB32(baseBytes)
 222  
 223  				var pJac GroupElementJacobian
 224  				EcmultConst(&pJac, &Generator, &baseScalar)
 225  				var p GroupElementAffine
 226  				p.setGEJ(&pJac)
 227  				p.x.normalize()
 228  				p.y.normalize()
 229  
 230  				// Serialize P as compressed for libsecp256k1
 231  				pCompressed := make([]byte, 33)
 232  				var pX [32]byte
 233  				var pY [32]byte
 234  				p.x.getB32(pX[:])
 235  				p.y.getB32(pY[:])
 236  				if pY[31]&1 == 0 {
 237  					pCompressed[0] = 0x02
 238  				} else {
 239  					pCompressed[0] = 0x03
 240  				}
 241  				copy(pCompressed[1:], pX[:])
 242  
 243  				// Compute multScalar * P using libsecp256k1 via ECDH
 244  				// ECDH(seckey, pubkey) computes seckey * pubkey
 245  				libResult, err := lib.ECDH(multBytes, pCompressed)
 246  				if err != nil {
 247  					t.Fatalf("libsecp256k1 ECDH failed: %v", err)
 248  				}
 249  
 250  				// Compute multScalar * P using Go implementations
 251  				var multScalar Scalar
 252  				multScalar.setB32(multBytes)
 253  
 254  				implementations := []struct {
 255  					name string
 256  					fn   func(*GroupElementJacobian, *GroupElementAffine, *Scalar)
 257  				}{
 258  					{"EcmultConst", func(r *GroupElementJacobian, a *GroupElementAffine, s *Scalar) { EcmultConst(r, a, s) }},
 259  					{"ecmultWindowedVar", func(r *GroupElementJacobian, a *GroupElementAffine, s *Scalar) { ecmultWindowedVar(r, a, s) }},
 260  					{"ecmultStraussWNAFGLV", func(r *GroupElementJacobian, a *GroupElementAffine, s *Scalar) { ecmultStraussWNAFGLV(r, a, s) }},
 261  				}
 262  
 263  				for _, impl := range implementations {
 264  					t.Run(impl.name, func(t *testing.T) {
 265  						var result GroupElementJacobian
 266  						impl.fn(&result, &p, &multScalar)
 267  
 268  						var resultAff GroupElementAffine
 269  						resultAff.setGEJ(&result)
 270  						resultAff.x.normalize()
 271  						resultAff.y.normalize()
 272  
 273  						// libsecp256k1 ECDH returns sha256(compressed_point)
 274  						// so we need to hash our result the same way
 275  						var goCompressed [33]byte
 276  						var resultX, resultY [32]byte
 277  						resultAff.x.getB32(resultX[:])
 278  						resultAff.y.getB32(resultY[:])
 279  
 280  						if resultY[31]&1 == 0 {
 281  							goCompressed[0] = 0x02
 282  						} else {
 283  							goCompressed[0] = 0x03
 284  						}
 285  						copy(goCompressed[1:], resultX[:])
 286  
 287  						goHash := sha256Sum(goCompressed[:])
 288  
 289  						if !bytes.Equal(goHash[:], libResult) {
 290  							t.Errorf("Mismatch:\n  Go hash:  %s\n  Lib hash: %s\n  Go point: %s",
 291  								hex.EncodeToString(goHash[:]),
 292  								hex.EncodeToString(libResult),
 293  								hex.EncodeToString(goCompressed[:]))
 294  						}
 295  					})
 296  				}
 297  			})
 298  		}
 299  	}
 300  }
 301  
 302  // sha256Sum computes SHA256 hash
 303  func sha256Sum(data []byte) [32]byte {
 304  	return sha256.Sum256(data)
 305  }
 306  
 307  // TestSxSGLVConstants verifies GLV constants match libsecp256k1
 308  func TestSxSGLVConstants(t *testing.T) {
 309  	// These are the known GLV constants from libsecp256k1
 310  	// Lambda: cube root of unity mod n
 311  	expectedLambda := "5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72"
 312  	// Beta: cube root of unity mod p
 313  	expectedBeta := "7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee"
 314  
 315  	var lambdaBytes [32]byte
 316  	scalarLambda.getB32(lambdaBytes[:])
 317  	if hex.EncodeToString(lambdaBytes[:]) != expectedLambda {
 318  		t.Errorf("Lambda mismatch:\n  got:  %s\n  want: %s",
 319  			hex.EncodeToString(lambdaBytes[:]), expectedLambda)
 320  	}
 321  
 322  	var betaBytes [32]byte
 323  	fieldBeta.getB32(betaBytes[:])
 324  	if hex.EncodeToString(betaBytes[:]) != expectedBeta {
 325  		t.Errorf("Beta mismatch:\n  got:  %s\n  want: %s",
 326  			hex.EncodeToString(betaBytes[:]), expectedBeta)
 327  	}
 328  
 329  	// Verify lambda^3 = 1 (mod n)
 330  	var lambda2, lambda3 Scalar
 331  	lambda2.mul(&scalarLambda, &scalarLambda)
 332  	lambda3.mul(&lambda2, &scalarLambda)
 333  	if !lambda3.equal(&ScalarOne) {
 334  		t.Error("lambda^3 != 1 (mod n)")
 335  	}
 336  
 337  	// Verify beta^3 = 1 (mod p)
 338  	var beta2, beta3 FieldElement
 339  	beta2.sqr(&fieldBeta)
 340  	beta3.mul(&beta2, &fieldBeta)
 341  	beta3.normalize()
 342  	if !beta3.equal(&FieldElementOne) {
 343  		t.Error("beta^3 != 1 (mod p)")
 344  	}
 345  }
 346  
 347  // TestSxSEndomorphism verifies λ*P = (β*x, y)
 348  func TestSxSEndomorphism(t *testing.T) {
 349  	lib, err := GetLibSecp256k1()
 350  	if err != nil {
 351  		t.Skipf("libsecp256k1 not available: %v", err)
 352  	}
 353  
 354  	testCases := []string{
 355  		"0000000000000000000000000000000000000000000000000000000000000001",
 356  		"0000000000000000000000000000000000000000000000000000000000000002",
 357  		"e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35",
 358  	}
 359  
 360  	for _, scalarHex := range testCases {
 361  		t.Run(scalarHex[:8], func(t *testing.T) {
 362  			seckey, _ := hex.DecodeString(scalarHex)
 363  
 364  			// Compute P = k * G
 365  			var s Scalar
 366  			s.setB32(seckey)
 367  
 368  			var pJac GroupElementJacobian
 369  			EcmultConst(&pJac, &Generator, &s)
 370  			var p GroupElementAffine
 371  			p.setGEJ(&pJac)
 372  			p.x.normalize()
 373  			p.y.normalize()
 374  
 375  			// Compute λ*P using scalar multiplication
 376  			var lambdaPJac GroupElementJacobian
 377  			EcmultConst(&lambdaPJac, &p, &scalarLambda)
 378  			var lambdaP GroupElementAffine
 379  			lambdaP.setGEJ(&lambdaPJac)
 380  			lambdaP.x.normalize()
 381  			lambdaP.y.normalize()
 382  
 383  			// Compute λ*P using endomorphism (β*x, y)
 384  			var endoP GroupElementAffine
 385  			endoP.mulLambda(&p)
 386  			endoP.x.normalize()
 387  			endoP.y.normalize()
 388  
 389  			// They should match
 390  			var lambdaPX, lambdaPY, endoPX, endoPY [32]byte
 391  			lambdaP.x.getB32(lambdaPX[:])
 392  			lambdaP.y.getB32(lambdaPY[:])
 393  			endoP.x.getB32(endoPX[:])
 394  			endoP.y.getB32(endoPY[:])
 395  
 396  			if !bytes.Equal(lambdaPX[:], endoPX[:]) {
 397  				t.Errorf("X mismatch:\n  scalar mult: %s\n  endomorphism: %s",
 398  					hex.EncodeToString(lambdaPX[:]), hex.EncodeToString(endoPX[:]))
 399  			}
 400  			if !bytes.Equal(lambdaPY[:], endoPY[:]) {
 401  				t.Errorf("Y mismatch:\n  scalar mult: %s\n  endomorphism: %s",
 402  					hex.EncodeToString(lambdaPY[:]), hex.EncodeToString(endoPY[:]))
 403  			}
 404  
 405  			// Also verify against libsecp256k1
 406  			// Compute (k * lambda) * G via libsecp256k1
 407  			var kLambda Scalar
 408  			kLambda.mul(&s, &scalarLambda)
 409  			var kLambdaBytes [32]byte
 410  			kLambda.getB32(kLambdaBytes[:])
 411  
 412  			libPubkey, err := lib.CreatePubkeyUncompressed(kLambdaBytes[:])
 413  			if err != nil {
 414  				t.Fatalf("libsecp256k1 failed: %v", err)
 415  			}
 416  
 417  			libX := libPubkey[1:33]
 418  			libY := libPubkey[33:65]
 419  
 420  			if !bytes.Equal(lambdaPX[:], libX) {
 421  				t.Errorf("X vs libsecp256k1 mismatch:\n  Go:  %s\n  Lib: %s",
 422  					hex.EncodeToString(lambdaPX[:]), hex.EncodeToString(libX))
 423  			}
 424  			if !bytes.Equal(lambdaPY[:], libY) {
 425  				t.Errorf("Y vs libsecp256k1 mismatch:\n  Go:  %s\n  Lib: %s",
 426  					hex.EncodeToString(lambdaPY[:]), hex.EncodeToString(libY))
 427  			}
 428  		})
 429  	}
 430  }
 431  
 432  // TestSxSScalarSplit validates the GLV scalar splitting
 433  func TestSxSScalarSplit(t *testing.T) {
 434  	lib, err := GetLibSecp256k1()
 435  	if err != nil {
 436  		t.Skipf("libsecp256k1 not available: %v", err)
 437  	}
 438  
 439  	testCases := []string{
 440  		"0000000000000000000000000000000000000000000000000000000000000001",
 441  		"0000000000000000000000000000000000000000000000000000000000000002",
 442  		"00000000000000000000000000000000ffffffffffffffffffffffffffffffff",
 443  		"ffffffffffffffffffffffffffffffff00000000000000000000000000000000",
 444  		"e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35",
 445  		"deadbeefcafebabe1234567890abcdef0123456789abcdef0123456789abcdef",
 446  		"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", // n-1
 447  	}
 448  
 449  	for _, scalarHex := range testCases {
 450  		t.Run(scalarHex[:8], func(t *testing.T) {
 451  			kBytes, _ := hex.DecodeString(scalarHex)
 452  
 453  			var k Scalar
 454  			k.setB32(kBytes)
 455  
 456  			// Split k into k1, k2
 457  			var k1, k2 Scalar
 458  			scalarSplitLambda(&k1, &k2, &k)
 459  
 460  			// Log the split
 461  			var k1Bytes, k2Bytes [32]byte
 462  			k1.getB32(k1Bytes[:])
 463  			k2.getB32(k2Bytes[:])
 464  			t.Logf("k  = %s", scalarHex)
 465  			t.Logf("k1 = %s", hex.EncodeToString(k1Bytes[:]))
 466  			t.Logf("k2 = %s", hex.EncodeToString(k2Bytes[:]))
 467  
 468  			// Verify k1 + k2*lambda = k
 469  			var k2Lambda, reconstructed Scalar
 470  			k2Lambda.mul(&k2, &scalarLambda)
 471  			reconstructed.add(&k1, &k2Lambda)
 472  
 473  			if !reconstructed.equal(&k) {
 474  				var recBytes [32]byte
 475  				reconstructed.getB32(recBytes[:])
 476  				t.Errorf("Scalar reconstruction failed:\n  k1+k2*lambda = %s\n  k            = %s",
 477  					hex.EncodeToString(recBytes[:]), scalarHex)
 478  			}
 479  
 480  			// Verify k1*G + k2*(lambda*G) = k*G via libsecp256k1
 481  			expectedPubkey, err := lib.CreatePubkeyUncompressed(kBytes)
 482  			if err != nil {
 483  				t.Skipf("libsecp256k1 rejected key")
 484  			}
 485  
 486  			// Compute k1*G
 487  			var k1G GroupElementJacobian
 488  			EcmultConst(&k1G, &Generator, &k1)
 489  
 490  			// Compute lambda*G
 491  			var lambdaG GroupElementAffine
 492  			lambdaG.mulLambda(&Generator)
 493  
 494  			// Compute k2*(lambda*G)
 495  			var k2LambdaG GroupElementJacobian
 496  			EcmultConst(&k2LambdaG, &lambdaG, &k2)
 497  
 498  			// Add k1*G + k2*(lambda*G)
 499  			var sum GroupElementJacobian
 500  			sum.addVar(&k1G, &k2LambdaG)
 501  
 502  			var sumAff GroupElementAffine
 503  			sumAff.setGEJ(&sum)
 504  			sumAff.x.normalize()
 505  			sumAff.y.normalize()
 506  
 507  			var sumX, sumY [32]byte
 508  			sumAff.x.getB32(sumX[:])
 509  			sumAff.y.getB32(sumY[:])
 510  
 511  			expectedX := expectedPubkey[1:33]
 512  			expectedY := expectedPubkey[33:65]
 513  
 514  			if !bytes.Equal(sumX[:], expectedX) {
 515  				t.Errorf("X mismatch:\n  k1*G + k2*(λ*G) = %s\n  k*G (lib)       = %s",
 516  					hex.EncodeToString(sumX[:]), hex.EncodeToString(expectedX))
 517  			}
 518  			if !bytes.Equal(sumY[:], expectedY) {
 519  				t.Errorf("Y mismatch:\n  k1*G + k2*(λ*G) = %s\n  k*G (lib)       = %s",
 520  					hex.EncodeToString(sumY[:]), hex.EncodeToString(expectedY))
 521  			}
 522  		})
 523  	}
 524  }
 525  
 526  // TestSxSRandomScalars runs extensive random scalar tests
 527  func TestSxSRandomScalars(t *testing.T) {
 528  	lib, err := GetLibSecp256k1()
 529  	if err != nil {
 530  		t.Skipf("libsecp256k1 not available: %v", err)
 531  	}
 532  
 533  	seed := uint64(0xdeadbeef12345678)
 534  	const numTests = 500
 535  
 536  	var failures int
 537  	for i := 0; i < numTests; i++ {
 538  		// Generate pseudo-random scalar
 539  		var seckey [32]byte
 540  		for j := 0; j < 32; j++ {
 541  			seed = seed*6364136223846793005 + 1442695040888963407
 542  			seckey[j] = byte(seed >> 56)
 543  		}
 544  
 545  		// Get libsecp256k1 result
 546  		libPubkey, _, err := lib.CreatePubkeyCompressed(seckey[:])
 547  		if err != nil {
 548  			continue // Skip invalid keys
 549  		}
 550  
 551  		var s Scalar
 552  		s.setB32(seckey[:])
 553  
 554  		// Test each implementation
 555  		implementations := []struct {
 556  			name string
 557  			fn   func(*GroupElementJacobian, *Scalar)
 558  		}{
 559  			{"EcmultConst", func(r *GroupElementJacobian, s *Scalar) { EcmultConst(r, &Generator, s) }},
 560  			{"ecmultWindowedVar", func(r *GroupElementJacobian, s *Scalar) { ecmultWindowedVar(r, &Generator, s) }},
 561  			{"ecmultGenGLV", func(r *GroupElementJacobian, s *Scalar) { ecmultGenGLV(r, s) }},
 562  			{"ecmultStraussWNAFGLV", func(r *GroupElementJacobian, s *Scalar) { ecmultStraussWNAFGLV(r, &Generator, s) }},
 563  		}
 564  
 565  		for _, impl := range implementations {
 566  			var result GroupElementJacobian
 567  			impl.fn(&result, &s)
 568  
 569  			var aff GroupElementAffine
 570  			aff.setGEJ(&result)
 571  			aff.x.normalize()
 572  			aff.y.normalize()
 573  
 574  			var goCompressed [33]byte
 575  			var xBytes, yBytes [32]byte
 576  			aff.x.getB32(xBytes[:])
 577  			aff.y.getB32(yBytes[:])
 578  
 579  			if yBytes[31]&1 == 0 {
 580  				goCompressed[0] = 0x02
 581  			} else {
 582  				goCompressed[0] = 0x03
 583  			}
 584  			copy(goCompressed[1:], xBytes[:])
 585  
 586  			if !bytes.Equal(goCompressed[:], libPubkey) {
 587  				t.Errorf("Test %d %s mismatch:\n  scalar: %s\n  Go:     %s\n  Lib:    %s",
 588  					i, impl.name, hex.EncodeToString(seckey[:]),
 589  					hex.EncodeToString(goCompressed[:]),
 590  					hex.EncodeToString(libPubkey))
 591  				failures++
 592  				if failures > 10 {
 593  					t.Fatalf("Too many failures, stopping")
 594  				}
 595  			}
 596  		}
 597  	}
 598  
 599  	t.Logf("Tested %d random scalars with 4 implementations each (%d total operations)", numTests, numTests*4)
 600  }
 601  
 602  // TestSxSECDSASignVerify tests ECDSA signature creation and verification
 603  func TestSxSECDSASignVerify(t *testing.T) {
 604  	lib, err := GetLibSecp256k1()
 605  	if err != nil {
 606  		t.Skipf("libsecp256k1 not available: %v", err)
 607  	}
 608  
 609  	testCases := []struct {
 610  		name   string
 611  		seckey string
 612  		msg    string
 613  	}{
 614  		{"simple", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"},
 615  		{"random", "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35", "deadbeefcafebabe1234567890abcdef0123456789abcdef0123456789abcdef"},
 616  		{"msg_all_ff", "0000000000000000000000000000000000000000000000000000000000000002", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
 617  		{"high_scalar", "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"},
 618  	}
 619  
 620  	for _, tc := range testCases {
 621  		t.Run(tc.name, func(t *testing.T) {
 622  			seckey, _ := hex.DecodeString(tc.seckey)
 623  			msg, _ := hex.DecodeString(tc.msg)
 624  
 625  			// Get compressed pubkey from libsecp256k1
 626  			pubkeyCompressed, _, err := lib.CreatePubkeyCompressed(seckey)
 627  			if err != nil {
 628  				t.Skipf("libsecp256k1 rejected key: %v", err)
 629  			}
 630  
 631  			// 1. Sign with libsecp256k1, verify with libsecp256k1
 632  			libSig, err := lib.ECDSASign(msg, seckey)
 633  			if err != nil {
 634  				t.Fatalf("lib ECDSASign failed: %v", err)
 635  			}
 636  			if !lib.ECDSAVerify(libSig, msg, pubkeyCompressed) {
 637  				t.Error("lib failed to verify its own signature")
 638  			}
 639  
 640  			// 2. Sign with libsecp256k1, verify with Go
 641  			// Parse the pubkey for Go
 642  			var pubkey PublicKey
 643  			if err := ECPubkeyParse(&pubkey, pubkeyCompressed); err != nil {
 644  				t.Fatalf("ECPubkeyParse failed: %v", err)
 645  			}
 646  
 647  			var goSig ECDSASignature
 648  			if err := goSig.FromCompact((*ECDSASignatureCompact)(libSig)); err != nil {
 649  				t.Fatalf("FromCompact failed: %v", err)
 650  			}
 651  			if !ECDSAVerify(&goSig, msg, &pubkey) {
 652  				t.Error("Go failed to verify libsecp256k1 signature")
 653  			}
 654  
 655  			// 3. Sign with Go, verify with libsecp256k1
 656  			var goGenSig ECDSASignature
 657  			if err := ECDSASign(&goGenSig, msg, seckey); err != nil {
 658  				t.Fatalf("Go ECDSASign failed: %v", err)
 659  			}
 660  			goCompact := goGenSig.ToCompact()
 661  			if !lib.ECDSAVerify(goCompact[:], msg, pubkeyCompressed) {
 662  				t.Errorf("libsecp256k1 failed to verify Go signature")
 663  			}
 664  
 665  			// 4. Sign with Go, verify with Go
 666  			if !ECDSAVerify(&goGenSig, msg, &pubkey) {
 667  				t.Error("Go failed to verify its own signature")
 668  			}
 669  
 670  			// 5. Test DER encoding round-trip
 671  			t.Run("DER", func(t *testing.T) {
 672  				// Get DER from libsecp256k1
 673  				libDER, err := lib.ECDSASignDER(msg, seckey)
 674  				if err != nil {
 675  					t.Fatalf("lib ECDSASignDER failed: %v", err)
 676  				}
 677  
 678  				// Verify with libsecp256k1
 679  				if !lib.ECDSAVerifyDER(libDER, msg, pubkeyCompressed) {
 680  					t.Error("lib failed to verify its own DER signature")
 681  				}
 682  
 683  				// Parse with Go
 684  				var derSig ECDSASignature
 685  				if err := derSig.ParseDER(libDER); err != nil {
 686  					t.Fatalf("Go ParseDER failed: %v", err)
 687  				}
 688  				if !ECDSAVerify(&derSig, msg, &pubkey) {
 689  					t.Error("Go failed to verify lib DER signature")
 690  				}
 691  
 692  				// Sign DER with Go
 693  				goDER, err := ECDSASignDER(msg, seckey)
 694  				if err != nil {
 695  					t.Fatalf("Go ECDSASignDER failed: %v", err)
 696  				}
 697  
 698  				// Verify Go DER with libsecp256k1
 699  				if !lib.ECDSAVerifyDER(goDER, msg, pubkeyCompressed) {
 700  					t.Errorf("lib failed to verify Go DER signature:\n  Go:  %s\n  Lib: %s",
 701  						hex.EncodeToString(goDER), hex.EncodeToString(libDER))
 702  				}
 703  			})
 704  		})
 705  	}
 706  }
 707  
 708  // TestSxSECDSARandomMessages tests ECDSA with random messages
 709  func TestSxSECDSARandomMessages(t *testing.T) {
 710  	lib, err := GetLibSecp256k1()
 711  	if err != nil {
 712  		t.Skipf("libsecp256k1 not available: %v", err)
 713  	}
 714  
 715  	// Fixed secret key
 716  	seckey, _ := hex.DecodeString("e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35")
 717  	pubkeyCompressed, _, err := lib.CreatePubkeyCompressed(seckey)
 718  	if err != nil {
 719  		t.Fatalf("CreatePubkeyCompressed failed: %v", err)
 720  	}
 721  	var pubkey PublicKey
 722  	if err := ECPubkeyParse(&pubkey, pubkeyCompressed); err != nil {
 723  		t.Fatalf("ECPubkeyParse failed: %v", err)
 724  	}
 725  
 726  	seed := uint64(0xdeadbeef12345678)
 727  	const numTests = 100
 728  
 729  	for i := 0; i < numTests; i++ {
 730  		// Generate random message
 731  		var msg [32]byte
 732  		for j := 0; j < 32; j++ {
 733  			seed = seed*6364136223846793005 + 1442695040888963407
 734  			msg[j] = byte(seed >> 56)
 735  		}
 736  
 737  		// Sign with Go
 738  		var goSig ECDSASignature
 739  		if err := ECDSASign(&goSig, msg[:], seckey); err != nil {
 740  			t.Fatalf("Test %d: ECDSASign failed: %v", i, err)
 741  		}
 742  
 743  		// Verify with libsecp256k1
 744  		goCompact := goSig.ToCompact()
 745  		if !lib.ECDSAVerify(goCompact[:], msg[:], pubkeyCompressed) {
 746  			t.Errorf("Test %d: libsecp256k1 failed to verify Go signature", i)
 747  		}
 748  
 749  		// Verify with Go
 750  		if !ECDSAVerify(&goSig, msg[:], &pubkey) {
 751  			t.Errorf("Test %d: Go failed to verify its own signature", i)
 752  		}
 753  	}
 754  
 755  	t.Logf("Tested %d random messages", numTests)
 756  }
 757  
 758  // TestSxSECDSARecover tests public key recovery from ECDSA signatures
 759  func TestSxSECDSARecover(t *testing.T) {
 760  	testCases := []struct {
 761  		name   string
 762  		seckey string
 763  		msg    string
 764  	}{
 765  		{"simple", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"},
 766  		{"random", "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35", "deadbeefcafebabe1234567890abcdef0123456789abcdef0123456789abcdef"},
 767  	}
 768  
 769  	for _, tc := range testCases {
 770  		t.Run(tc.name, func(t *testing.T) {
 771  			seckey, _ := hex.DecodeString(tc.seckey)
 772  			msg, _ := hex.DecodeString(tc.msg)
 773  
 774  			// Create public key from secret key
 775  			var originalPubkey PublicKey
 776  			if err := ECPubkeyCreate(&originalPubkey, seckey); err != nil {
 777  				t.Fatalf("ECPubkeyCreate failed: %v", err)
 778  			}
 779  
 780  			// Sign with recovery
 781  			var sig ECDSARecoverableSignature
 782  			if err := ECDSASignRecoverable(&sig, msg, seckey); err != nil {
 783  				t.Fatalf("ECDSASignRecoverable failed: %v", err)
 784  			}
 785  
 786  			t.Logf("Recovery ID: %d", sig.recid)
 787  
 788  			// Recover public key
 789  			var recoveredPubkey PublicKey
 790  			if err := ECDSARecover(&recoveredPubkey, &sig, msg); err != nil {
 791  				t.Fatalf("ECDSARecover failed: %v", err)
 792  			}
 793  
 794  			// Compare public keys
 795  			if ECPubkeyCmp(&originalPubkey, &recoveredPubkey) != 0 {
 796  				// Serialize both for comparison
 797  				original := make([]byte, 33)
 798  				recovered := make([]byte, 33)
 799  				ECPubkeySerialize(original, &originalPubkey, 258) // compressed
 800  				ECPubkeySerialize(recovered, &recoveredPubkey, 258)
 801  				t.Errorf("Public key mismatch:\n  original:  %s\n  recovered: %s",
 802  					hex.EncodeToString(original), hex.EncodeToString(recovered))
 803  			}
 804  
 805  			// Also verify the signature
 806  			compact, recid := sig.ToCompact()
 807  			t.Logf("Compact signature: %s (recid=%d)", hex.EncodeToString(compact), recid)
 808  
 809  			var verifySig ECDSASignature
 810  			verifySig.r = sig.r
 811  			verifySig.s = sig.s
 812  			if !ECDSAVerify(&verifySig, msg, &originalPubkey) {
 813  				t.Error("Signature verification failed")
 814  			}
 815  		})
 816  	}
 817  }
 818  
 819  // TestSxSECDSARecoverRandom tests recovery with random keys
 820  func TestSxSECDSARecoverRandom(t *testing.T) {
 821  	seed := uint64(0xdeadbeef12345678)
 822  	const numTests = 50
 823  
 824  	for i := 0; i < numTests; i++ {
 825  		// Generate random secret key
 826  		var seckey [32]byte
 827  		for j := 0; j < 32; j++ {
 828  			seed = seed*6364136223846793005 + 1442695040888963407
 829  			seckey[j] = byte(seed >> 56)
 830  		}
 831  
 832  		// Create public key
 833  		var originalPubkey PublicKey
 834  		if err := ECPubkeyCreate(&originalPubkey, seckey[:]); err != nil {
 835  			continue // Skip invalid keys
 836  		}
 837  
 838  		// Generate random message
 839  		var msg [32]byte
 840  		for j := 0; j < 32; j++ {
 841  			seed = seed*6364136223846793005 + 1442695040888963407
 842  			msg[j] = byte(seed >> 56)
 843  		}
 844  
 845  		// Sign with recovery
 846  		var sig ECDSARecoverableSignature
 847  		if err := ECDSASignRecoverable(&sig, msg[:], seckey[:]); err != nil {
 848  			t.Fatalf("Test %d: ECDSASignRecoverable failed: %v", i, err)
 849  		}
 850  
 851  		// Recover public key
 852  		var recoveredPubkey PublicKey
 853  		if err := ECDSARecover(&recoveredPubkey, &sig, msg[:]); err != nil {
 854  			t.Fatalf("Test %d: ECDSARecover failed: %v", i, err)
 855  		}
 856  
 857  		// Compare
 858  		if ECPubkeyCmp(&originalPubkey, &recoveredPubkey) != 0 {
 859  			t.Errorf("Test %d: Public key mismatch", i)
 860  		}
 861  	}
 862  
 863  	t.Logf("Tested %d random key/message pairs", numTests)
 864  }
 865  
 866  // TestSxSECDSALowS verifies signatures have low-S values
 867  func TestSxSECDSALowS(t *testing.T) {
 868  	lib, err := GetLibSecp256k1()
 869  	if err != nil {
 870  		t.Skipf("libsecp256k1 not available: %v", err)
 871  	}
 872  
 873  	seckey, _ := hex.DecodeString("e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35")
 874  
 875  	seed := uint64(0xcafebabe)
 876  	for i := 0; i < 50; i++ {
 877  		var msg [32]byte
 878  		for j := 0; j < 32; j++ {
 879  			seed = seed*6364136223846793005 + 1442695040888963407
 880  			msg[j] = byte(seed >> 56)
 881  		}
 882  
 883  		// Sign with Go
 884  		var goSig ECDSASignature
 885  		if err := ECDSASign(&goSig, msg[:], seckey); err != nil {
 886  			t.Fatalf("ECDSASign failed: %v", err)
 887  		}
 888  
 889  		// Verify it's low-S
 890  		if !goSig.IsLowS() {
 891  			t.Errorf("Test %d: Go signature has high-S", i)
 892  		}
 893  
 894  		// Sign with libsecp256k1
 895  		libSig, err := lib.ECDSASign(msg[:], seckey)
 896  		if err != nil {
 897  			t.Fatalf("lib ECDSASign failed: %v", err)
 898  		}
 899  
 900  		var libGoSig ECDSASignature
 901  		libGoSig.FromCompact((*ECDSASignatureCompact)(libSig))
 902  		if !libGoSig.IsLowS() {
 903  			t.Errorf("Test %d: lib signature has high-S", i)
 904  		}
 905  	}
 906  }
 907  
 908  // TestSxSSchnorrSignVerify tests Schnorr signature creation and verification
 909  func TestSxSSchnorrSignVerify(t *testing.T) {
 910  	lib, err := GetLibSecp256k1()
 911  	if err != nil {
 912  		t.Skipf("libsecp256k1 not available: %v", err)
 913  	}
 914  
 915  	testCases := []struct {
 916  		name   string
 917  		seckey string
 918  		msg    string
 919  	}{
 920  		{"simple", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"},
 921  		{"random", "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35", "deadbeefcafebabe1234567890abcdef0123456789abcdef0123456789abcdef"},
 922  		{"msg_all_ff", "0000000000000000000000000000000000000000000000000000000000000002", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
 923  	}
 924  
 925  	for _, tc := range testCases {
 926  		t.Run(tc.name, func(t *testing.T) {
 927  			seckey, _ := hex.DecodeString(tc.seckey)
 928  			msg, _ := hex.DecodeString(tc.msg)
 929  
 930  			// Get pubkey from libsecp256k1
 931  			pubkey, err := lib.CreatePubkey(seckey)
 932  			if err != nil {
 933  				t.Fatalf("CreatePubkey failed: %v", err)
 934  			}
 935  
 936  			// 1. Sign with libsecp256k1, verify with libsecp256k1
 937  			libSig, err := lib.SchnorrSign(msg, seckey)
 938  			if err != nil {
 939  				t.Fatalf("lib SchnorrSign failed: %v", err)
 940  			}
 941  			if !lib.SchnorrVerify(libSig, msg, pubkey) {
 942  				t.Error("lib failed to verify its own signature")
 943  			}
 944  
 945  			// 2. Sign with libsecp256k1, verify with Go
 946  			xonlyPubkey, err := XOnlyPubkeyParse(pubkey)
 947  			if err != nil {
 948  				t.Fatalf("XOnlyPubkeyParse failed: %v", err)
 949  			}
 950  			if !SchnorrVerify(libSig, msg, xonlyPubkey) {
 951  				t.Error("Go failed to verify libsecp256k1 signature")
 952  			}
 953  
 954  			// 3. Sign with Go, verify with libsecp256k1
 955  			keypair, err := KeyPairCreate(seckey)
 956  			if err != nil {
 957  				t.Fatalf("KeyPairCreate failed: %v", err)
 958  			}
 959  
 960  			var goSig [64]byte
 961  			if err := SchnorrSign(goSig[:], msg, keypair, nil); err != nil {
 962  				t.Fatalf("Go SchnorrSign failed: %v", err)
 963  			}
 964  			if !lib.SchnorrVerify(goSig[:], msg, pubkey) {
 965  				t.Errorf("libsecp256k1 failed to verify Go signature:\n  sig: %s",
 966  					hex.EncodeToString(goSig[:]))
 967  			}
 968  
 969  			// 4. Sign with Go, verify with Go
 970  			if !SchnorrVerify(goSig[:], msg, xonlyPubkey) {
 971  				t.Error("Go failed to verify its own signature")
 972  			}
 973  		})
 974  	}
 975  }
 976