glv_test.go raw

   1  //go:build !js && !wasm && !tinygo && !wasm32
   2  
   3  package p256k1
   4  
   5  import (
   6  	"encoding/hex"
   7  	"testing"
   8  )
   9  
  10  // TestGLVScalarConstants verifies that the GLV scalar constants are correctly defined
  11  func TestGLVScalarConstants(t *testing.T) {
  12  	// Test lambda constant
  13  	// Expected: 0x5363AD4CC05C30E0A5261C028812645A122E22EA20816678DF02967C1B23BD72
  14  	var lambdaBytes [32]byte
  15  	scalarLambda.getB32(lambdaBytes[:])
  16  	expectedLambda := "5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72"
  17  	if hex.EncodeToString(lambdaBytes[:]) != expectedLambda {
  18  		t.Errorf("scalarLambda mismatch:\n  got:  %s\n  want: %s", hex.EncodeToString(lambdaBytes[:]), expectedLambda)
  19  	}
  20  
  21  	// Test g1 constant
  22  	// Expected: 0x3086D221A7D46BCDE86C90E49284EB153DAA8A1471E8CA7FE893209A45DBB031
  23  	var g1Bytes [32]byte
  24  	scalarG1.getB32(g1Bytes[:])
  25  	expectedG1 := "3086d221a7d46bcde86c90e49284eb153daa8a1471e8ca7fe893209a45dbb031"
  26  	if hex.EncodeToString(g1Bytes[:]) != expectedG1 {
  27  		t.Errorf("scalarG1 mismatch:\n  got:  %s\n  want: %s", hex.EncodeToString(g1Bytes[:]), expectedG1)
  28  	}
  29  
  30  	// Test g2 constant
  31  	// Expected: 0xE4437ED6010E88286F547FA90ABFE4C4221208AC9DF506C61571B4AE8AC47F71
  32  	var g2Bytes [32]byte
  33  	scalarG2.getB32(g2Bytes[:])
  34  	expectedG2 := "e4437ed6010e88286f547fa90abfe4c4221208ac9df506c61571b4ae8ac47f71"
  35  	if hex.EncodeToString(g2Bytes[:]) != expectedG2 {
  36  		t.Errorf("scalarG2 mismatch:\n  got:  %s\n  want: %s", hex.EncodeToString(g2Bytes[:]), expectedG2)
  37  	}
  38  
  39  	// Test -b1 constant
  40  	// Expected: 0x00000000000000000000000000000000E4437ED6010E88286F547FA90ABFE4C3
  41  	var minusB1Bytes [32]byte
  42  	scalarMinusB1.getB32(minusB1Bytes[:])
  43  	expectedMinusB1 := "00000000000000000000000000000000e4437ed6010e88286f547fa90abfe4c3"
  44  	if hex.EncodeToString(minusB1Bytes[:]) != expectedMinusB1 {
  45  		t.Errorf("scalarMinusB1 mismatch:\n  got:  %s\n  want: %s", hex.EncodeToString(minusB1Bytes[:]), expectedMinusB1)
  46  	}
  47  
  48  	// Test -b2 constant
  49  	// Expected: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE8A280AC50774346DD765CDA83DB1562C
  50  	var minusB2Bytes [32]byte
  51  	scalarMinusB2.getB32(minusB2Bytes[:])
  52  	expectedMinusB2 := "fffffffffffffffffffffffffffffffe8a280ac50774346dd765cda83db1562c"
  53  	if hex.EncodeToString(minusB2Bytes[:]) != expectedMinusB2 {
  54  		t.Errorf("scalarMinusB2 mismatch:\n  got:  %s\n  want: %s", hex.EncodeToString(minusB2Bytes[:]), expectedMinusB2)
  55  	}
  56  }
  57  
  58  // TestGLVLambdaCubeRoot verifies that λ^3 ≡ 1 (mod n)
  59  func TestGLVLambdaCubeRoot(t *testing.T) {
  60  	// Compute λ^2
  61  	var lambda2 Scalar
  62  	lambda2.mul(&scalarLambda, &scalarLambda)
  63  
  64  	// Compute λ^3 = λ^2 * λ
  65  	var lambda3 Scalar
  66  	lambda3.mul(&lambda2, &scalarLambda)
  67  
  68  	// Check if λ^3 ≡ 1 (mod n)
  69  	if !lambda3.equal(&ScalarOne) {
  70  		var bytes [32]byte
  71  		lambda3.getB32(bytes[:])
  72  		t.Errorf("λ^3 ≠ 1 (mod n), got: %s", hex.EncodeToString(bytes[:]))
  73  	}
  74  }
  75  
  76  // TestGLVLambdaProperty verifies that λ^2 + λ + 1 ≡ 0 (mod n)
  77  func TestGLVLambdaProperty(t *testing.T) {
  78  	// Compute λ^2
  79  	var lambda2 Scalar
  80  	lambda2.mul(&scalarLambda, &scalarLambda)
  81  
  82  	// Compute λ^2 + λ
  83  	var sum Scalar
  84  	sum.add(&lambda2, &scalarLambda)
  85  
  86  	// Compute λ^2 + λ + 1
  87  	sum.add(&sum, &ScalarOne)
  88  
  89  	// Check if λ^2 + λ + 1 ≡ 0 (mod n)
  90  	if !sum.isZero() {
  91  		var bytes [32]byte
  92  		sum.getB32(bytes[:])
  93  		t.Errorf("λ^2 + λ + 1 ≠ 0 (mod n), got: %s", hex.EncodeToString(bytes[:]))
  94  	}
  95  }
  96  
  97  // TestGLVBetaConstant verifies that the Beta field constant is correctly defined
  98  func TestGLVBetaConstant(t *testing.T) {
  99  	// Expected: 0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee
 100  	var betaBytes [32]byte
 101  	fieldBeta.getB32(betaBytes[:])
 102  	expectedBeta := "7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee"
 103  	if hex.EncodeToString(betaBytes[:]) != expectedBeta {
 104  		t.Errorf("fieldBeta mismatch:\n  got:  %s\n  want: %s", hex.EncodeToString(betaBytes[:]), expectedBeta)
 105  	}
 106  }
 107  
 108  // TestGLVBetaCubeRoot verifies that β^3 ≡ 1 (mod p)
 109  func TestGLVBetaCubeRoot(t *testing.T) {
 110  	// Compute β^2
 111  	var beta2 FieldElement
 112  	beta2.sqr(&fieldBeta)
 113  	beta2.normalize()
 114  
 115  	// Compute β^3 = β^2 * β
 116  	var beta3 FieldElement
 117  	beta3.mul(&beta2, &fieldBeta)
 118  	beta3.normalize()
 119  
 120  	// Check if β^3 ≡ 1 (mod p)
 121  	if !beta3.equal(&FieldElementOne) {
 122  		var bytes [32]byte
 123  		beta3.getB32(bytes[:])
 124  		t.Errorf("β^3 ≠ 1 (mod p), got: %s", hex.EncodeToString(bytes[:]))
 125  	}
 126  }
 127  
 128  // TestGLVBetaProperty verifies that β^2 + β + 1 ≡ 0 (mod p)
 129  func TestGLVBetaProperty(t *testing.T) {
 130  	// Compute β^2
 131  	var beta2 FieldElement
 132  	beta2.sqr(&fieldBeta)
 133  
 134  	// Compute β^2 + β
 135  	var sum FieldElement
 136  	sum = beta2
 137  	sum.add(&fieldBeta)
 138  
 139  	// Compute β^2 + β + 1
 140  	sum.add(&FieldElementOne)
 141  	sum.normalize()
 142  
 143  	// Check if β^2 + β + 1 ≡ 0 (mod p)
 144  	if !sum.normalizesToZeroVar() {
 145  		var bytes [32]byte
 146  		sum.getB32(bytes[:])
 147  		t.Errorf("β^2 + β + 1 ≠ 0 (mod p), got: %s", hex.EncodeToString(bytes[:]))
 148  	}
 149  }
 150  
 151  // TestGLVEndomorphismOnGenerator verifies that λ·G = (β·Gx, Gy)
 152  // This confirms that the endomorphism relationship holds
 153  func TestGLVEndomorphismOnGenerator(t *testing.T) {
 154  	// Compute λ·G using scalar multiplication
 155  	var lambdaG GroupElementJacobian
 156  	EcmultConst(&lambdaG, &Generator, &scalarLambda)
 157  
 158  	// Convert to affine
 159  	var lambdaGAff GroupElementAffine
 160  	lambdaGAff.setGEJ(&lambdaG)
 161  	lambdaGAff.x.normalize()
 162  	lambdaGAff.y.normalize()
 163  
 164  	// Compute β·Gx
 165  	var betaGx FieldElement
 166  	betaGx.mul(&Generator.x, &fieldBeta)
 167  	betaGx.normalize()
 168  
 169  	// Check X coordinate: λ·G.x should equal β·G.x
 170  	if !lambdaGAff.x.equal(&betaGx) {
 171  		var got, want [32]byte
 172  		lambdaGAff.x.getB32(got[:])
 173  		betaGx.getB32(want[:])
 174  		t.Errorf("λ·G.x ≠ β·G.x:\n  got:  %s\n  want: %s", hex.EncodeToString(got[:]), hex.EncodeToString(want[:]))
 175  	}
 176  
 177  	// Check Y coordinate: λ·G.y should equal G.y
 178  	genY := Generator.y
 179  	genY.normalize()
 180  	if !lambdaGAff.y.equal(&genY) {
 181  		var got, want [32]byte
 182  		lambdaGAff.y.getB32(got[:])
 183  		genY.getB32(want[:])
 184  		t.Errorf("λ·G.y ≠ G.y:\n  got:  %s\n  want: %s", hex.EncodeToString(got[:]), hex.EncodeToString(want[:]))
 185  	}
 186  }
 187  
 188  // =============================================================================
 189  // Phase 2 Tests: Scalar Splitting
 190  // =============================================================================
 191  
 192  // TestScalarSplitLambdaProperty verifies that k1 + k2*λ ≡ k (mod n)
 193  func TestScalarSplitLambdaProperty(t *testing.T) {
 194  	testCases := []struct {
 195  		name  string
 196  		kHex  string
 197  	}{
 198  		{"one", "0000000000000000000000000000000000000000000000000000000000000001"},
 199  		{"small", "0000000000000000000000000000000000000000000000000000000000000100"},
 200  		{"medium", "0000000000000000000000000000000100000000000000000000000000000000"},
 201  		{"large", "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413f"}, // n-2
 202  		{"random1", "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35"},
 203  		{"random2", "0000000000000000000000000000000014551231950b75fc4402da1732fc9bebe"},
 204  	}
 205  
 206  	for _, tc := range testCases {
 207  		t.Run(tc.name, func(t *testing.T) {
 208  			// Parse k
 209  			kBytes, _ := hex.DecodeString(tc.kHex)
 210  			var k Scalar
 211  			k.setB32(kBytes)
 212  
 213  			// Split k into k1, k2
 214  			var k1, k2 Scalar
 215  			scalarSplitLambda(&k1, &k2, &k)
 216  
 217  			// Verify: k1 + k2*λ ≡ k (mod n)
 218  			var k2Lambda, sum Scalar
 219  			k2Lambda.mul(&k2, &scalarLambda)
 220  			sum.add(&k1, &k2Lambda)
 221  
 222  			if !sum.equal(&k) {
 223  				var sumBytes, kBytes [32]byte
 224  				sum.getB32(sumBytes[:])
 225  				k.getB32(kBytes[:])
 226  				t.Errorf("k1 + k2*λ ≠ k:\n  sum: %s\n  k:   %s",
 227  					hex.EncodeToString(sumBytes[:]), hex.EncodeToString(kBytes[:]))
 228  			}
 229  		})
 230  	}
 231  }
 232  
 233  // TestScalarSplitLambdaBounds verifies that k1 and k2 are bounded (< 2^128)
 234  func TestScalarSplitLambdaBounds(t *testing.T) {
 235  	// Test with various random-ish scalars
 236  	testScalars := []string{
 237  		"e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35",
 238  		"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", // n-1
 239  		"7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", // n/2
 240  		"0000000000000000000000000000000100000000000000000000000000000000",
 241  		"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
 242  	}
 243  
 244  	// Bounds from libsecp256k1: k1 < 2^128 or -k1 < 2^128, same for k2
 245  	// Actually the bounds are tighter: (a1 + a2 + 1)/2 for k1, (-b1 + b2)/2 + 1 for k2
 246  	// But we'll just check they fit in ~128 bits (upper limbs are small)
 247  
 248  	for _, hexStr := range testScalars {
 249  		kBytes, _ := hex.DecodeString(hexStr)
 250  		var k Scalar
 251  		k.setB32(kBytes)
 252  
 253  		var k1, k2 Scalar
 254  		scalarSplitLambda(&k1, &k2, &k)
 255  
 256  		// Check that k1 is small (upper two limbs should be 0 or the scalar is negated)
 257  		// If k1 is "high", then -k1 should be small
 258  		k1Small := (k1.d[2] == 0 && k1.d[3] == 0)
 259  		var negK1 Scalar
 260  		negK1.negate(&k1)
 261  		negK1Small := (negK1.d[2] == 0 && negK1.d[3] == 0)
 262  
 263  		if !k1Small && !negK1Small {
 264  			t.Errorf("k1 not bounded for k=%s: k1.d[2]=%x, k1.d[3]=%x", hexStr, k1.d[2], k1.d[3])
 265  		}
 266  
 267  		// Same for k2
 268  		k2Small := (k2.d[2] == 0 && k2.d[3] == 0)
 269  		var negK2 Scalar
 270  		negK2.negate(&k2)
 271  		negK2Small := (negK2.d[2] == 0 && negK2.d[3] == 0)
 272  
 273  		if !k2Small && !negK2Small {
 274  			t.Errorf("k2 not bounded for k=%s: k2.d[2]=%x, k2.d[3]=%x", hexStr, k2.d[2], k2.d[3])
 275  		}
 276  	}
 277  }
 278  
 279  // TestScalarSplitLambdaRandom tests splitLambda with random scalars
 280  func TestScalarSplitLambdaRandom(t *testing.T) {
 281  	// Use deterministic "random" values based on hashing
 282  	for i := 0; i < 100; i++ {
 283  		// Create a pseudo-random scalar
 284  		sha := NewSHA256()
 285  		sha.Write([]byte{byte(i), byte(i >> 8)})
 286  		var kBytes [32]byte
 287  		sha.Finalize(kBytes[:])
 288  
 289  		var k Scalar
 290  		k.setB32(kBytes[:])
 291  
 292  		// Split
 293  		var k1, k2 Scalar
 294  		scalarSplitLambda(&k1, &k2, &k)
 295  
 296  		// Verify property
 297  		var k2Lambda, sum Scalar
 298  		k2Lambda.mul(&k2, &scalarLambda)
 299  		sum.add(&k1, &k2Lambda)
 300  
 301  		if !sum.equal(&k) {
 302  			t.Errorf("Iteration %d: k1 + k2*λ ≠ k", i)
 303  		}
 304  	}
 305  }
 306  
 307  // TestScalarSplit128 tests the 128-bit split function
 308  func TestScalarSplit128(t *testing.T) {
 309  	// Test scalar: 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20
 310  	kBytes, _ := hex.DecodeString("0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20")
 311  	var k Scalar
 312  	k.setB32(kBytes)
 313  
 314  	var k1, k2 Scalar
 315  	scalarSplit128(&k1, &k2, &k)
 316  
 317  	// k1 should be low 128 bits
 318  	// k2 should be high 128 bits
 319  	// In limb order: k.d[0], k.d[1] are low, k.d[2], k.d[3] are high
 320  	if k1.d[0] != k.d[0] || k1.d[1] != k.d[1] || k1.d[2] != 0 || k1.d[3] != 0 {
 321  		t.Errorf("k1 incorrect: got d[0:4]=%x,%x,%x,%x", k1.d[0], k1.d[1], k1.d[2], k1.d[3])
 322  	}
 323  	if k2.d[0] != k.d[2] || k2.d[1] != k.d[3] || k2.d[2] != 0 || k2.d[3] != 0 {
 324  		t.Errorf("k2 incorrect: got d[0:4]=%x,%x,%x,%x", k2.d[0], k2.d[1], k2.d[2], k2.d[3])
 325  	}
 326  }
 327  
 328  // TestMulShiftVar tests the mul_shift_var function
 329  func TestMulShiftVar(t *testing.T) {
 330  	// Test case: multiply two known values and shift by 384
 331  	// This is the exact operation used in splitLambda
 332  
 333  	// Simple test: 1 * g1 >> 384 should give a small result
 334  	var result Scalar
 335  	result.mulShiftVar(&ScalarOne, &scalarG1, 384)
 336  
 337  	// The result should be 0 since 1 * g1 < 2^384
 338  	// (g1 is a 256-bit value, so 1 * g1 = g1 which is < 2^256 < 2^384)
 339  	if result.d[0] != 0 || result.d[1] != 0 || result.d[2] != 0 || result.d[3] != 0 {
 340  		t.Errorf("1 * g1 >> 384 should be 0, got: %x %x %x %x",
 341  			result.d[0], result.d[1], result.d[2], result.d[3])
 342  	}
 343  
 344  	// Test with a larger value that will produce non-zero result
 345  	// Use a value close to n to get a meaningful result
 346  	nMinus1Bytes, _ := hex.DecodeString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140")
 347  	var nMinus1 Scalar
 348  	nMinus1.setB32(nMinus1Bytes)
 349  
 350  	result.mulShiftVar(&nMinus1, &scalarG1, 384)
 351  
 352  	// Result should be non-zero and fit in ~128 bits
 353  	// (since g1 is ~256 bits, (n-1)*g1 is ~512 bits, >> 384 gives ~128 bits)
 354  	if result.d[2] != 0 || result.d[3] != 0 {
 355  		t.Logf("Warning: result has high limbs set, may indicate issue")
 356  	}
 357  	t.Logf("(n-1) * g1 >> 384 = %x %x %x %x", result.d[3], result.d[2], result.d[1], result.d[0])
 358  }
 359  
 360  // TestIsHigh tests the isHigh function
 361  func TestIsHigh(t *testing.T) {
 362  	testCases := []struct {
 363  		name     string
 364  		hexValue string
 365  		expected bool
 366  	}{
 367  		{"zero", "0000000000000000000000000000000000000000000000000000000000000000", false},
 368  		{"one", "0000000000000000000000000000000000000000000000000000000000000001", false},
 369  		{"n/2", "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", false},
 370  		{"n/2+1", "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1", true},
 371  		{"n-1", "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", true},
 372  		{"n-2", "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413f", true},
 373  	}
 374  
 375  	for _, tc := range testCases {
 376  		t.Run(tc.name, func(t *testing.T) {
 377  			bytes, _ := hex.DecodeString(tc.hexValue)
 378  			var s Scalar
 379  			s.setB32(bytes)
 380  
 381  			result := s.isHigh()
 382  			if result != tc.expected {
 383  				t.Errorf("isHigh(%s) = %v, want %v", tc.name, result, tc.expected)
 384  			}
 385  		})
 386  	}
 387  }
 388  
 389  // BenchmarkScalarSplitLambda benchmarks the scalar splitting operation
 390  func BenchmarkScalarSplitLambda(b *testing.B) {
 391  	// Use a representative scalar
 392  	kBytes, _ := hex.DecodeString("e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35")
 393  	var k Scalar
 394  	k.setB32(kBytes)
 395  
 396  	var k1, k2 Scalar
 397  
 398  	b.ResetTimer()
 399  	for i := 0; i < b.N; i++ {
 400  		scalarSplitLambda(&k1, &k2, &k)
 401  	}
 402  }
 403  
 404  // BenchmarkMulShiftVar benchmarks the mulShiftVar operation
 405  func BenchmarkMulShiftVar(b *testing.B) {
 406  	kBytes, _ := hex.DecodeString("e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35")
 407  	var k Scalar
 408  	k.setB32(kBytes)
 409  
 410  	var result Scalar
 411  
 412  	b.ResetTimer()
 413  	for i := 0; i < b.N; i++ {
 414  		result.mulShiftVar(&k, &scalarG1, 384)
 415  	}
 416  }
 417  
 418  // =============================================================================
 419  // Phase 3 Tests: Point Operations with Endomorphism
 420  // =============================================================================
 421  
 422  // TestMulLambdaAffine tests the affine mulLambda operation
 423  func TestMulLambdaAffine(t *testing.T) {
 424  	// Test that mulLambda(G) produces the same result as λ*G via scalar multiplication
 425  	var lambdaG GroupElementJacobian
 426  	EcmultConst(&lambdaG, &Generator, &scalarLambda)
 427  
 428  	var lambdaGAff GroupElementAffine
 429  	lambdaGAff.setGEJ(&lambdaG)
 430  	lambdaGAff.x.normalize()
 431  	lambdaGAff.y.normalize()
 432  
 433  	// Now test mulLambda
 434  	var mulLambdaG GroupElementAffine
 435  	mulLambdaG.mulLambda(&Generator)
 436  	mulLambdaG.x.normalize()
 437  	mulLambdaG.y.normalize()
 438  
 439  	// They should be equal
 440  	if !lambdaGAff.x.equal(&mulLambdaG.x) {
 441  		var got, want [32]byte
 442  		mulLambdaG.x.getB32(got[:])
 443  		lambdaGAff.x.getB32(want[:])
 444  		t.Errorf("mulLambda(G).x ≠ λ*G.x:\n  got:  %s\n  want: %s",
 445  			hex.EncodeToString(got[:]), hex.EncodeToString(want[:]))
 446  	}
 447  
 448  	if !lambdaGAff.y.equal(&mulLambdaG.y) {
 449  		var got, want [32]byte
 450  		mulLambdaG.y.getB32(got[:])
 451  		lambdaGAff.y.getB32(want[:])
 452  		t.Errorf("mulLambda(G).y ≠ λ*G.y:\n  got:  %s\n  want: %s",
 453  			hex.EncodeToString(got[:]), hex.EncodeToString(want[:]))
 454  	}
 455  }
 456  
 457  // TestMulLambdaJacobian tests the Jacobian mulLambda operation
 458  func TestMulLambdaJacobian(t *testing.T) {
 459  	// Convert generator to Jacobian
 460  	var gJac GroupElementJacobian
 461  	gJac.setGE(&Generator)
 462  
 463  	// Apply mulLambda
 464  	var mulLambdaG GroupElementJacobian
 465  	mulLambdaG.mulLambda(&gJac)
 466  
 467  	// Convert back to affine for comparison
 468  	var mulLambdaGAff GroupElementAffine
 469  	mulLambdaGAff.setGEJ(&mulLambdaG)
 470  	mulLambdaGAff.x.normalize()
 471  	mulLambdaGAff.y.normalize()
 472  
 473  	// Compute expected via scalar multiplication
 474  	var lambdaG GroupElementJacobian
 475  	EcmultConst(&lambdaG, &Generator, &scalarLambda)
 476  	var lambdaGAff GroupElementAffine
 477  	lambdaGAff.setGEJ(&lambdaG)
 478  	lambdaGAff.x.normalize()
 479  	lambdaGAff.y.normalize()
 480  
 481  	// They should be equal
 482  	if !lambdaGAff.equal(&mulLambdaGAff) {
 483  		t.Errorf("Jacobian mulLambda(G) ≠ λ*G")
 484  	}
 485  }
 486  
 487  // TestMulLambdaInfinity tests that mulLambda handles infinity correctly
 488  func TestMulLambdaInfinity(t *testing.T) {
 489  	var inf GroupElementAffine
 490  	inf.setInfinity()
 491  
 492  	var result GroupElementAffine
 493  	result.mulLambda(&inf)
 494  
 495  	if !result.isInfinity() {
 496  		t.Errorf("mulLambda(infinity) should be infinity")
 497  	}
 498  
 499  	// Jacobian version
 500  	var infJac GroupElementJacobian
 501  	infJac.setInfinity()
 502  
 503  	var resultJac GroupElementJacobian
 504  	resultJac.mulLambda(&infJac)
 505  
 506  	if !resultJac.isInfinity() {
 507  		t.Errorf("Jacobian mulLambda(infinity) should be infinity")
 508  	}
 509  }
 510  
 511  // TestMulLambdaCubed tests that λ^3 * P = P (applying mulLambda 3 times returns to original)
 512  func TestMulLambdaCubed(t *testing.T) {
 513  	// Start with generator
 514  	p := Generator
 515  
 516  	// Apply mulLambda three times
 517  	var p1, p2, p3 GroupElementAffine
 518  	p1.mulLambda(&p)
 519  	p2.mulLambda(&p1)
 520  	p3.mulLambda(&p2)
 521  
 522  	// Normalize for comparison
 523  	p3.x.normalize()
 524  	p3.y.normalize()
 525  
 526  	genNorm := Generator
 527  	genNorm.x.normalize()
 528  	genNorm.y.normalize()
 529  
 530  	// p3 should equal the original generator
 531  	if !p3.equal(&genNorm) {
 532  		var got, want [32]byte
 533  		p3.x.getB32(got[:])
 534  		genNorm.x.getB32(want[:])
 535  		t.Errorf("λ^3 * G ≠ G:\n  got x:  %s\n  want x: %s",
 536  			hex.EncodeToString(got[:]), hex.EncodeToString(want[:]))
 537  	}
 538  }
 539  
 540  // TestEcmultEndoSplit tests the combined endomorphism split operation
 541  func TestEcmultEndoSplit(t *testing.T) {
 542  	testCases := []string{
 543  		"0000000000000000000000000000000000000000000000000000000000000001",
 544  		"e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35",
 545  		"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", // n-1
 546  		"7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", // n/2
 547  	}
 548  
 549  	for _, hexStr := range testCases {
 550  		t.Run(hexStr[:16]+"...", func(t *testing.T) {
 551  			// Parse scalar
 552  			sBytes, _ := hex.DecodeString(hexStr)
 553  			var s Scalar
 554  			s.setB32(sBytes)
 555  
 556  			// Use generator as the point
 557  			p := Generator
 558  
 559  			// Split
 560  			var s1, s2 Scalar
 561  			var p1, p2 GroupElementAffine
 562  			ecmultEndoSplit(&s1, &s2, &p1, &p2, &s, &p)
 563  
 564  			// Verify s1 and s2 are not high (after negation adjustment)
 565  			if s1.isHigh() {
 566  				t.Errorf("s1 should not be high after split")
 567  			}
 568  			if s2.isHigh() {
 569  				t.Errorf("s2 should not be high after split")
 570  			}
 571  
 572  			// Verify: s1*p1 + s2*p2 = s*p
 573  			var s1p1, s2p2, sum, expected GroupElementJacobian
 574  
 575  			EcmultConst(&s1p1, &p1, &s1)
 576  			EcmultConst(&s2p2, &p2, &s2)
 577  			sum.addVar(&s1p1, &s2p2)
 578  
 579  			EcmultConst(&expected, &p, &s)
 580  
 581  			// Convert to affine for comparison
 582  			var sumAff, expectedAff GroupElementAffine
 583  			sumAff.setGEJ(&sum)
 584  			expectedAff.setGEJ(&expected)
 585  			sumAff.x.normalize()
 586  			sumAff.y.normalize()
 587  			expectedAff.x.normalize()
 588  			expectedAff.y.normalize()
 589  
 590  			if !sumAff.equal(&expectedAff) {
 591  				t.Errorf("s1*p1 + s2*p2 ≠ s*p")
 592  			}
 593  		})
 594  	}
 595  }
 596  
 597  // TestEcmultEndoSplitRandom tests endomorphism split with random scalars
 598  func TestEcmultEndoSplitRandom(t *testing.T) {
 599  	for i := 0; i < 20; i++ {
 600  		// Generate pseudo-random scalar
 601  		sha := NewSHA256()
 602  		sha.Write([]byte{byte(i), byte(i >> 8), 0xAB, 0xCD})
 603  		var sBytes [32]byte
 604  		sha.Finalize(sBytes[:])
 605  
 606  		var s Scalar
 607  		s.setB32(sBytes[:])
 608  
 609  		// Use generator
 610  		p := Generator
 611  
 612  		// Split
 613  		var s1, s2 Scalar
 614  		var p1, p2 GroupElementAffine
 615  		ecmultEndoSplit(&s1, &s2, &p1, &p2, &s, &p)
 616  
 617  		// Verify: s1*p1 + s2*p2 = s*p
 618  		var s1p1, s2p2, sum, expected GroupElementJacobian
 619  
 620  		EcmultConst(&s1p1, &p1, &s1)
 621  		EcmultConst(&s2p2, &p2, &s2)
 622  		sum.addVar(&s1p1, &s2p2)
 623  
 624  		EcmultConst(&expected, &p, &s)
 625  
 626  		var sumAff, expectedAff GroupElementAffine
 627  		sumAff.setGEJ(&sum)
 628  		expectedAff.setGEJ(&expected)
 629  		sumAff.x.normalize()
 630  		sumAff.y.normalize()
 631  		expectedAff.x.normalize()
 632  		expectedAff.y.normalize()
 633  
 634  		if !sumAff.equal(&expectedAff) {
 635  			t.Errorf("Iteration %d: s1*p1 + s2*p2 ≠ s*p", i)
 636  		}
 637  	}
 638  }
 639  
 640  // BenchmarkMulLambdaAffine benchmarks the affine mulLambda operation
 641  func BenchmarkMulLambdaAffine(b *testing.B) {
 642  	p := Generator
 643  	var result GroupElementAffine
 644  
 645  	b.ResetTimer()
 646  	for i := 0; i < b.N; i++ {
 647  		result.mulLambda(&p)
 648  	}
 649  }
 650  
 651  // BenchmarkMulLambdaJacobian benchmarks the Jacobian mulLambda operation
 652  func BenchmarkMulLambdaJacobian(b *testing.B) {
 653  	var p GroupElementJacobian
 654  	p.setGE(&Generator)
 655  	var result GroupElementJacobian
 656  
 657  	b.ResetTimer()
 658  	for i := 0; i < b.N; i++ {
 659  		result.mulLambda(&p)
 660  	}
 661  }
 662  
 663  // BenchmarkEcmultEndoSplit benchmarks the combined endomorphism split
 664  func BenchmarkEcmultEndoSplit(b *testing.B) {
 665  	sBytes, _ := hex.DecodeString("e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35")
 666  	var s Scalar
 667  	s.setB32(sBytes)
 668  	p := Generator
 669  
 670  	var s1, s2 Scalar
 671  	var p1, p2 GroupElementAffine
 672  
 673  	b.ResetTimer()
 674  	for i := 0; i < b.N; i++ {
 675  		ecmultEndoSplit(&s1, &s2, &p1, &p2, &s, &p)
 676  	}
 677  }
 678  
 679  // =============================================================================
 680  // Phase 4 Tests: Strauss-GLV Algorithm
 681  // =============================================================================
 682  
 683  // TestBuildOddMultiplesTableAffine tests the odd multiples table building
 684  func TestBuildOddMultiplesTableAffine(t *testing.T) {
 685  	// Build table from generator
 686  	var gJac GroupElementJacobian
 687  	gJac.setGE(&Generator)
 688  
 689  	const tableSize = 16 // Window size 5
 690  	preA := make([]GroupElementAffine, tableSize)
 691  	preBetaX := make([]FieldElement, tableSize)
 692  
 693  	buildOddMultiplesTableAffine(preA, preBetaX, &gJac, tableSize)
 694  
 695  	// Verify: preA[i] should equal (2*i+1)*G
 696  	for i := 0; i < tableSize; i++ {
 697  		var scalar Scalar
 698  		scalar.setInt(uint(2*i + 1))
 699  
 700  		var expected GroupElementJacobian
 701  		EcmultConst(&expected, &Generator, &scalar)
 702  
 703  		var expectedAff GroupElementAffine
 704  		expectedAff.setGEJ(&expected)
 705  		expectedAff.x.normalize()
 706  		expectedAff.y.normalize()
 707  
 708  		preA[i].x.normalize()
 709  		preA[i].y.normalize()
 710  
 711  		if !preA[i].equal(&expectedAff) {
 712  			t.Errorf("preA[%d] ≠ %d*G", i, 2*i+1)
 713  		}
 714  
 715  		// Verify β*x is correct
 716  		var expectedBetaX FieldElement
 717  		expectedBetaX.mul(&expectedAff.x, &fieldBeta)
 718  		expectedBetaX.normalize()
 719  		preBetaX[i].normalize()
 720  
 721  		if !preBetaX[i].equal(&expectedBetaX) {
 722  			t.Errorf("preBetaX[%d] ≠ β * preA[%d].x", i, i)
 723  		}
 724  	}
 725  }
 726  
 727  // TestTableGetGE tests the table lookup function
 728  func TestTableGetGE(t *testing.T) {
 729  	// Build table from generator
 730  	var gJac GroupElementJacobian
 731  	gJac.setGE(&Generator)
 732  
 733  	const tableSize = 16
 734  	preA := make([]GroupElementAffine, tableSize)
 735  	preBetaX := make([]FieldElement, tableSize)
 736  
 737  	buildOddMultiplesTableAffine(preA, preBetaX, &gJac, tableSize)
 738  
 739  	testCases := []struct {
 740  		name     string
 741  		n        int
 742  		expected int // expected multiple (negative means negated)
 743  	}{
 744  		{"n=1", 1, 1},
 745  		{"n=3", 3, 3},
 746  		{"n=5", 5, 5},
 747  		{"n=15", 15, 15},
 748  		{"n=-1", -1, -1},
 749  		{"n=-3", -3, -3},
 750  		{"n=-15", -15, -15},
 751  	}
 752  
 753  	for _, tc := range testCases {
 754  		t.Run(tc.name, func(t *testing.T) {
 755  			var pt GroupElementAffine
 756  			tableGetGE(&pt, preA, tc.n)
 757  
 758  			// Compute expected point
 759  			absN := tc.expected
 760  			if absN < 0 {
 761  				absN = -absN
 762  			}
 763  			var scalar Scalar
 764  			scalar.setInt(uint(absN))
 765  
 766  			var expectedJac GroupElementJacobian
 767  			EcmultConst(&expectedJac, &Generator, &scalar)
 768  
 769  			var expectedAff GroupElementAffine
 770  			expectedAff.setGEJ(&expectedJac)
 771  
 772  			// Negate if needed
 773  			if tc.expected < 0 {
 774  				expectedAff.negate(&expectedAff)
 775  			}
 776  
 777  			expectedAff.x.normalize()
 778  			expectedAff.y.normalize()
 779  			pt.x.normalize()
 780  			pt.y.normalize()
 781  
 782  			if !pt.equal(&expectedAff) {
 783  				t.Errorf("tableGetGE(%d) returned wrong point", tc.n)
 784  			}
 785  		})
 786  	}
 787  
 788  	// Test n=0 returns infinity
 789  	var pt GroupElementAffine
 790  	tableGetGE(&pt, preA, 0)
 791  	if !pt.isInfinity() {
 792  		t.Error("tableGetGE(0) should return infinity")
 793  	}
 794  }
 795  
 796  // TestTableGetGELambda tests the lambda-transformed table lookup
 797  func TestTableGetGELambda(t *testing.T) {
 798  	// Build table from generator
 799  	var gJac GroupElementJacobian
 800  	gJac.setGE(&Generator)
 801  
 802  	const tableSize = 16
 803  	preA := make([]GroupElementAffine, tableSize)
 804  	preBetaX := make([]FieldElement, tableSize)
 805  
 806  	buildOddMultiplesTableAffine(preA, preBetaX, &gJac, tableSize)
 807  
 808  	// Test that tableGetGELambda(n) returns λ * tableGetGE(n)
 809  	for n := 1; n <= 15; n += 2 {
 810  		var ptLambda GroupElementAffine
 811  		tableGetGELambda(&ptLambda, preA, preBetaX, n)
 812  
 813  		// Get regular point and apply lambda
 814  		var pt GroupElementAffine
 815  		tableGetGE(&pt, preA, n)
 816  		var expected GroupElementAffine
 817  		expected.mulLambda(&pt)
 818  
 819  		ptLambda.x.normalize()
 820  		ptLambda.y.normalize()
 821  		expected.x.normalize()
 822  		expected.y.normalize()
 823  
 824  		if !ptLambda.equal(&expected) {
 825  			t.Errorf("tableGetGELambda(%d) ≠ λ * tableGetGE(%d)", n, n)
 826  		}
 827  
 828  		// Test negative n
 829  		tableGetGELambda(&ptLambda, preA, preBetaX, -n)
 830  		tableGetGE(&pt, preA, -n)
 831  		expected.mulLambda(&pt)
 832  
 833  		ptLambda.x.normalize()
 834  		ptLambda.y.normalize()
 835  		expected.x.normalize()
 836  		expected.y.normalize()
 837  
 838  		if !ptLambda.equal(&expected) {
 839  			t.Errorf("tableGetGELambda(%d) ≠ λ * tableGetGE(%d)", -n, -n)
 840  		}
 841  	}
 842  }
 843  
 844  // TestEcmultStraussWNAFGLV tests the full Strauss-GLV algorithm
 845  func TestEcmultStraussWNAFGLV(t *testing.T) {
 846  	testCases := []string{
 847  		"0000000000000000000000000000000000000000000000000000000000000001", // 1
 848  		"0000000000000000000000000000000000000000000000000000000000000002", // 2
 849  		"0000000000000000000000000000000000000000000000000000000000000010", // 16
 850  		"00000000000000000000000000000000000000000000000000000000000000ff", // 255
 851  		"e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35", // random
 852  		"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", // n-1
 853  		"7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", // n/2
 854  	}
 855  
 856  	for _, hexStr := range testCases {
 857  		t.Run(hexStr[:8]+"...", func(t *testing.T) {
 858  			// Parse scalar
 859  			sBytes, _ := hex.DecodeString(hexStr)
 860  			var s Scalar
 861  			s.setB32(sBytes)
 862  
 863  			// Compute using Strauss-GLV
 864  			var resultGLV GroupElementJacobian
 865  			ecmultStraussWNAFGLV(&resultGLV, &Generator, &s)
 866  
 867  			// Compute using reference implementation
 868  			var resultRef GroupElementJacobian
 869  			EcmultConst(&resultRef, &Generator, &s)
 870  
 871  			// Convert to affine for comparison
 872  			var resultGLVAff, resultRefAff GroupElementAffine
 873  			resultGLVAff.setGEJ(&resultGLV)
 874  			resultRefAff.setGEJ(&resultRef)
 875  			resultGLVAff.x.normalize()
 876  			resultGLVAff.y.normalize()
 877  			resultRefAff.x.normalize()
 878  			resultRefAff.y.normalize()
 879  
 880  			if !resultGLVAff.equal(&resultRefAff) {
 881  				var gotX, wantX [32]byte
 882  				resultGLVAff.x.getB32(gotX[:])
 883  				resultRefAff.x.getB32(wantX[:])
 884  				t.Errorf("GLV result mismatch:\n  got x:  %s\n  want x: %s",
 885  					hex.EncodeToString(gotX[:]), hex.EncodeToString(wantX[:]))
 886  			}
 887  		})
 888  	}
 889  }
 890  
 891  // TestEcmultStraussWNAFGLVRandom tests the Strauss-GLV algorithm with random scalars
 892  func TestEcmultStraussWNAFGLVRandom(t *testing.T) {
 893  	for i := 0; i < 50; i++ {
 894  		// Generate pseudo-random scalar
 895  		sha := NewSHA256()
 896  		sha.Write([]byte{byte(i), byte(i >> 8), 0xDE, 0xAD})
 897  		var sBytes [32]byte
 898  		sha.Finalize(sBytes[:])
 899  
 900  		var s Scalar
 901  		s.setB32(sBytes[:])
 902  
 903  		// Compute using Strauss-GLV
 904  		var resultGLV GroupElementJacobian
 905  		ecmultStraussWNAFGLV(&resultGLV, &Generator, &s)
 906  
 907  		// Compute using reference implementation
 908  		var resultRef GroupElementJacobian
 909  		EcmultConst(&resultRef, &Generator, &s)
 910  
 911  		// Convert to affine for comparison
 912  		var resultGLVAff, resultRefAff GroupElementAffine
 913  		resultGLVAff.setGEJ(&resultGLV)
 914  		resultRefAff.setGEJ(&resultRef)
 915  		resultGLVAff.x.normalize()
 916  		resultGLVAff.y.normalize()
 917  		resultRefAff.x.normalize()
 918  		resultRefAff.y.normalize()
 919  
 920  		if !resultGLVAff.equal(&resultRefAff) {
 921  			t.Errorf("Iteration %d: GLV result mismatch", i)
 922  		}
 923  	}
 924  }
 925  
 926  // TestEcmultStraussWNAFGLVNonGenerator tests with a non-generator point
 927  func TestEcmultStraussWNAFGLVNonGenerator(t *testing.T) {
 928  	// Create a non-generator point: 2*G
 929  	var two Scalar
 930  	two.setInt(2)
 931  	var twoGJac GroupElementJacobian
 932  	EcmultConst(&twoGJac, &Generator, &two)
 933  	var twoG GroupElementAffine
 934  	twoG.setGEJ(&twoGJac)
 935  
 936  	testCases := []string{
 937  		"0000000000000000000000000000000000000000000000000000000000000001",
 938  		"0000000000000000000000000000000000000000000000000000000000000003",
 939  		"e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35",
 940  	}
 941  
 942  	for _, hexStr := range testCases {
 943  		t.Run(hexStr[:8]+"...", func(t *testing.T) {
 944  			sBytes, _ := hex.DecodeString(hexStr)
 945  			var s Scalar
 946  			s.setB32(sBytes)
 947  
 948  			// Compute using Strauss-GLV
 949  			var resultGLV GroupElementJacobian
 950  			ecmultStraussWNAFGLV(&resultGLV, &twoG, &s)
 951  
 952  			// Compute using reference implementation
 953  			var resultRef GroupElementJacobian
 954  			EcmultConst(&resultRef, &twoG, &s)
 955  
 956  			// Convert to affine for comparison
 957  			var resultGLVAff, resultRefAff GroupElementAffine
 958  			resultGLVAff.setGEJ(&resultGLV)
 959  			resultRefAff.setGEJ(&resultRef)
 960  			resultGLVAff.x.normalize()
 961  			resultGLVAff.y.normalize()
 962  			resultRefAff.x.normalize()
 963  			resultRefAff.y.normalize()
 964  
 965  			if !resultGLVAff.equal(&resultRefAff) {
 966  				t.Errorf("GLV result mismatch for 2*G")
 967  			}
 968  		})
 969  	}
 970  }
 971  
 972  // TestEcmultStraussWNAFGLVEdgeCases tests edge cases
 973  func TestEcmultStraussWNAFGLVEdgeCases(t *testing.T) {
 974  	// Test with zero scalar
 975  	var zero Scalar
 976  	var result GroupElementJacobian
 977  	ecmultStraussWNAFGLV(&result, &Generator, &zero)
 978  	if !result.isInfinity() {
 979  		t.Error("0 * G should be infinity")
 980  	}
 981  
 982  	// Test with infinity point
 983  	var inf GroupElementAffine
 984  	inf.setInfinity()
 985  	var one Scalar
 986  	one.setInt(1)
 987  	ecmultStraussWNAFGLV(&result, &inf, &one)
 988  	if !result.isInfinity() {
 989  		t.Error("1 * infinity should be infinity")
 990  	}
 991  }
 992  
 993  // BenchmarkEcmultStraussWNAFGLV benchmarks the Strauss-GLV algorithm
 994  func BenchmarkEcmultStraussWNAFGLV(b *testing.B) {
 995  	sBytes, _ := hex.DecodeString("e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35")
 996  	var s Scalar
 997  	s.setB32(sBytes)
 998  
 999  	var result GroupElementJacobian
1000  
1001  	b.ResetTimer()
1002  	for i := 0; i < b.N; i++ {
1003  		ecmultStraussWNAFGLV(&result, &Generator, &s)
1004  	}
1005  }
1006  
1007  // BenchmarkEcmultConst benchmarks the reference constant-time implementation
1008  func BenchmarkEcmultConst(b *testing.B) {
1009  	sBytes, _ := hex.DecodeString("e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35")
1010  	var s Scalar
1011  	s.setB32(sBytes)
1012  
1013  	var result GroupElementJacobian
1014  
1015  	b.ResetTimer()
1016  	for i := 0; i < b.N; i++ {
1017  		EcmultConst(&result, &Generator, &s)
1018  	}
1019  }
1020  
1021  // BenchmarkEcmultWindowedVar benchmarks the windowed variable-time implementation
1022  func BenchmarkEcmultWindowedVar(b *testing.B) {
1023  	sBytes, _ := hex.DecodeString("e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35")
1024  	var s Scalar
1025  	s.setB32(sBytes)
1026  
1027  	var result GroupElementJacobian
1028  
1029  	b.ResetTimer()
1030  	for i := 0; i < b.N; i++ {
1031  		ecmultWindowedVar(&result, &Generator, &s)
1032  	}
1033  }
1034  
1035  // BenchmarkBuildOddMultiplesTableAffine benchmarks table building
1036  func BenchmarkBuildOddMultiplesTableAffine(b *testing.B) {
1037  	var gJac GroupElementJacobian
1038  	gJac.setGE(&Generator)
1039  
1040  	const tableSize = 16
1041  	preA := make([]GroupElementAffine, tableSize)
1042  	preBetaX := make([]FieldElement, tableSize)
1043  
1044  	b.ResetTimer()
1045  	for i := 0; i < b.N; i++ {
1046  		buildOddMultiplesTableAffine(preA, preBetaX, &gJac, tableSize)
1047  	}
1048  }
1049  
1050  // =============================================================================
1051  // Phase 5 Tests: Generator Precomputation
1052  // =============================================================================
1053  
1054  // TestGenTablesInitialization tests that the generator tables are properly initialized
1055  func TestGenTablesInitialization(t *testing.T) {
1056  	// Force initialization
1057  	EnsureGenTablesInitialized()
1058  
1059  	// Verify preGenG[0] = 1*G = G
1060  	preGenG[0].x.normalize()
1061  	preGenG[0].y.normalize()
1062  	genNorm := Generator
1063  	genNorm.x.normalize()
1064  	genNorm.y.normalize()
1065  
1066  	if !preGenG[0].equal(&genNorm) {
1067  		t.Error("preGenG[0] should equal G")
1068  	}
1069  
1070  	// Verify preGenG[i] = (2*i+1)*G
1071  	for i := 0; i < 8; i++ { // Check first 8 entries
1072  		var scalar Scalar
1073  		scalar.setInt(uint(2*i + 1))
1074  
1075  		var expected GroupElementJacobian
1076  		EcmultConst(&expected, &Generator, &scalar)
1077  
1078  		var expectedAff GroupElementAffine
1079  		expectedAff.setGEJ(&expected)
1080  		expectedAff.x.normalize()
1081  		expectedAff.y.normalize()
1082  
1083  		preGenG[i].x.normalize()
1084  		preGenG[i].y.normalize()
1085  
1086  		if !preGenG[i].equal(&expectedAff) {
1087  			t.Errorf("preGenG[%d] ≠ %d*G", i, 2*i+1)
1088  		}
1089  	}
1090  
1091  	// Verify preGenLambdaG[0] = λ*G
1092  	var lambdaG GroupElementAffine
1093  	lambdaG.mulLambda(&Generator)
1094  	lambdaG.x.normalize()
1095  	lambdaG.y.normalize()
1096  
1097  	preGenLambdaG[0].x.normalize()
1098  	preGenLambdaG[0].y.normalize()
1099  
1100  	if !preGenLambdaG[0].equal(&lambdaG) {
1101  		t.Error("preGenLambdaG[0] should equal λ*G")
1102  	}
1103  
1104  	// Verify preGenLambdaG[i] = (2*i+1)*(λ*G)
1105  	for i := 0; i < 8; i++ {
1106  		var scalar Scalar
1107  		scalar.setInt(uint(2*i + 1))
1108  
1109  		var expected GroupElementJacobian
1110  		EcmultConst(&expected, &lambdaG, &scalar)
1111  
1112  		var expectedAff GroupElementAffine
1113  		expectedAff.setGEJ(&expected)
1114  		expectedAff.x.normalize()
1115  		expectedAff.y.normalize()
1116  
1117  		preGenLambdaG[i].x.normalize()
1118  		preGenLambdaG[i].y.normalize()
1119  
1120  		if !preGenLambdaG[i].equal(&expectedAff) {
1121  			t.Errorf("preGenLambdaG[%d] ≠ %d*(λ*G)", i, 2*i+1)
1122  		}
1123  	}
1124  }
1125  
1126  // TestEcmultGenGLV tests the GLV generator multiplication
1127  func TestEcmultGenGLV(t *testing.T) {
1128  	testCases := []string{
1129  		"0000000000000000000000000000000000000000000000000000000000000001", // 1
1130  		"0000000000000000000000000000000000000000000000000000000000000002", // 2
1131  		"0000000000000000000000000000000000000000000000000000000000000010", // 16
1132  		"00000000000000000000000000000000000000000000000000000000000000ff", // 255
1133  		"e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35", // random
1134  		"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", // n-1
1135  		"7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", // n/2
1136  	}
1137  
1138  	for _, hexStr := range testCases {
1139  		t.Run(hexStr[:8]+"...", func(t *testing.T) {
1140  			// Parse scalar
1141  			sBytes, _ := hex.DecodeString(hexStr)
1142  			var s Scalar
1143  			s.setB32(sBytes)
1144  
1145  			// Compute using GLV generator multiplication
1146  			var resultGLV GroupElementJacobian
1147  			ecmultGenGLV(&resultGLV, &s)
1148  
1149  			// Compute using reference implementation
1150  			var resultRef GroupElementJacobian
1151  			EcmultConst(&resultRef, &Generator, &s)
1152  
1153  			// Convert to affine for comparison
1154  			var resultGLVAff, resultRefAff GroupElementAffine
1155  			resultGLVAff.setGEJ(&resultGLV)
1156  			resultRefAff.setGEJ(&resultRef)
1157  			resultGLVAff.x.normalize()
1158  			resultGLVAff.y.normalize()
1159  			resultRefAff.x.normalize()
1160  			resultRefAff.y.normalize()
1161  
1162  			if !resultGLVAff.equal(&resultRefAff) {
1163  				var gotX, wantX [32]byte
1164  				resultGLVAff.x.getB32(gotX[:])
1165  				resultRefAff.x.getB32(wantX[:])
1166  				t.Errorf("GLV gen result mismatch:\n  got x:  %s\n  want x: %s",
1167  					hex.EncodeToString(gotX[:]), hex.EncodeToString(wantX[:]))
1168  			}
1169  		})
1170  	}
1171  }
1172  
1173  // TestEcmultGenGLVRandom tests GLV generator multiplication with random scalars
1174  func TestEcmultGenGLVRandom(t *testing.T) {
1175  	for i := 0; i < 50; i++ {
1176  		// Generate pseudo-random scalar
1177  		sha := NewSHA256()
1178  		sha.Write([]byte{byte(i), byte(i >> 8), 0xBE, 0xEF})
1179  		var sBytes [32]byte
1180  		sha.Finalize(sBytes[:])
1181  
1182  		var s Scalar
1183  		s.setB32(sBytes[:])
1184  
1185  		// Compute using GLV
1186  		var resultGLV GroupElementJacobian
1187  		ecmultGenGLV(&resultGLV, &s)
1188  
1189  		// Compute using reference
1190  		var resultRef GroupElementJacobian
1191  		EcmultConst(&resultRef, &Generator, &s)
1192  
1193  		// Compare
1194  		var resultGLVAff, resultRefAff GroupElementAffine
1195  		resultGLVAff.setGEJ(&resultGLV)
1196  		resultRefAff.setGEJ(&resultRef)
1197  		resultGLVAff.x.normalize()
1198  		resultGLVAff.y.normalize()
1199  		resultRefAff.x.normalize()
1200  		resultRefAff.y.normalize()
1201  
1202  		if !resultGLVAff.equal(&resultRefAff) {
1203  			t.Errorf("Iteration %d: GLV gen result mismatch", i)
1204  		}
1205  	}
1206  }
1207  
1208  // TestEcmultGenSimple tests the simple generator multiplication
1209  func TestEcmultGenSimple(t *testing.T) {
1210  	testCases := []string{
1211  		"0000000000000000000000000000000000000000000000000000000000000001",
1212  		"00000000000000000000000000000000000000000000000000000000000000ff",
1213  		"e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35",
1214  	}
1215  
1216  	for _, hexStr := range testCases {
1217  		t.Run(hexStr[:8]+"...", func(t *testing.T) {
1218  			sBytes, _ := hex.DecodeString(hexStr)
1219  			var s Scalar
1220  			s.setB32(sBytes)
1221  
1222  			// Compute using simple generator multiplication
1223  			var resultSimple GroupElementJacobian
1224  			ecmultGenSimple(&resultSimple, &s)
1225  
1226  			// Compute using reference
1227  			var resultRef GroupElementJacobian
1228  			EcmultConst(&resultRef, &Generator, &s)
1229  
1230  			// Compare
1231  			var resultSimpleAff, resultRefAff GroupElementAffine
1232  			resultSimpleAff.setGEJ(&resultSimple)
1233  			resultRefAff.setGEJ(&resultRef)
1234  			resultSimpleAff.x.normalize()
1235  			resultSimpleAff.y.normalize()
1236  			resultRefAff.x.normalize()
1237  			resultRefAff.y.normalize()
1238  
1239  			if !resultSimpleAff.equal(&resultRefAff) {
1240  				t.Errorf("Simple gen result mismatch")
1241  			}
1242  		})
1243  	}
1244  }
1245  
1246  // TestEcmultGenGLVEdgeCases tests edge cases
1247  func TestEcmultGenGLVEdgeCases(t *testing.T) {
1248  	// Test with zero scalar
1249  	var zero Scalar
1250  	var result GroupElementJacobian
1251  	ecmultGenGLV(&result, &zero)
1252  	if !result.isInfinity() {
1253  		t.Error("0 * G should be infinity")
1254  	}
1255  
1256  	// Test with scalar 1
1257  	var one Scalar
1258  	one.setInt(1)
1259  	ecmultGenGLV(&result, &one)
1260  
1261  	var resultAff GroupElementAffine
1262  	resultAff.setGEJ(&result)
1263  	resultAff.x.normalize()
1264  	resultAff.y.normalize()
1265  
1266  	genNorm := Generator
1267  	genNorm.x.normalize()
1268  	genNorm.y.normalize()
1269  
1270  	if !resultAff.equal(&genNorm) {
1271  		t.Error("1 * G should equal G")
1272  	}
1273  }
1274  
1275  // BenchmarkEcmultGenGLV benchmarks the GLV generator multiplication
1276  func BenchmarkEcmultGenGLV(b *testing.B) {
1277  	sBytes, _ := hex.DecodeString("e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35")
1278  	var s Scalar
1279  	s.setB32(sBytes)
1280  
1281  	// Ensure tables are initialized before benchmark
1282  	EnsureGenTablesInitialized()
1283  
1284  	var result GroupElementJacobian
1285  
1286  	b.ResetTimer()
1287  	for i := 0; i < b.N; i++ {
1288  		ecmultGenGLV(&result, &s)
1289  	}
1290  }
1291  
1292  // BenchmarkEcmultGenSimple benchmarks the simple generator multiplication
1293  func BenchmarkEcmultGenSimple(b *testing.B) {
1294  	sBytes, _ := hex.DecodeString("e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35")
1295  	var s Scalar
1296  	s.setB32(sBytes)
1297  
1298  	// Ensure tables are initialized before benchmark
1299  	EnsureGenTablesInitialized()
1300  
1301  	var result GroupElementJacobian
1302  
1303  	b.ResetTimer()
1304  	for i := 0; i < b.N; i++ {
1305  		ecmultGenSimple(&result, &s)
1306  	}
1307  }
1308  
1309  // BenchmarkEcmultGenConstRef benchmarks the reference constant-time implementation for G
1310  func BenchmarkEcmultGenConstRef(b *testing.B) {
1311  	sBytes, _ := hex.DecodeString("e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35")
1312  	var s Scalar
1313  	s.setB32(sBytes)
1314  
1315  	var result GroupElementJacobian
1316  
1317  	b.ResetTimer()
1318  	for i := 0; i < b.N; i++ {
1319  		EcmultConst(&result, &Generator, &s)
1320  	}
1321  }
1322  
1323  // =============================================================================
1324  // Phase 6: Cross-Validation Tests
1325  // =============================================================================
1326  
1327  // TestCrossValidationAllImplementations verifies that all scalar multiplication
1328  // implementations produce the same results
1329  func TestCrossValidationAllImplementations(t *testing.T) {
1330  	testCases := []struct {
1331  		name   string
1332  		scalar string
1333  	}{
1334  		{"one", "0000000000000000000000000000000000000000000000000000000000000001"},
1335  		{"two", "0000000000000000000000000000000000000000000000000000000000000002"},
1336  		{"random1", "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35"},
1337  		{"random2", "deadbeefcafebabe1234567890abcdef0123456789abcdef0123456789abcdef"},
1338  		{"high_bits", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
1339  		{"half_order", "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0"},
1340  		{"small", "0000000000000000000000000000000000000000000000000000000000000010"},
1341  	}
1342  
1343  	for _, tc := range testCases {
1344  		t.Run(tc.name, func(t *testing.T) {
1345  			sBytes, _ := hex.DecodeString(tc.scalar)
1346  			var s Scalar
1347  			s.setB32(sBytes)
1348  
1349  			if s.isZero() {
1350  				return // Skip zero scalar
1351  			}
1352  
1353  			// Reference: EcmultConst (constant-time binary method)
1354  			var refResult GroupElementJacobian
1355  			EcmultConst(&refResult, &Generator, &s)
1356  			var refAff GroupElementAffine
1357  			refAff.setGEJ(&refResult)
1358  			refAff.x.normalize()
1359  			refAff.y.normalize()
1360  
1361  			// Test 1: ecmultGenGLV (GLV generator multiplication)
1362  			var glvResult GroupElementJacobian
1363  			ecmultGenGLV(&glvResult, &s)
1364  			var glvAff GroupElementAffine
1365  			glvAff.setGEJ(&glvResult)
1366  			glvAff.x.normalize()
1367  			glvAff.y.normalize()
1368  
1369  			if !refAff.x.equal(&glvAff.x) || !refAff.y.equal(&glvAff.y) {
1370  				t.Errorf("ecmultGenGLV mismatch for %s", tc.name)
1371  			}
1372  
1373  			// Test 2: ecmultGenSimple (simple generator multiplication)
1374  			var simpleResult GroupElementJacobian
1375  			ecmultGenSimple(&simpleResult, &s)
1376  			var simpleAff GroupElementAffine
1377  			simpleAff.setGEJ(&simpleResult)
1378  			simpleAff.x.normalize()
1379  			simpleAff.y.normalize()
1380  
1381  			if !refAff.x.equal(&simpleAff.x) || !refAff.y.equal(&simpleAff.y) {
1382  				t.Errorf("ecmultGenSimple mismatch for %s", tc.name)
1383  			}
1384  
1385  			// Test 3: EcmultGen (public interface)
1386  			var publicResult GroupElementJacobian
1387  			EcmultGen(&publicResult, &s)
1388  			var publicAff GroupElementAffine
1389  			publicAff.setGEJ(&publicResult)
1390  			publicAff.x.normalize()
1391  			publicAff.y.normalize()
1392  
1393  			if !refAff.x.equal(&publicAff.x) || !refAff.y.equal(&publicAff.y) {
1394  				t.Errorf("EcmultGen mismatch for %s", tc.name)
1395  			}
1396  
1397  			// Test 4: ecmultStraussWNAFGLV with Generator
1398  			var straussResult GroupElementJacobian
1399  			ecmultStraussWNAFGLV(&straussResult, &Generator, &s)
1400  			var straussAff GroupElementAffine
1401  			straussAff.setGEJ(&straussResult)
1402  			straussAff.x.normalize()
1403  			straussAff.y.normalize()
1404  
1405  			if !refAff.x.equal(&straussAff.x) || !refAff.y.equal(&straussAff.y) {
1406  				t.Errorf("ecmultStraussWNAFGLV mismatch for %s", tc.name)
1407  			}
1408  
1409  			// Test 5: Ecmult (public interface with Jacobian input)
1410  			var genJac GroupElementJacobian
1411  			genJac.setGE(&Generator)
1412  			var ecmultResult GroupElementJacobian
1413  			Ecmult(&ecmultResult, &genJac, &s)
1414  			var ecmultAff GroupElementAffine
1415  			ecmultAff.setGEJ(&ecmultResult)
1416  			ecmultAff.x.normalize()
1417  			ecmultAff.y.normalize()
1418  
1419  			if !refAff.x.equal(&ecmultAff.x) || !refAff.y.equal(&ecmultAff.y) {
1420  				t.Errorf("Ecmult mismatch for %s", tc.name)
1421  			}
1422  
1423  			// Test 6: ecmultWindowedVar
1424  			var windowedResult GroupElementJacobian
1425  			ecmultWindowedVar(&windowedResult, &Generator, &s)
1426  			var windowedAff GroupElementAffine
1427  			windowedAff.setGEJ(&windowedResult)
1428  			windowedAff.x.normalize()
1429  			windowedAff.y.normalize()
1430  
1431  			if !refAff.x.equal(&windowedAff.x) || !refAff.y.equal(&windowedAff.y) {
1432  				t.Errorf("ecmultWindowedVar mismatch for %s", tc.name)
1433  			}
1434  		})
1435  	}
1436  }
1437  
1438  // TestCrossValidationArbitraryPoint tests all implementations with a non-generator point
1439  func TestCrossValidationArbitraryPoint(t *testing.T) {
1440  	// Create an arbitrary point P = 7*G
1441  	var sevenScalar Scalar
1442  	sevenScalar.d[0] = 7
1443  
1444  	var pJac GroupElementJacobian
1445  	EcmultConst(&pJac, &Generator, &sevenScalar)
1446  	var p GroupElementAffine
1447  	p.setGEJ(&pJac)
1448  	p.x.normalize()
1449  	p.y.normalize()
1450  
1451  	testCases := []struct {
1452  		name   string
1453  		scalar string
1454  	}{
1455  		{"one", "0000000000000000000000000000000000000000000000000000000000000001"},
1456  		{"random1", "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35"},
1457  		{"random2", "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"},
1458  	}
1459  
1460  	for _, tc := range testCases {
1461  		t.Run(tc.name, func(t *testing.T) {
1462  			sBytes, _ := hex.DecodeString(tc.scalar)
1463  			var s Scalar
1464  			s.setB32(sBytes)
1465  
1466  			if s.isZero() {
1467  				return
1468  			}
1469  
1470  			// Reference: EcmultConst
1471  			var refResult GroupElementJacobian
1472  			EcmultConst(&refResult, &p, &s)
1473  			var refAff GroupElementAffine
1474  			refAff.setGEJ(&refResult)
1475  			refAff.x.normalize()
1476  			refAff.y.normalize()
1477  
1478  			// Test 1: ecmultStraussWNAFGLV
1479  			var straussResult GroupElementJacobian
1480  			ecmultStraussWNAFGLV(&straussResult, &p, &s)
1481  			var straussAff GroupElementAffine
1482  			straussAff.setGEJ(&straussResult)
1483  			straussAff.x.normalize()
1484  			straussAff.y.normalize()
1485  
1486  			if !refAff.x.equal(&straussAff.x) || !refAff.y.equal(&straussAff.y) {
1487  				t.Errorf("ecmultStraussWNAFGLV mismatch for arbitrary point with %s", tc.name)
1488  			}
1489  
1490  			// Test 2: ecmultWindowedVar
1491  			var windowedResult GroupElementJacobian
1492  			ecmultWindowedVar(&windowedResult, &p, &s)
1493  			var windowedAff GroupElementAffine
1494  			windowedAff.setGEJ(&windowedResult)
1495  			windowedAff.x.normalize()
1496  			windowedAff.y.normalize()
1497  
1498  			if !refAff.x.equal(&windowedAff.x) || !refAff.y.equal(&windowedAff.y) {
1499  				t.Errorf("ecmultWindowedVar mismatch for arbitrary point with %s", tc.name)
1500  			}
1501  
1502  			// Test 3: Ecmult (public interface)
1503  			var pJac2 GroupElementJacobian
1504  			pJac2.setGE(&p)
1505  			var ecmultResult GroupElementJacobian
1506  			Ecmult(&ecmultResult, &pJac2, &s)
1507  			var ecmultAff GroupElementAffine
1508  			ecmultAff.setGEJ(&ecmultResult)
1509  			ecmultAff.x.normalize()
1510  			ecmultAff.y.normalize()
1511  
1512  			if !refAff.x.equal(&ecmultAff.x) || !refAff.y.equal(&ecmultAff.y) {
1513  				t.Errorf("Ecmult mismatch for arbitrary point with %s", tc.name)
1514  			}
1515  		})
1516  	}
1517  }
1518  
1519  // TestCrossValidationRandomScalars tests with random scalars
1520  func TestCrossValidationRandomScalars(t *testing.T) {
1521  	seed := uint64(0xdeadbeef12345678)
1522  
1523  	for i := 0; i < 50; i++ {
1524  		// Generate pseudo-random scalar
1525  		var sBytes [32]byte
1526  		for j := 0; j < 32; j++ {
1527  			seed = seed*6364136223846793005 + 1442695040888963407
1528  			sBytes[j] = byte(seed >> 56)
1529  		}
1530  
1531  		var s Scalar
1532  		s.setB32(sBytes[:])
1533  
1534  		if s.isZero() {
1535  			continue
1536  		}
1537  
1538  		// Reference
1539  		var refResult GroupElementJacobian
1540  		EcmultConst(&refResult, &Generator, &s)
1541  		var refAff GroupElementAffine
1542  		refAff.setGEJ(&refResult)
1543  		refAff.x.normalize()
1544  		refAff.y.normalize()
1545  
1546  		// GLV
1547  		var glvResult GroupElementJacobian
1548  		ecmultGenGLV(&glvResult, &s)
1549  		var glvAff GroupElementAffine
1550  		glvAff.setGEJ(&glvResult)
1551  		glvAff.x.normalize()
1552  		glvAff.y.normalize()
1553  
1554  		if !refAff.x.equal(&glvAff.x) || !refAff.y.equal(&glvAff.y) {
1555  			var sHex [32]byte
1556  			s.getB32(sHex[:])
1557  			t.Errorf("Random test %d failed: scalar=%s", i, hex.EncodeToString(sHex[:]))
1558  		}
1559  	}
1560  }
1561  
1562  // =============================================================================
1563  // Phase 6: Property-Based Tests
1564  // =============================================================================
1565  
1566  // TestPropertyScalarSplitReconstruction verifies that k1 + k2*λ ≡ k (mod n)
1567  func TestPropertyScalarSplitReconstruction(t *testing.T) {
1568  	seed := uint64(0xcafebabe98765432)
1569  
1570  	for i := 0; i < 100; i++ {
1571  		// Generate pseudo-random scalar
1572  		var kBytes [32]byte
1573  		for j := 0; j < 32; j++ {
1574  			seed = seed*6364136223846793005 + 1442695040888963407
1575  			kBytes[j] = byte(seed >> 56)
1576  		}
1577  
1578  		var k Scalar
1579  		k.setB32(kBytes[:])
1580  
1581  		if k.isZero() {
1582  			continue
1583  		}
1584  
1585  		// Split k
1586  		var k1, k2 Scalar
1587  		scalarSplitLambda(&k1, &k2, &k)
1588  
1589  		// Compute k2 * λ
1590  		var k2Lambda Scalar
1591  		k2Lambda.mul(&k2, &scalarLambda)
1592  
1593  		// Compute k1 + k2*λ
1594  		var reconstructed Scalar
1595  		reconstructed.add(&k1, &k2Lambda)
1596  
1597  		// Verify k1 + k2*λ ≡ k (mod n)
1598  		if !reconstructed.equal(&k) {
1599  			var kHex, rHex [32]byte
1600  			k.getB32(kHex[:])
1601  			reconstructed.getB32(rHex[:])
1602  			t.Errorf("Scalar split reconstruction failed:\n  k=%s\n  reconstructed=%s",
1603  				hex.EncodeToString(kHex[:]), hex.EncodeToString(rHex[:]))
1604  		}
1605  	}
1606  }
1607  
1608  // TestPropertyLambdaEndomorphism verifies that λ·P = (β·x, y) for points on the curve
1609  func TestPropertyLambdaEndomorphism(t *testing.T) {
1610  	seed := uint64(0x1234567890abcdef)
1611  
1612  	for i := 0; i < 50; i++ {
1613  		// Generate a random point by multiplying G by a random scalar
1614  		var sBytes [32]byte
1615  		for j := 0; j < 32; j++ {
1616  			seed = seed*6364136223846793005 + 1442695040888963407
1617  			sBytes[j] = byte(seed >> 56)
1618  		}
1619  
1620  		var s Scalar
1621  		s.setB32(sBytes[:])
1622  
1623  		if s.isZero() {
1624  			continue
1625  		}
1626  
1627  		// P = s * G
1628  		var pJac GroupElementJacobian
1629  		EcmultConst(&pJac, &Generator, &s)
1630  		var p GroupElementAffine
1631  		p.setGEJ(&pJac)
1632  		p.x.normalize()
1633  		p.y.normalize()
1634  
1635  		if p.isInfinity() {
1636  			continue
1637  		}
1638  
1639  		// Method 1: λ·P via scalar multiplication
1640  		var lambdaPJac GroupElementJacobian
1641  		EcmultConst(&lambdaPJac, &p, &scalarLambda)
1642  		var lambdaP1 GroupElementAffine
1643  		lambdaP1.setGEJ(&lambdaPJac)
1644  		lambdaP1.x.normalize()
1645  		lambdaP1.y.normalize()
1646  
1647  		// Method 2: λ·P via endomorphism (β·x, y)
1648  		var lambdaP2 GroupElementAffine
1649  		lambdaP2.mulLambda(&p)
1650  		lambdaP2.x.normalize()
1651  		lambdaP2.y.normalize()
1652  
1653  		// Verify they match
1654  		if !lambdaP1.x.equal(&lambdaP2.x) || !lambdaP1.y.equal(&lambdaP2.y) {
1655  			t.Errorf("Lambda endomorphism mismatch at iteration %d", i)
1656  		}
1657  	}
1658  }
1659  
1660  // TestPropertyBetaCubeRoot verifies that β^3 ≡ 1 (mod p)
1661  func TestPropertyBetaCubeRoot(t *testing.T) {
1662  	// Compute β^2
1663  	var beta2 FieldElement
1664  	beta2.mul(&fieldBeta, &fieldBeta)
1665  
1666  	// Compute β^3 = β^2 * β
1667  	var beta3 FieldElement
1668  	beta3.mul(&beta2, &fieldBeta)
1669  	beta3.normalize()
1670  
1671  	// Check if β^3 ≡ 1 (mod p)
1672  	if !beta3.equal(&FieldElementOne) {
1673  		var bytes [32]byte
1674  		beta3.getB32(bytes[:])
1675  		t.Errorf("β^3 ≠ 1 (mod p), got: %s", hex.EncodeToString(bytes[:]))
1676  	}
1677  }
1678  
1679  // TestPropertyDoubleNegate verifies that -(-P) = P
1680  func TestPropertyDoubleNegate(t *testing.T) {
1681  	seed := uint64(0xfedcba0987654321)
1682  
1683  	for i := 0; i < 50; i++ {
1684  		// Generate a random point
1685  		var sBytes [32]byte
1686  		for j := 0; j < 32; j++ {
1687  			seed = seed*6364136223846793005 + 1442695040888963407
1688  			sBytes[j] = byte(seed >> 56)
1689  		}
1690  
1691  		var s Scalar
1692  		s.setB32(sBytes[:])
1693  
1694  		if s.isZero() {
1695  			continue
1696  		}
1697  
1698  		var pJac GroupElementJacobian
1699  		ecmultGenGLV(&pJac, &s)
1700  		var p GroupElementAffine
1701  		p.setGEJ(&pJac)
1702  		p.x.normalize()
1703  		p.y.normalize()
1704  
1705  		if p.isInfinity() {
1706  			continue
1707  		}
1708  
1709  		// Negate twice
1710  		var negP, negNegP GroupElementAffine
1711  		negP.negate(&p)
1712  		negNegP.negate(&negP)
1713  		negNegP.x.normalize()
1714  		negNegP.y.normalize()
1715  
1716  		// Verify -(-P) = P
1717  		if !p.x.equal(&negNegP.x) || !p.y.equal(&negNegP.y) {
1718  			t.Errorf("Double negation property failed at iteration %d", i)
1719  		}
1720  	}
1721  }
1722  
1723  // TestPropertyScalarAddition verifies that (a+b)*G = a*G + b*G
1724  func TestPropertyScalarAddition(t *testing.T) {
1725  	seed := uint64(0xabcdef1234567890)
1726  
1727  	for i := 0; i < 50; i++ {
1728  		// Generate two random scalars
1729  		var aBytes, bBytes [32]byte
1730  		for j := 0; j < 32; j++ {
1731  			seed = seed*6364136223846793005 + 1442695040888963407
1732  			aBytes[j] = byte(seed >> 56)
1733  			seed = seed*6364136223846793005 + 1442695040888963407
1734  			bBytes[j] = byte(seed >> 56)
1735  		}
1736  
1737  		var a, b Scalar
1738  		a.setB32(aBytes[:])
1739  		b.setB32(bBytes[:])
1740  
1741  		// Compute (a+b)*G
1742  		var sum Scalar
1743  		sum.add(&a, &b)
1744  
1745  		var sumG GroupElementJacobian
1746  		ecmultGenGLV(&sumG, &sum)
1747  		var sumGAff GroupElementAffine
1748  		sumGAff.setGEJ(&sumG)
1749  		sumGAff.x.normalize()
1750  		sumGAff.y.normalize()
1751  
1752  		// Compute a*G + b*G
1753  		var aG, bG GroupElementJacobian
1754  		ecmultGenGLV(&aG, &a)
1755  		ecmultGenGLV(&bG, &b)
1756  
1757  		var aGplusBG GroupElementJacobian
1758  		aGplusBG.addVar(&aG, &bG)
1759  		var aGplusBGAff GroupElementAffine
1760  		aGplusBGAff.setGEJ(&aGplusBG)
1761  		aGplusBGAff.x.normalize()
1762  		aGplusBGAff.y.normalize()
1763  
1764  		// Verify (a+b)*G = a*G + b*G
1765  		if sumGAff.isInfinity() != aGplusBGAff.isInfinity() {
1766  			t.Errorf("Scalar addition property failed at iteration %d (infinity mismatch)", i)
1767  			continue
1768  		}
1769  		if !sumGAff.isInfinity() {
1770  			if !sumGAff.x.equal(&aGplusBGAff.x) || !sumGAff.y.equal(&aGplusBGAff.y) {
1771  				t.Errorf("Scalar addition property failed at iteration %d", i)
1772  			}
1773  		}
1774  	}
1775  }
1776  
1777  // TestPropertyScalarMultiplication verifies that (a*b)*G = a*(b*G)
1778  func TestPropertyScalarMultiplication(t *testing.T) {
1779  	seed := uint64(0x9876543210fedcba)
1780  
1781  	for i := 0; i < 50; i++ {
1782  		// Generate two random scalars
1783  		var aBytes, bBytes [32]byte
1784  		for j := 0; j < 32; j++ {
1785  			seed = seed*6364136223846793005 + 1442695040888963407
1786  			aBytes[j] = byte(seed >> 56)
1787  			seed = seed*6364136223846793005 + 1442695040888963407
1788  			bBytes[j] = byte(seed >> 56)
1789  		}
1790  
1791  		var a, b Scalar
1792  		a.setB32(aBytes[:])
1793  		b.setB32(bBytes[:])
1794  
1795  		// Compute (a*b)*G
1796  		var product Scalar
1797  		product.mul(&a, &b)
1798  
1799  		var productG GroupElementJacobian
1800  		ecmultGenGLV(&productG, &product)
1801  		var productGAff GroupElementAffine
1802  		productGAff.setGEJ(&productG)
1803  		productGAff.x.normalize()
1804  		productGAff.y.normalize()
1805  
1806  		// Compute a*(b*G)
1807  		var bG GroupElementJacobian
1808  		ecmultGenGLV(&bG, &b)
1809  		var bGAff GroupElementAffine
1810  		bGAff.setGEJ(&bG)
1811  		bGAff.x.normalize()
1812  		bGAff.y.normalize()
1813  
1814  		var aBG GroupElementJacobian
1815  		ecmultStraussWNAFGLV(&aBG, &bGAff, &a)
1816  		var aBGAff GroupElementAffine
1817  		aBGAff.setGEJ(&aBG)
1818  		aBGAff.x.normalize()
1819  		aBGAff.y.normalize()
1820  
1821  		// Verify (a*b)*G = a*(b*G)
1822  		if productGAff.isInfinity() != aBGAff.isInfinity() {
1823  			t.Errorf("Scalar multiplication property failed at iteration %d (infinity mismatch)", i)
1824  			continue
1825  		}
1826  		if !productGAff.isInfinity() {
1827  			if !productGAff.x.equal(&aBGAff.x) || !productGAff.y.equal(&aBGAff.y) {
1828  				t.Errorf("Scalar multiplication property failed at iteration %d", i)
1829  			}
1830  		}
1831  	}
1832  }
1833  
1834  // TestEcmultCombined tests the combined na*a + ng*G function
1835  func TestEcmultCombined(t *testing.T) {
1836  	testCases := []struct {
1837  		name string
1838  		na   string
1839  		ng   string
1840  	}{
1841  		{"both_one", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001"},
1842  		{"na_only", "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35", "0000000000000000000000000000000000000000000000000000000000000000"},
1843  		{"ng_only", "0000000000000000000000000000000000000000000000000000000000000000", "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35"},
1844  		{"random_both", "deadbeefcafebabe1234567890abcdef0123456789abcdef0123456789abcdef", "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"},
1845  	}
1846  
1847  	// Create an arbitrary point P = 7*G
1848  	var sevenScalar Scalar
1849  	sevenScalar.d[0] = 7
1850  	var pJac GroupElementJacobian
1851  	EcmultConst(&pJac, &Generator, &sevenScalar)
1852  
1853  	for _, tc := range testCases {
1854  		t.Run(tc.name, func(t *testing.T) {
1855  			naBytes, _ := hex.DecodeString(tc.na)
1856  			ngBytes, _ := hex.DecodeString(tc.ng)
1857  
1858  			var na, ng Scalar
1859  			na.setB32(naBytes)
1860  			ng.setB32(ngBytes)
1861  
1862  			// Compute using EcmultCombined
1863  			var combined GroupElementJacobian
1864  			EcmultCombined(&combined, &pJac, &na, &ng)
1865  			var combinedAff GroupElementAffine
1866  			combinedAff.setGEJ(&combined)
1867  			combinedAff.x.normalize()
1868  			combinedAff.y.normalize()
1869  
1870  			// Compute reference: na*P + ng*G separately
1871  			var naP, ngG, ref GroupElementJacobian
1872  
1873  			if !na.isZero() {
1874  				var pAff GroupElementAffine
1875  				pAff.setGEJ(&pJac)
1876  				EcmultConst(&naP, &pAff, &na)
1877  			} else {
1878  				naP.setInfinity()
1879  			}
1880  
1881  			if !ng.isZero() {
1882  				EcmultConst(&ngG, &Generator, &ng)
1883  			} else {
1884  				ngG.setInfinity()
1885  			}
1886  
1887  			ref.addVar(&naP, &ngG)
1888  			var refAff GroupElementAffine
1889  			refAff.setGEJ(&ref)
1890  			refAff.x.normalize()
1891  			refAff.y.normalize()
1892  
1893  			// Verify
1894  			if combinedAff.isInfinity() != refAff.isInfinity() {
1895  				t.Errorf("EcmultCombined infinity mismatch for %s", tc.name)
1896  				return
1897  			}
1898  
1899  			if !combinedAff.isInfinity() {
1900  				if !combinedAff.x.equal(&refAff.x) || !combinedAff.y.equal(&refAff.y) {
1901  					t.Errorf("EcmultCombined result mismatch for %s", tc.name)
1902  				}
1903  			}
1904  		})
1905  	}
1906  }
1907  
1908  // BenchmarkEcmultCombined benchmarks the combined multiplication
1909  func BenchmarkEcmultCombined(b *testing.B) {
1910  	naBytes, _ := hex.DecodeString("e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35")
1911  	ngBytes, _ := hex.DecodeString("deadbeefcafebabe1234567890abcdef0123456789abcdef0123456789abcdef")
1912  
1913  	var na, ng Scalar
1914  	na.setB32(naBytes)
1915  	ng.setB32(ngBytes)
1916  
1917  	// Create point P = 7*G
1918  	var sevenScalar Scalar
1919  	sevenScalar.d[0] = 7
1920  	var pJac GroupElementJacobian
1921  	EcmultConst(&pJac, &Generator, &sevenScalar)
1922  
1923  	EnsureGenTablesInitialized()
1924  
1925  	var result GroupElementJacobian
1926  
1927  	b.ResetTimer()
1928  	for i := 0; i < b.N; i++ {
1929  		EcmultCombined(&result, &pJac, &na, &ng)
1930  	}
1931  }
1932  
1933  // BenchmarkEcmultSeparate benchmarks separate na*P and ng*G computations
1934  func BenchmarkEcmultSeparate(b *testing.B) {
1935  	naBytes, _ := hex.DecodeString("e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35")
1936  	ngBytes, _ := hex.DecodeString("deadbeefcafebabe1234567890abcdef0123456789abcdef0123456789abcdef")
1937  
1938  	var na, ng Scalar
1939  	na.setB32(naBytes)
1940  	ng.setB32(ngBytes)
1941  
1942  	// Create point P = 7*G
1943  	var sevenScalar Scalar
1944  	sevenScalar.d[0] = 7
1945  	var pJac GroupElementJacobian
1946  	EcmultConst(&pJac, &Generator, &sevenScalar)
1947  	var pAff GroupElementAffine
1948  	pAff.setGEJ(&pJac)
1949  
1950  	EnsureGenTablesInitialized()
1951  
1952  	var naP, ngG, result GroupElementJacobian
1953  
1954  	b.ResetTimer()
1955  	for i := 0; i < b.N; i++ {
1956  		ecmultStraussWNAFGLV(&naP, &pAff, &na)
1957  		ecmultGenGLV(&ngG, &ng)
1958  		result.addVar(&naP, &ngG)
1959  	}
1960  }
1961