wormhole/bridge/third_party/chainlink/secp256k1/point_test.go

233 lines
6.5 KiB
Go

package secp256k1
import (
"bytes"
"crypto/rand"
"fmt"
"math/big"
"testing"
"go.dedis.ch/kyber/v3/group/curve25519"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/smartcontractkit/chainlink/core/services/signatures/cryptotest"
)
var numPointSamples = 10
var randomStreamPoint = cryptotest.NewStream(&testing.T{}, 0)
func TestPoint_String(t *testing.T) {
require.Equal(t, newPoint().String(),
"Secp256k1{X: fieldElt{0}, Y: fieldElt{0}}")
}
func TestPoint_CloneAndEqual(t *testing.T) {
f := newPoint()
for i := 0; i < numPointSamples; i++ {
g := f.Clone()
f.Pick(randomStreamPoint)
assert.NotEqual(t, f, g,
"modifying original shouldn't change clone")
g, h := f.Clone(), f.Clone()
assert.Equal(t, f, g, "clones should be equal")
g.Add(g, f)
assert.Equal(t, h, f,
"modifying a clone shouldn't change originial")
}
}
func TestPoint_NullAndAdd(t *testing.T) {
f, g := newPoint(), newPoint()
for i := 0; i < numPointSamples; i++ {
g.Null()
f.Pick(randomStreamPoint)
g.Add(f, g)
assert.Equal(t, f, g, "adding zero should have no effect")
}
}
func TestPoint_Set(t *testing.T) {
p := newPoint()
base := newPoint().Base()
assert.NotEqual(t, p, base, "generator should not be zero")
p.Set(base)
assert.Equal(t, p, base, "setting to generator should yield generator")
}
func TestPoint_Embed(t *testing.T) {
p := newPoint()
for i := 0; i < numPointSamples; i++ {
data := make([]byte, p.EmbedLen())
_, err := rand.Read(data)
require.Nil(t, err)
p.Embed(data, randomStreamPoint)
require.True(t, s256.IsOnCurve(p.X.int(), p.Y.int()),
"should embed to a secp256k1 point")
output, err := p.Data()
require.NoError(t, err)
require.True(t, bytes.Equal(data, output),
"should get same value back after round-trip "+
"embedding, got %v, then %v", data, output)
}
var uint256Bytes [32]byte
uint256Bytes[0] = 30
p.X.SetBytes(uint256Bytes)
_, err := p.Data()
require.Error(t, err)
require.Contains(t, err.Error(), "specifies too much data")
var b bytes.Buffer
p.Pick(randomStreamPoint)
_, err = p.MarshalTo(&b)
require.NoError(t, err)
_, err = p.UnmarshalFrom(&b)
require.NoError(t, err)
data := make([]byte, p.EmbedLen()+1) // Check length validation. This test
defer func() { // comes last, because it triggers panic
r := recover()
require.NotNil(t, r, "calling embed with too much data should panic")
require.Contains(t, r, "too much data to embed in a point")
}()
p.Embed(data, randomStreamPoint)
}
func TestPoint_AddSubAndNeg(t *testing.T) {
zero := newPoint().Null()
p := newPoint()
for i := 0; i < numPointSamples; i++ {
p.Pick(randomStreamPoint)
q := p.Clone()
p.Sub(p, q)
require.True(t, p.Equal(zero),
"subtracting a point from itself should give zero, "+
"got %v - %v = %v ≠ %v", q, q, p, zero)
p.Neg(q)
r := newPoint().Add(p, q)
require.True(t, r.Equal(zero),
"adding a point to its negative should give zero"+
" got %v+%v=%v≠%v", q, p, r, zero)
r.Neg(q)
p.Sub(q, r)
s := newPoint().Add(q, q)
require.True(t, p.Equal(s), "q-(-q)=q+q?"+
" got %v-%v=%v≠%v", q, r, p, s)
}
}
func TestPoint_Mul(t *testing.T) {
zero := newPoint().Null()
multiplier := newScalar(bigZero)
one := newScalar(big.NewInt(int64(1)))
var p *secp256k1Point
for i := 0; i < numPointSamples/5; i++ {
if i%20 == 0 {
p = nil // Test default to generator point
} else {
p = newPoint()
p.Pick(randomStreamPoint)
}
multiplier.Pick(randomStreamPoint)
q := newPoint().Mul(one, p)
comparee := newPoint()
if p == (*secp256k1Point)(nil) {
comparee.Base()
} else {
comparee = p.Clone().(*secp256k1Point)
}
require.True(t, comparee.Equal(q), "1*p=p? %v * %v ≠ %v", one,
comparee, q)
q.Mul(multiplier, p)
negMultiplier := newScalar(bigZero).Neg(multiplier)
r := newPoint().Mul(negMultiplier, p)
s := newPoint().Add(q, r)
require.True(t, s.Equal(zero), "s*p+(-s)*p=0? got "+
"%v*%v + %v*%v = %v + %v = %v ≠ %v", multiplier, p,
)
}
}
func TestPoint_Marshal(t *testing.T) {
p := newPoint()
for i := 0; i < numPointSamples; i++ {
p.Pick(randomStreamPoint)
serialized, err := p.MarshalBinary()
require.Nil(t, err)
q := newPoint()
err = q.UnmarshalBinary(serialized)
require.Nil(t, err)
require.True(t, p.Equal(q), "%v marshalled to %x, which "+
"unmarshalled to %v", p, serialized, q)
}
p.X.SetInt(big.NewInt(0)) // 0³+7 is not a square in the base field.
_, err := p.MarshalBinary()
require.Error(t, err)
require.Contains(t, err.Error(), "not a square")
p.X.SetInt(big.NewInt(1))
_, err = p.MarshalBinary()
require.Error(t, err)
require.Contains(t, err.Error(), "not a point on the curve")
id := p.MarshalID()
require.Equal(t, string(id[:]), "sp256.po")
data := make([]byte, 34)
err = p.UnmarshalBinary(data)
require.Error(t, err)
require.Contains(t, err.Error(), "wrong length for marshaled point")
require.Contains(t, p.UnmarshalBinary(data[:32]).Error(),
"wrong length for marshaled point")
data[32] = 2
require.Contains(t, p.UnmarshalBinary(data[:33]).Error(),
"bad sign byte")
data[32] = 0
data[31] = 5 // I.e., x-ordinate is now 5
require.Contains(t, p.UnmarshalBinary(data[:33]).Error(),
"does not correspond to a curve point")
}
func TestPoint_BaseTakesCopy(t *testing.T) {
p := newPoint().Base()
p.Add(p, p)
q := newPoint().Base()
assert.False(t, p.Equal(q),
"modifying output from Base changes S256.G{x,y}")
}
func TestPoint_EthereumAddress(t *testing.T) {
// Example taken from
// https://theethereum.wiki/w/index.php/Accounts,_Addresses,_Public_And_Private_Keys,_And_Tokens
pString := "3a1076bf45ab87712ad64ccb3b10217737f7faacbf2872e88fdd9a537d8fe266"
pInt, ok := big.NewInt(0).SetString(pString, 16)
require.True(t, ok, "failed to parse private key")
private := newScalar(pInt)
public := newPoint().Mul(private, nil)
address := EthereumAddress(public)
assert.Equal(t, fmt.Sprintf("%x", address),
"c2d7cf95645d33006175b78989035c7c9061d3f9")
}
func TestIsSecp256k1Point(t *testing.T) {
p := curve25519.NewBlakeSHA256Curve25519(false).Point()
require.False(t, IsSecp256k1Point(p))
require.True(t, IsSecp256k1Point(newPoint()))
}
func TestCoordinates(t *testing.T) {
x, y := Coordinates(newPoint())
require.Equal(t, x, bigZero)
require.Equal(t, y, bigZero)
}
func TestValidPublicKey(t *testing.T) {
require.False(t, ValidPublicKey(newPoint()), "zero is not a valid key")
require.True(t, ValidPublicKey(newPoint().Base()))
}
func TestGenerate(t *testing.T) {
for {
if ValidPublicKey(Generate(randomStreamPoint).Public) {
break
}
}
}