// Copyright (c) 2020-2023 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. package secp256k1 import ( "bytes" "encoding/hex" "fmt" "math/big" "math/rand" "reflect" "testing" "time" ) // SetHex interprets the provided hex string as a 256-bit big-endian unsigned // integer (meaning it is truncated to the first 32 bytes), reduces it modulo // the group order and sets the scalar to the result. // // This is NOT constant time. // // The scalar is returned to support chaining. This enables syntax like: // s := new(ModNScalar).SetHex("0abc").Add(1) so that s = 0x0abc + 1 func (s *ModNScalar) SetHex(hexString string) *ModNScalar { if len(hexString)%2 != 0 { hexString = "0" + hexString } bytes, _ := hex.DecodeString(hexString) s.SetByteSlice(bytes) return s } // randModNScalar returns a mod N scalar created from a random value generated // by the passed rng. func randModNScalar(t *testing.T, rng *rand.Rand) *ModNScalar { t.Helper() var buf [32]byte if _, err := rng.Read(buf[:]); err != nil { t.Fatalf("failed to read random: %v", err) } // Create and return a mod N scalar. var modNVal ModNScalar modNVal.SetBytes(&buf) return &modNVal } // randIntAndModNScalar returns a big integer and mod N scalar both created from // the same random value generated by the passed rng. func randIntAndModNScalar(t *testing.T, rng *rand.Rand) (*big.Int, *ModNScalar) { t.Helper() var buf [32]byte if _, err := rng.Read(buf[:]); err != nil { t.Fatalf("failed to read random: %v", err) } // Create and return both a big integer and a mod N scalar. bigIntVal := new(big.Int).SetBytes(buf[:]) bigIntVal.Mod(bigIntVal, curveParams.N) var modNVal ModNScalar modNVal.SetBytes(&buf) return bigIntVal, &modNVal } // TestModNScalarZero ensures that zeroing a scalar modulo the group order works // as expected. func TestModNScalarZero(t *testing.T) { var s ModNScalar s.SetHex("a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5") s.Zero() for idx, rawInt := range s.n { if rawInt != 0 { t.Errorf("internal integer at index #%d is not zero - got %d", idx, rawInt) } } } // TestModNScalarIsZero ensures that checking if a scalar is zero via IsZero and // IsZeroBit works as expected. func TestModNScalarIsZero(t *testing.T) { var s ModNScalar if !s.IsZero() { t.Errorf("new scalar is not zero - got %v (rawints %x)", s, s.n) } if s.IsZeroBit() != 1 { t.Errorf("new scalar is not zero - got %v (rawints %x)", s, s.n) } s.SetInt(1) if s.IsZero() { t.Errorf("claims zero for nonzero scalar - got %v (rawints %x)", s, s.n) } if s.IsZeroBit() == 1 { t.Errorf("claims zero for nonzero scalar - got %v (rawints %x)", s, s.n) } s.SetInt(0) if !s.IsZero() { t.Errorf("claims nonzero for zero scalar - got %v (rawints %x)", s, s.n) } if s.IsZeroBit() != 1 { t.Errorf("claims nonzero for zero scalar - got %v (rawints %x)", s, s.n) } s.SetInt(1) s.Zero() if !s.IsZero() { t.Errorf("claims nonzero for zero scalar - got %v (rawints %x)", s, s.n) } if s.IsZeroBit() != 1 { t.Errorf("claims nonzero for zero scalar - got %v (rawints %x)", s, s.n) } } // TestModNScalarSetInt ensures that setting a scalar to various native integers // works as expected. func TestModNScalarSetInt(t *testing.T) { tests := []struct { name string // test description in uint32 // test value expected [8]uint32 // expected raw ints }{{ name: "five", in: 5, expected: [8]uint32{5, 0, 0, 0, 0, 0, 0, 0}, }, { name: "group order word zero", in: orderWordZero, expected: [8]uint32{orderWordZero, 0, 0, 0, 0, 0, 0, 0}, }, { name: "group order word zero + 1", in: orderWordZero + 1, expected: [8]uint32{orderWordZero + 1, 0, 0, 0, 0, 0, 0, 0}, }, { name: "2^32 - 1", in: 4294967295, expected: [8]uint32{4294967295, 0, 0, 0, 0, 0, 0, 0}, }} for _, test := range tests { s := new(ModNScalar).SetInt(test.in) if !reflect.DeepEqual(s.n, test.expected) { t.Errorf("%s: wrong result\ngot: %v\nwant: %v", test.name, s.n, test.expected) continue } } } // TestModNScalarSetBytes ensures that setting a scalar to a 256-bit big-endian // unsigned integer via both the slice and array methods works as expected for // edge cases. Random cases are tested via the various other tests. func TestModNScalarSetBytes(t *testing.T) { tests := []struct { name string // test description in string // hex encoded test value expected [8]uint32 // expected raw ints overflow bool // expected overflow result }{{ name: "zero", in: "00", expected: [8]uint32{0, 0, 0, 0, 0, 0, 0, 0}, overflow: false, }, { name: "group order (aka 0)", in: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", expected: [8]uint32{0, 0, 0, 0, 0, 0, 0, 0}, overflow: true, }, { name: "group order - 1", in: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", expected: [8]uint32{ 0xd0364140, 0xbfd25e8c, 0xaf48a03b, 0xbaaedce6, 0xfffffffe, 0xffffffff, 0xffffffff, 0xffffffff, }, overflow: false, }, { name: "group order + 1 (aka 1, overflow in word zero)", in: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", expected: [8]uint32{1, 0, 0, 0, 0, 0, 0, 0}, overflow: true, }, { name: "group order word zero", in: "d0364141", expected: [8]uint32{0xd0364141, 0, 0, 0, 0, 0, 0, 0}, overflow: false, }, { name: "group order word zero and one", in: "bfd25e8cd0364141", expected: [8]uint32{0xd0364141, 0xbfd25e8c, 0, 0, 0, 0, 0, 0}, overflow: false, }, { name: "group order words zero, one, and two", in: "af48a03bbfd25e8cd0364141", expected: [8]uint32{0xd0364141, 0xbfd25e8c, 0xaf48a03b, 0, 0, 0, 0, 0}, overflow: false, }, { name: "overflow in word one", in: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8dd0364141", expected: [8]uint32{0, 1, 0, 0, 0, 0, 0, 0}, overflow: true, }, { name: "overflow in word two", in: "fffffffffffffffffffffffffffffffebaaedce6af48a03cbfd25e8cd0364141", expected: [8]uint32{0, 0, 1, 0, 0, 0, 0, 0}, overflow: true, }, { name: "overflow in word three", in: "fffffffffffffffffffffffffffffffebaaedce7af48a03bbfd25e8cd0364141", expected: [8]uint32{0, 0, 0, 1, 0, 0, 0, 0}, overflow: true, }, { name: "overflow in word four", in: "ffffffffffffffffffffffffffffffffbaaedce6af48a03bbfd25e8cd0364141", expected: [8]uint32{0, 0, 0, 0, 1, 0, 0, 0}, overflow: true, }, { name: "(group order - 1) * 2 NOT mod N, truncated >32 bytes", in: "01fffffffffffffffffffffffffffffffd755db9cd5e9140777fa4bd19a06c8284", expected: [8]uint32{ 0x19a06c82, 0x777fa4bd, 0xcd5e9140, 0xfd755db9, 0xffffffff, 0xffffffff, 0xffffffff, 0x01ffffff, }, overflow: false, }, { name: "alternating bits", in: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", expected: [8]uint32{ 0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5, }, overflow: false, }, { name: "alternating bits 2", in: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", expected: [8]uint32{ 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, }, overflow: false, }} for _, test := range tests { inBytes := hexToBytes(test.in) // Ensure setting the bytes via the slice method works as expected. var s ModNScalar overflow := s.SetByteSlice(inBytes) if !reflect.DeepEqual(s.n, test.expected) { t.Errorf("%s: unexpected result\ngot: %x\nwant: %x", test.name, s.n, test.expected) continue } // Ensure the setting the bytes via the slice method produces the // expected overflow result. if overflow != test.overflow { t.Errorf("%s: unexpected overflow -- got: %v, want: %v", test.name, overflow, test.overflow) continue } // Ensure setting the bytes via the array method works as expected. var s2 ModNScalar var b32 [32]byte truncatedInBytes := inBytes if len(truncatedInBytes) > 32 { truncatedInBytes = truncatedInBytes[:32] } copy(b32[32-len(truncatedInBytes):], truncatedInBytes) overflow = s2.SetBytes(&b32) != 0 if !reflect.DeepEqual(s2.n, test.expected) { t.Errorf("%s: unexpected result\ngot: %x\nwant: %x", test.name, s2.n, test.expected) continue } // Ensure the setting the bytes via the array method produces the // expected overflow result. if overflow != test.overflow { t.Errorf("%s: unexpected overflow -- got: %v, want: %v", test.name, overflow, test.overflow) continue } } } // TestModNScalarBytes ensures that retrieving the bytes for a 256-bit // big-endian unsigned integer via the various methods works as expected for // edge cases. Random cases are tested via the various other tests. func TestModNScalarBytes(t *testing.T) { tests := []struct { name string // test description in string // hex encoded test value expected string // expected hex encoded bytes overflow bool // expected overflow result }{{ name: "zero", in: "0", expected: "0000000000000000000000000000000000000000000000000000000000000000", }, { name: "group order (aka 0)", in: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", expected: "0000000000000000000000000000000000000000000000000000000000000000", }, { name: "group order - 1", in: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", expected: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", }, { name: "group order + 1 (aka 1, overflow in word zero)", in: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", expected: "0000000000000000000000000000000000000000000000000000000000000001", }, { name: "group order word zero", in: "d0364141", expected: "00000000000000000000000000000000000000000000000000000000d0364141", }, { name: "group order word zero and one", in: "bfd25e8cd0364141", expected: "000000000000000000000000000000000000000000000000bfd25e8cd0364141", }, { name: "group order words zero, one, and two", in: "af48a03bbfd25e8cd0364141", expected: "0000000000000000000000000000000000000000af48a03bbfd25e8cd0364141", }, { name: "overflow in word one", in: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8dd0364141", expected: "0000000000000000000000000000000000000000000000000000000100000000", }, { name: "overflow in word two", in: "fffffffffffffffffffffffffffffffebaaedce6af48a03cbfd25e8cd0364141", expected: "0000000000000000000000000000000000000000000000010000000000000000", }, { name: "overflow in word three", in: "fffffffffffffffffffffffffffffffebaaedce7af48a03bbfd25e8cd0364141", expected: "0000000000000000000000000000000000000001000000000000000000000000", }, { name: "overflow in word four", in: "ffffffffffffffffffffffffffffffffbaaedce6af48a03bbfd25e8cd0364141", expected: "0000000000000000000000000000000100000000000000000000000000000000", }, { name: "alternating bits", in: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", expected: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", }, { name: "alternating bits 2", in: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", expected: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", }} for _, test := range tests { s := new(ModNScalar).SetHex(test.in) expected := hexToBytes(test.expected) // Ensure getting the bytes works as expected. gotBytes := s.Bytes() if !bytes.Equal(gotBytes[:], expected) { t.Errorf("%s: unexpected result\ngot: %x\nwant: %x", test.name, gotBytes, expected) continue } // Ensure getting the bytes directly into an array works as expected. var b32 [32]byte s.PutBytes(&b32) if !bytes.Equal(b32[:], expected) { t.Errorf("%s: unexpected result\ngot: %x\nwant: %x", test.name, b32, expected) continue } // Ensure getting the bytes directly into a slice works as expected. var buffer [64]byte s.PutBytesUnchecked(buffer[:]) if !bytes.Equal(buffer[:32], expected) { t.Errorf("%s: unexpected result\ngot: %x\nwant: %x", test.name, buffer[:32], expected) continue } } } // TestModNScalarIsOdd ensures that checking if a scalar is odd works as // expected. func TestModNScalarIsOdd(t *testing.T) { tests := []struct { name string // test description in string // hex encoded value expected bool // expected oddness }{{ name: "zero", in: "0", expected: false, }, { name: "one", in: "1", expected: true, }, { name: "two", in: "2", expected: false, }, { name: "2^32 - 1", in: "ffffffff", expected: true, }, { name: "2^64 - 2", in: "fffffffffffffffe", expected: false, }, { name: "group order (aka 0)", in: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", expected: false, }, { name: "group order + 1 (aka 1)", in: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", expected: true, }} for _, test := range tests { result := new(ModNScalar).SetHex(test.in).IsOdd() if result != test.expected { t.Errorf("%s: wrong result -- got: %v, want: %v", test.name, result, test.expected) continue } } } // TestModNScalarEquals ensures that checking two scalars for equality works as // expected for edge cases. func TestModNScalarEquals(t *testing.T) { tests := []struct { name string // test description in1 string // hex encoded value in2 string // hex encoded value expected bool // expected equality }{{ name: "0 == 0?", in1: "0", in2: "0", expected: true, }, { name: "0 == 1?", in1: "0", in2: "1", expected: false, }, { name: "1 == 0?", in1: "1", in2: "0", expected: false, }, { name: "2^32 - 1 == 2^32 - 1?", in1: "ffffffff", in2: "ffffffff", expected: true, }, { name: "2^64 - 1 == 2^64 - 2?", in1: "ffffffffffffffff", in2: "fffffffffffffffe", expected: false, }, { name: "0 == group order?", in1: "0", in2: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", expected: true, }, { name: "1 == group order + 1?", in1: "1", in2: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", expected: true, }} for _, test := range tests { s1 := new(ModNScalar).SetHex(test.in1) s2 := new(ModNScalar).SetHex(test.in2) result := s1.Equals(s2) if result != test.expected { t.Errorf("%s: wrong result -- got: %v, want: %v", test.name, result, test.expected) continue } } } // TestModNScalarEqualsRandom ensures that scalars for random values works as // expected. func TestModNScalarEqualsRandom(t *testing.T) { // Use a unique random seed each test instance and log it if the tests fail. seed := time.Now().Unix() rng := rand.New(rand.NewSource(seed)) defer func(t *testing.T, seed int64) { if t.Failed() { t.Logf("random seed: %d", seed) } }(t, seed) for i := 0; i < 100; i++ { // Ensure a randomly-generated scalar equals itself. s := randModNScalar(t, rng) if !s.Equals(s) { t.Fatalf("failed equality check\nscalar in: %v", s) } // Flip a random bit in a random word and ensure it's no longer equal. randomWord := rng.Int31n(int32(len(s.n))) randomBit := uint32(1 << uint32(rng.Int31n(32))) s2 := new(ModNScalar).Set(s) s2.n[randomWord] ^= randomBit if s2.Equals(s) { t.Fatalf("failed inequality check\nscalar in: %v", s2) } } } // TestModNScalarAdd ensures that adding two scalars works as expected for edge // cases. func TestModNScalarAdd(t *testing.T) { tests := []struct { name string // test description in1 string // first hex encoded test value in2 string // second hex encoded test value expected string // expected hex encoded bytes }{{ name: "zero + one", in1: "0", in2: "1", expected: "1", }, { name: "one + zero", in1: "1", in2: "0", expected: "1", }, { name: "group order (aka 0) + 1 (gets reduced, no overflow)", in1: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", in2: "1", expected: "1", }, { name: "group order - 1 + 1 (aka 0, overflow to prime)", in1: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", in2: "1", expected: "0", }, { name: "group order - 1 + 2 (aka 1, overflow in word zero)", in1: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", in2: "2", expected: "1", }, { name: "overflow in word one", in1: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8bd0364141", in2: "100000001", expected: "1", }, { name: "overflow in word two", in1: "fffffffffffffffffffffffffffffffebaaedce6af48a03abfd25e8cd0364141", in2: "10000000000000001", expected: "1", }, { name: "overflow in word three", in1: "fffffffffffffffffffffffffffffffebaaedce5af48a03bbfd25e8cd0364141", in2: "1000000000000000000000001", expected: "1", }, { name: "overflow in word four", in1: "fffffffffffffffffffffffffffffffdbaaedce6af48a03bbfd25e8cd0364141", in2: "100000000000000000000000000000001", expected: "1", }, { name: "overflow in word five", in1: "fffffffffffffffffffffffefffffffebaaedce6af48a03bbfd25e8cd0364141", in2: "10000000000000000000000000000000000000001", expected: "1", }, { name: "overflow in word six", in1: "fffffffffffffffefffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", in2: "1000000000000000000000000000000000000000000000001", expected: "1", }, { name: "overflow in word seven", in1: "fffffffefffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", in2: "100000000000000000000000000000000000000000000000000000001", expected: "1", }, { name: "alternating bits", in1: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", in2: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", expected: "14551231950b75fc4402da1732fc9bebe", }, { name: "alternating bits 2", in1: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", in2: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", expected: "14551231950b75fc4402da1732fc9bebe", }} for _, test := range tests { // Parse test hex. s1 := new(ModNScalar).SetHex(test.in1) s2 := new(ModNScalar).SetHex(test.in2) expected := new(ModNScalar).SetHex(test.expected) // Ensure the result has the expected value. s1.Add(s2) if !s1.Equals(expected) { t.Errorf("%s: unexpected result\ngot: %x\nwant: %x", test.name, s1, expected) continue } } } // TestModNScalarAddRandom ensures that adding two scalars together for random // values works as expected by also performing the same operation with big ints // and comparing the results. func TestModNScalarAddRandom(t *testing.T) { // Use a unique random seed each test instance and log it if the tests fail. seed := time.Now().Unix() rng := rand.New(rand.NewSource(seed)) defer func(t *testing.T, seed int64) { if t.Failed() { t.Logf("random seed: %d", seed) } }(t, seed) for i := 0; i < 100; i++ { // Generate two big integer and mod n scalar pairs. bigIntVal1, modNVal1 := randIntAndModNScalar(t, rng) bigIntVal2, modNVal2 := randIntAndModNScalar(t, rng) // Calculate the sum of the values using big ints. bigIntResult := new(big.Int).Add(bigIntVal1, bigIntVal2) bigIntResult.Mod(bigIntResult, curveParams.N) // Calculate the sum of the values using mod n scalars. modNValResult := new(ModNScalar).Add2(modNVal1, modNVal2) // Ensure they match. bigIntResultHex := fmt.Sprintf("%064x", bigIntResult) modNResultHex := fmt.Sprintf("%v", modNValResult) if bigIntResultHex != modNResultHex { t.Fatalf("mismatched add\nbig int in 1: %x\nbig int in 2: %x\n"+ "scalar in 1: %v\nscalar in 2: %v\nbig int result: %x\nscalar "+ "result %v", bigIntVal1, bigIntVal2, modNVal1, modNVal2, bigIntResult, modNValResult) } } } // TestAccumulator96Add ensures that the internal 96-bit accumulator used by // multiplication works as expected for overflow edge cases including overflow. func TestAccumulator96Add(t *testing.T) { tests := []struct { name string // test description start accumulator96 // starting value of accumulator in uint64 // value to add to accumulator expected accumulator96 // expected value of accumulator after addition }{{ name: "0 + 0 = 0", start: accumulator96{[3]uint32{0, 0, 0}}, in: 0, expected: accumulator96{[3]uint32{0, 0, 0}}, }, { name: "overflow in word zero", start: accumulator96{[3]uint32{0xffffffff, 0, 0}}, in: 1, expected: accumulator96{[3]uint32{0, 1, 0}}, }, { name: "overflow in word one", start: accumulator96{[3]uint32{0, 0xffffffff, 0}}, in: 0x100000000, expected: accumulator96{[3]uint32{0, 0, 1}}, }, { name: "overflow in words one and two", start: accumulator96{[3]uint32{0xffffffff, 0xffffffff, 0}}, in: 1, expected: accumulator96{[3]uint32{0, 0, 1}}, }, { // Start accumulator at 129127208455837319175 which is the result of // 4294967295 * 4294967295 accumulated seven times. name: "max result from eight adds of max uint32 multiplications", start: accumulator96{[3]uint32{7, 4294967282, 6}}, in: 18446744065119617025, expected: accumulator96{[3]uint32{8, 4294967280, 7}}, }} for _, test := range tests { acc := test.start acc.Add(test.in) if acc.n != test.expected.n { t.Errorf("%s: wrong result\ngot: %v\nwant: %v", test.name, acc.n, test.expected.n) } } } // TestModNScalarMul ensures that multiplying two scalars together works as // expected for edge cases. func TestModNScalarMul(t *testing.T) { tests := []struct { name string // test description in1 string // first hex encoded value in2 string // second hex encoded value to multiply with expected string // expected hex encoded value }{{ name: "zero * zero", in1: "0", in2: "0", expected: "0", }, { name: "one * zero", in1: "1", in2: "0", expected: "0", }, { name: "one * one", in1: "1", in2: "1", expected: "1", }, { name: "(group order-1) * 2", in1: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", in2: "2", expected: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413f", }, { name: "(group order-1) * (group order-1)", in1: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", in2: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", expected: "1", }, { name: "slightly over group order", in1: "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1", in2: "2", expected: "1", }, { name: "group order (aka 0) * 3", in1: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", in2: "3", expected: "0", }, { name: "overflow in word eight", in1: "100000000000000000000000000000000", in2: "100000000000000000000000000000000", expected: "14551231950b75fc4402da1732fc9bebf", }, { name: "overflow in word nine", in1: "1000000000000000000000000000000000000", in2: "1000000000000000000000000000000000000", expected: "14551231950b75fc4402da1732fc9bebf00000000", }, { name: "overflow in word ten", in1: "10000000000000000000000000000000000000000", in2: "10000000000000000000000000000000000000000", expected: "14551231950b75fc4402da1732fc9bebf0000000000000000", }, { name: "overflow in word eleven", in1: "100000000000000000000000000000000000000000000", in2: "100000000000000000000000000000000000000000000", expected: "14551231950b75fc4402da1732fc9bebf000000000000000000000000", }, { name: "overflow in word twelve", in1: "1000000000000000000000000000000000000000000000000", in2: "1000000000000000000000000000000000000000000000000", expected: "4551231950b75fc4402da1732fc9bec04551231950b75fc4402da1732fc9bebf", }, { name: "overflow in word thirteen", in1: "10000000000000000000000000000000000000000000000000000", in2: "10000000000000000000000000000000000000000000000000000", expected: "50b75fc4402da1732fc9bec09d671cd51b343a1b66926b57d2a4c1c61536bda7", }, { name: "overflow in word fourteen", in1: "100000000000000000000000000000000000000000000000000000000", in2: "100000000000000000000000000000000000000000000000000000000", expected: "402da1732fc9bec09d671cd581c69bc59509b0b074ec0aea8f564d667ec7eb3c", }, { name: "overflow in word fifteen", in1: "1000000000000000000000000000000000000000000000000000000000000", in2: "1000000000000000000000000000000000000000000000000000000000000", expected: "2fc9bec09d671cd581c69bc5e697f5e41f12c33a0a7b6f4e3302b92ea029cecd", }, { name: "double overflow in internal accumulator", in1: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", in2: "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c2", expected: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b7f", }, { name: "alternating bits", in1: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", in2: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", expected: "88edea3d29272800e7988455cfdf19b039dbfbb1c93b5b44a48c2ba462316838", }, { name: "alternating bits 2", in1: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", in2: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", expected: "88edea3d29272800e7988455cfdf19b039dbfbb1c93b5b44a48c2ba462316838", }} for _, test := range tests { v1 := new(ModNScalar).SetHex(test.in1) v2 := new(ModNScalar).SetHex(test.in2) expected := new(ModNScalar).SetHex(test.expected) // Ensure multiplying two other values produces the expected result. result := new(ModNScalar).Mul2(v1, v2) if !result.Equals(expected) { t.Errorf("%s: wrong result\ngot: %v\nwant: %v", test.name, result, expected) continue } // Ensure self multiplying with another value also produces the expected // result. result2 := new(ModNScalar).Set(v1).Mul(v2) if !result2.Equals(expected) { t.Errorf("%s: wrong result\ngot: %v\nwant: %v", test.name, result2, expected) continue } } } // TestModNScalarMulRandom ensures that multiplying two scalars together for // random values works as expected by also performing the same operation with // big ints and comparing the results. func TestModNScalarMulRandom(t *testing.T) { // Use a unique random seed each test instance and log it if the tests fail. seed := time.Now().Unix() rng := rand.New(rand.NewSource(seed)) defer func(t *testing.T, seed int64) { if t.Failed() { t.Logf("random seed: %d", seed) } }(t, seed) for i := 0; i < 100; i++ { // Generate two big integer and mod n scalar pairs. bigIntVal1, modNVal1 := randIntAndModNScalar(t, rng) bigIntVal2, modNVal2 := randIntAndModNScalar(t, rng) // Calculate the square of the value using big ints. bigIntResult := new(big.Int).Mul(bigIntVal1, bigIntVal2) bigIntResult.Mod(bigIntResult, curveParams.N) // Calculate the square of the value using mod n scalar. modNValResult := new(ModNScalar).Mul2(modNVal1, modNVal2) // Ensure they match. bigIntResultHex := fmt.Sprintf("%064x", bigIntResult) modNResultHex := fmt.Sprintf("%v", modNValResult) if bigIntResultHex != modNResultHex { t.Fatalf("mismatched mul\nbig int in 1: %x\nbig int in 2: %x\n"+ "scalar in 1: %v\nscalar in 2: %v\nbig int result: %x\nscalar "+ "result %v", bigIntVal1, bigIntVal2, modNVal1, modNVal2, bigIntResult, modNValResult) } } } // TestModNScalarSquare ensures that squaring scalars works as expected for edge // cases. func TestModNScalarSquare(t *testing.T) { tests := []struct { name string // test description in string // hex encoded test value expected string // expected hex encoded value }{{ name: "zero", in: "0", expected: "0", }, { name: "one", in: "1", expected: "1", }, { name: "over group order", in: "0000000000000000000000000000000100000000000000000000000000000000", expected: "000000000000000000000000000000014551231950b75fc4402da1732fc9bebf", }, { name: "group order - 1", in: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", expected: "0000000000000000000000000000000000000000000000000000000000000001", }, { name: "overflow in word eight", in: "100000000000000000000000000000000", expected: "14551231950b75fc4402da1732fc9bebf", }, { name: "overflow in word nine", in: "1000000000000000000000000000000000000", expected: "14551231950b75fc4402da1732fc9bebf00000000", }, { name: "overflow in word ten", in: "10000000000000000000000000000000000000000", expected: "14551231950b75fc4402da1732fc9bebf0000000000000000", }, { name: "overflow in word eleven", in: "100000000000000000000000000000000000000000000", expected: "14551231950b75fc4402da1732fc9bebf000000000000000000000000", }, { name: "overflow in word twelve", in: "1000000000000000000000000000000000000000000000000", expected: "4551231950b75fc4402da1732fc9bec04551231950b75fc4402da1732fc9bebf", }, { name: "overflow in word thirteen", in: "10000000000000000000000000000000000000000000000000000", expected: "50b75fc4402da1732fc9bec09d671cd51b343a1b66926b57d2a4c1c61536bda7", }, { name: "overflow in word fourteen", in: "100000000000000000000000000000000000000000000000000000000", expected: "402da1732fc9bec09d671cd581c69bc59509b0b074ec0aea8f564d667ec7eb3c", }, { name: "overflow in word fifteen", in: "1000000000000000000000000000000000000000000000000000000000000", expected: "2fc9bec09d671cd581c69bc5e697f5e41f12c33a0a7b6f4e3302b92ea029cecd", }, { name: "alternating bits", in: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", expected: "fb0982c5761d1eac534247f2a7c3af186a134d709b977ca88300faad5eafe9bc", }, { name: "alternating bits 2", in: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", expected: "9081c595b95b2d17c424a546144b25488104c5889d914635bc9d1a51859e1c19", }} for _, test := range tests { v := new(ModNScalar).SetHex(test.in) expected := new(ModNScalar).SetHex(test.expected) // Ensure squaring another value produces the expected result. result := new(ModNScalar).SquareVal(v) if !result.Equals(expected) { t.Errorf("%s: wrong result\ngot: %v\nwant: %v", test.name, result, expected) continue } // Ensure self squaring also produces the expected result. result2 := new(ModNScalar).Set(v).Square() if !result2.Equals(expected) { t.Errorf("%s: wrong result\ngot: %v\nwant: %v", test.name, result2, expected) continue } } } // TestModNScalarSquareRandom ensures that squaring scalars for random values // works as expected by also performing the same operation with big ints and // comparing the results. func TestModNScalarSquareRandom(t *testing.T) { // Use a unique random seed each test instance and log it if the tests fail. seed := time.Now().Unix() rng := rand.New(rand.NewSource(seed)) defer func(t *testing.T, seed int64) { if t.Failed() { t.Logf("random seed: %d", seed) } }(t, seed) for i := 0; i < 100; i++ { // Generate big integer and mod n scalar with the same random value. bigIntVal, modNVal := randIntAndModNScalar(t, rng) // Calculate the square of the value using big ints. bigIntResult := new(big.Int).Mul(bigIntVal, bigIntVal) bigIntResult.Mod(bigIntResult, curveParams.N) // Calculate the square of the value using mod n scalar. modNValResult := new(ModNScalar).SquareVal(modNVal) // Ensure they match. bigIntResultHex := fmt.Sprintf("%064x", bigIntResult) modNResultHex := fmt.Sprintf("%v", modNValResult) if bigIntResultHex != modNResultHex { t.Fatalf("mismatched square\nbig int in: %x\nscalar in: %v\n"+ "big int result: %x\nscalar result %v", bigIntVal, modNVal, bigIntResult, modNValResult) } } } // TestModNScalarNegate ensures that negating scalars works as expected for edge // cases. func TestModNScalarNegate(t *testing.T) { tests := []struct { name string // test description in string // hex encoded test value expected string // hex encoded expected result }{{ name: "zero", in: "0", expected: "0", }, { name: "one", in: "1", expected: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", }, { name: "negation in word one", in: "0000000000000000000000000000000000000000000000000000000100000000", expected: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8bd0364141", }, { name: "negation in word two", in: "0000000000000000000000000000000000000000000000010000000000000000", expected: "fffffffffffffffffffffffffffffffebaaedce6af48a03abfd25e8cd0364141", }, { name: "negation in word three", in: "0000000000000000000000000000000000000001000000000000000000000000", expected: "fffffffffffffffffffffffffffffffebaaedce5af48a03bbfd25e8cd0364141", }, { name: "negation in word four", in: "0000000000000000000000000000000100000000000000000000000000000000", expected: "fffffffffffffffffffffffffffffffdbaaedce6af48a03bbfd25e8cd0364141", }, { name: "negation in word five", in: "0000000000000000000000010000000000000000000000000000000000000000", expected: "fffffffffffffffffffffffefffffffebaaedce6af48a03bbfd25e8cd0364141", }, { name: "negation in word six", in: "0000000000000001000000000000000000000000000000000000000000000000", expected: "fffffffffffffffefffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", }, { name: "negation in word seven", in: "0000000100000000000000000000000000000000000000000000000000000000", expected: "fffffffefffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", }, { name: "alternating bits", in: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", expected: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a591509374109a2fa961a2cb8e72a909b9c", }, { name: "alternating bits 2", in: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", expected: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a46054828c54ee45e16578043275dbe6e7", }} for _, test := range tests { s := new(ModNScalar).SetHex(test.in) expected := new(ModNScalar).SetHex(test.expected) // Ensure negating another value produces the expected result. result := new(ModNScalar).NegateVal(s) if !result.Equals(expected) { t.Errorf("%s: unexpected result -- got: %v, want: %v", test.name, result, expected) continue } // Ensure self negating also produces the expected result. result2 := new(ModNScalar).Set(s).Negate() if !result2.Equals(expected) { t.Errorf("%s: unexpected result -- got: %v, want: %v", test.name, result2, expected) continue } } } // TestModNScalarNegateRandom ensures that negating scalars for random values // works as expected by also performing the same operation with big ints and // comparing the results. func TestModNScalarNegateRandom(t *testing.T) { // Use a unique random seed each test instance and log it if the tests fail. seed := time.Now().Unix() rng := rand.New(rand.NewSource(seed)) defer func(t *testing.T, seed int64) { if t.Failed() { t.Logf("random seed: %d", seed) } }(t, seed) for i := 0; i < 100; i++ { // Generate big integer and mod n scalar with the same random value. bigIntVal, modNVal := randIntAndModNScalar(t, rng) // Calculate the negation of the value using big ints. bigIntResult := new(big.Int).Neg(bigIntVal) bigIntResult.Mod(bigIntResult, curveParams.N) // Calculate the negation of the value using mod n scalar. modNValResult := new(ModNScalar).NegateVal(modNVal) // Ensure they match. bigIntResultHex := fmt.Sprintf("%064x", bigIntResult) modNResultHex := fmt.Sprintf("%v", modNValResult) if bigIntResultHex != modNResultHex { t.Fatalf("mismatched negate\nbig int in: %x\nscalar in: %v\n"+ "big int result: %x\nscalar result %v", bigIntVal, modNVal, bigIntResult, modNValResult) } } } // TestModNScalarInverseNonConst ensures that calculating the multiplicative // inverse of scalars in *non-constant* time works as expected for edge cases. func TestModNScalarInverseNonConst(t *testing.T) { tests := []struct { name string // test description in string // hex encoded test value expected string // hex encoded expected result }{{ name: "zero", in: "0", expected: "0", }, { name: "one", in: "1", expected: "1", }, { name: "inverse carry in word one", in: "0000000000000000000000000000000000000000000000000000000100000000", expected: "5588b13effffffffffffffffffffffff934e5b00ca8417bf50177f7ba415411a", }, { name: "inverse carry in word two", in: "0000000000000000000000000000000000000000000000010000000000000000", expected: "4b0dff665588b13effffffffffffffffa09f710af01555259d4ad302583de6dc", }, { name: "inverse carry in word three", in: "0000000000000000000000000000000000000001000000000000000000000000", expected: "34b9ec244b0dff665588b13effffffffbcff4127932a971a78274c9d74176b38", }, { name: "inverse carry in word four", in: "0000000000000000000000000000000100000000000000000000000000000000", expected: "50a51ac834b9ec244b0dff665588b13e9984d5b3cf80ef0fd6a23766a3ee9f22", }, { name: "inverse carry in word five", in: "0000000000000000000000010000000000000000000000000000000000000000", expected: "27cfab5e50a51ac834b9ec244b0dff6622f16e85b683d5a059bcd5a3b29d9dff", }, { name: "inverse carry in word six", in: "0000000000000001000000000000000000000000000000000000000000000000", expected: "897f30c127cfab5e50a51ac834b9ec239c53f268b4700c14f19b9499ac58d8ad", }, { name: "inverse carry in word seven", in: "0000000100000000000000000000000000000000000000000000000000000000", expected: "6494ef93897f30c127cfab5e50a51ac7b4e8f713e0cddd182234e907286ae6b3", }, { name: "alternating bits", in: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", expected: "cb6086e560b8597a85c934e46f5b6e8a445bf3f0a88e4160d7fa8d83fd10338d", }, { name: "alternating bits 2", in: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", expected: "9f864ca486a74eb5f546364d76d24aa93716dc78f84847aa6c1c09fca2707d77", }} for _, test := range tests { s := new(ModNScalar).SetHex(test.in) expected := new(ModNScalar).SetHex(test.expected) // Ensure calculating the multiplicative inverse of another value // produces the expected result. result := new(ModNScalar).InverseValNonConst(s) if !result.Equals(expected) { t.Errorf("%s: unexpected result -- got: %v, want: %v", test.name, result, expected) continue } // Ensure calculating the multiplicative inverse in place also produces // the expected result. result2 := new(ModNScalar).Set(s).InverseNonConst() if !result2.Equals(expected) { t.Errorf("%s: unexpected result -- got: %v, want: %v", test.name, result2, expected) continue } } } // TestModNScalarInverseNonConstRandom ensures that calculating the // multiplicative inverse of scalars in *non-constant* time for random values // works as expected by also performing the same operation with big ints and // comparing the results. func TestModNScalarInverseNonConstRandom(t *testing.T) { // Use a unique random seed each test instance and log it if the tests fail. seed := time.Now().Unix() rng := rand.New(rand.NewSource(seed)) defer func(t *testing.T, seed int64) { if t.Failed() { t.Logf("random seed: %d", seed) } }(t, seed) for i := 0; i < 100; i++ { // Generate big integer and mod n scalar with the same random value. bigIntVal, modNVal := randIntAndModNScalar(t, rng) // Calculate the inverse of the value using big ints. bigIntResult := new(big.Int).ModInverse(bigIntVal, curveParams.N) // Calculate the inverse of the value using a mod n scalar. modNValResult := new(ModNScalar).InverseValNonConst(modNVal) // Ensure they match. bigIntResultHex := fmt.Sprintf("%064x", bigIntResult) modNResultHex := fmt.Sprintf("%v", modNValResult) if bigIntResultHex != modNResultHex { t.Fatalf("mismatched inverse\nbig int in: %x\nscalar in: %v\n"+ "big int result: %x\nscalar result %v", bigIntVal, modNVal, bigIntResult, modNValResult) } } } // TestModNScalarIsOverHalfOrder ensures that scalars report whether or not they // exceeed the half order works as expected for edge cases. func TestModNScalarIsOverHalfOrder(t *testing.T) { tests := []struct { name string // test description in string // hex encoded test value expected bool // expected result }{{ name: "zero", in: "0", expected: false, }, { name: "one", in: "1", expected: false, }, { name: "group half order - 1", in: "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b209f", expected: false, }, { name: "group half order", in: "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", expected: false, }, { name: "group half order + 1", in: "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1", expected: true, }, { name: "over half order word one", in: "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f47681b20a0", expected: true, }, { name: "over half order word two", in: "7fffffffffffffffffffffffffffffff5d576e7357a4501edfe92f46681b20a0", expected: true, }, { name: "over half order word three", in: "7fffffffffffffffffffffffffffffff5d576e7457a4501ddfe92f46681b20a0", expected: true, }, { name: "over half order word seven", in: "8fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", expected: true, }} for _, test := range tests { result := new(ModNScalar).SetHex(test.in).IsOverHalfOrder() if result != test.expected { t.Errorf("%s: unexpected result -- got: %v, want: %v", test.name, result, test.expected) continue } } } // TestModNScalarIsOverHalfOrderRandom ensures that scalars report whether or // not they exceeed the half order for random values works as expected by also // performing the same operation with big ints and comparing the results. func TestModNScalarIsOverHalfOrderRandom(t *testing.T) { // Use a unique random seed each test instance and log it if the tests fail. seed := time.Now().Unix() rng := rand.New(rand.NewSource(seed)) defer func(t *testing.T, seed int64) { if t.Failed() { t.Logf("random seed: %d", seed) } }(t, seed) bigHalfOrder := new(big.Int).Rsh(curveParams.N, 1) for i := 0; i < 100; i++ { // Generate big integer and mod n scalar with the same random value. bigIntVal, modNVal := randIntAndModNScalar(t, rng) // Determine the value exceeds the half order using big ints. bigIntResult := bigIntVal.Cmp(bigHalfOrder) > 0 // Determine the value exceeds the half order using a mod n scalar. modNValResult := modNVal.IsOverHalfOrder() // Ensure they match. if bigIntResult != modNValResult { t.Fatalf("mismatched is over half order\nbig int in: %x\nscalar "+ "in: %v\nbig int result: %v\nscalar result %v", bigIntVal, modNVal, bigIntResult, modNValResult) } } }