package avx import ( "bytes" "crypto/rand" "encoding/hex" "testing" ) // Test vectors from Bitcoin/secp256k1 func TestUint128Add(t *testing.T) { tests := []struct { a, b Uint128 expect Uint128 carry uint64 }{ {Uint128{0, 0}, Uint128{0, 0}, Uint128{0, 0}, 0}, {Uint128{1, 0}, Uint128{1, 0}, Uint128{2, 0}, 0}, {Uint128{^uint64(0), 0}, Uint128{1, 0}, Uint128{0, 1}, 0}, {Uint128{^uint64(0), ^uint64(0)}, Uint128{1, 0}, Uint128{0, 0}, 1}, } for i, tt := range tests { result, carry := tt.a.Add(tt.b) if result != tt.expect || carry != tt.carry { t.Errorf("test %d: got (%v, %d), want (%v, %d)", i, result, carry, tt.expect, tt.carry) } } } func TestUint128Mul(t *testing.T) { // Test: 2^64 * 2^64 = 2^128 a := Uint128{0, 1} // 2^64 b := Uint128{0, 1} // 2^64 result := a.Mul(b) // Expected: 2^128 = [0, 0, 1, 0] expected := [4]uint64{0, 0, 1, 0} if result != expected { t.Errorf("2^64 * 2^64: got %v, want %v", result, expected) } // Test: (2^64 - 1) * (2^64 - 1) a = Uint128{^uint64(0), 0} b = Uint128{^uint64(0), 0} result = a.Mul(b) // (2^64 - 1)^2 = 2^128 - 2^65 + 1 // = [1, 0xFFFFFFFFFFFFFFFE, 0, 0] expected = [4]uint64{1, 0xFFFFFFFFFFFFFFFE, 0, 0} if result != expected { t.Errorf("(2^64-1)^2: got %v, want %v", result, expected) } } func TestScalarSetBytes(t *testing.T) { // Test with a known scalar bytes32 := make([]byte, 32) bytes32[31] = 1 // scalar = 1 var s Scalar s.SetBytes(bytes32) if !s.IsOne() { t.Errorf("expected scalar to be 1, got %+v", s) } // Test zero bytes32 = make([]byte, 32) s.SetBytes(bytes32) if !s.IsZero() { t.Errorf("expected scalar to be 0, got %+v", s) } } func TestScalarAddSub(t *testing.T) { var a, b, sum, diff, recovered Scalar // a = 1, b = 2 a = ScalarOne b.D[0].Lo = 2 sum.Add(&a, &b) if sum.D[0].Lo != 3 { t.Errorf("1 + 2: expected 3, got %d", sum.D[0].Lo) } diff.Sub(&sum, &b) if !diff.Equal(&a) { t.Errorf("(1+2) - 2: expected 1, got %+v", diff) } // Test with overflow a = ScalarN a.D[0].Lo-- // n - 1 b = ScalarOne sum.Add(&a, &b) // n - 1 + 1 = n ≡ 0 (mod n) if !sum.IsZero() { t.Errorf("(n-1) + 1 should be 0 mod n, got %+v", sum) } // Test subtraction with borrow a = ScalarZero b = ScalarOne diff.Sub(&a, &b) // 0 - 1 = -1 ≡ n - 1 (mod n) recovered.Add(&diff, &b) if !recovered.IsZero() { t.Errorf("(0-1) + 1 should be 0, got %+v", recovered) } } func TestScalarMul(t *testing.T) { var a, b, product Scalar // 2 * 3 = 6 a.D[0].Lo = 2 b.D[0].Lo = 3 product.Mul(&a, &b) if product.D[0].Lo != 6 || product.D[0].Hi != 0 || !product.D[1].IsZero() { t.Errorf("2 * 3: expected 6, got %+v", product) } // Test with larger values a.D[0].Lo = 0xFFFFFFFFFFFFFFFF a.D[0].Hi = 0 b.D[0].Lo = 2 product.Mul(&a, &b) // (2^64 - 1) * 2 = 2^65 - 2 if product.D[0].Lo != 0xFFFFFFFFFFFFFFFE || product.D[0].Hi != 1 { t.Errorf("(2^64-1) * 2: got %+v", product) } } func TestScalarNegate(t *testing.T) { var a, neg, sum Scalar a.D[0].Lo = 12345 neg.Negate(&a) sum.Add(&a, &neg) if !sum.IsZero() { t.Errorf("a + (-a) should be 0, got %+v", sum) } } func TestFieldSetBytes(t *testing.T) { bytes32 := make([]byte, 32) bytes32[31] = 1 var f FieldElement f.SetBytes(bytes32) if !f.IsOne() { t.Errorf("expected field element to be 1, got %+v", f) } } func TestFieldAddSub(t *testing.T) { var a, b, sum, diff FieldElement a.N[0].Lo = 100 b.N[0].Lo = 200 sum.Add(&a, &b) if sum.N[0].Lo != 300 { t.Errorf("100 + 200: expected 300, got %d", sum.N[0].Lo) } diff.Sub(&sum, &b) if !diff.Equal(&a) { t.Errorf("(100+200) - 200: expected 100, got %+v", diff) } } func TestFieldMul(t *testing.T) { var a, b, product FieldElement a.N[0].Lo = 7 b.N[0].Lo = 8 product.Mul(&a, &b) if product.N[0].Lo != 56 { t.Errorf("7 * 8: expected 56, got %d", product.N[0].Lo) } } func TestFieldInverse(t *testing.T) { var a, inv, product FieldElement a.N[0].Lo = 7 inv.Inverse(&a) product.Mul(&a, &inv) if !product.IsOne() { t.Errorf("7 * 7^(-1) should be 1, got %+v", product) } } func TestFieldSqrt(t *testing.T) { // Test sqrt(4) = 2 var four, root, check FieldElement four.N[0].Lo = 4 if !root.Sqrt(&four) { t.Fatal("sqrt(4) should exist") } check.Sqr(&root) if !check.Equal(&four) { t.Errorf("sqrt(4)^2 should be 4, got %+v", check) } } func TestGeneratorOnCurve(t *testing.T) { if !Generator.IsOnCurve() { t.Error("generator point should be on the curve") } } func TestPointDouble(t *testing.T) { var g, doubled JacobianPoint var affineResult AffinePoint g.FromAffine(&Generator) doubled.Double(&g) doubled.ToAffine(&affineResult) if affineResult.Infinity { t.Error("2G should not be infinity") } if !affineResult.IsOnCurve() { t.Error("2G should be on the curve") } } func TestPointAdd(t *testing.T) { var g, twoG, threeG JacobianPoint var affineResult AffinePoint g.FromAffine(&Generator) twoG.Double(&g) threeG.Add(&twoG, &g) threeG.ToAffine(&affineResult) if !affineResult.IsOnCurve() { t.Error("3G should be on the curve") } // Also test via scalar multiplication var three Scalar three.D[0].Lo = 3 var expected JacobianPoint expected.ScalarMult(&g, &three) var expectedAffine AffinePoint expected.ToAffine(&expectedAffine) if !affineResult.Equal(&expectedAffine) { t.Error("G + 2G should equal 3G") } } func TestPointAddInfinity(t *testing.T) { var g, inf, result JacobianPoint var affineResult AffinePoint g.FromAffine(&Generator) inf.SetInfinity() result.Add(&g, &inf) result.ToAffine(&affineResult) if !affineResult.Equal(&Generator) { t.Error("G + O should equal G") } result.Add(&inf, &g) result.ToAffine(&affineResult) if !affineResult.Equal(&Generator) { t.Error("O + G should equal G") } } func TestScalarBaseMult(t *testing.T) { // Test 1*G = G result := BasePointMult(&ScalarOne) if !result.Equal(&Generator) { t.Error("1*G should equal G") } // Test 2*G var two Scalar two.D[0].Lo = 2 result = BasePointMult(&two) var g, twoG JacobianPoint var expected AffinePoint g.FromAffine(&Generator) twoG.Double(&g) twoG.ToAffine(&expected) if !result.Equal(&expected) { t.Error("2*G via scalar mult should equal 2*G via doubling") } } func TestKnownScalarMult(t *testing.T) { // Test vector: private key and public key from Bitcoin // This is a well-known test vector privKeyHex := "0000000000000000000000000000000000000000000000000000000000000001" expectedXHex := "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798" expectedYHex := "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8" privKeyBytes, _ := hex.DecodeString(privKeyHex) var k Scalar k.SetBytes(privKeyBytes) result := BasePointMult(&k) xBytes := result.X.Bytes() yBytes := result.Y.Bytes() expectedX, _ := hex.DecodeString(expectedXHex) expectedY, _ := hex.DecodeString(expectedYHex) if !bytes.Equal(xBytes[:], expectedX) { t.Errorf("X coordinate mismatch:\ngot: %x\nwant: %x", xBytes, expectedX) } if !bytes.Equal(yBytes[:], expectedY) { t.Errorf("Y coordinate mismatch:\ngot: %x\nwant: %x", yBytes, expectedY) } } // Benchmark tests func BenchmarkUint128Mul(b *testing.B) { a := Uint128{0x123456789ABCDEF0, 0xFEDCBA9876543210} c := Uint128{0xABCDEF0123456789, 0x9876543210FEDCBA} b.ResetTimer() for i := 0; i < b.N; i++ { _ = a.Mul(c) } } func BenchmarkScalarAdd(b *testing.B) { var a, c, r Scalar aBytes := make([]byte, 32) cBytes := make([]byte, 32) rand.Read(aBytes) rand.Read(cBytes) a.SetBytes(aBytes) c.SetBytes(cBytes) b.ResetTimer() for i := 0; i < b.N; i++ { r.Add(&a, &c) } } func BenchmarkScalarMul(b *testing.B) { var a, c, r Scalar aBytes := make([]byte, 32) cBytes := make([]byte, 32) rand.Read(aBytes) rand.Read(cBytes) a.SetBytes(aBytes) c.SetBytes(cBytes) b.ResetTimer() for i := 0; i < b.N; i++ { r.Mul(&a, &c) } } func BenchmarkFieldAdd(b *testing.B) { var a, c, r FieldElement aBytes := make([]byte, 32) cBytes := make([]byte, 32) rand.Read(aBytes) rand.Read(cBytes) a.SetBytes(aBytes) c.SetBytes(cBytes) b.ResetTimer() for i := 0; i < b.N; i++ { r.Add(&a, &c) } } func BenchmarkFieldMul(b *testing.B) { var a, c, r FieldElement aBytes := make([]byte, 32) cBytes := make([]byte, 32) rand.Read(aBytes) rand.Read(cBytes) a.SetBytes(aBytes) c.SetBytes(cBytes) b.ResetTimer() for i := 0; i < b.N; i++ { r.Mul(&a, &c) } } func BenchmarkFieldInverse(b *testing.B) { var a, r FieldElement aBytes := make([]byte, 32) rand.Read(aBytes) a.SetBytes(aBytes) b.ResetTimer() for i := 0; i < b.N; i++ { r.Inverse(&a) } } func BenchmarkPointDouble(b *testing.B) { var g, r JacobianPoint g.FromAffine(&Generator) b.ResetTimer() for i := 0; i < b.N; i++ { r.Double(&g) } } func BenchmarkPointAdd(b *testing.B) { var g, twoG, r JacobianPoint g.FromAffine(&Generator) twoG.Double(&g) b.ResetTimer() for i := 0; i < b.N; i++ { r.Add(&g, &twoG) } } func BenchmarkScalarBaseMult(b *testing.B) { var k Scalar kBytes := make([]byte, 32) rand.Read(kBytes) k.SetBytes(kBytes) b.ResetTimer() for i := 0; i < b.N; i++ { _ = BasePointMult(&k) } }