Fix chainlink key generation/validation

This commit is contained in:
Hendrik Hofstadt 2020-08-04 23:52:39 +02:00
parent 7993a72dea
commit 8f333ba0fd
4 changed files with 39 additions and 31 deletions

View File

@ -21,6 +21,7 @@ import (
// q is the field characteristic (cardinality) of the secp256k1 base field. All
// arithmetic operations on the field are modulo this.
var q = s256.P
var halfQ = new(big.Int).Div(q, big.NewInt(2))
type fieldElt big.Int
@ -28,17 +29,17 @@ type fieldElt big.Int
func newFieldZero() *fieldElt { return (*fieldElt)(big.NewInt(0)) }
// Int returns f as a big.Int
func (f *fieldElt) int() *big.Int { return (*big.Int)(f) }
func (f *fieldElt) Int() *big.Int { return (*big.Int)(f) }
// modQ reduces f's underlying big.Int modulo q, and returns it
func (f *fieldElt) modQ() *fieldElt {
if f.int().Cmp(q) != -1 || f.int().Cmp(bigZero) == -1 {
if f.Int().Cmp(q) != -1 || f.Int().Cmp(bigZero) == -1 {
// f ∉ {0, ..., q-1}. Find the representative of f+q in that set.
//
// Per Mod docstring, "Mod implements Euclidean modulus", meaning that after
// this, f will be the smallest non-negative representative of its
// equivalence class in /q. TODO(alx): Make this faster
f.int().Mod(f.int(), q)
f.Int().Mod(f.Int(), q)
}
return f
}
@ -55,7 +56,7 @@ var bigZero = big.NewInt(0)
// String returns the string representation of f
func (f *fieldElt) String() string {
return fmt.Sprintf("fieldElt{%x}", f.int())
return fmt.Sprintf("fieldElt{%x}", f.Int())
}
// Equal returns true iff f=g, i.e. the backing big.Ints satisfy f ≡ g mod q
@ -69,30 +70,30 @@ func (f *fieldElt) Equal(g *fieldElt) bool {
if g == (*fieldElt)(nil) { // g is nil, f is not
return false
}
return bigZero.Cmp(newFieldZero().Sub(f, g).modQ().int()) == 0
return bigZero.Cmp(newFieldZero().Sub(f, g).modQ().Int()) == 0
}
// Add sets f to the sum of a and b modulo q, and returns it.
func (f *fieldElt) Add(a, b *fieldElt) *fieldElt {
f.int().Add(a.int(), b.int())
f.Int().Add(a.Int(), b.Int())
return f.modQ()
}
// Sub sets f to a-b mod q, and returns it.
func (f *fieldElt) Sub(a, b *fieldElt) *fieldElt {
f.int().Sub(a.int(), b.int())
f.Int().Sub(a.Int(), b.Int())
return f.modQ()
}
// Set sets f's value to v, and returns f.
func (f *fieldElt) Set(v *fieldElt) *fieldElt {
f.int().Set(v.int())
f.Int().Set(v.Int())
return f.modQ()
}
// SetInt sets f's value to v mod q, and returns f.
func (f *fieldElt) SetInt(v *big.Int) *fieldElt {
f.int().Set(v)
f.Int().Set(v)
return f.modQ()
}
@ -103,7 +104,7 @@ func (f *fieldElt) Pick(rand cipher.Stream) *fieldElt {
// Neg sets f to the negation of g modulo q, and returns it
func (f *fieldElt) Neg(g *fieldElt) *fieldElt {
f.int().Neg(g.int())
f.Int().Neg(g.Int())
return f.modQ()
}
@ -113,13 +114,13 @@ func (f *fieldElt) Clone() *fieldElt { return newFieldZero().Set(f.modQ()) }
// SetBytes sets f to the 32-byte big-endian value represented by buf, reduces
// it, and returns it.
func (f *fieldElt) SetBytes(buf [32]byte) *fieldElt {
f.int().SetBytes(buf[:])
f.Int().SetBytes(buf[:])
return f.modQ()
}
// Bytes returns the 32-byte big-endian representation of f
func (f *fieldElt) Bytes() [32]byte {
bytes := f.modQ().int().Bytes()
bytes := f.modQ().Int().Bytes()
if len(bytes) > 32 {
panic("field element longer than 256 bits")
}
@ -132,7 +133,7 @@ var two = big.NewInt(2)
// square returns y² mod q
func fieldSquare(y *fieldElt) *fieldElt {
return fieldEltFromBigInt(newFieldZero().int().Exp(y.int(), two, q))
return fieldEltFromBigInt(newFieldZero().Int().Exp(y.Int(), two, q))
}
// sqrtPower is s.t. n^sqrtPower≡sqrt(n) mod q, if n has a root at all. See
@ -146,7 +147,7 @@ var sqrtPower = s256.QPlus1Div4()
// maybeSqrtInField returns a square root of v, if it has any, else nil
func maybeSqrtInField(v *fieldElt) *fieldElt {
s := newFieldZero()
s.int().Exp(v.int(), sqrtPower, q)
s.Int().Exp(v.Int(), sqrtPower, q)
if !fieldSquare(s).Equal(v) {
return nil
}
@ -159,11 +160,11 @@ var seven = fieldEltFromInt(7)
// rightHandSide returns the RHS of the secp256k1 equation, x³+7 mod q, given x
func rightHandSide(x *fieldElt) *fieldElt {
xCubed := newFieldZero()
xCubed.int().Exp(x.int(), three, q)
xCubed.Int().Exp(x.Int(), three, q)
return xCubed.Add(xCubed, seven)
}
// isEven returns true if f is even, false otherwise
func (f *fieldElt) isEven() bool {
return big.NewInt(0).Mod(f.int(), two).Cmp(big.NewInt(0)) == 0
return big.NewInt(0).Mod(f.Int(), two).Cmp(big.NewInt(0)) == 0
}

View File

@ -74,7 +74,7 @@ func TestField_SmokeTestPick(t *testing.T) {
f := newFieldZero()
f.Pick(randomStream)
observedFieldElt(t, f)
assert.True(t, f.int().Cmp(big.NewInt(1000000000)) == 1,
assert.True(t, f.Int().Cmp(big.NewInt(1000000000)) == 1,
"should be greater than 1000000000, with very high probability")
}
@ -132,9 +132,9 @@ func TestField_MaybeSquareRootInField(t *testing.T) {
for i := 0; i < numFieldSamples; i++ {
f.Pick(randomStream)
observedFieldElt(t, f)
require.True(t, f.int().Cmp(q) == -1, "picked larger value than q: %s", f)
require.True(t, f.int().Cmp(big.NewInt(-1)) != -1,
"backing int must be non-negative")
require.True(t, f.Int().Cmp(q) == -1, "picked larger value than q: %s", f)
require.True(t, f.Int().Cmp(big.NewInt(-1)) != -1,
"backing Int must be non-negative")
s := fieldSquare(f)
g := maybeSqrtInField(s)
require.NotEqual(t, g, (*fieldElt)(nil))
@ -142,11 +142,11 @@ func TestField_MaybeSquareRootInField(t *testing.T) {
require.True(t, f.Equal(g) || f.Equal(ng), "squaring something and "+
"taking the square root should give ± the original: failed with %s", f)
bigIntSqrt := newFieldZero() // Cross-check against big.ModSqrt
rv := bigIntSqrt.int().ModSqrt(s.int(), q)
rv := bigIntSqrt.Int().ModSqrt(s.Int(), q)
require.NotNil(t, rv)
require.True(t, bigIntSqrt.Equal(g) || bigIntSqrt.Equal(ng))
nonSquare := newFieldZero().Neg(s)
rv = bigIntSqrt.int().ModSqrt(nonSquare.int(), q)
rv = bigIntSqrt.Int().ModSqrt(nonSquare.Int(), q)
require.Nil(t, rv, "ModSqrt indicates nonSquare is square")
require.Nil(t, maybeSqrtInField(nonSquare), "the negative of square "+
"should not be a square")

View File

@ -148,8 +148,8 @@ func (P *Secp256k1Point) Data() ([]byte, error) {
// Add sets P to a+b (secp256k1 group operation) and returns it.
func (P *Secp256k1Point) Add(a, b kyber.Point) kyber.Point {
X, Y := s256.Add(
a.(*Secp256k1Point).X.int(), a.(*Secp256k1Point).Y.int(),
b.(*Secp256k1Point).X.int(), b.(*Secp256k1Point).Y.int())
a.(*Secp256k1Point).X.Int(), a.(*Secp256k1Point).Y.Int(),
b.(*Secp256k1Point).X.Int(), b.(*Secp256k1Point).Y.Int())
P.X.SetInt(X)
P.Y.SetInt(Y)
return P
@ -158,9 +158,9 @@ func (P *Secp256k1Point) Add(a, b kyber.Point) kyber.Point {
// Add sets P to a-b (secp256k1 group operation), and returns it.
func (P *Secp256k1Point) Sub(a, b kyber.Point) kyber.Point {
X, Y := s256.Add(
a.(*Secp256k1Point).X.int(), a.(*Secp256k1Point).Y.int(),
b.(*Secp256k1Point).X.int(),
newFieldZero().Neg(b.(*Secp256k1Point).Y).int()) // -b_y
a.(*Secp256k1Point).X.Int(), a.(*Secp256k1Point).Y.Int(),
b.(*Secp256k1Point).X.Int(),
newFieldZero().Neg(b.(*Secp256k1Point).Y).Int()) // -b_y
P.X.SetInt(X)
P.Y.SetInt(Y)
return P
@ -185,8 +185,8 @@ func (P *Secp256k1Point) Mul(s kyber.Scalar, a kyber.Point) kyber.Point {
if a == (*Secp256k1Point)(nil) || a == nil {
X, Y = s256.ScalarBaseMult(sBytes)
} else {
X, Y = s256.ScalarMult(a.(*Secp256k1Point).X.int(),
a.(*Secp256k1Point).Y.int(), sBytes)
X, Y = s256.ScalarMult(a.(*Secp256k1Point).X.Int(),
a.(*Secp256k1Point).Y.Int(), sBytes)
}
P.X.SetInt(X)
P.Y.SetInt(Y)
@ -309,7 +309,7 @@ func IsSecp256k1Point(p kyber.Point) bool {
// Coordinates returns the coordinates of p
func Coordinates(p kyber.Point) (*big.Int, *big.Int) {
return p.(*Secp256k1Point).X.int(), p.(*Secp256k1Point).Y.int()
return p.(*Secp256k1Point).X.Int(), p.(*Secp256k1Point).Y.Int()
}
// ValidPublicKey returns true iff p can be used in the optimized on-chain
@ -322,6 +322,13 @@ func ValidPublicKey(p kyber.Point) bool {
if !ok {
return false
}
// Verify that X < HALF_Q so it can be used for optimized on-chain verification
if P.X.Int().Cmp(halfQ) == 1 {
return false
}
// Verify that the pub key is a valid curve point
maybeY := maybeSqrtInField(rightHandSide(P.X))
return maybeY != nil && (P.Y.Equal(maybeY) || P.Y.Equal(maybeY.Neg(maybeY)))
}

View File

@ -64,7 +64,7 @@ func TestPoint_Embed(t *testing.T) {
_, err := rand.Read(data)
require.Nil(t, err)
p.Embed(data, randomStreamPoint)
require.True(t, s256.IsOnCurve(p.X.int(), p.Y.int()),
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)