field_test.go raw
1 package p256k1
2
3 import (
4 "testing"
5 )
6
7 func TestFieldElementBasics(t *testing.T) {
8 // Test zero field element
9 var zero FieldElement
10 zero.setInt(0)
11 zero.normalize()
12 if !zero.isZero() {
13 t.Error("Zero field element should be zero")
14 }
15
16 // Test one field element
17 var one FieldElement
18 one.setInt(1)
19 one.normalize()
20 if one.isZero() {
21 t.Error("One field element should not be zero")
22 }
23
24 // Test equality
25 var one2 FieldElement
26 one2.setInt(1)
27 one2.normalize()
28 if !one.equal(&one2) {
29 t.Error("Two normalized ones should be equal")
30 }
31 }
32
33 func TestFieldElementSetB32(t *testing.T) {
34 // Test setting from 32-byte array
35 testCases := []struct {
36 name string
37 bytes [32]byte
38 }{
39 {
40 name: "zero",
41 bytes: [32]byte{},
42 },
43 {
44 name: "one",
45 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},
46 },
47 {
48 name: "max_value",
49 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},
50 },
51 }
52
53 for _, tc := range testCases {
54 t.Run(tc.name, func(t *testing.T) {
55 var fe FieldElement
56 fe.setB32(tc.bytes[:])
57
58 // Test round-trip
59 var result [32]byte
60 fe.normalize()
61 fe.getB32(result[:])
62
63 // For field modulus reduction, we need to check if the result is valid
64 if tc.name == "max_value" {
65 // This should be reduced modulo p
66 var expected FieldElement
67 expected.setInt(0) // p mod p = 0
68 expected.normalize()
69 if !fe.equal(&expected) {
70 t.Error("Field modulus should reduce to zero")
71 }
72 }
73 })
74 }
75 }
76
77 func TestFieldElementArithmetic(t *testing.T) {
78 // Test addition
79 var a, b, c FieldElement
80 a.setInt(5)
81 b.setInt(7)
82 c = a
83 c.add(&b)
84 c.normalize()
85
86 var expected FieldElement
87 expected.setInt(12)
88 expected.normalize()
89 if !c.equal(&expected) {
90 t.Error("5 + 7 should equal 12")
91 }
92
93 // Test negation
94 var neg FieldElement
95 neg.negate(&a, a.magnitude)
96 neg.normalize()
97
98 var sum FieldElement
99 sum = a
100 sum.add(&neg)
101 sum.normalize()
102
103 if !sum.isZero() {
104 t.Error("a + (-a) should equal zero")
105 }
106 }
107
108 func TestFieldElementMultiplication(t *testing.T) {
109 // Test multiplication
110 var a, b, c FieldElement
111 a.setInt(5)
112 b.setInt(7)
113 c.mul(&a, &b)
114 c.normalize()
115
116 var expected FieldElement
117 expected.setInt(35)
118 expected.normalize()
119 if !c.equal(&expected) {
120 t.Error("5 * 7 should equal 35")
121 }
122
123 // Test squaring
124 var sq FieldElement
125 sq.sqr(&a)
126 sq.normalize()
127
128 expected.setInt(25)
129 expected.normalize()
130 if !sq.equal(&expected) {
131 t.Error("5^2 should equal 25")
132 }
133 }
134
135 func TestFieldElementNormalization(t *testing.T) {
136 var fe FieldElement
137 fe.setInt(42)
138
139 // Before normalization
140 if fe.normalized {
141 fe.normalized = false // Force non-normalized state
142 }
143
144 // After normalization
145 fe.normalize()
146 if !fe.normalized {
147 t.Error("Field element should be normalized after normalize()")
148 }
149 if fe.magnitude != 1 {
150 t.Error("Normalized field element should have magnitude 1")
151 }
152 }
153
154 func TestFieldElementOddness(t *testing.T) {
155 var even, odd FieldElement
156 even.setInt(4)
157 even.normalize()
158 odd.setInt(5)
159 odd.normalize()
160
161 if even.isOdd() {
162 t.Error("4 should be even")
163 }
164 if !odd.isOdd() {
165 t.Error("5 should be odd")
166 }
167 }
168
169 func TestFieldElementConditionalMove(t *testing.T) {
170 var a, b, original FieldElement
171 a.setInt(5)
172 b.setInt(10)
173 original = a
174
175 // Test conditional move with flag = 0
176 a.cmov(&b, 0)
177 if !a.equal(&original) {
178 t.Error("Conditional move with flag=0 should not change value")
179 }
180
181 // Test conditional move with flag = 1
182 a.cmov(&b, 1)
183 if !a.equal(&b) {
184 t.Error("Conditional move with flag=1 should copy value")
185 }
186 }
187
188 func TestFieldElementStorage(t *testing.T) {
189 var fe FieldElement
190 fe.setInt(12345)
191 fe.normalize()
192
193 // Convert to storage
194 var storage FieldElementStorage
195 fe.toStorage(&storage)
196
197 // Convert back
198 var restored FieldElement
199 restored.fromStorage(&storage)
200 restored.normalize()
201
202 if !fe.equal(&restored) {
203 t.Error("Storage round-trip should preserve value")
204 }
205 }
206
207 func TestFieldElementEdgeCases(t *testing.T) {
208 // Test field modulus boundary
209 // Set to p-1 (field modulus minus 1)
210 // p-1 = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2E
211 p_minus_1 := [32]byte{
212 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
213 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
214 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
215 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2E,
216 }
217
218 var fe FieldElement
219 fe.setB32(p_minus_1[:])
220 fe.normalize()
221
222 // Add 1 should give 0
223 var one FieldElement
224 one.setInt(1)
225 fe.add(&one)
226 fe.normalize()
227
228 if !fe.isZero() {
229 t.Error("(p-1) + 1 should equal 0 in field arithmetic")
230 }
231 }
232
233 func TestFieldElementClear(t *testing.T) {
234 var fe FieldElement
235 fe.setInt(12345)
236
237 fe.clear()
238
239 // After clearing, should be zero and normalized
240 if !fe.isZero() {
241 t.Error("Cleared field element should be zero")
242 }
243 if !fe.normalized {
244 t.Error("Cleared field element should be normalized")
245 }
246 }
247
248 // TestMontgomery tests Montgomery multiplication (currently disabled due to incomplete implementation)
249 // TODO: Re-enable once Montgomery multiplication is fully implemented
250 func TestMontgomery(t *testing.T) {
251 t.Skip("Montgomery multiplication implementation is incomplete - see MONTGOMERY_NOTES.md")
252
253 // Test Montgomery conversion round-trip
254 t.Run("RoundTrip", func(t *testing.T) {
255 var a, b FieldElement
256 a.setInt(123)
257 b.setInt(456)
258 a.normalize()
259 b.normalize()
260
261 // Convert to Montgomery form
262 aMont := a.ToMontgomery()
263 bMont := b.ToMontgomery()
264
265 // Convert back
266 aBack := aMont.FromMontgomery()
267 bBack := bMont.FromMontgomery()
268
269 // Normalize for comparison
270 aBack.normalize()
271 bBack.normalize()
272
273 if !aBack.equal(&a) {
274 t.Errorf("Round-trip conversion failed for a: got %x, want %x", aBack.n, a.n)
275 }
276 if !bBack.equal(&b) {
277 t.Errorf("Round-trip conversion failed for b: got %x, want %x", bBack.n, b.n)
278 }
279 })
280
281 // Test Montgomery multiplication correctness
282 t.Run("Multiplication", func(t *testing.T) {
283 testCases := []struct {
284 name string
285 a, b int
286 }{
287 {"small", 123, 456},
288 {"medium", 1000, 2000},
289 {"one", 1, 1},
290 {"zero_a", 0, 123},
291 {"zero_b", 123, 0},
292 }
293
294 for _, tc := range testCases {
295 t.Run(tc.name, func(t *testing.T) {
296 var a, b FieldElement
297 a.setInt(tc.a)
298 b.setInt(tc.b)
299 a.normalize()
300 b.normalize()
301
302 // Standard multiplication
303 var stdResult FieldElement
304 stdResult.mul(&a, &b)
305 stdResult.normalize()
306
307 // Montgomery multiplication
308 aMont := a.ToMontgomery()
309 bMont := b.ToMontgomery()
310 montResult := MontgomeryMul(aMont, bMont)
311 montResult = montResult.FromMontgomery()
312 montResult.normalize()
313
314 if !montResult.equal(&stdResult) {
315 t.Errorf("Montgomery multiplication failed for %d * %d:\nGot: %x\nWant: %x",
316 tc.a, tc.b, montResult.n, stdResult.n)
317 }
318 })
319 }
320 })
321
322 // Test Montgomery multiplication with field modulus boundary values
323 t.Run("BoundaryValues", func(t *testing.T) {
324 // Test with p-1
325 pMinus1Bytes := [32]byte{
326 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
327 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
328 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
329 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2E,
330 }
331
332 var pMinus1 FieldElement
333 pMinus1.setB32(pMinus1Bytes[:])
334 pMinus1.normalize()
335
336 // (p-1) * (p-1) should equal 1 mod p
337 var expected FieldElement
338 expected.setInt(1)
339 expected.normalize()
340
341 // Standard multiplication
342 var stdResult FieldElement
343 stdResult.mul(&pMinus1, &pMinus1)
344 stdResult.normalize()
345
346 // Montgomery multiplication
347 pMinus1Mont := pMinus1.ToMontgomery()
348 montResult := MontgomeryMul(pMinus1Mont, pMinus1Mont)
349 montResult = montResult.FromMontgomery()
350 montResult.normalize()
351
352 if !montResult.equal(&expected) {
353 t.Errorf("Montgomery multiplication failed for (p-1)*(p-1):\nGot: %x\nWant: %x",
354 montResult.n, expected.n)
355 }
356
357 if !stdResult.equal(&expected) {
358 t.Errorf("Standard multiplication failed for (p-1)*(p-1):\nGot: %x\nWant: %x",
359 stdResult.n, expected.n)
360 }
361 })
362
363 // Test multiple Montgomery multiplications in sequence
364 t.Run("SequentialMultiplications", func(t *testing.T) {
365 var a, b, c FieldElement
366 a.setInt(123)
367 b.setInt(456)
368 c.setInt(789)
369 a.normalize()
370 b.normalize()
371 c.normalize()
372
373 // Standard: (a * b) * c
374 var stdResult FieldElement
375 stdResult.mul(&a, &b)
376 stdResult.mul(&stdResult, &c)
377 stdResult.normalize()
378
379 // Montgomery: convert once, multiply multiple times
380 aMont := a.ToMontgomery()
381 bMont := b.ToMontgomery()
382 cMont := c.ToMontgomery()
383
384 montResult := MontgomeryMul(aMont, bMont)
385 montResult = MontgomeryMul(montResult, cMont)
386 montResult = montResult.FromMontgomery()
387 montResult.normalize()
388
389 if !montResult.equal(&stdResult) {
390 t.Errorf("Sequential Montgomery multiplication failed:\nGot: %x\nWant: %x",
391 montResult.n, stdResult.n)
392 }
393 })
394 }
395