Replaced legacy `*.orly` module imports with `next.orly.dev/pkg` paths across the codebase for consistency. Removed legacy `go.mod` files from sub-packages, consolidating dependency management. Added Dockerfiles and configurations for benchmarking environments.
1324 lines
44 KiB
Go
1324 lines
44 KiB
Go
// 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 (
|
|
"fmt"
|
|
"math/big"
|
|
"math/rand"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"lol.mleku.dev/chk"
|
|
"next.orly.dev/pkg/encoders/hex"
|
|
"next.orly.dev/pkg/utils"
|
|
)
|
|
|
|
// 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.Dec(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[:]); chk.T(err) {
|
|
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[:]); chk.T(err) {
|
|
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 !utils.FastEqual(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 !utils.FastEqual(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 !utils.FastEqual(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,
|
|
)
|
|
}
|
|
}
|
|
}
|