field_4x64_test.go raw

   1  //go:build amd64 && !purego
   2  
   3  package p256k1
   4  
   5  import (
   6  	"crypto/rand"
   7  	"testing"
   8  )
   9  
  10  func TestField4x64SetGet(t *testing.T) {
  11  	// Test round-trip through SetB32/GetB32
  12  	original := make([]byte, 32)
  13  	rand.Read(original)
  14  
  15  	// Make sure it's less than p
  16  	original[0] &= 0x7F // Clear top bit to ensure < p
  17  
  18  	var f Field4x64
  19  	if err := f.SetB32(original); err != nil {
  20  		t.Fatalf("SetB32 failed: %v", err)
  21  	}
  22  
  23  	result := make([]byte, 32)
  24  	f.GetB32(result)
  25  
  26  	for i := 0; i < 32; i++ {
  27  		if original[i] != result[i] {
  28  			t.Errorf("byte %d: got %02x, want %02x", i, result[i], original[i])
  29  		}
  30  	}
  31  }
  32  
  33  func TestField4x64Add(t *testing.T) {
  34  	// Test: a + 0 = a
  35  	var a, zero, result Field4x64
  36  	a.n = [4]uint64{0x123456789ABCDEF0, 0xFEDCBA9876543210, 0x1111111111111111, 0x2222222222222222}
  37  	a.magnitude = 1
  38  	zero.SetZero()
  39  
  40  	result.Add(&a, &zero)
  41  	result.Reduce()
  42  
  43  	if !result.Equal(&a) {
  44  		t.Error("a + 0 != a")
  45  	}
  46  
  47  	// Test: a + (-a) = 0
  48  	var negA Field4x64
  49  	negA.Negate(&a)
  50  	result.Add(&a, &negA)
  51  	result.Reduce()
  52  
  53  	if !result.IsZero() {
  54  		t.Errorf("a + (-a) != 0, got %v", result.n)
  55  	}
  56  }
  57  
  58  func TestField4x64Mul(t *testing.T) {
  59  	// Test: a * 1 = a
  60  	var a, one, result Field4x64
  61  	a.n = [4]uint64{0x123456789ABCDEF0, 0xFEDCBA9876543210, 0x1111111111111111, 0x2222222222222222}
  62  	a.magnitude = 1
  63  	one.SetOne()
  64  
  65  	result.Mul(&a, &one)
  66  
  67  	if !result.Equal(&a) {
  68  		t.Errorf("a * 1 != a\ngot:  %v\nwant: %v", result.n, a.n)
  69  	}
  70  
  71  	// Test: a * 0 = 0
  72  	var zero Field4x64
  73  	zero.SetZero()
  74  	result.Mul(&a, &zero)
  75  
  76  	if !result.IsZero() {
  77  		t.Errorf("a * 0 != 0, got %v", result.n)
  78  	}
  79  
  80  	// Test: 2 * 3 = 6
  81  	var two, three, six Field4x64
  82  	two.n = [4]uint64{2, 0, 0, 0}
  83  	two.magnitude = 1
  84  	three.n = [4]uint64{3, 0, 0, 0}
  85  	three.magnitude = 1
  86  	six.n = [4]uint64{6, 0, 0, 0}
  87  	six.magnitude = 1
  88  
  89  	result.Mul(&two, &three)
  90  	if !result.Equal(&six) {
  91  		t.Errorf("2 * 3 != 6, got %v", result.n)
  92  	}
  93  }
  94  
  95  func TestField4x64MulLarge(t *testing.T) {
  96  	// Test multiplication of larger values
  97  	// (2^128) * (2^128) = 2^256 ≡ R (mod p) where R = 2^32 + 977
  98  
  99  	var a, result Field4x64
 100  	a.n = [4]uint64{0, 0, 1, 0} // 2^128
 101  	a.magnitude = 1
 102  
 103  	result.Mul(&a, &a) // (2^128)^2 = 2^256
 104  
 105  	// Expected: 2^32 + 977 = 0x1000003D1
 106  	expected := Field4x64{n: [4]uint64{0x1000003D1, 0, 0, 0}, magnitude: 1}
 107  
 108  	if !result.Equal(&expected) {
 109  		t.Errorf("2^256 mod p != R\ngot:  %v\nwant: %v", result.n, expected.n)
 110  	}
 111  }
 112  
 113  func TestField4x64Comparison5x52(t *testing.T) {
 114  	// Compare 4x64 results with existing 5x52 implementation
 115  	for i := 0; i < 100; i++ {
 116  		// Generate random field elements
 117  		aBytes := make([]byte, 32)
 118  		bBytes := make([]byte, 32)
 119  		rand.Read(aBytes)
 120  		rand.Read(bBytes)
 121  
 122  		// Ensure < p
 123  		aBytes[0] &= 0x7F
 124  		bBytes[0] &= 0x7F
 125  
 126  		// 4x64 multiplication
 127  		var a4, b4, r4 Field4x64
 128  		a4.SetB32(aBytes)
 129  		b4.SetB32(bBytes)
 130  		r4.Mul(&a4, &b4)
 131  
 132  		// 5x52 multiplication
 133  		var a5, b5, r5 FieldElement
 134  		a5.setB32(aBytes)
 135  		b5.setB32(bBytes)
 136  		r5.mul(&a5, &b5)
 137  		r5.normalize()
 138  
 139  		// Compare results
 140  		result4 := make([]byte, 32)
 141  		result5 := make([]byte, 32)
 142  		r4.GetB32(result4)
 143  		r5.getB32(result5)
 144  
 145  		for j := 0; j < 32; j++ {
 146  			if result4[j] != result5[j] {
 147  				t.Errorf("iteration %d: 4x64 and 5x52 differ at byte %d\n4x64: %x\n5x52: %x",
 148  					i, j, result4, result5)
 149  				break
 150  			}
 151  		}
 152  	}
 153  }
 154  
 155  func BenchmarkField4x64Mul(b *testing.B) {
 156  	var a, c, r Field4x64
 157  	a.n = [4]uint64{0x123456789ABCDEF0, 0xFEDCBA9876543210, 0x1111111111111111, 0x2222222222222222}
 158  	a.magnitude = 1
 159  	c.n = [4]uint64{0xAAAABBBBCCCCDDDD, 0xEEEEFFFF00001111, 0x3333444455556666, 0x7777888899990000}
 160  	c.magnitude = 1
 161  
 162  	b.ResetTimer()
 163  	b.ReportAllocs()
 164  	for i := 0; i < b.N; i++ {
 165  		r.Mul(&a, &c)
 166  	}
 167  }
 168  
 169  func BenchmarkField4x64Add(b *testing.B) {
 170  	var a, c, r Field4x64
 171  	a.n = [4]uint64{0x123456789ABCDEF0, 0xFEDCBA9876543210, 0x1111111111111111, 0x2222222222222222}
 172  	a.magnitude = 1
 173  	c.n = [4]uint64{0xAAAABBBBCCCCDDDD, 0xEEEEFFFF00001111, 0x3333444455556666, 0x7777888899990000}
 174  	c.magnitude = 1
 175  
 176  	b.ResetTimer()
 177  	b.ReportAllocs()
 178  	for i := 0; i < b.N; i++ {
 179  		r.Add(&a, &c)
 180  	}
 181  }
 182  
 183  func BenchmarkField4x64Sqr(b *testing.B) {
 184  	var a, r Field4x64
 185  	a.n = [4]uint64{0x123456789ABCDEF0, 0xFEDCBA9876543210, 0x1111111111111111, 0x2222222222222222}
 186  	a.magnitude = 1
 187  
 188  	b.ResetTimer()
 189  	b.ReportAllocs()
 190  	for i := 0; i < b.N; i++ {
 191  		r.Sqr(&a)
 192  	}
 193  }
 194  
 195  // Comparison benchmarks
 196  func BenchmarkField5x52Mul(b *testing.B) {
 197  	var a, c, r FieldElement
 198  	aBytes := []byte{0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
 199  		0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
 200  		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
 201  		0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22}
 202  	cBytes := []byte{0xAA, 0xAA, 0xBB, 0xBB, 0xCC, 0xCC, 0xDD, 0xDD,
 203  		0xEE, 0xEE, 0xFF, 0xFF, 0x00, 0x00, 0x11, 0x11,
 204  		0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66, 0x66,
 205  		0x77, 0x77, 0x88, 0x88, 0x99, 0x99, 0x00, 0x00}
 206  
 207  	a.setB32(aBytes)
 208  	c.setB32(cBytes)
 209  
 210  	b.ResetTimer()
 211  	b.ReportAllocs()
 212  	for i := 0; i < b.N; i++ {
 213  		r.mul(&a, &c)
 214  	}
 215  }
 216  
 217  func BenchmarkField5x52Sqr(b *testing.B) {
 218  	var a, r FieldElement
 219  	aBytes := []byte{0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
 220  		0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
 221  		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
 222  		0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22}
 223  
 224  	a.setB32(aBytes)
 225  
 226  	b.ResetTimer()
 227  	b.ReportAllocs()
 228  	for i := 0; i < b.N; i++ {
 229  		r.sqr(&a)
 230  	}
 231  }
 232  
 233  func BenchmarkField4x64Inv(b *testing.B) {
 234  	var a, r Field4x64
 235  	a.n = [4]uint64{0x123456789ABCDEF0, 0xFEDCBA9876543210, 0x1111111111111111, 0x2222222222222222}
 236  	a.magnitude = 1
 237  	a.normalized = true
 238  
 239  	b.ResetTimer()
 240  	b.ReportAllocs()
 241  	for i := 0; i < b.N; i++ {
 242  		r.inv(&a)
 243  	}
 244  }
 245  
 246  func BenchmarkField5x52Inv(b *testing.B) {
 247  	var a, r FieldElement
 248  	aBytes := []byte{0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
 249  		0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
 250  		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
 251  		0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22}
 252  
 253  	a.setB32(aBytes)
 254  	a.normalize()
 255  
 256  	b.ResetTimer()
 257  	b.ReportAllocs()
 258  	for i := 0; i < b.N; i++ {
 259  		r.inv(&a)
 260  	}
 261  }
 262