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