411 lines
11 KiB
Go
411 lines
11 KiB
Go
package avx
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"fmt"
|
|
"testing"
|
|
)
|
|
|
|
func TestGeneratorConstants(t *testing.T) {
|
|
// Verify the generator X and Y constants
|
|
expectedGx := "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
|
|
expectedGy := "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"
|
|
|
|
gx := Generator.X.Bytes()
|
|
gy := Generator.Y.Bytes()
|
|
|
|
t.Logf("Generator X: %x", gx)
|
|
t.Logf("Expected X: %s", expectedGx)
|
|
t.Logf("Generator Y: %x", gy)
|
|
t.Logf("Expected Y: %s", expectedGy)
|
|
|
|
// They should match
|
|
if expectedGx != fmt.Sprintf("%x", gx) {
|
|
t.Error("Generator X mismatch")
|
|
}
|
|
if expectedGy != fmt.Sprintf("%x", gy) {
|
|
t.Error("Generator Y mismatch")
|
|
}
|
|
|
|
// Verify G is on the curve
|
|
if !Generator.IsOnCurve() {
|
|
t.Error("Generator should be on curve")
|
|
}
|
|
|
|
// Let me test squaring and multiplication more carefully
|
|
// Y² should equal X³ + 7
|
|
var y2, x2, x3, seven, rhs FieldElement
|
|
y2.Sqr(&Generator.Y)
|
|
x2.Sqr(&Generator.X)
|
|
x3.Mul(&x2, &Generator.X)
|
|
seven.N[0].Lo = 7
|
|
rhs.Add(&x3, &seven)
|
|
|
|
t.Logf("Y² = %x", y2.Bytes())
|
|
t.Logf("X³ + 7 = %x", rhs.Bytes())
|
|
|
|
if !y2.Equal(&rhs) {
|
|
t.Error("Y² != X³ + 7 for generator")
|
|
}
|
|
}
|
|
|
|
func TestTraceDouble(t *testing.T) {
|
|
// Test the point doubling step by step
|
|
var g JacobianPoint
|
|
g.FromAffine(&Generator)
|
|
|
|
t.Logf("Input G:")
|
|
t.Logf(" X = %x", g.X.Bytes())
|
|
t.Logf(" Y = %x", g.Y.Bytes())
|
|
t.Logf(" Z = %x", g.Z.Bytes())
|
|
|
|
// Standard Jacobian doubling for y²=x³+b (secp256k1 has a=0):
|
|
// M = 3*X₁²
|
|
// S = 4*X₁*Y₁²
|
|
// T = 8*Y₁⁴
|
|
// X₃ = M² - 2*S
|
|
// Y₃ = M*(S - X₃) - T
|
|
// Z₃ = 2*Y₁*Z₁
|
|
|
|
var y2, m, x2, s, t_val, x3, y3, z3, tmp FieldElement
|
|
|
|
// Y² = Y₁²
|
|
y2.Sqr(&g.Y)
|
|
t.Logf("Y² = %x", y2.Bytes())
|
|
|
|
// M = 3*X²
|
|
x2.Sqr(&g.X)
|
|
t.Logf("X² = %x", x2.Bytes())
|
|
m.MulInt(&x2, 3)
|
|
t.Logf("M = 3*X² = %x", m.Bytes())
|
|
|
|
// S = 4*X₁*Y₁²
|
|
s.Mul(&g.X, &y2)
|
|
t.Logf("X*Y² = %x", s.Bytes())
|
|
s.MulInt(&s, 4)
|
|
t.Logf("S = 4*X*Y² = %x", s.Bytes())
|
|
|
|
// T = 8*Y₁⁴
|
|
t_val.Sqr(&y2)
|
|
t.Logf("Y⁴ = %x", t_val.Bytes())
|
|
t_val.MulInt(&t_val, 8)
|
|
t.Logf("T = 8*Y⁴ = %x", t_val.Bytes())
|
|
|
|
// X₃ = M² - 2*S
|
|
x3.Sqr(&m)
|
|
t.Logf("M² = %x", x3.Bytes())
|
|
tmp.Double(&s)
|
|
t.Logf("2*S = %x", tmp.Bytes())
|
|
x3.Sub(&x3, &tmp)
|
|
t.Logf("X₃ = M² - 2*S = %x", x3.Bytes())
|
|
|
|
// Y₃ = M*(S - X₃) - T
|
|
tmp.Sub(&s, &x3)
|
|
t.Logf("S - X₃ = %x", tmp.Bytes())
|
|
y3.Mul(&m, &tmp)
|
|
t.Logf("M*(S-X₃) = %x", y3.Bytes())
|
|
y3.Sub(&y3, &t_val)
|
|
t.Logf("Y₃ = M*(S-X₃) - T = %x", y3.Bytes())
|
|
|
|
// Z₃ = 2*Y₁*Z₁
|
|
z3.Mul(&g.Y, &g.Z)
|
|
z3.Double(&z3)
|
|
t.Logf("Z₃ = 2*Y*Z = %x", z3.Bytes())
|
|
|
|
// Now convert to affine
|
|
var doubled JacobianPoint
|
|
doubled.X = x3
|
|
doubled.Y = y3
|
|
doubled.Z = z3
|
|
doubled.Infinity = false
|
|
|
|
var affineResult AffinePoint
|
|
doubled.ToAffine(&affineResult)
|
|
t.Logf("Affine result (correct formula):")
|
|
t.Logf(" X = %x", affineResult.X.Bytes())
|
|
t.Logf(" Y = %x", affineResult.Y.Bytes())
|
|
|
|
// Expected 2G
|
|
expectedX := "c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
|
|
expectedY := "1ae168fea63dc339a3c58419466ceae1061b7c24a6b3e36e3b4d04f7a8f63301"
|
|
t.Logf("Expected:")
|
|
t.Logf(" X = %s", expectedX)
|
|
t.Logf(" Y = %s", expectedY)
|
|
|
|
// Verify by computing 2G using the existing Double method
|
|
var doubled2 JacobianPoint
|
|
doubled2.Double(&g)
|
|
var affine2 AffinePoint
|
|
doubled2.ToAffine(&affine2)
|
|
t.Logf("Current Double method result:")
|
|
t.Logf(" X = %x", affine2.X.Bytes())
|
|
t.Logf(" Y = %x", affine2.Y.Bytes())
|
|
|
|
// Compare results
|
|
expectedXBytes, _ := hex.DecodeString(expectedX)
|
|
expectedYBytes, _ := hex.DecodeString(expectedY)
|
|
|
|
if fmt.Sprintf("%x", affineResult.X.Bytes()) == expectedX &&
|
|
fmt.Sprintf("%x", affineResult.Y.Bytes()) == expectedY {
|
|
t.Logf("Correct formula produces expected result!")
|
|
} else {
|
|
t.Logf("Even correct formula doesn't match - problem elsewhere")
|
|
}
|
|
|
|
_ = expectedXBytes
|
|
_ = expectedYBytes
|
|
|
|
// Verify the result is on the curve
|
|
t.Logf("Result is on curve: %v", affineResult.IsOnCurve())
|
|
|
|
// Compute y² for the computed result
|
|
var verifyY2, verifyX2, verifyX3, verifySeven, verifyRhs FieldElement
|
|
verifyY2.Sqr(&affineResult.Y)
|
|
verifyX2.Sqr(&affineResult.X)
|
|
verifyX3.Mul(&verifyX2, &affineResult.X)
|
|
verifySeven.N[0].Lo = 7
|
|
verifyRhs.Add(&verifyX3, &verifySeven)
|
|
t.Logf("Computed y² = %x", verifyY2.Bytes())
|
|
t.Logf("Computed x³+7 = %x", verifyRhs.Bytes())
|
|
t.Logf("y² == x³+7: %v", verifyY2.Equal(&verifyRhs))
|
|
|
|
// Now test with the expected Y value
|
|
var expectedYField, expectedY2Field FieldElement
|
|
expectedYField.SetBytes(expectedYBytes)
|
|
expectedY2Field.Sqr(&expectedYField)
|
|
t.Logf("Expected Y² = %x", expectedY2Field.Bytes())
|
|
t.Logf("Expected Y² == x³+7: %v", expectedY2Field.Equal(&verifyRhs))
|
|
|
|
// Maybe I have the negative Y - let's check the negation
|
|
var negY FieldElement
|
|
negY.Negate(&affineResult.Y)
|
|
t.Logf("Negated computed Y = %x", negY.Bytes())
|
|
|
|
// Also check if the expected value is valid at all
|
|
// The expected 2G should be:
|
|
// X = c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5
|
|
// Y = 1ae168fea63dc339a3c58419466ceae1061b7c24a6b3e36e3b4d04f7a8f63301
|
|
// Let me verify this is correct by computing y² directly
|
|
t.Log("--- Verifying expected 2G values ---")
|
|
var expXField FieldElement
|
|
expXField.SetBytes(expectedXBytes)
|
|
|
|
// Compute x³ + 7 for the expected X
|
|
var expX2, expX3, expRhs FieldElement
|
|
expX2.Sqr(&expXField)
|
|
expX3.Mul(&expX2, &expXField)
|
|
var seven2 FieldElement
|
|
seven2.N[0].Lo = 7
|
|
expRhs.Add(&expX3, &seven2)
|
|
t.Logf("For expected X, x³+7 = %x", expRhs.Bytes())
|
|
|
|
// Compute sqrt
|
|
var sqrtY FieldElement
|
|
if sqrtY.Sqrt(&expRhs) {
|
|
t.Logf("sqrt(x³+7) = %x", sqrtY.Bytes())
|
|
var negSqrtY FieldElement
|
|
negSqrtY.Negate(&sqrtY)
|
|
t.Logf("-sqrt(x³+7) = %x", negSqrtY.Bytes())
|
|
}
|
|
}
|
|
|
|
func TestDebugPointAdd(t *testing.T) {
|
|
// Compute 3G two ways: (1) G + 2G and (2) 3*G via scalar mult
|
|
var g, twoG, threeGAdd JacobianPoint
|
|
var affine3GAdd, affine3GSM AffinePoint
|
|
|
|
g.FromAffine(&Generator)
|
|
twoG.Double(&g)
|
|
threeGAdd.Add(&twoG, &g)
|
|
threeGAdd.ToAffine(&affine3GAdd)
|
|
|
|
t.Logf("2G (Jacobian):")
|
|
t.Logf(" X = %x", twoG.X.Bytes())
|
|
t.Logf(" Y = %x", twoG.Y.Bytes())
|
|
t.Logf(" Z = %x", twoG.Z.Bytes())
|
|
|
|
t.Logf("3G via Add (affine):")
|
|
t.Logf(" X = %x", affine3GAdd.X.Bytes())
|
|
t.Logf(" Y = %x", affine3GAdd.Y.Bytes())
|
|
t.Logf(" On curve: %v", affine3GAdd.IsOnCurve())
|
|
|
|
// Now via scalar mult
|
|
var three Scalar
|
|
three.D[0].Lo = 3
|
|
var threeGSM JacobianPoint
|
|
threeGSM.ScalarMult(&g, &three)
|
|
threeGSM.ToAffine(&affine3GSM)
|
|
|
|
t.Logf("3G via ScalarMult (affine):")
|
|
t.Logf(" X = %x", affine3GSM.X.Bytes())
|
|
t.Logf(" Y = %x", affine3GSM.Y.Bytes())
|
|
t.Logf(" On curve: %v", affine3GSM.IsOnCurve())
|
|
|
|
// Compute expected 3G using Python
|
|
// This should be:
|
|
// X = f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9
|
|
// Y = 388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672
|
|
t.Logf("Equal: %v", affine3GAdd.Equal(&affine3GSM))
|
|
}
|
|
|
|
func TestAVX2Operations(t *testing.T) {
|
|
// Test that AVX2 assembly produces same results as Go code
|
|
if !hasAVX2() {
|
|
t.Skip("AVX2 not available")
|
|
}
|
|
|
|
// Test field addition
|
|
var a, b, resultGo, resultAVX FieldElement
|
|
a.N[0].Lo = 0x123456789ABCDEF0
|
|
a.N[0].Hi = 0xFEDCBA9876543210
|
|
a.N[1].Lo = 0x1111111111111111
|
|
a.N[1].Hi = 0x2222222222222222
|
|
|
|
b.N[0].Lo = 0x0FEDCBA987654321
|
|
b.N[0].Hi = 0x123456789ABCDEF0
|
|
b.N[1].Lo = 0x3333333333333333
|
|
b.N[1].Hi = 0x4444444444444444
|
|
|
|
resultGo.Add(&a, &b)
|
|
FieldAddAVX2(&resultAVX, &a, &b)
|
|
|
|
if !resultGo.Equal(&resultAVX) {
|
|
t.Errorf("FieldAddAVX2 mismatch:\n Go: %x\n AVX2: %x", resultGo.Bytes(), resultAVX.Bytes())
|
|
}
|
|
|
|
// Test field subtraction
|
|
resultGo.Sub(&a, &b)
|
|
FieldSubAVX2(&resultAVX, &a, &b)
|
|
|
|
if !resultGo.Equal(&resultAVX) {
|
|
t.Errorf("FieldSubAVX2 mismatch:\n Go: %x\n AVX2: %x", resultGo.Bytes(), resultAVX.Bytes())
|
|
}
|
|
|
|
// Test field multiplication
|
|
resultGo.Mul(&a, &b)
|
|
FieldMulAVX2(&resultAVX, &a, &b)
|
|
|
|
if !resultGo.Equal(&resultAVX) {
|
|
t.Errorf("FieldMulAVX2 mismatch:\n Go: %x\n AVX2: %x", resultGo.Bytes(), resultAVX.Bytes())
|
|
}
|
|
|
|
// Test scalar addition
|
|
var sa, sb, sResultGo, sResultAVX Scalar
|
|
sa.D[0].Lo = 0x123456789ABCDEF0
|
|
sa.D[0].Hi = 0xFEDCBA9876543210
|
|
sa.D[1].Lo = 0x1111111111111111
|
|
sa.D[1].Hi = 0x2222222222222222
|
|
|
|
sb.D[0].Lo = 0x0FEDCBA987654321
|
|
sb.D[0].Hi = 0x123456789ABCDEF0
|
|
sb.D[1].Lo = 0x3333333333333333
|
|
sb.D[1].Hi = 0x4444444444444444
|
|
|
|
sResultGo.Add(&sa, &sb)
|
|
ScalarAddAVX2(&sResultAVX, &sa, &sb)
|
|
|
|
if !sResultGo.Equal(&sResultAVX) {
|
|
t.Errorf("ScalarAddAVX2 mismatch:\n Go: %x\n AVX2: %x", sResultGo.Bytes(), sResultAVX.Bytes())
|
|
}
|
|
|
|
// Test scalar multiplication
|
|
sResultGo.Mul(&sa, &sb)
|
|
ScalarMulAVX2(&sResultAVX, &sa, &sb)
|
|
|
|
if !sResultGo.Equal(&sResultAVX) {
|
|
t.Errorf("ScalarMulAVX2 mismatch:\n Go: %x\n AVX2: %x", sResultGo.Bytes(), sResultAVX.Bytes())
|
|
}
|
|
|
|
t.Logf("Field and Scalar Add/Sub AVX2 operations match Go implementations")
|
|
}
|
|
|
|
func TestDebugScalarMult(t *testing.T) {
|
|
// Test 2*G via scalar mult
|
|
var g, twoGDouble, twoGSM JacobianPoint
|
|
var affineDouble, affineSM AffinePoint
|
|
|
|
g.FromAffine(&Generator)
|
|
|
|
// Via doubling
|
|
twoGDouble.Double(&g)
|
|
twoGDouble.ToAffine(&affineDouble)
|
|
|
|
// Via scalar mult (k=2)
|
|
var two Scalar
|
|
two.D[0].Lo = 2
|
|
|
|
// Print the bytes of k=2
|
|
twoBytes := two.Bytes()
|
|
t.Logf("k=2 bytes: %x", twoBytes[:])
|
|
|
|
twoGSM.ScalarMult(&g, &two)
|
|
twoGSM.ToAffine(&affineSM)
|
|
|
|
t.Logf("2G via Double (affine):")
|
|
t.Logf(" X = %x", affineDouble.X.Bytes())
|
|
t.Logf(" Y = %x", affineDouble.Y.Bytes())
|
|
t.Logf(" On curve: %v", affineDouble.IsOnCurve())
|
|
|
|
t.Logf("2G via ScalarMult (affine):")
|
|
t.Logf(" X = %x", affineSM.X.Bytes())
|
|
t.Logf(" Y = %x", affineSM.Y.Bytes())
|
|
t.Logf(" On curve: %v", affineSM.IsOnCurve())
|
|
|
|
t.Logf("Equal: %v", affineDouble.Equal(&affineSM))
|
|
|
|
// Manual scalar mult for k=2
|
|
// Binary: 10 (2 bits)
|
|
// Start with p = infinity
|
|
// bit 1: p = 2*infinity = infinity, then p = p + G = G
|
|
// bit 0: p = 2*G, no add
|
|
// Result should be 2G
|
|
|
|
var p JacobianPoint
|
|
p.SetInfinity()
|
|
|
|
// Process bit 1 (the high bit of 2)
|
|
p.Double(&p)
|
|
t.Logf("After double of infinity: IsInfinity=%v", p.IsInfinity())
|
|
p.Add(&p, &g)
|
|
t.Logf("After add G: IsInfinity=%v", p.IsInfinity())
|
|
|
|
var affineP AffinePoint
|
|
p.ToAffine(&affineP)
|
|
t.Logf("After first iteration (should be G):")
|
|
t.Logf(" X = %x", affineP.X.Bytes())
|
|
t.Logf(" Y = %x", affineP.Y.Bytes())
|
|
t.Logf(" Equal to G: %v", affineP.Equal(&Generator))
|
|
|
|
// Process bit 0
|
|
p.Double(&p)
|
|
p.ToAffine(&affineP)
|
|
t.Logf("After second iteration (should be 2G):")
|
|
t.Logf(" X = %x", affineP.X.Bytes())
|
|
t.Logf(" Y = %x", affineP.Y.Bytes())
|
|
t.Logf(" On curve: %v", affineP.IsOnCurve())
|
|
t.Logf(" Equal to Double result: %v", affineP.Equal(&affineDouble))
|
|
|
|
// Test: does doubling G into a fresh variable work?
|
|
var fresh JacobianPoint
|
|
var freshAffine AffinePoint
|
|
fresh.Double(&g)
|
|
fresh.ToAffine(&freshAffine)
|
|
t.Logf("Fresh Double(g):")
|
|
t.Logf(" X = %x", freshAffine.X.Bytes())
|
|
t.Logf(" Y = %x", freshAffine.Y.Bytes())
|
|
t.Logf(" On curve: %v", freshAffine.IsOnCurve())
|
|
|
|
// Test: what about p.Double(p) when p == g?
|
|
var pCopy JacobianPoint
|
|
pCopy = p // now p is already set to some value
|
|
pCopy.FromAffine(&Generator)
|
|
t.Logf("Before in-place double, pCopy X: %x", pCopy.X.Bytes())
|
|
pCopy.Double(&pCopy)
|
|
var pCopyAffine AffinePoint
|
|
pCopy.ToAffine(&pCopyAffine)
|
|
t.Logf("After in-place Double(&pCopy):")
|
|
t.Logf(" X = %x", pCopyAffine.X.Bytes())
|
|
t.Logf(" Y = %x", pCopyAffine.Y.Bytes())
|
|
t.Logf(" On curve: %v", pCopyAffine.IsOnCurve())
|
|
}
|