340 lines
6.6 KiB
Go
340 lines
6.6 KiB
Go
package p256k1
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"testing"
|
|
)
|
|
|
|
// Test field element creation and basic operations
|
|
func TestFieldElementBasics(t *testing.T) {
|
|
// Test zero element
|
|
var zero FieldElement
|
|
zero.setInt(0)
|
|
if !zero.isZero() {
|
|
t.Error("Zero element should be zero")
|
|
}
|
|
|
|
// Test one element
|
|
var one FieldElement
|
|
one.setInt(1)
|
|
if one.isZero() {
|
|
t.Error("One element should not be zero")
|
|
}
|
|
|
|
// Test normalization
|
|
one.normalize()
|
|
if !one.normalized {
|
|
t.Error("Element should be normalized after normalize()")
|
|
}
|
|
|
|
// Test equality
|
|
var one2 FieldElement
|
|
one2.setInt(1)
|
|
one2.normalize()
|
|
if !one.equal(&one2) {
|
|
t.Error("Two normalized ones should be equal")
|
|
}
|
|
}
|
|
|
|
func TestFieldElementSetB32(t *testing.T) {
|
|
// Test setting from 32-byte array
|
|
testCases := []struct {
|
|
name string
|
|
bytes [32]byte
|
|
}{
|
|
{
|
|
name: "zero",
|
|
bytes: [32]byte{},
|
|
},
|
|
{
|
|
name: "one",
|
|
bytes: [32]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
|
},
|
|
{
|
|
name: "max_value",
|
|
bytes: [32]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2F},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
var fe FieldElement
|
|
fe.setB32(tc.bytes[:])
|
|
|
|
// Test round-trip
|
|
var result [32]byte
|
|
fe.normalize()
|
|
fe.getB32(result[:])
|
|
|
|
// For field modulus reduction, we need to check if the result is valid
|
|
if tc.name == "max_value" {
|
|
// This should be reduced modulo p
|
|
var expected FieldElement
|
|
expected.setInt(0) // p - 1 mod p = 0
|
|
expected.normalize()
|
|
if !fe.equal(&expected) {
|
|
t.Error("Field modulus should reduce to zero")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFieldElementArithmetic(t *testing.T) {
|
|
// Test addition
|
|
var a, b, c FieldElement
|
|
a.setInt(5)
|
|
b.setInt(7)
|
|
c = a
|
|
c.add(&b)
|
|
c.normalize()
|
|
|
|
var expected FieldElement
|
|
expected.setInt(12)
|
|
expected.normalize()
|
|
|
|
if !c.equal(&expected) {
|
|
t.Error("5 + 7 should equal 12")
|
|
}
|
|
|
|
// Test negation
|
|
var neg FieldElement
|
|
neg.negate(&a, 1)
|
|
neg.normalize()
|
|
|
|
var sum FieldElement
|
|
sum = a
|
|
sum.add(&neg)
|
|
sum.normalize()
|
|
|
|
if !sum.isZero() {
|
|
t.Error("a + (-a) should equal zero")
|
|
}
|
|
}
|
|
|
|
func TestFieldElementMultiplication(t *testing.T) {
|
|
// Test multiplication by small integers
|
|
var a, result FieldElement
|
|
a.setInt(3)
|
|
result = a
|
|
result.mulInt(4)
|
|
result.normalize()
|
|
|
|
var expected FieldElement
|
|
expected.setInt(12)
|
|
expected.normalize()
|
|
|
|
if !result.equal(&expected) {
|
|
t.Error("3 * 4 should equal 12")
|
|
}
|
|
|
|
// Test multiplication by zero
|
|
result = a
|
|
result.mulInt(0)
|
|
result.normalize()
|
|
|
|
if !result.isZero() {
|
|
t.Error("a * 0 should equal zero")
|
|
}
|
|
}
|
|
|
|
func TestFieldElementNormalization(t *testing.T) {
|
|
var fe FieldElement
|
|
fe.setInt(42)
|
|
|
|
// Test weak normalization
|
|
fe.normalizeWeak()
|
|
if fe.magnitude != 1 {
|
|
t.Error("Weak normalization should set magnitude to 1")
|
|
}
|
|
|
|
// Test full normalization
|
|
fe.normalize()
|
|
if !fe.normalized {
|
|
t.Error("Full normalization should set normalized flag")
|
|
}
|
|
if fe.magnitude != 1 {
|
|
t.Error("Full normalization should set magnitude to 1")
|
|
}
|
|
}
|
|
|
|
func TestFieldElementOddness(t *testing.T) {
|
|
// Test even number
|
|
var even FieldElement
|
|
even.setInt(42)
|
|
even.normalize()
|
|
if even.isOdd() {
|
|
t.Error("42 should be even")
|
|
}
|
|
|
|
// Test odd number
|
|
var odd FieldElement
|
|
odd.setInt(43)
|
|
odd.normalize()
|
|
if !odd.isOdd() {
|
|
t.Error("43 should be odd")
|
|
}
|
|
}
|
|
|
|
func TestFieldElementConditionalMove(t *testing.T) {
|
|
var a, b, result FieldElement
|
|
a.setInt(10)
|
|
b.setInt(20)
|
|
result = a
|
|
|
|
// Test conditional move with flag = 0 (no move)
|
|
result.cmov(&b, 0)
|
|
result.normalize()
|
|
a.normalize()
|
|
if !result.equal(&a) {
|
|
t.Error("cmov with flag=0 should not change value")
|
|
}
|
|
|
|
// Test conditional move with flag = 1 (move)
|
|
result = a
|
|
result.cmov(&b, 1)
|
|
result.normalize()
|
|
b.normalize()
|
|
if !result.equal(&b) {
|
|
t.Error("cmov with flag=1 should change value")
|
|
}
|
|
}
|
|
|
|
func TestFieldElementStorage(t *testing.T) {
|
|
var fe FieldElement
|
|
fe.setInt(12345)
|
|
fe.normalize()
|
|
|
|
// Test conversion to storage format
|
|
var storage FieldElementStorage
|
|
fe.toStorage(&storage)
|
|
|
|
// Test conversion back from storage
|
|
var restored FieldElement
|
|
restored.fromStorage(&storage)
|
|
|
|
if !fe.equal(&restored) {
|
|
t.Error("Storage round-trip should preserve value")
|
|
}
|
|
}
|
|
|
|
func TestFieldElementRandomOperations(t *testing.T) {
|
|
// Test with random values
|
|
for i := 0; i < 100; i++ {
|
|
var bytes1, bytes2 [32]byte
|
|
rand.Read(bytes1[:])
|
|
rand.Read(bytes2[:])
|
|
|
|
var a, b, sum, diff FieldElement
|
|
a.setB32(bytes1[:])
|
|
b.setB32(bytes2[:])
|
|
|
|
// Test a + b - b = a
|
|
sum = a
|
|
sum.add(&b)
|
|
diff = sum
|
|
var negB FieldElement
|
|
negB.negate(&b, b.magnitude)
|
|
diff.add(&negB)
|
|
diff.normalize()
|
|
a.normalize()
|
|
|
|
if !diff.equal(&a) {
|
|
t.Errorf("Random test %d: (a + b) - b should equal a", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFieldElementEdgeCases(t *testing.T) {
|
|
// Test field modulus boundary
|
|
// Set to p-1 (field modulus minus 1)
|
|
// p-1 = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2E
|
|
p_minus_1 := [32]byte{
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2E,
|
|
}
|
|
|
|
var fe FieldElement
|
|
fe.setB32(p_minus_1[:])
|
|
fe.normalize()
|
|
|
|
// Add 1 should give 0
|
|
var one FieldElement
|
|
one.setInt(1)
|
|
fe.add(&one)
|
|
fe.normalize()
|
|
|
|
if !fe.isZero() {
|
|
t.Error("(p-1) + 1 should equal 0 in field arithmetic")
|
|
}
|
|
}
|
|
|
|
func TestFieldElementClear(t *testing.T) {
|
|
var fe FieldElement
|
|
fe.setInt(12345)
|
|
|
|
fe.clear()
|
|
|
|
// After clearing, should be zero and normalized
|
|
if !fe.isZero() {
|
|
t.Error("Cleared field element should be zero")
|
|
}
|
|
if !fe.normalized {
|
|
t.Error("Cleared field element should be normalized")
|
|
}
|
|
}
|
|
|
|
// Benchmark tests
|
|
func BenchmarkFieldElementSetB32(b *testing.B) {
|
|
var bytes [32]byte
|
|
rand.Read(bytes[:])
|
|
var fe FieldElement
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
fe.setB32(bytes[:])
|
|
}
|
|
}
|
|
|
|
func BenchmarkFieldElementNormalize(b *testing.B) {
|
|
var fe FieldElement
|
|
fe.setInt(12345)
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
fe.normalize()
|
|
}
|
|
}
|
|
|
|
func BenchmarkFieldElementAdd(b *testing.B) {
|
|
var a, c FieldElement
|
|
a.setInt(12345)
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
c.add(&a)
|
|
}
|
|
}
|
|
|
|
func BenchmarkFieldElementMulInt(b *testing.B) {
|
|
var fe FieldElement
|
|
fe.setInt(12345)
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
fe.mulInt(7)
|
|
}
|
|
}
|
|
|
|
func BenchmarkFieldElementNegate(b *testing.B) {
|
|
var a, result FieldElement
|
|
a.setInt(12345)
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
result.negate(&a, 1)
|
|
}
|
|
}
|