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