github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/crypto/internal/edwards25519/field/fe_test.go (about) 1 // Copyright (c) 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package field 6 7 import ( 8 "bytes" 9 "crypto/rand" 10 "encoding/hex" 11 "io" 12 "math/big" 13 "math/bits" 14 mathrand "math/rand" 15 "reflect" 16 "testing" 17 "testing/quick" 18 ) 19 20 func (v Element) String() string { 21 return hex.EncodeToString(v.Bytes()) 22 } 23 24 // quickCheckConfig returns a quick.Config that scales the max count by the 25 // given factor if the -short flag is not set. 26 func quickCheckConfig(slowScale int) *quick.Config { 27 cfg := new(quick.Config) 28 if !testing.Short() { 29 cfg.MaxCountScale = float64(slowScale) 30 } 31 return cfg 32 } 33 34 func generateFieldElement(rand *mathrand.Rand) Element { 35 const maskLow52Bits = (1 << 52) - 1 36 return Element{ 37 rand.Uint64() & maskLow52Bits, 38 rand.Uint64() & maskLow52Bits, 39 rand.Uint64() & maskLow52Bits, 40 rand.Uint64() & maskLow52Bits, 41 rand.Uint64() & maskLow52Bits, 42 } 43 } 44 45 // weirdLimbs can be combined to generate a range of edge-case field elements. 46 // 0 and -1 are intentionally more weighted, as they combine well. 47 var ( 48 weirdLimbs51 = []uint64{ 49 0, 0, 0, 0, 50 1, 51 19 - 1, 52 19, 53 0x2aaaaaaaaaaaa, 54 0x5555555555555, 55 (1 << 51) - 20, 56 (1 << 51) - 19, 57 (1 << 51) - 1, (1 << 51) - 1, 58 (1 << 51) - 1, (1 << 51) - 1, 59 } 60 weirdLimbs52 = []uint64{ 61 0, 0, 0, 0, 0, 0, 62 1, 63 19 - 1, 64 19, 65 0x2aaaaaaaaaaaa, 66 0x5555555555555, 67 (1 << 51) - 20, 68 (1 << 51) - 19, 69 (1 << 51) - 1, (1 << 51) - 1, 70 (1 << 51) - 1, (1 << 51) - 1, 71 (1 << 51) - 1, (1 << 51) - 1, 72 1 << 51, 73 (1 << 51) + 1, 74 (1 << 52) - 19, 75 (1 << 52) - 1, 76 } 77 ) 78 79 func generateWeirdFieldElement(rand *mathrand.Rand) Element { 80 return Element{ 81 weirdLimbs52[rand.Intn(len(weirdLimbs52))], 82 weirdLimbs51[rand.Intn(len(weirdLimbs51))], 83 weirdLimbs51[rand.Intn(len(weirdLimbs51))], 84 weirdLimbs51[rand.Intn(len(weirdLimbs51))], 85 weirdLimbs51[rand.Intn(len(weirdLimbs51))], 86 } 87 } 88 89 func (Element) Generate(rand *mathrand.Rand, size int) reflect.Value { 90 if rand.Intn(2) == 0 { 91 return reflect.ValueOf(generateWeirdFieldElement(rand)) 92 } 93 return reflect.ValueOf(generateFieldElement(rand)) 94 } 95 96 // isInBounds returns whether the element is within the expected bit size bounds 97 // after a light reduction. 98 func isInBounds(x *Element) bool { 99 return bits.Len64(x.l0) <= 52 && 100 bits.Len64(x.l1) <= 52 && 101 bits.Len64(x.l2) <= 52 && 102 bits.Len64(x.l3) <= 52 && 103 bits.Len64(x.l4) <= 52 104 } 105 106 func TestMultiplyDistributesOverAdd(t *testing.T) { 107 multiplyDistributesOverAdd := func(x, y, z Element) bool { 108 // Compute t1 = (x+y)*z 109 t1 := new(Element) 110 t1.Add(&x, &y) 111 t1.Multiply(t1, &z) 112 113 // Compute t2 = x*z + y*z 114 t2 := new(Element) 115 t3 := new(Element) 116 t2.Multiply(&x, &z) 117 t3.Multiply(&y, &z) 118 t2.Add(t2, t3) 119 120 return t1.Equal(t2) == 1 && isInBounds(t1) && isInBounds(t2) 121 } 122 123 if err := quick.Check(multiplyDistributesOverAdd, quickCheckConfig(1024)); err != nil { 124 t.Error(err) 125 } 126 } 127 128 func TestMul64to128(t *testing.T) { 129 a := uint64(5) 130 b := uint64(5) 131 r := mul64(a, b) 132 if r.lo != 0x19 || r.hi != 0 { 133 t.Errorf("lo-range wide mult failed, got %d + %d*(2**64)", r.lo, r.hi) 134 } 135 136 a = uint64(18014398509481983) // 2^54 - 1 137 b = uint64(18014398509481983) // 2^54 - 1 138 r = mul64(a, b) 139 if r.lo != 0xff80000000000001 || r.hi != 0xfffffffffff { 140 t.Errorf("hi-range wide mult failed, got %d + %d*(2**64)", r.lo, r.hi) 141 } 142 143 a = uint64(1125899906842661) 144 b = uint64(2097155) 145 r = mul64(a, b) 146 r = addMul64(r, a, b) 147 r = addMul64(r, a, b) 148 r = addMul64(r, a, b) 149 r = addMul64(r, a, b) 150 if r.lo != 16888498990613035 || r.hi != 640 { 151 t.Errorf("wrong answer: %d + %d*(2**64)", r.lo, r.hi) 152 } 153 } 154 155 func TestSetBytesRoundTrip(t *testing.T) { 156 f1 := func(in [32]byte, fe Element) bool { 157 fe.SetBytes(in[:]) 158 159 // Mask the most significant bit as it's ignored by SetBytes. (Now 160 // instead of earlier so we check the masking in SetBytes is working.) 161 in[len(in)-1] &= (1 << 7) - 1 162 163 return bytes.Equal(in[:], fe.Bytes()) && isInBounds(&fe) 164 } 165 if err := quick.Check(f1, nil); err != nil { 166 t.Errorf("failed bytes->FE->bytes round-trip: %v", err) 167 } 168 169 f2 := func(fe, r Element) bool { 170 r.SetBytes(fe.Bytes()) 171 172 // Intentionally not using Equal not to go through Bytes again. 173 // Calling reduce because both Generate and SetBytes can produce 174 // non-canonical representations. 175 fe.reduce() 176 r.reduce() 177 return fe == r 178 } 179 if err := quick.Check(f2, nil); err != nil { 180 t.Errorf("failed FE->bytes->FE round-trip: %v", err) 181 } 182 183 // Check some fixed vectors from dalek 184 type feRTTest struct { 185 fe Element 186 b []byte 187 } 188 var tests = []feRTTest{ 189 { 190 fe: Element{358744748052810, 1691584618240980, 977650209285361, 1429865912637724, 560044844278676}, 191 b: []byte{74, 209, 69, 197, 70, 70, 161, 222, 56, 226, 229, 19, 112, 60, 25, 92, 187, 74, 222, 56, 50, 153, 51, 233, 40, 74, 57, 6, 160, 185, 213, 31}, 192 }, 193 { 194 fe: Element{84926274344903, 473620666599931, 365590438845504, 1028470286882429, 2146499180330972}, 195 b: []byte{199, 23, 106, 112, 61, 77, 216, 79, 186, 60, 11, 118, 13, 16, 103, 15, 42, 32, 83, 250, 44, 57, 204, 198, 78, 199, 253, 119, 146, 172, 3, 122}, 196 }, 197 } 198 199 for _, tt := range tests { 200 b := tt.fe.Bytes() 201 fe, _ := new(Element).SetBytes(tt.b) 202 if !bytes.Equal(b, tt.b) || fe.Equal(&tt.fe) != 1 { 203 t.Errorf("Failed fixed roundtrip: %v", tt) 204 } 205 } 206 } 207 208 func swapEndianness(buf []byte) []byte { 209 for i := 0; i < len(buf)/2; i++ { 210 buf[i], buf[len(buf)-i-1] = buf[len(buf)-i-1], buf[i] 211 } 212 return buf 213 } 214 215 func TestBytesBigEquivalence(t *testing.T) { 216 f1 := func(in [32]byte, fe, fe1 Element) bool { 217 fe.SetBytes(in[:]) 218 219 in[len(in)-1] &= (1 << 7) - 1 // mask the most significant bit 220 b := new(big.Int).SetBytes(swapEndianness(in[:])) 221 fe1.fromBig(b) 222 223 if fe != fe1 { 224 return false 225 } 226 227 buf := make([]byte, 32) 228 buf = swapEndianness(fe1.toBig().FillBytes(buf)) 229 230 return bytes.Equal(fe.Bytes(), buf) && isInBounds(&fe) && isInBounds(&fe1) 231 } 232 if err := quick.Check(f1, nil); err != nil { 233 t.Error(err) 234 } 235 } 236 237 // fromBig sets v = n, and returns v. The bit length of n must not exceed 256. 238 func (v *Element) fromBig(n *big.Int) *Element { 239 if n.BitLen() > 32*8 { 240 panic("edwards25519: invalid field element input size") 241 } 242 243 buf := make([]byte, 0, 32) 244 for _, word := range n.Bits() { 245 for i := 0; i < bits.UintSize; i += 8 { 246 if len(buf) >= cap(buf) { 247 break 248 } 249 buf = append(buf, byte(word)) 250 word >>= 8 251 } 252 } 253 254 v.SetBytes(buf[:32]) 255 return v 256 } 257 258 func (v *Element) fromDecimal(s string) *Element { 259 n, ok := new(big.Int).SetString(s, 10) 260 if !ok { 261 panic("not a valid decimal: " + s) 262 } 263 return v.fromBig(n) 264 } 265 266 // toBig returns v as a big.Int. 267 func (v *Element) toBig() *big.Int { 268 buf := v.Bytes() 269 270 words := make([]big.Word, 32*8/bits.UintSize) 271 for n := range words { 272 for i := 0; i < bits.UintSize; i += 8 { 273 if len(buf) == 0 { 274 break 275 } 276 words[n] |= big.Word(buf[0]) << big.Word(i) 277 buf = buf[1:] 278 } 279 } 280 281 return new(big.Int).SetBits(words) 282 } 283 284 func TestDecimalConstants(t *testing.T) { 285 sqrtM1String := "19681161376707505956807079304988542015446066515923890162744021073123829784752" 286 if exp := new(Element).fromDecimal(sqrtM1String); sqrtM1.Equal(exp) != 1 { 287 t.Errorf("sqrtM1 is %v, expected %v", sqrtM1, exp) 288 } 289 // d is in the parent package, and we don't want to expose d or fromDecimal. 290 // dString := "37095705934669439343138083508754565189542113879843219016388785533085940283555" 291 // if exp := new(Element).fromDecimal(dString); d.Equal(exp) != 1 { 292 // t.Errorf("d is %v, expected %v", d, exp) 293 // } 294 } 295 296 func TestSetBytesRoundTripEdgeCases(t *testing.T) { 297 // TODO: values close to 0, close to 2^255-19, between 2^255-19 and 2^255-1, 298 // and between 2^255 and 2^256-1. Test both the documented SetBytes 299 // behavior, and that Bytes reduces them. 300 } 301 302 // Tests self-consistency between Multiply and Square. 303 func TestConsistency(t *testing.T) { 304 var x Element 305 var x2, x2sq Element 306 307 x = Element{1, 1, 1, 1, 1} 308 x2.Multiply(&x, &x) 309 x2sq.Square(&x) 310 311 if x2 != x2sq { 312 t.Fatalf("all ones failed\nmul: %x\nsqr: %x\n", x2, x2sq) 313 } 314 315 var bytes [32]byte 316 317 _, err := io.ReadFull(rand.Reader, bytes[:]) 318 if err != nil { 319 t.Fatal(err) 320 } 321 x.SetBytes(bytes[:]) 322 323 x2.Multiply(&x, &x) 324 x2sq.Square(&x) 325 326 if x2 != x2sq { 327 t.Fatalf("all ones failed\nmul: %x\nsqr: %x\n", x2, x2sq) 328 } 329 } 330 331 func TestEqual(t *testing.T) { 332 x := Element{1, 1, 1, 1, 1} 333 y := Element{5, 4, 3, 2, 1} 334 335 eq := x.Equal(&x) 336 if eq != 1 { 337 t.Errorf("wrong about equality") 338 } 339 340 eq = x.Equal(&y) 341 if eq != 0 { 342 t.Errorf("wrong about inequality") 343 } 344 } 345 346 func TestInvert(t *testing.T) { 347 x := Element{1, 1, 1, 1, 1} 348 one := Element{1, 0, 0, 0, 0} 349 var xinv, r Element 350 351 xinv.Invert(&x) 352 r.Multiply(&x, &xinv) 353 r.reduce() 354 355 if one != r { 356 t.Errorf("inversion identity failed, got: %x", r) 357 } 358 359 var bytes [32]byte 360 361 _, err := io.ReadFull(rand.Reader, bytes[:]) 362 if err != nil { 363 t.Fatal(err) 364 } 365 x.SetBytes(bytes[:]) 366 367 xinv.Invert(&x) 368 r.Multiply(&x, &xinv) 369 r.reduce() 370 371 if one != r { 372 t.Errorf("random inversion identity failed, got: %x for field element %x", r, x) 373 } 374 375 zero := Element{} 376 x.Set(&zero) 377 if xx := xinv.Invert(&x); xx != &xinv { 378 t.Errorf("inverting zero did not return the receiver") 379 } else if xinv.Equal(&zero) != 1 { 380 t.Errorf("inverting zero did not return zero") 381 } 382 } 383 384 func TestSelectSwap(t *testing.T) { 385 a := Element{358744748052810, 1691584618240980, 977650209285361, 1429865912637724, 560044844278676} 386 b := Element{84926274344903, 473620666599931, 365590438845504, 1028470286882429, 2146499180330972} 387 388 var c, d Element 389 390 c.Select(&a, &b, 1) 391 d.Select(&a, &b, 0) 392 393 if c.Equal(&a) != 1 || d.Equal(&b) != 1 { 394 t.Errorf("Select failed") 395 } 396 397 c.Swap(&d, 0) 398 399 if c.Equal(&a) != 1 || d.Equal(&b) != 1 { 400 t.Errorf("Swap failed") 401 } 402 403 c.Swap(&d, 1) 404 405 if c.Equal(&b) != 1 || d.Equal(&a) != 1 { 406 t.Errorf("Swap failed") 407 } 408 } 409 410 func TestMult32(t *testing.T) { 411 mult32EquivalentToMul := func(x Element, y uint32) bool { 412 t1 := new(Element) 413 for i := 0; i < 100; i++ { 414 t1.Mult32(&x, y) 415 } 416 417 ty := new(Element) 418 ty.l0 = uint64(y) 419 420 t2 := new(Element) 421 for i := 0; i < 100; i++ { 422 t2.Multiply(&x, ty) 423 } 424 425 return t1.Equal(t2) == 1 && isInBounds(t1) && isInBounds(t2) 426 } 427 428 if err := quick.Check(mult32EquivalentToMul, quickCheckConfig(1024)); err != nil { 429 t.Error(err) 430 } 431 } 432 433 func TestSqrtRatio(t *testing.T) { 434 // From draft-irtf-cfrg-ristretto255-decaf448-00, Appendix A.4. 435 type test struct { 436 u, v string 437 wasSquare int 438 r string 439 } 440 var tests = []test{ 441 // If u is 0, the function is defined to return (0, TRUE), even if v 442 // is zero. Note that where used in this package, the denominator v 443 // is never zero. 444 { 445 "0000000000000000000000000000000000000000000000000000000000000000", 446 "0000000000000000000000000000000000000000000000000000000000000000", 447 1, "0000000000000000000000000000000000000000000000000000000000000000", 448 }, 449 // 0/1 == 0² 450 { 451 "0000000000000000000000000000000000000000000000000000000000000000", 452 "0100000000000000000000000000000000000000000000000000000000000000", 453 1, "0000000000000000000000000000000000000000000000000000000000000000", 454 }, 455 // If u is non-zero and v is zero, defined to return (0, FALSE). 456 { 457 "0100000000000000000000000000000000000000000000000000000000000000", 458 "0000000000000000000000000000000000000000000000000000000000000000", 459 0, "0000000000000000000000000000000000000000000000000000000000000000", 460 }, 461 // 2/1 is not square in this field. 462 { 463 "0200000000000000000000000000000000000000000000000000000000000000", 464 "0100000000000000000000000000000000000000000000000000000000000000", 465 0, "3c5ff1b5d8e4113b871bd052f9e7bcd0582804c266ffb2d4f4203eb07fdb7c54", 466 }, 467 // 4/1 == 2² 468 { 469 "0400000000000000000000000000000000000000000000000000000000000000", 470 "0100000000000000000000000000000000000000000000000000000000000000", 471 1, "0200000000000000000000000000000000000000000000000000000000000000", 472 }, 473 // 1/4 == (2⁻¹)² == (2^(p-2))² per Euler's theorem 474 { 475 "0100000000000000000000000000000000000000000000000000000000000000", 476 "0400000000000000000000000000000000000000000000000000000000000000", 477 1, "f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f", 478 }, 479 } 480 481 for i, tt := range tests { 482 u, _ := new(Element).SetBytes(decodeHex(tt.u)) 483 v, _ := new(Element).SetBytes(decodeHex(tt.v)) 484 want, _ := new(Element).SetBytes(decodeHex(tt.r)) 485 got, wasSquare := new(Element).SqrtRatio(u, v) 486 if got.Equal(want) == 0 || wasSquare != tt.wasSquare { 487 t.Errorf("%d: got (%v, %v), want (%v, %v)", i, got, wasSquare, want, tt.wasSquare) 488 } 489 } 490 } 491 492 func TestCarryPropagate(t *testing.T) { 493 asmLikeGeneric := func(a [5]uint64) bool { 494 t1 := &Element{a[0], a[1], a[2], a[3], a[4]} 495 t2 := &Element{a[0], a[1], a[2], a[3], a[4]} 496 497 t1.carryPropagate() 498 t2.carryPropagateGeneric() 499 500 if *t1 != *t2 { 501 t.Logf("got: %#v,\nexpected: %#v", t1, t2) 502 } 503 504 return *t1 == *t2 && isInBounds(t2) 505 } 506 507 if err := quick.Check(asmLikeGeneric, quickCheckConfig(1024)); err != nil { 508 t.Error(err) 509 } 510 511 if !asmLikeGeneric([5]uint64{0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff}) { 512 t.Errorf("failed for {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff}") 513 } 514 } 515 516 func TestFeSquare(t *testing.T) { 517 asmLikeGeneric := func(a Element) bool { 518 t1 := a 519 t2 := a 520 521 feSquareGeneric(&t1, &t1) 522 feSquare(&t2, &t2) 523 524 if t1 != t2 { 525 t.Logf("got: %#v,\nexpected: %#v", t1, t2) 526 } 527 528 return t1 == t2 && isInBounds(&t2) 529 } 530 531 if err := quick.Check(asmLikeGeneric, quickCheckConfig(1024)); err != nil { 532 t.Error(err) 533 } 534 } 535 536 func TestFeMul(t *testing.T) { 537 asmLikeGeneric := func(a, b Element) bool { 538 a1 := a 539 a2 := a 540 b1 := b 541 b2 := b 542 543 feMulGeneric(&a1, &a1, &b1) 544 feMul(&a2, &a2, &b2) 545 546 if a1 != a2 || b1 != b2 { 547 t.Logf("got: %#v,\nexpected: %#v", a1, a2) 548 t.Logf("got: %#v,\nexpected: %#v", b1, b2) 549 } 550 551 return a1 == a2 && isInBounds(&a2) && 552 b1 == b2 && isInBounds(&b2) 553 } 554 555 if err := quick.Check(asmLikeGeneric, quickCheckConfig(1024)); err != nil { 556 t.Error(err) 557 } 558 } 559 560 func decodeHex(s string) []byte { 561 b, err := hex.DecodeString(s) 562 if err != nil { 563 panic(err) 564 } 565 return b 566 }