From 8f333ba0fd58b4ebb74fcf4f97d082dad277ed37 Mon Sep 17 00:00:00 2001 From: Hendrik Hofstadt Date: Tue, 4 Aug 2020 23:52:39 +0200 Subject: [PATCH] Fix chainlink key generation/validation --- bridge/pkg/signatures/secp256k1/field.go | 33 ++++++++++--------- bridge/pkg/signatures/secp256k1/field_test.go | 12 +++---- bridge/pkg/signatures/secp256k1/point.go | 23 ++++++++----- bridge/pkg/signatures/secp256k1/point_test.go | 2 +- 4 files changed, 39 insertions(+), 31 deletions(-) diff --git a/bridge/pkg/signatures/secp256k1/field.go b/bridge/pkg/signatures/secp256k1/field.go index 0cd2f8500..0d1a922c1 100644 --- a/bridge/pkg/signatures/secp256k1/field.go +++ b/bridge/pkg/signatures/secp256k1/field.go @@ -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 } diff --git a/bridge/pkg/signatures/secp256k1/field_test.go b/bridge/pkg/signatures/secp256k1/field_test.go index 966e63627..0287d667f 100644 --- a/bridge/pkg/signatures/secp256k1/field_test.go +++ b/bridge/pkg/signatures/secp256k1/field_test.go @@ -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") diff --git a/bridge/pkg/signatures/secp256k1/point.go b/bridge/pkg/signatures/secp256k1/point.go index de168f123..ccd2a5c97 100644 --- a/bridge/pkg/signatures/secp256k1/point.go +++ b/bridge/pkg/signatures/secp256k1/point.go @@ -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))) } diff --git a/bridge/pkg/signatures/secp256k1/point_test.go b/bridge/pkg/signatures/secp256k1/point_test.go index b3fceaea5..98156b1ee 100644 --- a/bridge/pkg/signatures/secp256k1/point_test.go +++ b/bridge/pkg/signatures/secp256k1/point_test.go @@ -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)