You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1323 lines
44 KiB
1323 lines
44 KiB
// 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, |
|
) |
|
} |
|
} |
|
}
|
|
|