Files
next.orly.dev/pkg/crypto/ec/secp256k1/ellipticadaptor_test.go

428 lines
17 KiB
Go

// Copyright (c) 2020-2022 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 (
"math/big"
"math/rand"
"testing"
"time"
"lol.mleku.dev/chk"
)
// randBytes returns a byte slice of the required size created from a random
// value generated by the passed rng.
func randBytes(t *testing.T, rng *rand.Rand, numBytes uint8) []byte {
t.Helper()
buf := make([]byte, numBytes)
if _, err := rng.Read(buf); chk.T(err) {
t.Fatalf("failed to read random: %v", err)
}
return buf
}
// TestIsOnCurveAdaptor ensures the IsOnCurve method used to satisfy the
// elliptic.Curve interface works as intended.
func TestIsOnCurveAdaptor(t *testing.T) {
s256 := S256()
if !s256.IsOnCurve(s256.Params().Gx, s256.Params().Gy) {
t.Fatal("generator point does not claim to be on the curve")
}
}
// isValidAffinePoint returns true if the point (x,y) is on the secp256k1 curve
// or is the point at infinity.
func isValidAffinePoint(x, y *big.Int) bool {
if x.Sign() == 0 && y.Sign() == 0 {
return true
}
return S256().IsOnCurve(x, y)
}
// TestAddAffineAdaptor tests addition of points in affine coordinates via the
// method used to satisfy the elliptic.Curve interface works as intended for
// some edge cases and known good values.
func TestAddAffineAdaptor(t *testing.T) {
tests := []struct {
name string // test description
x1, y1 string // hex encoded coordinates of first point to add
x2, y2 string // hex encoded coordinates of second point to add
x3, y3 string // hex encoded coordinates of expected point
}{
{
// Addition with the point at infinity (left hand side).
name: "∞ + P = P",
x1: "0",
y1: "0",
x2: "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575",
y2: "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d",
x3: "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575",
y3: "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d",
}, {
// Addition with the point at infinity (right hand side).
name: "P + ∞ = P",
x1: "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575",
y1: "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d",
x2: "0",
y2: "0",
x3: "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575",
y3: "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d",
}, {
// Addition with different x values.
name: "P(x1, y1) + P(x2, y2)",
x1: "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6",
y1: "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232",
x2: "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575",
y2: "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d",
x3: "fd5b88c21d3143518d522cd2796f3d726793c88b3e05636bc829448e053fed69",
y3: "21cf4f6a5be5ff6380234c50424a970b1f7e718f5eb58f68198c108d642a137f",
}, {
// Addition with same x opposite y.
name: "P(x, y) + P(x, -y) = ∞",
x1: "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6",
y1: "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232",
x2: "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6",
y2: "f48e156428cf0276dc092da5856e182288d7569f97934a56fe44be60f0d359fd",
x3: "0",
y3: "0",
}, {
// Addition with same point.
name: "P(x, y) + P(x, y) = 2P",
x1: "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6",
y1: "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232",
x2: "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6",
y2: "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232",
x3: "59477d88ae64a104dbb8d31ec4ce2d91b2fe50fa628fb6a064e22582196b365b",
y3: "938dc8c0f13d1e75c987cb1a220501bd614b0d3dd9eb5c639847e1240216e3b6",
},
}
curve := S256()
for _, test := range tests {
// Parse the test data.
x1, y1 := fromHex(test.x1), fromHex(test.y1)
x2, y2 := fromHex(test.x2), fromHex(test.y2)
x3, y3 := fromHex(test.x3), fromHex(test.y3)
// Ensure the test data is using points that are actually on the curve
// (or the point at infinity).
if !isValidAffinePoint(x1, y1) {
t.Errorf("%s: first point is not on curve", test.name)
continue
}
if !isValidAffinePoint(x2, y2) {
t.Errorf("%s: second point is not on curve", test.name)
continue
}
if !isValidAffinePoint(x3, y3) {
t.Errorf("%s: expected point is not on curve", test.name)
continue
}
// Add the two points and ensure the result matches expected.
rx, ry := curve.Add(x1, y1, x2, y2)
if rx.Cmp(x3) != 0 || ry.Cmp(y3) != 0 {
t.Errorf(
"%s: wrong result\ngot: (%x, %x)\nwant: (%x, %x)",
test.name, rx, ry, x3, y3,
)
continue
}
}
}
// TestDoubleAffineAdaptor tests doubling of points in affine coordinates via
// the method used to satisfy the elliptic.Curve interface works as intended for
// some edge cases and known good values.
func TestDoubleAffineAdaptor(t *testing.T) {
tests := []struct {
name string // test description
x1, y1 string // hex encoded coordinates of point to double
x3, y3 string // hex encoded coordinates of expected point
}{
{
// Doubling the point at infinity is still the point at infinity.
name: "2*∞ = ∞ (point at infinity)",
x1: "0",
y1: "0",
x3: "0",
y3: "0",
}, {
name: "random point 1",
x1: "e41387ffd8baaeeb43c2faa44e141b19790e8ac1f7ff43d480dc132230536f86",
y1: "1b88191d430f559896149c86cbcb703193105e3cf3213c0c3556399836a2b899",
x3: "88da47a089d333371bd798c548ef7caae76e737c1980b452d367b3cfe3082c19",
y3: "3b6f659b09a362821dfcfefdbfbc2e59b935ba081b6c249eb147b3c2100b1bc1",
}, {
name: "random point 2",
x1: "b3589b5d984f03ef7c80aeae444f919374799edf18d375cab10489a3009cff0c",
y1: "c26cf343875b3630e15bccc61202815b5d8f1fd11308934a584a5babe69db36a",
x3: "e193860172998751e527bb12563855602a227fc1f612523394da53b746bb2fb1",
y3: "2bfcf13d2f5ab8bb5c611fab5ebbed3dc2f057062b39a335224c22f090c04789",
}, {
name: "random point 3",
x1: "2b31a40fbebe3440d43ac28dba23eee71c62762c3fe3dbd88b4ab82dc6a82340",
y1: "9ba7deb02f5c010e217607fd49d58db78ec273371ea828b49891ce2fd74959a1",
x3: "2c8d5ef0d343b1a1a48aa336078eadda8481cb048d9305dc4fdf7ee5f65973a2",
y3: "bb4914ac729e26d3cd8f8dc8f702f3f4bb7e0e9c5ae43335f6e94c2de6c3dc95",
}, {
name: "random point 4",
x1: "61c64b760b51981fab54716d5078ab7dffc93730b1d1823477e27c51f6904c7a",
y1: "ef6eb16ea1a36af69d7f66524c75a3a5e84c13be8fbc2e811e0563c5405e49bd",
x3: "5f0dcdd2595f5ad83318a0f9da481039e36f135005420393e72dfca985b482f4",
y3: "a01c849b0837065c1cb481b0932c441f49d1cab1b4b9f355c35173d93f110ae0",
},
}
curve := S256()
for _, test := range tests {
// Parse test data.
x1, y1 := fromHex(test.x1), fromHex(test.y1)
x3, y3 := fromHex(test.x3), fromHex(test.y3)
// Ensure the test data is using points that are actually on
// the curve (or the point at infinity).
if !isValidAffinePoint(x1, y1) {
t.Errorf("%s: first point is not on the curve", test.name)
continue
}
if !isValidAffinePoint(x3, y3) {
t.Errorf("%s: expected point is not on the curve", test.name)
continue
}
// Double the point and ensure the result matches expected.
rx, ry := curve.Double(x1, y1)
if rx.Cmp(x3) != 0 || ry.Cmp(y3) != 0 {
t.Errorf(
"%s: wrong result\ngot: (%x, %x)\nwant: (%x, %x)",
test.name, rx, ry, x3, y3,
)
continue
}
}
}
// TestScalarBaseMultAdaptor ensures the ScalarBaseMult method used to satisfy
// the elliptic.Curve interface works as intended for some edge cases and known
// good values.
func TestScalarBaseMultAdaptor(t *testing.T) {
tests := []struct {
name string // test description
k string // hex encoded scalar
rx, ry string // hex encoded coordinates of expected point
}{
{
name: "zero",
k: "0000000000000000000000000000000000000000000000000000000000000000",
rx: "0000000000000000000000000000000000000000000000000000000000000000",
ry: "0000000000000000000000000000000000000000000000000000000000000000",
}, {
name: "one (aka 1*G = G)",
k: "0000000000000000000000000000000000000000000000000000000000000001",
rx: "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
ry: "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8",
}, {
name: "group order - 1 (aka -1*G = -G)",
k: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
rx: "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
ry: "b7c52588d95c3b9aa25b0403f1eef75702e84bb7597aabe663b82f6f04ef2777",
}, {
name: "known good point 1",
k: "aa5e28d6a97a2479a65527f7290311a3624d4cc0fa1578598ee3c2613bf99522",
rx: "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6",
ry: "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232",
}, {
name: "known good point 2",
k: "7e2b897b8cebc6361663ad410835639826d590f393d90a9538881735256dfae3",
rx: "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575",
ry: "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d",
}, {
name: "known good point 3",
k: "6461e6df0fe7dfd05329f41bf771b86578143d4dd1f7866fb4ca7e97c5fa945d",
rx: "e8aecc370aedd953483719a116711963ce201ac3eb21d3f3257bb48668c6a72f",
ry: "c25caf2f0eba1ddb2f0f3f47866299ef907867b7d27e95b3873bf98397b24ee1",
}, {
name: "known good point 4",
k: "376a3a2cdcd12581efff13ee4ad44c4044b8a0524c42422a7e1e181e4deeccec",
rx: "14890e61fcd4b0bd92e5b36c81372ca6fed471ef3aa60a3e415ee4fe987daba1",
ry: "297b858d9f752ab42d3bca67ee0eb6dcd1c2b7b0dbe23397e66adc272263f982",
}, {
name: "known good point 5",
k: "1b22644a7be026548810c378d0b2994eefa6d2b9881803cb02ceff865287d1b9",
rx: "f73c65ead01c5126f28f442d087689bfa08e12763e0cec1d35b01751fd735ed3",
ry: "f449a8376906482a84ed01479bd18882b919c140d638307f0c0934ba12590bde",
},
}
curve := S256()
for _, test := range tests {
// Parse the test data.
k := fromHex(test.k)
xWant, yWant := fromHex(test.rx), fromHex(test.ry)
// Ensure the test data is using points that are actually on the curve
// (or the point at infinity).
if !isValidAffinePoint(xWant, yWant) {
t.Errorf("%s: expected point is not on curve", test.name)
continue
}
rx, ry := curve.ScalarBaseMult(k.Bytes())
if rx.Cmp(xWant) != 0 || ry.Cmp(yWant) != 0 {
t.Errorf(
"%s: wrong result:\ngot (%x, %x)\nwant (%x, %x)",
test.name, rx, ry, xWant, yWant,
)
}
}
}
// TestScalarBaseMultAdaptorRandom ensures that the ScalarBaseMult method used
// to satisfy the elliptic.Curve interface works as intended for
// randomly-generated scalars of all lengths up to 40 bytes.
func TestScalarBaseMultAdaptorRandom(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)
s256 := S256()
const maxBytes = 40
const iterations = 10
for numBytes := uint8(1); numBytes < maxBytes; numBytes++ {
for i := 0; i < iterations; i++ {
// Generate a random scalar of the current length.
k := randBytes(t, rng, numBytes)
// Ensure the correct results by performing the multiplication with
// both the func under test as well as the generic scalar mult func.
x, y := s256.ScalarBaseMult(k)
xWant, yWant := s256.ScalarMult(s256.Gx, s256.Gy, k)
if x.Cmp(xWant) != 0 || y.Cmp(yWant) != 0 {
t.Errorf(
"bad output for %x: got (%x, %x), want (%x, %x)", k,
x, y, xWant, yWant,
)
continue
}
}
}
}
// TestScalarMultAdaptor ensures the ScalarMult method used to satisfy the
// elliptic.Curve interface works as intended for some edge cases and known good
// values.
func TestScalarMultAdaptor(t *testing.T) {
tests := []struct {
name string // test description
k string // hex encoded scalar
x, y string // hex encoded coordinates of point to multiply
rx, ry string // hex encoded coordinates of expected point
}{
{
name: "0*P = ∞ (point at infinity)",
k: "0",
x: "7e660beda020e9cc20391cef85374576853b0f22b8925d5d81c5845bb834c21e",
y: "2d114a5edb320cc9806527d1daf1bbb96a8fedc6f9e8ead421eaef2c7208e409",
rx: "0",
ry: "0",
}, {
name: "1*P = P",
k: "1",
x: "c00be8830995d1e44f1420dd3b90d3441fb66f6861c84a35f959c495a3be5440",
y: "ecf9665e6eba45720de652a340600c7356efe24d228bfe6ea2043e7791c51bb7",
rx: "c00be8830995d1e44f1420dd3b90d3441fb66f6861c84a35f959c495a3be5440",
ry: "ecf9665e6eba45720de652a340600c7356efe24d228bfe6ea2043e7791c51bb7",
}, {
name: "(group order - 1)*P = -P (aka -1*P = -P)",
k: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
x: "74a1ad6b5f76e39db2dd249410eac7f99e74c59cb83d2d0ed5ff1543da7703e9",
y: "cc6157ef18c9c63cd6193d83631bbea0093e0968942e8c33d5737fd790e0db08",
rx: "74a1ad6b5f76e39db2dd249410eac7f99e74c59cb83d2d0ed5ff1543da7703e9",
ry: "339ea810e73639c329e6c27c9ce4415ff6c1f6976bd173cc2a8c80276f1f2127",
}, {
name: "(group order - 1)*-P = P (aka -1*-P = -P, with P from prev test)",
k: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
x: "74a1ad6b5f76e39db2dd249410eac7f99e74c59cb83d2d0ed5ff1543da7703e9",
y: "339ea810e73639c329e6c27c9ce4415ff6c1f6976bd173cc2a8c80276f1f2127",
rx: "74a1ad6b5f76e39db2dd249410eac7f99e74c59cb83d2d0ed5ff1543da7703e9",
ry: "cc6157ef18c9c63cd6193d83631bbea0093e0968942e8c33d5737fd790e0db08",
}, {
name: "known good point from base mult tests (aka k*G)",
k: "aa5e28d6a97a2479a65527f7290311a3624d4cc0fa1578598ee3c2613bf99522",
x: "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
y: "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8",
rx: "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6",
ry: "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232",
}, {
name: "known good result 1",
k: "7e2b897b8cebc6361663ad410835639826d590f393d90a9538881735256dfae3",
x: "1697ffa6fd9de627c077e3d2fe541084ce13300b0bec1146f95ae57f0d0bd6a5",
y: "b9c398f186806f5d27561506e4557433a2cf15009e498ae7adee9d63d01b2396",
rx: "6951f3b50aafbc63e21707dd53623b7f42badd633a0567ef1b37f6e42a4237ad",
ry: "9c930796a49110122fbfdedc36418af726197ed950b783a2d29058f8c02130de",
}, {
name: "known good result 2",
k: "6461e6df0fe7dfd05329f41bf771b86578143d4dd1f7866fb4ca7e97c5fa945d",
x: "659214ac1a1790023f53c4cf55a0a63b9e20c1151efa971215b395a558aa151",
y: "b126363aa4243d2759320a356230569a4eea355d9dabd94ed7f4590701e5364d",
rx: "4ffad856833396ef753c0bd4ea40319295f107c476793df0adac2caea53b3df4",
ry: "586fa6b1e9a3ff7df8a2b9b3698badcf40aa06af5600fefc56dd8ae4db5451c5",
}, {
name: "known good result 3",
k: "376a3a2cdcd12581efff13ee4ad44c4044b8a0524c42422a7e1e181e4deeccec",
x: "3f0e80e574456d8f8fa64e044b2eb72ea22eb53fe1efe3a443933aca7f8cb0e3",
y: "cb66d7d7296cbc91e90b9c08485d01b39501253aa65b53a4cb0289e2ea5f404f",
rx: "35ae6480b18e48070709d9276ed97a50c6ee1fc05ac44386c85826533233d28f",
ry: "f88abee3efabd95e80ce8c664bbc3d4d12b24e1a0f4d2b98ba6542789c6715fd",
}, {
name: "known good result 4",
k: "1b22644a7be026548810c378d0b2994eefa6d2b9881803cb02ceff865287d1b9",
x: "d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e",
y: "581e2872a86c72a683842ec228cc6defea40af2bd896d3a5c504dc9ff6a26b58",
rx: "cca7f9a4b0d379c31c438050e163a8945f2f910498bd3b545be20ed862bd6cd9",
ry: "cfc7bbf37bef62da6e5753ed419168fa1376a3fe949c139a8dd0f5303f4ae947",
}, {
name: "known good result 5",
k: "7f5b2cb4b43840c75e4afad83d792e1965d8c21c1109505f45c7d46df422d73e",
x: "bce74de6d5f98dc027740c2bbff05b6aafe5fd8d103f827e48894a2bd3460117",
y: "5bea1fa17a41b115525a3e7dbf0d8d5a4f7ce5c6fc73a6f4f216512417c9f6b4",
rx: "3d96b9290fe6c4f2d62fe2175f4333907d0c3637fada1010b45c7d80690e16de",
ry: "d59c0e8192d7fbd4846172d6479630b751cd03d0d9be0dca2759c6212b70575d",
}, {
// From btcd issue #709.
name: "early implementation regression point",
k: "a2e8ba2e8ba2e8ba2e8ba2e8ba2e8ba219b51835b55cc30ebfe2f6599bc56f58",
x: "000000000000000000000000000000000000000000000000000000000000002c",
y: "420e7a99bba18a9d3952597510fd2b6728cfeafc21a4e73951091d4d8ddbe94e",
rx: "a2112dcdfbcd10ae1133a358de7b82db68e0a3eb4b492cc8268d1e7118c98788",
ry: "27fc7463b7bb3c5f98ecf2c84a6272bb1681ed553d92c69f2dfe25a9f9fd3836",
},
}
curve := S256()
for _, test := range tests {
// Parse the test data.
k := fromHex(test.k)
x, y := fromHex(test.x), fromHex(test.y)
xWant, yWant := fromHex(test.rx), fromHex(test.ry)
// Ensure the test data is using points that are actually on the curve
// (or the point at infinity).
if !isValidAffinePoint(x, y) {
t.Errorf("%s: point is not on curve", test.name)
continue
}
if !isValidAffinePoint(xWant, yWant) {
t.Errorf("%s: expected point is not on curve", test.name)
continue
}
// Perform scalar point multiplication ensure the result matches
// expected.
rx, ry := curve.ScalarMult(x, y, k.Bytes())
if rx.Cmp(xWant) != 0 || ry.Cmp(yWant) != 0 {
t.Errorf(
"%s: wrong result\ngot: (%x, %x)\nwant: (%x, %x)",
test.name, rx, ry, xWant, yWant,
)
}
}
}